mirror of
https://github.com/azerothcore/azerothcore-wotlk.git
synced 2025-12-06 02:30:26 -08:00
Properly update Recastnav:
The commit not contained the last updates, added them and correct CMakeLists include. MMaps re-extraction wil be REQUIRED.
This commit is contained in:
parent
2d464f94ed
commit
17802ab6e5
36 changed files with 5118 additions and 1930 deletions
|
|
@ -41,5 +41,5 @@ gSOAP (a portable development toolkit for C and C++ XML Web services and XML dat
|
|||
Version: 2.8.10
|
||||
|
||||
recastnavigation (Recast is state of the art navigation mesh construction toolset for games)
|
||||
http://code.google.com/p/recastnavigation/
|
||||
Version: 1.4
|
||||
https://github.com/memononen/recastnavigation
|
||||
Version: 64385e9ed0822427bca5814d03a3f4c4d7a6db9f
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (C)
|
||||
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
# Copyright (C) 2005-2011 MaNGOS project <http://getmangos.com/>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef DETOURALLOCATOR_H
|
||||
#define DETOURALLOCATOR_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/// Provides hint values to the memory allocator on how long the
|
||||
/// memory is expected to be used.
|
||||
enum dtAllocHint
|
||||
|
|
@ -32,7 +34,7 @@ enum dtAllocHint
|
|||
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
|
||||
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see dtAllocSetCustom
|
||||
typedef void* (dtAllocFunc)(int size, dtAllocHint hint);
|
||||
typedef void* (dtAllocFunc)(size_t size, dtAllocHint hint);
|
||||
|
||||
/// A memory deallocation function.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAllocFunc.
|
||||
|
|
@ -49,7 +51,7 @@ void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc);
|
|||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see dtFree
|
||||
void* dtAlloc(int size, dtAllocHint hint);
|
||||
void* dtAlloc(size_t size, dtAllocHint hint);
|
||||
|
||||
/// Deallocates a memory block.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAlloc.
|
||||
|
|
|
|||
|
|
@ -20,11 +20,14 @@
|
|||
#define DETOURCOMMON_H
|
||||
|
||||
#include "DetourMath.h"
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
@defgroup detour Detour
|
||||
|
||||
Members in this module are used to create, manipulate, and query navigation
|
||||
meshes.
|
||||
|
||||
@note This is a summary list of members. Use the index or search
|
||||
feature to find minor members.
|
||||
*/
|
||||
|
|
@ -480,6 +483,23 @@ inline void dtSwapEndian(float* v)
|
|||
void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
|
||||
const float s, const float t, float* out);
|
||||
|
||||
template<typename TypeToRetrieveAs>
|
||||
TypeToRetrieveAs* dtGetThenAdvanceBufferPointer(const unsigned char*& buffer, const size_t distanceToAdvance)
|
||||
{
|
||||
TypeToRetrieveAs* returnPointer = reinterpret_cast<TypeToRetrieveAs*>(buffer);
|
||||
buffer += distanceToAdvance;
|
||||
return returnPointer;
|
||||
}
|
||||
|
||||
template<typename TypeToRetrieveAs>
|
||||
TypeToRetrieveAs* dtGetThenAdvanceBufferPointer(unsigned char*& buffer, const size_t distanceToAdvance)
|
||||
{
|
||||
TypeToRetrieveAs* returnPointer = reinterpret_cast<TypeToRetrieveAs*>(buffer);
|
||||
buffer += distanceToAdvance;
|
||||
return returnPointer;
|
||||
}
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
#endif // DETOURCOMMON_H
|
||||
|
|
@ -490,28 +510,41 @@ void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
|
|||
// a source file. It reduces clutter in the main section of the header.
|
||||
|
||||
/**
|
||||
|
||||
@fn float dtTriArea2D(const float* a, const float* b, const float* c)
|
||||
@par
|
||||
|
||||
The vertices are projected onto the xz-plane, so the y-values are ignored.
|
||||
|
||||
This is a low cost function than can be used for various purposes. Its main purpose
|
||||
is for point/line relationship testing.
|
||||
|
||||
In all cases: A value of zero indicates that all vertices are collinear or represent the same point.
|
||||
(On the xz-plane.)
|
||||
|
||||
When used for point/line relationship tests, AB usually represents a line against which
|
||||
the C point is to be tested. In this case:
|
||||
|
||||
A positive value indicates that point C is to the left of line AB, looking from A toward B.<br/>
|
||||
A negative value indicates that point C is to the right of lineAB, looking from A toward B.
|
||||
|
||||
When used for evaluating a triangle:
|
||||
|
||||
The absolute value of the return value is two times the area of the triangle when it is
|
||||
projected onto the xz-plane.
|
||||
|
||||
A positive return value indicates:
|
||||
|
||||
<ul>
|
||||
<li>The vertices are wrapped in the normal Detour wrap direction.</li>
|
||||
<li>The triangle's 3D face normal is in the general up direction.</li>
|
||||
</ul>
|
||||
|
||||
A negative return value indicates:
|
||||
|
||||
<ul>
|
||||
<li>The vertices are reverse wrapped. (Wrapped opposite the normal Detour wrap direction.)</li>
|
||||
<li>The triangle's 3D face normal is in the general down direction.</li>
|
||||
</ul>
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/**
|
||||
@defgroup detour Detour
|
||||
|
||||
Members in this module are wrappers around the standard math library
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,15 @@
|
|||
#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
|
||||
|
||||
// Edited by TC
|
||||
#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
|
||||
#if defined(WIN32) && !defined(__MINGW32__)
|
||||
/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition
|
||||
typedef unsigned __int64 uint64_d;
|
||||
|
|
@ -37,20 +44,29 @@ typedef unsigned __int64 uint64_d;
|
|||
/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition
|
||||
typedef uint64_t uint64_d;
|
||||
#endif
|
||||
#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.
|
||||
|
||||
// Edited by TC
|
||||
// We cannot have over 31 bits for either tile nor poly
|
||||
// without changing polyCount to use 64bits too.
|
||||
/// A handle to a polygon within a navigation mesh tile.
|
||||
/// @ingroup detour
|
||||
typedef uint64_d dtPolyRef; // Edited by TC
|
||||
#ifdef DT_POLYREF64
|
||||
static const unsigned int DT_SALT_BITS = 12;
|
||||
static const unsigned int DT_TILE_BITS = 21;
|
||||
static const unsigned int DT_POLY_BITS = 31;
|
||||
typedef uint64_d dtPolyRef;
|
||||
#else
|
||||
typedef unsigned int dtPolyRef;
|
||||
#endif
|
||||
|
||||
/// A handle to a tile within a navigation mesh.
|
||||
/// @ingroup detour
|
||||
typedef uint64_d dtTileRef; // Edited by TC
|
||||
#ifdef DT_POLYREF64
|
||||
typedef uint64_d dtTileRef;
|
||||
#else
|
||||
typedef unsigned int dtTileRef;
|
||||
#endif
|
||||
|
||||
/// The maximum number of vertices per navigation polygon.
|
||||
/// @ingroup detour
|
||||
|
|
@ -90,12 +106,6 @@ static const unsigned int DT_OFFMESH_CON_BIDIR = 1;
|
|||
/// @ingroup detour
|
||||
static const int DT_MAX_AREAS = 64;
|
||||
|
||||
static const int STATIC_SALT_BITS = 12;
|
||||
static const int STATIC_TILE_BITS = 21;
|
||||
static const int STATIC_POLY_BITS = 31;
|
||||
// we cannot have over 31 bits for either tile nor poly
|
||||
// without changing polyCount to use 64bits too.
|
||||
|
||||
/// Tile flags used for various functions and fields.
|
||||
/// For an example, see dtNavMesh::addTile().
|
||||
enum dtTileFlags
|
||||
|
|
@ -120,11 +130,10 @@ enum dtStraightPathOptions
|
|||
};
|
||||
|
||||
|
||||
/// Options for dtNavMeshQuery::findPath
|
||||
/// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath
|
||||
enum dtFindPathOptions
|
||||
{
|
||||
DT_FINDPATH_LOW_QUALITY_FAR = 0x01, ///< [provisional] trade quality for performance far from the origin. The idea is that by then a new query will be issued
|
||||
DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
|
||||
DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
|
||||
};
|
||||
|
||||
/// Options for dtNavMeshQuery::raycast
|
||||
|
|
@ -148,7 +157,7 @@ enum dtPolyTypes
|
|||
};
|
||||
|
||||
|
||||
/// Defines a polyogn within a dtMeshTile object.
|
||||
/// Defines a polygon within a dtMeshTile object.
|
||||
/// @ingroup detour
|
||||
struct dtPoly
|
||||
{
|
||||
|
|
@ -303,6 +312,9 @@ struct dtMeshTile
|
|||
int dataSize; ///< Size of the tile data.
|
||||
int flags; ///< Tile flags. (See: #dtTileFlags)
|
||||
dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid.
|
||||
private:
|
||||
dtMeshTile(const dtMeshTile&);
|
||||
dtMeshTile& operator=(const dtMeshTile&);
|
||||
};
|
||||
|
||||
/// Configuration parameters used to define multi-tile navigation meshes.
|
||||
|
|
@ -513,7 +525,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.
|
||||
|
|
@ -525,12 +541,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.
|
||||
|
|
@ -539,8 +564,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.
|
||||
|
|
@ -549,8 +579,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.
|
||||
|
|
@ -559,13 +594,21 @@ 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
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
dtNavMesh(const dtNavMesh&);
|
||||
dtNavMesh& operator=(const dtNavMesh&);
|
||||
|
||||
/// Returns pointer to tile in the tile array.
|
||||
dtMeshTile* getTile(int i);
|
||||
|
|
@ -594,7 +637,7 @@ private:
|
|||
void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
|
||||
|
||||
/// Removes external links at specified side.
|
||||
void unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target);
|
||||
void unconnectLinks(dtMeshTile* tile, dtMeshTile* target);
|
||||
|
||||
|
||||
// TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.
|
||||
|
|
@ -619,9 +662,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.
|
||||
|
|
@ -642,41 +687,57 @@ void dtFreeNavMesh(dtNavMesh* navmesh);
|
|||
// a source file. It reduces clutter in the main section of the header.
|
||||
|
||||
/**
|
||||
|
||||
@typedef dtPolyRef
|
||||
@par
|
||||
|
||||
Polygon references are subject to the same invalidate/preserve/restore
|
||||
rules that apply to #dtTileRef's. If the #dtTileRef for the polygon's
|
||||
tile changes, the polygon reference becomes invalid.
|
||||
|
||||
Changing a polygon's flags, area id, etc. does not impact its polygon
|
||||
reference.
|
||||
|
||||
@typedef dtTileRef
|
||||
@par
|
||||
|
||||
The following changes will invalidate a tile reference:
|
||||
|
||||
- The referenced tile has been removed from the navigation mesh.
|
||||
- The navigation mesh has been initialized using a different set
|
||||
of #dtNavMeshParams.
|
||||
|
||||
A tile reference is preserved/restored if the tile is added to a navigation
|
||||
mesh initialized with the original #dtNavMeshParams and is added at the
|
||||
original reference location. (E.g. The lastRef parameter is used with
|
||||
dtNavMesh::addTile.)
|
||||
|
||||
Basically, if the storage structure of a tile changes, its associated
|
||||
tile reference changes.
|
||||
|
||||
|
||||
@var unsigned short dtPoly::neis[DT_VERTS_PER_POLYGON]
|
||||
@par
|
||||
|
||||
Each entry represents data for the edge starting at the vertex of the same index.
|
||||
E.g. The entry at index n represents the edge data for vertex[n] to vertex[n+1].
|
||||
|
||||
A value of zero indicates the edge has no polygon connection. (It makes up the
|
||||
border of the navigation mesh.)
|
||||
|
||||
The information can be extracted as follows:
|
||||
@code
|
||||
neighborRef = neis[n] & 0xff; // Get the neighbor polygon reference.
|
||||
|
||||
if (neis[n] & #DT_EX_LINK)
|
||||
{
|
||||
// The edge is an external (portal) edge.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@var float dtMeshHeader::bvQuantFactor
|
||||
@par
|
||||
|
||||
This value is used for converting between world and bounding volume coordinates.
|
||||
For example:
|
||||
@code
|
||||
|
|
@ -690,19 +751,27 @@ if (n->i >= 0)
|
|||
// Etc...
|
||||
}
|
||||
@endcode
|
||||
|
||||
@struct dtMeshTile
|
||||
@par
|
||||
|
||||
Tiles generally only exist within the context of a dtNavMesh object.
|
||||
|
||||
Some tile content is optional. For example, a tile may not contain any
|
||||
off-mesh connections. In this case the associated pointer will be null.
|
||||
|
||||
If a detail mesh exists it will share vertices with the base polygon mesh.
|
||||
Only the vertices unique to the detail mesh will be stored in #detailVerts.
|
||||
|
||||
@warning Tiles returned by a dtNavMesh object are not guarenteed to be populated.
|
||||
For example: The tile at a location might not have been loaded yet, or may have been removed.
|
||||
In this case, pointers will be null. So if in doubt, check the polygon count in the
|
||||
tile's header to determine if a tile has polygons defined.
|
||||
|
||||
@var float dtOffMeshConnection::pos[6]
|
||||
@par
|
||||
|
||||
For a properly built navigation mesh, vertex A will always be within the bounds of the mesh.
|
||||
Vertex B is not required to be within the bounds of the mesh.
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -128,14 +128,22 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int dataSize);
|
|||
// a source file. It reduces clutter in the main section of the header.
|
||||
|
||||
/**
|
||||
|
||||
@struct dtNavMeshCreateParams
|
||||
@par
|
||||
|
||||
This structure is used to marshal data between the Recast mesh generation pipeline and Detour navigation components.
|
||||
|
||||
See the rcPolyMesh and rcPolyMeshDetail documentation for detailed information related to mesh structure.
|
||||
|
||||
Units are usually in voxels (vx) or world units (wu). The units for voxels, grid size, and cell size
|
||||
are all based on the values of #cs and #ch.
|
||||
|
||||
The standard navigation mesh build process is to create tile data using dtCreateNavMeshData, then add the tile
|
||||
to a navigation mesh using either the dtNavMesh single tile <tt>init()</tt> function or the dtNavMesh::addTile()
|
||||
function.
|
||||
|
||||
@see dtCreateNavMeshData
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -131,6 +131,9 @@ struct dtRaycastHit
|
|||
|
||||
/// hitNormal The normal of the nearest wall hit. [(x, y, z)]
|
||||
float hitNormal[3];
|
||||
|
||||
/// The index of the edge on the final polygon where the wall was hit.
|
||||
int hitEdgeIndex;
|
||||
|
||||
/// Pointer to an array of reference ids of the visited polygons. [opt]
|
||||
dtPolyRef* path;
|
||||
|
|
@ -145,7 +148,18 @@ struct dtRaycastHit
|
|||
float pathCost;
|
||||
};
|
||||
|
||||
/// Provides custom polygon query behavior.
|
||||
/// Used by dtNavMeshQuery::queryPolygons.
|
||||
/// @ingroup detour
|
||||
class dtPolyQuery
|
||||
{
|
||||
public:
|
||||
virtual ~dtPolyQuery() { }
|
||||
|
||||
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
|
||||
/// This can be called multiple times for a single query.
|
||||
virtual void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count) = 0;
|
||||
};
|
||||
|
||||
/// Provides the ability to perform pathfinding related queries against
|
||||
/// a navigation mesh.
|
||||
|
|
@ -158,7 +172,7 @@ public:
|
|||
|
||||
/// Initializes the query object.
|
||||
/// @param[in] nav Pointer to the dtNavMesh object to use for all queries.
|
||||
/// @param[in] maxNodes Maximum number of search nodes. [Limits: 0 < value <= 65536]
|
||||
/// @param[in] maxNodes Maximum number of search nodes. [Limits: 0 < value <= 65535]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus init(const dtNavMesh* nav, const int maxNodes);
|
||||
|
||||
|
|
@ -179,7 +193,7 @@ public:
|
|||
const float* startPos, const float* endPos,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* path, int* pathCount, const int maxPath) const;
|
||||
|
||||
|
||||
/// Finds the straight path from the start to the end position within the polygon corridor.
|
||||
/// @param[in] startPos Path start position. [(x, y, z)]
|
||||
/// @param[in] endPos Path end position. [(x, y, z)]
|
||||
|
|
@ -282,6 +296,20 @@ public:
|
|||
dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
|
||||
int* resultCount, const int maxResult) const;
|
||||
|
||||
/// Gets a path from the explored nodes in the previous search.
|
||||
/// @param[in] endRef The reference id of the end polygon.
|
||||
/// @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.
|
||||
/// @param[in] maxPath The maximum number of polygons the @p path array can hold. [Limit: >= 0]
|
||||
/// @returns The status flags. Returns DT_FAILURE | DT_INVALID_PARAM if any parameter is wrong, or if
|
||||
/// @p endRef was not explored in the previous search. Returns DT_SUCCESS | DT_BUFFER_TOO_SMALL
|
||||
/// if @p path cannot contain the entire path. In this case it is filled to capacity with a partial path.
|
||||
/// Otherwise returns DT_SUCCESS.
|
||||
/// @remarks The result of this function depends on the state of the query object. For that reason it should only
|
||||
/// be used immediately after one of the two Dijkstra searches, findPolysAroundCircle or findPolysAroundShape.
|
||||
dtStatus getPathFromDijkstraSearch(dtPolyRef endRef, dtPolyRef* path, int* pathCount, int maxPath) const;
|
||||
|
||||
/// @}
|
||||
/// @name Local Query Functions
|
||||
///@{
|
||||
|
|
@ -309,6 +337,14 @@ public:
|
|||
const dtQueryFilter* filter,
|
||||
dtPolyRef* polys, int* polyCount, const int maxPolys) const;
|
||||
|
||||
/// Finds polygons that overlap the search box.
|
||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
||||
/// @param[in] extents The search distance along each axis. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[in] query The query. Polygons found will be batched together and passed to this query.
|
||||
dtStatus queryPolygons(const float* center, const float* extents,
|
||||
const dtQueryFilter* filter, dtPolyQuery* query) const;
|
||||
|
||||
/// Finds the non-overlapping navigation polygons in the local neighbourhood around the center position.
|
||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
||||
/// @param[in] centerPos The center of the query circle. [(x, y, z)]
|
||||
|
|
@ -472,13 +508,13 @@ public:
|
|||
/// @}
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator
|
||||
dtNavMeshQuery(const dtNavMeshQuery&);
|
||||
dtNavMeshQuery& operator=(const dtNavMeshQuery&);
|
||||
|
||||
/// Returns neighbour tile based on side.
|
||||
dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
|
||||
|
||||
/// 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;
|
||||
void queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
|
||||
const dtQueryFilter* filter, dtPolyQuery* query) const;
|
||||
|
||||
/// Returns portal points between two polygons.
|
||||
dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
|
||||
|
|
@ -502,6 +538,9 @@ private:
|
|||
dtStatus appendPortals(const int startIdx, const int endIdx, const float* endPos, const dtPolyRef* path,
|
||||
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
|
||||
int* straightPathCount, const int maxStraightPath, const int options) const;
|
||||
|
||||
// Gets the path leading to the specified end node.
|
||||
dtStatus getPathToNode(struct dtNode* endNode, dtPolyRef* path, int* pathCount, int maxPath) const;
|
||||
|
||||
const dtNavMesh* m_nav; ///< Pointer to navmesh data.
|
||||
|
||||
|
|
|
|||
|
|
@ -31,28 +31,26 @@ enum dtNodeFlags
|
|||
typedef unsigned short dtNodeIndex;
|
||||
static const dtNodeIndex DT_NULL_IDX = (dtNodeIndex)~0;
|
||||
|
||||
static const int DT_NODE_PARENT_BITS = 24;
|
||||
static const int DT_NODE_STATE_BITS = 2;
|
||||
struct dtNode
|
||||
{
|
||||
float pos[3]; ///< Position of the node.
|
||||
float cost; ///< Cost from previous node to current node.
|
||||
float total; ///< Cost up to the node.
|
||||
unsigned int pidx : 24; ///< Index to parent node.
|
||||
unsigned int state : 2; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
|
||||
unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags.
|
||||
dtPolyRef id; ///< Polygon ref the node corresponds to.
|
||||
float pos[3]; ///< Position of the node.
|
||||
float cost; ///< Cost from previous node to current node.
|
||||
float total; ///< Cost up to the node.
|
||||
unsigned int pidx : DT_NODE_PARENT_BITS; ///< Index to parent node.
|
||||
unsigned int state : DT_NODE_STATE_BITS; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
|
||||
unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags.
|
||||
dtPolyRef id; ///< Polygon ref the node corresponds to.
|
||||
};
|
||||
|
||||
|
||||
static const int DT_MAX_STATES_PER_NODE = 4; // number of extra states per node. See dtNode::state
|
||||
|
||||
|
||||
static const int DT_MAX_STATES_PER_NODE = 1 << DT_NODE_STATE_BITS; // number of extra states per node. See dtNode::state
|
||||
|
||||
class dtNodePool
|
||||
{
|
||||
public:
|
||||
dtNodePool(int maxNodes, int hashSize);
|
||||
~dtNodePool();
|
||||
inline void operator=(const dtNodePool&) {}
|
||||
void clear();
|
||||
|
||||
// Get a dtNode by ref and extra state information. If there is none then - allocate
|
||||
|
|
@ -64,19 +62,19 @@ public:
|
|||
inline unsigned int getNodeIdx(const dtNode* node) const
|
||||
{
|
||||
if (!node) return 0;
|
||||
return (unsigned int)(node - m_nodes)+1;
|
||||
return (unsigned int)(node - m_nodes) + 1;
|
||||
}
|
||||
|
||||
inline dtNode* getNodeAtIdx(unsigned int idx)
|
||||
{
|
||||
if (!idx) return 0;
|
||||
return &m_nodes[idx-1];
|
||||
return &m_nodes[idx - 1];
|
||||
}
|
||||
|
||||
inline const dtNode* getNodeAtIdx(unsigned int idx) const
|
||||
{
|
||||
if (!idx) return 0;
|
||||
return &m_nodes[idx-1];
|
||||
return &m_nodes[idx - 1];
|
||||
}
|
||||
|
||||
inline int getMemUsed() const
|
||||
|
|
@ -95,6 +93,9 @@ public:
|
|||
inline int getNodeCount() const { return m_nodeCount; }
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
dtNodePool(const dtNodePool&);
|
||||
dtNodePool& operator=(const dtNodePool&);
|
||||
|
||||
dtNode* m_nodes;
|
||||
dtNodeIndex* m_first;
|
||||
|
|
@ -109,17 +110,10 @@ class dtNodeQueue
|
|||
public:
|
||||
dtNodeQueue(int n);
|
||||
~dtNodeQueue();
|
||||
inline void operator=(dtNodeQueue&) {}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
m_size = 0;
|
||||
}
|
||||
inline void clear() { m_size = 0; }
|
||||
|
||||
inline dtNode* top()
|
||||
{
|
||||
return m_heap[0];
|
||||
}
|
||||
inline dtNode* top() { return m_heap[0]; }
|
||||
|
||||
inline dtNode* pop()
|
||||
{
|
||||
|
|
@ -152,12 +146,16 @@ public:
|
|||
inline int getMemUsed() const
|
||||
{
|
||||
return sizeof(*this) +
|
||||
sizeof(dtNode*)*(m_capacity+1);
|
||||
sizeof(dtNode*) * (m_capacity + 1);
|
||||
}
|
||||
|
||||
inline int getCapacity() const { return m_capacity; }
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
dtNodeQueue(const dtNodeQueue&);
|
||||
dtNodeQueue& operator=(const dtNodeQueue&);
|
||||
|
||||
void bubbleUp(int i, dtNode* node);
|
||||
void trickleDown(int i, dtNode* node);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
#include <stdlib.h>
|
||||
#include "DetourAlloc.h"
|
||||
|
||||
static void *dtAllocDefault(int size, dtAllocHint)
|
||||
static void *dtAllocDefault(size_t size, dtAllocHint)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc)
|
|||
sFreeFunc = freeFunc ? freeFunc : dtFreeDefault;
|
||||
}
|
||||
|
||||
void* dtAlloc(int size, dtAllocHint hint)
|
||||
void* dtAlloc(size_t size, dtAllocHint hint)
|
||||
{
|
||||
return sAllocFunc(size, hint);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -385,3 +385,4 @@ bool dtIntersectSegSeg2D(const float* ap, const float* aq,
|
|||
t = vperpXZ(u,w) / d;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,11 +157,15 @@ void dtFreeNavMesh(dtNavMesh* navmesh)
|
|||
|
||||
/**
|
||||
@class dtNavMesh
|
||||
|
||||
The navigation mesh consists of one or more tiles defining three primary types of structural data:
|
||||
|
||||
A polygon mesh which defines most of the navigation graph. (See rcPolyMesh for its structure.)
|
||||
A detail mesh used for determining surface height on the polygon mesh. (See rcPolyMeshDetail for its structure.)
|
||||
Off-mesh connections, which define custom point-to-point edges within the navigation graph.
|
||||
|
||||
The general build process is as follows:
|
||||
|
||||
-# Create rcPolyMesh and rcPolyMeshDetail data using the Recast build pipeline.
|
||||
-# Optionally, create off-mesh connection data.
|
||||
-# Combine the source data into a dtNavMeshCreateParams structure.
|
||||
|
|
@ -169,12 +173,15 @@ The general build process is as follows:
|
|||
-# Allocate at dtNavMesh object and initialize it. (For single tile navigation meshes,
|
||||
the tile data is loaded during this step.)
|
||||
-# For multi-tile navigation meshes, load the tile data using dtNavMesh::addTile().
|
||||
|
||||
Notes:
|
||||
|
||||
- This class is usually used in conjunction with the dtNavMeshQuery class for pathfinding.
|
||||
- Technically, all navigation meshes are tiled. A 'solo' mesh is simply a navigation mesh initialized
|
||||
to have only a single tile.
|
||||
- This class does not implement any asynchronous methods. So the ::dtStatus result of all methods will
|
||||
always contain either a success or failure flag.
|
||||
|
||||
@see dtNavMeshQuery, dtCreateNavMeshData, dtNavMeshCreateParams, #dtAllocNavMesh, #dtFreeNavMesh
|
||||
*/
|
||||
|
||||
|
|
@ -186,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;
|
||||
|
|
@ -241,11 +250,17 @@ dtStatus dtNavMesh::init(const dtNavMeshParams* params)
|
|||
m_nextFree = &m_tiles[i];
|
||||
}
|
||||
|
||||
// Edited by TC
|
||||
m_tileBits = STATIC_TILE_BITS;
|
||||
m_polyBits = STATIC_POLY_BITS;
|
||||
m_saltBits = STATIC_SALT_BITS;
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +304,7 @@ int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
|
|||
if (!tile) return 0;
|
||||
|
||||
float amin[2], amax[2];
|
||||
calcSlabEndPoints(va,vb, amin,amax, side);
|
||||
calcSlabEndPoints(va, vb, amin, amax, side);
|
||||
const float apos = getSlabCoord(va, side);
|
||||
|
||||
// Remove links pointing to 'side' and compact the links array.
|
||||
|
|
@ -335,7 +350,7 @@ int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
|
|||
return n;
|
||||
}
|
||||
|
||||
void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target)
|
||||
void dtNavMesh::unconnectLinks(dtMeshTile* tile, dtMeshTile* target)
|
||||
{
|
||||
if (!tile || !target) return;
|
||||
|
||||
|
|
@ -348,10 +363,9 @@ void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target)
|
|||
unsigned int pj = DT_NULL_LINK;
|
||||
while (j != DT_NULL_LINK)
|
||||
{
|
||||
if (tile->links[j].side != 0xff &&
|
||||
decodePolyIdTile(tile->links[j].ref) == targetNum)
|
||||
if (decodePolyIdTile(tile->links[j].ref) == targetNum)
|
||||
{
|
||||
// Revove link.
|
||||
// Remove link.
|
||||
unsigned int nj = tile->links[j].next;
|
||||
if (pj == DT_NULL_LINK)
|
||||
poly->firstLink = nj;
|
||||
|
|
@ -637,9 +651,9 @@ void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* close
|
|||
if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
|
||||
{
|
||||
// Point is outside the polygon, dtClamp to nearest edge.
|
||||
float dmin = FLT_MAX;
|
||||
int imin = -1;
|
||||
for (int i = 0; i < nv; ++i)
|
||||
float dmin = edged[0];
|
||||
int imin = 0;
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
if (edged[i] < dmin)
|
||||
{
|
||||
|
|
@ -823,6 +837,11 @@ int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, co
|
|||
/// tile will be restored to the same values they were before the tile was
|
||||
/// removed.
|
||||
///
|
||||
/// The nav mesh assumes exclusive access to the data passed and will make
|
||||
/// changes to the dynamic portion of the data. For that reason the data
|
||||
/// should not be reused in other nav meshes until the tile has been successfully
|
||||
/// removed from this nav mesh.
|
||||
///
|
||||
/// @see dtCreateNavMeshData, #removeTile
|
||||
dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
|
||||
dtTileRef lastRef, dtTileRef* result)
|
||||
|
|
@ -898,14 +917,14 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
|
|||
const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
|
||||
|
||||
unsigned char* d = data + headerSize;
|
||||
tile->verts = (float*)d; d += vertsSize;
|
||||
tile->polys = (dtPoly*)d; d += polysSize;
|
||||
tile->links = (dtLink*)d; d += linksSize;
|
||||
tile->detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
|
||||
tile->detailVerts = (float*)d; d += detailVertsSize;
|
||||
tile->detailTris = (unsigned char*)d; d += detailTrisSize;
|
||||
tile->bvTree = (dtBVNode*)d; d += bvtreeSize;
|
||||
tile->offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
|
||||
tile->verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
|
||||
tile->polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
|
||||
tile->links = dtGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
|
||||
tile->detailMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
|
||||
tile->detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
|
||||
tile->detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
|
||||
tile->bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
|
||||
tile->offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
|
||||
|
||||
// If there are no items in the bvtree, reset the tree pointer.
|
||||
if (!bvtreeSize)
|
||||
|
|
@ -924,7 +943,10 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
|
|||
tile->flags = flags;
|
||||
|
||||
connectIntLinks(tile);
|
||||
|
||||
// Base off-mesh connections to their starting polygons and connect connections inside the tile.
|
||||
baseOffMeshLinks(tile);
|
||||
connectExtOffMeshLinks(tile, tile, -1);
|
||||
|
||||
// Create connections with neighbour tiles.
|
||||
static const int MAX_NEIS = 32;
|
||||
|
|
@ -935,11 +957,11 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
|
|||
nneis = getTilesAt(header->x, header->y, neis, MAX_NEIS);
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
{
|
||||
if (neis[j] != tile)
|
||||
{
|
||||
connectExtLinks(tile, neis[j], -1);
|
||||
connectExtLinks(neis[j], tile, -1);
|
||||
}
|
||||
if (neis[j] == tile)
|
||||
continue;
|
||||
|
||||
connectExtLinks(tile, neis[j], -1);
|
||||
connectExtLinks(neis[j], tile, -1);
|
||||
connectExtOffMeshLinks(tile, neis[j], -1);
|
||||
connectExtOffMeshLinks(neis[j], tile, -1);
|
||||
}
|
||||
|
|
@ -1177,25 +1199,24 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz
|
|||
}
|
||||
|
||||
// Remove connections to neighbour tiles.
|
||||
// Create connections with neighbour tiles.
|
||||
static const int MAX_NEIS = 32;
|
||||
dtMeshTile* neis[MAX_NEIS];
|
||||
int nneis;
|
||||
|
||||
// Connect with layers in current tile.
|
||||
// Disconnect from other layers in current tile.
|
||||
nneis = getTilesAt(tile->header->x, tile->header->y, neis, MAX_NEIS);
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
{
|
||||
if (neis[j] == tile) continue;
|
||||
unconnectExtLinks(neis[j], tile);
|
||||
unconnectLinks(neis[j], tile);
|
||||
}
|
||||
|
||||
// Connect with neighbour tiles.
|
||||
// Disconnect from neighbour tiles.
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
nneis = getNeighbourTilesAt(tile->header->x, tile->header->y, i, neis, MAX_NEIS);
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
unconnectExtLinks(neis[j], tile);
|
||||
unconnectLinks(neis[j], tile);
|
||||
}
|
||||
|
||||
// Reset tile.
|
||||
|
|
@ -1227,7 +1248,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++;
|
||||
|
||||
|
|
@ -1300,8 +1325,8 @@ dtStatus dtNavMesh::storeTileState(const dtMeshTile* tile, unsigned char* data,
|
|||
if (maxDataSize < sizeReq)
|
||||
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
||||
|
||||
dtTileState* tileState = (dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
|
||||
dtPolyState* polyStates = (dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
|
||||
dtTileState* tileState = dtGetThenAdvanceBufferPointer<dtTileState>(data, dtAlign4(sizeof(dtTileState)));
|
||||
dtPolyState* polyStates = dtGetThenAdvanceBufferPointer<dtPolyState>(data, dtAlign4(sizeof(dtPolyState) * tile->header->polyCount));
|
||||
|
||||
// Store tile state.
|
||||
tileState->magic = DT_NAVMESH_STATE_MAGIC;
|
||||
|
|
@ -1332,8 +1357,8 @@ dtStatus dtNavMesh::restoreTileState(dtMeshTile* tile, const unsigned char* data
|
|||
if (maxDataSize < sizeReq)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
const dtTileState* tileState = (const dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
|
||||
const dtPolyState* polyStates = (const dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
|
||||
const dtTileState* tileState = dtGetThenAdvanceBufferPointer<const dtTileState>(data, dtAlign4(sizeof(dtTileState)));
|
||||
const dtPolyState* polyStates = dtGetThenAdvanceBufferPointer<const dtPolyState>(data, dtAlign4(sizeof(dtPolyState) * tile->header->polyCount));
|
||||
|
||||
// Check that the restore is possible.
|
||||
if (tileState->magic != DT_NAVMESH_STATE_MAGIC)
|
||||
|
|
@ -1494,3 +1519,4 @@ dtStatus dtNavMesh::getPolyArea(dtPolyRef ref, unsigned char* resultArea) const
|
|||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ inline int longestAxis(unsigned short x, unsigned short y, unsigned short z)
|
|||
if (z > maxVal)
|
||||
{
|
||||
axis = 2;
|
||||
maxVal = z;
|
||||
}
|
||||
return axis;
|
||||
}
|
||||
|
|
@ -421,15 +420,16 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
|
|||
memset(data, 0, dataSize);
|
||||
|
||||
unsigned char* d = data;
|
||||
dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize;
|
||||
float* navVerts = (float*)d; d += vertsSize;
|
||||
dtPoly* navPolys = (dtPoly*)d; d += polysSize;
|
||||
d += linksSize;
|
||||
dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
|
||||
float* navDVerts = (float*)d; d += detailVertsSize;
|
||||
unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
|
||||
dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize;
|
||||
dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize;
|
||||
|
||||
dtMeshHeader* header = dtGetThenAdvanceBufferPointer<dtMeshHeader>(d, headerSize);
|
||||
float* navVerts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
|
||||
dtPoly* navPolys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
|
||||
d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load.
|
||||
dtPolyDetail* navDMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
|
||||
float* navDVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
|
||||
unsigned char* navDTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
|
||||
dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize);
|
||||
dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize);
|
||||
|
||||
|
||||
// Store header
|
||||
|
|
@ -705,14 +705,16 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
|
|||
const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
|
||||
|
||||
unsigned char* d = data + headerSize;
|
||||
float* verts = (float*)d; d += vertsSize;
|
||||
dtPoly* polys = (dtPoly*)d; d += polysSize;
|
||||
/*dtLink* links = (dtLink*)d;*/ d += linksSize;
|
||||
dtPolyDetail* detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
|
||||
float* detailVerts = (float*)d; d += detailVertsSize;
|
||||
/*unsigned char* detailTris = (unsigned char*)d;*/ d += detailTrisSize;
|
||||
dtBVNode* bvTree = (dtBVNode*)d; d += bvtreeSize;
|
||||
dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
|
||||
float* verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
|
||||
dtPoly* polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
|
||||
d += linksSize; // Ignore links; they technically should be endian-swapped but all their data is overwritten on load anyway.
|
||||
//dtLink* links = dtGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
|
||||
dtPolyDetail* detailMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
|
||||
float* detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
|
||||
d += detailTrisSize; // Ignore detail tris; single bytes can't be endian-swapped.
|
||||
//unsigned char* detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
|
||||
dtBVNode* bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
|
||||
dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
|
||||
|
||||
// Vertices
|
||||
for (int i = 0; i < header->vertCount*3; ++i)
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ inline float dtQueryFilter::getCost(const float* pa, const float* pb,
|
|||
}
|
||||
#endif
|
||||
|
||||
// Edited by TC
|
||||
static const float H_SCALE = 2.0f; // Search heuristic scale.
|
||||
|
||||
|
||||
|
|
@ -166,6 +165,9 @@ dtNavMeshQuery::~dtNavMeshQuery()
|
|||
/// This function can be used multiple times.
|
||||
dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes)
|
||||
{
|
||||
if (maxNodes > DT_NULL_IDX || maxNodes > (1 << DT_NODE_PARENT_BITS) - 1)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
m_nav = nav;
|
||||
|
||||
if (!m_nodePool || m_nodePool->getMaxNodes() < maxNodes)
|
||||
|
|
@ -196,7 +198,6 @@ dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes)
|
|||
m_tinyNodePool->clear();
|
||||
}
|
||||
|
||||
// TODO: check the open list size too.
|
||||
if (!m_openList || m_openList->getCapacity() < maxNodes)
|
||||
{
|
||||
if (m_openList)
|
||||
|
|
@ -541,9 +542,9 @@ dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, flo
|
|||
if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
|
||||
{
|
||||
// Point is outside the polygon, dtClamp to nearest edge.
|
||||
float dmin = FLT_MAX;
|
||||
int imin = -1;
|
||||
for (int i = 0; i < nv; ++i)
|
||||
float dmin = edged[0];
|
||||
int imin = 0;
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
if (edged[i] < dmin)
|
||||
{
|
||||
|
|
@ -627,9 +628,9 @@ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float*
|
|||
else
|
||||
{
|
||||
// Point is outside the polygon, dtClamp to nearest edge.
|
||||
float dmin = FLT_MAX;
|
||||
int imin = -1;
|
||||
for (int i = 0; i < nv; ++i)
|
||||
float dmin = edged[0];
|
||||
int imin = 0;
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
if (edged[i] < dmin)
|
||||
{
|
||||
|
|
@ -698,78 +699,98 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h
|
|||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
class dtFindNearestPolyQuery : public dtPolyQuery
|
||||
{
|
||||
const dtNavMeshQuery* m_query;
|
||||
const float* m_center;
|
||||
float m_nearestDistanceSqr;
|
||||
dtPolyRef m_nearestRef;
|
||||
float m_nearestPoint[3];
|
||||
|
||||
public:
|
||||
dtFindNearestPolyQuery(const dtNavMeshQuery* query, const float* center)
|
||||
: m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint()
|
||||
{
|
||||
}
|
||||
|
||||
dtPolyRef nearestRef() const { return m_nearestRef; }
|
||||
const float* nearestPoint() const { return m_nearestPoint; }
|
||||
|
||||
void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count)
|
||||
{
|
||||
dtIgnoreUnused(polys);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
dtPolyRef ref = refs[i];
|
||||
float closestPtPoly[3];
|
||||
float diff[3];
|
||||
bool posOverPoly = false;
|
||||
float d;
|
||||
m_query->closestPointOnPoly(ref, m_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, m_center, closestPtPoly);
|
||||
if (posOverPoly)
|
||||
{
|
||||
d = dtAbs(diff[1]) - tile->header->walkableClimb;
|
||||
d = d > 0 ? d*d : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
d = dtVlenSqr(diff);
|
||||
}
|
||||
|
||||
if (d < m_nearestDistanceSqr)
|
||||
{
|
||||
dtVcopy(m_nearestPoint, closestPtPoly);
|
||||
|
||||
m_nearestDistanceSqr = d;
|
||||
m_nearestRef = ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// @note If the search box does not intersect any polygons the search will
|
||||
/// return #DT_SUCCESS, but @p nearestRef will be zero. So if in doubt, check
|
||||
/// @p nearestRef before using @p nearestPt.
|
||||
///
|
||||
/// @warning This function is not suitable for large area searches. If the search
|
||||
/// extents overlaps more than MAX_SEARCH (128) polygons it may return an invalid result.
|
||||
///
|
||||
dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* extents,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* nearestRef, float* nearestPt) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
|
||||
*nearestRef = 0;
|
||||
|
||||
// Get nearby polygons from proximity grid.
|
||||
const int MAX_SEARCH = 128;
|
||||
dtPolyRef polys[MAX_SEARCH];
|
||||
int polyCount = 0;
|
||||
if (dtStatusFailed(queryPolygons(center, extents, filter, polys, &polyCount, MAX_SEARCH)))
|
||||
if (!nearestRef)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
// 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];
|
||||
float closestPtPoly[3];
|
||||
float diff[3];
|
||||
bool posOverPoly = false;
|
||||
float d = 0;
|
||||
closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly);
|
||||
dtFindNearestPolyQuery query(this, center);
|
||||
|
||||
// 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)
|
||||
dtVcopy(nearestPt, closestPtPoly);
|
||||
nearestDistanceSqr = d;
|
||||
nearest = ref;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearestRef)
|
||||
*nearestRef = nearest;
|
||||
dtStatus status = queryPolygons(center, extents, filter, &query);
|
||||
if (dtStatusFailed(status))
|
||||
return status;
|
||||
|
||||
*nearestRef = query.nearestRef();
|
||||
// Only override nearestPt if we actually found a poly so the nearest point
|
||||
// is valid.
|
||||
if (nearestPt && *nearestRef)
|
||||
dtVcopy(nearestPt, query.nearestPoint());
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* polys, const int maxPolys) const
|
||||
void dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
|
||||
const dtQueryFilter* filter, dtPolyQuery* query) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
static const int batchSize = 32;
|
||||
dtPolyRef polyRefs[batchSize];
|
||||
dtPoly* polys[batchSize];
|
||||
int n = 0;
|
||||
|
||||
if (tile->bvTree)
|
||||
{
|
||||
|
|
@ -778,7 +799,7 @@ int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmi
|
|||
const float* tbmin = tile->header->bmin;
|
||||
const float* tbmax = tile->header->bmax;
|
||||
const float qfac = tile->header->bvQuantFactor;
|
||||
|
||||
|
||||
// Calculate quantized box
|
||||
unsigned short bmin[3], bmax[3];
|
||||
// dtClamp query box to world box.
|
||||
|
|
@ -795,25 +816,34 @@ int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmi
|
|||
bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
|
||||
bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
|
||||
bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
|
||||
|
||||
|
||||
// Traverse tree
|
||||
const dtPolyRef base = m_nav->getPolyRefBase(tile);
|
||||
int n = 0;
|
||||
while (node < end)
|
||||
{
|
||||
const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
|
||||
const bool isLeafNode = node->i >= 0;
|
||||
|
||||
|
||||
if (isLeafNode && overlap)
|
||||
{
|
||||
dtPolyRef ref = base | (dtPolyRef)node->i;
|
||||
if (filter->passFilter(ref, tile, &tile->polys[node->i]))
|
||||
{
|
||||
if (n < maxPolys)
|
||||
polys[n++] = ref;
|
||||
polyRefs[n] = ref;
|
||||
polys[n] = &tile->polys[node->i];
|
||||
|
||||
if (n == batchSize - 1)
|
||||
{
|
||||
query->process(tile, polys, polyRefs, batchSize);
|
||||
n = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (overlap || isLeafNode)
|
||||
node++;
|
||||
else
|
||||
|
|
@ -822,17 +852,14 @@ int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmi
|
|||
node += escapeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
else
|
||||
{
|
||||
float bmin[3], bmax[3];
|
||||
int n = 0;
|
||||
const dtPolyRef base = m_nav->getPolyRefBase(tile);
|
||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
||||
{
|
||||
const dtPoly* p = &tile->polys[i];
|
||||
dtPoly* p = &tile->polys[i];
|
||||
// Do not return off-mesh connection polygons.
|
||||
if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
continue;
|
||||
|
|
@ -850,16 +877,63 @@ int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmi
|
|||
dtVmin(bmin, v);
|
||||
dtVmax(bmax, v);
|
||||
}
|
||||
if (dtOverlapBounds(qmin,qmax, bmin,bmax))
|
||||
if (dtOverlapBounds(qmin, qmax, bmin, bmax))
|
||||
{
|
||||
if (n < maxPolys)
|
||||
polys[n++] = ref;
|
||||
polyRefs[n] = ref;
|
||||
polys[n] = p;
|
||||
|
||||
if (n == batchSize - 1)
|
||||
{
|
||||
query->process(tile, polys, polyRefs, batchSize);
|
||||
n = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Process the last polygons that didn't make a full batch.
|
||||
if (n > 0)
|
||||
query->process(tile, polys, polyRefs, n);
|
||||
}
|
||||
|
||||
class dtCollectPolysQuery : public dtPolyQuery
|
||||
{
|
||||
dtPolyRef* m_polys;
|
||||
const int m_maxPolys;
|
||||
int m_numCollected;
|
||||
bool m_overflow;
|
||||
|
||||
public:
|
||||
dtCollectPolysQuery(dtPolyRef* polys, const int maxPolys)
|
||||
: m_polys(polys), m_maxPolys(maxPolys), m_numCollected(0), m_overflow(false)
|
||||
{
|
||||
}
|
||||
|
||||
int numCollected() const { return m_numCollected; }
|
||||
bool overflowed() const { return m_overflow; }
|
||||
|
||||
void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count)
|
||||
{
|
||||
dtIgnoreUnused(tile);
|
||||
dtIgnoreUnused(polys);
|
||||
|
||||
int numLeft = m_maxPolys - m_numCollected;
|
||||
int toCopy = count;
|
||||
if (toCopy > numLeft)
|
||||
{
|
||||
m_overflow = true;
|
||||
toCopy = numLeft;
|
||||
}
|
||||
|
||||
memcpy(m_polys + m_numCollected, refs, (size_t)toCopy * sizeof(dtPolyRef));
|
||||
m_numCollected += toCopy;
|
||||
}
|
||||
};
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// If no polygons are found, the function will return #DT_SUCCESS with a
|
||||
|
|
@ -872,9 +946,35 @@ int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmi
|
|||
dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* polys, int* polyCount, const int maxPolys) const
|
||||
{
|
||||
if (!polys || !polyCount || maxPolys < 0)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
dtCollectPolysQuery collector(polys, maxPolys);
|
||||
|
||||
dtStatus status = queryPolygons(center, extents, filter, &collector);
|
||||
if (dtStatusFailed(status))
|
||||
return status;
|
||||
|
||||
*polyCount = collector.numCollected();
|
||||
return collector.overflowed() ? DT_SUCCESS | DT_BUFFER_TOO_SMALL : DT_SUCCESS;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The query will be invoked with batches of polygons. Polygons passed
|
||||
/// to the query have bounding boxes that overlap with the center and extents
|
||||
/// passed to this function. The dtPolyQuery::process function is invoked multiple
|
||||
/// times until all overlapping polygons have been processed.
|
||||
///
|
||||
dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents,
|
||||
const dtQueryFilter* filter, dtPolyQuery* query) const
|
||||
{
|
||||
dtAssert(m_nav);
|
||||
|
||||
|
||||
if (!center || !extents || !filter || !query)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
dtVsub(bmin, center, extents);
|
||||
dtVadd(bmax, center, extents);
|
||||
|
|
@ -887,7 +987,6 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents
|
|||
static const int MAX_NEIS = 32;
|
||||
const dtMeshTile* neis[MAX_NEIS];
|
||||
|
||||
int n = 0;
|
||||
for (int y = miny; y <= maxy; ++y)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
|
|
@ -895,16 +994,10 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents
|
|||
const int nneis = m_nav->getTilesAt(x,y,neis,MAX_NEIS);
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
{
|
||||
n += queryPolygonsInTile(neis[j], bmin, bmax, filter, polys+n, maxPolys-n);
|
||||
if (n >= maxPolys)
|
||||
{
|
||||
*polyCount = n;
|
||||
return DT_SUCCESS | DT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
queryPolygonsInTile(neis[j], bmin, bmax, filter, query);
|
||||
}
|
||||
}
|
||||
}
|
||||
*polyCount = n;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
|
@ -929,18 +1022,14 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
|
|||
dtAssert(m_nodePool);
|
||||
dtAssert(m_openList);
|
||||
|
||||
*pathCount = 0;
|
||||
|
||||
if (!startRef || !endRef)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
if (!maxPath)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
if (pathCount)
|
||||
*pathCount = 0;
|
||||
|
||||
// Validate input
|
||||
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
|
||||
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) ||
|
||||
!startPos || !endPos || !filter || maxPath <= 0 || !path || !pathCount)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
|
||||
if (startRef == endRef)
|
||||
{
|
||||
path[0] = startRef;
|
||||
|
|
@ -963,7 +1052,7 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
|
|||
dtNode* lastBestNode = startNode;
|
||||
float lastBestNodeCost = startNode->total;
|
||||
|
||||
dtStatus status = DT_SUCCESS;
|
||||
bool outOfNodes = false;
|
||||
|
||||
while (!m_openList->empty())
|
||||
{
|
||||
|
|
@ -1021,7 +1110,7 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
|
|||
dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide);
|
||||
if (!neighbourNode)
|
||||
{
|
||||
status |= DT_OUT_OF_NODES;
|
||||
outOfNodes = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1100,42 +1189,59 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dtStatus status = getPathToNode(lastBestNode, path, pathCount, maxPath);
|
||||
|
||||
if (lastBestNode->id != endRef)
|
||||
status |= DT_PARTIAL_RESULT;
|
||||
|
||||
// Reverse the path.
|
||||
dtNode* prev = 0;
|
||||
dtNode* node = lastBestNode;
|
||||
do
|
||||
{
|
||||
dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
|
||||
node->pidx = m_nodePool->getNodeIdx(prev);
|
||||
prev = node;
|
||||
node = next;
|
||||
}
|
||||
while (node);
|
||||
|
||||
// Store path
|
||||
node = prev;
|
||||
int n = 0;
|
||||
do
|
||||
{
|
||||
path[n++] = node->id;
|
||||
if (n >= maxPath)
|
||||
{
|
||||
status |= DT_BUFFER_TOO_SMALL;
|
||||
break;
|
||||
}
|
||||
node = m_nodePool->getNodeAtIdx(node->pidx);
|
||||
}
|
||||
while (node);
|
||||
|
||||
*pathCount = n;
|
||||
|
||||
if (outOfNodes)
|
||||
status |= DT_OUT_OF_NODES;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
dtStatus dtNavMeshQuery::getPathToNode(dtNode* endNode, dtPolyRef* path, int* pathCount, int maxPath) const
|
||||
{
|
||||
// Find the length of the entire path.
|
||||
dtNode* curNode = endNode;
|
||||
int length = 0;
|
||||
do
|
||||
{
|
||||
length++;
|
||||
curNode = m_nodePool->getNodeAtIdx(curNode->pidx);
|
||||
} while (curNode);
|
||||
|
||||
// If the path cannot be fully stored then advance to the last node we will be able to store.
|
||||
curNode = endNode;
|
||||
int writeCount;
|
||||
for (writeCount = length; writeCount > maxPath; writeCount--)
|
||||
{
|
||||
dtAssert(curNode);
|
||||
|
||||
curNode = m_nodePool->getNodeAtIdx(curNode->pidx);
|
||||
}
|
||||
|
||||
// Write path
|
||||
for (int i = writeCount - 1; i >= 0; i--)
|
||||
{
|
||||
dtAssert(curNode);
|
||||
|
||||
path[i] = curNode->id;
|
||||
curNode = m_nodePool->getNodeAtIdx(curNode->pidx);
|
||||
}
|
||||
|
||||
dtAssert(!curNode);
|
||||
|
||||
*pathCount = dtMin(length, maxPath);
|
||||
|
||||
if (length > maxPath)
|
||||
return DT_SUCCESS | DT_BUFFER_TOO_SMALL;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// @warning Calling any non-slice methods before calling finalizeSlicedFindPath()
|
||||
|
|
@ -1628,10 +1734,17 @@ dtStatus dtNavMeshQuery::appendVertex(const float* pos, const unsigned char flag
|
|||
if (straightPathRefs)
|
||||
straightPathRefs[(*straightPathCount)] = ref;
|
||||
(*straightPathCount)++;
|
||||
// If reached end of path or there is no space to append more vertices, return.
|
||||
if (flags == DT_STRAIGHTPATH_END || (*straightPathCount) >= maxStraightPath)
|
||||
|
||||
// If there is no space to append more vertices, return.
|
||||
if ((*straightPathCount) >= maxStraightPath)
|
||||
{
|
||||
return DT_SUCCESS | (((*straightPathCount) >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
|
||||
return DT_SUCCESS | DT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
// If reached end of path, return.
|
||||
if (flags == DT_STRAIGHTPATH_END)
|
||||
{
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
}
|
||||
return DT_IN_PROGRESS;
|
||||
|
|
@ -1756,10 +1869,12 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
|
|||
for (int i = 0; i < pathSize; ++i)
|
||||
{
|
||||
float left[3], right[3];
|
||||
unsigned char fromType, toType;
|
||||
unsigned char toType;
|
||||
|
||||
if (i+1 < pathSize)
|
||||
{
|
||||
unsigned char fromType; // fromType is ignored.
|
||||
|
||||
// Next portal.
|
||||
if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType)))
|
||||
{
|
||||
|
|
@ -1775,12 +1890,14 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
|
|||
// Apeend portals along the current straight path segment.
|
||||
if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
|
||||
{
|
||||
stat = appendPortals(apexIndex, i, closestEndPos, path,
|
||||
// Ignore status return value as we're just about to return anyway.
|
||||
appendPortals(apexIndex, i, closestEndPos, path,
|
||||
straightPath, straightPathFlags, straightPathRefs,
|
||||
straightPathCount, maxStraightPath, options);
|
||||
}
|
||||
|
||||
stat = appendVertex(closestEndPos, 0, path[i],
|
||||
// Ignore status return value as we're just about to return anyway.
|
||||
appendVertex(closestEndPos, 0, path[i],
|
||||
straightPath, straightPathFlags, straightPathRefs,
|
||||
straightPathCount, maxStraightPath);
|
||||
|
||||
|
|
@ -1801,7 +1918,7 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
|
|||
dtVcopy(left, closestEndPos);
|
||||
dtVcopy(right, closestEndPos);
|
||||
|
||||
fromType = toType = DT_POLYTYPE_GROUND;
|
||||
toType = DT_POLYTYPE_GROUND;
|
||||
}
|
||||
|
||||
// Right vertex.
|
||||
|
|
@ -1918,7 +2035,8 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
|
|||
}
|
||||
}
|
||||
|
||||
stat = appendVertex(closestEndPos, DT_STRAIGHTPATH_END, 0,
|
||||
// Ignore status return value as we're just about to return anyway.
|
||||
appendVertex(closestEndPos, DT_STRAIGHTPATH_END, 0,
|
||||
straightPath, straightPathFlags, straightPathRefs,
|
||||
straightPathCount, maxStraightPath);
|
||||
|
||||
|
|
@ -2389,10 +2507,10 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
|
|||
|
||||
const dtMeshTile* prevTile, *tile, *nextTile;
|
||||
const dtPoly* prevPoly, *poly, *nextPoly;
|
||||
dtPolyRef curRef, nextRef;
|
||||
dtPolyRef curRef;
|
||||
|
||||
// The API input has been checked already, skip checking internal data.
|
||||
nextRef = curRef = startRef;
|
||||
curRef = startRef;
|
||||
tile = 0;
|
||||
poly = 0;
|
||||
m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
|
||||
|
|
@ -2421,6 +2539,9 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
|
|||
hit->pathCount = n;
|
||||
return status;
|
||||
}
|
||||
|
||||
hit->hitEdgeIndex = segMax;
|
||||
|
||||
// Keep track of furthest t so far.
|
||||
if (tmax > hit->t)
|
||||
hit->t = tmax;
|
||||
|
|
@ -2444,7 +2565,7 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
|
|||
}
|
||||
|
||||
// Follow neighbours.
|
||||
nextRef = 0;
|
||||
dtPolyRef nextRef = 0;
|
||||
|
||||
for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
|
||||
{
|
||||
|
|
@ -2635,20 +2756,6 @@ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float*
|
|||
dtStatus status = DT_SUCCESS;
|
||||
|
||||
int n = 0;
|
||||
if (n < maxResult)
|
||||
{
|
||||
if (resultRef)
|
||||
resultRef[n] = startNode->id;
|
||||
if (resultParent)
|
||||
resultParent[n] = 0;
|
||||
if (resultCost)
|
||||
resultCost[n] = 0;
|
||||
++n;
|
||||
}
|
||||
else
|
||||
{
|
||||
status |= DT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
const float radiusSqr = dtSqr(radius);
|
||||
|
||||
|
|
@ -2673,6 +2780,21 @@ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float*
|
|||
parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
|
||||
if (parentRef)
|
||||
m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
|
||||
|
||||
if (n < maxResult)
|
||||
{
|
||||
if (resultRef)
|
||||
resultRef[n] = bestRef;
|
||||
if (resultParent)
|
||||
resultParent[n] = parentRef;
|
||||
if (resultCost)
|
||||
resultCost[n] = bestNode->total;
|
||||
++n;
|
||||
}
|
||||
else
|
||||
{
|
||||
status |= DT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
|
||||
{
|
||||
|
|
@ -2716,14 +2838,19 @@ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float*
|
|||
if (neighbourNode->flags == 0)
|
||||
dtVlerp(neighbourNode->pos, va, vb, 0.5f);
|
||||
|
||||
const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
|
||||
float cost = filter->getCost(
|
||||
bestNode->pos, neighbourNode->pos,
|
||||
parentRef, parentTile, parentPoly,
|
||||
bestRef, bestTile, bestPoly,
|
||||
neighbourRef, neighbourTile, neighbourPoly);
|
||||
|
||||
const float total = bestNode->total + cost;
|
||||
|
||||
// The node is already in open list and the new result is worse, skip.
|
||||
if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
|
||||
continue;
|
||||
|
||||
neighbourNode->id = neighbourRef;
|
||||
neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
|
||||
neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
|
||||
neighbourNode->total = total;
|
||||
|
||||
|
|
@ -2733,20 +2860,6 @@ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float*
|
|||
}
|
||||
else
|
||||
{
|
||||
if (n < maxResult)
|
||||
{
|
||||
if (resultRef)
|
||||
resultRef[n] = neighbourNode->id;
|
||||
if (resultParent)
|
||||
resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
|
||||
if (resultCost)
|
||||
resultCost[n] = neighbourNode->total;
|
||||
++n;
|
||||
}
|
||||
else
|
||||
{
|
||||
status |= DT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
neighbourNode->flags = DT_NODE_OPEN;
|
||||
m_openList->push(neighbourNode);
|
||||
}
|
||||
|
|
@ -2815,20 +2928,6 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v
|
|||
dtStatus status = DT_SUCCESS;
|
||||
|
||||
int n = 0;
|
||||
if (n < maxResult)
|
||||
{
|
||||
if (resultRef)
|
||||
resultRef[n] = startNode->id;
|
||||
if (resultParent)
|
||||
resultParent[n] = 0;
|
||||
if (resultCost)
|
||||
resultCost[n] = 0;
|
||||
++n;
|
||||
}
|
||||
else
|
||||
{
|
||||
status |= DT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
while (!m_openList->empty())
|
||||
{
|
||||
|
|
@ -2851,6 +2950,22 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v
|
|||
parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
|
||||
if (parentRef)
|
||||
m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
|
||||
|
||||
if (n < maxResult)
|
||||
{
|
||||
if (resultRef)
|
||||
resultRef[n] = bestRef;
|
||||
if (resultParent)
|
||||
resultParent[n] = parentRef;
|
||||
if (resultCost)
|
||||
resultCost[n] = bestNode->total;
|
||||
|
||||
++n;
|
||||
}
|
||||
else
|
||||
{
|
||||
status |= DT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
|
||||
{
|
||||
|
|
@ -2896,14 +3011,19 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v
|
|||
if (neighbourNode->flags == 0)
|
||||
dtVlerp(neighbourNode->pos, va, vb, 0.5f);
|
||||
|
||||
const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
|
||||
float cost = filter->getCost(
|
||||
bestNode->pos, neighbourNode->pos,
|
||||
parentRef, parentTile, parentPoly,
|
||||
bestRef, bestTile, bestPoly,
|
||||
neighbourRef, neighbourTile, neighbourPoly);
|
||||
|
||||
const float total = bestNode->total + cost;
|
||||
|
||||
// The node is already in open list and the new result is worse, skip.
|
||||
if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
|
||||
continue;
|
||||
|
||||
neighbourNode->id = neighbourRef;
|
||||
neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
|
||||
neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
|
||||
neighbourNode->total = total;
|
||||
|
||||
|
|
@ -2913,20 +3033,6 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v
|
|||
}
|
||||
else
|
||||
{
|
||||
if (n < maxResult)
|
||||
{
|
||||
if (resultRef)
|
||||
resultRef[n] = neighbourNode->id;
|
||||
if (resultParent)
|
||||
resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
|
||||
if (resultCost)
|
||||
resultCost[n] = neighbourNode->total;
|
||||
++n;
|
||||
}
|
||||
else
|
||||
{
|
||||
status |= DT_BUFFER_TOO_SMALL;
|
||||
}
|
||||
neighbourNode->flags = DT_NODE_OPEN;
|
||||
m_openList->push(neighbourNode);
|
||||
}
|
||||
|
|
@ -2938,6 +3044,21 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v
|
|||
return status;
|
||||
}
|
||||
|
||||
dtStatus dtNavMeshQuery::getPathFromDijkstraSearch(dtPolyRef endRef, dtPolyRef* path, int* pathCount, int maxPath) const
|
||||
{
|
||||
if (!m_nav->isValidPolyRef(endRef) || !path || !pathCount || maxPath < 0)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
*pathCount = 0;
|
||||
|
||||
dtNode* endNode;
|
||||
if (m_nodePool->findNodes(endRef, &endNode, 1) != 1 ||
|
||||
(endNode->flags & DT_NODE_CLOSED) == 0)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
return getPathToNode(endNode, path, pathCount, maxPath);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// This method is optimized for a small search radius and small number of result
|
||||
|
|
|
|||
|
|
@ -22,17 +22,30 @@
|
|||
#include "DetourCommon.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef DT_POLYREF64
|
||||
// From Thomas Wang, https://gist.github.com/badboy/6267743
|
||||
inline unsigned int dtHashRef(dtPolyRef a)
|
||||
{
|
||||
// Edited by TC
|
||||
a = (~a) + (a << 18);
|
||||
a = a ^ (a >> 31);
|
||||
a = a * 21;
|
||||
a = a ^ (a >> 11);
|
||||
a = a + (a << 6);
|
||||
a = a ^ (a >> 22);
|
||||
return (unsigned int)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);
|
||||
a ^= (a>>10);
|
||||
a += (a<<3);
|
||||
a ^= (a>>6);
|
||||
a += ~(a<<11);
|
||||
a ^= (a>>16);
|
||||
return (unsigned int)a;
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
dtNodePool::dtNodePool(int maxNodes, int hashSize) :
|
||||
|
|
@ -44,7 +57,9 @@ dtNodePool::dtNodePool(int maxNodes, int hashSize) :
|
|||
m_nodeCount(0)
|
||||
{
|
||||
dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
|
||||
dtAssert(m_maxNodes > 0);
|
||||
// pidx is special as 0 means "none" and 1 is the first node. For that reason
|
||||
// we have 1 fewer nodes available than the number of values it can contain.
|
||||
dtAssert(m_maxNodes > 0 && m_maxNodes <= DT_NULL_IDX && m_maxNodes <= (1 << DT_NODE_PARENT_BITS) - 1);
|
||||
|
||||
m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
|
||||
m_next = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*m_maxNodes, DT_ALLOC_PERM);
|
||||
|
|
|
|||
89
modules/worldengine/deps/recastnavigation/README.md
Normal file
89
modules/worldengine/deps/recastnavigation/README.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
|
||||
Recast & Detour
|
||||
===============
|
||||
|
||||
[](https://travis-ci.org/recastnavigation/recastnavigation)
|
||||
[](https://ci.appveyor.com/project/recastnavigation/recastnavigation/branch/master)
|
||||
|
||||
[](http://www.issuestats.com/github/recastnavigation/recastnavigation)
|
||||
[](http://www.issuestats.com/github/recastnavigation/recastnavigation)
|
||||
|
||||

|
||||
|
||||
## Recast
|
||||
|
||||
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 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,
|
||||
building the voxel mold, partitioning the mold into simple regions, peeling off
|
||||
the regions as simple polygons.
|
||||
|
||||
1. The voxel mold is build 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.
|
||||
2. The walkable areas described by the mold are divided into simple overlayed 2D regions. The resulting regions have only one non-overlapping contour, which simplifies the final step of the process tremendously.
|
||||
3. The navigation polygons are peeled off from the regions by first tracing the boundaries and then simplifying them. The resulting polygons are finally converted to convex polygons which makes them perfect for pathfinding and spatial reasoning about the level.
|
||||
|
||||
|
||||
## Detour
|
||||
|
||||
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 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
|
||||
|
||||
You can find a comprehensive demo project in RecastDemo folder. It is a kitchen sink demo containing all the functionality of the library. If you are new to Recast & Detour, check out [Sample_SoloMesh.cpp](/RecastDemo/Source/Sample_SoloMesh.cpp) to get started with building navmeshes and [NavMeshTesterTool.cpp](/RecastDemo/Source/NavMeshTesterTool.cpp) to see how Detour can be used to find paths.
|
||||
|
||||
### Building RecastDemo
|
||||
|
||||
RecastDemo uses [premake5](http://premake.github.io/) to build platform specific projects. Download it and make sure it's available on your path, or specify the path to it.
|
||||
|
||||
#### Linux
|
||||
|
||||
- Install SDl2 and its dependencies according to your distro's guidelines.
|
||||
- run `premake5 gmake` from the `RecastDemo` folder.
|
||||
- `cd Build/gmake` then `make`
|
||||
- Run `RecastDemo\Bin\RecastDemo`
|
||||
|
||||
#### OSX
|
||||
|
||||
- Grab the latest SDL2 development library dmg from [here](https://www.libsdl.org/download-2.0.php) and place `SDL2.framework` in `/Library/Frameworks/`
|
||||
- Navigate to the `RecastDemo` folder and run `premake5 xcode4`
|
||||
- Open `Build/xcode4/recastnavigation.xcworkspace`
|
||||
- Select the "RecastDemo" project in the left pane, go to the "BuildPhases" tab and expand "Link Binary With Libraries"
|
||||
- Remove the existing entry for SDL2 (it should have a white box icon) and re-add it by hitting the plus, selecting "Add Other", and selecting `/Library/Frameworks/SDL2.framework`. It should now have a suitcase icon.
|
||||
- Set the RecastDemo project as the target and build.
|
||||
|
||||
#### Windows
|
||||
|
||||
- Grab the latest SDL2 development library release from [here](https://www.libsdl.org/download-2.0.php) and unzip it `RecastDemo\Contrib`. Rename the SDL folder such that the path `RecastDemo\Contrib\SDL\lib\x86` is valid.
|
||||
- Run `"premake5" vs2015` from the `RecastDemo` folder
|
||||
- Open the solution, build, and run.
|
||||
|
||||
### Running Unit tests
|
||||
|
||||
- Follow the instructions to build RecastDemo above. Premake should generate another build target called "Tests".
|
||||
- Build the "Tests" project. This will generate an executable named "Tests" in `RecastDemo/Bin/`
|
||||
- Run the "Tests" executable. It will execute all the unit tests, indicate those that failed, and display a count of those that succeeded.
|
||||
|
||||
## Integrating with your own project
|
||||
|
||||
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`.
|
||||
|
||||
## Contributing
|
||||
|
||||
See the [Contributing document](CONTRIBUTING.md) for guidelines for making contributions.
|
||||
|
||||
## Discuss
|
||||
|
||||
- Discuss Recast & Detour: http://groups.google.com/group/recastnavigation
|
||||
- Development blog: http://digestingduck.blogspot.com/
|
||||
|
||||
## License
|
||||
|
||||
Recast & Detour is licensed under ZLib license, see License.txt for more information.
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
|
||||
Recast & Detour Version 1.4
|
||||
|
||||
|
||||
Recast
|
||||
|
||||
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.
|
||||
|
||||
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,
|
||||
building the voxel mold, partitioning the mold into simple regions, peeling off
|
||||
the regions as simple polygons.
|
||||
|
||||
1. The voxel mold is build 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.
|
||||
2. The walkable areas described by the mold are divided into simple
|
||||
overlayed 2D regions. The resulting regions have only one non-overlapping
|
||||
contour, which simplifies the final step of the process tremendously.
|
||||
3. The navigation polygons are peeled off from the regions by first tracing
|
||||
the boundaries and then simplifying them. The resulting polygons are
|
||||
finally converted to convex polygons which makes them perfect for
|
||||
pathfinding and spatial reasoning about the level.
|
||||
|
||||
The toolset code is located in the Recast folder and demo application using the Recast
|
||||
toolset is located in the RecastDemo folder.
|
||||
|
||||
The project files with this distribution can be compiled with Microsoft Visual C++ 2008
|
||||
(you can download it for free) and XCode 3.1.
|
||||
|
||||
|
||||
Detour
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Latest code available at http://code.google.com/p/recastnavigation/
|
||||
|
||||
|
||||
--
|
||||
|
||||
Release Notes
|
||||
|
||||
----------------
|
||||
* Recast 1.4
|
||||
Released August 24th, 2009
|
||||
|
||||
- Added detail height mesh generation (RecastDetailMesh.cpp) for single,
|
||||
tiled statmeshes as well as tilemesh.
|
||||
- Added feature to contour tracing which detects extra vertices along
|
||||
tile edges which should be removed later.
|
||||
- Changed the tiled stat mesh preprocess, so that it first generated
|
||||
polymeshes per tile and finally combines them.
|
||||
- Fixed bug in the GUI code where invisible buttons could be pressed.
|
||||
|
||||
----------------
|
||||
* Recast 1.31
|
||||
Released July 24th, 2009
|
||||
|
||||
- Better cost and heuristic functions.
|
||||
- Fixed tile navmesh raycast on tile borders.
|
||||
|
||||
----------------
|
||||
* Recast 1.3
|
||||
Released July 14th, 2009
|
||||
|
||||
- Added dtTileNavMesh which allows to dynamically add and remove navmesh pieces at runtime.
|
||||
- Renamed stat navmesh types to dtStat* (i.e. dtPoly is now dtStatPoly).
|
||||
- Moved common code used by tile and stat navmesh to DetourNode.h/cpp and DetourCommon.h/cpp.
|
||||
- Refactores the demo code.
|
||||
|
||||
----------------
|
||||
* Recast 1.2
|
||||
Released June 17th, 2009
|
||||
|
||||
- Added tiled mesh generation. The tiled generation allows to generate navigation for
|
||||
much larger worlds, it removes some of the artifacts that comes from distance fields
|
||||
in open areas, and allows later streaming and dynamic runtime generation
|
||||
- Improved and added some debug draw modes
|
||||
- API change: The helper function rcBuildNavMesh does not exists anymore,
|
||||
had to change few internal things to cope with the tiled processing,
|
||||
similar API functionality will be added later once the tiled process matures
|
||||
- The demo is getting way too complicated, need to split demos
|
||||
- Fixed several filtering functions so that the mesh is tighter to the geometry,
|
||||
sometimes there could be up error up to tow voxel units close to walls,
|
||||
now it should be just one.
|
||||
|
||||
----------------
|
||||
* Recast 1.1
|
||||
Released April 11th, 2009
|
||||
|
||||
This is the first release of Detour.
|
||||
|
||||
----------------
|
||||
* Recast 1.0
|
||||
Released March 29th, 2009
|
||||
|
||||
This is the first release of Recast.
|
||||
|
||||
The process is not always as robust as I would wish. The watershed phase sometimes swallows tiny islands
|
||||
which are close to edges. These droppings are handled in rcBuildContours, but the code is not
|
||||
particularly robust either.
|
||||
|
||||
Another non-robust case is when portal contours (contours shared between two regions) are always
|
||||
assumed to be straight. That can lead to overlapping contours specially when the level has
|
||||
large open areas.
|
||||
|
||||
|
||||
|
||||
Mikko Mononen
|
||||
memon@inside.org
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (C)
|
||||
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
|
|
@ -9,23 +9,29 @@
|
|||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
set(Recast_STAT_SRCS
|
||||
Recast.cpp
|
||||
RecastAlloc.cpp
|
||||
RecastArea.cpp
|
||||
RecastContour.cpp
|
||||
RecastFilter.cpp
|
||||
RecastMesh.cpp
|
||||
RecastMeshDetail.cpp
|
||||
RecastRasterization.cpp
|
||||
RecastRegion.cpp
|
||||
Source/Recast.cpp
|
||||
Source/RecastAlloc.cpp
|
||||
Source/RecastArea.cpp
|
||||
Source/RecastContour.cpp
|
||||
Source/RecastFilter.cpp
|
||||
Source/RecastLayers.cpp
|
||||
Source/RecastMesh.cpp
|
||||
Source/RecastMeshDetail.cpp
|
||||
Source/RecastRasterization.cpp
|
||||
Source/RecastRegion.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/zlib
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(Recast STATIC ${Recast_STAT_SRCS})
|
||||
|
||||
target_link_libraries(Recast ${ZLIB_LIBRARIES})
|
||||
target_include_directories(Recast
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Include)
|
||||
|
||||
target_link_libraries(Recast
|
||||
PUBLIC
|
||||
zlib)
|
||||
|
||||
set_target_properties(Recast
|
||||
PROPERTIES
|
||||
FOLDER
|
||||
"dep")
|
||||
|
|
|
|||
1200
modules/worldengine/deps/recastnavigation/Recast/Include/Recast.h
Normal file
1200
modules/worldengine/deps/recastnavigation/Recast/Include/Recast.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,146 @@
|
|||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECASTALLOC_H
|
||||
#define RECASTALLOC_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/// Provides hint values to the memory allocator on how long the
|
||||
/// memory is expected to be used.
|
||||
enum rcAllocHint
|
||||
{
|
||||
RC_ALLOC_PERM, ///< Memory will persist after a function call.
|
||||
RC_ALLOC_TEMP ///< Memory used temporarily within a function.
|
||||
};
|
||||
|
||||
/// A memory allocation function.
|
||||
// @param[in] size The size, in bytes of memory, to allocate.
|
||||
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
|
||||
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see rcAllocSetCustom
|
||||
typedef void* (rcAllocFunc)(size_t size, rcAllocHint hint);
|
||||
|
||||
/// A memory deallocation function.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAllocFunc.
|
||||
/// @see rcAllocSetCustom
|
||||
typedef void (rcFreeFunc)(void* ptr);
|
||||
|
||||
/// Sets the base custom allocation functions to be used by Recast.
|
||||
/// @param[in] allocFunc The memory allocation function to be used by #rcAlloc
|
||||
/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree
|
||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
|
||||
|
||||
/// Allocates a memory block.
|
||||
/// @param[in] size The size, in bytes of memory, to allocate.
|
||||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see rcFree
|
||||
void* rcAlloc(size_t size, rcAllocHint hint);
|
||||
|
||||
/// Deallocates a memory block.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
|
||||
/// @see rcAlloc
|
||||
void rcFree(void* ptr);
|
||||
|
||||
|
||||
/// A simple dynamic array of integers.
|
||||
class rcIntArray
|
||||
{
|
||||
int* m_data;
|
||||
int m_size, m_cap;
|
||||
|
||||
void doResize(int n);
|
||||
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
rcIntArray(const rcIntArray&);
|
||||
rcIntArray& operator=(const rcIntArray&);
|
||||
|
||||
public:
|
||||
/// Constructs an instance with an initial array size of zero.
|
||||
rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
|
||||
|
||||
/// Constructs an instance initialized to the specified size.
|
||||
/// @param[in] n The initial size of the integer array.
|
||||
rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
|
||||
~rcIntArray() { rcFree(m_data); }
|
||||
|
||||
/// Specifies the new size of the integer array.
|
||||
/// @param[in] n The new size of the integer array.
|
||||
void resize(int n)
|
||||
{
|
||||
if (n > m_cap)
|
||||
doResize(n);
|
||||
|
||||
m_size = n;
|
||||
}
|
||||
|
||||
/// Push the specified integer onto the end of the array and increases the size by one.
|
||||
/// @param[in] item The new value.
|
||||
void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
|
||||
|
||||
/// Returns the value at the end of the array and reduces the size by one.
|
||||
/// @return The value at the end of the array.
|
||||
int pop()
|
||||
{
|
||||
if (m_size > 0)
|
||||
m_size--;
|
||||
|
||||
return m_data[m_size];
|
||||
}
|
||||
|
||||
/// The value at the specified array index.
|
||||
/// @warning Does not provide overflow protection.
|
||||
/// @param[in] i The index of the value.
|
||||
const int& operator[](int i) const { return m_data[i]; }
|
||||
|
||||
/// The value at the specified array index.
|
||||
/// @warning Does not provide overflow protection.
|
||||
/// @param[in] i The index of the value.
|
||||
int& operator[](int i) { return m_data[i]; }
|
||||
|
||||
/// The current size of the integer array.
|
||||
int size() const { return m_size; }
|
||||
};
|
||||
|
||||
/// A simple helper class used to delete an array when it goes out of scope.
|
||||
/// @note This class is rarely if ever used by the end user.
|
||||
template<class T> class rcScopedDelete
|
||||
{
|
||||
T* ptr;
|
||||
public:
|
||||
|
||||
/// Constructs an instance with a null pointer.
|
||||
inline rcScopedDelete() : ptr(0) {}
|
||||
|
||||
/// Constructs an instance with the specified pointer.
|
||||
/// @param[in] p An pointer to an allocated array.
|
||||
inline rcScopedDelete(T* p) : ptr(p) {}
|
||||
inline ~rcScopedDelete() { rcFree(ptr); }
|
||||
|
||||
/// The root array pointer.
|
||||
/// @return The root array pointer.
|
||||
inline operator T*() { return ptr; }
|
||||
|
||||
private:
|
||||
// Explicitly disabled copy constructor and copy assignment operator.
|
||||
rcScopedDelete(const rcScopedDelete&);
|
||||
rcScopedDelete& operator=(const rcScopedDelete&);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#ifdef NDEBUG
|
||||
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
||||
# define rcAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false)
|
||||
# define rcAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
|
||||
#else
|
||||
# include <assert.h>
|
||||
# define rcAssert assert
|
||||
|
|
@ -1,688 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECAST_H
|
||||
#define RECAST_H
|
||||
|
||||
// Some math headers don't have PI defined.
|
||||
static const float RC_PI = 3.14159265f;
|
||||
|
||||
enum rcLogCategory
|
||||
{
|
||||
RC_LOG_PROGRESS = 1,
|
||||
RC_LOG_WARNING,
|
||||
RC_LOG_ERROR,
|
||||
};
|
||||
|
||||
enum rcTimerLabel
|
||||
{
|
||||
RC_TIMER_TOTAL,
|
||||
RC_TIMER_TEMP,
|
||||
RC_TIMER_RASTERIZE_TRIANGLES,
|
||||
RC_TIMER_BUILD_COMPACTHEIGHTFIELD,
|
||||
RC_TIMER_BUILD_CONTOURS,
|
||||
RC_TIMER_BUILD_CONTOURS_TRACE,
|
||||
RC_TIMER_BUILD_CONTOURS_SIMPLIFY,
|
||||
RC_TIMER_FILTER_BORDER,
|
||||
RC_TIMER_FILTER_WALKABLE,
|
||||
RC_TIMER_MEDIAN_AREA,
|
||||
RC_TIMER_FILTER_LOW_OBSTACLES,
|
||||
RC_TIMER_BUILD_POLYMESH,
|
||||
RC_TIMER_MERGE_POLYMESH,
|
||||
RC_TIMER_ERODE_AREA,
|
||||
RC_TIMER_MARK_BOX_AREA,
|
||||
RC_TIMER_MARK_CONVEXPOLY_AREA,
|
||||
RC_TIMER_BUILD_DISTANCEFIELD,
|
||||
RC_TIMER_BUILD_DISTANCEFIELD_DIST,
|
||||
RC_TIMER_BUILD_DISTANCEFIELD_BLUR,
|
||||
RC_TIMER_BUILD_REGIONS,
|
||||
RC_TIMER_BUILD_REGIONS_WATERSHED,
|
||||
RC_TIMER_BUILD_REGIONS_EXPAND,
|
||||
RC_TIMER_BUILD_REGIONS_FLOOD,
|
||||
RC_TIMER_BUILD_REGIONS_FILTER,
|
||||
RC_TIMER_BUILD_POLYMESHDETAIL,
|
||||
RC_TIMER_MERGE_POLYMESHDETAIL,
|
||||
RC_MAX_TIMERS
|
||||
};
|
||||
|
||||
// Build context provides several optional utilities needed for the build process,
|
||||
// such as timing, logging, and build time collecting.
|
||||
class rcContext
|
||||
{
|
||||
public:
|
||||
inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {}
|
||||
virtual ~rcContext() {}
|
||||
|
||||
// Enables or disables logging.
|
||||
inline void enableLog(bool state) { m_logEnabled = state; }
|
||||
// Resets log.
|
||||
inline void resetLog() { if (m_logEnabled) doResetLog(); }
|
||||
// Logs a message.
|
||||
void log(const rcLogCategory category, const char* format, ...);
|
||||
|
||||
// Enables or disables timer.
|
||||
inline void enableTimer(bool state) { m_timerEnabled = state; }
|
||||
// Resets all timers.
|
||||
inline void resetTimers() { if (m_timerEnabled) doResetTimers(); }
|
||||
// Starts timer, used for performance timing.
|
||||
inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); }
|
||||
// Stops timer, used for performance timing.
|
||||
inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); }
|
||||
// Returns time accumulated between timer start/stop.
|
||||
inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; }
|
||||
|
||||
protected:
|
||||
// Virtual functions to override for custom implementations.
|
||||
virtual void doResetLog() {}
|
||||
virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {}
|
||||
virtual void doResetTimers() {}
|
||||
virtual void doStartTimer(const rcTimerLabel /*label*/) {}
|
||||
virtual void doStopTimer(const rcTimerLabel /*label*/) {}
|
||||
virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; }
|
||||
|
||||
bool m_logEnabled;
|
||||
bool m_timerEnabled;
|
||||
};
|
||||
|
||||
|
||||
// The units of the parameters are specified in parenthesis as follows:
|
||||
// (vx) voxels, (wu) world units
|
||||
struct rcConfig
|
||||
{
|
||||
int width, height; // Dimensions of the rasterized heightfield (vx)
|
||||
int tileSize; // Width and Height of a tile (vx)
|
||||
int borderSize; // Non-navigable Border around the heightfield (vx)
|
||||
float cs, ch; // Grid cell size and height (wu)
|
||||
float bmin[3], bmax[3]; // Grid bounds (wu)
|
||||
float walkableSlopeAngle; // Maximum walkable slope angle in degrees.
|
||||
int walkableHeight; // Minimum height where the agent can still walk (vx)
|
||||
int walkableClimb; // Maximum height between grid cells the agent can climb (vx)
|
||||
int walkableRadius; // Radius of the agent in cells (vx)
|
||||
int maxEdgeLen; // Maximum contour edge length (vx)
|
||||
float maxSimplificationError; // Maximum distance error from contour to cells (vx)
|
||||
int minRegionArea; // Regions whose area is smaller than this threshold will be removed. (vx)
|
||||
int mergeRegionArea; // Regions whose area is smaller than this threshold will be merged (vx)
|
||||
int maxVertsPerPoly; // Max number of vertices per polygon
|
||||
float detailSampleDist; // Detail mesh sample spacing.
|
||||
float detailSampleMaxError; // Detail mesh simplification max sample error.
|
||||
};
|
||||
|
||||
// Define number of bits in the above structure for smin/smax.
|
||||
// The max height is used for clamping rasterized values.
|
||||
static const int RC_SPAN_HEIGHT_BITS = 16;
|
||||
static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1;
|
||||
|
||||
// Heightfield span.
|
||||
struct rcSpan
|
||||
{
|
||||
unsigned int smin : 16; // Span min height.
|
||||
unsigned int smax : 16; // Span max height.
|
||||
unsigned char area; // Span area type.
|
||||
rcSpan* next; // Next span in column.
|
||||
};
|
||||
|
||||
// Number of spans allocated per pool.
|
||||
static const int RC_SPANS_PER_POOL = 2048;
|
||||
|
||||
// Memory pool used for quick span allocation.
|
||||
struct rcSpanPool
|
||||
{
|
||||
rcSpanPool* next; // Pointer to next pool.
|
||||
rcSpan items[RC_SPANS_PER_POOL]; // Array of spans.
|
||||
};
|
||||
|
||||
// Dynamic span-heightfield.
|
||||
struct rcHeightfield
|
||||
{
|
||||
int width, height; // Dimension of the heightfield.
|
||||
float bmin[3], bmax[3]; // Bounding box of the heightfield
|
||||
float cs, ch; // Cell size and height.
|
||||
rcSpan** spans; // Heightfield of spans (width*height).
|
||||
rcSpanPool* pools; // Linked list of span pools.
|
||||
rcSpan* freelist; // Pointer to next free span.
|
||||
};
|
||||
|
||||
rcHeightfield* rcAllocHeightfield();
|
||||
void rcFreeHeightField(rcHeightfield* hf);
|
||||
|
||||
|
||||
struct rcCompactCell
|
||||
{
|
||||
unsigned int index : 24; // Index to first span in column.
|
||||
unsigned int count : 8; // Number of spans in this column.
|
||||
};
|
||||
|
||||
struct rcCompactSpan
|
||||
{
|
||||
unsigned short y; // Bottom coordinate of the span.
|
||||
unsigned short reg;
|
||||
unsigned int con : 24; // Connections to neighbour cells.
|
||||
unsigned int h : 8; // Height of the span.
|
||||
};
|
||||
|
||||
// Compact static heightfield.
|
||||
struct rcCompactHeightfield
|
||||
{
|
||||
int width, height; // Width and height of the heightfield.
|
||||
int spanCount; // Number of spans in the heightfield.
|
||||
int walkableHeight, walkableClimb; // Agent properties.
|
||||
unsigned short maxDistance; // Maximum distance value stored in heightfield.
|
||||
unsigned short maxRegions; // Maximum Region Id stored in heightfield.
|
||||
float bmin[3], bmax[3]; // Bounding box of the heightfield.
|
||||
float cs, ch; // Cell size and height.
|
||||
rcCompactCell* cells; // Pointer to width*height cells.
|
||||
rcCompactSpan* spans; // Pointer to spans.
|
||||
unsigned short* dist; // Pointer to per span distance to border.
|
||||
unsigned char* areas; // Pointer to per span area ID.
|
||||
};
|
||||
|
||||
rcCompactHeightfield* rcAllocCompactHeightfield();
|
||||
void rcFreeCompactHeightfield(rcCompactHeightfield* chf);
|
||||
|
||||
|
||||
struct rcContour
|
||||
{
|
||||
int* verts; // Vertex coordinates, each vertex contains 4 components.
|
||||
int nverts; // Number of vertices.
|
||||
int* rverts; // Raw vertex coordinates, each vertex contains 4 components.
|
||||
int nrverts; // Number of raw vertices.
|
||||
unsigned short reg; // Region ID of the contour.
|
||||
unsigned char area; // Area ID of the contour.
|
||||
};
|
||||
|
||||
struct rcContourSet
|
||||
{
|
||||
rcContour* conts; // Pointer to all contours.
|
||||
int nconts; // Number of contours.
|
||||
float bmin[3], bmax[3]; // Bounding box of the heightfield.
|
||||
float cs, ch; // Cell size and height.
|
||||
};
|
||||
|
||||
rcContourSet* rcAllocContourSet();
|
||||
void rcFreeContourSet(rcContourSet* cset);
|
||||
|
||||
|
||||
// Polymesh store a connected mesh of polygons.
|
||||
// The polygons are store in an array where each polygons takes
|
||||
// 'nvp*2' elements. The first 'nvp' elements are indices to vertices
|
||||
// and the second 'nvp' elements are indices to neighbour polygons.
|
||||
// If a polygon has less than 'bvp' vertices, the remaining indices
|
||||
// are set to RC_MESH_NULL_IDX. If an polygon edge does not have a neighbour
|
||||
// the neighbour index is set to RC_MESH_NULL_IDX.
|
||||
// Vertices can be transformed into world space as follows:
|
||||
// x = bmin[0] + verts[i*3+0]*cs;
|
||||
// y = bmin[1] + verts[i*3+1]*ch;
|
||||
// z = bmin[2] + verts[i*3+2]*cs;
|
||||
struct rcPolyMesh
|
||||
{
|
||||
unsigned short* verts; // Vertices of the mesh, 3 elements per vertex.
|
||||
unsigned short* polys; // Polygons of the mesh, nvp*2 elements per polygon.
|
||||
unsigned short* regs; // Region ID of the polygons.
|
||||
unsigned short* flags; // Per polygon flags.
|
||||
unsigned char* areas; // Area ID of polygons.
|
||||
int nverts; // Number of vertices.
|
||||
int npolys; // Number of polygons.
|
||||
int maxpolys; // Number of allocated polygons.
|
||||
int nvp; // Max number of vertices per polygon.
|
||||
float bmin[3], bmax[3]; // Bounding box of the mesh.
|
||||
float cs, ch; // Cell size and height.
|
||||
};
|
||||
|
||||
rcPolyMesh* rcAllocPolyMesh();
|
||||
void rcFreePolyMesh(rcPolyMesh* pmesh);
|
||||
|
||||
|
||||
// Detail mesh generated from a rcPolyMesh.
|
||||
// Each submesh represents a polygon in the polymesh and they are stored in
|
||||
// exactly same order. Each submesh is described as 4 values:
|
||||
// base vertex, vertex count, base triangle, triangle count. That is,
|
||||
// const unsigned char* t = &dmesh.tris[(tbase+i)*3]; and
|
||||
// const float* v = &dmesh.verts[(vbase+t[j])*3];
|
||||
// If the input polygon has 'n' vertices, those vertices are first in the
|
||||
// submesh vertex list. This allows to compres the mesh by not storing the
|
||||
// first vertices and using the polymesh vertices instead.
|
||||
// Max number of vertices per submesh is 127 and
|
||||
// max number of triangles per submesh is 255.
|
||||
|
||||
struct rcPolyMeshDetail
|
||||
{
|
||||
unsigned int* meshes; // Pointer to all mesh data.
|
||||
float* verts; // Pointer to all vertex data.
|
||||
unsigned char* tris; // Pointer to all triangle data.
|
||||
int nmeshes; // Number of meshes.
|
||||
int nverts; // Number of total vertices.
|
||||
int ntris; // Number of triangles.
|
||||
};
|
||||
|
||||
rcPolyMeshDetail* rcAllocPolyMeshDetail();
|
||||
void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
|
||||
|
||||
|
||||
// If heightfield region ID has the following bit set, the region is on border area
|
||||
// and excluded from many calculations.
|
||||
static const unsigned short RC_BORDER_REG = 0x8000;
|
||||
|
||||
// If contour region ID has the following bit set, the vertex will be later
|
||||
// removed in order to match the segments and vertices at tile boundaries.
|
||||
static const int RC_BORDER_VERTEX = 0x10000;
|
||||
|
||||
static const int RC_AREA_BORDER = 0x20000;
|
||||
|
||||
enum rcBuildContoursFlags
|
||||
{
|
||||
RC_CONTOUR_TESS_WALL_EDGES = 0x01, // Tessellate wall edges
|
||||
RC_CONTOUR_TESS_AREA_EDGES = 0x02, // Tessellate edges between areas.
|
||||
};
|
||||
|
||||
// Mask used with contours to extract region id.
|
||||
static const int RC_CONTOUR_REG_MASK = 0xffff;
|
||||
|
||||
// Null index which is used with meshes to mark unset or invalid indices.
|
||||
static const unsigned short RC_MESH_NULL_IDX = 0xffff;
|
||||
|
||||
// Area ID that is considered empty.
|
||||
static const unsigned char RC_NULL_AREA = 0;
|
||||
|
||||
// Area ID that is considered generally walkable.
|
||||
static const unsigned char RC_WALKABLE_AREA = 63;
|
||||
|
||||
// Value returned by rcGetCon() if the direction is not connected.
|
||||
static const int RC_NOT_CONNECTED = 0x3f;
|
||||
|
||||
// Compact span neighbour helpers.
|
||||
inline void rcSetCon(rcCompactSpan& s, int dir, int i)
|
||||
{
|
||||
const unsigned int shift = (unsigned int)dir*6;
|
||||
unsigned int con = s.con;
|
||||
s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift);
|
||||
}
|
||||
|
||||
inline int rcGetCon(const rcCompactSpan& s, int dir)
|
||||
{
|
||||
const unsigned int shift = (unsigned int)dir*6;
|
||||
return (s.con >> shift) & 0x3f;
|
||||
}
|
||||
|
||||
inline int rcGetDirOffsetX(int dir)
|
||||
{
|
||||
const int offset[4] = { -1, 0, 1, 0, };
|
||||
return offset[dir&0x03];
|
||||
}
|
||||
|
||||
inline int rcGetDirOffsetY(int dir)
|
||||
{
|
||||
const int offset[4] = { 0, 1, 0, -1 };
|
||||
return offset[dir&0x03];
|
||||
}
|
||||
|
||||
// Common helper functions
|
||||
template<class T> inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; }
|
||||
template<class T> inline T rcMin(T a, T b) { return a < b ? a : b; }
|
||||
template<class T> inline T rcMax(T a, T b) { return a > b ? a : b; }
|
||||
template<class T> inline T rcAbs(T a) { return a < 0 ? -a : a; }
|
||||
template<class T> inline T rcSqr(T a) { return a*a; }
|
||||
template<class T> inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
|
||||
float rcSqrt(float x);
|
||||
|
||||
// Common vector helper functions.
|
||||
inline void rcVcross(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
|
||||
dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
|
||||
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
||||
}
|
||||
|
||||
inline float rcVdot(const float* v1, const float* v2)
|
||||
{
|
||||
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
|
||||
}
|
||||
|
||||
inline void rcVmad(float* dest, const float* v1, const float* v2, const float s)
|
||||
{
|
||||
dest[0] = v1[0]+v2[0]*s;
|
||||
dest[1] = v1[1]+v2[1]*s;
|
||||
dest[2] = v1[2]+v2[2]*s;
|
||||
}
|
||||
|
||||
inline void rcVadd(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[0]+v2[0];
|
||||
dest[1] = v1[1]+v2[1];
|
||||
dest[2] = v1[2]+v2[2];
|
||||
}
|
||||
|
||||
inline void rcVsub(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[0]-v2[0];
|
||||
dest[1] = v1[1]-v2[1];
|
||||
dest[2] = v1[2]-v2[2];
|
||||
}
|
||||
|
||||
inline void rcVmin(float* mn, const float* v)
|
||||
{
|
||||
mn[0] = rcMin(mn[0], v[0]);
|
||||
mn[1] = rcMin(mn[1], v[1]);
|
||||
mn[2] = rcMin(mn[2], v[2]);
|
||||
}
|
||||
|
||||
inline void rcVmax(float* mx, const float* v)
|
||||
{
|
||||
mx[0] = rcMax(mx[0], v[0]);
|
||||
mx[1] = rcMax(mx[1], v[1]);
|
||||
mx[2] = rcMax(mx[2], v[2]);
|
||||
}
|
||||
|
||||
inline void rcVcopy(float* dest, const float* v)
|
||||
{
|
||||
dest[0] = v[0];
|
||||
dest[1] = v[1];
|
||||
dest[2] = v[2];
|
||||
}
|
||||
|
||||
inline float rcVdist(const float* v1, const float* v2)
|
||||
{
|
||||
float dx = v2[0] - v1[0];
|
||||
float dy = v2[1] - v1[1];
|
||||
float dz = v2[2] - v1[2];
|
||||
return rcSqrt(dx*dx + dy*dy + dz*dz);
|
||||
}
|
||||
|
||||
inline float rcVdistSqr(const float* v1, const float* v2)
|
||||
{
|
||||
float dx = v2[0] - v1[0];
|
||||
float dy = v2[1] - v1[1];
|
||||
float dz = v2[2] - v1[2];
|
||||
return dx*dx + dy*dy + dz*dz;
|
||||
}
|
||||
|
||||
inline void rcVnormalize(float* v)
|
||||
{
|
||||
float d = 1.0f / rcSqrt(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2]));
|
||||
v[0] *= d;
|
||||
v[1] *= d;
|
||||
v[2] *= d;
|
||||
}
|
||||
|
||||
inline bool rcVequal(const float* p0, const float* p1)
|
||||
{
|
||||
static const float thr = rcSqr(1.0f/16384.0f);
|
||||
const float d = rcVdistSqr(p0, p1);
|
||||
return d < thr;
|
||||
}
|
||||
|
||||
// Calculated bounding box of array of vertices.
|
||||
// Params:
|
||||
// verts - (in) array of vertices
|
||||
// nv - (in) vertex count
|
||||
// bmin, bmax - (out) bounding box
|
||||
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax);
|
||||
|
||||
// Calculates grid size based on bounding box and grid cell size.
|
||||
// Params:
|
||||
// bmin, bmax - (in) bounding box
|
||||
// cs - (in) grid cell size
|
||||
// w - (out) grid width
|
||||
// h - (out) grid height
|
||||
void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h);
|
||||
|
||||
// Creates and initializes new heightfield.
|
||||
// Params:
|
||||
// hf - (in/out) heightfield to initialize.
|
||||
// width - (in) width of the heightfield.
|
||||
// height - (in) height of the heightfield.
|
||||
// bmin, bmax - (in) bounding box of the heightfield
|
||||
// cs - (in) grid cell size
|
||||
// ch - (in) grid cell height
|
||||
bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
|
||||
const float* bmin, const float* bmax,
|
||||
float cs, float ch);
|
||||
|
||||
// Sets the RC_WALKABLE_AREA for every triangle whose slope is below
|
||||
// the maximum walkable slope angle.
|
||||
// Params:
|
||||
// walkableSlopeAngle - (in) maximum slope angle in degrees.
|
||||
// verts - (in) array of vertices
|
||||
// nv - (in) vertex count
|
||||
// tris - (in) array of triangle vertex indices
|
||||
// nt - (in) triangle count
|
||||
// areas - (out) array of triangle area types
|
||||
void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
|
||||
const int* tris, int nt, unsigned char* areas);
|
||||
|
||||
// Sets the RC_NULL_AREA for every triangle whose slope is steeper than
|
||||
// the maximum walkable slope angle.
|
||||
// Params:
|
||||
// walkableSlopeAngle - (in) maximum slope angle in degrees.
|
||||
// verts - (in) array of vertices
|
||||
// nv - (in) vertex count
|
||||
// tris - (in) array of triangle vertex indices
|
||||
// nt - (in) triangle count
|
||||
// areas - (out) array of triangle are types
|
||||
void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
|
||||
const int* tris, int nt, unsigned char* areas);
|
||||
|
||||
// Adds span to heightfield.
|
||||
// The span addition can set to favor flags. If the span is merged to
|
||||
// another span and the new smax is within 'flagMergeThr' units away
|
||||
// from the existing span the span flags are merged and stored.
|
||||
// Params:
|
||||
// solid - (in) heightfield where the spans is added to
|
||||
// x,y - (in) location on the heightfield where the span is added
|
||||
// smin,smax - (in) spans min/max height
|
||||
// flags - (in) span flags (zero or WALKABLE)
|
||||
// flagMergeThr - (in) merge threshold.
|
||||
void rcAddSpan(rcContext* ctx, rcHeightfield& solid, const int x, const int y,
|
||||
const unsigned short smin, const unsigned short smax,
|
||||
const unsigned short area, const int flagMergeThr);
|
||||
|
||||
// Rasterizes a triangle into heightfield spans.
|
||||
// Params:
|
||||
// v0,v1,v2 - (in) the vertices of the triangle.
|
||||
// area - (in) area type of the triangle.
|
||||
// solid - (in) heightfield where the triangle is rasterized
|
||||
// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
|
||||
void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char area, rcHeightfield& solid,
|
||||
const int flagMergeThr = 1);
|
||||
|
||||
// Rasterizes indexed triangle mesh into heightfield spans.
|
||||
// Params:
|
||||
// verts - (in) array of vertices
|
||||
// nv - (in) vertex count
|
||||
// tris - (in) array of triangle vertex indices
|
||||
// area - (in) array of triangle area types.
|
||||
// nt - (in) triangle count
|
||||
// solid - (in) heightfield where the triangles are rasterized
|
||||
// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
|
||||
const int* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr = 1);
|
||||
|
||||
// Rasterizes indexed triangle mesh into heightfield spans.
|
||||
// Params:
|
||||
// verts - (in) array of vertices
|
||||
// nv - (in) vertex count
|
||||
// tris - (in) array of triangle vertex indices
|
||||
// area - (in) array of triangle area types.
|
||||
// nt - (in) triangle count
|
||||
// solid - (in) heightfield where the triangles are rasterized
|
||||
// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
|
||||
const unsigned short* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr = 1);
|
||||
|
||||
// Rasterizes the triangles into heightfield spans.
|
||||
// Params:
|
||||
// verts - (in) array of vertices
|
||||
// area - (in) array of triangle area types.
|
||||
// nt - (in) triangle count
|
||||
// solid - (in) heightfield where the triangles are rasterized
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr = 1);
|
||||
|
||||
// Marks non-walkable low obstacles as walkable if they are closer than walkableClimb
|
||||
// from a walkable surface. Applying this filter allows to step over low hanging
|
||||
// low obstacles.
|
||||
// Params:
|
||||
// walkableHeight - (in) minimum height where the agent can still walk
|
||||
// solid - (in/out) heightfield describing the solid space
|
||||
// TODO: Missuses ledge flag, must be called before rcFilterLedgeSpans!
|
||||
void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid);
|
||||
|
||||
// Removes WALKABLE flag from all spans that are at ledges. This filtering
|
||||
// removes possible overestimation of the conservative voxelization so that
|
||||
// the resulting mesh will not have regions hanging in air over ledges.
|
||||
// Params:
|
||||
// walkableHeight - (in) minimum height where the agent can still walk
|
||||
// walkableClimb - (in) maximum height between grid cells the agent can climb
|
||||
// solid - (in/out) heightfield describing the solid space
|
||||
void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight,
|
||||
const int walkableClimb, rcHeightfield& solid);
|
||||
|
||||
// Removes WALKABLE flag from all spans which have smaller than
|
||||
// 'walkableHeight' clearance above them.
|
||||
// Params:
|
||||
// walkableHeight - (in) minimum height where the agent can still walk
|
||||
// solid - (in/out) heightfield describing the solid space
|
||||
void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid);
|
||||
|
||||
// Returns number of spans contained in a heightfield.
|
||||
// Params:
|
||||
// hf - (in) heightfield to be compacted
|
||||
// Returns number of spans.
|
||||
int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf);
|
||||
|
||||
// Builds compact representation of the heightfield.
|
||||
// Params:
|
||||
// walkableHeight - (in) minimum height where the agent can still walk
|
||||
// walkableClimb - (in) maximum height between grid cells the agent can climb
|
||||
// flags - (in) require flags for a cell to be included in the compact heightfield.
|
||||
// hf - (in) heightfield to be compacted
|
||||
// chf - (out) compact heightfield representing the open space.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
||||
rcHeightfield& hf, rcCompactHeightfield& chf);
|
||||
|
||||
// Erodes walkable area.
|
||||
// Params:
|
||||
// radius - (in) radius of erosion (max 255).
|
||||
// chf - (in/out) compact heightfield to erode.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf);
|
||||
|
||||
// Applies median filter to walkable area types, removing noise.
|
||||
// Params:
|
||||
// chf - (in/out) compact heightfield to erode.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf);
|
||||
|
||||
// Marks the area of the convex polygon into the area type of the compact heightfield.
|
||||
// Params:
|
||||
// bmin/bmax - (in) bounds of the axis aligned box.
|
||||
// areaId - (in) area ID to mark.
|
||||
// chf - (in/out) compact heightfield to mark.
|
||||
void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf);
|
||||
|
||||
// Marks the area of the convex polygon into the area type of the compact heightfield.
|
||||
// Params:
|
||||
// verts - (in) vertices of the convex polygon.
|
||||
// nverts - (in) number of vertices in the polygon.
|
||||
// hmin/hmax - (in) min and max height of the polygon.
|
||||
// areaId - (in) area ID to mark.
|
||||
// chf - (in/out) compact heightfield to mark.
|
||||
void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
|
||||
const float hmin, const float hmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf);
|
||||
|
||||
// Builds distance field and stores it into the combat heightfield.
|
||||
// Params:
|
||||
// chf - (in/out) compact heightfield representing the open space.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
|
||||
|
||||
// Divides the walkable heighfied into simple regions using watershed partitioning.
|
||||
// Each region has only one contour and no overlaps.
|
||||
// The regions are stored in the compact heightfield 'reg' field.
|
||||
// The process sometimes creates small regions. If the area of a regions is
|
||||
// smaller than 'mergeRegionArea' then the region will be merged with a neighbour
|
||||
// region if possible. If multiple regions form an area which is smaller than
|
||||
// 'minRegionArea' all the regions belonging to that area will be removed.
|
||||
// Here area means the count of spans in an area.
|
||||
// Params:
|
||||
// chf - (in/out) compact heightfield representing the open space.
|
||||
// minRegionArea - (in) the smallest allowed region area.
|
||||
// maxMergeRegionArea - (in) the largest allowed region area which can be merged.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int minRegionArea, const int mergeRegionArea);
|
||||
|
||||
// Divides the walkable heighfied into simple regions using simple monotone partitioning.
|
||||
// Each region has only one contour and no overlaps.
|
||||
// The regions are stored in the compact heightfield 'reg' field.
|
||||
// The process sometimes creates small regions. If the area of a regions is
|
||||
// smaller than 'mergeRegionArea' then the region will be merged with a neighbour
|
||||
// region if possible. If multiple regions form an area which is smaller than
|
||||
// 'minRegionArea' all the regions belonging to that area will be removed.
|
||||
// Here area means the count of spans in an area.
|
||||
// Params:
|
||||
// chf - (in/out) compact heightfield representing the open space.
|
||||
// minRegionArea - (in) the smallest allowed regions size.
|
||||
// maxMergeRegionArea - (in) the largest allowed regions size which can be merged.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int minRegionArea, const int mergeRegionArea);
|
||||
|
||||
// Builds simplified contours from the regions outlines.
|
||||
// Params:
|
||||
// chf - (in) compact heightfield which has regions set.
|
||||
// maxError - (in) maximum allowed distance between simplified contour and cells.
|
||||
// maxEdgeLen - (in) maximum allowed contour edge length in cells.
|
||||
// cset - (out) Resulting contour set.
|
||||
// flags - (in) build flags, see rcBuildContoursFlags.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const float maxError, const int maxEdgeLen,
|
||||
rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES);
|
||||
|
||||
// Builds connected convex polygon mesh from contour polygons.
|
||||
// Params:
|
||||
// cset - (in) contour set.
|
||||
// nvp - (in) maximum number of vertices per polygon.
|
||||
// mesh - (out) poly mesh.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh);
|
||||
|
||||
bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh);
|
||||
|
||||
// Builds detail triangle mesh for each polygon in the poly mesh.
|
||||
// Params:
|
||||
// mesh - (in) poly mesh to detail.
|
||||
// chf - (in) compact height field, used to query height for new vertices.
|
||||
// sampleDist - (in) spacing between height samples used to generate more detail into mesh.
|
||||
// sampleMaxError - (in) maximum allowed distance between simplified detail mesh and height sample.
|
||||
// pmdtl - (out) detail mesh.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
|
||||
const float sampleDist, const float sampleMaxError,
|
||||
rcPolyMeshDetail& dmesh);
|
||||
|
||||
bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh);
|
||||
|
||||
|
||||
#endif // RECAST_H
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECASTALLOC_H
|
||||
#define RECASTALLOC_H
|
||||
|
||||
enum rcAllocHint
|
||||
{
|
||||
RC_ALLOC_PERM, // Memory persist after a function call.
|
||||
RC_ALLOC_TEMP // Memory used temporarily within a function.
|
||||
};
|
||||
|
||||
typedef void* (rcAllocFunc)(int size, rcAllocHint hint);
|
||||
typedef void (rcFreeFunc)(void* ptr);
|
||||
|
||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
|
||||
|
||||
void* rcAlloc(int size, rcAllocHint hint);
|
||||
void rcFree(void* ptr);
|
||||
|
||||
|
||||
|
||||
// Simple dynamic array ints.
|
||||
class rcIntArray
|
||||
{
|
||||
int* m_data;
|
||||
int m_size, m_cap;
|
||||
inline rcIntArray(const rcIntArray&);
|
||||
inline rcIntArray& operator=(const rcIntArray&);
|
||||
public:
|
||||
inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
|
||||
inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
|
||||
inline ~rcIntArray() { rcFree(m_data); }
|
||||
void resize(int n);
|
||||
inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
|
||||
inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
|
||||
inline const int& operator[](int i) const { return m_data[i]; }
|
||||
inline int& operator[](int i) { return m_data[i]; }
|
||||
inline int size() const { return m_size; }
|
||||
};
|
||||
|
||||
// Simple internal helper class to delete array in scope
|
||||
template<class T> class rcScopedDelete
|
||||
{
|
||||
T* ptr;
|
||||
inline T* operator=(T* p);
|
||||
public:
|
||||
inline rcScopedDelete() : ptr(0) {}
|
||||
inline rcScopedDelete(T* p) : ptr(p) {}
|
||||
inline ~rcScopedDelete() { rcFree(ptr); }
|
||||
inline operator T*() { return ptr; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <new>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
|
@ -32,7 +33,26 @@ float rcSqrt(float x)
|
|||
return sqrtf(x);
|
||||
}
|
||||
|
||||
/// @class rcContext
|
||||
/// @par
|
||||
///
|
||||
/// This class does not provide logging or timer functionality on its
|
||||
/// own. Both must be provided by a concrete implementation
|
||||
/// by overriding the protected member functions. Also, this class does not
|
||||
/// provide an interface for extracting log messages. (Only adding them.)
|
||||
/// So concrete implementations must provide one.
|
||||
///
|
||||
/// If no logging or timers are required, just pass an instance of this
|
||||
/// class through the Recast build process.
|
||||
///
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Example:
|
||||
/// @code
|
||||
/// // Where ctx is an instance of rcContext and filepath is a char array.
|
||||
/// ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath);
|
||||
/// @endcode
|
||||
void rcContext::log(const rcLogCategory category, const char* format, ...)
|
||||
{
|
||||
if (!m_logEnabled)
|
||||
|
|
@ -53,23 +73,39 @@ void rcContext::log(const rcLogCategory category, const char* format, ...)
|
|||
|
||||
rcHeightfield* rcAllocHeightfield()
|
||||
{
|
||||
rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM);
|
||||
memset(hf, 0, sizeof(rcHeightfield));
|
||||
return hf;
|
||||
return new (rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM)) rcHeightfield;
|
||||
}
|
||||
|
||||
rcHeightfield::rcHeightfield()
|
||||
: width()
|
||||
, height()
|
||||
, bmin()
|
||||
, bmax()
|
||||
, cs()
|
||||
, ch()
|
||||
, spans()
|
||||
, pools()
|
||||
, freelist()
|
||||
{
|
||||
}
|
||||
|
||||
rcHeightfield::~rcHeightfield()
|
||||
{
|
||||
// Delete span array.
|
||||
rcFree(spans);
|
||||
// Delete span pools.
|
||||
while (pools)
|
||||
{
|
||||
rcSpanPool* next = pools->next;
|
||||
rcFree(pools);
|
||||
pools = next;
|
||||
}
|
||||
}
|
||||
|
||||
void rcFreeHeightField(rcHeightfield* hf)
|
||||
{
|
||||
if (!hf) return;
|
||||
// Delete span array.
|
||||
rcFree(hf->spans);
|
||||
// Delete span pools.
|
||||
while (hf->pools)
|
||||
{
|
||||
rcSpanPool* next = hf->pools->next;
|
||||
rcFree(hf->pools);
|
||||
hf->pools = next;
|
||||
}
|
||||
hf->~rcHeightfield();
|
||||
rcFree(hf);
|
||||
}
|
||||
|
||||
|
|
@ -90,6 +126,27 @@ void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
|
|||
rcFree(chf);
|
||||
}
|
||||
|
||||
rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
|
||||
{
|
||||
rcHeightfieldLayerSet* lset = (rcHeightfieldLayerSet*)rcAlloc(sizeof(rcHeightfieldLayerSet), RC_ALLOC_PERM);
|
||||
memset(lset, 0, sizeof(rcHeightfieldLayerSet));
|
||||
return lset;
|
||||
}
|
||||
|
||||
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset)
|
||||
{
|
||||
if (!lset) return;
|
||||
for (int i = 0; i < lset->nlayers; ++i)
|
||||
{
|
||||
rcFree(lset->layers[i].heights);
|
||||
rcFree(lset->layers[i].areas);
|
||||
rcFree(lset->layers[i].cons);
|
||||
}
|
||||
rcFree(lset->layers);
|
||||
rcFree(lset);
|
||||
}
|
||||
|
||||
|
||||
rcContourSet* rcAllocContourSet()
|
||||
{
|
||||
rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM);
|
||||
|
|
@ -143,7 +200,6 @@ void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
|
|||
rcFree(dmesh);
|
||||
}
|
||||
|
||||
|
||||
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
|
||||
{
|
||||
// Calculate bounding box.
|
||||
|
|
@ -163,12 +219,16 @@ void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int*
|
|||
*h = (int)((bmax[2] - bmin[2])/cs+0.5f);
|
||||
}
|
||||
|
||||
bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height,
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocHeightfield, rcHeightfield
|
||||
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;
|
||||
|
|
@ -192,13 +252,21 @@ static void calcTriNormal(const float* v0, const float* v1, const float* v2, flo
|
|||
rcVnormalize(norm);
|
||||
}
|
||||
|
||||
void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
|
||||
const float* verts, int /*nv*/,
|
||||
/// @par
|
||||
///
|
||||
/// Only sets the area id's for the walkable triangles. Does not alter the
|
||||
/// area id's for unwalkable triangles.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
|
||||
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);
|
||||
rcIgnoreUnused(nv);
|
||||
|
||||
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
|
||||
|
||||
|
|
@ -214,13 +282,20 @@ void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
|
|||
}
|
||||
}
|
||||
|
||||
void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
|
||||
/// @par
|
||||
///
|
||||
/// Only sets the area id's for the unwalkable triangles. Does not alter the
|
||||
/// area id's for walkable triangles.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
|
||||
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);
|
||||
|
||||
|
|
@ -236,10 +311,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;
|
||||
|
|
@ -258,12 +332,21 @@ int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf)
|
|||
return spanCount;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// This is just the beginning of the process of fully building a compact heightfield.
|
||||
/// Various filters may be applied, then the distance field and regions built.
|
||||
/// E.g: #rcBuildDistanceField and #rcBuildRegions
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig
|
||||
bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
||||
rcHeightfield& hf, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||
|
||||
const int w = hf.width;
|
||||
const int h = hf.height;
|
||||
|
|
@ -369,13 +452,13 @@ bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const i
|
|||
if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
|
||||
{
|
||||
// Mark direction as walkable.
|
||||
const int idx = k - (int)nc.index;
|
||||
if (idx < 0 || idx > MAX_LAYERS)
|
||||
const int lidx = k - (int)nc.index;
|
||||
if (lidx < 0 || lidx > MAX_LAYERS)
|
||||
{
|
||||
tooHighNeighbour = rcMax(tooHighNeighbour, idx);
|
||||
tooHighNeighbour = rcMax(tooHighNeighbour, lidx);
|
||||
continue;
|
||||
}
|
||||
rcSetCon(s, dir, idx);
|
||||
rcSetCon(s, dir, lidx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -390,8 +473,6 @@ bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const i
|
|||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
|
||||
tooHighNeighbour, MAX_LAYERS);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -420,4 +501,4 @@ static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
|
|||
size += sizeof(rcCompactCell) * chf.width * chf.height;
|
||||
return size;
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
|
@ -19,8 +19,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
static void *rcAllocDefault(int size, rcAllocHint)
|
||||
static void *rcAllocDefault(size_t size, rcAllocHint)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
|
@ -33,35 +34,53 @@ static void rcFreeDefault(void *ptr)
|
|||
static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
|
||||
static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
|
||||
|
||||
/// @see rcAlloc, rcFree
|
||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
|
||||
{
|
||||
sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
|
||||
sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
|
||||
}
|
||||
|
||||
void* rcAlloc(int size, rcAllocHint hint)
|
||||
/// @see rcAllocSetCustom
|
||||
void* rcAlloc(size_t size, rcAllocHint hint)
|
||||
{
|
||||
return sRecastAllocFunc(size, hint);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// @warning This function leaves the value of @p ptr unchanged. So it still
|
||||
/// points to the same (now invalid) location, and not to null.
|
||||
///
|
||||
/// @see rcAllocSetCustom
|
||||
void rcFree(void* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
sRecastFreeFunc(ptr);
|
||||
}
|
||||
|
||||
/// @class rcIntArray
|
||||
///
|
||||
/// While it is possible to pre-allocate a specific array size during
|
||||
/// construction or by using the #resize method, certain methods will
|
||||
/// automatically resize the array as needed.
|
||||
///
|
||||
/// @warning The array memory is not initialized to zero when the size is
|
||||
/// manually set during construction or when using #resize.
|
||||
|
||||
void rcIntArray::resize(int n)
|
||||
/// @par
|
||||
///
|
||||
/// Using this method ensures the array is at least large enough to hold
|
||||
/// the specified number of elements. This can improve performance by
|
||||
/// avoiding auto-resizing during use.
|
||||
void rcIntArray::doResize(int n)
|
||||
{
|
||||
if (n > m_cap)
|
||||
{
|
||||
if (!m_cap) m_cap = n;
|
||||
while (m_cap < n) m_cap *= 2;
|
||||
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
|
||||
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
|
||||
rcFree(m_data);
|
||||
m_data = newData;
|
||||
}
|
||||
m_size = n;
|
||||
if (!m_cap) m_cap = n;
|
||||
while (m_cap < n) m_cap *= 2;
|
||||
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
|
||||
rcAssert(newData);
|
||||
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
|
||||
rcFree(m_data);
|
||||
m_data = newData;
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +26,14 @@
|
|||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Basically, any spans that are closer to a boundary or obstruction than the specified radius
|
||||
/// are marked as unwalkable.
|
||||
///
|
||||
/// This method is usually called immediately after the heightfield has been built.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
|
||||
bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
|
@ -34,7 +41,7 @@ bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
|
|||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
ctx->startTimer(RC_TIMER_ERODE_AREA);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_ERODE_AREA);
|
||||
|
||||
unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!dist)
|
||||
|
|
@ -54,14 +61,26 @@ bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
|
|||
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)
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
{
|
||||
dist[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
int nc = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
nc++;
|
||||
{
|
||||
const int nx = x + rcGetDirOffsetX(dir);
|
||||
const int ny = y + rcGetDirOffsetY(dir);
|
||||
const int nidx = (int)chf.cells[nx+ny*w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[nidx] != RC_NULL_AREA)
|
||||
{
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// At least one missing neighbour.
|
||||
if (nc != 4)
|
||||
|
|
@ -196,8 +215,6 @@ bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
|
|||
|
||||
rcFree(dist);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_ERODE_AREA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +230,12 @@ static void insertSort(unsigned char* a, const int n)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// This filter is usually applied after applying area id's using functions
|
||||
/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.
|
||||
///
|
||||
/// @see rcCompactHeightfield
|
||||
bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
|
@ -221,7 +243,7 @@ bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
|
|||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
ctx->startTimer(RC_TIMER_MEDIAN_AREA);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!areas)
|
||||
|
|
@ -282,18 +304,21 @@ bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
|
|||
memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
rcFree(areas);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MARK_BOX_AREA);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
|
|
@ -328,9 +353,6 @@ void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigne
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -348,13 +370,21 @@ static int pointInPoly(int nvert, const float* verts, const float* p)
|
|||
return c;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// The y-values of the polygon vertices are ignored. So the polygon is effectively
|
||||
/// projected onto the xz-plane at @p hmin, then extruded to @p hmax.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
|
||||
const float hmin, const float hmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
rcVcopy(bmin, verts);
|
||||
|
|
@ -411,6 +441,151 @@ void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
}
|
||||
|
||||
int rcOffsetPoly(const float* verts, const int nverts, const float offset,
|
||||
float* outVerts, const int maxOutVerts)
|
||||
{
|
||||
const float MITER_LIMIT = 1.20f;
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < nverts; i++)
|
||||
{
|
||||
const int a = (i+nverts-1) % nverts;
|
||||
const int b = i;
|
||||
const int c = (i+1) % nverts;
|
||||
const float* va = &verts[a*3];
|
||||
const float* vb = &verts[b*3];
|
||||
const float* vc = &verts[c*3];
|
||||
float dx0 = vb[0] - va[0];
|
||||
float dy0 = vb[2] - va[2];
|
||||
float d0 = dx0*dx0 + dy0*dy0;
|
||||
if (d0 > 1e-6f)
|
||||
{
|
||||
d0 = 1.0f/rcSqrt(d0);
|
||||
dx0 *= d0;
|
||||
dy0 *= d0;
|
||||
}
|
||||
float dx1 = vc[0] - vb[0];
|
||||
float dy1 = vc[2] - vb[2];
|
||||
float d1 = dx1*dx1 + dy1*dy1;
|
||||
if (d1 > 1e-6f)
|
||||
{
|
||||
d1 = 1.0f/rcSqrt(d1);
|
||||
dx1 *= d1;
|
||||
dy1 *= d1;
|
||||
}
|
||||
const float dlx0 = -dy0;
|
||||
const float dly0 = dx0;
|
||||
const float dlx1 = -dy1;
|
||||
const float dly1 = dx1;
|
||||
float cross = dx1*dy0 - dx0*dy1;
|
||||
float dmx = (dlx0 + dlx1) * 0.5f;
|
||||
float dmy = (dly0 + dly1) * 0.5f;
|
||||
float dmr2 = dmx*dmx + dmy*dmy;
|
||||
bool bevel = dmr2 * MITER_LIMIT*MITER_LIMIT < 1.0f;
|
||||
if (dmr2 > 1e-6f)
|
||||
{
|
||||
const float scale = 1.0f / dmr2;
|
||||
dmx *= scale;
|
||||
dmy *= scale;
|
||||
}
|
||||
|
||||
if (bevel && cross < 0.0f)
|
||||
{
|
||||
if (n+2 >= maxOutVerts)
|
||||
return 0;
|
||||
float d = (1.0f - (dx0*dx1 + dy0*dy1))*0.5f;
|
||||
outVerts[n*3+0] = vb[0] + (-dlx0+dx0*d)*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] + (-dly0+dy0*d)*offset;
|
||||
n++;
|
||||
outVerts[n*3+0] = vb[0] + (-dlx1-dx1*d)*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] + (-dly1-dy1*d)*offset;
|
||||
n++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n+1 >= maxOutVerts)
|
||||
return 0;
|
||||
outVerts[n*3+0] = vb[0] - dmx*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] - dmy*offset;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkCylinderArea(rcContext* ctx, const float* pos,
|
||||
const float r, const float h, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MARK_CYLINDER_AREA);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
bmin[0] = pos[0] - r;
|
||||
bmin[1] = pos[1];
|
||||
bmin[2] = pos[2] - r;
|
||||
bmax[0] = pos[0] + r;
|
||||
bmax[1] = pos[1] + h;
|
||||
bmax[2] = pos[2] + r;
|
||||
const float r2 = r*r;
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
const float sx = chf.bmin[0] + (x+0.5f)*chf.cs;
|
||||
const float sz = chf.bmin[2] + (z+0.5f)*chf.cs;
|
||||
const float dx = sx - pos[0];
|
||||
const float dz = sz - pos[2];
|
||||
|
||||
if (dx*dx + dz*dz < r2)
|
||||
{
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
|
@ -36,7 +37,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
|
|||
unsigned int regs[4] = {0,0,0,0};
|
||||
|
||||
// Combine region and area codes in order to prevent
|
||||
// border vertices which are in between two areas to be removed.
|
||||
// border vertices which are in between two areas to be removed.
|
||||
regs[0] = chf.spans[i].reg | (chf.areas[i] << 16);
|
||||
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
|
|
@ -187,27 +188,6 @@ static float distancePtSeg(const int x, const int z,
|
|||
const int px, const int pz,
|
||||
const int qx, const int qz)
|
||||
{
|
||||
/* float pqx = (float)(qx - px);
|
||||
float pqy = (float)(qy - py);
|
||||
float pqz = (float)(qz - pz);
|
||||
float dx = (float)(x - px);
|
||||
float dy = (float)(y - py);
|
||||
float dz = (float)(z - pz);
|
||||
float d = pqx*pqx + pqy*pqy + pqz*pqz;
|
||||
float t = pqx*dx + pqy*dy + pqz*dz;
|
||||
if (d > 0)
|
||||
t /= d;
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
else if (t > 1)
|
||||
t = 1;
|
||||
|
||||
dx = px + t*pqx - x;
|
||||
dy = py + t*pqy - y;
|
||||
dz = pz + t*pqz - z;
|
||||
|
||||
return dx*dx + dy*dy + dz*dz;*/
|
||||
|
||||
float pqx = (float)(qx - px);
|
||||
float pqz = (float)(qz - pz);
|
||||
float dx = (float)(x - px);
|
||||
|
|
@ -257,13 +237,13 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
|
|||
simplified.push(points[i*4+2]);
|
||||
simplified.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (simplified.size() == 0)
|
||||
{
|
||||
// If there is no connections at all,
|
||||
// create some initial points for the simplification process.
|
||||
// create some initial points for the simplification process.
|
||||
// Find lower-left and upper-right vertices of the contour.
|
||||
int llx = points[0];
|
||||
int lly = points[1];
|
||||
|
|
@ -311,19 +291,19 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
|
|||
{
|
||||
int ii = (i+1) % (simplified.size()/4);
|
||||
|
||||
const int ax = simplified[i*4+0];
|
||||
const int az = simplified[i*4+2];
|
||||
const int ai = simplified[i*4+3];
|
||||
|
||||
const int bx = simplified[ii*4+0];
|
||||
const int bz = simplified[ii*4+2];
|
||||
const int bi = simplified[ii*4+3];
|
||||
int ax = simplified[i*4+0];
|
||||
int az = simplified[i*4+2];
|
||||
int ai = simplified[i*4+3];
|
||||
|
||||
int bx = simplified[ii*4+0];
|
||||
int bz = simplified[ii*4+2];
|
||||
int bi = simplified[ii*4+3];
|
||||
|
||||
// Find maximum deviation from the segment.
|
||||
float maxd = 0;
|
||||
int maxi = -1;
|
||||
int ci, cinc, endi;
|
||||
|
||||
|
||||
// Traverse the segment in lexilogical order so that the
|
||||
// max deviation is calculated similarly when traversing
|
||||
// opposite segments.
|
||||
|
|
@ -338,9 +318,11 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
|
|||
cinc = pn-1;
|
||||
ci = (bi+cinc) % pn;
|
||||
endi = ai;
|
||||
rcSwap(ax, bx);
|
||||
rcSwap(az, bz);
|
||||
}
|
||||
|
||||
// Tessellate only outer edges oredges between areas.
|
||||
// Tessellate only outer edges or edges between areas.
|
||||
if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 ||
|
||||
(points[ci*4+3] & RC_AREA_BORDER))
|
||||
{
|
||||
|
|
@ -397,11 +379,11 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
|
|||
const int bx = simplified[ii*4+0];
|
||||
const int bz = simplified[ii*4+2];
|
||||
const int bi = simplified[ii*4+3];
|
||||
|
||||
|
||||
// Find maximum deviation from the segment.
|
||||
int maxi = -1;
|
||||
int ci = (ai+1) % pn;
|
||||
|
||||
|
||||
// Tessellate only outer edges or edges between areas.
|
||||
bool tess = false;
|
||||
// Wall edges.
|
||||
|
|
@ -469,32 +451,6 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
|
|||
|
||||
}
|
||||
|
||||
static void removeDegenerateSegments(rcIntArray& simplified)
|
||||
{
|
||||
// Remove adjacent vertices which are equal on xz-plane,
|
||||
// or else the triangulator will get confused.
|
||||
for (int i = 0; i < simplified.size()/4; ++i)
|
||||
{
|
||||
int ni = i+1;
|
||||
if (ni >= (simplified.size()/4))
|
||||
ni = 0;
|
||||
|
||||
if (simplified[i*4+0] == simplified[ni*4+0] &&
|
||||
simplified[i*4+2] == simplified[ni*4+2])
|
||||
{
|
||||
// Degenerate segment, remove.
|
||||
for (int j = i; j < simplified.size()/4-1; ++j)
|
||||
{
|
||||
simplified[j*4+0] = simplified[(j+1)*4+0];
|
||||
simplified[j*4+1] = simplified[(j+1)*4+1];
|
||||
simplified[j*4+2] = simplified[(j+1)*4+2];
|
||||
simplified[j*4+3] = simplified[(j+1)*4+3];
|
||||
}
|
||||
simplified.resize(simplified.size()-4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int calcAreaOfPolygon2D(const int* verts, const int nverts)
|
||||
{
|
||||
int area = 0;
|
||||
|
|
@ -507,54 +463,155 @@ static int calcAreaOfPolygon2D(const int* verts, const int nverts)
|
|||
return (area+1) / 2;
|
||||
}
|
||||
|
||||
inline bool ileft(const int* a, const int* b, const int* c)
|
||||
// TODO: these are the same as in RecastMesh.cpp, consider using the same.
|
||||
// Last time I checked the if version got compiled using cmov, which was a lot faster than module (with idiv).
|
||||
inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
|
||||
inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
|
||||
|
||||
inline int area2(const int* a, const int* b, const int* c)
|
||||
{
|
||||
return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0;
|
||||
return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]);
|
||||
}
|
||||
|
||||
static void getClosestIndices(const int* vertsa, const int nvertsa,
|
||||
const int* vertsb, const int nvertsb,
|
||||
int& ia, int& ib)
|
||||
// Exclusive or: true iff exactly one argument is true.
|
||||
// The arguments are negated to ensure that they are 0/1
|
||||
// values. Then the bitwise Xor operator may apply.
|
||||
// (This idea is due to Michael Baldwin.)
|
||||
inline bool xorb(bool x, bool y)
|
||||
{
|
||||
int closestDist = 0xfffffff;
|
||||
ia = -1, ib = -1;
|
||||
for (int i = 0; i < nvertsa; ++i)
|
||||
return !x ^ !y;
|
||||
}
|
||||
|
||||
// Returns true iff c is strictly to the left of the directed
|
||||
// line through a to b.
|
||||
inline bool left(const int* a, const int* b, const int* c)
|
||||
{
|
||||
return area2(a, b, c) < 0;
|
||||
}
|
||||
|
||||
inline bool leftOn(const int* a, const int* b, const int* c)
|
||||
{
|
||||
return area2(a, b, c) <= 0;
|
||||
}
|
||||
|
||||
inline bool collinear(const int* a, const int* b, const int* c)
|
||||
{
|
||||
return area2(a, b, c) == 0;
|
||||
}
|
||||
|
||||
// Returns true iff ab properly intersects cd: they share
|
||||
// a point interior to both segments. The properness of the
|
||||
// intersection is ensured by using strict leftness.
|
||||
static bool intersectProp(const int* a, const int* b, const int* c, const int* d)
|
||||
{
|
||||
// Eliminate improper cases.
|
||||
if (collinear(a,b,c) || collinear(a,b,d) ||
|
||||
collinear(c,d,a) || collinear(c,d,b))
|
||||
return false;
|
||||
|
||||
return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b));
|
||||
}
|
||||
|
||||
// Returns T iff (a,b,c) are collinear and point c lies
|
||||
// on the closed segement ab.
|
||||
static bool between(const int* a, const int* b, const int* c)
|
||||
{
|
||||
if (!collinear(a, b, c))
|
||||
return false;
|
||||
// If ab not vertical, check betweenness on x; else on y.
|
||||
if (a[0] != b[0])
|
||||
return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0]));
|
||||
else
|
||||
return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2]));
|
||||
}
|
||||
|
||||
// Returns true iff segments ab and cd intersect, properly or improperly.
|
||||
static bool intersect(const int* a, const int* b, const int* c, const int* d)
|
||||
{
|
||||
if (intersectProp(a, b, c, d))
|
||||
return true;
|
||||
else if (between(a, b, c) || between(a, b, d) ||
|
||||
between(c, d, a) || between(c, d, b))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool vequal(const int* a, const int* b)
|
||||
{
|
||||
return a[0] == b[0] && a[2] == b[2];
|
||||
}
|
||||
|
||||
static bool intersectSegCountour(const int* d0, const int* d1, int i, int n, const int* verts)
|
||||
{
|
||||
// For each edge (k,k+1) of P
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
const int in = (i+1) % nvertsa;
|
||||
const int ip = (i+nvertsa-1) % nvertsa;
|
||||
const int* va = &vertsa[i*4];
|
||||
const int* van = &vertsa[in*4];
|
||||
const int* vap = &vertsa[ip*4];
|
||||
int k1 = next(k, n);
|
||||
// Skip edges incident to i.
|
||||
if (i == k || i == k1)
|
||||
continue;
|
||||
const int* p0 = &verts[k * 4];
|
||||
const int* p1 = &verts[k1 * 4];
|
||||
if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < nvertsb; ++j)
|
||||
if (intersect(d0, d1, p0, p1))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool inCone(int i, int n, const int* verts, const int* pj)
|
||||
{
|
||||
const int* pi = &verts[i * 4];
|
||||
const int* pi1 = &verts[next(i, n) * 4];
|
||||
const int* pin1 = &verts[prev(i, n) * 4];
|
||||
|
||||
// If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
|
||||
if (leftOn(pin1, pi, pi1))
|
||||
return left(pi, pj, pin1) && left(pj, pi, pi1);
|
||||
// Assume (i-1,i,i+1) not collinear.
|
||||
// else P[i] is reflex.
|
||||
return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
|
||||
}
|
||||
|
||||
|
||||
static void removeDegenerateSegments(rcIntArray& simplified)
|
||||
{
|
||||
// Remove adjacent vertices which are equal on xz-plane,
|
||||
// or else the triangulator will get confused.
|
||||
int npts = simplified.size()/4;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
int ni = next(i, npts);
|
||||
|
||||
if (vequal(&simplified[i*4], &simplified[ni*4]))
|
||||
{
|
||||
const int* vb = &vertsb[j*4];
|
||||
// vb must be "infront" of va.
|
||||
if (ileft(vap,va,vb) && ileft(va,van,vb))
|
||||
// Degenerate segment, remove.
|
||||
for (int j = i; j < simplified.size()/4-1; ++j)
|
||||
{
|
||||
const int dx = vb[0] - va[0];
|
||||
const int dz = vb[2] - va[2];
|
||||
const int d = dx*dx + dz*dz;
|
||||
if (d < closestDist)
|
||||
{
|
||||
ia = i;
|
||||
ib = j;
|
||||
closestDist = d;
|
||||
}
|
||||
simplified[j*4+0] = simplified[(j+1)*4+0];
|
||||
simplified[j*4+1] = simplified[(j+1)*4+1];
|
||||
simplified[j*4+2] = simplified[(j+1)*4+2];
|
||||
simplified[j*4+3] = simplified[(j+1)*4+3];
|
||||
}
|
||||
simplified.resize(simplified.size()-4);
|
||||
npts--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
|
||||
{
|
||||
const int maxVerts = ca.nverts + cb.nverts + 2;
|
||||
int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
|
||||
if (!verts)
|
||||
return false;
|
||||
|
||||
|
||||
int nv = 0;
|
||||
|
||||
|
||||
// Copy contour A.
|
||||
for (int i = 0; i <= ca.nverts; ++i)
|
||||
{
|
||||
|
|
@ -582,7 +639,7 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
|
|||
rcFree(ca.verts);
|
||||
ca.verts = verts;
|
||||
ca.nverts = nv;
|
||||
|
||||
|
||||
rcFree(cb.verts);
|
||||
cb.verts = 0;
|
||||
cb.nverts = 0;
|
||||
|
|
@ -590,6 +647,180 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
|
|||
return true;
|
||||
}
|
||||
|
||||
struct rcContourHole
|
||||
{
|
||||
rcContour* contour;
|
||||
int minx, minz, leftmost;
|
||||
};
|
||||
|
||||
struct rcContourRegion
|
||||
{
|
||||
rcContour* outline;
|
||||
rcContourHole* holes;
|
||||
int nholes;
|
||||
};
|
||||
|
||||
struct rcPotentialDiagonal
|
||||
{
|
||||
int vert;
|
||||
int dist;
|
||||
};
|
||||
|
||||
// Finds the lowest leftmost vertex of a contour.
|
||||
static void findLeftMostVertex(rcContour* contour, int* minx, int* minz, int* leftmost)
|
||||
{
|
||||
*minx = contour->verts[0];
|
||||
*minz = contour->verts[2];
|
||||
*leftmost = 0;
|
||||
for (int i = 1; i < contour->nverts; i++)
|
||||
{
|
||||
const int x = contour->verts[i*4+0];
|
||||
const int z = contour->verts[i*4+2];
|
||||
if (x < *minx || (x == *minx && z < *minz))
|
||||
{
|
||||
*minx = x;
|
||||
*minz = z;
|
||||
*leftmost = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int compareHoles(const void* va, const void* vb)
|
||||
{
|
||||
const rcContourHole* a = (const rcContourHole*)va;
|
||||
const rcContourHole* b = (const rcContourHole*)vb;
|
||||
if (a->minx == b->minx)
|
||||
{
|
||||
if (a->minz < b->minz)
|
||||
return -1;
|
||||
if (a->minz > b->minz)
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a->minx < b->minx)
|
||||
return -1;
|
||||
if (a->minx > b->minx)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int compareDiagDist(const void* va, const void* vb)
|
||||
{
|
||||
const rcPotentialDiagonal* a = (const rcPotentialDiagonal*)va;
|
||||
const rcPotentialDiagonal* b = (const rcPotentialDiagonal*)vb;
|
||||
if (a->dist < b->dist)
|
||||
return -1;
|
||||
if (a->dist > b->dist)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
|
||||
{
|
||||
// Sort holes from left to right.
|
||||
for (int i = 0; i < region.nholes; i++)
|
||||
findLeftMostVertex(region.holes[i].contour, ®ion.holes[i].minx, ®ion.holes[i].minz, ®ion.holes[i].leftmost);
|
||||
|
||||
qsort(region.holes, region.nholes, sizeof(rcContourHole), compareHoles);
|
||||
|
||||
int maxVerts = region.outline->nverts;
|
||||
for (int i = 0; i < region.nholes; i++)
|
||||
maxVerts += region.holes[i].contour->nverts;
|
||||
|
||||
rcScopedDelete<rcPotentialDiagonal> diags((rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP));
|
||||
if (!diags)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "mergeRegionHoles: Failed to allocated diags %d.", maxVerts);
|
||||
return;
|
||||
}
|
||||
|
||||
rcContour* outline = region.outline;
|
||||
|
||||
// Merge holes into the outline one by one.
|
||||
for (int i = 0; i < region.nholes; i++)
|
||||
{
|
||||
rcContour* hole = region.holes[i].contour;
|
||||
|
||||
int index = -1;
|
||||
int bestVertex = region.holes[i].leftmost;
|
||||
for (int iter = 0; iter < hole->nverts; iter++)
|
||||
{
|
||||
// Find potential diagonals.
|
||||
// The 'best' vertex must be in the cone described by 3 cosequtive vertices of the outline.
|
||||
// ..o j-1
|
||||
// |
|
||||
// | * best
|
||||
// |
|
||||
// j o-----o j+1
|
||||
// :
|
||||
int ndiags = 0;
|
||||
const int* corner = &hole->verts[bestVertex*4];
|
||||
for (int j = 0; j < outline->nverts; j++)
|
||||
{
|
||||
if (inCone(j, outline->nverts, outline->verts, corner))
|
||||
{
|
||||
int dx = outline->verts[j*4+0] - corner[0];
|
||||
int dz = outline->verts[j*4+2] - corner[2];
|
||||
diags[ndiags].vert = j;
|
||||
diags[ndiags].dist = dx*dx + dz*dz;
|
||||
ndiags++;
|
||||
}
|
||||
}
|
||||
// Sort potential diagonals by distance, we want to make the connection as short as possible.
|
||||
qsort(diags, ndiags, sizeof(rcPotentialDiagonal), compareDiagDist);
|
||||
|
||||
// Find a diagonal that is not intersecting the outline not the remaining holes.
|
||||
index = -1;
|
||||
for (int j = 0; j < ndiags; j++)
|
||||
{
|
||||
const int* pt = &outline->verts[diags[j].vert*4];
|
||||
bool intersect = intersectSegCountour(pt, corner, diags[i].vert, outline->nverts, outline->verts);
|
||||
for (int k = i; k < region.nholes && !intersect; k++)
|
||||
intersect |= intersectSegCountour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts);
|
||||
if (!intersect)
|
||||
{
|
||||
index = diags[j].vert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If found non-intersecting diagonal, stop looking.
|
||||
if (index != -1)
|
||||
break;
|
||||
// All the potential diagonals for the current vertex were intersecting, try next vertex.
|
||||
bestVertex = (bestVertex + 1) % hole->nverts;
|
||||
}
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to find merge points for %p and %p.", region.outline, hole);
|
||||
continue;
|
||||
}
|
||||
if (!mergeContours(*region.outline, *hole, index, bestVertex))
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to merge contours %p and %p.", region.outline, hole);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen
|
||||
/// parameters control how closely the simplified contours will match the raw contours.
|
||||
///
|
||||
/// Simplified contours are generated such that the vertices for portals between areas match up.
|
||||
/// (They are considered mandatory vertices.)
|
||||
///
|
||||
/// Setting @p maxEdgeLength to zero will disabled the edge length feature.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
|
||||
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const float maxError, const int maxEdgeLen,
|
||||
rcContourSet& cset, const int buildFlags)
|
||||
|
|
@ -598,13 +829,27 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
const int borderSize = chf.borderSize;
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_BUILD_CONTOURS);
|
||||
|
||||
rcVcopy(cset.bmin, chf.bmin);
|
||||
rcVcopy(cset.bmax, chf.bmax);
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
const float pad = borderSize*chf.cs;
|
||||
cset.bmin[0] += pad;
|
||||
cset.bmin[2] += pad;
|
||||
cset.bmax[0] -= pad;
|
||||
cset.bmax[2] -= pad;
|
||||
}
|
||||
cset.cs = chf.cs;
|
||||
cset.ch = chf.ch;
|
||||
cset.width = chf.width - chf.borderSize*2;
|
||||
cset.height = chf.height - chf.borderSize*2;
|
||||
cset.borderSize = chf.borderSize;
|
||||
cset.maxError = maxError;
|
||||
|
||||
int maxContours = rcMax((int)chf.maxRegions, 8);
|
||||
cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
|
||||
|
|
@ -612,7 +857,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
return false;
|
||||
cset.nconts = 0;
|
||||
|
||||
rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<unsigned char> flags((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP));
|
||||
if (!flags)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount);
|
||||
|
|
@ -656,8 +901,6 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
|
||||
|
||||
rcIntArray verts(256);
|
||||
rcIntArray simplified(64);
|
||||
|
||||
|
|
@ -680,9 +923,16 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
|
||||
verts.resize(0);
|
||||
simplified.resize(0);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
walkContour(x, y, i, chf, flags, verts);
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
|
||||
simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
|
||||
removeDegenerateSegments(simplified);
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
|
||||
|
||||
|
||||
// Store region->contour remap info.
|
||||
// Create contour.
|
||||
|
|
@ -691,7 +941,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
if (cset.nconts >= maxContours)
|
||||
{
|
||||
// Allocate more contours.
|
||||
// This can happen when there are tiny holes in the heightfield.
|
||||
// This happens when a region has holes.
|
||||
const int oldMax = maxContours;
|
||||
maxContours *= 2;
|
||||
rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
|
||||
|
|
@ -704,10 +954,10 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
}
|
||||
rcFree(cset.conts);
|
||||
cset.conts = newConts;
|
||||
|
||||
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours);
|
||||
}
|
||||
|
||||
|
||||
rcContour* cont = &cset.conts[cset.nconts++];
|
||||
|
||||
cont->nverts = simplified.size()/4;
|
||||
|
|
@ -718,6 +968,16 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
return false;
|
||||
}
|
||||
memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4);
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
for (int j = 0; j < cont->nverts; ++j)
|
||||
{
|
||||
int* v = &cont->verts[j*4];
|
||||
v[0] -= borderSize;
|
||||
v[2] -= borderSize;
|
||||
}
|
||||
}
|
||||
|
||||
cont->nrverts = verts.size()/4;
|
||||
cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM);
|
||||
|
|
@ -727,17 +987,16 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
return false;
|
||||
}
|
||||
memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4);
|
||||
|
||||
/* cont->cx = cont->cy = cont->cz = 0;
|
||||
for (int i = 0; i < cont->nverts; ++i)
|
||||
if (borderSize > 0)
|
||||
{
|
||||
cont->cx += cont->verts[i*4+0];
|
||||
cont->cy += cont->verts[i*4+1];
|
||||
cont->cz += cont->verts[i*4+2];
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
for (int j = 0; j < cont->nrverts; ++j)
|
||||
{
|
||||
int* v = &cont->rverts[j*4];
|
||||
v[0] -= borderSize;
|
||||
v[2] -= borderSize;
|
||||
}
|
||||
}
|
||||
cont->cx /= cont->nverts;
|
||||
cont->cy /= cont->nverts;
|
||||
cont->cz /= cont->nverts;*/
|
||||
|
||||
cont->reg = reg;
|
||||
cont->area = area;
|
||||
|
|
@ -746,57 +1005,101 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
|||
}
|
||||
}
|
||||
|
||||
// Check and merge droppings.
|
||||
// Sometimes the previous algorithms can fail and create several contours
|
||||
// per area. This pass will try to merge the holes into the main region.
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
// Merge holes if needed.
|
||||
if (cset.nconts > 0)
|
||||
{
|
||||
rcContour& cont = cset.conts[i];
|
||||
// Check if the contour is would backwards.
|
||||
if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
|
||||
// Calculate winding of all polygons.
|
||||
rcScopedDelete<char> winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP));
|
||||
if (!winding)
|
||||
{
|
||||
// Find another contour which has the same region ID.
|
||||
int mergeIdx = -1;
|
||||
for (int j = 0; j < cset.nconts; ++j)
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts);
|
||||
return false;
|
||||
}
|
||||
int nholes = 0;
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
rcContour& cont = cset.conts[i];
|
||||
// If the contour is wound backwards, it is a hole.
|
||||
winding[i] = calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0 ? -1 : 1;
|
||||
if (winding[i] < 0)
|
||||
nholes++;
|
||||
}
|
||||
|
||||
if (nholes > 0)
|
||||
{
|
||||
// Collect outline contour and holes contours per region.
|
||||
// We assume that there is one outline and multiple holes.
|
||||
const int nregions = chf.maxRegions+1;
|
||||
rcScopedDelete<rcContourRegion> regions((rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP));
|
||||
if (!regions)
|
||||
{
|
||||
if (i == j) continue;
|
||||
if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg)
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions);
|
||||
return false;
|
||||
}
|
||||
memset(regions, 0, sizeof(rcContourRegion)*nregions);
|
||||
|
||||
rcScopedDelete<rcContourHole> holes((rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP));
|
||||
if (!holes)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts);
|
||||
return false;
|
||||
}
|
||||
memset(holes, 0, sizeof(rcContourHole)*cset.nconts);
|
||||
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
rcContour& cont = cset.conts[i];
|
||||
// Positively would contours are outlines, negative holes.
|
||||
if (winding[i] > 0)
|
||||
{
|
||||
// Make sure the polygon is correctly oriented.
|
||||
if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts))
|
||||
{
|
||||
mergeIdx = j;
|
||||
break;
|
||||
}
|
||||
if (regions[cont.reg].outline)
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Multiple outlines for region %d.", cont.reg);
|
||||
regions[cont.reg].outline = &cont;
|
||||
}
|
||||
else
|
||||
{
|
||||
regions[cont.reg].nholes++;
|
||||
}
|
||||
}
|
||||
if (mergeIdx == -1)
|
||||
int index = 0;
|
||||
for (int i = 0; i < nregions; i++)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
|
||||
}
|
||||
else
|
||||
{
|
||||
rcContour& mcont = cset.conts[mergeIdx];
|
||||
// Merge by closest points.
|
||||
int ia = 0, ib = 0;
|
||||
getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib);
|
||||
if (ia == -1 || ib == -1)
|
||||
if (regions[i].nholes > 0)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx);
|
||||
continue;
|
||||
regions[i].holes = &holes[index];
|
||||
index += regions[i].nholes;
|
||||
regions[i].nholes = 0;
|
||||
}
|
||||
if (!mergeContours(mcont, cont, ia, ib))
|
||||
}
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
rcContour& cont = cset.conts[i];
|
||||
rcContourRegion& reg = regions[cont.reg];
|
||||
if (winding[i] < 0)
|
||||
reg.holes[reg.nholes++].contour = &cont;
|
||||
}
|
||||
|
||||
// Finally merge each regions holes into the outline.
|
||||
for (int i = 0; i < nregions; i++)
|
||||
{
|
||||
rcContourRegion& reg = regions[i];
|
||||
if (!reg.nholes) continue;
|
||||
|
||||
if (reg.outline)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
|
||||
continue;
|
||||
mergeRegionHoles(ctx, reg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The region does not have an outline.
|
||||
// This can happen if the contour becaomes selfoverlapping because of
|
||||
// too aggressive simplification settings.
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Bad outline for region %d, contour simplification is likely too aggressive.", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -22,12 +22,22 @@
|
|||
#include "Recast.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Allows the formation of walkable regions that will flow over low lying
|
||||
/// objects such as curbs, and up structures such as stairways.
|
||||
///
|
||||
/// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
|
||||
///
|
||||
/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call
|
||||
/// #rcFilterLedgeSpans after calling this filter.
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
|
|
@ -57,16 +67,24 @@ void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
}
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
|
||||
/// from the current span's maximum.
|
||||
/// This method removes the impact of the overestimation of conservative voxelization
|
||||
/// so the resulting mesh will not have regions hanging in the air over ledges.
|
||||
///
|
||||
/// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
||||
rcHeightfield& solid)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_FILTER_BORDER);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_FILTER_BORDER);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
|
|
@ -136,26 +154,31 @@ void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walk
|
|||
// The current span is close to a ledge if the drop to any
|
||||
// neighbour span is less than the walkableClimb.
|
||||
if (minh < -walkableClimb)
|
||||
{
|
||||
s->area = RC_NULL_AREA;
|
||||
|
||||
}
|
||||
// If the difference between all neighbours is too large,
|
||||
// we are at steep slope, mark the span as ledge.
|
||||
if ((asmax - asmin) > walkableClimb)
|
||||
else if ((asmax - asmin) > walkableClimb)
|
||||
{
|
||||
s->area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_FILTER_BORDER);
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// For this filter, the clearance above the span is the distance from the span's
|
||||
/// maximum to the next higher span's minimum. (Same grid column.)
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_FILTER_WALKABLE);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_FILTER_WALKABLE);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
|
|
@ -176,6 +199,4 @@ void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeight
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_FILTER_WALKABLE);
|
||||
}
|
||||
|
|
@ -0,0 +1,644 @@
|
|||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <float.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
// Must be 255 or smaller (not 256) because layer IDs are stored as
|
||||
// a byte where 255 is a special value.
|
||||
static const int RC_MAX_LAYERS = 63;
|
||||
static const int RC_MAX_NEIS = 16;
|
||||
|
||||
struct rcLayerRegion
|
||||
{
|
||||
unsigned char layers[RC_MAX_LAYERS];
|
||||
unsigned char neis[RC_MAX_NEIS];
|
||||
unsigned short ymin, ymax;
|
||||
unsigned char layerId; // Layer ID
|
||||
unsigned char nlayers; // Layer count
|
||||
unsigned char nneis; // Neighbour count
|
||||
unsigned char base; // Flag indicating if the region is the base of merged regions.
|
||||
};
|
||||
|
||||
|
||||
static bool contains(const unsigned char* a, const unsigned char an, const unsigned char v)
|
||||
{
|
||||
const int n = (int)an;
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
if (a[i] == v)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool addUnique(unsigned char* a, unsigned char& an, int anMax, unsigned char v)
|
||||
{
|
||||
if (contains(a, an, v))
|
||||
return true;
|
||||
|
||||
if ((int)an >= anMax)
|
||||
return false;
|
||||
|
||||
a[an] = v;
|
||||
an++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline bool overlapRange(const unsigned short amin, const unsigned short amax,
|
||||
const unsigned short bmin, const unsigned short bmax)
|
||||
{
|
||||
return (amin > bmax || amax < bmin) ? false : true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct rcLayerSweepSpan
|
||||
{
|
||||
unsigned short ns; // number samples
|
||||
unsigned char id; // region id
|
||||
unsigned char nei; // neighbour id
|
||||
};
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
|
||||
bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int walkableHeight,
|
||||
rcHeightfieldLayerSet& lset)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
rcScopedTimer timer(ctx, RC_TIMER_BUILD_LAYERS);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
rcScopedDelete<unsigned char> srcReg((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP));
|
||||
if (!srcReg)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
const int nsweeps = chf.width;
|
||||
rcScopedDelete<rcLayerSweepSpan> sweeps((rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP));
|
||||
if (!sweeps)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Partition walkable area into monotone regions.
|
||||
int prevCount[256];
|
||||
unsigned char regId = 0;
|
||||
|
||||
for (int y = borderSize; y < h-borderSize; ++y)
|
||||
{
|
||||
memset(prevCount,0,sizeof(int)*regId);
|
||||
unsigned char sweepId = 0;
|
||||
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA) continue;
|
||||
|
||||
unsigned char sid = 0xff;
|
||||
|
||||
// -x
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
const int ay = y + rcGetDirOffsetY(0);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff)
|
||||
sid = srcReg[ai];
|
||||
}
|
||||
|
||||
if (sid == 0xff)
|
||||
{
|
||||
sid = sweepId++;
|
||||
sweeps[sid].nei = 0xff;
|
||||
sweeps[sid].ns = 0;
|
||||
}
|
||||
|
||||
// -y
|
||||
if (rcGetCon(s,3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
const int ay = y + rcGetDirOffsetY(3);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
const unsigned char nr = srcReg[ai];
|
||||
if (nr != 0xff)
|
||||
{
|
||||
// Set neighbour when first valid neighbour is encoutered.
|
||||
if (sweeps[sid].ns == 0)
|
||||
sweeps[sid].nei = nr;
|
||||
|
||||
if (sweeps[sid].nei == nr)
|
||||
{
|
||||
// Update existing neighbour
|
||||
sweeps[sid].ns++;
|
||||
prevCount[nr]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is hit if there is nore than one neighbour.
|
||||
// Invalidate the neighbour.
|
||||
sweeps[sid].nei = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcReg[i] = sid;
|
||||
}
|
||||
}
|
||||
|
||||
// Create unique ID.
|
||||
for (int i = 0; i < sweepId; ++i)
|
||||
{
|
||||
// If the neighbour is set and there is only one continuous connection to it,
|
||||
// the sweep will be merged with the previous one, else new region is created.
|
||||
if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns)
|
||||
{
|
||||
sweeps[i].id = sweeps[i].nei;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (regId == 255)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow.");
|
||||
return false;
|
||||
}
|
||||
sweeps[i].id = regId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Remap local sweep ids to region ids.
|
||||
for (int x = borderSize; x < w-borderSize; ++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 (srcReg[i] != 0xff)
|
||||
srcReg[i] = sweeps[srcReg[i]].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate and init layer regions.
|
||||
const int nregs = (int)regId;
|
||||
rcScopedDelete<rcLayerRegion> regs((rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP));
|
||||
if (!regs)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs);
|
||||
return false;
|
||||
}
|
||||
memset(regs, 0, sizeof(rcLayerRegion)*nregs);
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
regs[i].layerId = 0xff;
|
||||
regs[i].ymin = 0xffff;
|
||||
regs[i].ymax = 0;
|
||||
}
|
||||
|
||||
// Find region neighbours and overlapping regions.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
unsigned char lregs[RC_MAX_LAYERS];
|
||||
int nlregs = 0;
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
const unsigned char ri = srcReg[i];
|
||||
if (ri == 0xff) continue;
|
||||
|
||||
regs[ri].ymin = rcMin(regs[ri].ymin, s.y);
|
||||
regs[ri].ymax = rcMax(regs[ri].ymax, s.y);
|
||||
|
||||
// Collect all region layers.
|
||||
if (nlregs < RC_MAX_LAYERS)
|
||||
lregs[nlregs++] = ri;
|
||||
|
||||
// Update neighbours
|
||||
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*w].index + rcGetCon(s, dir);
|
||||
const unsigned char rai = srcReg[ai];
|
||||
if (rai != 0xff && rai != ri)
|
||||
{
|
||||
// Don't check return value -- if we cannot add the neighbor
|
||||
// it will just cause a few more regions to be created, which
|
||||
// is fine.
|
||||
addUnique(regs[ri].neis, regs[ri].nneis, RC_MAX_NEIS, rai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update overlapping regions.
|
||||
for (int i = 0; i < nlregs-1; ++i)
|
||||
{
|
||||
for (int j = i+1; j < nlregs; ++j)
|
||||
{
|
||||
if (lregs[i] != lregs[j])
|
||||
{
|
||||
rcLayerRegion& ri = regs[lregs[i]];
|
||||
rcLayerRegion& rj = regs[lregs[j]];
|
||||
|
||||
if (!addUnique(ri.layers, ri.nlayers, RC_MAX_LAYERS, lregs[j]) ||
|
||||
!addUnique(rj.layers, rj.nlayers, RC_MAX_LAYERS, lregs[i]))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Create 2D layers from regions.
|
||||
unsigned char layerId = 0;
|
||||
|
||||
static const int MAX_STACK = 64;
|
||||
unsigned char stack[MAX_STACK];
|
||||
int nstack = 0;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion& root = regs[i];
|
||||
// Skip already visited.
|
||||
if (root.layerId != 0xff)
|
||||
continue;
|
||||
|
||||
// Start search.
|
||||
root.layerId = layerId;
|
||||
root.base = 1;
|
||||
|
||||
nstack = 0;
|
||||
stack[nstack++] = (unsigned char)i;
|
||||
|
||||
while (nstack)
|
||||
{
|
||||
// Pop front
|
||||
rcLayerRegion& reg = regs[stack[0]];
|
||||
nstack--;
|
||||
for (int j = 0; j < nstack; ++j)
|
||||
stack[j] = stack[j+1];
|
||||
|
||||
const int nneis = (int)reg.nneis;
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
{
|
||||
const unsigned char nei = reg.neis[j];
|
||||
rcLayerRegion& regn = regs[nei];
|
||||
// Skip already visited.
|
||||
if (regn.layerId != 0xff)
|
||||
continue;
|
||||
// Skip if the neighbour is overlapping root region.
|
||||
if (contains(root.layers, root.nlayers, nei))
|
||||
continue;
|
||||
// Skip if the height range would become too large.
|
||||
const int ymin = rcMin(root.ymin, regn.ymin);
|
||||
const int ymax = rcMax(root.ymax, regn.ymax);
|
||||
if ((ymax - ymin) >= 255)
|
||||
continue;
|
||||
|
||||
if (nstack < MAX_STACK)
|
||||
{
|
||||
// Deepen
|
||||
stack[nstack++] = (unsigned char)nei;
|
||||
|
||||
// Mark layer id
|
||||
regn.layerId = layerId;
|
||||
// Merge current layers to root.
|
||||
for (int k = 0; k < regn.nlayers; ++k)
|
||||
{
|
||||
if (!addUnique(root.layers, root.nlayers, RC_MAX_LAYERS, regn.layers[k]))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
root.ymin = rcMin(root.ymin, regn.ymin);
|
||||
root.ymax = rcMax(root.ymax, regn.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layerId++;
|
||||
}
|
||||
|
||||
// Merge non-overlapping regions that are close in height.
|
||||
const unsigned short mergeHeight = (unsigned short)walkableHeight * 4;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion& ri = regs[i];
|
||||
if (!ri.base) continue;
|
||||
|
||||
unsigned char newId = ri.layerId;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unsigned char oldId = 0xff;
|
||||
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (i == j) continue;
|
||||
rcLayerRegion& rj = regs[j];
|
||||
if (!rj.base) continue;
|
||||
|
||||
// Skip if the regions are not close to each other.
|
||||
if (!overlapRange(ri.ymin,ri.ymax+mergeHeight, rj.ymin,rj.ymax+mergeHeight))
|
||||
continue;
|
||||
// Skip if the height range would become too large.
|
||||
const int ymin = rcMin(ri.ymin, rj.ymin);
|
||||
const int ymax = rcMax(ri.ymax, rj.ymax);
|
||||
if ((ymax - ymin) >= 255)
|
||||
continue;
|
||||
|
||||
// Make sure that there is no overlap when merging 'ri' and 'rj'.
|
||||
bool overlap = false;
|
||||
// Iterate over all regions which have the same layerId as 'rj'
|
||||
for (int k = 0; k < nregs; ++k)
|
||||
{
|
||||
if (regs[k].layerId != rj.layerId)
|
||||
continue;
|
||||
// Check if region 'k' is overlapping region 'ri'
|
||||
// Index to 'regs' is the same as region id.
|
||||
if (contains(ri.layers,ri.nlayers, (unsigned char)k))
|
||||
{
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Cannot merge of regions overlap.
|
||||
if (overlap)
|
||||
continue;
|
||||
|
||||
// Can merge i and j.
|
||||
oldId = rj.layerId;
|
||||
break;
|
||||
}
|
||||
|
||||
// Could not find anything to merge with, stop.
|
||||
if (oldId == 0xff)
|
||||
break;
|
||||
|
||||
// Merge
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
rcLayerRegion& rj = regs[j];
|
||||
if (rj.layerId == oldId)
|
||||
{
|
||||
rj.base = 0;
|
||||
// Remap layerIds.
|
||||
rj.layerId = newId;
|
||||
// Add overlaid layers from 'rj' to 'ri'.
|
||||
for (int k = 0; k < rj.nlayers; ++k)
|
||||
{
|
||||
if (!addUnique(ri.layers, ri.nlayers, RC_MAX_LAYERS, rj.layers[k]))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update height bounds.
|
||||
ri.ymin = rcMin(ri.ymin, rj.ymin);
|
||||
ri.ymax = rcMax(ri.ymax, rj.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compact layerIds
|
||||
unsigned char remap[256];
|
||||
memset(remap, 0, 256);
|
||||
|
||||
// Find number of unique layers.
|
||||
layerId = 0;
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
remap[regs[i].layerId] = 1;
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
if (remap[i])
|
||||
remap[i] = layerId++;
|
||||
else
|
||||
remap[i] = 0xff;
|
||||
}
|
||||
// Remap ids.
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
regs[i].layerId = remap[regs[i].layerId];
|
||||
|
||||
// No layers, return empty.
|
||||
if (layerId == 0)
|
||||
return true;
|
||||
|
||||
// Create layers.
|
||||
rcAssert(lset.layers == 0);
|
||||
|
||||
const int lw = w - borderSize*2;
|
||||
const int lh = h - borderSize*2;
|
||||
|
||||
// Build contracted bbox for layers.
|
||||
float bmin[3], bmax[3];
|
||||
rcVcopy(bmin, chf.bmin);
|
||||
rcVcopy(bmax, chf.bmax);
|
||||
bmin[0] += borderSize*chf.cs;
|
||||
bmin[2] += borderSize*chf.cs;
|
||||
bmax[0] -= borderSize*chf.cs;
|
||||
bmax[2] -= borderSize*chf.cs;
|
||||
|
||||
lset.nlayers = (int)layerId;
|
||||
|
||||
lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM);
|
||||
if (!lset.layers)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' (%d).", lset.nlayers);
|
||||
return false;
|
||||
}
|
||||
memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers);
|
||||
|
||||
|
||||
// Store layers.
|
||||
for (int i = 0; i < lset.nlayers; ++i)
|
||||
{
|
||||
unsigned char curId = (unsigned char)i;
|
||||
|
||||
rcHeightfieldLayer* layer = &lset.layers[i];
|
||||
|
||||
const int gridSize = sizeof(unsigned char)*lw*lh;
|
||||
|
||||
layer->heights = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->heights)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->heights, 0xff, gridSize);
|
||||
|
||||
layer->areas = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->areas, 0, gridSize);
|
||||
|
||||
layer->cons = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->cons)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->cons, 0, gridSize);
|
||||
|
||||
// Find layer height bounds.
|
||||
int hmin = 0, hmax = 0;
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (regs[j].base && regs[j].layerId == curId)
|
||||
{
|
||||
hmin = (int)regs[j].ymin;
|
||||
hmax = (int)regs[j].ymax;
|
||||
}
|
||||
}
|
||||
|
||||
layer->width = lw;
|
||||
layer->height = lh;
|
||||
layer->cs = chf.cs;
|
||||
layer->ch = chf.ch;
|
||||
|
||||
// Adjust the bbox to fit the heightfield.
|
||||
rcVcopy(layer->bmin, bmin);
|
||||
rcVcopy(layer->bmax, bmax);
|
||||
layer->bmin[1] = bmin[1] + hmin*chf.ch;
|
||||
layer->bmax[1] = bmin[1] + hmax*chf.ch;
|
||||
layer->hmin = hmin;
|
||||
layer->hmax = hmax;
|
||||
|
||||
// Update usable data region.
|
||||
layer->minx = layer->width;
|
||||
layer->maxx = 0;
|
||||
layer->miny = layer->height;
|
||||
layer->maxy = 0;
|
||||
|
||||
// Copy height and area from compact heightfield.
|
||||
for (int y = 0; y < lh; ++y)
|
||||
{
|
||||
for (int x = 0; x < lw; ++x)
|
||||
{
|
||||
const int cx = borderSize+x;
|
||||
const int cy = borderSize+y;
|
||||
const rcCompactCell& c = chf.cells[cx+cy*w];
|
||||
for (int j = (int)c.index, nj = (int)(c.index+c.count); j < nj; ++j)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[j];
|
||||
// Skip unassigned regions.
|
||||
if (srcReg[j] == 0xff)
|
||||
continue;
|
||||
// Skip of does nto belong to current layer.
|
||||
unsigned char lid = regs[srcReg[j]].layerId;
|
||||
if (lid != curId)
|
||||
continue;
|
||||
|
||||
// Update data bounds.
|
||||
layer->minx = rcMin(layer->minx, x);
|
||||
layer->maxx = rcMax(layer->maxx, x);
|
||||
layer->miny = rcMin(layer->miny, y);
|
||||
layer->maxy = rcMax(layer->maxy, y);
|
||||
|
||||
// Store height and area type.
|
||||
const int idx = x+y*lw;
|
||||
layer->heights[idx] = (unsigned char)(s.y - hmin);
|
||||
layer->areas[idx] = chf.areas[j];
|
||||
|
||||
// Check connection.
|
||||
unsigned char portal = 0;
|
||||
unsigned char con = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = cx + rcGetDirOffsetX(dir);
|
||||
const int ay = cy + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
unsigned char alid = srcReg[ai] != 0xff ? regs[srcReg[ai]].layerId : 0xff;
|
||||
// Portal mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid != alid)
|
||||
{
|
||||
portal |= (unsigned char)(1<<dir);
|
||||
// Update height so that it matches on both sides of the portal.
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
if (as.y > hmin)
|
||||
layer->heights[idx] = rcMax(layer->heights[idx], (unsigned char)(as.y - hmin));
|
||||
}
|
||||
// Valid connection mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid == alid)
|
||||
{
|
||||
const int nx = ax - borderSize;
|
||||
const int ny = ay - borderSize;
|
||||
if (nx >= 0 && ny >= 0 && nx < lw && ny < lh)
|
||||
con |= (unsigned char)(1<<dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer->cons[idx] = (portal << 4) | con;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layer->minx > layer->maxx)
|
||||
layer->minx = layer->maxx = 0;
|
||||
if (layer->miny > layer->maxy)
|
||||
layer->miny = layer->maxy = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -160,6 +160,7 @@ static unsigned short addVertex(unsigned short x, unsigned short y, unsigned sho
|
|||
return (unsigned short)i;
|
||||
}
|
||||
|
||||
// Last time I checked the if version got compiled using cmov, which was a lot faster than module (with idiv).
|
||||
inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
|
||||
inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
|
||||
|
||||
|
|
@ -197,7 +198,7 @@ inline bool collinear(const int* a, const int* b, const int* c)
|
|||
// Returns true iff ab properly intersects cd: they share
|
||||
// a point interior to both segments. The properness of the
|
||||
// intersection is ensured by using strict leftness.
|
||||
bool intersectProp(const int* a, const int* b, const int* c, const int* d)
|
||||
static bool intersectProp(const int* a, const int* b, const int* c, const int* d)
|
||||
{
|
||||
// Eliminate improper cases.
|
||||
if (collinear(a,b,c) || collinear(a,b,d) ||
|
||||
|
|
@ -288,6 +289,53 @@ static bool diagonal(int i, int j, int n, const int* verts, int* indices)
|
|||
return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
|
||||
}
|
||||
|
||||
|
||||
static bool diagonalieLoose(int i, int j, int n, const int* verts, int* indices)
|
||||
{
|
||||
const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
|
||||
const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
|
||||
|
||||
// For each edge (k,k+1) of P
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
int k1 = next(k, n);
|
||||
// Skip edges incident to i or j
|
||||
if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
|
||||
{
|
||||
const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4];
|
||||
const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4];
|
||||
|
||||
if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
|
||||
continue;
|
||||
|
||||
if (intersectProp(d0, d1, p0, p1))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool inConeLoose(int i, int j, int n, const int* verts, int* indices)
|
||||
{
|
||||
const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
|
||||
const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
|
||||
const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4];
|
||||
const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4];
|
||||
|
||||
// If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
|
||||
if (leftOn(pin1, pi, pi1))
|
||||
return leftOn(pi, pj, pin1) && leftOn(pj, pi, pi1);
|
||||
// Assume (i-1,i,i+1) not collinear.
|
||||
// else P[i] is reflex.
|
||||
return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
|
||||
}
|
||||
|
||||
static bool diagonalLoose(int i, int j, int n, const int* verts, int* indices)
|
||||
{
|
||||
return inConeLoose(i, j, n, verts, indices) && diagonalieLoose(i, j, n, verts, indices);
|
||||
}
|
||||
|
||||
|
||||
static int triangulate(int n, const int* verts, int* indices, int* tris)
|
||||
{
|
||||
int ntris = 0;
|
||||
|
|
@ -328,14 +376,41 @@ static int triangulate(int n, const int* verts, int* indices, int* tris)
|
|||
|
||||
if (mini == -1)
|
||||
{
|
||||
// Should not happen.
|
||||
/* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
|
||||
// We might get here because the contour has overlapping segments, like this:
|
||||
//
|
||||
// A o-o=====o---o B
|
||||
// / |C D| \
|
||||
// o o o o
|
||||
// : : : :
|
||||
// We'll try to recover by loosing up the inCone test a bit so that a diagonal
|
||||
// like A-B or C-D can be found and we can continue.
|
||||
minLen = -1;
|
||||
mini = -1;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
printf("%d ", indices[i] & 0x0fffffff);
|
||||
int i1 = next(i, n);
|
||||
int i2 = next(i1, n);
|
||||
if (diagonalLoose(i, i2, n, verts, indices))
|
||||
{
|
||||
const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4];
|
||||
const int* p2 = &verts[(indices[next(i2, n)] & 0x0fffffff) * 4];
|
||||
int dx = p2[0] - p0[0];
|
||||
int dy = p2[2] - p0[2];
|
||||
int len = dx*dx + dy*dy;
|
||||
|
||||
if (minLen < 0 || len < minLen)
|
||||
{
|
||||
minLen = len;
|
||||
mini = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mini == -1)
|
||||
{
|
||||
// The contour is messed up. This sometimes happens
|
||||
// if the contour simplification is too aggressive.
|
||||
return -ntris;
|
||||
}
|
||||
printf("\n");*/
|
||||
return -ntris;
|
||||
}
|
||||
|
||||
int i = mini;
|
||||
|
|
@ -453,8 +528,8 @@ static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
|
|||
return dx*dx + dy*dy;
|
||||
}
|
||||
|
||||
static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb,
|
||||
unsigned short* tmp, const int nvp)
|
||||
static void mergePolyVerts(unsigned short* pa, unsigned short* pb, int ea, int eb,
|
||||
unsigned short* tmp, const int nvp)
|
||||
{
|
||||
const int na = countPolyVerts(pa, nvp);
|
||||
const int nb = countPolyVerts(pb, nvp);
|
||||
|
|
@ -472,6 +547,7 @@ static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb,
|
|||
memcpy(pa, tmp, sizeof(unsigned short)*nvp);
|
||||
}
|
||||
|
||||
|
||||
static void pushFront(int v, int* arr, int& an)
|
||||
{
|
||||
an++;
|
||||
|
|
@ -525,7 +601,7 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
|
|||
// Find edges which share the removed vertex.
|
||||
const int maxEdges = numTouchedVerts*2;
|
||||
int nedges = 0;
|
||||
rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> edges((int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP));
|
||||
if (!edges)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3);
|
||||
|
|
@ -549,9 +625,9 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
|
|||
|
||||
// Check if the edge exists
|
||||
bool exists = false;
|
||||
for (int k = 0; k < nedges; ++k)
|
||||
for (int m = 0; m < nedges; ++m)
|
||||
{
|
||||
int* e = &edges[k*3];
|
||||
int* e = &edges[m*3];
|
||||
if (e[1] == b)
|
||||
{
|
||||
// Exists, increment vertex share count.
|
||||
|
|
@ -605,7 +681,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
}
|
||||
|
||||
int nedges = 0;
|
||||
rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> edges((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP));
|
||||
if (!edges)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4);
|
||||
|
|
@ -613,15 +689,15 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
}
|
||||
|
||||
int nhole = 0;
|
||||
rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> hole((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
|
||||
if (!hole)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int nhreg = 0;
|
||||
rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> hreg((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
|
||||
if (!hreg)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp);
|
||||
|
|
@ -629,7 +705,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
}
|
||||
|
||||
int nharea = 0;
|
||||
rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> harea((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
|
||||
if (!harea)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp);
|
||||
|
|
@ -660,7 +736,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];
|
||||
|
|
@ -670,7 +747,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
}
|
||||
|
||||
// Remove vertex.
|
||||
for (int i = (int)rem; i < mesh.nverts; ++i)
|
||||
for (int i = (int)rem; i < mesh.nverts - 1; ++i)
|
||||
{
|
||||
mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
|
||||
mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
|
||||
|
|
@ -745,22 +822,22 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
break;
|
||||
}
|
||||
|
||||
rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> tris((int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP));
|
||||
if (!tris)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
|
||||
return false;
|
||||
}
|
||||
|
||||
rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> tverts((int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP));
|
||||
if (!tverts)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
|
||||
return false;
|
||||
}
|
||||
|
||||
rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP);
|
||||
if (!tverts)
|
||||
rcScopedDelete<int> thole((int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP));
|
||||
if (!thole)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole);
|
||||
return false;
|
||||
|
|
@ -786,20 +863,20 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
}
|
||||
|
||||
// Merge the hole triangles back to polygons.
|
||||
rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<unsigned short> polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP));
|
||||
if (!polys)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
|
||||
return false;
|
||||
}
|
||||
rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<unsigned short> pregs((unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP));
|
||||
if (!pregs)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris);
|
||||
return false;
|
||||
}
|
||||
rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP);
|
||||
if (!pregs)
|
||||
rcScopedDelete<unsigned char> pareas((unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP));
|
||||
if (!pareas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris);
|
||||
return false;
|
||||
|
|
@ -818,7 +895,14 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
polys[npolys*nvp+0] = (unsigned short)hole[t[0]];
|
||||
polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
|
||||
polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
|
||||
pregs[npolys] = (unsigned short)hreg[t[0]];
|
||||
|
||||
// If this polygon covers multiple region types then
|
||||
// mark it as such
|
||||
if (hreg[t[0]] != hreg[t[1]] || hreg[t[1]] != hreg[t[2]])
|
||||
pregs[npolys] = RC_MULTIPLE_REGS;
|
||||
else
|
||||
pregs[npolys] = (unsigned short)hreg[t[0]];
|
||||
|
||||
pareas[npolys] = (unsigned char)harea[t[0]];
|
||||
npolys++;
|
||||
}
|
||||
|
|
@ -859,8 +943,13 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
// Found best, merge.
|
||||
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);
|
||||
mergePolyVerts(pa, pb, bestEa, bestEb, tmpPoly, nvp);
|
||||
if (pregs[bestPa] != pregs[bestPb])
|
||||
pregs[bestPa] = RC_MULTIPLE_REGS;
|
||||
|
||||
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--;
|
||||
|
|
@ -894,17 +983,24 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
/// @par
|
||||
///
|
||||
/// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper
|
||||
/// limit must be retricted to <= #DT_VERTS_PER_POLYGON.
|
||||
///
|
||||
/// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig
|
||||
bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_POLYMESH);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_BUILD_POLYMESH);
|
||||
|
||||
rcVcopy(mesh.bmin, cset.bmin);
|
||||
rcVcopy(mesh.bmax, cset.bmax);
|
||||
mesh.cs = cset.cs;
|
||||
mesh.ch = cset.ch;
|
||||
mesh.borderSize = cset.borderSize;
|
||||
mesh.maxEdgeError = cset.maxError;
|
||||
|
||||
int maxVertices = 0;
|
||||
int maxTris = 0;
|
||||
|
|
@ -924,10 +1020,10 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
return false;
|
||||
}
|
||||
|
||||
rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<unsigned char> vflags((unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP));
|
||||
if (!vflags)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'vflags' (%d).", maxVertices);
|
||||
return false;
|
||||
}
|
||||
memset(vflags, 0, maxVertices);
|
||||
|
|
@ -938,7 +1034,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
|
||||
return false;
|
||||
}
|
||||
mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2*2, RC_ALLOC_PERM);
|
||||
mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2, RC_ALLOC_PERM);
|
||||
if (!mesh.polys)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2);
|
||||
|
|
@ -967,7 +1063,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
memset(mesh.regs, 0, sizeof(unsigned short)*maxTris);
|
||||
memset(mesh.areas, 0, sizeof(unsigned char)*maxTris);
|
||||
|
||||
rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> nextVert((int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP));
|
||||
if (!nextVert)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
|
||||
|
|
@ -975,7 +1071,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
}
|
||||
memset(nextVert, 0, sizeof(int)*maxVertices);
|
||||
|
||||
rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP));
|
||||
if (!firstVert)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
|
||||
|
|
@ -984,19 +1080,19 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
|
||||
firstVert[i] = -1;
|
||||
|
||||
rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> indices((int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP));
|
||||
if (!indices)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
|
||||
return false;
|
||||
}
|
||||
rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> tris((int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP));
|
||||
if (!tris)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
|
||||
return false;
|
||||
}
|
||||
rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<unsigned short> polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP));
|
||||
if (!polys)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
|
||||
|
|
@ -1046,7 +1142,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
vflags[indices[j]] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Build initial polygons.
|
||||
int npolys = 0;
|
||||
memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short));
|
||||
|
|
@ -1097,8 +1193,10 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
// Found best, merge.
|
||||
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);
|
||||
mergePolyVerts(pa, pb, bestEa, bestEb, tmpPoly, nvp);
|
||||
unsigned short* lastPoly = &polys[(npolys-1)*nvp];
|
||||
if (pb != lastPoly)
|
||||
memcpy(pb, lastPoly, sizeof(unsigned short)*nvp);
|
||||
npolys--;
|
||||
}
|
||||
else
|
||||
|
|
@ -1143,6 +1241,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
}
|
||||
// Remove vertex
|
||||
// Note: mesh.nverts is already decremented inside removeVertex()!
|
||||
// Fixup vertex flags
|
||||
for (int j = i; j < mesh.nverts; ++j)
|
||||
vflags[j] = vflags[j+1];
|
||||
--i;
|
||||
|
|
@ -1155,6 +1254,37 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find portal edges
|
||||
if (mesh.borderSize > 0)
|
||||
{
|
||||
const int w = cset.width;
|
||||
const int h = cset.height;
|
||||
for (int i = 0; i < mesh.npolys; ++i)
|
||||
{
|
||||
unsigned short* p = &mesh.polys[i*2*nvp];
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == RC_MESH_NULL_IDX) break;
|
||||
// Skip connected edges.
|
||||
if (p[nvp+j] != RC_MESH_NULL_IDX)
|
||||
continue;
|
||||
int nj = j+1;
|
||||
if (nj >= nvp || p[nj] == RC_MESH_NULL_IDX) nj = 0;
|
||||
const unsigned short* va = &mesh.verts[p[j]*3];
|
||||
const unsigned short* vb = &mesh.verts[p[nj]*3];
|
||||
|
||||
if ((int)va[0] == 0 && (int)vb[0] == 0)
|
||||
p[nvp+j] = 0x8000 | 0;
|
||||
else if ((int)va[2] == h && (int)vb[2] == h)
|
||||
p[nvp+j] = 0x8000 | 1;
|
||||
else if ((int)va[0] == w && (int)vb[0] == w)
|
||||
p[nvp+j] = 0x8000 | 2;
|
||||
else if ((int)va[2] == 0 && (int)vb[2] == 0)
|
||||
p[nvp+j] = 0x8000 | 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Just allocate the mesh flags array. The user is resposible to fill it.
|
||||
mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM);
|
||||
|
|
@ -1167,18 +1297,17 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& me
|
|||
|
||||
if (mesh.nverts > 0xffff)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
|
||||
}
|
||||
if (mesh.npolys > 0xffff)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_POLYMESH);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @see rcAllocPolyMesh, rcPolyMesh
|
||||
bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
|
@ -1186,7 +1315,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
|
|||
if (!nmeshes || !meshes)
|
||||
return true;
|
||||
|
||||
ctx->startTimer(RC_TIMER_MERGE_POLYMESH);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_MERGE_POLYMESH);
|
||||
|
||||
mesh.nvp = meshes[0]->nvp;
|
||||
mesh.cs = meshes[0]->cs;
|
||||
|
|
@ -1247,7 +1376,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
|
|||
}
|
||||
memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys);
|
||||
|
||||
rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> nextVert((int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP));
|
||||
if (!nextVert)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts);
|
||||
|
|
@ -1255,7 +1384,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
|
|||
}
|
||||
memset(nextVert, 0, sizeof(int)*maxVerts);
|
||||
|
||||
rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
|
||||
rcScopedDelete<int> firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP));
|
||||
if (!firstVert)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
|
||||
|
|
@ -1264,7 +1393,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
|
|||
for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
|
||||
firstVert[i] = -1;
|
||||
|
||||
rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM);
|
||||
rcScopedDelete<unsigned short> vremap((unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM));
|
||||
if (!vremap)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh);
|
||||
|
|
@ -1279,6 +1408,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];
|
||||
|
|
@ -1299,6 +1434,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1318,7 +1483,70 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
|
|||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MERGE_POLYMESH);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
// Destination must be empty.
|
||||
rcAssert(dst.verts == 0);
|
||||
rcAssert(dst.polys == 0);
|
||||
rcAssert(dst.regs == 0);
|
||||
rcAssert(dst.areas == 0);
|
||||
rcAssert(dst.flags == 0);
|
||||
|
||||
dst.nverts = src.nverts;
|
||||
dst.npolys = src.npolys;
|
||||
dst.maxpolys = src.npolys;
|
||||
dst.nvp = src.nvp;
|
||||
rcVcopy(dst.bmin, src.bmin);
|
||||
rcVcopy(dst.bmax, src.bmax);
|
||||
dst.cs = src.cs;
|
||||
dst.ch = src.ch;
|
||||
dst.borderSize = src.borderSize;
|
||||
dst.maxEdgeError = src.maxEdgeError;
|
||||
|
||||
dst.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.nverts*3, RC_ALLOC_PERM);
|
||||
if (!dst.verts)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.verts' (%d).", src.nverts*3);
|
||||
return false;
|
||||
}
|
||||
memcpy(dst.verts, src.verts, sizeof(unsigned short)*src.nverts*3);
|
||||
|
||||
dst.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.npolys*2*src.nvp, RC_ALLOC_PERM);
|
||||
if (!dst.polys)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.polys' (%d).", src.npolys*2*src.nvp);
|
||||
return false;
|
||||
}
|
||||
memcpy(dst.polys, src.polys, sizeof(unsigned short)*src.npolys*2*src.nvp);
|
||||
|
||||
dst.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.npolys, RC_ALLOC_PERM);
|
||||
if (!dst.regs)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.regs' (%d).", src.npolys);
|
||||
return false;
|
||||
}
|
||||
memcpy(dst.regs, src.regs, sizeof(unsigned short)*src.npolys);
|
||||
|
||||
dst.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*src.npolys, RC_ALLOC_PERM);
|
||||
if (!dst.areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.areas' (%d).", src.npolys);
|
||||
return false;
|
||||
}
|
||||
memcpy(dst.areas, src.areas, sizeof(unsigned char)*src.npolys);
|
||||
|
||||
dst.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.npolys, RC_ALLOC_PERM);
|
||||
if (!dst.flags)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (%d).", src.npolys);
|
||||
return false;
|
||||
}
|
||||
memcpy(dst.flags, src.flags, sizeof(unsigned short)*src.npolys);
|
||||
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -50,7 +50,7 @@ static rcSpan* allocSpan(rcHeightfield& hf)
|
|||
// Allocate memory for the new pool.
|
||||
rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
|
||||
if (!pool) return 0;
|
||||
pool->next = 0;
|
||||
|
||||
// Add the pool into the list of pools.
|
||||
pool->next = hf.pools;
|
||||
hf.pools = pool;
|
||||
|
|
@ -82,7 +82,7 @@ static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
|
|||
hf.freelist = ptr;
|
||||
}
|
||||
|
||||
static void addSpan(rcHeightfield& hf, const int x, const int y,
|
||||
static bool addSpan(rcHeightfield& hf, const int x, const int y,
|
||||
const unsigned short smin, const unsigned short smax,
|
||||
const unsigned char area, const int flagMergeThr)
|
||||
{
|
||||
|
|
@ -90,16 +90,18 @@ static void addSpan(rcHeightfield& hf, const int x, const int y,
|
|||
int idx = x + y*hf.width;
|
||||
|
||||
rcSpan* s = allocSpan(hf);
|
||||
if (!s)
|
||||
return false;
|
||||
s->smin = smin;
|
||||
s->smax = smax;
|
||||
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;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
rcSpan* prev = 0;
|
||||
rcSpan* cur = hf.spans[idx];
|
||||
|
|
@ -152,47 +154,91 @@ static void addSpan(rcHeightfield& hf, const int x, const int y,
|
|||
s->next = hf.spans[idx];
|
||||
hf.spans[idx] = s;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
|
||||
/// @par
|
||||
///
|
||||
/// The span addition can be set to favor flags. If the span is merged to
|
||||
/// another span and the new @p smax is within @p flagMergeThr units
|
||||
/// from the existing span, the span flags are merged.
|
||||
///
|
||||
/// @see rcHeightfield, rcSpan.
|
||||
bool rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y,
|
||||
const unsigned short smin, const unsigned short smax,
|
||||
const unsigned char area, const int flagMergeThr)
|
||||
{
|
||||
// rcAssert(ctx);
|
||||
addSpan(hf, x,y, smin, smax, area, flagMergeThr);
|
||||
rcAssert(ctx);
|
||||
|
||||
if (!addSpan(hf, x, y, smin, smax, area, flagMergeThr))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
|
||||
static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char area, rcHeightfield& hf,
|
||||
const float* bmin, const float* bmax,
|
||||
const float cs, const float ics, const float ich,
|
||||
|
|
@ -213,50 +259,59 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
|
|||
|
||||
// If the triangle does not touch the bbox of the heightfield, skip the triagle.
|
||||
if (!overlapBounds(bmin, bmax, tmin, tmax))
|
||||
return;
|
||||
return true;
|
||||
|
||||
// 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];
|
||||
|
|
@ -271,33 +326,50 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
|
|||
unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
|
||||
unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
|
||||
|
||||
addSpan(hf, x, y, ismin, ismax, area, flagMergeThr);
|
||||
if (!addSpan(hf, x, y, ismin, ismax, area, flagMergeThr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
|
||||
/// @par
|
||||
///
|
||||
/// No spans will be added if the triangle does not overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char area, rcHeightfield& solid,
|
||||
const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
if (!rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
return true;
|
||||
}
|
||||
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
||||
const int* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
|
|
@ -308,19 +380,28 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
|||
const float* v1 = &verts[tris[i*3+1]*3];
|
||||
const float* v2 = &verts[tris[i*3+2]*3];
|
||||
// Rasterize.
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
||||
const unsigned short* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
|
|
@ -331,18 +412,27 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
|||
const float* v1 = &verts[tris[i*3+1]*3];
|
||||
const float* v2 = &verts[tris[i*3+2]*3];
|
||||
// Rasterize.
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
|
|
@ -353,8 +443,12 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned cha
|
|||
const float* v1 = &verts[(i*3+1)*3];
|
||||
const float* v2 = &verts[(i*3+2)*3];
|
||||
// Rasterize.
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +0,0 @@
|
|||
TODO/Roadmap
|
||||
|
||||
Summer/Autumn 2009
|
||||
|
||||
- Off mesh links (jump links)
|
||||
- Area annotations
|
||||
- Embed extra data per polygon
|
||||
- Height conforming navmesh
|
||||
|
||||
|
||||
Autumn/Winter 2009/2010
|
||||
|
||||
- Detour path following
|
||||
- More dynamic example with tile navmesh
|
||||
- Faster small tile process
|
||||
|
||||
|
||||
More info at http://digestingduck.blogspot.com/2009/07/recast-and-detour-roadmap.html
|
||||
|
||||
-
|
||||
|
|
@ -112,7 +112,7 @@ include_directories(
|
|||
${game_INCLUDE_DIRS}
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/recastnavigation/Detour/Include
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/recastnavigation/Recast
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/recastnavigation/Recast/Include
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/g3dlite/include
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/SFMT
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/zlib
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ include_directories(
|
|||
${scripts_INCLUDE_DIRS}
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/recastnavigation/Detour/Include
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/recastnavigation/Recast
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/recastnavigation/Recast/Include
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/g3dlite/include
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/SFMT
|
||||
${CMAKE_SOURCE_DIR}/modules/worldengine/deps/zlib
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue