recast: Update to upstream version 1.6.0

Release notes:
- https://github.com/recastnavigation/recastnavigation/releases/tag/v1.6.0
This commit is contained in:
Rémi Verschelde 2023-05-22 14:47:42 +02:00
parent 65fa775ff6
commit 2058b63067
No known key found for this signature in database
GPG Key ID: C3336907360768E1
15 changed files with 1366 additions and 1189 deletions

View File

@ -617,7 +617,7 @@ in 10.40, it can be found in the `patches` folder.
## recastnavigation ## recastnavigation
- Upstream: https://github.com/recastnavigation/recastnavigation - Upstream: https://github.com/recastnavigation/recastnavigation
- Version: git (4fef0446609b23d6ac180ed822817571525528a1, 2022) - Version: 1.6.0 (6dc1667f580357e8a2154c28b7867bea7e8ad3a7, 2023)
- License: zlib - License: zlib
Files extracted from upstream source: Files extracted from upstream source:

View File

@ -100,11 +100,21 @@ enum rcTimerLabel
/// Provides an interface for optional logging and performance tracking of the Recast /// Provides an interface for optional logging and performance tracking of the Recast
/// build process. /// build process.
///
/// 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.
///
/// @ingroup recast /// @ingroup recast
class rcContext class rcContext
{ {
public: public:
/// Contructor. /// Constructor.
/// @param[in] state TRUE if the logging and performance timers should be enabled. [Default: true] /// @param[in] state TRUE if the logging and performance timers should be enabled. [Default: true]
inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {} inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {}
virtual ~rcContext() {} virtual ~rcContext() {}
@ -117,6 +127,13 @@ public:
inline void resetLog() { if (m_logEnabled) doResetLog(); } inline void resetLog() { if (m_logEnabled) doResetLog(); }
/// Logs a message. /// Logs a message.
///
/// 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
///
/// @param[in] category The category of the message. /// @param[in] category The category of the message.
/// @param[in] format The message. /// @param[in] format The message.
void log(const rcLogCategory category, const char* format, ...); void log(const rcLogCategory category, const char* format, ...);
@ -125,7 +142,7 @@ public:
/// @param[in] state TRUE if timers should be enabled. /// @param[in] state TRUE if timers should be enabled.
inline void enableTimer(bool state) { m_timerEnabled = state; } inline void enableTimer(bool state) { m_timerEnabled = state; }
/// Clears all peformance timers. (Resets all to unused.) /// Clears all performance timers. (Resets all to unused.)
inline void resetTimers() { if (m_timerEnabled) doResetTimers(); } inline void resetTimers() { if (m_timerEnabled) doResetTimers(); }
/// Starts the specified performance timer. /// Starts the specified performance timer.
@ -239,7 +256,7 @@ struct rcConfig
/// The maximum allowed length for contour edges along the border of the mesh. [Limit: >=0] [Units: vx] /// The maximum allowed length for contour edges along the border of the mesh. [Limit: >=0] [Units: vx]
int maxEdgeLen; int maxEdgeLen;
/// The maximum distance a simplfied contour's border edges should deviate /// The maximum distance a simplified contour's border edges should deviate
/// the original raw contour. [Limit: >=0] [Units: vx] /// the original raw contour. [Limit: >=0] [Units: vx]
float maxSimplificationError; float maxSimplificationError;
@ -335,6 +352,7 @@ struct rcCompactHeightfield
{ {
rcCompactHeightfield(); rcCompactHeightfield();
~rcCompactHeightfield(); ~rcCompactHeightfield();
int width; ///< The width of the heightfield. (Along the x-axis in cell units.) int width; ///< The width of the heightfield. (Along the x-axis in cell units.)
int height; ///< The height of the heightfield. (Along the z-axis in cell units.) int height; ///< The height of the heightfield. (Along the z-axis in cell units.)
int spanCount; ///< The number of spans in the heightfield. int spanCount; ///< The number of spans in the heightfield.
@ -351,6 +369,11 @@ struct rcCompactHeightfield
rcCompactSpan* spans; ///< Array of spans. [Size: #spanCount] rcCompactSpan* spans; ///< Array of spans. [Size: #spanCount]
unsigned short* dist; ///< Array containing border distance data. [Size: #spanCount] unsigned short* dist; ///< Array containing border distance data. [Size: #spanCount]
unsigned char* areas; ///< Array containing area id data. [Size: #spanCount] unsigned char* areas; ///< Array containing area id data. [Size: #spanCount]
private:
// Explicitly-disabled copy constructor and copy assignment operator.
rcCompactHeightfield(const rcCompactHeightfield&);
rcCompactHeightfield& operator=(const rcCompactHeightfield&);
}; };
/// Represents a heightfield layer within a layer set. /// Represents a heightfield layer within a layer set.
@ -381,8 +404,14 @@ struct rcHeightfieldLayerSet
{ {
rcHeightfieldLayerSet(); rcHeightfieldLayerSet();
~rcHeightfieldLayerSet(); ~rcHeightfieldLayerSet();
rcHeightfieldLayer* layers; ///< The layers in the set. [Size: #nlayers] rcHeightfieldLayer* layers; ///< The layers in the set. [Size: #nlayers]
int nlayers; ///< The number of layers in the set. int nlayers; ///< The number of layers in the set.
private:
// Explicitly-disabled copy constructor and copy assignment operator.
rcHeightfieldLayerSet(const rcHeightfieldLayerSet&);
rcHeightfieldLayerSet& operator=(const rcHeightfieldLayerSet&);
}; };
/// Represents a simple, non-overlapping contour in field space. /// Represents a simple, non-overlapping contour in field space.
@ -402,6 +431,7 @@ struct rcContourSet
{ {
rcContourSet(); rcContourSet();
~rcContourSet(); ~rcContourSet();
rcContour* conts; ///< An array of the contours in the set. [Size: #nconts] rcContour* conts; ///< An array of the contours in the set. [Size: #nconts]
int nconts; ///< The number of contours in the set. int nconts; ///< The number of contours in the set.
float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)] float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)]
@ -412,6 +442,11 @@ struct rcContourSet
int height; ///< The height of the set. (Along the z-axis in cell units.) int height; ///< The height of the set. (Along the z-axis in cell units.)
int borderSize; ///< The AABB border size used to generate the source data from which the contours were derived. int borderSize; ///< The AABB border size used to generate the source data from which the contours were derived.
float maxError; ///< The max edge error that this contour set was simplified with. float maxError; ///< The max edge error that this contour set was simplified with.
private:
// Explicitly-disabled copy constructor and copy assignment operator.
rcContourSet(const rcContourSet&);
rcContourSet& operator=(const rcContourSet&);
}; };
/// Represents a polygon mesh suitable for use in building a navigation mesh. /// Represents a polygon mesh suitable for use in building a navigation mesh.
@ -420,6 +455,7 @@ struct rcPolyMesh
{ {
rcPolyMesh(); rcPolyMesh();
~rcPolyMesh(); ~rcPolyMesh();
unsigned short* verts; ///< The mesh vertices. [Form: (x, y, z) * #nverts] unsigned short* verts; ///< The mesh vertices. [Form: (x, y, z) * #nverts]
unsigned short* polys; ///< Polygon and neighbor data. [Length: #maxpolys * 2 * #nvp] unsigned short* polys; ///< Polygon and neighbor data. [Length: #maxpolys * 2 * #nvp]
unsigned short* regs; ///< The region id assigned to each polygon. [Length: #maxpolys] unsigned short* regs; ///< The region id assigned to each polygon. [Length: #maxpolys]
@ -435,6 +471,11 @@ struct rcPolyMesh
float ch; ///< The height of each cell. (The minimum increment along the y-axis.) float ch; ///< The height of each cell. (The minimum increment along the y-axis.)
int borderSize; ///< The AABB border size used to generate the source data from which the mesh was derived. int borderSize; ///< The AABB border size used to generate the source data from which the mesh was derived.
float maxEdgeError; ///< The max error of the polygon edges in the mesh. float maxEdgeError; ///< The max error of the polygon edges in the mesh.
private:
// Explicitly-disabled copy constructor and copy assignment operator.
rcPolyMesh(const rcPolyMesh&);
rcPolyMesh& operator=(const rcPolyMesh&);
}; };
/// Contains triangle meshes that represent detailed height data associated /// Contains triangle meshes that represent detailed height data associated
@ -442,12 +483,19 @@ struct rcPolyMesh
/// @ingroup recast /// @ingroup recast
struct rcPolyMeshDetail struct rcPolyMeshDetail
{ {
rcPolyMeshDetail();
unsigned int* meshes; ///< The sub-mesh data. [Size: 4*#nmeshes] unsigned int* meshes; ///< The sub-mesh data. [Size: 4*#nmeshes]
float* verts; ///< The mesh vertices. [Size: 3*#nverts] float* verts; ///< The mesh vertices. [Size: 3*#nverts]
unsigned char* tris; ///< The mesh triangles. [Size: 4*#ntris] unsigned char* tris; ///< The mesh triangles. [Size: 4*#ntris]
int nmeshes; ///< The number of sub-meshes defined by #meshes. int nmeshes; ///< The number of sub-meshes defined by #meshes.
int nverts; ///< The number of vertices in #verts. int nverts; ///< The number of vertices in #verts.
int ntris; ///< The number of triangles in #tris. int ntris; ///< The number of triangles in #tris.
private:
// Explicitly-disabled copy constructor and copy assignment operator.
rcPolyMeshDetail(const rcPolyMeshDetail&);
rcPolyMeshDetail& operator=(const rcPolyMeshDetail&);
}; };
/// @name Allocation Functions /// @name Allocation Functions
@ -462,10 +510,10 @@ struct rcPolyMeshDetail
rcHeightfield* rcAllocHeightfield(); rcHeightfield* rcAllocHeightfield();
/// Frees the specified heightfield object using the Recast allocator. /// Frees the specified heightfield object using the Recast allocator.
/// @param[in] hf A heightfield allocated using #rcAllocHeightfield /// @param[in] heightfield A heightfield allocated using #rcAllocHeightfield
/// @ingroup recast /// @ingroup recast
/// @see rcAllocHeightfield /// @see rcAllocHeightfield
void rcFreeHeightField(rcHeightfield* hf); void rcFreeHeightField(rcHeightfield* heightfield);
/// Allocates a compact heightfield object using the Recast allocator. /// Allocates a compact heightfield object using the Recast allocator.
/// @return A compact heightfield that is ready for initialization, or null on failure. /// @return A compact heightfield that is ready for initialization, or null on failure.
@ -474,10 +522,10 @@ void rcFreeHeightField(rcHeightfield* hf);
rcCompactHeightfield* rcAllocCompactHeightfield(); rcCompactHeightfield* rcAllocCompactHeightfield();
/// Frees the specified compact heightfield object using the Recast allocator. /// Frees the specified compact heightfield object using the Recast allocator.
/// @param[in] chf A compact heightfield allocated using #rcAllocCompactHeightfield /// @param[in] compactHeightfield A compact heightfield allocated using #rcAllocCompactHeightfield
/// @ingroup recast /// @ingroup recast
/// @see rcAllocCompactHeightfield /// @see rcAllocCompactHeightfield
void rcFreeCompactHeightfield(rcCompactHeightfield* chf); void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield);
/// Allocates a heightfield layer set using the Recast allocator. /// Allocates a heightfield layer set using the Recast allocator.
/// @return A heightfield layer set that is ready for initialization, or null on failure. /// @return A heightfield layer set that is ready for initialization, or null on failure.
@ -486,10 +534,10 @@ void rcFreeCompactHeightfield(rcCompactHeightfield* chf);
rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet(); rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet();
/// Frees the specified heightfield layer set using the Recast allocator. /// Frees the specified heightfield layer set using the Recast allocator.
/// @param[in] lset A heightfield layer set allocated using #rcAllocHeightfieldLayerSet /// @param[in] layerSet A heightfield layer set allocated using #rcAllocHeightfieldLayerSet
/// @ingroup recast /// @ingroup recast
/// @see rcAllocHeightfieldLayerSet /// @see rcAllocHeightfieldLayerSet
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset); void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* layerSet);
/// Allocates a contour set object using the Recast allocator. /// Allocates a contour set object using the Recast allocator.
/// @return A contour set that is ready for initialization, or null on failure. /// @return A contour set that is ready for initialization, or null on failure.
@ -498,10 +546,10 @@ void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset);
rcContourSet* rcAllocContourSet(); rcContourSet* rcAllocContourSet();
/// Frees the specified contour set using the Recast allocator. /// Frees the specified contour set using the Recast allocator.
/// @param[in] cset A contour set allocated using #rcAllocContourSet /// @param[in] contourSet A contour set allocated using #rcAllocContourSet
/// @ingroup recast /// @ingroup recast
/// @see rcAllocContourSet /// @see rcAllocContourSet
void rcFreeContourSet(rcContourSet* cset); void rcFreeContourSet(rcContourSet* contourSet);
/// Allocates a polygon mesh object using the Recast allocator. /// Allocates a polygon mesh object using the Recast allocator.
/// @return A polygon mesh that is ready for initialization, or null on failure. /// @return A polygon mesh that is ready for initialization, or null on failure.
@ -510,10 +558,10 @@ void rcFreeContourSet(rcContourSet* cset);
rcPolyMesh* rcAllocPolyMesh(); rcPolyMesh* rcAllocPolyMesh();
/// Frees the specified polygon mesh using the Recast allocator. /// Frees the specified polygon mesh using the Recast allocator.
/// @param[in] pmesh A polygon mesh allocated using #rcAllocPolyMesh /// @param[in] polyMesh A polygon mesh allocated using #rcAllocPolyMesh
/// @ingroup recast /// @ingroup recast
/// @see rcAllocPolyMesh /// @see rcAllocPolyMesh
void rcFreePolyMesh(rcPolyMesh* pmesh); void rcFreePolyMesh(rcPolyMesh* polyMesh);
/// Allocates a detail mesh object using the Recast allocator. /// Allocates a detail mesh object using the Recast allocator.
/// @return A detail mesh that is ready for initialization, or null on failure. /// @return A detail mesh that is ready for initialization, or null on failure.
@ -522,16 +570,16 @@ void rcFreePolyMesh(rcPolyMesh* pmesh);
rcPolyMeshDetail* rcAllocPolyMeshDetail(); rcPolyMeshDetail* rcAllocPolyMeshDetail();
/// Frees the specified detail mesh using the Recast allocator. /// Frees the specified detail mesh using the Recast allocator.
/// @param[in] dmesh A detail mesh allocated using #rcAllocPolyMeshDetail /// @param[in] detailMesh A detail mesh allocated using #rcAllocPolyMeshDetail
/// @ingroup recast /// @ingroup recast
/// @see rcAllocPolyMeshDetail /// @see rcAllocPolyMeshDetail
void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh); void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh);
/// @} /// @}
/// Heighfield border flag. /// Heightfield border flag.
/// If a heightfield region ID has this bit set, then the region is a border /// If a heightfield region ID has this bit set, then the region is a border
/// region and its spans are considered unwalkable. /// region and its spans are considered un-walkable.
/// (Used during the region and contour build process.) /// (Used during the region and contour build process.)
/// @see rcCompactSpan::reg /// @see rcCompactSpan::reg
static const unsigned short RC_BORDER_REG = 0x8000; static const unsigned short RC_BORDER_REG = 0x8000;
@ -581,7 +629,7 @@ static const unsigned short RC_MESH_NULL_IDX = 0xffff;
/// Represents the null area. /// Represents the null area.
/// When a data element is given this value it is considered to no longer be /// When a data element is given this value it is considered to no longer be
/// assigned to a usable area. (E.g. It is unwalkable.) /// assigned to a usable area. (E.g. It is un-walkable.)
static const unsigned char RC_NULL_AREA = 0; static const unsigned char RC_NULL_AREA = 0;
/// The default area id used to indicate a walkable polygon. /// The default area id used to indicate a walkable polygon.
@ -624,11 +672,14 @@ 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 rcSqr(T a) { return a*a; }
/// Clamps the value to the specified range. /// Clamps the value to the specified range.
/// @param[in] v The value to clamp. /// @param[in] value The value to clamp.
/// @param[in] mn The minimum permitted return value. /// @param[in] minInclusive The minimum permitted return value.
/// @param[in] mx The maximum permitted return value. /// @param[in] maxInclusive The maximum permitted return value.
/// @return The value, clamped to the specified range. /// @return The value, clamped to the specified range.
template<class T> inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); } template<class T> inline T rcClamp(T value, T minInclusive, T maxInclusive)
{
return value < minInclusive ? minInclusive: (value > maxInclusive ? maxInclusive : value);
}
/// Returns the square root of the value. /// Returns the square root of the value.
/// @param[in] x The value. /// @param[in] x The value.
@ -765,172 +816,247 @@ inline void rcVnormalize(float* v)
/// Calculates the bounding box of an array of vertices. /// Calculates the bounding box of an array of vertices.
/// @ingroup recast /// @ingroup recast
/// @param[in] verts An array of vertices. [(x, y, z) * @p nv] /// @param[in] verts An array of vertices. [(x, y, z) * @p nv]
/// @param[in] nv The number of vertices in the @p verts array. /// @param[in] numVerts The number of vertices in the @p verts array.
/// @param[out] bmin The minimum bounds of the AABB. [(x, y, z)] [Units: wu] /// @param[out] minBounds The minimum bounds of the AABB. [(x, y, z)] [Units: wu]
/// @param[out] bmax The maximum bounds of the AABB. [(x, y, z)] [Units: wu] /// @param[out] maxBounds The maximum bounds of the AABB. [(x, y, z)] [Units: wu]
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax); void rcCalcBounds(const float* verts, int numVerts, float* minBounds, float* maxBounds);
/// Calculates the grid size based on the bounding box and grid cell size. /// Calculates the grid size based on the bounding box and grid cell size.
/// @ingroup recast /// @ingroup recast
/// @param[in] bmin The minimum bounds of the AABB. [(x, y, z)] [Units: wu] /// @param[in] minBounds The minimum bounds of the AABB. [(x, y, z)] [Units: wu]
/// @param[in] bmax The maximum bounds of the AABB. [(x, y, z)] [Units: wu] /// @param[in] maxBounds The maximum bounds of the AABB. [(x, y, z)] [Units: wu]
/// @param[in] cs The xz-plane cell size. [Limit: > 0] [Units: wu] /// @param[in] cellSize The xz-plane cell size. [Limit: > 0] [Units: wu]
/// @param[out] w The width along the x-axis. [Limit: >= 0] [Units: vx] /// @param[out] sizeX The width along the x-axis. [Limit: >= 0] [Units: vx]
/// @param[out] h The height along the z-axis. [Limit: >= 0] [Units: vx] /// @param[out] sizeZ The height along the z-axis. [Limit: >= 0] [Units: vx]
void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h); void rcCalcGridSize(const float* minBounds, const float* maxBounds, float cellSize, int* sizeX, int* sizeZ);
/// Initializes a new heightfield. /// Initializes a new heightfield.
/// See the #rcConfig documentation for more information on the configuration parameters.
///
/// @see rcAllocHeightfield, rcHeightfield
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. ///
/// @param[in,out] hf The allocated heightfield to initialize. /// @param[in,out] context The build context to use during the operation.
/// @param[in] width The width of the field along the x-axis. [Limit: >= 0] [Units: vx] /// @param[in,out] heightfield The allocated heightfield to initialize.
/// @param[in] height The height of the field along the z-axis. [Limit: >= 0] [Units: vx] /// @param[in] sizeX The width of the field along the x-axis. [Limit: >= 0] [Units: vx]
/// @param[in] bmin The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu] /// @param[in] sizeZ The height of the field along the z-axis. [Limit: >= 0] [Units: vx]
/// @param[in] bmax The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu] /// @param[in] minBounds The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu]
/// @param[in] cs The xz-plane cell size to use for the field. [Limit: > 0] [Units: wu] /// @param[in] maxBounds The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu]
/// @param[in] ch The y-axis cell size to use for field. [Limit: > 0] [Units: wu] /// @param[in] cellSize The xz-plane cell size to use for the field. [Limit: > 0] [Units: wu]
/// @param[in] cellHeight The y-axis cell size to use for field. [Limit: > 0] [Units: wu]
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ,
const float* bmin, const float* bmax, const float* minBounds, const float* maxBounds,
float cs, float ch); float cellSize, float cellHeight);
/// Sets the area id of all triangles with a slope below the specified value /// Sets the area id of all triangles with a slope below the specified value
/// to #RC_WALKABLE_AREA. /// to #RC_WALKABLE_AREA.
///
/// Only sets the area id's for the walkable triangles. Does not alter the
/// area id's for un-walkable triangles.
///
/// See the #rcConfig documentation for more information on the configuration parameters.
///
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
///
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. /// @param[in] walkableSlopeAngle The maximum slope that is considered walkable.
/// [Limits: 0 <= value < 90] [Units: Degrees] /// [Limits: 0 <= value < 90] [Units: Degrees]
/// @param[in] verts The vertices. [(x, y, z) * @p nv] /// @param[in] verts The vertices. [(x, y, z) * @p nv]
/// @param[in] nv The number of vertices. /// @param[in] numVerts The number of vertices.
/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] /// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt]
/// @param[in] nt The number of triangles. /// @param[in] numTris The number of triangles.
/// @param[out] areas The triangle area ids. [Length: >= @p nt] /// @param[out] triAreaIDs The triangle area ids. [Length: >= @p nt]
void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, void rcMarkWalkableTriangles(rcContext* context, float walkableSlopeAngle, const float* verts, int numVerts,
const int* tris, int nt, unsigned char* areas); const int* tris, int numTris, unsigned char* triAreaIDs);
/// Sets the area id of all triangles with a slope greater than or equal to the specified value to #RC_NULL_AREA. /// Sets the area id of all triangles with a slope greater than or equal to the specified value to #RC_NULL_AREA.
///
/// Only sets the area id's for the un-walkable 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
///
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. /// @param[in] walkableSlopeAngle The maximum slope that is considered walkable.
/// [Limits: 0 <= value < 90] [Units: Degrees] /// [Limits: 0 <= value < 90] [Units: Degrees]
/// @param[in] verts The vertices. [(x, y, z) * @p nv] /// @param[in] verts The vertices. [(x, y, z) * @p nv]
/// @param[in] nv The number of vertices. /// @param[in] numVerts The number of vertices.
/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] /// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt]
/// @param[in] nt The number of triangles. /// @param[in] numTris The number of triangles.
/// @param[out] areas The triangle area ids. [Length: >= @p nt] /// @param[out] triAreaIDs The triangle area ids. [Length: >= @p nt]
void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, void rcClearUnwalkableTriangles(rcContext* context, float walkableSlopeAngle, const float* verts, int numVerts,
const int* tris, int nt, unsigned char* areas); const int* tris, int numTris, unsigned char* triAreaIDs);
/// Adds a span to the specified heightfield. /// Adds a span to the specified heightfield.
///
/// The span addition can be set to favor flags. If the span is merged to
/// another span and the new @p spanMax is within @p flagMergeThreshold units
/// from the existing span, the span flags are merged.
///
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in,out] hf An initialized heightfield. /// @param[in,out] heightfield An initialized heightfield.
/// @param[in] x The width index where the span is to be added. /// @param[in] x The column x index where the span is to be added.
/// [Limits: 0 <= value < rcHeightfield::width] /// [Limits: 0 <= value < rcHeightfield::width]
/// @param[in] y The height index where the span is to be added. /// @param[in] z The column z index where the span is to be added.
/// [Limits: 0 <= value < rcHeightfield::height] /// [Limits: 0 <= value < rcHeightfield::height]
/// @param[in] smin The minimum height of the span. [Limit: < @p smax] [Units: vx] /// @param[in] spanMin The minimum height of the span. [Limit: < @p spanMax] [Units: vx]
/// @param[in] smax The maximum height of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] [Units: vx] /// @param[in] spanMax The maximum height of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] [Units: vx]
/// @param[in] area The area id of the span. [Limit: <= #RC_WALKABLE_AREA) /// @param[in] areaID The area id of the span. [Limit: <= #RC_WALKABLE_AREA)
/// @param[in] flagMergeThr The merge theshold. [Limit: >= 0] [Units: vx] /// @param[in] flagMergeThreshold The merge threshold. [Limit: >= 0] [Units: vx]
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y, bool rcAddSpan(rcContext* context, rcHeightfield& heightfield,
const unsigned short smin, const unsigned short smax, int x, int z,
const unsigned char area, const int flagMergeThr); unsigned short spanMin, unsigned short spanMax,
unsigned char areaID, int flagMergeThreshold);
/// Rasterizes a triangle into the specified heightfield. /// Rasterizes a single triangle into the specified heightfield.
///
/// Calling this for each triangle in a mesh is less efficient than calling rcRasterizeTriangles
///
/// No spans will be added if the triangle does not overlap the heightfield grid.
///
/// @see rcHeightfield
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] v0 Triangle vertex 0 [(x, y, z)] /// @param[in] v0 Triangle vertex 0 [(x, y, z)]
/// @param[in] v1 Triangle vertex 1 [(x, y, z)] /// @param[in] v1 Triangle vertex 1 [(x, y, z)]
/// @param[in] v2 Triangle vertex 2 [(x, y, z)] /// @param[in] v2 Triangle vertex 2 [(x, y, z)]
/// @param[in] area The area id of the triangle. [Limit: <= #RC_WALKABLE_AREA] /// @param[in] areaID The area id of the triangle. [Limit: <= #RC_WALKABLE_AREA]
/// @param[in,out] solid An initialized heightfield. /// @param[in,out] heightfield An initialized heightfield.
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// @param[in] flagMergeThreshold The distance where the walkable flag is favored over the non-walkable flag.
/// [Limit: >= 0] [Units: vx] /// [Limit: >= 0] [Units: vx]
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, bool rcRasterizeTriangle(rcContext* context,
const unsigned char area, rcHeightfield& solid, const float* v0, const float* v1, const float* v2,
const int flagMergeThr = 1); unsigned char areaID, rcHeightfield& heightfield, int flagMergeThreshold = 1);
/// Rasterizes an indexed triangle mesh into the specified heightfield. /// Rasterizes an indexed triangle mesh into the specified heightfield.
///
/// Spans will only be added for triangles that overlap the heightfield grid.
///
/// @see rcHeightfield
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] verts The vertices. [(x, y, z) * @p nv] /// @param[in] verts The vertices. [(x, y, z) * @p nv]
/// @param[in] nv The number of vertices. /// @param[in] numVerts The number of vertices. (unused) TODO (graham): Remove in next major release
/// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] /// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt]
/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] /// @param[in] triAreaIDs The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt]
/// @param[in] nt The number of triangles. /// @param[in] numTris The number of triangles.
/// @param[in,out] solid An initialized heightfield. /// @param[in,out] heightfield An initialized heightfield.
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// @param[in] flagMergeThreshold The distance where the walkable flag is favored over the non-walkable flag.
/// [Limit: >= 0] [Units: vx] /// [Limit: >= 0] [Units: vx]
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, bool rcRasterizeTriangles(rcContext* context,
const int* tris, const unsigned char* areas, const int nt, const float* verts, int numVerts,
rcHeightfield& solid, const int flagMergeThr = 1); const int* tris, const unsigned char* triAreaIDs, int numTris,
rcHeightfield& heightfield, int flagMergeThreshold = 1);
/// Rasterizes an indexed triangle mesh into the specified heightfield. /// Rasterizes an indexed triangle mesh into the specified heightfield.
///
/// Spans will only be added for triangles that overlap the heightfield grid.
///
/// @see rcHeightfield
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] verts The vertices. [(x, y, z) * @p nv] /// @param[in] verts The vertices. [(x, y, z) * @p nv]
/// @param[in] nv The number of vertices. /// @param[in] numVerts The number of vertices. (unused) TODO (graham): Remove in next major release
/// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] /// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt]
/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] /// @param[in] triAreaIDs The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt]
/// @param[in] nt The number of triangles. /// @param[in] numTris The number of triangles.
/// @param[in,out] solid An initialized heightfield. /// @param[in,out] heightfield An initialized heightfield.
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// @param[in] flagMergeThreshold The distance where the walkable flag is favored over the non-walkable flag.
/// [Limit: >= 0] [Units: vx] /// [Limit: >= 0] [Units: vx]
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, bool rcRasterizeTriangles(rcContext* context,
const unsigned short* tris, const unsigned char* areas, const int nt, const float* verts, int numVerts,
rcHeightfield& solid, const int flagMergeThr = 1); const unsigned short* tris, const unsigned char* triAreaIDs, int numTris,
rcHeightfield& heightfield, int flagMergeThreshold = 1);
/// Rasterizes triangles into the specified heightfield. /// Rasterizes a triangle list into the specified heightfield.
///
/// Expects each triangle to be specified as three sequential vertices of 3 floats.
///
/// Spans will only be added for triangles that overlap the heightfield grid.
///
/// @see rcHeightfield
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] verts The triangle vertices. [(ax, ay, az, bx, by, bz, cx, by, cx) * @p nt] /// @param[in] verts The triangle vertices. [(ax, ay, az, bx, by, bz, cx, by, cx) * @p nt]
/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] /// @param[in] triAreaIDs The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt]
/// @param[in] nt The number of triangles. /// @param[in] numTris The number of triangles.
/// @param[in,out] solid An initialized heightfield. /// @param[in,out] heightfield An initialized heightfield.
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// @param[in] flagMergeThreshold The distance where the walkable flag is favored over the non-walkable flag.
/// [Limit: >= 0] [Units: vx] /// [Limit: >= 0] [Units: vx]
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, bool rcRasterizeTriangles(rcContext* context,
rcHeightfield& solid, const int flagMergeThr = 1); const float* verts, const unsigned char* triAreaIDs, int numTris,
rcHeightfield& heightfield, int flagMergeThreshold = 1);
/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neighbor. /// Marks non-walkable spans as walkable if their maximum is within @p walkableClimb of a walkable neighbor.
///
/// 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
///
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. /// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable.
/// [Limit: >=0] [Units: vx] /// [Limit: >=0] [Units: vx]
/// @param[in,out] solid A fully built heightfield. (All spans have been added.) /// @param[in,out] heightfield A fully built heightfield. (All spans have been added.)
void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid); void rcFilterLowHangingWalkableObstacles(rcContext* context, int walkableClimb, rcHeightfield& heightfield);
/// Marks spans that are ledges as not-walkable. /// Marks spans that are ledges as not-walkable.
///
/// 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
///
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to /// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to
/// be considered walkable. [Limit: >= 3] [Units: vx] /// be considered walkable. [Limit: >= 3] [Units: vx]
/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. /// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable.
/// [Limit: >=0] [Units: vx] /// [Limit: >=0] [Units: vx]
/// @param[in,out] solid A fully built heightfield. (All spans have been added.) /// @param[in,out] heightfield A fully built heightfield. (All spans have been added.)
void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, void rcFilterLedgeSpans(rcContext* context, int walkableHeight, int walkableClimb, rcHeightfield& heightfield);
const int walkableClimb, rcHeightfield& solid);
/// Marks walkable spans as not walkable if the clearence above the span is less than the specified height. /// Marks walkable spans as not walkable if the clearance above the span is less than the specified height.
///
/// 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
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. ///
/// @param[in,out] context The build context to use during the operation.
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to /// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to
/// be considered walkable. [Limit: >= 3] [Units: vx] /// be considered walkable. [Limit: >= 3] [Units: vx]
/// @param[in,out] solid A fully built heightfield. (All spans have been added.) /// @param[in,out] heightfield A fully built heightfield. (All spans have been added.)
void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid); void rcFilterWalkableLowHeightSpans(rcContext* context, int walkableHeight, rcHeightfield& heightfield);
/// Returns the number of spans contained in the specified heightfield. /// Returns the number of spans contained in the specified heightfield.
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] context The build context to use during the operation.
/// @param[in] hf An initialized heightfield. /// @param[in] heightfield An initialized heightfield.
/// @returns The number of spans in the heightfield. /// @returns The number of spans in the heightfield.
int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf); int rcGetHeightFieldSpanCount(rcContext* context, const rcHeightfield& heightfield);
/// @} /// @}
/// @name Compact Heightfield Functions /// @name Compact Heightfield Functions
@ -938,17 +1064,26 @@ int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf);
/// @{ /// @{
/// Builds a compact heightfield representing open space, from a heightfield representing solid space. /// Builds a compact heightfield representing open space, from a heightfield representing solid space.
///
/// 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
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. ///
/// @param[in,out] context The build context to use during the operation.
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area /// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area
/// to be considered walkable. [Limit: >= 3] [Units: vx] /// to be considered walkable. [Limit: >= 3] [Units: vx]
/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. /// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable.
/// [Limit: >=0] [Units: vx] /// [Limit: >=0] [Units: vx]
/// @param[in] hf The heightfield to be compacted. /// @param[in] heightfield The heightfield to be compacted.
/// @param[out] chf The resulting compact heightfield. (Must be pre-allocated.) /// @param[out] compactHeightfield The resulting compact heightfield. (Must be pre-allocated.)
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb, bool rcBuildCompactHeightfield(rcContext* context, int walkableHeight, int walkableClimb,
rcHeightfield& hf, rcCompactHeightfield& chf); const rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield);
/// Erodes the walkable area within the heightfield by the specified radius. /// Erodes the walkable area within the heightfield by the specified radius.
/// @ingroup recast /// @ingroup recast
@ -1029,8 +1164,7 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
/// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, /// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible,
/// be merged with larger regions. [Limit: >=0] [Units: vx] /// be merged with larger regions. [Limit: >=0] [Units: vx]
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, int borderSize, int minRegionArea, int mergeRegionArea);
const int borderSize, const int minRegionArea, const int mergeRegionArea);
/// Builds region data for the heightfield by partitioning the heightfield in non-overlapping layers. /// Builds region data for the heightfield by partitioning the heightfield in non-overlapping layers.
/// @ingroup recast /// @ingroup recast
@ -1041,8 +1175,7 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. /// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas.
/// [Limit: >=0] [Units: vx]. /// [Limit: >=0] [Units: vx].
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, int borderSize, int minRegionArea);
const int borderSize, const int minRegionArea);
/// Builds region data for the heightfield using simple monotone partitioning. /// Builds region data for the heightfield using simple monotone partitioning.
/// @ingroup recast /// @ingroup recast
@ -1056,58 +1189,56 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
/// be merged with larger regions. [Limit: >=0] [Units: vx] /// be merged with larger regions. [Limit: >=0] [Units: vx]
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
const int borderSize, const int minRegionArea, const int mergeRegionArea); int borderSize, int minRegionArea, int mergeRegionArea);
/// Sets the neighbor connection data for the specified direction. /// Sets the neighbor connection data for the specified direction.
/// @param[in] s The span to update. /// @param[in] span The span to update.
/// @param[in] dir The direction to set. [Limits: 0 <= value < 4] /// @param[in] direction The direction to set. [Limits: 0 <= value < 4]
/// @param[in] i The index of the neighbor span. /// @param[in] neighborIndex The index of the neighbor span.
inline void rcSetCon(rcCompactSpan& s, int dir, int i) inline void rcSetCon(rcCompactSpan& span, int direction, int neighborIndex)
{ {
const unsigned int shift = (unsigned int)dir*6; const unsigned int shift = (unsigned int)direction * 6;
unsigned int con = s.con; const unsigned int con = span.con;
s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift); span.con = (con & ~(0x3f << shift)) | (((unsigned int)neighborIndex & 0x3f) << shift);
} }
/// Gets neighbor connection data for the specified direction. /// Gets neighbor connection data for the specified direction.
/// @param[in] s The span to check. /// @param[in] span The span to check.
/// @param[in] dir The direction to check. [Limits: 0 <= value < 4] /// @param[in] direction The direction to check. [Limits: 0 <= value < 4]
/// @return The neighbor connection data for the specified direction, /// @return The neighbor connection data for the specified direction, or #RC_NOT_CONNECTED if there is no connection.
/// or #RC_NOT_CONNECTED if there is no connection. inline int rcGetCon(const rcCompactSpan& span, int direction)
inline int rcGetCon(const rcCompactSpan& s, int dir)
{ {
const unsigned int shift = (unsigned int)dir*6; const unsigned int shift = (unsigned int)direction * 6;
return (s.con >> shift) & 0x3f; return (span.con >> shift) & 0x3f;
} }
/// Gets the standard width (x-axis) offset for the specified direction. /// Gets the standard width (x-axis) offset for the specified direction.
/// @param[in] dir The direction. [Limits: 0 <= value < 4] /// @param[in] direction The direction. [Limits: 0 <= value < 4]
/// @return The width offset to apply to the current cell position to move /// @return The width offset to apply to the current cell position to move in the direction.
/// in the direction. inline int rcGetDirOffsetX(int direction)
inline int rcGetDirOffsetX(int dir)
{ {
static const int offset[4] = { -1, 0, 1, 0, }; static const int offset[4] = { -1, 0, 1, 0, };
return offset[dir&0x03]; return offset[direction & 0x03];
} }
// TODO (graham): Rename this to rcGetDirOffsetZ
/// Gets the standard height (z-axis) offset for the specified direction. /// Gets the standard height (z-axis) offset for the specified direction.
/// @param[in] dir The direction. [Limits: 0 <= value < 4] /// @param[in] direction The direction. [Limits: 0 <= value < 4]
/// @return The height offset to apply to the current cell position to move /// @return The height offset to apply to the current cell position to move in the direction.
/// in the direction. inline int rcGetDirOffsetY(int direction)
inline int rcGetDirOffsetY(int dir)
{ {
static const int offset[4] = { 0, 1, 0, -1 }; static const int offset[4] = { 0, 1, 0, -1 };
return offset[dir&0x03]; return offset[direction & 0x03];
} }
/// Gets the direction for the specified offset. One of x and y should be 0. /// Gets the direction for the specified offset. One of x and y should be 0.
/// @param[in] x The x offset. [Limits: -1 <= value <= 1] /// @param[in] offsetX The x offset. [Limits: -1 <= value <= 1]
/// @param[in] y The y offset. [Limits: -1 <= value <= 1] /// @param[in] offsetZ The z offset. [Limits: -1 <= value <= 1]
/// @return The direction that represents the offset. /// @return The direction that represents the offset.
inline int rcGetDirForOffset(int x, int y) inline int rcGetDirForOffset(int offsetX, int offsetZ)
{ {
static const int dirs[5] = { 3, 0, -1, 2, 1 }; static const int dirs[5] = { 3, 0, -1, 2, 1 };
return dirs[((y+1)<<1)+x]; return dirs[((offsetZ + 1) << 1) + offsetX];
} }
/// @} /// @}
@ -1125,24 +1256,24 @@ inline int rcGetDirForOffset(int x, int y)
/// to be considered walkable. [Limit: >= 3] [Units: vx] /// to be considered walkable. [Limit: >= 3] [Units: vx]
/// @param[out] lset The resulting layer set. (Must be pre-allocated.) /// @param[out] lset The resulting layer set. (Must be pre-allocated.)
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildHeightfieldLayers(rcContext* ctx, const rcCompactHeightfield& chf,
const int borderSize, const int walkableHeight, int borderSize, int walkableHeight,
rcHeightfieldLayerSet& lset); rcHeightfieldLayerSet& lset);
/// Builds a contour set from the region outlines in the provided compact heightfield. /// Builds a contour set from the region outlines in the provided compact heightfield.
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] ctx The build context to use during the operation.
/// @param[in] chf A fully built compact heightfield. /// @param[in] chf A fully built compact heightfield.
/// @param[in] maxError The maximum distance a simplfied contour's border edges should deviate /// @param[in] maxError The maximum distance a simplified contour's border edges should deviate
/// the original raw contour. [Limit: >=0] [Units: wu] /// the original raw contour. [Limit: >=0] [Units: wu]
/// @param[in] maxEdgeLen The maximum allowed length for contour edges along the border of the mesh. /// @param[in] maxEdgeLen The maximum allowed length for contour edges along the border of the mesh.
/// [Limit: >=0] [Units: vx] /// [Limit: >=0] [Units: vx]
/// @param[out] cset The resulting contour set. (Must be pre-allocated.) /// @param[out] cset The resulting contour set. (Must be pre-allocated.)
/// @param[in] buildFlags The build flags. (See: #rcBuildContoursFlags) /// @param[in] buildFlags The build flags. (See: #rcBuildContoursFlags)
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildContours(rcContext* ctx, const rcCompactHeightfield& chf,
const float maxError, const int maxEdgeLen, float maxError, int maxEdgeLen,
rcContourSet& cset, const int buildFlags = RC_CONTOUR_TESS_WALL_EDGES); rcContourSet& cset, int buildFlags = RC_CONTOUR_TESS_WALL_EDGES);
/// Builds a polygon mesh from the provided contours. /// Builds a polygon mesh from the provided contours.
/// @ingroup recast /// @ingroup recast
@ -1152,7 +1283,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
/// contour to polygon conversion process. [Limit: >= 3] /// contour to polygon conversion process. [Limit: >= 3]
/// @param[out] mesh The resulting polygon mesh. (Must be re-allocated.) /// @param[out] mesh The resulting polygon mesh. (Must be re-allocated.)
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh); bool rcBuildPolyMesh(rcContext* ctx, const rcContourSet& cset, const int nvp, rcPolyMesh& mesh);
/// Merges multiple polygon meshes into a single mesh. /// Merges multiple polygon meshes into a single mesh.
/// @ingroup recast /// @ingroup recast
@ -1168,13 +1299,13 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] ctx The build context to use during the operation.
/// @param[in] mesh A fully built polygon mesh. /// @param[in] mesh A fully built polygon mesh.
/// @param[in] chf The compact heightfield used to build the polygon mesh. /// @param[in] chf The compact heightfield used to build the polygon mesh.
/// @param[in] sampleDist Sets the distance to use when samping the heightfield. [Limit: >=0] [Units: wu] /// @param[in] sampleDist Sets the distance to use when sampling the heightfield. [Limit: >=0] [Units: wu]
/// @param[in] sampleMaxError The maximum distance the detail mesh surface should deviate from /// @param[in] sampleMaxError The maximum distance the detail mesh surface should deviate from
/// heightfield data. [Limit: >=0] [Units: wu] /// heightfield data. [Limit: >=0] [Units: wu]
/// @param[out] dmesh The resulting detail mesh. (Must be pre-allocated.) /// @param[out] dmesh The resulting detail mesh. (Must be pre-allocated.)
/// @returns True if the operation completed successfully. /// @returns True if the operation completed successfully.
bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf, bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
const float sampleDist, const float sampleMaxError, float sampleDist, float sampleMaxError,
rcPolyMeshDetail& dmesh); rcPolyMeshDetail& dmesh);
/// Copies the poly mesh data from src to dst. /// Copies the poly mesh data from src to dst.

