Crowd: Detail API documentation for members declared in DetourPathCorridor.h.

This commit is contained in:
Stephen Pratt 2011-09-26 19:10:54 +00:00
parent 82a5c863eb
commit dee5f6a9bc
2 changed files with 183 additions and 3 deletions

View File

@ -21,7 +21,7 @@
#include "DetourNavMeshQuery.h" #include "DetourNavMeshQuery.h"
/// Represents a dynamic polygon corridor used to plan agent movement
class dtPathCorridor class dtPathCorridor
{ {
float m_pos[3]; float m_pos[3];
@ -35,17 +35,40 @@ public:
dtPathCorridor(); dtPathCorridor();
~dtPathCorridor(); ~dtPathCorridor();
/// Allocates the corridor's path buffer.
/// @param[in] maxPath The maximum path size the corridor can handle.
/// @return True if the initialization succeeded.
bool init(const int maxPath); bool init(const int maxPath);
/// Resets the path corridor to the specified position.
/// @param[in] ref The polygon reference containing the position.
/// @param[in] pos The new position in the corridor. [(x, y, z)]
void reset(dtPolyRef ref, const float* pos); void reset(dtPolyRef ref, const float* pos);
/// Finds the corners in the corridor from the position toward the target. (The straightened path.)
/// @param[out] cornerVerts The corner vertices. [(x, y, z) * cornerCount] [Size: <= maxCorners]
/// @param[out] cornerFlags The flag for each corner. [(flag) * cornerCount] [Size: <= maxCorners]
/// @param[out] cornerPolys The polygon reference for each corner. [(polyRef) * cornerCount]
/// [Size: <= @p maxCorners]
/// @param[in] maxCorners The maximum number of corners the buffers can hold.
/// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation.
/// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners]
int findCorners(float* cornerVerts, unsigned char* cornerFlags, int findCorners(float* cornerVerts, unsigned char* cornerFlags,
dtPolyRef* cornerPolys, const int maxCorners, dtPolyRef* cornerPolys, const int maxCorners,
dtNavMeshQuery* navquery, const dtQueryFilter* filter); dtNavMeshQuery* navquery, const dtQueryFilter* filter);
/// Attempts to optimize the path if the specified point is visible from the current position.
/// @param[in] next The point to search toward. [(x, y, z])
/// @param[in] pathOptimizationRange The maximum range to search. [Limit: > 0]
/// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation.
void optimizePathVisibility(const float* next, const float pathOptimizationRange, void optimizePathVisibility(const float* next, const float pathOptimizationRange,
dtNavMeshQuery* navquery, const dtQueryFilter* filter); dtNavMeshQuery* navquery, const dtQueryFilter* filter);
/// Attempts to optimize the path using a local area search. (Partial replanning.)
/// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation.
bool optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter); bool optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter);
bool moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs, bool moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs,
@ -55,20 +78,54 @@ public:
bool trimInvalidPath(dtPolyRef safeRef, const float* safePos, bool trimInvalidPath(dtPolyRef safeRef, const float* safePos,
dtNavMeshQuery* navquery, const dtQueryFilter* filter); dtNavMeshQuery* navquery, const dtQueryFilter* filter);
/// Checks the current corridor path to see if its polygon references remain valid.
/// @param[in] maxLookAhead The number of polygons from the beginning of the corridor to search.
/// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation.
bool isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter); bool isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
/// Moves the position from the current location to the desired location, adjusting the corridor
/// as needed to reflect the change.
/// @param[in] npos The desired new position. [(x, y, z)]
/// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation.
void movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter); void movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
/// Moves the target from the curent location to the desired location, adjusting the corridor
/// as needed to reflect the change.
/// @param[in] npos The desired new target position. [(x, y, z)]
/// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation.
void moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter); void moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
void setCorridor(const float* target, const dtPolyRef* polys, const int npolys); /// Loads a new path and target into the corridor.
/// @param[in] target The target location within the last polygon of the path. [(x, y, z)]
/// @param[in] path The path corridor. [(polyRef) * @p npolys]
/// @param[in] npath The number of polygons in the path.
void setCorridor(const float* target, const dtPolyRef* polys, const int npath);
/// Gets the current position within the corridor. (In the first polygon.)
/// @return The current position within the corridor.
inline const float* getPos() const { return m_pos; } inline const float* getPos() const { return m_pos; }
/// Gets the current target within the corridor. (In the last polygon.)
/// @return The current target within the corridor.
inline const float* getTarget() const { return m_target; } inline const float* getTarget() const { return m_target; }
/// The polygon reference id of the first polygon in the corridor, the polygon containing the position.
/// @return The polygon reference id of the first polygon in the corridor. (Or zero if there is no path.)
inline dtPolyRef getFirstPoly() const { return m_npath ? m_path[0] : 0; } inline dtPolyRef getFirstPoly() const { return m_npath ? m_path[0] : 0; }
/// The polygon reference id of the last polygon in the corridor, the polygon containing the target.
/// @return The polygon reference id of the last polygon in the corridor. (Or zero if there is no path.)
inline dtPolyRef getLastPoly() const { return m_npath ? m_path[m_npath-1] : 0; } inline dtPolyRef getLastPoly() const { return m_npath ? m_path[m_npath-1] : 0; }
/// The corridor's path.
/// @return The corridor's path. [(polyRef) * #getPathCount()]
inline const dtPolyRef* getPath() const { return m_path; } inline const dtPolyRef* getPath() const { return m_path; }
/// The number of polygons in the current corridor path.
/// @return The number of polygons in the current corridor path.
inline int getPathCount() const { return m_npath; } inline int getPathCount() const { return m_npath; }
}; };

