This commit is contained in:
Mikko Mononen 2014-06-13 13:25:07 +03:00
commit 3cb87f2432
41 changed files with 6701 additions and 5246 deletions

8
.gitignore vendored
View File

@ -14,6 +14,9 @@ RecastDemo/Bin/RecastDemo
# Build directory
RecastDemo/Build
# Ignore some meshes based on name
RecastDemo/Bin/Meshes/_*
## Logs and databases #
*.log
*.sql
@ -27,9 +30,14 @@ RecastDemo/Build
.Trashes
ehthumbs.db
Thumbs.db
*.swp
*.swo
## xcode specific
*xcuserdata*
## SDL contrib
RecastDemo/Contrib/SDL/*
## Generated doc files
Docs/html

View File

@ -32,6 +32,11 @@ feature to find minor members.
/// @name General helper functions
/// @{
/// Used to ignore a function parameter. VS complains about unused parameters
/// and this silences the warning.
/// @param [in] _ Unused parameter
template<class T> void dtIgnoreUnused(const T&) { }
/// Swaps the values of the two parameters.
/// @param[in,out] a Value A
/// @param[in,out] b Value B

View File

@ -22,16 +22,39 @@
#include "DetourAlloc.h"
#include "DetourStatus.h"
// Undefine (or define in a build cofnig) the following line to use 64bit polyref.
// Generally not needed, useful for very large worlds.
// Note: tiles build using 32bit refs are not compatible with 64bit refs!
//#define DT_POLYREF64 1
#ifdef DT_POLYREF64
// TODO: figure out a multiplatform version of uint64_t
// - maybe: https://code.google.com/p/msinttypes/
// - or: http://www.azillionmonkeys.com/qed/pstdint.h
#include <stdint.h>
#endif
// Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef.
// It is also recommended that you change dtHashRef() to a proper 64-bit hash.
/// A handle to a polygon within a navigation mesh tile.
/// @ingroup detour
#ifdef DT_POLYREF64
static const unsigned int DT_SALT_BITS = 16;
static const unsigned int DT_TILE_BITS = 28;
static const unsigned int DT_POLY_BITS = 20;
typedef uint64_t dtPolyRef;
#else
typedef unsigned int dtPolyRef;
#endif
/// A handle to a tile within a navigation mesh.
/// @ingroup detour
#ifdef DT_POLYREF64
typedef uint64_t dtTileRef;
#else
typedef unsigned int dtTileRef;
#endif
/// The maximum number of vertices per navigation polygon.
/// @ingroup detour
@ -469,7 +492,11 @@ public:
/// @param[in] ip The index of the polygon within the tile.
inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
{
#ifdef DT_POLYREF64
return ((dtPolyRef)salt << (DT_POLY_BITS+DT_TILE_BITS)) | ((dtPolyRef)it << DT_POLY_BITS) | (dtPolyRef)ip;
#else
return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip;
#endif
}
/// Decodes a standard polygon reference.
@ -481,12 +508,21 @@ public:
/// @see #encodePolyId
inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
{
#ifdef DT_POLYREF64
const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
salt = (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
it = (unsigned int)((ref >> DT_POLY_BITS) & tileMask);
ip = (unsigned int)(ref & polyMask);
#else
const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
it = (unsigned int)((ref >> m_polyBits) & tileMask);
ip = (unsigned int)(ref & polyMask);
#endif
}
/// Extracts a tile's salt value from the specified polygon reference.
@ -495,8 +531,13 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
{
#ifdef DT_POLYREF64
const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
return (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
#else
const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
#endif
}
/// Extracts the tile's index from the specified polygon reference.
@ -505,8 +546,13 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdTile(dtPolyRef ref) const
{
#ifdef DT_POLYREF64
const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
return (unsigned int)((ref >> DT_POLY_BITS) & tileMask);
#else
const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
return (unsigned int)((ref >> m_polyBits) & tileMask);
#endif
}
/// Extracts the polygon's index (within its tile) from the specified polygon reference.
@ -515,8 +561,13 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
{
#ifdef DT_POLYREF64
const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
return (unsigned int)(ref & polyMask);
#else
const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
return (unsigned int)(ref & polyMask);
#endif
}
/// @}
@ -562,8 +613,7 @@ private:
dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
const float* extents, float* nearestPt) const;
/// Returns closest point on polygon.
void closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip,
const float* pos, float* closest) const;
void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice.
float m_orig[3]; ///< Origin of the tile (0,0)
@ -576,9 +626,11 @@ private:
dtMeshTile* m_nextFree; ///< Freelist of tiles.
dtMeshTile* m_tiles; ///< List of tiles.
#ifndef DT_POLYREF64
unsigned int m_saltBits; ///< Number of salt bits in the tile ID.
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
unsigned int m_polyBits; ///< Number of poly bits in the tile ID.
#endif
};
/// Allocates a navigation mesh object using the Detour allocator.

View File

@ -200,8 +200,8 @@ public:
/// Finalizes and returns the results of an incomplete sliced path query, returning the path to the furthest
/// polygon on the existing path that was visited during the search.
/// @param[out] existing An array of polygon references for the existing path.
/// @param[out] existingSize The number of polygon in the @p existing array.
/// @param[in] existing An array of polygon references for the existing path.
/// @param[in] existingSize The number of polygon in the @p existing array.
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
/// [(polyRef) * @p pathCount]
/// @param[out] pathCount The number of polygons returned in the @p path array.
@ -378,8 +378,9 @@ public:
/// @param[in] ref The reference id of the polygon.
/// @param[in] pos The position to check. [(x, y, z)]
/// @param[out] closest The closest point on the polygon. [(x, y, z)]
/// @param[out] posOverPoly True of the position is over the polygon.
/// @returns The status flags for the query.
dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const;
dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
/// Returns a point on the boundary closest to the source point if the source point is outside the
/// polygon's xz-bounds.
@ -428,12 +429,7 @@ private:
/// Queries polygons within a tile.
int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter,
dtPolyRef* polys, const int maxPolys) const;
/// Find nearest polygon within a tile.
dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents,
const dtQueryFilter* filter, float* nearestPt) const;
/// Returns closest point on polygon.
void closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest) const;
/// Returns portal points between two polygons.
dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
unsigned char& fromType, unsigned char& toType) const;

View File

@ -193,11 +193,13 @@ dtNavMesh::dtNavMesh() :
m_tileLutMask(0),
m_posLookup(0),
m_nextFree(0),
m_tiles(0),
m_saltBits(0),
m_tileBits(0),
m_polyBits(0)
m_tiles(0)
{
#ifndef DT_POLYREF64
m_saltBits = 0;
m_tileBits = 0;
m_polyBits = 0;
#endif
memset(&m_params, 0, sizeof(dtNavMeshParams));
m_orig[0] = 0;
m_orig[1] = 0;
@ -249,12 +251,15 @@ dtStatus dtNavMesh::init(const dtNavMeshParams* params)
}
// Init ID generator values.
#ifndef DT_POLYREF64
m_tileBits = dtIlog2(dtNextPow2((unsigned int)params->maxTiles));
m_polyBits = dtIlog2(dtNextPow2((unsigned int)params->maxPolys));
// Only allow 31 salt bits, since the salt mask is calculated using 32bit uint and it will overflow.
m_saltBits = dtMin((unsigned int)31, 32 - m_tileBits - m_polyBits);
if (m_saltBits < 10)
return DT_FAILURE | DT_INVALID_PARAM;
#endif
return DT_SUCCESS;
}
@ -612,10 +617,12 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
}
}
void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip,
const float* pos, float* closest) const
void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
{
const dtPoly* poly = &tile->polys[ip];
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
getTileAndPolyByRefUnsafe(ref, &tile, &poly);
// Off-mesh connections don't have detail polygons.
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
{
@ -625,11 +632,14 @@ void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip
const float d1 = dtVdist(pos, v1);
const float u = d0 / (d0+d1);
dtVlerp(closest, v0, v1, u);
if (posOverPoly)
*posOverPoly = false;
return;
}
const unsigned int ip = (unsigned int)(poly - tile->polys);
const dtPolyDetail* pd = &tile->detailMeshes[ip];
// Clamp point to be inside the polygon.
float verts[DT_VERTS_PER_POLYGON*3];
float edged[DT_VERTS_PER_POLYGON];
@ -655,6 +665,14 @@ void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip
const float* va = &verts[imin*3];
const float* vb = &verts[((imin+1)%nv)*3];
dtVlerp(closest, va, vb, edget[imin]);
if (posOverPoly)
*posOverPoly = false;
}
else
{
if (posOverPoly)
*posOverPoly = true;
}
// Find height at the location.
@ -697,12 +715,27 @@ dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
{
dtPolyRef ref = polys[i];
float closestPtPoly[3];
closestPointOnPolyInTile(tile, decodePolyIdPoly(ref), center, closestPtPoly);
float d = dtVdistSqr(center, closestPtPoly);
float diff[3];
bool posOverPoly = false;
float d = 0;
closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly);
// If a point is directly over a polygon and closer than
// climb height, favor that instead of straight line nearest point.
dtVsub(diff, center, closestPtPoly);
if (posOverPoly)
{
d = dtAbs(diff[1]) - tile->header->walkableClimb;
d = d > 0 ? d*d : 0;
}
else
{
d = dtVlenSqr(diff);
}
if (d < nearestDistanceSqr)
{
if (nearestPt)
dtVcopy(nearestPt, closestPtPoly);
dtVcopy(nearestPt, closestPtPoly);
nearestDistanceSqr = d;
nearest = ref;
}
@ -1209,7 +1242,11 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz
tile->offMeshCons = 0;
// Update salt, salt should never be zero.
#ifdef DT_POLYREF64
tile->salt = (tile->salt+1) & ((1<<DT_SALT_BITS)-1);
#else
tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1);
#endif
if (tile->salt == 0)
tile->salt++;

View File

@ -501,7 +501,7 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
///
/// See closestPointOnPolyBoundary() for a limited but faster option.
///
dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const
dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
{
dtAssert(m_nav);
const dtMeshTile* tile = 0;
@ -511,14 +511,6 @@ dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, flo
if (!tile)
return DT_FAILURE | DT_INVALID_PARAM;
closestPointOnPolyInTile(tile, poly, pos, closest);
return DT_SUCCESS;
}
void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly,
const float* pos, float* closest) const
{
// Off-mesh connections don't have detail polygons.
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
{
@ -528,7 +520,9 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo
const float d1 = dtVdist(pos, v1);
const float u = d0 / (d0+d1);
dtVlerp(closest, v0, v1, u);
return;
if (posOverPoly)
*posOverPoly = false;
return DT_SUCCESS;
}
const unsigned int ip = (unsigned int)(poly - tile->polys);
@ -559,6 +553,14 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo
const float* va = &verts[imin*3];
const float* vb = &verts[((imin+1)%nv)*3];
dtVlerp(closest, va, vb, edget[imin]);
if (posOverPoly)
*posOverPoly = false;
}
else
{
if (posOverPoly)
*posOverPoly = true;
}
// Find height at the location.
@ -580,30 +582,8 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo
break;
}
}
/* float closestDistSqr = FLT_MAX;
for (int j = 0; j < pd->triCount; ++j)
{
const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
const float* v[3];
for (int k = 0; k < 3; ++k)
{
if (t[k] < poly->vertCount)
v[k] = &tile->verts[poly->verts[t[k]]*3];
else
v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
}
float pt[3];
dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
float d = dtVdistSqr(pos, pt);
if (d < closestDistSqr)
{
dtVcopy(closest, pt);
closestDistSqr = d;
}
}*/
return DT_SUCCESS;
}
/// @par
@ -682,8 +662,8 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h
{
const float* v0 = &tile->verts[poly->verts[0]*3];
const float* v1 = &tile->verts[poly->verts[1]*3];
const float d0 = dtVdist(pos, v0);
const float d1 = dtVdist(pos, v1);
const float d0 = dtVdist2D(pos, v0);
const float d1 = dtVdist2D(pos, v1);
const float u = d0 / (d0+d1);
if (height)
*height = v0[1] + (v1[1] - v0[1]) * u;
@ -747,8 +727,27 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten
{
dtPolyRef ref = polys[i];
float closestPtPoly[3];
closestPointOnPoly(ref, center, closestPtPoly);
float d = dtVdistSqr(center, closestPtPoly);
float diff[3];
bool posOverPoly = false;
float d = 0;
closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly);
// If a point is directly over a polygon and closer than
// climb height, favor that instead of straight line nearest point.
dtVsub(diff, center, closestPtPoly);
if (posOverPoly)
{
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
m_nav->getTileAndPolyByRefUnsafe(polys[i], &tile, &poly);
d = dtAbs(diff[1]) - tile->header->walkableClimb;
d = d > 0 ? d*d : 0;
}
else
{
d = dtVlenSqr(diff);
}
if (d < nearestDistanceSqr)
{
if (nearestPt)
@ -764,42 +763,6 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten
return DT_SUCCESS;
}
dtPolyRef dtNavMeshQuery::findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents,
const dtQueryFilter* filter, float* nearestPt) const
{
dtAssert(m_nav);
float bmin[3], bmax[3];
dtVsub(bmin, center, extents);
dtVadd(bmax, center, extents);
// Get nearby polygons from proximity grid.
dtPolyRef polys[128];
int polyCount = queryPolygonsInTile(tile, bmin, bmax, filter, polys, 128);
// Find nearest polygon amongst the nearby polygons.
dtPolyRef nearest = 0;
float nearestDistanceSqr = FLT_MAX;
for (int i = 0; i < polyCount; ++i)
{
dtPolyRef ref = polys[i];
const dtPoly* poly = &tile->polys[m_nav->decodePolyIdPoly(ref)];
float closestPtPoly[3];
closestPointOnPolyInTile(tile, poly, center, closestPtPoly);
float d = dtVdistSqr(center, closestPtPoly);
if (d < nearestDistanceSqr)
{
if (nearestPt)
dtVcopy(nearestPt, closestPtPoly);
nearestDistanceSqr = d;
nearest = ref;
}
}
return nearest;
}
int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
const dtQueryFilter* filter,
dtPolyRef* polys, const int maxPolys) const