View File

@ -19,11 +19,11 @@
#ifndef RECASTALLOC_H #ifndef RECASTALLOC_H
#define RECASTALLOC_H #define RECASTALLOC_H
#include <stddef.h>
#include <stdint.h>
#include "RecastAssert.h" #include "RecastAssert.h"
#include <stdlib.h>
#include <stdint.h>
/// Provides hint values to the memory allocator on how long the /// Provides hint values to the memory allocator on how long the
/// memory is expected to be used. /// memory is expected to be used.
enum rcAllocHint enum rcAllocHint
@ -47,18 +47,27 @@ typedef void (rcFreeFunc)(void* ptr);
/// Sets the base custom allocation functions to be used by Recast. /// 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] allocFunc The memory allocation function to be used by #rcAlloc
/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree /// @param[in] freeFunc The memory de-allocation function to be used by #rcFree
///
/// @see rcAlloc, rcFree
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc); void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
/// Allocates a memory block. /// Allocates a memory block.
///
/// @param[in] size The size, in bytes of memory, to allocate. /// @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. /// @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. /// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
/// @see rcFree ///
/// @see rcFree, rcAllocSetCustom
void* rcAlloc(size_t size, rcAllocHint hint); void* rcAlloc(size_t size, rcAllocHint hint);
/// Deallocates a memory block. /// Deallocates a memory block. If @p ptr is NULL, this does nothing.
///
/// @warning This function leaves the value of @p ptr unchanged. So it still
/// points to the same (now invalid) location, and not to null.
///
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc. /// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
/// @see rcAlloc ///
/// @see rcAlloc, rcAllocSetCustom
void rcFree(void* ptr); void rcFree(void* ptr);
/// An implementation of operator new usable for placement new. The default one is part of STL (which we don't use). /// An implementation of operator new usable for placement new. The default one is part of STL (which we don't use).

