Merge branch 'master' of https://github.com/memononen/recastnavigation
This commit is contained in:
commit
3cb87f2432
8
.gitignore
vendored
8
.gitignore
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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++;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) :
|
||||
|
@ -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)]
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
///
|
@ -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 |
@ -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)"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
<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
|
||||
<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>
|
||||
|
@ -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 -->
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
|
6
RecastDemo/Bin/Tests/raycast_test.txt
Normal file
6
RecastDemo/Bin/Tests/raycast_test.txt
Normal 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
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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++"
|
||||
|
Loading…
x
Reference in New Issue
Block a user