View File

@ -22,6 +22,19 @@
#include "DetourCommon.h"
#include <string.h>
#ifdef DT_POLYREF64
// From Thomas Wang, https://gist.github.com/badboy/6267743
inline unsigned int dtHashRef(dtPolyRef a)
{
a = (~a) + (a << 18); // a = (a << 18) - a - 1;
a = a ^ (a >> 31);
a = a * 21; // a = (a + (a << 2)) + (a << 4);
a = a ^ (a >> 11);
a = a + (a << 6);
a = a ^ (a >> 22);
return (unsigned int)a;
}
#else
inline unsigned int dtHashRef(dtPolyRef a)
{
a += ~(a<<15);
@ -32,6 +45,7 @@ inline unsigned int dtHashRef(dtPolyRef a)
a ^= (a>>16);
return (unsigned int)a;
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
dtNodePool::dtNodePool(int maxNodes, int hashSize) :

View File

@ -45,6 +45,12 @@ static const int DT_CROWDAGENT_MAX_CORNERS = 4;
/// 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
@ -87,6 +93,9 @@ struct dtCrowdAgentParams
/// [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;
};
@ -106,12 +115,15 @@ enum MoveRequestState
/// @ingroup crowd
struct dtCrowdAgent
{
/// 1 if the agent is active, or 0 if the agent is in an unused slot in the agent pool.
unsigned char active;
/// 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;
@ -161,7 +173,7 @@ struct dtCrowdAgent
struct dtCrowdAgentAnimation
{
unsigned char active;
bool active;
float initPos[3], startPos[3], endPos[3];
dtPolyRef polyRef;
float t, tmax;
@ -206,8 +218,9 @@ class dtCrowd
int m_maxPathResult;
float m_ext[3];
dtQueryFilter m_filter;
dtQueryFilter m_filters[DT_CROWD_MAX_QUERY_FILTER_TYPE];
float m_maxAgentRadius;
int m_velocitySampleCount;
@ -251,6 +264,11 @@ public:
/// @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;
@ -301,11 +319,11 @@ public:
/// Gets the filter used by the crowd.
/// @return The filter used by the crowd.
const dtQueryFilter* getFilter() const { return &m_filter; }
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.
dtQueryFilter* getEditableFilter() { return &m_filter; }
inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
/// Gets the search extents [(x, y, z)] used by the crowd for query operations.
/// @return The search extents used by the crowd. [(x, y, z)]

View File

@ -441,14 +441,14 @@ bool dtCrowd::init(const int maxAgents, const float maxAgentRadius, dtNavMesh* n
for (int i = 0; i < m_maxAgents; ++i)
{
new(&m_agents[i]) dtCrowdAgent();
m_agents[i].active = 0;
m_agents[i].active = false;
if (!m_agents[i].corridor.init(m_maxPathResult))
return false;
}
for (int i = 0; i < m_maxAgents; ++i)
{
m_agentAnims[i].active = 0;
m_agentAnims[i].active = false;
}
// The navquery is mostly used for local searches, no need for large node pool.
@ -484,6 +484,17 @@ int dtCrowd::getAgentCount() const
/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
const dtCrowdAgent* dtCrowd::getAgent(const int idx)
{
if (idx < 0 || idx >= m_maxAgents)
return 0;
return &m_agents[idx];
}
///
/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
dtCrowdAgent* dtCrowd::getEditableAgent(const int idx)
{
if (idx < 0 || idx >= m_maxAgents)
return 0;
return &m_agents[idx];
}
@ -512,13 +523,15 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
if (idx == -1)
return -1;
dtCrowdAgent* ag = &m_agents[idx];
dtCrowdAgent* ag = &m_agents[idx];
updateAgentParameters(idx, params);
// Find nearest position on navmesh and place the agent there.
float nearest[3];
dtPolyRef ref = 0;
dtVcopy(nearest, pos);
dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filter, &ref, nearest);
dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest);
if (dtStatusFailed(status))
{
dtVcopy(nearest, pos);
@ -527,9 +540,8 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
ag->corridor.reset(ref, nearest);
ag->boundary.reset();
ag->partial = false;
updateAgentParameters(idx, params);
ag->topologyOptTime = 0;
ag->targetReplanTime = 0;
ag->nneis = 0;
@ -548,7 +560,7 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
ag->targetState = DT_CROWDAGENT_TARGET_NONE;
ag->active = 1;
ag->active = true;
return idx;
}
@ -561,7 +573,7 @@ void dtCrowd::removeAgent(const int idx)
{
if (idx >= 0 && idx < m_maxAgents)
{
m_agents[idx].active = 0;
m_agents[idx].active = false;
}
}
@ -691,7 +703,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
// Quick seach towards the goal.
static const int MAX_ITER = 20;
m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filter);
m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->params.queryFilterType]);
m_navquery->updateSlicedFindPath(MAX_ITER, 0);
dtStatus status = 0;
if (ag->targetReplan) // && npath > 10)
@ -711,7 +723,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
if (reqPath[reqPathCount-1] != ag->targetRef)
{
// Partial path, constrain target position inside the last polygon.
status = m_navquery->closestPointOnPoly(reqPath[reqPathCount-1], ag->targetPos, reqPos);
status = m_navquery->closestPointOnPoly(reqPath[reqPathCount-1], ag->targetPos, reqPos, 0);
if (dtStatusFailed(status))
reqPathCount = 0;
}
@ -735,6 +747,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
ag->corridor.setCorridor(reqPos, reqPath, reqPathCount);
ag->boundary.reset();
ag->partial = false;
if (reqPath[reqPathCount-1] == ag->targetRef)
{
@ -758,7 +771,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
{
dtCrowdAgent* ag = queue[i];
ag->targetPathqRef = m_pathq.request(ag->corridor.getLastPoly(), ag->targetRef,
ag->corridor.getTarget(), ag->targetPos, &m_filter);
ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->params.queryFilterType]);
if (ag->targetPathqRef != DT_PATHQ_INVALID)
ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_PATH;
}
@ -808,7 +821,12 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
status = m_pathq.getPathResult(ag->targetPathqRef, res, &nres, m_maxPathResult);
if (dtStatusFailed(status) || !nres)
valid = false;
if (dtStatusDetail(status, DT_PARTIAL_RESULT))
ag->partial = true;
else
ag->partial = false;
// Merge result and existing path.
// The agent might have moved whilst the request is
// being processed, so the path may have changed.
@ -855,7 +873,7 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
{
// Partial path, constrain target position inside the last polygon.
float nearest[3];
status = m_navquery->closestPointOnPoly(res[nres-1], targetPos, nearest);
status = m_navquery->closestPointOnPoly(res[nres-1], targetPos, nearest, 0);
if (dtStatusSucceed(status))
dtVcopy(targetPos, nearest);
else
@ -912,7 +930,7 @@ void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagent
for (int i = 0; i < nqueue; ++i)
{
dtCrowdAgent* ag = queue[i];
ag->corridor.optimizePathTopology(m_navquery, &m_filter);
ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->params.queryFilterType]);
ag->topologyOptTime = 0;
}
@ -929,9 +947,6 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
continue;
if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
continue;
ag->targetReplanTime += dt;
@ -942,20 +957,21 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
float agentPos[3];
dtPolyRef agentRef = ag->corridor.getFirstPoly();
dtVcopy(agentPos, ag->npos);
if (!m_navquery->isValidPolyRef(agentRef, &m_filter))
if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->params.queryFilterType]))
{
// Current location is not valid, try to reposition.
// TODO: this can snap agents, how to handle that?
float nearest[3];
dtVcopy(nearest, agentPos);
agentRef = 0;
dtStatus status = m_navquery->findNearestPoly(ag->npos, m_ext, &m_filter, &agentRef, nearest);
m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
dtVcopy(agentPos, nearest);
if (!agentRef)
{
// Could not find location in navmesh, set state to invalid.
ag->corridor.reset(0, agentPos);
ag->partial = false;
ag->boundary.reset();
ag->state = DT_CROWDAGENT_STATE_INVALID;
continue;
@ -971,16 +987,20 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
replan = true;
}
// If the agent does not have move target or is controlled by velocity, no need to recover the target nor replan.
if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
continue;
// Try to recover move request position.
if (ag->targetState != DT_CROWDAGENT_TARGET_NONE && ag->targetState != DT_CROWDAGENT_TARGET_FAILED)
{
if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filter))
if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->params.queryFilterType]))
{
// Current target is not valid, try to reposition.
float nearest[3];
dtVcopy(nearest, ag->targetPos);
ag->targetRef = 0;
m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filter, &ag->targetRef, nearest);
m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
dtVcopy(ag->targetPos, nearest);
replan = true;
}
@ -988,12 +1008,13 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
{
// Failed to reposition target, fail moverequest.
ag->corridor.reset(agentRef, agentPos);
ag->partial = false;
ag->targetState = DT_CROWDAGENT_TARGET_NONE;
}
}
// If nearby corridor is not valid, replan.
if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filter))
if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->params.queryFilterType]))
{
// Fix current path.
// ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
@ -1029,7 +1050,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
dtCrowdAgent** agents = m_activeAgents;
int nagents = getActiveAgents(agents, m_maxAgents);
// Check that all agents still have valid paths.
checkPathValidity(agents, nagents, dt);
@ -1060,10 +1081,10 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// if it has become invalid.
const float updateThr = ag->params.collisionQueryRange*0.25f;
if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) ||
!ag->boundary.isValid(m_navquery, &m_filter))
!ag->boundary.isValid(m_navquery, &m_filters[ag->params.queryFilterType]))
{
ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange,
m_navquery, &m_filter);
m_navquery, &m_filters[ag->params.queryFilterType]);
}
// Query neighbour agents
ag->nneis = getNeighbours(ag->npos, ag->params.height, ag->params.collisionQueryRange,
@ -1085,14 +1106,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
// Find corners for steering
ag->ncorners = ag->corridor.findCorners(ag->cornerVerts, ag->cornerFlags, ag->cornerPolys,
DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filter);
DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->params.queryFilterType]);
// Check to see if the corner after the next corner is directly visible,
// and short cut to there.
if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_VIS) && ag->ncorners > 0)
{
const float* target = &ag->cornerVerts[dtMin(1,ag->ncorners-1)*3];
ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filter);
ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->params.queryFilterType]);
// Copy data for debug purposes.
if (debugIdx == i)
@ -1137,7 +1158,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
{
dtVcopy(anim->initPos, ag->npos);
anim->polyRef = refs[1];
anim->active = 1;
anim->active = true;
anim->t = 0.0f;
anim->tmax = (dtVdist2D(anim->startPos, anim->endPos) / ag->params.maxSpeed) * 0.5f;
@ -1372,7 +1393,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
continue;
// Move along navmesh.
ag->corridor.movePosition(ag->npos, m_navquery, &m_filter);
ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->params.queryFilterType]);
// Get valid constrained position back.
dtVcopy(ag->npos, ag->corridor.getPos());
@ -1380,6 +1401,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
{
ag->corridor.reset(ag->corridor.getFirstPoly(), ag->npos);
ag->partial = false;
}
}
@ -1396,7 +1418,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
if (anim->t > anim->tmax)
{
// Reset animation
anim->active = 0;
anim->active = false;
// Prepare agent for walking.
ag->state = DT_CROWDAGENT_STATE_WALKING;
continue;
@ -1422,5 +1444,3 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
}
}