View File

@ -19,12 +19,9 @@
#ifndef RECASTASSERT_H #ifndef RECASTASSERT_H
#define RECASTASSERT_H #define RECASTASSERT_H
// Note: This header file's only purpose is to include define assert.
// Feel free to change the file and include your own implementation instead.
#ifdef NDEBUG #ifdef NDEBUG
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ // From https://web.archive.org/web/20210117002833/http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
# define rcAssert(x) do { (void)sizeof(x); } while ((void)(__LINE__==-1), false) # define rcAssert(x) do { (void)sizeof(x); } while ((void)(__LINE__==-1), false)
#else #else

View File

@ -16,25 +16,23 @@
// 3. This notice may not be removed or altered from any source distribution. // 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 <stdarg.h>
#include "Recast.h" #include "Recast.h"
#include "RecastAlloc.h" #include "RecastAlloc.h"
#include "RecastAssert.h" #include "RecastAssert.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
namespace namespace
{ {
/// Allocates and constructs an object of the given type, returning a pointer. /// Allocates and constructs an object of the given type, returning a pointer.
/// TODO: Support constructor args. /// @param[in] allocLifetime Allocation lifetime hint
/// @param[in] hint Hint to the allocator.
template<typename T> template<typename T>
T* rcNew(rcAllocHint hint) { T* rcNew(const rcAllocHint allocLifetime)
T* ptr = (T*)rcAlloc(sizeof(T), hint); {
T* ptr = (T*)rcAlloc(sizeof(T), allocLifetime);
::new(rcNewTag(), (void*)ptr) T(); ::new(rcNewTag(), (void*)ptr) T();
return ptr; return ptr;
} }
@ -42,55 +40,41 @@ T* rcNew(rcAllocHint hint) {
/// Destroys and frees an object allocated with rcNew. /// Destroys and frees an object allocated with rcNew.
/// @param[in] ptr The object pointer to delete. /// @param[in] ptr The object pointer to delete.
template<typename T> template<typename T>
void rcDelete(T* ptr) { void rcDelete(T* ptr)
if (ptr) { {
if (ptr)
{
ptr->~T(); ptr->~T();
rcFree((void*)ptr); rcFree((void*)ptr);
} }
} }
} // namespace } // anonymous namespace
float rcSqrt(float x) float rcSqrt(float x)
{ {
return sqrtf(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, ...) void rcContext::log(const rcLogCategory category, const char* format, ...)
{ {
if (!m_logEnabled) if (!m_logEnabled)
{
return; return;
}
static const int MSG_SIZE = 512; static const int MSG_SIZE = 512;
char msg[MSG_SIZE]; char msg[MSG_SIZE];
va_list ap; va_list argList;
va_start(ap, format); va_start(argList, format);
int len = vsnprintf(msg, MSG_SIZE, format, ap); int len = vsnprintf(msg, MSG_SIZE, format, argList);
if (len >= MSG_SIZE) if (len >= MSG_SIZE)
{ {
len = MSG_SIZE - 1; len = MSG_SIZE - 1;
msg[MSG_SIZE - 1] = '\0'; msg[MSG_SIZE - 1] = '\0';
const char* errorMessage = "Log message was truncated";
doLog(RC_LOG_ERROR, errorMessage, (int)strlen(errorMessage));
} }
va_end(ap); va_end(argList);
doLog(category, msg, len); doLog(category, msg, len);
} }
@ -103,6 +87,12 @@ rcHeightfield* rcAllocHeightfield()
{ {
return rcNew<rcHeightfield>(RC_ALLOC_PERM); return rcNew<rcHeightfield>(RC_ALLOC_PERM);
} }
void rcFreeHeightField(rcHeightfield* heightfield)
{
rcDelete(heightfield);
}
rcHeightfield::rcHeightfield() rcHeightfield::rcHeightfield()
: width() : width()
, height() , height()
@ -129,40 +119,36 @@ rcHeightfield::~rcHeightfield()
} }
} }
void rcFreeHeightField(rcHeightfield* hf)
{
rcDelete(hf);
}
rcCompactHeightfield* rcAllocCompactHeightfield() rcCompactHeightfield* rcAllocCompactHeightfield()
{ {
return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM); return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);
} }
void rcFreeCompactHeightfield(rcCompactHeightfield* chf) void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield)
{ {
rcDelete(chf); rcDelete(compactHeightfield);
} }
rcCompactHeightfield::rcCompactHeightfield() rcCompactHeightfield::rcCompactHeightfield()
: width(), : width()
height(), , height()
spanCount(), , spanCount()
walkableHeight(), , walkableHeight()
walkableClimb(), , walkableClimb()
borderSize(), , borderSize()
maxDistance(), , maxDistance()
maxRegions(), , maxRegions()
bmin(), , bmin()
bmax(), , bmax()
cs(), , cs()
ch(), , ch()
cells(), , cells()
spans(), , spans()
dist(), , dist()
areas() , areas()
{ {
} }
rcCompactHeightfield::~rcCompactHeightfield() rcCompactHeightfield::~rcCompactHeightfield()
{ {
rcFree(cells); rcFree(cells);
@ -175,13 +161,18 @@ rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
{ {
return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM); return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM);
} }
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset)
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* layerSet)
{ {
rcDelete(lset); rcDelete(layerSet);
} }
rcHeightfieldLayerSet::rcHeightfieldLayerSet() rcHeightfieldLayerSet::rcHeightfieldLayerSet()
: layers(), nlayers() {} : layers()
, nlayers()
{
}
rcHeightfieldLayerSet::~rcHeightfieldLayerSet() rcHeightfieldLayerSet::~rcHeightfieldLayerSet()
{ {
for (int i = 0; i < nlayers; ++i) for (int i = 0; i < nlayers; ++i)
@ -198,22 +189,26 @@ rcContourSet* rcAllocContourSet()
{ {
return rcNew<rcContourSet>(RC_ALLOC_PERM); return rcNew<rcContourSet>(RC_ALLOC_PERM);
} }
void rcFreeContourSet(rcContourSet* cset)
void rcFreeContourSet(rcContourSet* contourSet)
{ {
rcDelete(cset); rcDelete(contourSet);
} }
rcContourSet::rcContourSet() rcContourSet::rcContourSet()
: conts(), : conts()
nconts(), , nconts()
bmin(), , bmin()
bmax(), , bmax()
cs(), , cs()
ch(), , ch()
width(), , width()
height(), , height()
borderSize(), , borderSize()
maxError() {} , maxError()
{
}
rcContourSet::~rcContourSet() rcContourSet::~rcContourSet()
{ {
for (int i = 0; i < nconts; ++i) for (int i = 0; i < nconts; ++i)
@ -224,32 +219,34 @@ rcContourSet::~rcContourSet()
rcFree(conts); rcFree(conts);
} }
rcPolyMesh* rcAllocPolyMesh() rcPolyMesh* rcAllocPolyMesh()
{ {
return rcNew<rcPolyMesh>(RC_ALLOC_PERM); return rcNew<rcPolyMesh>(RC_ALLOC_PERM);
} }
void rcFreePolyMesh(rcPolyMesh* pmesh)
void rcFreePolyMesh(rcPolyMesh* polyMesh)
{ {
rcDelete(pmesh); rcDelete(polyMesh);
} }
rcPolyMesh::rcPolyMesh() rcPolyMesh::rcPolyMesh()
: verts(), : verts()
polys(), , polys()
regs(), , regs()
flags(), , flags()
areas(), , areas()
nverts(), , nverts()
npolys(), , npolys()
maxpolys(), , maxpolys()
nvp(), , nvp()
bmin(), , bmin()
bmax(), , bmax()
cs(), , cs()
ch(), , ch()
borderSize(), , borderSize()
maxEdgeError() {} , maxEdgeError()
{
}
rcPolyMesh::~rcPolyMesh() rcPolyMesh::~rcPolyMesh()
{ {
@ -262,145 +259,140 @@ rcPolyMesh::~rcPolyMesh()
rcPolyMeshDetail* rcAllocPolyMeshDetail() rcPolyMeshDetail* rcAllocPolyMeshDetail()
{ {
rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM); return rcNew<rcPolyMeshDetail>(RC_ALLOC_PERM);
memset(dmesh, 0, sizeof(rcPolyMeshDetail));
return dmesh;
} }
void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh) void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh)
{ {
if (!dmesh) return; if (detailMesh == NULL)
rcFree(dmesh->meshes); {
rcFree(dmesh->verts); return;
rcFree(dmesh->tris); }
rcFree(dmesh); rcFree(detailMesh->meshes);
rcFree(detailMesh->verts);
rcFree(detailMesh->tris);
rcFree(detailMesh);
} }
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax) rcPolyMeshDetail::rcPolyMeshDetail()
: meshes()
, verts()
, tris()
, nmeshes()
, nverts()
, ntris()
{
}
void rcCalcBounds(const float* verts, int numVerts, float* minBounds, float* maxBounds)
{ {
// Calculate bounding box. // Calculate bounding box.
rcVcopy(bmin, verts); rcVcopy(minBounds, verts);
rcVcopy(bmax, verts); rcVcopy(maxBounds, verts);
for (int i = 1; i < nv; ++i) for (int i = 1; i < numVerts; ++i)
{ {
const float* v = &verts[i * 3]; const float* v = &verts[i * 3];
rcVmin(bmin, v); rcVmin(minBounds, v);
rcVmax(bmax, v); rcVmax(maxBounds, v);
} }
} }
void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h) void rcCalcGridSize(const float* minBounds, const float* maxBounds, const float cellSize, int* sizeX, int* sizeZ)
{ {
*w = (int)((bmax[0] - bmin[0])/cs+0.5f); *sizeX = (int)((maxBounds[0] - minBounds[0]) / cellSize + 0.5f);
*h = (int)((bmax[2] - bmin[2])/cs+0.5f); *sizeZ = (int)((maxBounds[2] - minBounds[2]) / cellSize + 0.5f);
} }
/// @par bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ,
/// const float* minBounds, const float* maxBounds,
/// See the #rcConfig documentation for more information on the configuration parameters. float cellSize, float cellHeight)
///
/// @see rcAllocHeightfield, rcHeightfield
bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
const float* bmin, const float* bmax,
float cs, float ch)
{ {
rcIgnoreUnused(ctx); rcIgnoreUnused(context);
hf.width = width; heightfield.width = sizeX;
hf.height = height; heightfield.height = sizeZ;
rcVcopy(hf.bmin, bmin); rcVcopy(heightfield.bmin, minBounds);
rcVcopy(hf.bmax, bmax); rcVcopy(heightfield.bmax, maxBounds);
hf.cs = cs; heightfield.cs = cellSize;
hf.ch = ch; heightfield.ch = cellHeight;
hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM); heightfield.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*) * heightfield.width * heightfield.height, RC_ALLOC_PERM);
if (!hf.spans) if (!heightfield.spans)
{
return false; return false;
memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height); }
memset(heightfield.spans, 0, sizeof(rcSpan*) * heightfield.width * heightfield.height);
return true; return true;
} }
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm) static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* faceNormal)
{ {
float e0[3], e1[3]; float e0[3], e1[3];
rcVsub(e0, v1, v0); rcVsub(e0, v1, v0);
rcVsub(e1, v2, v0); rcVsub(e1, v2, v0);
rcVcross(norm, e0, e1); rcVcross(faceNormal, e0, e1);
rcVnormalize(norm); rcVnormalize(faceNormal);
} }
/// @par void rcMarkWalkableTriangles(rcContext* context, const float walkableSlopeAngle,
/// const float* verts, const int numVerts,
/// Only sets the area id's for the walkable triangles. Does not alter the const int* tris, const int numTris,
/// area id's for unwalkable triangles. unsigned char* triAreaIDs)
///
/// 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)
{ {
rcIgnoreUnused(ctx); rcIgnoreUnused(context);
rcIgnoreUnused(nv); rcIgnoreUnused(numVerts);
const float walkableThr = cosf(walkableSlopeAngle / 180.0f * RC_PI); const float walkableThr = cosf(walkableSlopeAngle / 180.0f * RC_PI);
float norm[3]; float norm[3];
for (int i = 0; i < nt; ++i) for (int i = 0; i < numTris; ++i)
{ {
const int* tri = &tris[i * 3]; const int* tri = &tris[i * 3];
calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], norm); calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], norm);
// Check if the face is walkable. // Check if the face is walkable.
if (norm[1] > walkableThr) if (norm[1] > walkableThr)
areas[i] = RC_WALKABLE_AREA;
}
}
/// @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)
{ {
rcIgnoreUnused(ctx); triAreaIDs[i] = RC_WALKABLE_AREA;
}
}
}
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); void rcClearUnwalkableTriangles(rcContext* context, const float walkableSlopeAngle,
const float* verts, int numVerts,
const int* tris, int numTris,
unsigned char* triAreaIDs)
{
rcIgnoreUnused(context);
rcIgnoreUnused(numVerts);
float norm[3]; // The minimum Y value for a face normal of a triangle with a walkable slope.
const float walkableLimitY = cosf(walkableSlopeAngle / 180.0f * RC_PI);
for (int i = 0; i < nt; ++i) float faceNormal[3];
for (int i = 0; i < numTris; ++i)
{ {
const int* tri = &tris[i * 3]; const int* tri = &tris[i * 3];
calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm); calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], faceNormal);
// Check if the face is walkable. // Check if the face is walkable.
if (norm[1] <= walkableThr) if (faceNormal[1] <= walkableLimitY)
areas[i] = RC_NULL_AREA;
}
}
int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf)
{ {
rcIgnoreUnused(ctx); triAreaIDs[i] = RC_NULL_AREA;
}
}
}
const int w = hf.width; int rcGetHeightFieldSpanCount(rcContext* context, const rcHeightfield& heightfield)
const int h = hf.height; {
rcIgnoreUnused(context);
const int numCols = heightfield.width * heightfield.height;
int spanCount = 0; int spanCount = 0;
for (int y = 0; y < h; ++y) for (int columnIndex = 0; columnIndex < numCols; ++columnIndex)
{ {
for (int x = 0; x < w; ++x) for (rcSpan* span = heightfield.spans[columnIndex]; span != NULL; span = span->next)
{ {
for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next) if (span->area != RC_NULL_AREA)
{ {
if (s->area != RC_NULL_AREA)
spanCount++; spanCount++;
} }
} }
@ -408,173 +400,143 @@ int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf)
return spanCount; return spanCount;
} }
/// @par bool rcBuildCompactHeightfield(rcContext* context, const int walkableHeight, const int walkableClimb,
/// const rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield)
/// 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); rcAssert(context);
rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD); rcScopedTimer timer(context, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
const int w = hf.width; const int xSize = heightfield.width;
const int h = hf.height; const int zSize = heightfield.height;
const int spanCount = rcGetHeightFieldSpanCount(ctx, hf); const int spanCount = rcGetHeightFieldSpanCount(context, heightfield);
// Fill in header. // Fill in header.
chf.width = w; compactHeightfield.width = xSize;
chf.height = h; compactHeightfield.height = zSize;
chf.spanCount = spanCount; compactHeightfield.spanCount = spanCount;
chf.walkableHeight = walkableHeight; compactHeightfield.walkableHeight = walkableHeight;
chf.walkableClimb = walkableClimb; compactHeightfield.walkableClimb = walkableClimb;
chf.maxRegions = 0; compactHeightfield.maxRegions = 0;
rcVcopy(chf.bmin, hf.bmin); rcVcopy(compactHeightfield.bmin, heightfield.bmin);
rcVcopy(chf.bmax, hf.bmax); rcVcopy(compactHeightfield.bmax, heightfield.bmax);
chf.bmax[1] += walkableHeight*hf.ch; compactHeightfield.bmax[1] += walkableHeight * heightfield.ch;
chf.cs = hf.cs; compactHeightfield.cs = heightfield.cs;
chf.ch = hf.ch; compactHeightfield.ch = heightfield.ch;
chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM); compactHeightfield.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell) * xSize * zSize, RC_ALLOC_PERM);
if (!chf.cells) if (!compactHeightfield.cells)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h); context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", xSize * zSize);
return false; return false;
} }
memset(chf.cells, 0, sizeof(rcCompactCell)*w*h); memset(compactHeightfield.cells, 0, sizeof(rcCompactCell) * xSize * zSize);
chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM); compactHeightfield.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan) * spanCount, RC_ALLOC_PERM);
if (!chf.spans) if (!compactHeightfield.spans)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount); context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
return false; return false;
} }
memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount); memset(compactHeightfield.spans, 0, sizeof(rcCompactSpan) * spanCount);
chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM); compactHeightfield.areas = (unsigned char*)rcAlloc(sizeof(unsigned char) * spanCount, RC_ALLOC_PERM);
if (!chf.areas) if (!compactHeightfield.areas)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount); context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
return false; return false;
} }
memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount); memset(compactHeightfield.areas, RC_NULL_AREA, sizeof(unsigned char) * spanCount);
const int MAX_HEIGHT = 0xffff; const int MAX_HEIGHT = 0xffff;
// Fill in cells and spans. // Fill in cells and spans.
int idx = 0; int currentCellIndex = 0;
for (int y = 0; y < h; ++y) const int numColumns = xSize * zSize;
for (int columnIndex = 0; columnIndex < numColumns; ++columnIndex)
{ {
for (int x = 0; x < w; ++x) const rcSpan* span = heightfield.spans[columnIndex];
{
const rcSpan* s = hf.spans[x + y*w];
// If there are no spans at this cell, just leave the data to index=0, count=0. // If there are no spans at this cell, just leave the data to index=0, count=0.
if (!s) continue; if (span == NULL)
rcCompactCell& c = chf.cells[x+y*w];
c.index = idx;
c.count = 0;
while (s)
{ {
if (s->area != RC_NULL_AREA) continue;
{
const int bot = (int)s->smax;
const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff);
chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
chf.areas[idx] = s->area;
idx++;
c.count++;
} }
s = s->next;
rcCompactCell& cell = compactHeightfield.cells[columnIndex];
cell.index = currentCellIndex;
cell.count = 0;
for (; span != NULL; span = span->next)
{
if (span->area != RC_NULL_AREA)
{
const int bot = (int)span->smax;
const int top = span->next ? (int)span->next->smin : MAX_HEIGHT;
compactHeightfield.spans[currentCellIndex].y = (unsigned short)rcClamp(bot, 0, 0xffff);
compactHeightfield.spans[currentCellIndex].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
compactHeightfield.areas[currentCellIndex] = span->area;
currentCellIndex++;
cell.count++;
} }
} }
} }
// Find neighbour connections. // Find neighbour connections.
const int MAX_LAYERS = RC_NOT_CONNECTED - 1; const int MAX_LAYERS = RC_NOT_CONNECTED - 1;
int tooHighNeighbour = 0; int maxLayerIndex = 0;
for (int y = 0; y < h; ++y) const int zStride = xSize; // for readability
for (int z = 0; z < zSize; ++z)
{ {
for (int x = 0; x < w; ++x) for (int x = 0; x < xSize; ++x)
{ {
const rcCompactCell& c = chf.cells[x+y*w]; const rcCompactCell& cell = compactHeightfield.cells[x + z * zStride];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) for (int i = (int)cell.index, ni = (int)(cell.index + cell.count); i < ni; ++i)
{ {
rcCompactSpan& s = chf.spans[i]; rcCompactSpan& span = compactHeightfield.spans[i];
for (int dir = 0; dir < 4; ++dir) for (int dir = 0; dir < 4; ++dir)
{ {
rcSetCon(s, dir, RC_NOT_CONNECTED); rcSetCon(span, dir, RC_NOT_CONNECTED);
const int nx = x + rcGetDirOffsetX(dir); const int neighborX = x + rcGetDirOffsetX(dir);
const int ny = y + rcGetDirOffsetY(dir); const int neighborZ = z + rcGetDirOffsetY(dir);
// First check that the neighbour cell is in bounds. // First check that the neighbour cell is in bounds.
if (nx < 0 || ny < 0 || nx >= w || ny >= h) if (neighborX < 0 || neighborZ < 0 || neighborX >= xSize || neighborZ >= zSize)
{
continue; continue;
}
// Iterate over all neighbour spans and check if any of the is // Iterate over all neighbour spans and check if any of the is
// accessible from current cell. // accessible from current cell.
const rcCompactCell& nc = chf.cells[nx+ny*w]; const rcCompactCell& neighborCell = compactHeightfield.cells[neighborX + neighborZ * zStride];
for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k) for (int k = (int)neighborCell.index, nk = (int)(neighborCell.index + neighborCell.count); k < nk; ++k)
{ {
const rcCompactSpan& ns = chf.spans[k]; const rcCompactSpan& neighborSpan = compactHeightfield.spans[k];
const int bot = rcMax(s.y, ns.y); const int bot = rcMax(span.y, neighborSpan.y);
const int top = rcMin(s.y+s.h, ns.y+ns.h); const int top = rcMin(span.y + span.h, neighborSpan.y + neighborSpan.h);
// Check that the gap between the spans is walkable, // Check that the gap between the spans is walkable,
// and that the climb height between the gaps is not too high. // and that the climb height between the gaps is not too high.
if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb) if ((top - bot) >= walkableHeight && rcAbs((int)neighborSpan.y - (int)span.y) <= walkableClimb)
{ {
// Mark direction as walkable. // Mark direction as walkable.
const int lidx = k - (int)nc.index; const int layerIndex = k - (int)neighborCell.index;
if (lidx < 0 || lidx > MAX_LAYERS) if (layerIndex < 0 || layerIndex > MAX_LAYERS)
{ {
tooHighNeighbour = rcMax(tooHighNeighbour, lidx); maxLayerIndex = rcMax(maxLayerIndex, layerIndex);
continue; continue;
} }
rcSetCon(s, dir, lidx); rcSetCon(span, dir, layerIndex);
break; break;
} }
} }
} }
} }
} }
} }
if (tooHighNeighbour > MAX_LAYERS) if (maxLayerIndex > MAX_LAYERS)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)", context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
tooHighNeighbour, MAX_LAYERS); maxLayerIndex, MAX_LAYERS);
} }
return true; return true;
} }
/*
static int getHeightfieldMemoryUsage(const rcHeightfield& hf)
{
int size = 0;
size += sizeof(hf);
size += hf.width * hf.height * sizeof(rcSpan*);
rcSpanPool* pool = hf.pools;
while (pool)
{
size += (sizeof(rcSpanPool) - sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL;
pool = pool->next;
}
return size;
}
static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
{
int size = 0;
size += sizeof(rcCompactHeightfield);
size += sizeof(rcCompactSpan) * chf.spanCount;
size += sizeof(rcCompactCell) * chf.width * chf.height;
return size;
}
*/

