123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- //
- // Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgment in the product documentation would be
- // appreciated but is not required.
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- // 3. This notice may not be removed or altered from any source distribution.
- //
- #ifndef DETOURCROWD_H
- #define DETOURCROWD_H
- #include "DetourNavMeshQuery.h"
- #include "DetourObstacleAvoidance.h"
- #include "DetourLocalBoundary.h"
- #include "DetourPathCorridor.h"
- #include "DetourProximityGrid.h"
- #include "DetourPathQueue.h"
- /// The maximum number of neighbors that a crowd agent can take into account
- /// for steering decisions.
- /// @ingroup crowd
- static const int DT_CROWDAGENT_MAX_NEIGHBOURS = 6;
- /// The maximum number of corners a crowd agent will look ahead in the path.
- /// This value is used for sizing the crowd agent corner buffers.
- /// Due to the behavior of the crowd manager, the actual number of useful
- /// corners will be one less than this number.
- /// @ingroup crowd
- static const int DT_CROWDAGENT_MAX_CORNERS = 4;
- /// The maximum number of crowd avoidance configurations supported by the
- /// crowd manager.
- /// @ingroup crowd
- /// @see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(), dtCrowd::getObstacleAvoidanceParams(),
- /// dtCrowdAgentParams::obstacleAvoidanceType
- static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
- /// The maximum number of query filter types supported by the crowd manager.
- /// @ingroup crowd
- /// @see dtQueryFilter, dtCrowd::getFilter() dtCrowd::getEditableFilter(),
- /// dtCrowdAgentParams::queryFilterType
- static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
- /// Provides neighbor data for agents managed by the crowd.
- /// @ingroup crowd
- /// @see dtCrowdAgent::neis, dtCrowd
- struct dtCrowdNeighbour
- {
- int idx; ///< The index of the neighbor in the crowd.
- float dist; ///< The distance between the current agent and the neighbor.
- };
- /// The type of navigation mesh polygon the agent is currently traversing.
- /// @ingroup crowd
- enum CrowdAgentState
- {
- DT_CROWDAGENT_STATE_INVALID, ///< The agent is not in a valid state.
- DT_CROWDAGENT_STATE_WALKING, ///< The agent is traversing a normal navigation mesh polygon.
- DT_CROWDAGENT_STATE_OFFMESH, ///< The agent is traversing an off-mesh connection.
- };
- /// Configuration parameters for a crowd agent.
- /// @ingroup crowd
- struct dtCrowdAgentParams
- {
- float radius; ///< Agent radius. [Limit: >= 0]
- float height; ///< Agent height. [Limit: > 0]
- float maxAcceleration; ///< Maximum allowed acceleration. [Limit: >= 0]
- float maxSpeed; ///< Maximum allowed speed. [Limit: >= 0]
- /// Defines how close a collision element must be before it is considered for steering behaviors. [Limits: > 0]
- float collisionQueryRange;
- float pathOptimizationRange; ///< The path visibility optimization range. [Limit: > 0]
- /// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
- float separationWeight;
- /// Flags that impact steering behavior. (See: #UpdateFlags)
- unsigned char updateFlags;
- /// The index of the avoidance configuration to use for the agent.
- /// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
- unsigned char obstacleAvoidanceType;
- /// The index of the query filter used by this agent.
- unsigned char queryFilterType;
- /// User defined data attached to the agent.
- void* userData;
- };
- enum MoveRequestState
- {
- DT_CROWDAGENT_TARGET_NONE = 0,
- DT_CROWDAGENT_TARGET_FAILED,
- DT_CROWDAGENT_TARGET_VALID,
- DT_CROWDAGENT_TARGET_REQUESTING,
- DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE,
- DT_CROWDAGENT_TARGET_WAITING_FOR_PATH,
- DT_CROWDAGENT_TARGET_VELOCITY,
- };
- /// Represents an agent managed by a #dtCrowd object.
- /// @ingroup crowd
- struct dtCrowdAgent
- {
- /// True if the agent is active, false if the agent is in an unused slot in the agent pool.
- bool active;
- /// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState)
- unsigned char state;
- /// True if the agent has valid path (targetState == DT_CROWDAGENT_TARGET_VALID) and the path does not lead to the requested position, else false.
- bool partial;
- /// The path corridor the agent is using.
- dtPathCorridor corridor;
- /// The local boundary data for the agent.
- dtLocalBoundary boundary;
-
- /// Time since the agent's path corridor was optimized.
- float topologyOptTime;
-
- /// The known neighbors of the agent.
- dtCrowdNeighbour neis[DT_CROWDAGENT_MAX_NEIGHBOURS];
- /// The number of neighbors.
- int nneis;
-
- /// The desired speed.
- float desiredSpeed;
- float npos[3]; ///< The current agent position. [(x, y, z)]
- float disp[3]; ///< A temporary value used to accumulate agent displacement during iterative collision resolution. [(x, y, z)]
- float dvel[3]; ///< The desired velocity of the agent. Based on the current path, calculated from scratch each frame. [(x, y, z)]
- float nvel[3]; ///< The desired velocity adjusted by obstacle avoidance, calculated from scratch each frame. [(x, y, z)]
- float vel[3]; ///< The actual velocity of the agent. The change from nvel -> vel is constrained by max acceleration. [(x, y, z)]
- /// The agent's configuration parameters.
- dtCrowdAgentParams params;
- /// The local path corridor corners for the agent. (Staight path.) [(x, y, z) * #ncorners]
- float cornerVerts[DT_CROWDAGENT_MAX_CORNERS*3];
- /// The local path corridor corner flags. (See: #dtStraightPathFlags) [(flags) * #ncorners]
- unsigned char cornerFlags[DT_CROWDAGENT_MAX_CORNERS];
- /// The reference id of the polygon being entered at the corner. [(polyRef) * #ncorners]
- dtPolyRef cornerPolys[DT_CROWDAGENT_MAX_CORNERS];
- /// The number of corners.
- int ncorners;
-
- unsigned char targetState; ///< State of the movement request.
- dtPolyRef targetRef; ///< Target polyref of the movement request.
- float targetPos[3]; ///< Target position of the movement request (or velocity in case of DT_CROWDAGENT_TARGET_VELOCITY).
- dtPathQueueRef targetPathqRef; ///< Path finder ref.
- bool targetReplan; ///< Flag indicating that the current path is being replanned.
- float targetReplanTime; /// <Time since the agent's target was replanned.
- };
- struct dtCrowdAgentAnimation
- {
- bool active;
- float initPos[3], startPos[3], endPos[3];
- dtPolyRef polyRef;
- float t, tmax;
- };
- /// Crowd agent update flags.
- /// @ingroup crowd
- /// @see dtCrowdAgentParams::updateFlags
- enum UpdateFlags
- {
- DT_CROWD_ANTICIPATE_TURNS = 1,
- DT_CROWD_OBSTACLE_AVOIDANCE = 2,
- DT_CROWD_SEPARATION = 4,
- DT_CROWD_OPTIMIZE_VIS = 8, ///< Use #dtPathCorridor::optimizePathVisibility() to optimize the agent path.
- DT_CROWD_OPTIMIZE_TOPO = 16, ///< Use dtPathCorridor::optimizePathTopology() to optimize the agent path.
- };
- struct dtCrowdAgentDebugInfo
- {
- int idx;
- float optStart[3], optEnd[3];
- dtObstacleAvoidanceDebugData* vod;
- };
- /// Provides local steering behaviors for a group of agents.
- /// @ingroup crowd
- class dtCrowd
- {
- int m_maxAgents;
- dtCrowdAgent* m_agents;
- dtCrowdAgent** m_activeAgents;
- dtCrowdAgentAnimation* m_agentAnims;
-
- dtPathQueue m_pathq;
- dtObstacleAvoidanceParams m_obstacleQueryParams[DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS];
- dtObstacleAvoidanceQuery* m_obstacleQuery;
-
- dtProximityGrid* m_grid;
-
- dtPolyRef* m_pathResult;
- int m_maxPathResult;
-
- float m_agentPlacementHalfExtents[3];
- dtQueryFilter m_filters[DT_CROWD_MAX_QUERY_FILTER_TYPE];
- float m_maxAgentRadius;
- int m_velocitySampleCount;
- dtNavMeshQuery* m_navquery;
- void updateTopologyOptimization(dtCrowdAgent** agents, const int nagents, const float dt);
- void updateMoveRequest(const float dt);
- void checkPathValidity(dtCrowdAgent** agents, const int nagents, const float dt);
- inline int getAgentIndex(const dtCrowdAgent* agent) const { return (int)(agent - m_agents); }
- bool requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos);
- void purge();
-
- public:
- dtCrowd();
- ~dtCrowd();
-
- /// Initializes the crowd.
- /// @param[in] maxAgents The maximum number of agents the crowd can manage. [Limit: >= 1]
- /// @param[in] maxAgentRadius The maximum radius of any agent that will be added to the crowd. [Limit: > 0]
- /// @param[in] nav The navigation mesh to use for planning.
- /// @return True if the initialization succeeded.
- bool init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav);
-
- /// Sets the shared avoidance configuration for the specified index.
- /// @param[in] idx The index. [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
- /// @param[in] params The new configuration.
- void setObstacleAvoidanceParams(const int idx, const dtObstacleAvoidanceParams* params);
- /// Gets the shared avoidance configuration for the specified index.
- /// @param[in] idx The index of the configuration to retreive.
- /// [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
- /// @return The requested configuration.
- const dtObstacleAvoidanceParams* getObstacleAvoidanceParams(const int idx) const;
-
- /// Gets the specified agent from the pool.
- /// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
- /// @return The requested agent.
- const dtCrowdAgent* getAgent(const int idx);
- /// Gets the specified agent from the pool.
- /// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
- /// @return The requested agent.
- dtCrowdAgent* getEditableAgent(const int idx);
- /// The maximum number of agents that can be managed by the object.
- /// @return The maximum number of agents.
- int getAgentCount() const;
-
- /// Adds a new agent to the crowd.
- /// @param[in] pos The requested position of the agent. [(x, y, z)]
- /// @param[in] params The configutation of the agent.
- /// @return The index of the agent in the agent pool. Or -1 if the agent could not be added.
- int addAgent(const float* pos, const dtCrowdAgentParams* params);
- /// Updates the specified agent's configuration.
- /// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
- /// @param[in] params The new agent configuration.
- void updateAgentParameters(const int idx, const dtCrowdAgentParams* params);
- /// Removes the agent from the crowd.
- /// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
- void removeAgent(const int idx);
-
- /// Submits a new move request for the specified agent.
- /// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
- /// @param[in] ref The position's polygon reference.
- /// @param[in] pos The position within the polygon. [(x, y, z)]
- /// @return True if the request was successfully submitted.
- bool requestMoveTarget(const int idx, dtPolyRef ref, const float* pos);
- /// Submits a new move request for the specified agent.
- /// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
- /// @param[in] vel The movement velocity. [(x, y, z)]
- /// @return True if the request was successfully submitted.
- bool requestMoveVelocity(const int idx, const float* vel);
- /// Resets any request for the specified agent.
- /// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
- /// @return True if the request was successfully reseted.
- bool resetMoveTarget(const int idx);
- /// Gets the active agents int the agent pool.
- /// @param[out] agents An array of agent pointers. [(#dtCrowdAgent *) * maxAgents]
- /// @param[in] maxAgents The size of the crowd agent array.
- /// @return The number of agents returned in @p agents.
- int getActiveAgents(dtCrowdAgent** agents, const int maxAgents);
- /// Updates the steering and positions of all agents.
- /// @param[in] dt The time, in seconds, to update the simulation. [Limit: > 0]
- /// @param[out] debug A debug object to load with debug information. [Opt]
- void update(const float dt, dtCrowdAgentDebugInfo* debug);
-
- /// Gets the filter used by the crowd.
- /// @return The filter used by the crowd.
- inline const dtQueryFilter* getFilter(const int i) const { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
-
- /// Gets the filter used by the crowd.
- /// @return The filter used by the crowd.
- inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
- /// Gets the search halfExtents [(x, y, z)] used by the crowd for query operations.
- /// @return The search halfExtents used by the crowd. [(x, y, z)]
- const float* getQueryHalfExtents() const { return m_agentPlacementHalfExtents; }
- /// Same as getQueryHalfExtents. Left to maintain backwards compatibility.
- /// @return The search halfExtents used by the crowd. [(x, y, z)]
- const float* getQueryExtents() const { return m_agentPlacementHalfExtents; }
-
- /// Gets the velocity sample count.
- /// @return The velocity sample count.
- inline int getVelocitySampleCount() const { return m_velocitySampleCount; }
-
- /// Gets the crowd's proximity grid.
- /// @return The crowd's proximity grid.
- const dtProximityGrid* getGrid() const { return m_grid; }
- /// Gets the crowd's path request queue.
- /// @return The crowd's path request queue.
- const dtPathQueue* getPathQueue() const { return &m_pathq; }
- /// Gets the query object used by the crowd.
- const dtNavMeshQuery* getNavMeshQuery() const { return m_navquery; }
- private:
- // Explicitly disabled copy constructor and copy assignment operator.
- dtCrowd(const dtCrowd&);
- dtCrowd& operator=(const dtCrowd&);
- };
- /// Allocates a crowd object using the Detour allocator.
- /// @return A crowd object that is ready for initialization, or null on failure.
- /// @ingroup crowd
- dtCrowd* dtAllocCrowd();
- /// Frees the specified crowd object using the Detour allocator.
- /// @param[in] ptr A crowd object allocated using #dtAllocCrowd
- /// @ingroup crowd
- void dtFreeCrowd(dtCrowd* ptr);
- #endif // DETOURCROWD_H
- ///////////////////////////////////////////////////////////////////////////
- // This section contains detailed documentation for members that don't have
- // a source file. It reduces clutter in the main section of the header.
- /**
- @defgroup crowd Crowd
- Members in this module implement local steering and dynamic avoidance features.
- The crowd is the big beast of the navigation features. It not only handles a
- lot of the path management for you, but also local steering and dynamic
- avoidance between members of the crowd. I.e. It can keep your agents from
- running into each other.
- Main class: #dtCrowd
- The #dtNavMeshQuery and #dtPathCorridor classes provide perfectly good, easy
- to use path planning features. But in the end they only give you points that
- your navigation client should be moving toward. When it comes to deciding things
- like agent velocity and steering to avoid other agents, that is up to you to
- implement. Unless, of course, you decide to use #dtCrowd.
- Basically, you add an agent to the crowd, providing various configuration
- settings such as maximum speed and acceleration. You also provide a local
- target to more toward. The crowd manager then provides, with every update, the
- new agent position and velocity for the frame. The movement will be
- constrained to the navigation mesh, and steering will be applied to ensure
- agents managed by the crowd do not collide with each other.
- This is very powerful feature set. But it comes with limitations.
- The biggest limitation is that you must give control of the agent's position
- completely over to the crowd manager. You can update things like maximum speed
- and acceleration. But in order for the crowd manager to do its thing, it can't
- allow you to constantly be giving it overrides to position and velocity. So
- you give up direct control of the agent's movement. It belongs to the crowd.
- The second biggest limitation revolves around the fact that the crowd manager
- deals with local planning. So the agent's target should never be more than
- 256 polygons aways from its current position. If it is, you risk
- your agent failing to reach its target. So you may still need to do long
- distance planning and provide the crowd manager with intermediate targets.
- Other significant limitations:
- - All agents using the crowd manager will use the same #dtQueryFilter.
- - Crowd management is relatively expensive. The maximum agents under crowd
- management at any one time is between 20 and 30. A good place to start
- is a maximum of 25 agents for 0.5ms per frame.
- @note This is a summary list of members. Use the index or search
- feature to find minor members.
- @struct dtCrowdAgentParams
- @see dtCrowdAgent, dtCrowd::addAgent(), dtCrowd::updateAgentParameters()
- @var dtCrowdAgentParams::obstacleAvoidanceType
- @par
- #dtCrowd permits agents to use different avoidance configurations. This value
- is the index of the #dtObstacleAvoidanceParams within the crowd.
- @see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(),
- dtCrowd::getObstacleAvoidanceParams()
- @var dtCrowdAgentParams::collisionQueryRange
- @par
- Collision elements include other agents and navigation mesh boundaries.
- This value is often based on the agent radius and/or maximum speed. E.g. radius * 8
- @var dtCrowdAgentParams::pathOptimizationRange
- @par
- Only applicalbe if #updateFlags includes the #DT_CROWD_OPTIMIZE_VIS flag.
- This value is often based on the agent radius. E.g. radius * 30
- @see dtPathCorridor::optimizePathVisibility()
- @var dtCrowdAgentParams::separationWeight
- @par
- A higher value will result in agents trying to stay farther away from each other at
- the cost of more difficult steering in tight spaces.
- */
|