View File

@ -185,6 +185,7 @@ dtStatus dtPathQueue::getPathResult(dtPathQueueRef ref, dtPolyRef* path, int* pa
if (m_queue[i].ref == ref)
{
PathQuery& q = m_queue[i];
dtStatus details = q.status & DT_STATUS_DETAIL_MASK;
// Free request for reuse.
q.ref = DT_PATHQ_INVALID;
q.status = 0;
@ -192,7 +193,7 @@ dtStatus dtPathQueue::getPathResult(dtPathQueueRef ref, dtPolyRef* path, int* pa
int n = dtMin(q.npath, maxPath);
memcpy(path, q.path, sizeof(dtPolyRef)*n);
*pathSize = n;
return DT_SUCCESS;
return details | DT_SUCCESS;
}
}
return DT_FAILURE;

View File

@ -2116,6 +2116,7 @@ dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompress
bool dtTileCacheHeaderSwapEndian(unsigned char* data, const int dataSize)
{
dtIgnoreUnused(dataSize);
dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)data;
int swappedMagic = DT_TILECACHE_MAGIC;

View File

@ -1,34 +1,27 @@
/// @mainpage Recast Navigation
///
/// @note A documentation effort is currently underway.
/// This documentation is in a draft state until the effort is complete.
/// @image html recast_intro.png
///
/// <h2>Recast</h2>
///
/// @section intro_recast Recast
///
/// Recast is state of the art navigation mesh construction toolset for
/// _Recast_ is state of the art navigation mesh construction toolset for
/// games.
///
/// - It is <b>automatic</b>, which means that you can throw any level
/// geometry at it and you will get robust mesh out.
/// - It is <b>fast</b> which means swift turnaround times for level
/// designers.
/// - It is <b>open source</b> so it comes with full source and you can
/// - It is automatic, which means that you can throw any level
/// geometry at it and you will get a robust mesh out.
/// - It is fast which means swift turnaround times for level designers.
/// - It is open source so it comes with full source and you can
/// customize it to your hearts content.
///
/// The Library is free for commercial use and open source under the
/// ZLib License.
///
/// <b>Please use the latest version from the
/// <a href="http://code.google.com/p/recastnavigation/">SVN</a>.</b>
///
/// @image html recast_intro.png
/// The latest version can be found on
/// <a href="https://github.com/memononen/recastnavigation">GitHub</a>.
///
/// The Recast process starts with constructing a voxel mold from a level
/// The _Recast_ process starts with constructing a voxel mold from level
/// geometry and then casting a navigation mesh over it. The process
/// consists of three steps, building the voxel mold, partitioning the
/// mold into simple regions, peeling off the regions as simple polygons.
/// consists of three steps, building the voxel mold, partitioning the
/// mold into simple regions, and peeling off the regions as simple polygons.
///
/// -# The voxel mold is build from the input triangle mesh by
/// -# The voxel mold is built from the input triangle mesh by
/// rasterizing the triangles into a multi-layer heightfield. Some
/// simple filters are then applied to the mold to prune out locations
/// where the character would not be able to move.
@ -41,20 +34,62 @@
/// polygons are finally converted to convex polygons which makes them
/// perfect for pathfinding and spatial reasoning about the level.
///
/// @section intro_detour Detour
/// <h2>Detour</h2>
///
/// Recast is accompanied by Detour, a path-finding and spatial reasoning
/// toolkit. You can use any navigation mesh with Detour, but of course
/// the data generated by Recast fits perfectly.
/// _Recast_ is accompanied by _Detour_, a path-finding and spatial reasoning
/// toolkit. You can use any navigation mesh with _Detour_, but of course
/// the data generated by _Recast_ fits perfectly.
///
/// Detour offers a simple static navigation mesh that is suitable for
/// _Detour_ offers a simple static navigation mesh that is suitable for
/// many simple cases, as well as a tiled navigation mesh that allows you
/// to add and remove pieces of the mesh. The tiled mesh allows you to
/// create systems where you stream new navigation data in and out as
/// the player progresses the level, or regenerate tiles as the
/// world changes.
///
/// You can discuss and give feedback for Recast and Detour in the
/// <h2>Recast Demo</h2>
///
/// You can find a comprehensive demo project in the `RecastDemo` folder. It
/// is a kitchen sink demo containing all the functionality of the library.
/// If you are new to _Recast_ & _Detour_, check out
/// <a href="https://github.com/memononen/recastnavigation/blob/master/RecastDemo/Source/Sample_SoloMesh.cpp">
/// Sample_SoloMesh.cpp</a> to get started with building navmeshes and
/// <a href="https://github.com/memononen/recastnavigation/blob/master/RecastDemo/Source/NavMeshTesterTool.cpp">
/// NavMeshTesterTool.cpp</a> to see how _Detour_ can be used to find paths.
///
/// <h3>Building RecastDemo</h3>
///
/// _RecastDemo_ uses <a href="http://industriousone.com/premake">premake4</a>
/// to build platform specific projects, now is good time to install it if
/// you don't have it already. To build _RecastDemo_, in your favourite terminal
/// navigate into the `RecastDemo` folder, then:
///
/// - OS X: `premake4 xcode4`
/// - Windows: `premake4 vs2010`
/// - Linux: `premake4 gmake`
///
/// See the _premake4_ documentation for full list of supported build file types.
/// The projects will be created in the `RecastDemo/Build` folder. After you have
/// compiled the project, the _RecastDemo_ executable will be located in
/// `RecastDemo/Bin` folder.
///
/// <h2>Integrating With Your Own Project</h2>
///
/// It is recommended to add the source directories `DebugUtils`, `Detour`,
/// `DetourCrowd`, `DetourTileCache`, and `Recast` into your own project
/// depending on which parts of the project you need. For example your
/// level building tool could include `DebugUtils`, `Recast`, and `Detour`,
/// and your game runtime could just include `Detour`.
///
/// <h2>Discuss</h2>
///
/// - Discuss _Recast_ and _Detour_:
/// <a href="http://groups.google.com/group/recastnavigation">
/// discussion group</a>.
/// Recast Navigation Group</a>
/// - Development Blog:
/// <a href="http://digestingduck.blogspot.com/">Digesting Duck</a>
///
/// <h2>License</h2>
///
/// _Recast Navigation_ is licensed under the ZLib license.
///

View File

@ -1,24 +1,24 @@
<doxygenlayout version="1.0">
<!-- Generated by doxygen 1.8.6 -->
<!-- Navigation index tabs for HTML output -->
<navindex>
<tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes" title="" intro=""/>
<tab type="namespaces" visible="no" title="">
<tab type="namespaces" visible="yes" title="" intro=""/>
<tab type="namespacelist" visible="yes" title="" intro=""/>
<tab type="namespacemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="classes" visible="yes" title="">
<tab type="classes" visible="no" title="" intro=""/>
<tab type="classindex" visible="no" title=""/>
<tab type="hierarchy" visible="no" title="" intro=""/>
<tab type="classmembers" visible="no" title="" intro=""/>
<tab type="classlist" visible="yes" title="" intro=""/>
<tab type="classindex" visible="yes" title=""/>
<tab type="hierarchy" visible="yes" title="" intro=""/>
<tab type="classmembers" visible="yes" title="" intro=""/>
</tab>
<tab type="files" visible="no" title="">
<tab type="files" visible="no" title="" intro=""/>
<tab type="globals" visible="no" title="" intro=""/>
<tab type="files" visible="yes" title="">
<tab type="filelist" visible="yes" title="" intro=""/>
<tab type="globals" visible="yes" title="" intro=""/>
</tab>
<tab type="dirs" visible="no" title="" intro=""/>
<tab type="examples" visible="yes" title="" intro=""/>
</navindex>
@ -28,10 +28,11 @@
<includes visible="$SHOW_INCLUDE_FILES"/>
<inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
<allmemberslink visible="yes"/>
<memberdecl>
<nestedclasses visible="no" title=""/> <!-- If set to 'yes', the private nested classes will be visible. -->
<nestedclasses visible="yes" title=""/>
<publictypes title=""/>
<services title=""/>
<interfaces title=""/>
<publicslots title=""/>
<signals title=""/>
<publicmethods title=""/>
@ -61,10 +62,13 @@
<related title="" subtitle=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<detaileddescription title="Description"/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<services title=""/>
<interfaces title=""/>
<constructors title=""/>
<functions title=""/>
<related title=""/>
@ -72,6 +76,7 @@
<properties title=""/>
<events title=""/>
</memberdef>
<allmemberslink visible="yes"/>
<usedfiles visible="$SHOW_USED_FILES"/>
<authorsection visible="yes"/>
</class>
@ -81,6 +86,7 @@
<briefdescription visible="yes"/>
<memberdecl>
<nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<classes visible="yes" title=""/>
<typedefs title=""/>
<enums title=""/>
@ -88,8 +94,9 @@
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<detaileddescription title="Description"/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<functions title=""/>
@ -100,7 +107,8 @@
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="yes"/>
<briefdescription visible="Description"/>
<detaileddescription title=""/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
@ -108,6 +116,7 @@
<memberdecl>
<classes visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
@ -115,8 +124,8 @@
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
@ -128,15 +137,14 @@
<!-- Layout definition for a group page -->
<group>
<briefdescription visible="no"/>
<detaileddescription title="Description"/>
<briefdescription visible="yes"/>
<groupgraph visible="$GROUP_GRAPHS"/>
<memberdecl>
<classes visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<dirs visible="yes" title=""/>
<nestedgroups visible="yes" title=""/>
<dirs visible="yes" title=""/>
<files visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<enums title=""/>
@ -152,6 +160,7 @@
<friends title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title="Description"/>
<memberdef>
<pagedocs/>
<inlineclasses title=""/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 370 KiB

View File

@ -23,6 +23,10 @@ Directory Layout
Images related to the documentation.
./html
The target for the Doxygen build. (Created during the build process.)
Miscellany
One of the requirements for the API documentation is that it
@ -38,5 +42,23 @@ is placed as follows:
3. If there is a lot of detail documentation cluttering up
the end of a header file, then the content is moved to
a separate file in the Extern directory.
Building the Documentation
1. Download and install the appropriate Doxygen version. (See the first
line in the Doxyfile for the current version.)
2. Run "doxygen" in the project root directory. (The location of the Doxyfile.)
No arguments are required.
The generated html files will be located in the /Docs/html directory.
If you want to "version" the documentation, you can set the PROJECT_NUMBER
setting in the Doxyfile. E.g. PROJECT_NUMBER = "(2014-04-23)". The project
number will be added to the header of the documentation.
E.g. "Recast Navigation (2014-04-23)"

View File

@ -1,847 +0,0 @@
/* The standard CSS for doxygen */
body, table, div, p, dl {
font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
font-size: 12px;
}
/* @group Heading Levels */
h1 {
font-size: 150%;
}
.title {
font-size: 150%;
font-weight: bold;
margin: 10px 2px;
}
h2 {
font-size: 120%;
}
div.textblock h2 {
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: #336699;
}
h3 {
font-size: 100%;
}
dt {
font-weight: bold;
}
div.multicol {
-moz-column-gap: 1em;
-webkit-column-gap: 1em;
-moz-column-count: 3;
-webkit-column-count: 3;
}
p.startli, p.startdd, p.starttd {
margin-top: 2px;
}
p.endli {
margin-bottom: 0px;
}
p.enddd {
margin-bottom: 4px;
}
p.endtd {
margin-bottom: 2px;
}
/* @end */
caption {
font-weight: bold;
}
span.legend {
font-size: 70%;
text-align: center;
}
h3.version {
font-size: 90%;
text-align: center;
}
div.qindex, div.navtab{
background-color: #EBEFF6;
border: 1px solid #A3B4D7;
text-align: center;
margin: 2px;
padding: 2px;
}
div.qindex, div.navpath {
width: 100%;
line-height: 140%;
}
div.navtab {
margin-right: 15px;
}
/* @group Link Styling */
a {
color: #3D578C;
font-weight: normal;
text-decoration: none;
}
.contents a:visited {
color: #4665A2;
}
a:hover {
text-decoration: underline;
}
a.qindex {
font-weight: bold;
}
a.qindexHL {
font-weight: bold;
background-color: #9CAFD4;
color: #ffffff;
border: 1px double #869DCA;
}
.contents a.qindexHL:visited {
color: #ffffff;
}
a.el {
font-weight: bold;
}
a.elRef {
}
a.code {
color: #4665A2;
}
a.codeRef {
color: #4665A2;
}
/* @end */
dl.el {
margin-left: -1cm;
}
.fragment {
font-family: monospace, fixed;
font-size: 105%;
}
pre.fragment {
border: 1px solid #C4CFE5;
background-color: #FBFCFD;
padding: 4px 6px;
margin: 4px 8px 4px 2px;
overflow: auto;
word-wrap: break-word;
font-size: 9pt;
line-height: 125%;
}
div.ah {
background-color: black;
font-weight: bold;
color: #ffffff;
margin-bottom: 3px;
margin-top: 3px;
padding: 0.2em;
border: solid thin #333;
border-radius: 0.5em;
-webkit-border-radius: .5em;
-moz-border-radius: .5em;
box-shadow: 2px 2px 3px #999;
-webkit-box-shadow: 2px 2px 3px #999;
-moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
font-weight: bold;
padding-bottom: 8px;
}
div.groupText {
margin-left: 16px;
font-style: italic;
}
body {
background: white;
color: black;
margin: 0;
}
div.contents {
margin-top: 10px;
margin-left: 10px;
margin-right: 5px;
}
td.indexkey {
background-color: #EBEFF6;
font-weight: bold;
border: 1px solid #C4CFE5;
margin: 2px 0px 2px 0;
padding: 2px 10px;
}
td.indexvalue {
background-color: #EBEFF6;
border: 1px solid #C4CFE5;
padding: 2px 10px;
margin: 2px 0px;
}
tr.memlist {
background-color: #EEF1F7;
}
p.formulaDsp {
text-align: center;
}
img.formulaDsp {
}
img.formulaInl {
vertical-align: middle;
}
div.center {
text-align: center;
margin-top: 0px;
margin-bottom: 0px;
padding: 0px;
}
div.center img {
border: 0px;
}
address.footer {
text-align: right;
padding-right: 12px;
}
img.footer {
border: 0px;
vertical-align: middle;
}
/* @group Code Colorization */
span.keyword {
color: #008000
}
span.keywordtype {
color: #604020
}
span.keywordflow {
color: #e08000
}
span.comment {
color: #800000
}
span.preprocessor {
color: #806020
}
span.stringliteral {
color: #002080
}
span.charliteral {
color: #008080
}
span.vhdldigit {
color: #ff00ff
}
span.vhdlchar {
color: #000000
}
span.vhdlkeyword {
color: #700070
}
span.vhdllogic {
color: #ff0000
}
/* @end */
/*
.search {
color: #003399;
font-weight: bold;
}
form.search {
margin-bottom: 0px;
margin-top: 0px;
}
input.search {
font-size: 75%;
color: #000080;
font-weight: normal;
background-color: #e8eef2;
}
*/
td.tiny {
font-size: 75%;
}
.dirtab {
padding: 4px;
border-collapse: collapse;
border: 1px solid #A3B4D7;
}
th.dirtab {
background: #EBEFF6;
font-weight: bold;
}
hr {
height: 0px;
border: none;
border-top: 1px solid #4A6AAA;
}
hr.footer {
height: 1px;
}
/* @group Member Descriptions */
table.memberdecls {
border-spacing: 0px;
padding: 0px;
}
.mdescLeft, .mdescRight,
.memItemLeft, .memItemRight,
.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
background-color: #F9FAFC;
border: none;
margin: 4px;
padding: 8px 0 8px 8px;
}
.mdescLeft {
padding: 0px 8px 4px 8px;
color: #555;
}
.mdescRight {
padding: 4px 8px 8px 24px;
color: #555;
}
.memItemLeft, .memItemRight, .memTemplParams {
border-top: 1px solid #C4CFE5;
}
.memItemLeft, .memTemplItemLeft {
white-space: nowrap;
}
.memItemRight {
width: 100%;
}
.memTemplParams {
color: #4665A2;
white-space: nowrap;
}
/* @end */
/* @group Member Details */
/* Styles for detailed member documentation */
.memtemplate {
font-size: 80%;
color: #4665A2;
font-weight: normal;
margin-left: 9px;
}
.memnav {
background-color: #EBEFF6;
border: 1px solid #A3B4D7;
text-align: center;
margin: 2px;
margin-right: 15px;
padding: 2px;
}
.mempage {
width: 100%;
}
.memitem {
padding: 0;
margin-bottom: 10px;
margin-right: 5px;
}
.memname {
white-space: nowrap;
font-weight: bold;
margin-left: 6px;
}
.memproto {
border-top: 1px solid #A8B8D9;
border-left: 1px solid #A8B8D9;
border-right: 1px solid #A8B8D9;
padding: 6px 0px 6px 0px;
color: #253555;
font-weight: bold;
text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
/* opera specific markup */
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
border-top-right-radius: 8px;
border-top-left-radius: 8px;
/* firefox specific markup */
-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
-moz-border-radius-topright: 8px;
-moz-border-radius-topleft: 8px;
/* webkit specific markup */
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
-webkit-border-top-right-radius: 8px;
-webkit-border-top-left-radius: 8px;
background-image:url('nav_f.png');
background-repeat:repeat-x;
background-color: #E2E8F2;
}
.memdoc {
border-bottom: 1px solid #A8B8D9;
border-left: 1px solid #A8B8D9;
border-right: 1px solid #A8B8D9;
padding: 2px 5px;
background-color: #FBFCFD;
border-top-width: 0;
/* opera specific markup */
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
/* firefox specific markup */
-moz-border-radius-bottomleft: 8px;
-moz-border-radius-bottomright: 8px;
-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7);
/* webkit specific markup */
-webkit-border-bottom-left-radius: 8px;
-webkit-border-bottom-right-radius: 8px;
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7));
}
.paramkey {
text-align: right;
}
.paramtype {
white-space: nowrap;
}
.paramname {
color: #602020;
white-space: nowrap;
}
.paramname em {
font-style: normal;
}
.params, .retval, .exception, .tparams {
border-spacing: 6px 2px;
}
.params .paramname, .retval .paramname {
font-weight: bold;
vertical-align: top;
}
.params .paramtype {
font-style: italic;
vertical-align: top;
}
.params .paramdir {
font-family: "courier new",courier,monospace;
vertical-align: top;
}
/* @end */
/* @group Directory (tree) */
/* for the tree view */
.ftvtree {
font-family: sans-serif;
margin: 0px;
}
/* these are for tree view when used as main index */
.directory {
font-size: 9pt;
font-weight: bold;
margin: 5px;
}
.directory h3 {
margin: 0px;
margin-top: 1em;
font-size: 11pt;
}
/*
The following two styles can be used to replace the root node title
with an image of your choice. Simply uncomment the next two styles,
specify the name of your image and be sure to set 'height' to the
proper pixel height of your image.
*/
/*
.directory h3.swap {
height: 61px;
background-repeat: no-repeat;
background-image: url("yourimage.gif");
}
.directory h3.swap span {
display: none;
}
*/
.directory > h3 {
margin-top: 0;
}
.directory p {
margin: 0px;
white-space: nowrap;
}
.directory div {
display: none;
margin: 0px;
}
.directory img {
vertical-align: -30%;
}
/* these are for tree view when not used as main index */
.directory-alt {
font-size: 100%;
font-weight: bold;
}
.directory-alt h3 {
margin: 0px;
margin-top: 1em;
font-size: 11pt;
}
.directory-alt > h3 {
margin-top: 0;
}
.directory-alt p {
margin: 0px;
white-space: nowrap;
}
.directory-alt div {
display: none;
margin: 0px;
}
.directory-alt img {
vertical-align: -30%;
}
/* @end */
div.dynheader {
margin-top: 8px;
}
address {
font-style: normal;
color: #2A3D61;
}
table.doxtable {
border-collapse:collapse;
}
table.doxtable td, table.doxtable th {
border: 1px solid #2D4068;
padding: 3px 7px 2px;
}
table.doxtable th {
background-color: #374F7F;
color: #FFFFFF;
font-size: 110%;
padding-bottom: 4px;
padding-top: 5px;
text-align:left;
}
.tabsearch {
top: 0px;
left: 10px;
height: 36px;
background-image: url('tab_b.png');
z-index: 101;
overflow: hidden;
font-size: 13px;
}
.navpath ul
{
font-size: 11px;
background-image:url('tab_b.png');
background-repeat:repeat-x;
height:30px;
line-height:30px;
color:#8AA0CC;
border:solid 1px #C2CDE4;
overflow:hidden;
margin:0px;
padding:0px;
}
.navpath li
{
list-style-type:none;
float:left;
padding-left:10px;
padding-right:15px;
background-image:url('bc_s.png');
background-repeat:no-repeat;
background-position:right;
color:#364D7C;
}
.navpath li.navelem a
{
height:32px;
display:block;
text-decoration: none;
outline: none;
}
.navpath li.navelem a:hover
{
color:#6884BD;
}
.navpath li.footer
{
list-style-type:none;
float:right;
padding-left:10px;
padding-right:15px;
background-image:none;
background-repeat:no-repeat;
background-position:right;
color:#364D7C;
font-size: 8pt;
}
div.summary
{
float: right;
font-size: 8pt;
padding-right: 5px;
width: 50%;
text-align: right;
}
div.summary a
{
white-space: nowrap;
}
div.ingroups
{
font-size: 8pt;
padding-left: 5px;
width: 50%;
text-align: left;
}
div.ingroups a
{
white-space: nowrap;
}
div.header
{
background-image:url('nav_h.png');
background-repeat:repeat-x;
background-color: #F9FAFC;
margin: 0px;
border-bottom: 1px solid #C4CFE5;
}
div.headertitle
{
padding: 5px 5px 5px 10px;
}
dl
{
padding: 0 0 0 10px;
}
dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug
{
border-left:4px solid;
padding: 0 0 0 6px;
}
dl.note
{
border-color: #D0C000;
}
dl.warning, dl.attention
{
border-color: #FF0000;
}
dl.pre, dl.post, dl.invariant
{
border-color: #00D000;
}
dl.deprecated
{
border-color: #505050;
}
dl.todo
{
border-color: #00C0E0;
}
dl.test
{
border-color: #3030E0;
}
dl.bug
{
border-color: #C08050;
}
#projectlogo
{
text-align: center;
vertical-align: bottom;
border-collapse: separate;
}
#projectlogo img
{
border: 0px none;
}
#projectname
{
font: 300% Tahoma, Arial,sans-serif;
margin: 0px;
padding: 2px 0px;
}
#projectbrief
{
font: 120% Tahoma, Arial,sans-serif;
margin: 0px;
padding: 0px;
}
#projectnumber
{
font: 50% Tahoma, Arial,sans-serif;
margin: 0px;
padding: 0px;
}
#titlearea
{
padding: 0px;
margin: 0px;
width: 100%;
border-bottom: 1px solid #5373B4;
}
.image
{
text-align: left
}
.dotgraph
{
text-align: center;
}
.mscgraph
{
text-align: center;
}
.caption
{
font-weight: bold;
}