View File

@ -16,10 +16,7 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
// //
#include <stdlib.h>
#include <string.h>
#include "RecastAlloc.h" #include "RecastAlloc.h"
#include "RecastAssert.h"
static void* rcAllocDefault(size_t size, rcAllocHint) static void* rcAllocDefault(size_t size, rcAllocHint)
{ {
@ -34,27 +31,21 @@ static void rcFreeDefault(void *ptr)
static rcAllocFunc* sRecastAllocFunc = rcAllocDefault; static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
static rcFreeFunc* sRecastFreeFunc = rcFreeDefault; static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
/// @see rcAlloc, rcFree
void rcAllocSetCustom(rcAllocFunc* allocFunc, rcFreeFunc* freeFunc) void rcAllocSetCustom(rcAllocFunc* allocFunc, rcFreeFunc* freeFunc)
{ {
sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault; sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault; sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
} }
/// @see rcAllocSetCustom
void* rcAlloc(size_t size, rcAllocHint hint) void* rcAlloc(size_t size, rcAllocHint hint)
{ {
return sRecastAllocFunc(size, 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) void rcFree(void* ptr)
{ {
if (ptr) if (ptr != NULL)
{
sRecastFreeFunc(ptr); sRecastFreeFunc(ptr);
} }
}

View File

@ -17,7 +17,6 @@
// //
#include <float.h> #include <float.h>
#define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>

View File

@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
// //
#define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@ -102,7 +101,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
} }
static void walkContour(int x, int y, int i, static void walkContour(int x, int y, int i,
rcCompactHeightfield& chf, const rcCompactHeightfield& chf,
unsigned char* flags, rcIntArray& points) unsigned char* flags, rcIntArray& points)
{ {
// Choose the first non-connected edge // Choose the first non-connected edge
@ -542,7 +541,7 @@ static bool vequal(const int* a, const int* b)
return a[0] == b[0] && a[2] == b[2]; 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) static bool intersectSegContour(const int* d0, const int* d1, int i, int n, const int* verts)
{ {
// For each edge (k,k+1) of P // For each edge (k,k+1) of P
for (int k = 0; k < n; k++) for (int k = 0; k < n; k++)
@ -778,9 +777,9 @@ static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
for (int j = 0; j < ndiags; j++) for (int j = 0; j < ndiags; j++)
{ {
const int* pt = &outline->verts[diags[j].vert*4]; const int* pt = &outline->verts[diags[j].vert*4];
bool intersect = intersectSegCountour(pt, corner, diags[i].vert, outline->nverts, outline->verts); bool intersect = intersectSegContour(pt, corner, diags[i].vert, outline->nverts, outline->verts);
for (int k = i; k < region.nholes && !intersect; k++) for (int k = i; k < region.nholes && !intersect; k++)
intersect |= intersectSegCountour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts); intersect |= intersectSegContour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts);
if (!intersect) if (!intersect)
{ {
index = diags[j].vert; index = diags[j].vert;
@ -821,7 +820,7 @@ static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
/// See the #rcConfig documentation for more information on the configuration parameters. /// See the #rcConfig documentation for more information on the configuration parameters.
/// ///
/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig /// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildContours(rcContext* ctx, const rcCompactHeightfield& chf,
const float maxError, const int maxEdgeLen, const float maxError, const int maxEdgeLen,
rcContourSet& cset, const int buildFlags) rcContourSet& cset, const int buildFlags)
{ {

View File

@ -16,135 +16,121 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
// //
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include "Recast.h" #include "Recast.h"
#include "RecastAssert.h" #include "RecastAssert.h"
/// @par #include <stdlib.h>
///
/// 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);
rcScopedTimer timer(ctx, RC_TIMER_FILTER_LOW_OBSTACLES); void rcFilterLowHangingWalkableObstacles(rcContext* context, const int walkableClimb, rcHeightfield& heightfield)
const int w = solid.width;
const int h = solid.height;
for (int y = 0; y < h; ++y)
{ {
for (int x = 0; x < w; ++x) rcAssert(context);
rcScopedTimer timer(context, RC_TIMER_FILTER_LOW_OBSTACLES);
const int xSize = heightfield.width;
const int zSize = heightfield.height;
for (int z = 0; z < zSize; ++z)
{ {
rcSpan* ps = 0; for (int x = 0; x < xSize; ++x)
bool previousWalkable = false; {
rcSpan* previousSpan = NULL;
bool previousWasWalkable = false;
unsigned char previousArea = RC_NULL_AREA; unsigned char previousArea = RC_NULL_AREA;
for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next) for (rcSpan* span = heightfield.spans[x + z * xSize]; span != NULL; previousSpan = span, span = span->next)
{ {
const bool walkable = s->area != RC_NULL_AREA; const bool walkable = span->area != RC_NULL_AREA;
// If current span is not walkable, but there is walkable // If current span is not walkable, but there is walkable
// span just below it, mark the span above it walkable too. // span just below it, mark the span above it walkable too.
if (!walkable && previousWalkable) if (!walkable && previousWasWalkable)
{ {
if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb) if (rcAbs((int)span->smax - (int)previousSpan->smax) <= walkableClimb)
s->area = previousArea; {
span->area = previousArea;
}
} }
// Copy walkable flag so that it cannot propagate // Copy walkable flag so that it cannot propagate
// past multiple non-walkable objects. // past multiple non-walkable objects.
previousWalkable = walkable; previousWasWalkable = walkable;
previousArea = s->area; previousArea = span->area;
} }
} }
} }
} }
/// @par void rcFilterLedgeSpans(rcContext* context, const int walkableHeight, const int walkableClimb,
/// rcHeightfield& heightfield)
/// 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); rcAssert(context);
rcScopedTimer timer(ctx, RC_TIMER_FILTER_BORDER); rcScopedTimer timer(context, RC_TIMER_FILTER_BORDER);
const int w = solid.width; const int xSize = heightfield.width;
const int h = solid.height; const int zSize = heightfield.height;
const int MAX_HEIGHT = 0xffff; const int MAX_HEIGHT = 0xffff; // TODO (graham): Move this to a more visible constant and update usages.
// Mark border spans. // Mark border spans.
for (int y = 0; y < h; ++y) for (int z = 0; z < zSize; ++z)
{ {
for (int x = 0; x < w; ++x) for (int x = 0; x < xSize; ++x)
{ {
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next) for (rcSpan* span = heightfield.spans[x + z * xSize]; span; span = span->next)
{ {
// Skip non walkable spans. // Skip non walkable spans.
if (s->area == RC_NULL_AREA) if (span->area == RC_NULL_AREA)
{
continue; continue;
}
const int bot = (int)(s->smax); const int bot = (int)(span->smax);
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT;
// Find neighbours minimum height. // Find neighbours minimum height.
int minh = MAX_HEIGHT; int minNeighborHeight = MAX_HEIGHT;
// Min and max height of accessible neighbours. // Min and max height of accessible neighbours.
int asmin = s->smax; int accessibleNeighborMinHeight = span->smax;
int asmax = s->smax; int accessibleNeighborMaxHeight = span->smax;
for (int dir = 0; dir < 4; ++dir) for (int direction = 0; direction < 4; ++direction)
{ {
int dx = x + rcGetDirOffsetX(dir); int dx = x + rcGetDirOffsetX(direction);
int dy = y + rcGetDirOffsetY(dir); int dy = z + rcGetDirOffsetY(direction);
// Skip neighbours which are out of bounds. // Skip neighbours which are out of bounds.
if (dx < 0 || dy < 0 || dx >= w || dy >= h) if (dx < 0 || dy < 0 || dx >= xSize || dy >= zSize)
{ {
minh = rcMin(minh, -walkableClimb - bot); minNeighborHeight = rcMin(minNeighborHeight, -walkableClimb - bot);
continue; continue;
} }
// From minus infinity to the first span. // From minus infinity to the first span.
rcSpan* ns = solid.spans[dx + dy*w]; const rcSpan* neighborSpan = heightfield.spans[dx + dy * xSize];
int nbot = -walkableClimb; int neighborBot = -walkableClimb;
int ntop = ns ? (int)ns->smin : MAX_HEIGHT; int neighborTop = neighborSpan ? (int)neighborSpan->smin : MAX_HEIGHT;
// Skip neightbour if the gap between the spans is too small.
if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) // Skip neighbour if the gap between the spans is too small.
minh = rcMin(minh, nbot - bot); if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight)
{
minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot);
}
// Rest of the spans. // Rest of the spans.
for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next) for (neighborSpan = heightfield.spans[dx + dy * xSize]; neighborSpan; neighborSpan = neighborSpan->next)
{ {
nbot = (int)ns->smax; neighborBot = (int)neighborSpan->smax;
ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT; neighborTop = neighborSpan->next ? (int)neighborSpan->next->smin : MAX_HEIGHT;
// Skip neightbour if the gap between the spans is too small.
if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) // Skip neighbour if the gap between the spans is too small.
if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight)
{ {
minh = rcMin(minh, nbot - bot); minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot);
// Find min/max accessible neighbour height. // Find min/max accessible neighbour height.
if (rcAbs(nbot - bot) <= walkableClimb) if (rcAbs(neighborBot - bot) <= walkableClimb)
{ {
if (nbot < asmin) asmin = nbot; if (neighborBot < accessibleNeighborMinHeight) accessibleNeighborMinHeight = neighborBot;
if (nbot > asmax) asmax = nbot; if (neighborBot > accessibleNeighborMaxHeight) accessibleNeighborMaxHeight = neighborBot;
} }
} }
@ -153,49 +139,45 @@ void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walk
// The current span is close to a ledge if the drop to any // The current span is close to a ledge if the drop to any
// neighbour span is less than the walkableClimb. // neighbour span is less than the walkableClimb.
if (minh < -walkableClimb) if (minNeighborHeight < -walkableClimb)
{ {
s->area = RC_NULL_AREA; span->area = RC_NULL_AREA;
} }
// If the difference between all neighbours is too large, // If the difference between all neighbours is too large,
// we are at steep slope, mark the span as ledge. // we are at steep slope, mark the span as ledge.
else if ((asmax - asmin) > walkableClimb) else if ((accessibleNeighborMaxHeight - accessibleNeighborMinHeight) > walkableClimb)
{ {
s->area = RC_NULL_AREA; span->area = RC_NULL_AREA;
} }
} }
} }
} }
} }
/// @par void rcFilterWalkableLowHeightSpans(rcContext* context, const int walkableHeight, rcHeightfield& heightfield)
///
/// 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); rcAssert(context);
rcScopedTimer timer(ctx, RC_TIMER_FILTER_WALKABLE); rcScopedTimer timer(context, RC_TIMER_FILTER_WALKABLE);
const int w = solid.width; const int xSize = heightfield.width;
const int h = solid.height; const int zSize = heightfield.height;
const int MAX_HEIGHT = 0xffff; const int MAX_HEIGHT = 0xffff;
// Remove walkable flag from spans which do not have enough // Remove walkable flag from spans which do not have enough
// space above them for the agent to stand there. // space above them for the agent to stand there.
for (int y = 0; y < h; ++y) for (int z = 0; z < zSize; ++z)
{ {
for (int x = 0; x < w; ++x) for (int x = 0; x < xSize; ++x)
{ {
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next) for (rcSpan* span = heightfield.spans[x + z*xSize]; span; span = span->next)
{ {
const int bot = (int)(s->smax); const int bot = (int)(span->smax);
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT;
if ((top - bot) <= walkableHeight) if ((top - bot) < walkableHeight)
s->area = RC_NULL_AREA; {
span->area = RC_NULL_AREA;
}
} }
} }
} }