View File

@ -155,6 +155,47 @@ int dtMergeCorridorStartShortcut(dtPolyRef* path, const int npath, const int max
return req+size; return req+size;
} }
/**
@class dtPathCorridor
@par
The corridor is loaded with a path, usually obtained from a #dtNavMeshQuery::findPath() query. The corridor
is then used to plan local movement, with the corridor automatically updating as needed to deal with inaccurate
agent locomotion.
Example of a common use case:
-# Construct the corridor object and call #init() to allocate its path buffer.
-# Obtain a path from a #dtNavMeshQuery object.
-# Use #reset() to set the agent's current position. (At the beginning of the path.)
-# Use #setCorridor() to load the path and target.
-# Use #findCorners() to plan movement. (This handles dynamic path straightening.)
-# Use #movePosition() to feed agent movement back into the corridor. (The corridor will automatically adjust as needed.)
-# If the target is moving, use #moveTargetPosition() to update the end of the corridor.
(The corridor will automatically adjust as needed.)
-# Repeat the previous 3 steps to continue to move the agent.
The corridor position and target are always constrained to the navigation mesh.
One of the difficulties in maintaining a path is that floating point errors, locomotion inaccuracies, and/or local
steering can result in the agent crossing the boundary of the path corridor, temporarily invalidating the path.
This class uses local mesh queries to detect and update the corridor as needed to handle these types of issues.
The fact that local mesh queries are used to move the position and target locations results in two beahviors that
need to be considered:
Every time a move function is used there is a chance that the path will become non-optimial. Basically, the further
the target is moved from its original location, and the further the position is moved outside the original corridor,
the more likely the path will become non-optimal. This issue can be addressed by periodically running the
#optimizePathTopology() and #optimizePathVisibility() methods.
All local mesh queries have distance limitations. (Review the #dtNavMeshQuery methods for details.) So the most accurate
use case is to move the position and target in small increments. If a large increment is used, then the corridor
may not be able to accurately find the new location. Because of this limiation, if a position is moved in a large
increment, then compare the desired and resulting polygon references. If the two do not match, then path replanning
may be needed. E.g. If you move the target, check #getLastPoly() to see if it is the expected polygon.
*/
dtPathCorridor::dtPathCorridor() : dtPathCorridor::dtPathCorridor() :
m_path(0), m_path(0),
@ -168,6 +209,9 @@ dtPathCorridor::~dtPathCorridor()
dtFree(m_path); dtFree(m_path);
} }
/// @par
///
/// @warning Cannot be called more than once.
bool dtPathCorridor::init(const int maxPath) bool dtPathCorridor::init(const int maxPath)
{ {
dtAssert(!m_path); dtAssert(!m_path);
@ -179,6 +223,10 @@ bool dtPathCorridor::init(const int maxPath)
return true; return true;
} }
/// @par
///
/// Essentially, the corridor is set of one polygon in size with the target
/// equal to the position.
void dtPathCorridor::reset(dtPolyRef ref, const float* pos) void dtPathCorridor::reset(dtPolyRef ref, const float* pos)
{ {
dtAssert(m_path); dtAssert(m_path);
@ -188,6 +236,18 @@ void dtPathCorridor::reset(dtPolyRef ref, const float* pos)
m_npath = 1; m_npath = 1;
} }
/**
@par
This is the function used to plan local movement within the corridor. One or more corners can be
detected in order to plan movement. It performs essentially the same function as #dtNavMeshQuery::findStraightPath.
Due to internal optimizations, the maximum number of corners returned will be (@p maxCorners - 1)
For example: If the buffers are sized to hold 10 corners, the function will never return more than 9 corners.
So if 10 corners are needed, the buffers should be sized for 11 corners.
If the target is within range, it will be the last corner and have a polygon reference id of zero.
*/
int dtPathCorridor::findCorners(float* cornerVerts, unsigned char* cornerFlags, int dtPathCorridor::findCorners(float* cornerVerts, unsigned char* cornerFlags,
dtPolyRef* cornerPolys, const int maxCorners, dtPolyRef* cornerPolys, const int maxCorners,
dtNavMeshQuery* navquery, const dtQueryFilter* /*filter*/) dtNavMeshQuery* navquery, const dtQueryFilter* /*filter*/)
@ -229,6 +289,21 @@ int dtPathCorridor::findCorners(float* cornerVerts, unsigned char* cornerFlags,
return ncorners; return ncorners;
} }
/**
@par
Inaccurate locomotion or dynamic obstacle avoidance can force the argent position significantly outside the
original corridor. Over time this can result in the formation of a non-optimal corridor. This function uses an
efficient local visibility search to try to re-optimize the corridor between the current position and @p next.
The corridor will change only if @p next is visible from the current position and moving directly toward the point
is better than following the existing path.
The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency
of the call to match the needs to the agent.
This function is not suitable for long distance searches.
*/
void dtPathCorridor::optimizePathVisibility(const float* next, const float pathOptimizationRange, void dtPathCorridor::optimizePathVisibility(const float* next, const float pathOptimizationRange,
dtNavMeshQuery* navquery, const dtQueryFilter* filter) dtNavMeshQuery* navquery, const dtQueryFilter* filter)
{ {
@ -262,6 +337,16 @@ void dtPathCorridor::optimizePathVisibility(const float* next, const float pathO
} }
} }
/**
@par
Inaccurate locomotion or dynamic obstacle avoidance can force the agent position significantly outside the
original corridor. Over time this can result in the formation of a non-optimal corridor. This function will use a
local area path search to try to re-optimize the corridor.
The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency of
the call to match the needs to the agent.
*/
bool dtPathCorridor::optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter) bool dtPathCorridor::optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
{ {
dtAssert(navquery); dtAssert(navquery);
@ -333,6 +418,21 @@ bool dtPathCorridor::moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRe
return false; return false;
} }
/**
@par
Behavior:
- The movement is constrained to the surface of the navigation mesh.
- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid.
- The new position will be located in the adjusted corridor's first polygon.
The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near'
depends on local polygon density, query search extents, etc.
The resulting position will differ from the desired position if the desired position is not on the navigation mesh,
or it can't be reached using a local search.
*/
void dtPathCorridor::movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter) void dtPathCorridor::movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
{ {
dtAssert(m_path); dtAssert(m_path);
@ -354,6 +454,19 @@ void dtPathCorridor::movePosition(const float* npos, dtNavMeshQuery* navquery, c
dtVcopy(m_pos, result); dtVcopy(m_pos, result);
} }
/**
@par
Behavior:
- The movement is constrained to the surface of the navigation mesh.
- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid.
- The new target will be located in the adjusted corridor's last polygon.
The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search extents, etc.
The resulting target will differ from the desired target if the desired target is not on the navigation mesh, or it can't be reached using a local search.
*/
void dtPathCorridor::moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter) void dtPathCorridor::moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
{ {
dtAssert(m_path); dtAssert(m_path);
@ -377,6 +490,12 @@ void dtPathCorridor::moveTargetPosition(const float* npos, dtNavMeshQuery* navqu
dtVcopy(m_target, result); dtVcopy(m_target, result);
} }
/// @par
///
/// The current corridor position is expected to be within the first polygon in the path. The target
/// is expected to be in the last polygon.
///
/// @warning The size of the path must not exceed the size of corridor's path buffer set during #init().
void dtPathCorridor::setCorridor(const float* target, const dtPolyRef* path, const int npath) void dtPathCorridor::setCorridor(const float* target, const dtPolyRef* path, const int npath)
{ {
dtAssert(m_path); dtAssert(m_path);
@ -427,6 +546,10 @@ bool dtPathCorridor::trimInvalidPath(dtPolyRef safeRef, const float* safePos,
return true; return true;
} }
/// @par
///
/// The path can be invalidated if there are structural changes to the underlying navigation mesh, or the state of
/// a polygon within the path changes resulting in it being filtered out. (E.g. An exclusion or inclusion flag changes.)
bool dtPathCorridor::isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter) bool dtPathCorridor::isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
{ {
// Check that all polygons still pass query filter. // Check that all polygons still pass query filter.