View File

@ -1,17 +1,20 @@
<!-- HTML footer for doxygen 1.8.6-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<li class="footer">
<a href="http://code.google.com/p/recastnavigation/">Project Home</a>
| <a href="./License.html"> Licence</a>
| Copyright (c) 2009-2011 Mikko Mononen
</li>
</ul>
</div>
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
&nbsp;&nbsp;<a href="https://github.com/memononen/recastnavigation">Project Home</a>
| <a href="./License.html">Licence: ZLib</a>
| Copyright (c) 2009-2014 Mikko Mononen
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/><address class="footer"><small>
<a href="http://code.google.com/p/recastnavigation/">Project Home</a>
| <a href="./License.html">Licence</a>
| Copyright (c) 2009-2011 Mikko Mononen
&nbsp;&nbsp;<a href="https://github.com/memononen/recastnavigation">Project Home</a>
| <a href="./License.html">Licence: ZLib</a>
| Copyright (c) 2009-2014 Mikko Mononen
</small></address>
<!--END !GENERATE_TREEVIEW-->
</body>

View File

@ -1,23 +1,31 @@
<!-- HTML header for doxygen 1.8.6-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<title>$title</title>
<link href="$relpath$tabs.css" rel="stylesheet" type="text/css"/>
<link href="$relpath$customdoxygen.css" rel="stylesheet" type="text/css" />
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="top"><!-- do not remove this div! -->
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath$$projectlogo"></td>
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td style="padding-left: 0.5em;">
@ -44,3 +52,4 @@ $mathjax
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