View File

@ -17,7 +17,6 @@
// //
#include <float.h> #include <float.h>
#define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -29,8 +28,21 @@
// Must be 255 or smaller (not 256) because layer IDs are stored as // Must be 255 or smaller (not 256) because layer IDs are stored as
// a byte where 255 is a special value. // a byte where 255 is a special value.
static const int RC_MAX_LAYERS = 63; #ifndef RC_MAX_LAYERS_DEF
static const int RC_MAX_NEIS = 16; #define RC_MAX_LAYERS_DEF 63
#endif
#if RC_MAX_LAYERS_DEF > 255
#error RC_MAX_LAYERS_DEF must be 255 or smaller
#endif
#ifndef RC_MAX_NEIS_DEF
#define RC_MAX_NEIS_DEF 16
#endif
// Keep type checking.
static const int RC_MAX_LAYERS = RC_MAX_LAYERS_DEF;
static const int RC_MAX_NEIS = RC_MAX_NEIS_DEF;
struct rcLayerRegion struct rcLayerRegion
{ {
@ -89,7 +101,7 @@ struct rcLayerSweepSpan
/// See the #rcConfig documentation for more information on the configuration parameters. /// See the #rcConfig documentation for more information on the configuration parameters.
/// ///
/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig /// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildHeightfieldLayers(rcContext* ctx, const rcCompactHeightfield& chf,
const int borderSize, const int walkableHeight, const int borderSize, const int walkableHeight,
rcHeightfieldLayerSet& lset) rcHeightfieldLayerSet& lset)
{ {

View File

@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
// //
#define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@ -35,7 +34,7 @@ static bool buildMeshAdjacency(unsigned short* polys, const int npolys,
const int nverts, const int vertsPerPoly) const int nverts, const int vertsPerPoly)
{ {
// Based on code by Eric Lengyel from: // Based on code by Eric Lengyel from:
// http://www.terathon.com/code/edges.php // https://web.archive.org/web/20080704083314/http://www.terathon.com/code/edges.php
int maxEdgeCount = npolys*vertsPerPoly; int maxEdgeCount = npolys*vertsPerPoly;
unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP); unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP);
@ -987,7 +986,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
/// limit must be retricted to <= #DT_VERTS_PER_POLYGON. /// limit must be retricted to <= #DT_VERTS_PER_POLYGON.
/// ///
/// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig /// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig
bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh) bool rcBuildPolyMesh(rcContext* ctx, const rcContourSet& cset, const int nvp, rcPolyMesh& mesh)
{ {
rcAssert(ctx); rcAssert(ctx);

View File

@ -17,7 +17,6 @@
// //
#include <float.h> #include <float.h>
#define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>

View File

@ -16,321 +16,485 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
// //
#define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include "Recast.h" #include "Recast.h"
#include "RecastAlloc.h" #include "RecastAlloc.h"
#include "RecastAssert.h" #include "RecastAssert.h"
inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax) /// Check whether two bounding boxes overlap
///
/// @param[in] aMin Min axis extents of bounding box A
/// @param[in] aMax Max axis extents of bounding box A
/// @param[in] bMin Min axis extents of bounding box B
/// @param[in] bMax Max axis extents of bounding box B
/// @returns true if the two bounding boxes overlap. False otherwise.
static bool overlapBounds(const float* aMin, const float* aMax, const float* bMin, const float* bMax)
{ {
bool overlap = true; return
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; aMin[0] <= bMax[0] && aMax[0] >= bMin[0] &&
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; aMin[1] <= bMax[1] && aMax[1] >= bMin[1] &&
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; aMin[2] <= bMax[2] && aMax[2] >= bMin[2];
return overlap;
} }
inline bool overlapInterval(unsigned short amin, unsigned short amax, /// Allocates a new span in the heightfield.
unsigned short bmin, unsigned short bmax) /// Use a memory pool and free list to minimize actual allocations.
{ ///
if (amax < bmin) return false; /// @param[in] hf The heightfield
if (amin > bmax) return false; /// @returns A pointer to the allocated or re-used span memory.
return true;
}
static rcSpan* allocSpan(rcHeightfield& hf) static rcSpan* allocSpan(rcHeightfield& hf)
{ {
// If running out of memory, allocate new page and update the freelist. // If necessary, allocate new page and update the freelist.
if (!hf.freelist || !hf.freelist->next) if (hf.freelist == NULL || hf.freelist->next == NULL)
{ {
// Create new page. // Create new page.
// Allocate memory for the new pool. // Allocate memory for the new pool.
rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); rcSpanPool* spanPool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
if (!pool) return 0; if (spanPool == NULL)
{
return NULL;
}
// Add the pool into the list of pools. // Add the pool into the list of pools.
pool->next = hf.pools; spanPool->next = hf.pools;
hf.pools = pool; hf.pools = spanPool;
// Add new items to the free list.
rcSpan* freelist = hf.freelist; // Add new spans to the free list.
rcSpan* head = &pool->items[0]; rcSpan* freeList = hf.freelist;
rcSpan* it = &pool->items[RC_SPANS_PER_POOL]; rcSpan* head = &spanPool->items[0];
rcSpan* it = &spanPool->items[RC_SPANS_PER_POOL];
do do
{ {
--it; --it;
it->next = freelist; it->next = freeList;
freelist = it; freeList = it;
} }
while (it != head); while (it != head);
hf.freelist = it; hf.freelist = it;
} }
// Pop item from in front of the free list. // Pop item from the front of the free list.
rcSpan* it = hf.freelist; rcSpan* newSpan = hf.freelist;
hf.freelist = hf.freelist->next; hf.freelist = hf.freelist->next;
return it; return newSpan;
} }
static void freeSpan(rcHeightfield& hf, rcSpan* ptr) /// Releases the memory used by the span back to the heightfield, so it can be re-used for new spans.
/// @param[in] hf The heightfield.
/// @param[in] span A pointer to the span to free
static void freeSpan(rcHeightfield& hf, rcSpan* span)
{ {
if (!ptr) return; if (span == NULL)
// Add the node in front of the free list. {
ptr->next = hf.freelist; return;
hf.freelist = ptr; }
// Add the span to the front of the free list.
span->next = hf.freelist;
hf.freelist = span;
} }
static bool addSpan(rcHeightfield& hf, const int x, const int y, /// Adds a span to the heightfield. If the new span overlaps existing spans,
const unsigned short smin, const unsigned short smax, /// it will merge the new span with the existing ones.
const unsigned char area, const int flagMergeThr) ///
/// @param[in] hf Heightfield to add spans to
/// @param[in] x The new span's column cell x index
/// @param[in] z The new span's column cell z index
/// @param[in] min The new span's minimum cell index
/// @param[in] max The new span's maximum cell index
/// @param[in] areaID The new span's area type ID
/// @param[in] flagMergeThreshold How close two spans maximum extents need to be to merge area type IDs
static bool addSpan(rcHeightfield& hf,
const int x, const int z,
const unsigned short min, const unsigned short max,
const unsigned char areaID, const int flagMergeThreshold)
{
// Create the new span.
rcSpan* newSpan = allocSpan(hf);
if (newSpan == NULL)
{ {
int idx = x + y*hf.width;
rcSpan* s = allocSpan(hf);
if (!s)
return false; return false;
s->smin = smin;
s->smax = smax;
s->area = area;
s->next = 0;
// Empty cell, add the first span.
if (!hf.spans[idx])
{
hf.spans[idx] = s;
return true;
} }
rcSpan* prev = 0; newSpan->smin = min;
rcSpan* cur = hf.spans[idx]; newSpan->smax = max;
newSpan->area = areaID;
newSpan->next = NULL;
// Insert and merge spans. const int columnIndex = x + z * hf.width;
while (cur) rcSpan* previousSpan = NULL;
rcSpan* currentSpan = hf.spans[columnIndex];
// Insert the new span, possibly merging it with existing spans.
while (currentSpan != NULL)
{ {
if (cur->smin > s->smax) if (currentSpan->smin > newSpan->smax)
{ {
// Current span is further than the new span, break. // Current span is completely after the new span, break.
break; break;
} }
else if (cur->smax < s->smin)
if (currentSpan->smax < newSpan->smin)
{ {
// Current span is before the new span advance. // Current span is completely before the new span. Keep going.
prev = cur; previousSpan = currentSpan;
cur = cur->next; currentSpan = currentSpan->next;
} }
else else
{ {
// Merge spans. // The new span overlaps with an existing span. Merge them.
if (cur->smin < s->smin) if (currentSpan->smin < newSpan->smin)
s->smin = cur->smin; {
if (cur->smax > s->smax) newSpan->smin = currentSpan->smin;
s->smax = cur->smax; }
if (currentSpan->smax > newSpan->smax)
{
newSpan->smax = currentSpan->smax;
}
// Merge flags. // Merge flags.
if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr) if (rcAbs((int)newSpan->smax - (int)currentSpan->smax) <= flagMergeThreshold)
s->area = rcMax(s->area, cur->area);
// Remove current span.
rcSpan* next = cur->next;
freeSpan(hf, cur);
if (prev)
prev->next = next;
else
hf.spans[idx] = next;
cur = next;
}
}
// Insert new span.
if (prev)
{ {
s->next = prev->next; // Higher area ID numbers indicate higher resolution priority.
prev->next = s; newSpan->area = rcMax(newSpan->area, currentSpan->area);
}
// Remove the current span since it's now merged with newSpan.
// Keep going because there might be other overlapping spans that also need to be merged.
rcSpan* next = currentSpan->next;
freeSpan(hf, currentSpan);
if (previousSpan)
{
previousSpan->next = next;
} }
else else
{ {
s->next = hf.spans[idx]; hf.spans[columnIndex] = next;
hf.spans[idx] = s; }
currentSpan = next;
}
}
// Insert new span after prev
if (previousSpan != NULL)
{
newSpan->next = previousSpan->next;
previousSpan->next = newSpan;
}
else
{
// This span should go before the others in the list
newSpan->next = hf.spans[columnIndex];
hf.spans[columnIndex] = newSpan;
} }
return true; return true;
} }
/// @par bool rcAddSpan(rcContext* context, rcHeightfield& heightfield,
/// const int x, const int z,
/// The span addition can be set to favor flags. If the span is merged to const unsigned short spanMin, const unsigned short spanMax,
/// another span and the new @p smax is within @p flagMergeThr units const unsigned char areaID, const int flagMergeThreshold)
/// 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); rcAssert(context);
if (!addSpan(hf, x, y, smin, smax, area, flagMergeThr)) if (!addSpan(heightfield, x, z, spanMin, spanMax, areaID, flagMergeThreshold))
{ {
ctx->log(RC_LOG_ERROR, "rcAddSpan: Out of memory."); context->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");
return false; return false;
} }
return true; return true;
} }
// divides a convex polygons into two convex polygons on both sides of a line enum rcAxis
static void dividePoly(const float* in, int nin,
float* out1, int* nout1,
float* out2, int* nout2,
float x, int axis)
{ {
float d[12]; RC_AXIS_X = 0,
for (int i = 0; i < nin; ++i) RC_AXIS_Y = 1,
d[i] = x - in[i*3+axis]; RC_AXIS_Z = 2
};
int m = 0, n = 0; /// Divides a convex polygon of max 12 vertices into two convex polygons
for (int i = 0, j = nin-1; i < nin; j=i, ++i) /// across a separating axis.
///
/// @param[in] inVerts The input polygon vertices
/// @param[in] inVertsCount The number of input polygon vertices
/// @param[out] outVerts1 Resulting polygon 1's vertices
/// @param[out] outVerts1Count The number of resulting polygon 1 vertices
/// @param[out] outVerts2 Resulting polygon 2's vertices
/// @param[out] outVerts2Count The number of resulting polygon 2 vertices
/// @param[in] axisOffset THe offset along the specified axis
/// @param[in] axis The separating axis
static void dividePoly(const float* inVerts, int inVertsCount,
float* outVerts1, int* outVerts1Count,
float* outVerts2, int* outVerts2Count,
float axisOffset, rcAxis axis)
{ {
bool ina = d[j] >= 0; rcAssert(inVertsCount <= 12);
bool inb = d[i] >= 0;
if (ina != inb) // How far positive or negative away from the separating axis is each vertex.
float inVertAxisDelta[12];
for (int inVert = 0; inVert < inVertsCount; ++inVert)
{ {
float s = d[j] / (d[j] - d[i]); inVertAxisDelta[inVert] = axisOffset - inVerts[inVert * 3 + axis];
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; int poly1Vert = 0;
rcVcopy(out2 + n*3, out1 + m*3); int poly2Vert = 0;
m++; for (int inVertA = 0, inVertB = inVertsCount - 1; inVertA < inVertsCount; inVertB = inVertA, ++inVertA)
n++; {
// add the i'th point to the right polygon. Do NOT add points that are on the dividing line // If the two vertices are on the same side of the separating axis
bool sameSide = (inVertAxisDelta[inVertA] >= 0) == (inVertAxisDelta[inVertB] >= 0);
if (!sameSide)
{
float s = inVertAxisDelta[inVertB] / (inVertAxisDelta[inVertB] - inVertAxisDelta[inVertA]);
outVerts1[poly1Vert * 3 + 0] = inVerts[inVertB * 3 + 0] + (inVerts[inVertA * 3 + 0] - inVerts[inVertB * 3 + 0]) * s;
outVerts1[poly1Vert * 3 + 1] = inVerts[inVertB * 3 + 1] + (inVerts[inVertA * 3 + 1] - inVerts[inVertB * 3 + 1]) * s;
outVerts1[poly1Vert * 3 + 2] = inVerts[inVertB * 3 + 2] + (inVerts[inVertA * 3 + 2] - inVerts[inVertB * 3 + 2]) * s;
rcVcopy(&outVerts2[poly2Vert * 3], &outVerts1[poly1Vert * 3]);
poly1Vert++;
poly2Vert++;
// add the inVertA point to the right polygon. Do NOT add points that are on the dividing line
// since these were already added above // since these were already added above
if (d[i] > 0) if (inVertAxisDelta[inVertA] > 0)
{ {
rcVcopy(out1 + m*3, in + i*3); rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
m++; poly1Vert++;
} }
else if (d[i] < 0) else if (inVertAxisDelta[inVertA] < 0)
{ {
rcVcopy(out2 + n*3, in + i*3); rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
n++; poly2Vert++;
} }
} }
else // same side else
{ {
// add the i'th point to the right polygon. Addition is done even for points on the dividing line // add the inVertA point to the right polygon. Addition is done even for points on the dividing line
if (d[i] >= 0) if (inVertAxisDelta[inVertA] >= 0)
{
rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
poly1Vert++;
if (inVertAxisDelta[inVertA] != 0)
{ {
rcVcopy(out1 + m*3, in + i*3);
m++;
if (d[i] != 0)
continue; continue;
} }
rcVcopy(out2 + n*3, in + i*3); }
n++; rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
poly2Vert++;
} }
} }
*nout1 = m; *outVerts1Count = poly1Vert;
*nout2 = n; *outVerts2Count = poly2Vert;
} }
/// Rasterize a single triangle to the heightfield.
///
/// This code is extremely hot, so much care should be given to maintaining maximum perf here.
///
/// @param[in] v0 Triangle vertex 0
/// @param[in] v1 Triangle vertex 1
/// @param[in] v2 Triangle vertex 2
/// @param[in] areaID The area ID to assign to the rasterized spans
/// @param[in] hf Heightfield to rasterize into
/// @param[in] hfBBMin The min extents of the heightfield bounding box
/// @param[in] hfBBMax The max extents of the heightfield bounding box
/// @param[in] cellSize The x and z axis size of a voxel in the heightfield
/// @param[in] inverseCellSize 1 / cellSize
/// @param[in] inverseCellHeight 1 / cellHeight
/// @param[in] flagMergeThreshold The threshold in which area flags will be merged
/// @returns true if the operation completes successfully. false if there was an error adding spans to the heightfield.
static bool 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 unsigned char areaID, rcHeightfield& hf,
const float* bmin, const float* bmax, const float* hfBBMin, const float* hfBBMax,
const float cs, const float ics, const float ich, const float cellSize, const float inverseCellSize, const float inverseCellHeight,
const int flagMergeThr) const int flagMergeThreshold)
{ {
// Calculate the bounding box of the triangle.
float triBBMin[3];
rcVcopy(triBBMin, v0);
rcVmin(triBBMin, v1);
rcVmin(triBBMin, v2);
float triBBMax[3];
rcVcopy(triBBMax, v0);
rcVmax(triBBMax, v1);
rcVmax(triBBMax, v2);
// If the triangle does not touch the bounding box of the heightfield, skip the triangle.
if (!overlapBounds(triBBMin, triBBMax, hfBBMin, hfBBMax))
{
return true;
}
const int w = hf.width; const int w = hf.width;
const int h = hf.height; const int h = hf.height;
float tmin[3], tmax[3]; const float by = hfBBMax[1] - hfBBMin[1];
const float by = bmax[1] - bmin[1];
// Calculate the bounding box of the triangle. // Calculate the footprint of the triangle on the grid's z-axis
rcVcopy(tmin, v0); int z0 = (int)((triBBMin[2] - hfBBMin[2]) * inverseCellSize);
rcVcopy(tmax, v0); int z1 = (int)((triBBMax[2] - hfBBMin[2]) * inverseCellSize);
rcVmin(tmin, v1);
rcVmin(tmin, v2);
rcVmax(tmax, v1);
rcVmax(tmax, v2);
// If the triangle does not touch the bbox of the heightfield, skip the triagle.
if (!overlapBounds(bmin, bmax, tmin, tmax))
return true;
// Calculate the footprint of the triangle on the grid's y-axis
int y0 = (int)((tmin[2] - bmin[2])*ics);
int y1 = (int)((tmax[2] - bmin[2])*ics);
// use -1 rather than 0 to cut the polygon properly at the start of the tile // use -1 rather than 0 to cut the polygon properly at the start of the tile
y0 = rcClamp(y0, -1, h-1); z0 = rcClamp(z0, -1, h - 1);
y1 = rcClamp(y1, 0, h-1); z1 = rcClamp(z1, 0, h - 1);
// Clip the triangle into all grid cells it touches. // Clip the triangle into all grid cells it touches.
float buf[7 * 3 * 4]; float buf[7 * 3 * 4];
float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3; float* in = buf;
float* inRow = buf + 7 * 3;
float* p1 = inRow + 7 * 3;
float* p2 = p1 + 7 * 3;
rcVcopy(&in[0], v0); rcVcopy(&in[0], v0);
rcVcopy(&in[1 * 3], v1); rcVcopy(&in[1 * 3], v1);
rcVcopy(&in[2 * 3], v2); rcVcopy(&in[2 * 3], v2);
int nvrow, nvIn = 3; int nvRow;
int nvIn = 3;
for (int y = y0; y <= y1; ++y) for (int z = z0; z <= z1; ++z)
{ {
// Clip polygon to row. Store the remaining polygon as well // Clip polygon to row. Store the remaining polygon as well
const float cz = bmin[2] + y*cs; const float cellZ = hfBBMin[2] + (float)z * cellSize;
dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2); dividePoly(in, nvIn, inRow, &nvRow, p1, &nvIn, cellZ + cellSize, RC_AXIS_Z);
rcSwap(in, p1); rcSwap(in, p1);
if (nvrow < 3) continue;
if (y < 0) continue; if (nvRow < 3)
// 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]; continue;
if (maxX < inrow[i*3]) maxX = inrow[i*3];
} }
int x0 = (int)((minX - bmin[0])*ics); if (z < 0)
int x1 = (int)((maxX - bmin[0])*ics); {
if (x1 < 0 || x0 >= w) { continue;
}
// find X-axis bounds of the row
float minX = inRow[0];
float maxX = inRow[0];
for (int vert = 1; vert < nvRow; ++vert)
{
if (minX > inRow[vert * 3])
{
minX = inRow[vert * 3];
}
if (maxX < inRow[vert * 3])
{
maxX = inRow[vert * 3];
}
}
int x0 = (int)((minX - hfBBMin[0]) * inverseCellSize);
int x1 = (int)((maxX - hfBBMin[0]) * inverseCellSize);
if (x1 < 0 || x0 >= w)
{
continue; continue;
} }
x0 = rcClamp(x0, -1, w - 1); x0 = rcClamp(x0, -1, w - 1);
x1 = rcClamp(x1, 0, w - 1); x1 = rcClamp(x1, 0, w - 1);
int nv, nv2 = nvrow; int nv;
int nv2 = nvRow;
for (int x = x0; x <= x1; ++x) for (int x = x0; x <= x1; ++x)
{ {
// Clip polygon to column. store the remaining polygon as well // Clip polygon to column. store the remaining polygon as well
const float cx = bmin[0] + x*cs; const float cx = hfBBMin[0] + (float)x * cellSize;
dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0); dividePoly(inRow, nv2, p1, &nv, p2, &nv2, cx + cellSize, RC_AXIS_X);
rcSwap(inrow, p2); rcSwap(inRow, p2);
if (nv < 3) continue;
if (x < 0) continue; if (nv < 3)
// Calculate min and max of the span.
float smin = p1[1], smax = p1[1];
for (int i = 1; i < nv; ++i)
{ {
smin = rcMin(smin, p1[i*3+1]); continue;
smax = rcMax(smax, p1[i*3+1]); }
if (x < 0)
{
continue;
}
// Calculate min and max of the span.
float spanMin = p1[1];
float spanMax = p1[1];
for (int vert = 1; vert < nv; ++vert)
{
spanMin = rcMin(spanMin, p1[vert * 3 + 1]);
spanMax = rcMax(spanMax, p1[vert * 3 + 1]);
}
spanMin -= hfBBMin[1];
spanMax -= hfBBMin[1];
// Skip the span if it's completely outside the heightfield bounding box
if (spanMax < 0.0f)
{
continue;
}
if (spanMin > by)
{
continue;
}
// Clamp the span to the heightfield bounding box.
if (spanMin < 0.0f)
{
spanMin = 0;
}
if (spanMax > by)
{
spanMax = by;
} }
smin -= bmin[1];
smax -= bmin[1];
// Skip the span if it is outside the heightfield bbox
if (smax < 0.0f) continue;
if (smin > by) continue;
// Clamp the span to the heightfield bbox.
if (smin < 0.0f) smin = 0;
if (smax > by) smax = by;
// Snap the span to the heightfield height grid. // Snap the span to the heightfield height grid.
unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT); unsigned short spanMinCellIndex = (unsigned short)rcClamp((int)floorf(spanMin * inverseCellHeight), 0, RC_SPAN_MAX_HEIGHT);
unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT); unsigned short spanMaxCellIndex = (unsigned short)rcClamp((int)ceilf(spanMax * inverseCellHeight), (int)spanMinCellIndex + 1, RC_SPAN_MAX_HEIGHT);
if (!addSpan(hf, x, y, ismin, ismax, area, flagMergeThr)) if (!addSpan(hf, x, z, spanMinCellIndex, spanMaxCellIndex, areaID, flagMergeThreshold))
{
return false;
}
}
}
return true;
}
bool rcRasterizeTriangle(rcContext* context,
const float* v0, const float* v1, const float* v2,
const unsigned char areaID, rcHeightfield& heightfield, const int flagMergeThreshold)
{
rcAssert(context != NULL);
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
// Rasterize the single triangle.
const float inverseCellSize = 1.0f / heightfield.cs;
const float inverseCellHeight = 1.0f / heightfield.ch;
if (!rasterizeTri(v0, v1, v2, areaID, heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
{
context->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");
return false;
}
return true;
}
bool rcRasterizeTriangles(rcContext* context,
const float* verts, const int /*nv*/,
const int* tris, const unsigned char* triAreaIDs, const int numTris,
rcHeightfield& heightfield, const int flagMergeThreshold)
{
rcAssert(context != NULL);
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
// Rasterize the triangles.
const float inverseCellSize = 1.0f / heightfield.cs;
const float inverseCellHeight = 1.0f / heightfield.ch;
for (int triIndex = 0; triIndex < numTris; ++triIndex)
{
const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
{
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
return false; return false;
} }
} }
@ -338,55 +502,26 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
return true; return true;
} }
/// @par bool rcRasterizeTriangles(rcContext* context,
/// const float* verts, const int /*nv*/,
/// No spans will be added if the triangle does not overlap the heightfield grid. const unsigned short* tris, const unsigned char* triAreaIDs, const int numTris,
/// rcHeightfield& heightfield, const int flagMergeThreshold)
/// @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); rcAssert(context != NULL);
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs; // Rasterize the triangles.
const float ich = 1.0f/solid.ch; const float inverseCellSize = 1.0f / heightfield.cs;
if (!rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) const float inverseCellHeight = 1.0f / heightfield.ch;
for (int triIndex = 0; triIndex < numTris; ++triIndex)
{ {
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory."); const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
return false; const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
} const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
return true;
}
/// @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); context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs;
const float ich = 1.0f/solid.ch;
// Rasterize triangles.
for (int i = 0; i < nt; ++i)
{
const float* v0 = &verts[tris[i*3+0]*3];
const float* v1 = &verts[tris[i*3+1]*3];
const float* v2 = &verts[tris[i*3+2]*3];
// Rasterize.
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; return false;
} }
} }
@ -394,62 +529,25 @@ bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
return true; return true;
} }
/// @par bool rcRasterizeTriangles(rcContext* context,
/// const float* verts, const unsigned char* triAreaIDs, const int numTris,
/// Spans will only be added for triangles that overlap the heightfield grid. rcHeightfield& heightfield, const int flagMergeThreshold)
///
/// @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); rcAssert(context != NULL);
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs; // Rasterize the triangles.
const float ich = 1.0f/solid.ch; const float inverseCellSize = 1.0f / heightfield.cs;
// Rasterize triangles. const float inverseCellHeight = 1.0f / heightfield.ch;
for (int i = 0; i < nt; ++i) for (int triIndex = 0; triIndex < numTris; ++triIndex)
{ {
const float* v0 = &verts[tris[i*3+0]*3]; const float* v0 = &verts[(triIndex * 3 + 0) * 3];
const float* v1 = &verts[tris[i*3+1]*3]; const float* v1 = &verts[(triIndex * 3 + 1) * 3];
const float* v2 = &verts[tris[i*3+2]*3]; const float* v2 = &verts[(triIndex * 3 + 2) * 3];
// Rasterize. if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
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."); context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
return false;
}
}
return true;
}
/// @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);
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs;
const float ich = 1.0f/solid.ch;
// Rasterize triangles.
for (int i = 0; i < nt; ++i)
{
const float* v0 = &verts[(i*3+0)*3];
const float* v1 = &verts[(i*3+1)*3];
const float* v2 = &verts[(i*3+2)*3];
// Rasterize.
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; return false;
} }
} }

View File

@ -17,7 +17,6 @@
// //
#include <float.h> #include <float.h>
#define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>