2506
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ Recast is state of the art navigation mesh construction toolset for games.
* It is automatic, which means that you can throw any level geometry at it and you will get robust mesh out
* It is fast which means swift turnaround times for level designers
* It is open source so it comes with full source and you can customize it to your hearts content.
* It is open source so it comes with full source and you can customize it to your heart's content.
The Recast process starts with constructing a voxel mold from a level geometry
and then casting a navigation mesh over it. The process consists of three steps,
@ -28,7 +28,7 @@ the regions as simple polygons.
Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes.
Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows you to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes.
## Recast Demo
@ -54,9 +54,9 @@ It is recommended to add the source directories `DebugUtils`, `Detour`, `DetourC
## Discuss
- Discuss Recast & Detour: http://groups.google.com/group/recastnavigation
- Develoment blog: http://digestingduck.blogspot.com/
- Development blog: http://digestingduck.blogspot.com/
## License
Recast & Detour is licensed under ZLib license, see License.txt for more information.
Recast & Detour is licensed under ZLib license, see License.txt for more information.

View File

@ -219,7 +219,7 @@ struct rcConfig
int maxEdgeLen;
/// The maximum distance a simplfied contour's border edges should deviate
/// the original raw contour. [Limit: >=0] [Units: wu]
/// the original raw contour. [Limit: >=0] [Units: vx]
float maxSimplificationError;
/// The minimum number of cells allowed to form isolated island areas. [Limit: >=0] [Units: vx]
@ -549,6 +549,11 @@ static const int RC_NOT_CONNECTED = 0x3f;
/// @name General helper functions
/// @{
/// Used to ignore a function parameter. VS complains about unused parameters
/// and this silences the warning.
/// @param [in] _ Unused parameter
template<class T> void rcIgnoreUnused(const T&) { }
/// Swaps the values of the two parameters.
/// @param[in,out] a Value A
/// @param[in,out] b Value B

View File

@ -208,12 +208,11 @@ void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int*
/// See the #rcConfig documentation for more information on the configuration parameters.
///
/// @see rcAllocHeightfield, rcHeightfield
bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height,
bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
const float* bmin, const float* bmax,
float cs, float ch)
{
// TODO: VC complains about unref formal variable, figure out a way to handle this better.
// rcAssert(ctx);
rcIgnoreUnused(ctx);
hf.width = width;
hf.height = height;
@ -245,13 +244,12 @@ static void calcTriNormal(const float* v0, const float* v1, const float* v2, flo
/// See the #rcConfig documentation for more information on the configuration parameters.
///
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
const float* verts, int /*nv*/,
const int* tris, int nt,
unsigned char* areas)
{
// TODO: VC complains about unref formal variable, figure out a way to handle this better.
// rcAssert(ctx);
rcIgnoreUnused(ctx);
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
@ -275,13 +273,12 @@ void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
/// See the #rcConfig documentation for more information on the configuration parameters.
///
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
const float* verts, int /*nv*/,
const int* tris, int nt,
unsigned char* areas)
{
// TODO: VC complains about unref formal variable, figure out a way to handle this better.
// rcAssert(ctx);
rcIgnoreUnused(ctx);
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
@ -297,10 +294,9 @@ void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAng
}
}
int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf)
int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf)
{
// TODO: VC complains about unref formal variable, figure out a way to handle this better.
// rcAssert(ctx);
rcIgnoreUnused(ctx);
const int w = hf.width;
const int h = hf.height;

View File

@ -735,7 +735,8 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
}
// Remove the polygon.
unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2];
memcpy(p,p2,sizeof(unsigned short)*nvp);
if (p != p2)
memcpy(p,p2,sizeof(unsigned short)*nvp);
memset(p+nvp,0xff,sizeof(unsigned short)*nvp);
mesh.regs[i] = mesh.regs[mesh.npolys-1];
mesh.areas[i] = mesh.areas[mesh.npolys-1];
@ -935,7 +936,9 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
unsigned short* pa = &polys[bestPa*nvp];
unsigned short* pb = &polys[bestPb*nvp];
mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
unsigned short* last = &polys[(npolys-1)*nvp];
if (pb != last)
memcpy(pb, last, sizeof(unsigned short)*nvp);
pregs[bestPb] = pregs[npolys-1];
pareas[bestPb] = pareas[npolys-1];
npolys--;
@ -1395,6 +1398,12 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f);
const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f);
bool isMinX = (ox == 0);
bool isMinZ = (oz == 0);
bool isMaxX = ((unsigned short)floorf((mesh.bmax[0] - pmesh->bmax[0]) / mesh.cs + 0.5f)) == 0;
bool isMaxZ = ((unsigned short)floorf((mesh.bmax[2] - pmesh->bmax[2]) / mesh.cs + 0.5f)) == 0;
bool isOnBorder = (isMinX || isMinZ || isMaxX || isMaxZ);
for (int j = 0; j < pmesh->nverts; ++j)
{
unsigned short* v = &pmesh->verts[j*3];
@ -1415,6 +1424,36 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
if (src[k] == RC_MESH_NULL_IDX) break;
tgt[k] = vremap[src[k]];
}
if (isOnBorder)
{
for (int k = mesh.nvp; k < mesh.nvp * 2; ++k)
{
if (src[k] & 0x8000 && src[k] != 0xffff)
{
unsigned short dir = src[k] & 0xf;
switch (dir)
{
case 0: // Portal x-
if (isMinX)
tgt[k] = src[k];
break;
case 1: // Portal z+
if (isMaxZ)
tgt[k] = src[k];
break;
case 2: // Portal x+
if (isMaxX)
tgt[k] = src[k];
break;
case 3: // Portal z-
if (isMinZ)
tgt[k] = src[k];
break;
}
}
}
}
}
}

View File

@ -839,7 +839,8 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
return true;
}
static void getHeightData(const rcCompactHeightfield& chf,
static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
const unsigned short* poly, const int npoly,
const unsigned short* verts, const int bs,
rcHeightPatch& hp, rcIntArray& stack)
@ -967,8 +968,83 @@ static void getHeightData(const rcCompactHeightfield& chf,
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
const rcCompactSpan& cs = chf.spans[ci];
hp.data[idx] = cs.y;
// getHeightData seeds are given in coordinates with borders
stack[i+0] += bs;
stack[i+1] += bs;
}
}
static void getHeightData(const rcCompactHeightfield& chf,
const unsigned short* poly, const int npoly,
const unsigned short* verts, const int bs,
rcHeightPatch& hp, rcIntArray& stack,
int region)
{
// Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices.
stack.resize(0);
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
bool empty = true;
// Copy the height from the same region, and mark region borders
// as seed points to fill the rest.
for (int hy = 0; hy < hp.height; hy++)
{
int y = hp.ymin + hy + bs;
for (int hx = 0; hx < hp.width; hx++)
{
int x = hp.xmin + hx + bs;
const rcCompactCell& c = chf.cells[x+y*chf.width];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
const rcCompactSpan& s = chf.spans[i];
if (s.reg == region)
{
// Store height
hp.data[hx + hy*hp.width] = s.y;
empty = false;
// If any of the neighbours is not in same region,
// add the current location as flood fill start
bool border = false;
for (int dir = 0; dir < 4; ++dir)
{
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
{
const int ax = x + rcGetDirOffsetX(dir);
const int ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
const rcCompactSpan& as = chf.spans[ai];
if (as.reg != region)
{
border = true;
break;
}
}
}
if (border)
{
stack.push(x);
stack.push(y);
stack.push(i);
}
break;
}
}
}
}
// if the polygon does not contian any points from the current region (rare, but happens)
// then use the cells closest to the polygon vertices as seeds to fill the height field
if (empty)
getHeightDataSeedsFromVertices(chf, poly, npoly, verts, bs, hp, stack);
static const int RETRACT_SIZE = 256;
int head = 0;
@ -993,26 +1069,25 @@ static void getHeightData(const rcCompactHeightfield& chf,
const int ax = cx + rcGetDirOffsetX(dir);
const int ay = cy + rcGetDirOffsetY(dir);
const int hx = ax - hp.xmin - bs;
const int hy = ay - hp.ymin - bs;
if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
ay < hp.ymin || ay >= (hp.ymin+hp.height))
if (hx < 0 || hx >= hp.width || hy < 0 || hy >= hp.height)
continue;
if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT)
if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT)
continue;
const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);
const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir);
const rcCompactSpan& as = chf.spans[ai];
int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
hp.data[idx] = as.y;
hp.data[hx + hy*hp.width] = as.y;
stack.push(ax);
stack.push(ay);
stack.push(ai);
}
}
}
static unsigned char getEdgeFlags(const float* va, const float* vb,
@ -1170,7 +1245,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
hp.ymin = bounds[i*4+2];
hp.width = bounds[i*4+1]-bounds[i*4+0];
hp.height = bounds[i*4+3]-bounds[i*4+2];
getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack);
getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack, mesh.regs[i]);
// Build detail mesh.
int nverts = 0;
@ -1340,4 +1415,3 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
return true;
}

View File

@ -95,7 +95,7 @@ static void addSpan(rcHeightfield& hf, const int x, const int y,
s->area = area;
s->next = 0;
// Empty cell, add he first span.
// Empty cell, add the first span.
if (!hf.spans[idx])
{
hf.spans[idx] = s;
@ -169,36 +169,64 @@ void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
addSpan(hf, x,y, smin, smax, area, flagMergeThr);
}
static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd)
// divides a convex polygons into two convex polygons on both sides of a line
static void dividePoly(const float* in, int nin,
float* out1, int* nout1,
float* out2, int* nout2,
float x, int axis)
{
float d[12];
for (int i = 0; i < n; ++i)
d[i] = pnx*in[i*3+0] + pnz*in[i*3+2] + pd;
int m = 0;
for (int i = 0, j = n-1; i < n; j=i, ++i)
for (int i = 0; i < nin; ++i)
d[i] = x - in[i*3+axis];
int m = 0, n = 0;
for (int i = 0, j = nin-1; i < nin; j=i, ++i)
{
bool ina = d[j] >= 0;
bool inb = d[i] >= 0;
if (ina != inb)
{
float s = d[j] / (d[j] - d[i]);
out[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s;
out[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s;
out[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s;
out1[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s;
out1[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s;
out1[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s;
rcVcopy(out2 + n*3, out1 + m*3);
m++;
n++;
// add the i'th point to the right polygon. Do NOT add points that are on the dividing line
// since these were already added above
if (d[i] > 0)
{
rcVcopy(out1 + m*3, in + i*3);
m++;
}
else if (d[i] < 0)
{
rcVcopy(out2 + n*3, in + i*3);
n++;
}
}
if (inb)
else // same side
{
out[m*3+0] = in[i*3+0];
out[m*3+1] = in[i*3+1];
out[m*3+2] = in[i*3+2];
m++;
// add the i'th point to the right polygon. Addition is done even for points on the dividing line
if (d[i] >= 0)
{
rcVcopy(out1 + m*3, in + i*3);
m++;
if (d[i] != 0)
continue;
}
rcVcopy(out2 + n*3, in + i*3);
n++;
}
}
return m;
*nout1 = m;
*nout2 = n;
}
static void rasterizeTri(const float* v0, const float* v1, const float* v2,
const unsigned char area, rcHeightfield& hf,
const float* bmin, const float* bmax,
@ -222,48 +250,57 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
if (!overlapBounds(bmin, bmax, tmin, tmax))
return;
// Calculate the footpring of the triangle on the grid.
int x0 = (int)((tmin[0] - bmin[0])*ics);
// Calculate the footprint of the triangle on the grid's y-axis
int y0 = (int)((tmin[2] - bmin[2])*ics);
int x1 = (int)((tmax[0] - bmin[0])*ics);
int y1 = (int)((tmax[2] - bmin[2])*ics);
x0 = rcClamp(x0, 0, w-1);
y0 = rcClamp(y0, 0, h-1);
x1 = rcClamp(x1, 0, w-1);
y1 = rcClamp(y1, 0, h-1);
// Clip the triangle into all grid cells it touches.
float in[7*3], out[7*3], inrow[7*3];
float buf[7*3*4];
float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3;
rcVcopy(&in[0], v0);
rcVcopy(&in[1*3], v1);
rcVcopy(&in[2*3], v2);
int nvrow, nvIn = 3;
for (int y = y0; y <= y1; ++y)
{
// Clip polygon to row.
rcVcopy(&in[0], v0);
rcVcopy(&in[1*3], v1);
rcVcopy(&in[2*3], v2);
int nvrow = 3;
// Clip polygon to row. Store the remaining polygon as well
const float cz = bmin[2] + y*cs;
nvrow = clipPoly(in, nvrow, out, 0, 1, -cz);
if (nvrow < 3) continue;
nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs);
dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2);
rcSwap(in, p1);
if (nvrow < 3) continue;
// find the horizontal bounds in the row
float minX = inrow[0], maxX = inrow[0];
for (int i=1; i<nvrow; ++i)
{
if (minX > inrow[i*3]) minX = inrow[i*3];
if (maxX < inrow[i*3]) maxX = inrow[i*3];
}
int x0 = (int)((minX - bmin[0])*ics);
int x1 = (int)((maxX - bmin[0])*ics);
x0 = rcClamp(x0, 0, w-1);
x1 = rcClamp(x1, 0, w-1);
int nv, nv2 = nvrow;
for (int x = x0; x <= x1; ++x)
{
// Clip polygon to column.
int nv = nvrow;
// Clip polygon to column. store the remaining polygon as well
const float cx = bmin[0] + x*cs;
nv = clipPoly(inrow, nv, out, 1, 0, -cx);
if (nv < 3) continue;
nv = clipPoly(out, nv, in, -1, 0, cx+cs);
dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0);
rcSwap(inrow, p2);
if (nv < 3) continue;
// Calculate min and max of the span.
float smin = in[1], smax = in[1];
float smin = p1[1], smax = p1[1];
for (int i = 1; i < nv; ++i)
{
smin = rcMin(smin, in[i*3+1]);
smax = rcMax(smax, in[i*3+1]);
smin = rcMin(smin, p1[i*3+1]);
smax = rcMax(smax, p1[i*3+1]);
}
smin -= bmin[1];
smax -= bmin[1];

View File

@ -299,7 +299,10 @@ static bool floodRegion(int x, int y, int i,
if (nr & RC_BORDER_REG) // Do not take borders into account.
continue;
if (nr != 0 && nr != r)
{
ar = nr;
break;
}
const rcCompactSpan& as = chf.spans[ai];
@ -313,7 +316,10 @@ static bool floodRegion(int x, int y, int i,
continue;
unsigned short nr2 = srcReg[ai2];
if (nr2 != 0 && nr2 != r)
{
ar = nr2;
break;
}
}
}
}
@ -399,30 +405,44 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
rcCompactHeightfield& chf,
unsigned short* srcReg, unsigned short* srcDist,
unsigned short* dstReg, unsigned short* dstDist,
rcIntArray& stack)
rcIntArray& stack,
bool fillStack)
{
const int w = chf.width;
const int h = chf.height;
// Find cells revealed by the raised level.
stack.resize(0);
for (int y = 0; y < h; ++y)
if (fillStack)
{
for (int x = 0; x < w; ++x)
// Find cells revealed by the raised level.
stack.resize(0);
for (int y = 0; y < h; ++y)
{
const rcCompactCell& c = chf.cells[x+y*w];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
for (int x = 0; x < w; ++x)
{
if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
const rcCompactCell& c = chf.cells[x+y*w];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
stack.push(x);
stack.push(y);
stack.push(i);
if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
{
stack.push(x);
stack.push(y);
stack.push(i);
}
}
}
}
}
else // use cells in the input stack
{
// mark all cells which already have a region
for (int j=0; j<stack.size(); j+=3)
{
int i = stack[j+2];
if (srcReg[i] != 0)
stack[j+2] = -1;
}
}
int iter = 0;
while (stack.size() > 0)
{
@ -493,6 +513,61 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
}
static void sortCellsByLevel(unsigned short startLevel,
rcCompactHeightfield& chf,
unsigned short* srcReg,
unsigned int nbStacks, rcIntArray* stacks,
unsigned short loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift
{
const int w = chf.width;
const int h = chf.height;
startLevel = startLevel >> loglevelsPerStack;
for (unsigned int j=0; j<nbStacks; ++j)
stacks[j].resize(0);
// put all cells in the level range into the appropriate stacks
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
const rcCompactCell& c = chf.cells[x+y*w];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
if (chf.areas[i] == RC_NULL_AREA || srcReg[i] != 0)
continue;
int level = chf.dist[i] >> loglevelsPerStack;
int sId = startLevel - level;
if (sId >= (int)nbStacks)
continue;
if (sId < 0)
sId = 0;
stacks[sId].push(x);
stacks[sId].push(y);
stacks[sId].push(i);
}
}
}
}
static void appendStacks(rcIntArray& srcStack, rcIntArray& dstStack,
unsigned short* srcReg)
{
for (int j=0; j<srcStack.size(); j+=3)
{
int i = srcStack[j+2];
if ((i < 0) || (srcReg[i] != 0))
continue;
dstStack.push(srcStack[j]);
dstStack.push(srcStack[j+1]);
dstStack.push(srcStack[j+2]);
}
}
struct rcRegion
{
inline rcRegion(unsigned short i) :
@ -1530,7 +1605,13 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
}
ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
const int LOG_NB_STACKS = 3;
const int NB_STACKS = 1 << LOG_NB_STACKS;
rcIntArray lvlStacks[NB_STACKS];
for (int i=0; i<NB_STACKS; ++i)
lvlStacks[i].resize(1024);
rcIntArray stack(1024);
rcIntArray visited(1024);
@ -1565,14 +1646,25 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
chf.borderSize = borderSize;
}
int sId = -1;
while (level > 0)
{
level = level >= 2 ? level-2 : 0;
sId = (sId+1) & (NB_STACKS-1);
// ctx->startTimer(RC_TIMER_DIVIDE_TO_LEVELS);
if (sId == 0)
sortCellsByLevel(level, chf, srcReg, NB_STACKS, lvlStacks, 1);
else
appendStacks(lvlStacks[sId-1], lvlStacks[sId], srcReg); // copy left overs from last level
// ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS);
ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
// Expand current regions until no empty connected cells found.
if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg)
{
rcSwap(srcReg, dstReg);
rcSwap(srcDist, dstDist);
@ -1583,18 +1675,15 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
// Mark new regions with IDs.
for (int y = 0; y < h; ++y)
for (int j=0; j<lvlStacks[sId].size(); j+=3)
{
for (int x = 0; x < w; ++x)
int x = lvlStacks[sId][j];
int y = lvlStacks[sId][j+1];
int i = lvlStacks[sId][j+2];
if (i >= 0 && srcReg[i] == 0)
{
const rcCompactCell& c = chf.cells[x+y*w];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA)
continue;
if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
regionId++;
}
if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
regionId++;
}
}
@ -1602,7 +1691,7 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
}
// Expand current regions until no empty connected cells found.
if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack, true) != srcReg)
{
rcSwap(srcReg, dstReg);
rcSwap(srcDist, dstDist);

View File

@ -0,0 +1,6 @@
s Tile Mesh
f nav_test.obj
rc 45.133884 -0.533207 -3.775568 47.078232 7.797605 14.293253 0xffef 0x0
rc 52.979847 -2.778793 -2.914886 50.628868 -2.350212 13.917850 0xffef 0x0
rc 45.209217 2.024442 1.838851 46.888412 7.797606 15.772338 0xffef 0x0
rc 45.388317 -0.562073 -3.673226 46.651001 7.797606 15.513507 0xffef 0x0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@ class TestCase
enum TestType
{
TEST_PATHFIND,
TEST_RAYCAST,
};
struct Test
@ -39,6 +40,7 @@ class TestCase
TestType type;
float spos[3], epos[3];
float nspos[3], nepos[3];
float radius;
int includeFlags, excludeFlags;
bool expand;

View File

@ -155,7 +155,7 @@ void CrowdToolState::init(class Sample* sample)
crowd->init(MAX_AGENTS, m_sample->getAgentRadius(), nav);
// Make polygons with 'disabled' flag invalid.
crowd->getEditableFilter()->setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
crowd->getEditableFilter(0)->setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
// Setup local avoidance params to different qualities.
dtObstacleAvoidanceParams params;
@ -707,7 +707,7 @@ void CrowdToolState::setMoveTarget(const float* p, bool adjust)
// Find nearest point on navmesh and set move request to that location.
dtNavMeshQuery* navquery = m_sample->getNavMeshQuery();
dtCrowd* crowd = m_sample->getCrowd();
const dtQueryFilter* filter = crowd->getFilter();
const dtQueryFilter* filter = crowd->getFilter(0);
const float* ext = crowd->getQueryExtents();
if (adjust)
@ -1068,6 +1068,7 @@ void CrowdTool::handleToggle()
void CrowdTool::handleUpdate(const float dt)
{
rcIgnoreUnused(dt);
}
void CrowdTool::handleRender()
@ -1076,6 +1077,9 @@ void CrowdTool::handleRender()
void CrowdTool::handleRenderOverlay(double* proj, double* model, int* view)
{
rcIgnoreUnused(model);
rcIgnoreUnused(proj);
// Tool help
const int h = view[3];
int ty = h-40;

View File

@ -174,8 +174,12 @@ bool InputGeom::load(rcContext* ctx, const char* filePath)
fclose(fp);
return false;
}
fread(buf, bufSize, 1, fp);
size_t readLen = fread(buf, bufSize, 1, fp);
fclose(fp);
if (readLen != 1)
{
return false;
}
m_offMeshConCount = 0;
m_volumeCount = 0;

View File

@ -78,7 +78,6 @@ void rcMeshLoaderObj::addTriangle(int a, int b, int c, int& cap)
static char* parseRow(char* buf, char* bufEnd, char* row, int len)
{
bool cont = false;
bool start = true;
bool done = false;
int n = 0;
@ -90,7 +89,6 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len)
switch (c)
{
case '\\':
cont = true; // multirow
break;
case '\n':
if (start) break;
@ -103,7 +101,6 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len)
if (start) break;
default:
start = false;
cont = false;
row[n++] = c;
if (n >= len-1)
done = true;
@ -153,9 +150,14 @@ bool rcMeshLoaderObj::load(const char* filename)
fclose(fp);
return false;
}
fread(buf, bufSize, 1, fp);
size_t readLen = fread(buf, bufSize, 1, fp);
fclose(fp);
if (readLen != 1)
{
return false;
}
char* src = buf;
char* srcEnd = buf + bufSize;
char row[512];

View File

@ -63,7 +63,7 @@ public:
}
m_size = n;
}
inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
inline void push(dtPolyRef item) { resize(m_size+1); m_data[m_size-1] = item; }
inline dtPolyRef pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
inline const dtPolyRef& operator[](int i) const { return m_data[i]; }
inline dtPolyRef& operator[](int i) { return m_data[i]; }
@ -261,8 +261,11 @@ void NavMeshPruneTool::handleMenu()
}
}
void NavMeshPruneTool::handleClick(const float* /*s*/, const float* p, bool shift)
void NavMeshPruneTool::handleClick(const float* s, const float* p, bool shift)
{
rcIgnoreUnused(s);
rcIgnoreUnused(shift);
if (!m_sample) return;
InputGeom* geom = m_sample->getInputGeom();
if (!geom) return;
@ -341,9 +344,11 @@ void NavMeshPruneTool::handleRender()
void NavMeshPruneTool::handleRenderOverlay(double* proj, double* model, int* view)
{
rcIgnoreUnused(model);
rcIgnoreUnused(proj);
// Tool help
const int h = view[3];
imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Click fill area.", imguiRGBA(255,255,255,192));
imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Click fill area.", imguiRGBA(255,255,255,192));
}

View File

@ -101,6 +101,66 @@ static int fixupCorridor(dtPolyRef* path, const int npath, const int maxPath,
return req+size;
}
// This function checks if the path has a small U-turn, that is,
// a polygon further in the path is adjacent to the first polygon
// in the path. If that happens, a shortcut is taken.
// This can happen if the target (T) location is at tile boundary,
// and we're (S) approaching it parallel to the tile edge.
// The choice at the vertex can be arbitrary,
// +---+---+
// |:::|:::|
// +-S-+-T-+
// |:::| | <-- the step can end up in here, resulting U-turn path.
// +---+---+
static int fixupShortcuts(dtPolyRef* path, int npath, dtNavMeshQuery* navQuery)
{
if (npath < 3)
return npath;
// Get connected polygons
static const int maxNeis = 16;
dtPolyRef neis[maxNeis];
int nneis = 0;
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
if (dtStatusFailed(navQuery->getAttachedNavMesh()->getTileAndPolyByRef(path[0], &tile, &poly)))
return npath;
for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
{
const dtLink* link = &tile->links[k];
if (link->ref != 0)
{
if (nneis < maxNeis)
neis[nneis++] = link->ref;
}
}
// If any of the neighbour polygons is within the next few polygons
// in the path, short cut to that polygon directly.
static const int maxLookAhead = 6;
int cut = 0;
for (int i = dtMin(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--) {
for (int j = 0; j < nneis; j++)
{
if (path[i] == neis[j]) {
cut = i;
break;
}
}
}
if (cut > 1)
{
int offset = cut-1;
npath -= offset;
for (int i = 1; i < npath; i++)
path[i] = path[i+offset];
}
return npath;
}
static bool getSteerTarget(dtNavMeshQuery* navQuery, const float* startPos, const float* endPos,
const float minTargetDist,
const dtPolyRef* path, const int pathSize,
@ -446,8 +506,8 @@ void NavMeshTesterTool::handleToggle()
if (m_pathIterPolyCount)
{
// Iterate over the path to find smooth path on the detail mesh surface.
m_navQuery->closestPointOnPoly(m_startRef, m_spos, m_iterPos);
m_navQuery->closestPointOnPoly(m_pathIterPolys[m_pathIterPolyCount-1], m_epos, m_targetPos);
m_navQuery->closestPointOnPoly(m_startRef, m_spos, m_iterPos, 0);
m_navQuery->closestPointOnPoly(m_pathIterPolys[m_pathIterPolyCount-1], m_epos, m_targetPos, 0);
m_nsmoothPath = 0;
@ -503,6 +563,8 @@ void NavMeshTesterTool::handleToggle()
m_navQuery->moveAlongSurface(m_pathIterPolys[0], m_iterPos, moveTgt, &m_filter,
result, visited, &nvisited, 16);
m_pathIterPolyCount = fixupCorridor(m_pathIterPolys, m_pathIterPolyCount, MAX_POLYS, visited, nvisited);
m_pathIterPolyCount = fixupShortcuts(m_pathIterPolys, m_pathIterPolyCount, m_navQuery);
float h = 0;
m_navQuery->getPolyHeight(m_pathIterPolys[0], result, &h);
result[1] = h;
@ -588,7 +650,7 @@ void NavMeshTesterTool::handleUpdate(const float /*dt*/)
float epos[3];
dtVcopy(epos, m_epos);
if (m_polys[m_npolys-1] != m_endRef)
m_navQuery->closestPointOnPoly(m_polys[m_npolys-1], m_epos, epos);
m_navQuery->closestPointOnPoly(m_polys[m_npolys-1], m_epos, epos, 0);
m_navQuery->findStraightPath(m_spos, epos, m_polys, m_npolys,
m_straightPath, m_straightPathFlags,
@ -653,8 +715,8 @@ void NavMeshTesterTool::recalc()
int npolys = m_npolys;
float iterPos[3], targetPos[3];
m_navQuery->closestPointOnPoly(m_startRef, m_spos, iterPos);
m_navQuery->closestPointOnPoly(polys[npolys-1], m_epos, targetPos);
m_navQuery->closestPointOnPoly(m_startRef, m_spos, iterPos, 0);
m_navQuery->closestPointOnPoly(polys[npolys-1], m_epos, targetPos, 0);
static const float STEP_SIZE = 0.5f;
static const float SLOP = 0.01f;
@ -698,8 +760,10 @@ void NavMeshTesterTool::recalc()
int nvisited = 0;
m_navQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &m_filter,
result, visited, &nvisited, 16);
npolys = fixupCorridor(polys, npolys, MAX_POLYS, visited, nvisited);
npolys = fixupShortcuts(polys, npolys, m_navQuery);
float h = 0;
m_navQuery->getPolyHeight(polys[0], result, &h);
result[1] = h;
@ -791,7 +855,7 @@ void NavMeshTesterTool::recalc()
float epos[3];
dtVcopy(epos, m_epos);
if (m_polys[m_npolys-1] != m_endRef)
m_navQuery->closestPointOnPoly(m_polys[m_npolys-1], m_epos, epos);
m_navQuery->closestPointOnPoly(m_polys[m_npolys-1], m_epos, epos, 0);
m_navQuery->findStraightPath(m_spos, epos, m_polys, m_npolys,
m_straightPath, m_straightPathFlags,
@ -1297,7 +1361,7 @@ void NavMeshTesterTool::handleRender()
for (int i = 0; i < m_nrandPoints; i++)
{
const float* p = &m_randPoints[i*3];
dd.vertex(p[0],p[1]+0.1,p[2], duRGBA(220,32,16,192));
dd.vertex(p[0],p[1]+0.1f,p[2], duRGBA(220,32,16,192));
}
dd.end();

View File

@ -312,8 +312,8 @@ bool FileIO::write(const void* ptr, const size_t size)
bool FileIO::read(void* ptr, const size_t size)
{
if (!m_fp || m_mode != 2) return false;
fread(ptr, size, 1, m_fp);
return true;
size_t readLen = fread(ptr, size, 1, m_fp);
return readLen == 1;
}

View File

@ -283,7 +283,12 @@ dtNavMesh* Sample_TileMesh::loadAll(const char* path)
// Read header.
NavMeshSetHeader header;
fread(&header, sizeof(NavMeshSetHeader), 1, fp);
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
if (readLen != 1)
{
fclose(fp);
return 0;
}
if (header.magic != NAVMESHSET_MAGIC)
{
fclose(fp);
@ -312,15 +317,20 @@ dtNavMesh* Sample_TileMesh::loadAll(const char* path)
for (int i = 0; i < header.numTiles; ++i)
{
NavMeshTileHeader tileHeader;
fread(&tileHeader, sizeof(tileHeader), 1, fp);
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
if (readLen != 1)
return 0;
if (!tileHeader.tileRef || !tileHeader.dataSize)
break;
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
if (!data) break;
memset(data, 0, tileHeader.dataSize);
fread(data, tileHeader.dataSize, 1, fp);
readLen = fread(data, tileHeader.dataSize, 1, fp);
if (readLen != 1)
return 0;
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
}

View File

@ -106,8 +106,12 @@ bool TestCase::load(const char* filePath)
fclose(fp);
return false;
}
fread(buf, bufSize, 1, fp);
size_t readLen = fread(buf, bufSize, 1, fp);
fclose(fp);
if (readLen != 1)
{
return false;
}
char* src = buf;
char* srcEnd = buf + bufSize;
@ -141,6 +145,20 @@ bool TestCase::load(const char* filePath)
&test->epos[0], &test->epos[1], &test->epos[2],
&test->includeFlags, &test->excludeFlags);
}
else if (row[0] == 'r' && row[1] == 'c')
{
// Pathfind test.
Test* test = new Test;
memset(test, 0, sizeof(Test));
test->type = TEST_RAYCAST;
test->expand = false;
test->next = m_tests;
m_tests = test;
sscanf(row+2, "%f %f %f %f %f %f %x %x",
&test->spos[0], &test->spos[1], &test->spos[2],
&test->epos[0], &test->epos[1], &test->epos[2],
&test->includeFlags, &test->excludeFlags);
}
}
delete [] buf;
@ -187,8 +205,8 @@ void TestCase::doTests(dtNavMesh* navmesh, dtNavMeshQuery* navquery)
TimeVal findNearestPolyStart = getPerfTime();
dtPolyRef startRef, endRef;
navquery->findNearestPoly(iter->spos, polyPickExt, &filter, &startRef, 0);
navquery->findNearestPoly(iter->epos, polyPickExt, &filter, &endRef, 0);
navquery->findNearestPoly(iter->spos, polyPickExt, &filter, &startRef, iter->nspos);
navquery->findNearestPoly(iter->epos, polyPickExt, &filter, &endRef, iter->nepos);
TimeVal findNearestPolyEnd = getPerfTime();
iter->findNearestPolyTime += getPerfDeltaTimeUsec(findNearestPolyStart, findNearestPolyEnd);
@ -196,35 +214,82 @@ void TestCase::doTests(dtNavMesh* navmesh, dtNavMeshQuery* navquery)
if (!startRef || ! endRef)
continue;
// Find path
TimeVal findPathStart = getPerfTime();
if (iter->type == TEST_PATHFIND)
{
// Find path
TimeVal findPathStart = getPerfTime();
navquery->findPath(startRef, endRef, iter->spos, iter->epos, &filter, polys, &iter->npolys, MAX_POLYS);
TimeVal findPathEnd = getPerfTime();
iter->findPathTime += getPerfDeltaTimeUsec(findPathStart, findPathEnd);
// Find straight path
if (iter->npolys)
{
TimeVal findStraightPathStart = getPerfTime();
navquery->findPath(startRef, endRef, iter->spos, iter->epos, &filter, polys, &iter->npolys, MAX_POLYS);
navquery->findStraightPath(iter->spos, iter->epos, polys, iter->npolys,
straight, 0, 0, &iter->nstraight, MAX_POLYS);
TimeVal findStraightPathEnd = getPerfTime();
iter->findStraightPathTime += getPerfDeltaTimeUsec(findStraightPathStart, findStraightPathEnd);
}
TimeVal findPathEnd = getPerfTime();
iter->findPathTime += getPerfDeltaTimeUsec(findPathStart, findPathEnd);
// Copy results
if (iter->npolys)
{
iter->polys = new dtPolyRef[iter->npolys];
memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
// Find straight path
if (iter->npolys)
{
TimeVal findStraightPathStart = getPerfTime();
navquery->findStraightPath(iter->spos, iter->epos, polys, iter->npolys,
straight, 0, 0, &iter->nstraight, MAX_POLYS);
TimeVal findStraightPathEnd = getPerfTime();
iter->findStraightPathTime += getPerfDeltaTimeUsec(findStraightPathStart, findStraightPathEnd);
}
// Copy results
if (iter->npolys)
{
iter->polys = new dtPolyRef[iter->npolys];
memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
}
if (iter->nstraight)
{
iter->straight = new float[iter->nstraight*3];
memcpy(iter->straight, straight, sizeof(float)*3*iter->nstraight);
}
}
if (iter->nstraight)
else if (iter->type == TEST_RAYCAST)
{
iter->straight = new float[iter->nstraight*3];
memcpy(iter->straight, straight, sizeof(float)*3*iter->nstraight);
float t = 0;
float hitNormal[3], hitPos[3];
iter->straight = new float[2*3];
iter->nstraight = 2;
iter->straight[0] = iter->spos[0];
iter->straight[1] = iter->spos[1];
iter->straight[2] = iter->spos[2];
TimeVal findPathStart = getPerfTime();
navquery->raycast(startRef, iter->spos, iter->epos, &filter, &t, hitNormal, polys, &iter->npolys, MAX_POLYS);
TimeVal findPathEnd = getPerfTime();
iter->findPathTime += getPerfDeltaTimeUsec(findPathStart, findPathEnd);
if (t > 1)
{
// No hit
dtVcopy(hitPos, iter->epos);
}
else
{
// Hit
dtVlerp(hitPos, iter->spos, iter->epos, t);
}
// Adjust height.
if (iter->npolys > 0)
{
float h = 0;
navquery->getPolyHeight(polys[iter->npolys-1], hitPos, &h);
hitPos[1] = h;
}
dtVcopy(&iter->straight[3], hitPos);
if (iter->npolys)
{
iter->polys = new dtPolyRef[iter->npolys];
memcpy(iter->polys, polys, sizeof(dtPolyRef)*iter->npolys);
}
}
}
@ -259,6 +324,32 @@ void TestCase::handleRender()
glColor4ub(51,102,0,129);
glVertex3f(iter->epos[0],iter->epos[1]-0.3f,iter->epos[2]);
glVertex3f(iter->epos[0],iter->epos[1]+0.3f,iter->epos[2]);
if (iter->expand)
{
const float s = 0.1f;
glColor4ub(255,32,0,128);
glVertex3f(iter->spos[0]-s,iter->spos[1],iter->spos[2]);
glVertex3f(iter->spos[0]+s,iter->spos[1],iter->spos[2]);
glVertex3f(iter->spos[0],iter->spos[1],iter->spos[2]-s);
glVertex3f(iter->spos[0],iter->spos[1],iter->spos[2]+s);
glColor4ub(255,192,0,255);
glVertex3f(iter->nspos[0]-s,iter->nspos[1],iter->nspos[2]);
glVertex3f(iter->nspos[0]+s,iter->nspos[1],iter->nspos[2]);
glVertex3f(iter->nspos[0],iter->nspos[1],iter->nspos[2]-s);
glVertex3f(iter->nspos[0],iter->nspos[1],iter->nspos[2]+s);
glColor4ub(255,32,0,128);
glVertex3f(iter->epos[0]-s,iter->epos[1],iter->epos[2]);
glVertex3f(iter->epos[0]+s,iter->epos[1],iter->epos[2]);
glVertex3f(iter->epos[0],iter->epos[1],iter->epos[2]-s);
glVertex3f(iter->epos[0],iter->epos[1],iter->epos[2]+s);
glColor4ub(255,192,0,255);
glVertex3f(iter->nepos[0]-s,iter->nepos[1],iter->nepos[2]);
glVertex3f(iter->nepos[0]+s,iter->nepos[1],iter->nepos[2]);
glVertex3f(iter->nepos[0],iter->nepos[1],iter->nepos[2]-s);
glVertex3f(iter->nepos[0],iter->nepos[1],iter->nepos[2]+s);
}
if (iter->expand)
glColor4ub(255,192,0,255);

View File

@ -247,7 +247,7 @@ bool imguiRenderGLInit(const char* fontpath)
FILE* fp = fopen(fontpath, "rb");
if (!fp) return false;
fseek(fp, 0, SEEK_END);
int size = ftell(fp);
size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
unsigned char* ttfBuffer = (unsigned char*)malloc(size);
@ -257,8 +257,13 @@ bool imguiRenderGLInit(const char* fontpath)
return false;
}
fread(ttfBuffer, 1, size, fp);
size_t readLen = fread(ttfBuffer, 1, size, fp);
fclose(fp);
if (readLen != size)
{
return false;
}
fp = 0;
unsigned char* bmap = (unsigned char*)malloc(512*512);

View File

@ -101,7 +101,8 @@ int main(int /*argc*/, char** /*argv*/)
}
else
{
width = vi->current_w - 20;
width = rcMin(vi->current_w, (int)(vi->current_h * 16.0 / 9.0));
width = width - 80;
height = vi->current_h - 80;
screen = SDL_SetVideoMode(width, height, 0, SDL_OPENGL);
}
@ -817,7 +818,6 @@ int main(int /*argc*/, char** /*argv*/)
{
delete geom;
geom = 0;
showLog = true;
logScroll = 0;
ctx.dumpLog("Geom load log %s:", meshName);
@ -827,6 +827,10 @@ int main(int /*argc*/, char** /*argv*/)
sample->handleMeshChanged(geom);
}
// This will ensure that tile & poly bits are updated in tiled sample.
if (sample)
sample->handleSettings();
ctx.resetLog();
if (sample && !sample->handleBuild())
{
@ -860,7 +864,7 @@ int main(int /*argc*/, char** /*argv*/)
}
rx = 45;
ry = -45;
glFogf(GL_FOG_START, camr*0.1f);
glFogf(GL_FOG_START, camr*0.2f);
glFogf(GL_FOG_END, camr*1.25f);
}

View File

@ -16,21 +16,22 @@ solution "recastnavigation"
-- extra warnings, no exceptions or rtti
flags {
"ExtraWarnings",
"FloatFast",
"NoExceptions",
"NoRTTI"
"NoRTTI",
"Symbols"
}
-- debug configs
configuration "Debug*"
defines { "DEBUG" }
flags { "Symbols" }
targetdir ( "Build/" .. action .. "/Debug" )
targetdir ( todir .. "/lib/Debug" )
-- release configs
configuration "Release*"
defines { "NDEBUG" }
flags { "Optimize" }
targetdir ( "Build/" .. action .. "/Release" )
targetdir ( todir .. "/lib/Release" )
-- windows specific
configuration "windows"
@ -50,7 +51,6 @@ project "DebugUtils"
"../DebugUtils/Include/*.h",
"../DebugUtils/Source/*.cpp"
}
targetdir (todir .. "/lib")
project "Detour"
language "C++"
@ -62,7 +62,6 @@ project "Detour"
"../Detour/Include/*.h",
"../Detour/Source/*.cpp"
}
targetdir (todir .. "/lib")
project "DetourCrowd"
language "C++"
@ -76,7 +75,6 @@ project "DetourCrowd"
"../DetourCrowd/Include/*.h",
"../DetourCrowd/Source/*.cpp"
}
targetdir (todir .. "/lib")
project "DetourTileCache"
language "C++"
@ -90,7 +88,6 @@ project "DetourTileCache"
"../DetourTileCache/Include/*.h",
"../DetourTileCache/Source/*.cpp"
}
targetdir (todir .. "/lib")
project "Recast"
language "C++"
@ -102,7 +99,6 @@ project "Recast"
"../Recast/Include/*.h",
"../Recast/Source/*.cpp"
}
targetdir (todir .. "/lib")
project "RecastDemo"
language "C++"