Recast: upgrade library.
- Upgrade Recast library to latest portable version - Implement recast_qsort based on FreeBSD qsort.c to have portable thread safe quick sort for use in conversion routine. - Better default value for the Build Navigation Mesh operator
This commit is contained in:
11
extern/recastnavigation/CMakeLists.txt
vendored
11
extern/recastnavigation/CMakeLists.txt
vendored
@@ -53,18 +53,19 @@ set(SRC
|
||||
Detour/Include/DetourTileNavMeshBuilder.h
|
||||
|
||||
Recast/Source/Recast.cpp
|
||||
Recast/Source/RecastContour.cpp
|
||||
Recast/Source/RecastAlloc.cpp
|
||||
Recast/Source/RecastArea.cpp
|
||||
Recast/Source/RecastContour.cpp
|
||||
Recast/Source/RecastFilter.cpp
|
||||
Recast/Source/RecastLog.cpp
|
||||
Recast/Source/RecastLayers.cpp
|
||||
Recast/Source/RecastMesh.cpp
|
||||
Recast/Source/RecastMeshDetail.cpp
|
||||
Recast/Source/RecastRasterization.cpp
|
||||
Recast/Source/RecastRegion.cpp
|
||||
Recast/Source/RecastTimer.cpp
|
||||
|
||||
Recast/Include/Recast.h
|
||||
Recast/Include/RecastLog.h
|
||||
Recast/Include/RecastTimer.h
|
||||
Recast/Include/RecastAlloc.h
|
||||
Recast/Include/RecastAssert.h
|
||||
)
|
||||
|
||||
blender_add_lib(extern_recastnavigation "${SRC}" "${INC}" "${INC_SYS}")
|
||||
|
||||
1275
extern/recastnavigation/Recast/Include/Recast.h
vendored
1275
extern/recastnavigation/Recast/Include/Recast.h
vendored
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -19,298 +19,685 @@
|
||||
#ifndef RECAST_H
|
||||
#define RECAST_H
|
||||
|
||||
// The units of the parameters are specified in parenthesis as follows:
|
||||
// (vx) voxels, (wu) world units
|
||||
/// The value of PI used by Recast.
|
||||
static const float RC_PI = 3.14159265f;
|
||||
|
||||
/// Recast log categories.
|
||||
/// @see rcContext
|
||||
enum rcLogCategory
|
||||
{
|
||||
RC_LOG_PROGRESS = 1, ///< A progress log entry.
|
||||
RC_LOG_WARNING, ///< A warning log entry.
|
||||
RC_LOG_ERROR, ///< An error log entry.
|
||||
};
|
||||
|
||||
/// Recast performance timer categories.
|
||||
/// @see rcContext
|
||||
enum rcTimerLabel
|
||||
{
|
||||
/// The user defined total time of the build.
|
||||
RC_TIMER_TOTAL,
|
||||
/// A user defined build time.
|
||||
RC_TIMER_TEMP,
|
||||
/// The time to rasterize the triangles. (See: #rcRasterizeTriangle)
|
||||
RC_TIMER_RASTERIZE_TRIANGLES,
|
||||
/// The time to build the compact heightfield. (See: #rcBuildCompactHeightfield)
|
||||
RC_TIMER_BUILD_COMPACTHEIGHTFIELD,
|
||||
/// The total time to build the contours. (See: #rcBuildContours)
|
||||
RC_TIMER_BUILD_CONTOURS,
|
||||
/// The time to trace the boundaries of the contours. (See: #rcBuildContours)
|
||||
RC_TIMER_BUILD_CONTOURS_TRACE,
|
||||
/// The time to simplify the contours. (See: #rcBuildContours)
|
||||
RC_TIMER_BUILD_CONTOURS_SIMPLIFY,
|
||||
/// The time to filter ledge spans. (See: #rcFilterLedgeSpans)
|
||||
RC_TIMER_FILTER_BORDER,
|
||||
/// The time to filter low height spans. (See: #rcFilterWalkableLowHeightSpans)
|
||||
RC_TIMER_FILTER_WALKABLE,
|
||||
/// The time to apply the median filter. (See: #rcMedianFilterWalkableArea)
|
||||
RC_TIMER_MEDIAN_AREA,
|
||||
/// The time to filter low obstacles. (See: #rcFilterLowHangingWalkableObstacles)
|
||||
RC_TIMER_FILTER_LOW_OBSTACLES,
|
||||
/// The time to build the polygon mesh. (See: #rcBuildPolyMesh)
|
||||
RC_TIMER_BUILD_POLYMESH,
|
||||
/// The time to merge polygon meshes. (See: #rcMergePolyMeshes)
|
||||
RC_TIMER_MERGE_POLYMESH,
|
||||
/// The time to erode the walkable area. (See: #rcErodeWalkableArea)
|
||||
RC_TIMER_ERODE_AREA,
|
||||
/// The time to mark a box area. (See: #rcMarkBoxArea)
|
||||
RC_TIMER_MARK_BOX_AREA,
|
||||
/// The time to mark a cylinder area. (See: #rcMarkCylinderArea)
|
||||
RC_TIMER_MARK_CYLINDER_AREA,
|
||||
/// The time to mark a convex polygon area. (See: #rcMarkConvexPolyArea)
|
||||
RC_TIMER_MARK_CONVEXPOLY_AREA,
|
||||
/// The total time to build the distance field. (See: #rcBuildDistanceField)
|
||||
RC_TIMER_BUILD_DISTANCEFIELD,
|
||||
/// The time to build the distances of the distance field. (See: #rcBuildDistanceField)
|
||||
RC_TIMER_BUILD_DISTANCEFIELD_DIST,
|
||||
/// The time to blur the distance field. (See: #rcBuildDistanceField)
|
||||
RC_TIMER_BUILD_DISTANCEFIELD_BLUR,
|
||||
/// The total time to build the regions. (See: #rcBuildRegions, #rcBuildRegionsMonotone)
|
||||
RC_TIMER_BUILD_REGIONS,
|
||||
/// The total time to apply the watershed algorithm. (See: #rcBuildRegions)
|
||||
RC_TIMER_BUILD_REGIONS_WATERSHED,
|
||||
/// The time to expand regions while applying the watershed algorithm. (See: #rcBuildRegions)
|
||||
RC_TIMER_BUILD_REGIONS_EXPAND,
|
||||
/// The time to flood regions while applying the watershed algorithm. (See: #rcBuildRegions)
|
||||
RC_TIMER_BUILD_REGIONS_FLOOD,
|
||||
/// The time to filter out small regions. (See: #rcBuildRegions, #rcBuildRegionsMonotone)
|
||||
RC_TIMER_BUILD_REGIONS_FILTER,
|
||||
/// The time to build heightfield layers. (See: #rcBuildHeightfieldLayers)
|
||||
RC_TIMER_BUILD_LAYERS,
|
||||
/// The time to build the polygon mesh detail. (See: #rcBuildPolyMeshDetail)
|
||||
RC_TIMER_BUILD_POLYMESHDETAIL,
|
||||
/// The time to merge polygon mesh details. (See: #rcMergePolyMeshDetails)
|
||||
RC_TIMER_MERGE_POLYMESHDETAIL,
|
||||
/// The maximum number of timers. (Used for iterating timers.)
|
||||
RC_MAX_TIMERS
|
||||
};
|
||||
|
||||
/// Provides an interface for optional logging and performance tracking of the Recast
|
||||
/// build process.
|
||||
/// @ingroup recast
|
||||
class rcContext
|
||||
{
|
||||
public:
|
||||
|
||||
/// Contructor.
|
||||
/// @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) {}
|
||||
virtual ~rcContext() {}
|
||||
|
||||
/// Enables or disables logging.
|
||||
/// @param[in] state TRUE if logging should be enabled.
|
||||
inline void enableLog(bool state) { m_logEnabled = state; }
|
||||
|
||||
/// Clears all log entries.
|
||||
inline void resetLog() { if (m_logEnabled) doResetLog(); }
|
||||
|
||||
/// Logs a message.
|
||||
/// @param[in] category The category of the message.
|
||||
/// @param[in] format The message.
|
||||
void log(const rcLogCategory category, const char* format, ...);
|
||||
|
||||
/// Enables or disables the performance timers.
|
||||
/// @param[in] state TRUE if timers should be enabled.
|
||||
inline void enableTimer(bool state) { m_timerEnabled = state; }
|
||||
|
||||
/// Clears all peformance timers. (Resets all to unused.)
|
||||
inline void resetTimers() { if (m_timerEnabled) doResetTimers(); }
|
||||
|
||||
/// Starts the specified performance timer.
|
||||
/// @param label The category of timer.
|
||||
inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); }
|
||||
|
||||
/// Stops the specified performance timer.
|
||||
/// @param label The category of the timer.
|
||||
inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); }
|
||||
|
||||
/// Returns the total accumulated time of the specified performance timer.
|
||||
/// @param label The category of the timer.
|
||||
/// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started.
|
||||
inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; }
|
||||
|
||||
protected:
|
||||
|
||||
/// Clears all log entries.
|
||||
virtual void doResetLog() {}
|
||||
|
||||
/// Logs a message.
|
||||
/// @param[in] category The category of the message.
|
||||
/// @param[in] msg The formatted message.
|
||||
/// @param[in] len The length of the formatted message.
|
||||
virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {}
|
||||
|
||||
/// Clears all timers. (Resets all to unused.)
|
||||
virtual void doResetTimers() {}
|
||||
|
||||
/// Starts the specified performance timer.
|
||||
/// @param[in] label The category of timer.
|
||||
virtual void doStartTimer(const rcTimerLabel /*label*/) {}
|
||||
|
||||
/// Stops the specified performance timer.
|
||||
/// @param[in] label The category of the timer.
|
||||
virtual void doStopTimer(const rcTimerLabel /*label*/) {}
|
||||
|
||||
/// Returns the total accumulated time of the specified performance timer.
|
||||
/// @param[in] label The category of the timer.
|
||||
/// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started.
|
||||
virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; }
|
||||
|
||||
/// True if logging is enabled.
|
||||
bool m_logEnabled;
|
||||
|
||||
/// True if the performance timers are enabled.
|
||||
bool m_timerEnabled;
|
||||
};
|
||||
|
||||
/// Specifies a configuration to use when performing Recast builds.
|
||||
/// @ingroup recast
|
||||
struct rcConfig
|
||||
{
|
||||
int width, height; // Dimensions of the rasterized heighfield (vx)
|
||||
int tileSize; // Width and Height of a tile (vx)
|
||||
int borderSize; // Non-navigable Border around the heightfield (vx)
|
||||
float cs, ch; // Grid cell size and height (wu)
|
||||
float bmin[3], bmax[3]; // Grid bounds (wu)
|
||||
float walkableSlopeAngle; // Maximum walkble slope angle in degrees.
|
||||
int walkableHeight; // Minimum height where the agent can still walk (vx)
|
||||
int walkableClimb; // Maximum height between grid cells the agent can climb (vx)
|
||||
int walkableRadius; // Radius of the agent in cells (vx)
|
||||
int maxEdgeLen; // Maximum contour edge length (vx)
|
||||
float maxSimplificationError; // Maximum distance error from contour to cells (vx)
|
||||
int minRegionSize; // Minimum regions size. Smaller regions will be deleted (vx)
|
||||
int mergeRegionSize; // Minimum regions size. Smaller regions will be merged (vx)
|
||||
int maxVertsPerPoly; // Max number of vertices per polygon
|
||||
float detailSampleDist; // Detail mesh sample spacing.
|
||||
float detailSampleMaxError; // Detail mesh simplification max sample error.
|
||||
/// The width of the field along the x-axis. [Limit: >= 0] [Units: vx]
|
||||
int width;
|
||||
|
||||
/// The height of the field along the z-axis. [Limit: >= 0] [Units: vx]
|
||||
int height;
|
||||
|
||||
/// The width/height size of tile's on the xz-plane. [Limit: >= 0] [Units: vx]
|
||||
int tileSize;
|
||||
|
||||
/// The size of the non-navigable border around the heightfield. [Limit: >=0] [Units: vx]
|
||||
int borderSize;
|
||||
|
||||
/// The xz-plane cell size to use for fields. [Limit: > 0] [Units: wu]
|
||||
float cs;
|
||||
|
||||
/// The y-axis cell size to use for fields. [Limit: > 0] [Units: wu]
|
||||
float ch;
|
||||
|
||||
/// The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu]
|
||||
float bmin[3];
|
||||
|
||||
/// The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu]
|
||||
float bmax[3];
|
||||
|
||||
/// The maximum slope that is considered walkable. [Limits: 0 <= value < 90] [Units: Degrees]
|
||||
float walkableSlopeAngle;
|
||||
|
||||
/// Minimum floor to 'ceiling' height that will still allow the floor area to
|
||||
/// be considered walkable. [Limit: >= 3] [Units: vx]
|
||||
int walkableHeight;
|
||||
|
||||
/// Maximum ledge height that is considered to still be traversable. [Limit: >=0] [Units: vx]
|
||||
int walkableClimb;
|
||||
|
||||
/// The distance to erode/shrink the walkable area of the heightfield away from
|
||||
/// obstructions. [Limit: >=0] [Units: vx]
|
||||
int walkableRadius;
|
||||
|
||||
/// The maximum allowed length for contour edges along the border of the mesh. [Limit: >=0] [Units: vx]
|
||||
int maxEdgeLen;
|
||||
|
||||
/// The maximum distance a simplfied contour's border edges should deviate
|
||||
/// the original raw contour. [Limit: >=0] [Units: wu]
|
||||
float maxSimplificationError;
|
||||
|
||||
/// The minimum number of cells allowed to form isolated island areas. [Limit: >=0] [Units: vx]
|
||||
int minRegionArea;
|
||||
|
||||
/// Any regions with a span count smaller than this value will, if possible,
|
||||
/// be merged with larger regions. [Limit: >=0] [Units: vx]
|
||||
int mergeRegionArea;
|
||||
|
||||
/// The maximum number of vertices allowed for polygons generated during the
|
||||
/// contour to polygon conversion process. [Limit: >= 3]
|
||||
int maxVertsPerPoly;
|
||||
|
||||
/// Sets the sampling distance to use when generating the detail mesh.
|
||||
/// (For height detail only.) [Limits: 0 or >= 0.9] [Units: wu]
|
||||
float detailSampleDist;
|
||||
|
||||
/// The maximum distance the detail mesh surface should deviate from heightfield
|
||||
/// data. (For height detail only.) [Limit: >=0] [Units: wu]
|
||||
float detailSampleMaxError;
|
||||
};
|
||||
|
||||
// Heightfield span.
|
||||
struct rcSpan
|
||||
{
|
||||
unsigned int smin : 15; // Span min height.
|
||||
unsigned int smax : 15; // Span max height.
|
||||
unsigned int flags : 2; // Span flags.
|
||||
rcSpan* next; // Next span in column.
|
||||
};
|
||||
/// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax.
|
||||
static const int RC_SPAN_HEIGHT_BITS = 13;
|
||||
/// Defines the maximum value for rcSpan::smin and rcSpan::smax.
|
||||
static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1;
|
||||
|
||||
/// The number of spans allocated per span spool.
|
||||
/// @see rcSpanPool
|
||||
static const int RC_SPANS_PER_POOL = 2048;
|
||||
|
||||
// Memory pool used for quick span allocation.
|
||||
/// Represents a span in a heightfield.
|
||||
/// @see rcHeightfield
|
||||
struct rcSpan
|
||||
{
|
||||
unsigned int smin : 13; ///< The lower limit of the span. [Limit: < #smax]
|
||||
unsigned int smax : 13; ///< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT]
|
||||
unsigned int area : 6; ///< The area id assigned to the span.
|
||||
rcSpan* next; ///< The next span higher up in column.
|
||||
};
|
||||
|
||||
/// A memory pool used for quick allocation of spans within a heightfield.
|
||||
/// @see rcHeightfield
|
||||
struct rcSpanPool
|
||||
{
|
||||
rcSpanPool* next; // Pointer to next pool.
|
||||
rcSpan items[1]; // Array of spans (size RC_SPANS_PER_POOL).
|
||||
rcSpanPool* next; ///< The next span pool.
|
||||
rcSpan items[RC_SPANS_PER_POOL]; ///< Array of spans in the pool.
|
||||
};
|
||||
|
||||
// Dynamic span-heightfield.
|
||||
/// A dynamic heightfield representing obstructed space.
|
||||
/// @ingroup recast
|
||||
struct rcHeightfield
|
||||
{
|
||||
inline rcHeightfield() : width(0), height(0), spans(0), pools(0), freelist(0) {}
|
||||
inline ~rcHeightfield()
|
||||
{
|
||||
// Delete span array.
|
||||
delete [] spans;
|
||||
// Delete span pools.
|
||||
while (pools)
|
||||
{
|
||||
rcSpanPool* next = pools->next;
|
||||
delete [] reinterpret_cast<unsigned char*>(pools);
|
||||
pools = next;
|
||||
}
|
||||
}
|
||||
int width, height; // Dimension of the heightfield.
|
||||
float bmin[3], bmax[3]; // Bounding box of the heightfield
|
||||
float cs, ch; // Cell size and height.
|
||||
rcSpan** spans; // Heightfield of spans (width*height).
|
||||
rcSpanPool* pools; // Linked list of span pools.
|
||||
rcSpan* freelist; // Pointer to next free span.
|
||||
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.)
|
||||
float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)]
|
||||
float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)]
|
||||
float cs; ///< The size of each cell. (On the xz-plane.)
|
||||
float ch; ///< The height of each cell. (The minimum increment along the y-axis.)
|
||||
rcSpan** spans; ///< Heightfield of spans (width*height).
|
||||
rcSpanPool* pools; ///< Linked list of span pools.
|
||||
rcSpan* freelist; ///< The next free span.
|
||||
};
|
||||
|
||||
/// Provides information on the content of a cell column in a compact heightfield.
|
||||
struct rcCompactCell
|
||||
{
|
||||
unsigned int index : 24; // Index to first span in column.
|
||||
unsigned int count : 8; // Number of spans in this column.
|
||||
unsigned int index : 24; ///< Index to the first span in the column.
|
||||
unsigned int count : 8; ///< Number of spans in the column.
|
||||
};
|
||||
|
||||
/// Represents a span of unobstructed space within a compact heightfield.
|
||||
struct rcCompactSpan
|
||||
{
|
||||
unsigned short y; // Bottom coordinate of the span.
|
||||
unsigned short reg; // Region ID
|
||||
unsigned short dist; // Distance to border
|
||||
unsigned short con; // Connections to neighbour cells.
|
||||
unsigned char h; // Height of the span.
|
||||
unsigned char flags; // Flags.
|
||||
unsigned short y; ///< The lower extent of the span. (Measured from the heightfield's base.)
|
||||
unsigned short reg; ///< The id of the region the span belongs to. (Or zero if not in a region.)
|
||||
unsigned int con : 24; ///< Packed neighbor connection data.
|
||||
unsigned int h : 8; ///< The height of the span. (Measured from #y.)
|
||||
};
|
||||
|
||||
// Compact static heightfield.
|
||||
/// A compact, static heightfield representing unobstructed space.
|
||||
/// @ingroup recast
|
||||
struct rcCompactHeightfield
|
||||
{
|
||||
inline rcCompactHeightfield() : maxDistance(0), maxRegions(0), cells(0), spans(0) {}
|
||||
inline ~rcCompactHeightfield() { delete [] cells; delete [] spans; }
|
||||
int width, height; // Width and height of the heighfield.
|
||||
int spanCount; // Number of spans in the heightfield.
|
||||
int walkableHeight, walkableClimb; // Agent properties.
|
||||
unsigned short maxDistance; // Maximum distance value stored in heightfield.
|
||||
unsigned short maxRegions; // Maximum Region Id stored in heightfield.
|
||||
float bmin[3], bmax[3]; // Bounding box of the heightfield.
|
||||
float cs, ch; // Cell size and height.
|
||||
rcCompactCell* cells; // Pointer to width*height cells.
|
||||
rcCompactSpan* spans; // Pointer to spans.
|
||||
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 spanCount; ///< The number of spans in the heightfield.
|
||||
int walkableHeight; ///< The walkable height used during the build of the field. (See: rcConfig::walkableHeight)
|
||||
int walkableClimb; ///< The walkable climb used during the build of the field. (See: rcConfig::walkableClimb)
|
||||
int borderSize; ///< The AABB border size used during the build of the field. (See: rcConfig::borderSize)
|
||||
unsigned short maxDistance; ///< The maximum distance value of any span within the field.
|
||||
unsigned short maxRegions; ///< The maximum region id of any span within the field.
|
||||
float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)]
|
||||
float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)]
|
||||
float cs; ///< The size of each cell. (On the xz-plane.)
|
||||
float ch; ///< The height of each cell. (The minimum increment along the y-axis.)
|
||||
rcCompactCell* cells; ///< Array of cells. [Size: #width*#height]
|
||||
rcCompactSpan* spans; ///< Array of spans. [Size: #spanCount]
|
||||
unsigned short* dist; ///< Array containing border distance data. [Size: #spanCount]
|
||||
unsigned char* areas; ///< Array containing area id data. [Size: #spanCount]
|
||||
};
|
||||
|
||||
/// Represents a heightfield layer within a layer set.
|
||||
/// @see rcHeightfieldLayerSet
|
||||
struct rcHeightfieldLayer
|
||||
{
|
||||
float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)]
|
||||
float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)]
|
||||
float cs; ///< The size of each cell. (On the xz-plane.)
|
||||
float ch; ///< The height of each cell. (The minimum increment along the y-axis.)
|
||||
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 minx; ///< The minimum x-bounds of usable data.
|
||||
int maxx; ///< The maximum x-bounds of usable data.
|
||||
int miny; ///< The minimum y-bounds of usable data. (Along the z-axis.)
|
||||
int maxy; ///< The maximum y-bounds of usable data. (Along the z-axis.)
|
||||
int hmin; ///< The minimum height bounds of usable data. (Along the y-axis.)
|
||||
int hmax; ///< The maximum height bounds of usable data. (Along the y-axis.)
|
||||
unsigned char* heights; ///< The heightfield. [Size: (width - borderSize*2) * (h - borderSize*2)]
|
||||
unsigned char* areas; ///< Area ids. [Size: Same as #heights]
|
||||
unsigned char* cons; ///< Packed neighbor connection information. [Size: Same as #heights]
|
||||
};
|
||||
|
||||
/// Represents a set of heightfield layers.
|
||||
/// @ingroup recast
|
||||
/// @see rcAllocHeightfieldLayerSet, rcFreeHeightfieldLayerSet
|
||||
struct rcHeightfieldLayerSet
|
||||
{
|
||||
rcHeightfieldLayer* layers; ///< The layers in the set. [Size: #nlayers]
|
||||
int nlayers; ///< The number of layers in the set.
|
||||
};
|
||||
|
||||
/// Represents a simple, non-overlapping contour in field space.
|
||||
struct rcContour
|
||||
{
|
||||
inline rcContour() : verts(0), nverts(0), rverts(0), nrverts(0) { }
|
||||
inline ~rcContour() { delete [] verts; delete [] rverts; }
|
||||
int* verts; // Vertex coordinates, each vertex contains 4 components.
|
||||
int nverts; // Number of vertices.
|
||||
int* rverts; // Raw vertex coordinates, each vertex contains 4 components.
|
||||
int nrverts; // Number of raw vertices.
|
||||
unsigned short reg; // Region ID of the contour.
|
||||
int* verts; ///< Simplified contour vertex and connection data. [Size: 4 * #nverts]
|
||||
int nverts; ///< The number of vertices in the simplified contour.
|
||||
int* rverts; ///< Raw contour vertex and connection data. [Size: 4 * #nrverts]
|
||||
int nrverts; ///< The number of vertices in the raw contour.
|
||||
unsigned short reg; ///< The region id of the contour.
|
||||
unsigned char area; ///< The area id of the contour.
|
||||
};
|
||||
|
||||
/// Represents a group of related contours.
|
||||
/// @ingroup recast
|
||||
struct rcContourSet
|
||||
{
|
||||
inline rcContourSet() : conts(0), nconts(0) {}
|
||||
inline ~rcContourSet() { delete [] conts; }
|
||||
rcContour* conts; // Pointer to all contours.
|
||||
int nconts; // Number of contours.
|
||||
float bmin[3], bmax[3]; // Bounding box of the heightfield.
|
||||
float cs, ch; // Cell size and height.
|
||||
rcContour* conts; ///< An array of the contours in the set. [Size: #nconts]
|
||||
int nconts; ///< The number of contours in the set.
|
||||
float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)]
|
||||
float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)]
|
||||
float cs; ///< The size of each cell. (On the xz-plane.)
|
||||
float ch; ///< The height of each cell. (The minimum increment along the y-axis.)
|
||||
int width; ///< The width of the set. (Along the x-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.
|
||||
};
|
||||
|
||||
// Polymesh store a connected mesh of polygons.
|
||||
// The polygons are store in an array where each polygons takes
|
||||
// 'nvp*2' elements. The first 'nvp' elements are indices to vertices
|
||||
// and the second 'nvp' elements are indices to neighbour polygons.
|
||||
// If a polygona has less than 'bvp' vertices, the remaining indices
|
||||
// are set os 0xffff. If an polygon edge does not have a neighbour
|
||||
// the neighbour index is set to 0xffff.
|
||||
// Vertices can be transformed into world space as follows:
|
||||
// x = bmin[0] + verts[i*3+0]*cs;
|
||||
// y = bmin[1] + verts[i*3+1]*ch;
|
||||
// z = bmin[2] + verts[i*3+2]*cs;
|
||||
/// Represents a polygon mesh suitable for use in building a navigation mesh.
|
||||
/// @ingroup recast
|
||||
struct rcPolyMesh
|
||||
{
|
||||
inline rcPolyMesh() : verts(0), polys(0), regs(0), nverts(0), npolys(0), nvp(3) {}
|
||||
inline ~rcPolyMesh() { delete [] verts; delete [] polys; delete [] regs; }
|
||||
unsigned short* verts; // Vertices of the mesh, 3 elements per vertex.
|
||||
unsigned short* polys; // Polygons of the mesh, nvp*2 elements per polygon.
|
||||
unsigned short* regs; // Regions of the polygons.
|
||||
int nverts; // Number of vertices.
|
||||
int npolys; // Number of polygons.
|
||||
int nvp; // Max number of vertices per polygon.
|
||||
float bmin[3], bmax[3]; // Bounding box of the mesh.
|
||||
float cs, ch; // Cell size and height.
|
||||
{
|
||||
unsigned short* verts; ///< The mesh vertices. [Form: (x, y, z) * #nverts]
|
||||
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* flags; ///< The user defined flags for each polygon. [Length: #maxpolys]
|
||||
unsigned char* areas; ///< The area id assigned to each polygon. [Length: #maxpolys]
|
||||
int nverts; ///< The number of vertices.
|
||||
int npolys; ///< The number of polygons.
|
||||
int maxpolys; ///< The number of allocated polygons.
|
||||
int nvp; ///< The maximum number of vertices per polygon.
|
||||
float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)]
|
||||
float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)]
|
||||
float cs; ///< The size of each cell. (On the xz-plane.)
|
||||
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.
|
||||
};
|
||||
|
||||
// Detail mesh generated from a rcPolyMesh.
|
||||
// Each submesh represents a polygon in the polymesh and they are stored in
|
||||
// excatly same order. Each submesh is described as 4 values:
|
||||
// base vertex, vertex count, base triangle, triangle count. That is,
|
||||
// const unsigned char* t = &dtl.tris[(tbase+i)*3]; and
|
||||
// const float* v = &dtl.verts[(vbase+t[j])*3];
|
||||
// If the input polygon has 'n' vertices, those vertices are first in the
|
||||
// submesh vertex list. This allows to compres the mesh by not storing the
|
||||
// first vertices and using the polymesh vertices instead.
|
||||
|
||||
/// Contains triangle meshes that represent detailed height data associated
|
||||
/// with the polygons in its associated polygon mesh object.
|
||||
/// @ingroup recast
|
||||
struct rcPolyMeshDetail
|
||||
{
|
||||
inline rcPolyMeshDetail() :
|
||||
meshes(0), verts(0), tris(0),
|
||||
nmeshes(0), nverts(0), ntris(0) {}
|
||||
inline ~rcPolyMeshDetail()
|
||||
{
|
||||
delete [] meshes; delete [] verts; delete [] tris;
|
||||
}
|
||||
|
||||
unsigned short* meshes; // Pointer to all mesh data.
|
||||
float* verts; // Pointer to all vertex data.
|
||||
unsigned char* tris; // Pointer to all triangle data.
|
||||
int nmeshes; // Number of meshes.
|
||||
int nverts; // Number of total vertices.
|
||||
int ntris; // Number of triangles.
|
||||
unsigned int* meshes; ///< The sub-mesh data. [Size: 4*#nmeshes]
|
||||
float* verts; ///< The mesh vertices. [Size: 3*#nverts]
|
||||
unsigned char* tris; ///< The mesh triangles. [Size: 4*#ntris]
|
||||
int nmeshes; ///< The number of sub-meshes defined by #meshes.
|
||||
int nverts; ///< The number of vertices in #verts.
|
||||
int ntris; ///< The number of triangles in #tris.
|
||||
};
|
||||
|
||||
/// @name Allocation Functions
|
||||
/// Functions used to allocate and de-allocate Recast objects.
|
||||
/// @see rcAllocSetCustom
|
||||
/// @{
|
||||
|
||||
// Simple dynamic array ints.
|
||||
class rcIntArray
|
||||
{
|
||||
int* m_data;
|
||||
int m_size, m_cap;
|
||||
public:
|
||||
inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
|
||||
inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(n) { m_data = new int[n]; }
|
||||
inline ~rcIntArray() { delete [] m_data; }
|
||||
void resize(int n);
|
||||
inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
|
||||
inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
|
||||
inline const int& operator[](int i) const { return m_data[i]; }
|
||||
inline int& operator[](int i) { return m_data[i]; }
|
||||
inline int size() const { return m_size; }
|
||||
};
|
||||
/// Allocates a heightfield object using the Recast allocator.
|
||||
/// @return A heightfield that is ready for initialization, or null on failure.
|
||||
/// @ingroup recast
|
||||
/// @see rcCreateHeightfield, rcFreeHeightField
|
||||
rcHeightfield* rcAllocHeightfield();
|
||||
|
||||
enum rcSpanFlags
|
||||
{
|
||||
RC_WALKABLE = 0x01,
|
||||
RC_REACHABLE = 0x02,
|
||||
};
|
||||
/// Frees the specified heightfield object using the Recast allocator.
|
||||
/// @param[in] hf A heightfield allocated using #rcAllocHeightfield
|
||||
/// @ingroup recast
|
||||
/// @see rcAllocHeightfield
|
||||
void rcFreeHeightField(rcHeightfield* hf);
|
||||
|
||||
// If heightfield region ID has the following bit set, the region is on border area
|
||||
// and excluded from many calculations.
|
||||
/// Allocates a compact heightfield object using the Recast allocator.
|
||||
/// @return A compact heightfield that is ready for initialization, or null on failure.
|
||||
/// @ingroup recast
|
||||
/// @see rcBuildCompactHeightfield, rcFreeCompactHeightfield
|
||||
rcCompactHeightfield* rcAllocCompactHeightfield();
|
||||
|
||||
/// Frees the specified compact heightfield object using the Recast allocator.
|
||||
/// @param[in] chf A compact heightfield allocated using #rcAllocCompactHeightfield
|
||||
/// @ingroup recast
|
||||
/// @see rcAllocCompactHeightfield
|
||||
void rcFreeCompactHeightfield(rcCompactHeightfield* chf);
|
||||
|
||||
/// Allocates a heightfield layer set using the Recast allocator.
|
||||
/// @return A heightfield layer set that is ready for initialization, or null on failure.
|
||||
/// @ingroup recast
|
||||
/// @see rcBuildHeightfieldLayers, rcFreeHeightfieldLayerSet
|
||||
rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet();
|
||||
|
||||
/// Frees the specified heightfield layer set using the Recast allocator.
|
||||
/// @param[in] lset A heightfield layer set allocated using #rcAllocHeightfieldLayerSet
|
||||
/// @ingroup recast
|
||||
/// @see rcAllocHeightfieldLayerSet
|
||||
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset);
|
||||
|
||||
/// Allocates a contour set object using the Recast allocator.
|
||||
/// @return A contour set that is ready for initialization, or null on failure.
|
||||
/// @ingroup recast
|
||||
/// @see rcBuildContours, rcFreeContourSet
|
||||
rcContourSet* rcAllocContourSet();
|
||||
|
||||
/// Frees the specified contour set using the Recast allocator.
|
||||
/// @param[in] cset A contour set allocated using #rcAllocContourSet
|
||||
/// @ingroup recast
|
||||
/// @see rcAllocContourSet
|
||||
void rcFreeContourSet(rcContourSet* cset);
|
||||
|
||||
/// Allocates a polygon mesh object using the Recast allocator.
|
||||
/// @return A polygon mesh that is ready for initialization, or null on failure.
|
||||
/// @ingroup recast
|
||||
/// @see rcBuildPolyMesh, rcFreePolyMesh
|
||||
rcPolyMesh* rcAllocPolyMesh();
|
||||
|
||||
/// Frees the specified polygon mesh using the Recast allocator.
|
||||
/// @param[in] pmesh A polygon mesh allocated using #rcAllocPolyMesh
|
||||
/// @ingroup recast
|
||||
/// @see rcAllocPolyMesh
|
||||
void rcFreePolyMesh(rcPolyMesh* pmesh);
|
||||
|
||||
/// Allocates a detail mesh object using the Recast allocator.
|
||||
/// @return A detail mesh that is ready for initialization, or null on failure.
|
||||
/// @ingroup recast
|
||||
/// @see rcBuildPolyMeshDetail, rcFreePolyMeshDetail
|
||||
rcPolyMeshDetail* rcAllocPolyMeshDetail();
|
||||
|
||||
/// Frees the specified detail mesh using the Recast allocator.
|
||||
/// @param[in] dmesh A detail mesh allocated using #rcAllocPolyMeshDetail
|
||||
/// @ingroup recast
|
||||
/// @see rcAllocPolyMeshDetail
|
||||
void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
|
||||
|
||||
/// @}
|
||||
|
||||
/// Heighfield border flag.
|
||||
/// If a heightfield region ID has this bit set, then the region is a border
|
||||
/// region and its spans are considered unwalkable.
|
||||
/// (Used during the region and contour build process.)
|
||||
/// @see rcCompactSpan::reg
|
||||
static const unsigned short RC_BORDER_REG = 0x8000;
|
||||
|
||||
// If contour region ID has the following bit set, the vertex will be later
|
||||
// removed in order to match the segments and vertices at tile boundaries.
|
||||
/// Border vertex flag.
|
||||
/// If a region ID has this bit set, then the associated element lies on
|
||||
/// a tile border. If a contour vertex's region ID has this bit set, the
|
||||
/// vertex will later be removed in order to match the segments and vertices
|
||||
/// at tile boundaries.
|
||||
/// (Used during the build process.)
|
||||
/// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts
|
||||
static const int RC_BORDER_VERTEX = 0x10000;
|
||||
|
||||
// Compact span neighbour helpers.
|
||||
inline int rcGetCon(const rcCompactSpan& s, int dir)
|
||||
{
|
||||
return (s.con >> (dir*4)) & 0xf;
|
||||
}
|
||||
/// Area border flag.
|
||||
/// If a region ID has this bit set, then the associated element lies on
|
||||
/// the border of an area.
|
||||
/// (Used during the region and contour build process.)
|
||||
/// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts
|
||||
static const int RC_AREA_BORDER = 0x20000;
|
||||
|
||||
inline int rcGetDirOffsetX(int dir)
|
||||
/// Contour build flags.
|
||||
/// @see rcBuildContours
|
||||
enum rcBuildContoursFlags
|
||||
{
|
||||
const int offset[4] = { -1, 0, 1, 0, };
|
||||
return offset[dir&0x03];
|
||||
}
|
||||
RC_CONTOUR_TESS_WALL_EDGES = 0x01, ///< Tessellate solid (impassable) edges during contour simplification.
|
||||
RC_CONTOUR_TESS_AREA_EDGES = 0x02, ///< Tessellate edges between areas during contour simplification.
|
||||
};
|
||||
|
||||
inline int rcGetDirOffsetY(int dir)
|
||||
{
|
||||
const int offset[4] = { 0, 1, 0, -1 };
|
||||
return offset[dir&0x03];
|
||||
}
|
||||
/// Applied to the region id field of contour vertices in order to extract the region id.
|
||||
/// The region id field of a vertex may have several flags applied to it. So the
|
||||
/// fields value can't be used directly.
|
||||
/// @see rcContour::verts, rcContour::rverts
|
||||
static const int RC_CONTOUR_REG_MASK = 0xffff;
|
||||
|
||||
// Common helper functions
|
||||
/// An value which indicates an invalid index within a mesh.
|
||||
/// @note This does not necessarily indicate an error.
|
||||
/// @see rcPolyMesh::polys
|
||||
static const unsigned short RC_MESH_NULL_IDX = 0xffff;
|
||||
|
||||
/// Represents the null area.
|
||||
/// 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.)
|
||||
static const unsigned char RC_NULL_AREA = 0;
|
||||
|
||||
/// The default area id used to indicate a walkable polygon.
|
||||
/// This is also the maximum allowed area id, and the only non-null area id
|
||||
/// recognized by some steps in the build process.
|
||||
static const unsigned char RC_WALKABLE_AREA = 63;
|
||||
|
||||
/// The value returned by #rcGetCon if the specified direction is not connected
|
||||
/// to another span. (Has no neighbor.)
|
||||
static const int RC_NOT_CONNECTED = 0x3f;
|
||||
|
||||
/// @name General helper functions
|
||||
/// @{
|
||||
|
||||
/// Swaps the values of the two parameters.
|
||||
/// @param[in,out] a Value A
|
||||
/// @param[in,out] b Value B
|
||||
template<class T> inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; }
|
||||
|
||||
/// Returns the minimum of two values.
|
||||
/// @param[in] a Value A
|
||||
/// @param[in] b Value B
|
||||
/// @return The minimum of the two values.
|
||||
template<class T> inline T rcMin(T a, T b) { return a < b ? a : b; }
|
||||
|
||||
/// Returns the maximum of two values.
|
||||
/// @param[in] a Value A
|
||||
/// @param[in] b Value B
|
||||
/// @return The maximum of the two values.
|
||||
template<class T> inline T rcMax(T a, T b) { return a > b ? a : b; }
|
||||
|
||||
/// Returns the absolute value.
|
||||
/// @param[in] a The value.
|
||||
/// @return The absolute value of the specified value.
|
||||
template<class T> inline T rcAbs(T a) { return a < 0 ? -a : a; }
|
||||
|
||||
/// Return the square of a value.
|
||||
/// @param[in] a The value.
|
||||
/// @return The square of the value.
|
||||
template<class T> inline T rcSqr(T a) { return a*a; }
|
||||
|
||||
/// Clamps the value to the specified range.
|
||||
/// @param[in] v The value to clamp.
|
||||
/// @param[in] mn The minimum permitted return value.
|
||||
/// @param[in] mx The maximum permitted return value.
|
||||
/// @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); }
|
||||
|
||||
// Common vector helper functions.
|
||||
inline void vcross(float* dest, const float* v1, const float* v2)
|
||||
/// Returns the square root of the value.
|
||||
/// @param[in] x The value.
|
||||
/// @return The square root of the vlaue.
|
||||
float rcSqrt(float x);
|
||||
|
||||
/// Not documented. Internal use only.
|
||||
/// @param[in] x Not documented.
|
||||
/// @return Not documented.
|
||||
inline int rcAlign4(int x) { return (x+3) & ~3; }
|
||||
|
||||
/// @}
|
||||
/// @name Vector helper functions.
|
||||
/// @{
|
||||
|
||||
/// Derives the cross product of two vectors. (v1 x v2)
|
||||
/// @param[out] dest The cross product. [(x, y, z)]
|
||||
/// @param[in] v1 A Vector [(x, y, z)]
|
||||
/// @param[in] v2 A vector [(x, y, z)]
|
||||
inline void rcVcross(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
|
||||
dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
|
||||
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
||||
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
||||
}
|
||||
|
||||
inline float vdot(const float* v1, const float* v2)
|
||||
/// Derives the dot product of two vectors. (v1 . v2)
|
||||
/// @param[in] v1 A Vector [(x, y, z)]
|
||||
/// @param[in] v2 A vector [(x, y, z)]
|
||||
/// @return The dot product.
|
||||
inline float rcVdot(const float* v1, const float* v2)
|
||||
{
|
||||
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
|
||||
}
|
||||
|
||||
inline void vmad(float* dest, const float* v1, const float* v2, const float s)
|
||||
/// Performs a scaled vector addition. (v1 + (v2 * s))
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector [(x, y, z)]
|
||||
/// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)]
|
||||
/// @param[in] s The amount to scale @p v2 by before adding to @p v1.
|
||||
inline void rcVmad(float* dest, const float* v1, const float* v2, const float s)
|
||||
{
|
||||
dest[0] = v1[0]+v2[0]*s;
|
||||
dest[1] = v1[1]+v2[1]*s;
|
||||
dest[2] = v1[2]+v2[2]*s;
|
||||
}
|
||||
|
||||
inline void vadd(float* dest, const float* v1, const float* v2)
|
||||
/// Performs a vector addition. (@p v1 + @p v2)
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector [(x, y, z)]
|
||||
/// @param[in] v2 The vector to add to @p v1. [(x, y, z)]
|
||||
inline void rcVadd(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[0]+v2[0];
|
||||
dest[1] = v1[1]+v2[1];
|
||||
dest[2] = v1[2]+v2[2];
|
||||
}
|
||||
|
||||
inline void vsub(float* dest, const float* v1, const float* v2)
|
||||
/// Performs a vector subtraction. (@p v1 - @p v2)
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector [(x, y, z)]
|
||||
/// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)]
|
||||
inline void rcVsub(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[0]-v2[0];
|
||||
dest[1] = v1[1]-v2[1];
|
||||
dest[2] = v1[2]-v2[2];
|
||||
}
|
||||
|
||||
inline void vmin(float* mn, const float* v)
|
||||
/// Selects the minimum value of each element from the specified vectors.
|
||||
/// @param[in, out] mn A vector. (Will be updated with the result.) [(x, y, z)]
|
||||
/// @param[in] v A vector. [(x, y, z)]
|
||||
inline void rcVmin(float* mn, const float* v)
|
||||
{
|
||||
mn[0] = rcMin(mn[0], v[0]);
|
||||
mn[1] = rcMin(mn[1], v[1]);
|
||||
mn[2] = rcMin(mn[2], v[2]);
|
||||
}
|
||||
|
||||
inline void vmax(float* mx, const float* v)
|
||||
/// Selects the maximum value of each element from the specified vectors.
|
||||
/// @param[in, out] mx A vector. (Will be updated with the result.) [(x, y, z)]
|
||||
/// @param[in] v A vector. [(x, y, z)]
|
||||
inline void rcVmax(float* mx, const float* v)
|
||||
{
|
||||
mx[0] = rcMax(mx[0], v[0]);
|
||||
mx[1] = rcMax(mx[1], v[1]);
|
||||
mx[2] = rcMax(mx[2], v[2]);
|
||||
}
|
||||
|
||||
inline void vcopy(float* dest, const float* v)
|
||||
/// Performs a vector copy.
|
||||
/// @param[out] dest The result. [(x, y, z)]
|
||||
/// @param[in] v The vector to copy [(x, y, z)]
|
||||
inline void rcVcopy(float* dest, const float* v)
|
||||
{
|
||||
dest[0] = v[0];
|
||||
dest[1] = v[1];
|
||||
dest[2] = v[2];
|
||||
}
|
||||
|
||||
inline float vdist(const float* v1, const float* v2)
|
||||
/// Returns the distance between two points.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The distance between the two points.
|
||||
inline float rcVdist(const float* v1, const float* v2)
|
||||
{
|
||||
float dx = v2[0] - v1[0];
|
||||
float dy = v2[1] - v1[1];
|
||||
float dz = v2[2] - v1[2];
|
||||
return sqrtf(dx*dx + dy*dy + dz*dz);
|
||||
return rcSqrt(dx*dx + dy*dy + dz*dz);
|
||||
}
|
||||
|
||||
inline float vdistSqr(const float* v1, const float* v2)
|
||||
/// Returns the square of the distance between two points.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The square of the distance between the two points.
|
||||
inline float rcVdistSqr(const float* v1, const float* v2)
|
||||
{
|
||||
float dx = v2[0] - v1[0];
|
||||
float dy = v2[1] - v1[1];
|
||||
@@ -318,184 +705,424 @@ inline float vdistSqr(const float* v1, const float* v2)
|
||||
return dx*dx + dy*dy + dz*dz;
|
||||
}
|
||||
|
||||
inline void vnormalize(float* v)
|
||||
/// Normalizes the vector.
|
||||
/// @param[in,out] v The vector to normalize. [(x, y, z)]
|
||||
inline void rcVnormalize(float* v)
|
||||
{
|
||||
float d = 1.0f / sqrtf(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2]));
|
||||
float d = 1.0f / rcSqrt(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2]));
|
||||
v[0] *= d;
|
||||
v[1] *= d;
|
||||
v[2] *= d;
|
||||
}
|
||||
|
||||
inline bool vequal(const float* p0, const float* p1)
|
||||
/// Not documented. Internal use only.
|
||||
/// @param[in] p0 Not documented.
|
||||
/// @param[in] p1 Not documented.
|
||||
/// @return Not documented.
|
||||
inline bool rcVequal(const float* p0, const float* p1)
|
||||
{
|
||||
static const float thr = rcSqr(1.0f/16384.0f);
|
||||
const float d = vdistSqr(p0, p1);
|
||||
const float d = rcVdistSqr(p0, p1);
|
||||
return d < thr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// @name Heightfield Functions
|
||||
/// @see rcHeightfield
|
||||
/// @{
|
||||
|
||||
// Calculated bounding box of array of vertices.
|
||||
// Params:
|
||||
// verts - (in) array of vertices
|
||||
// nv - (in) vertex count
|
||||
// bmin, bmax - (out) bounding box
|
||||
/// Calculates the bounding box of an array of vertices.
|
||||
/// @ingroup recast
|
||||
/// @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[out] bmin 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]
|
||||
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax);
|
||||
|
||||
// Calculates grid size based on bounding box and grid cell size.
|
||||
// Params:
|
||||
// bmin, bmax - (in) bounding box
|
||||
// cs - (in) grid cell size
|
||||
// w - (out) grid width
|
||||
// h - (out) grid height
|
||||
/// Calculates the grid size based on the bounding box and grid cell size.
|
||||
/// @ingroup recast
|
||||
/// @param[in] bmin 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] cs The xz-plane cell size. [Limit: > 0] [Units: wu]
|
||||
/// @param[out] w The width along the x-axis. [Limit: >= 0] [Units: vx]
|
||||
/// @param[out] h The height along the z-axis. [Limit: >= 0] [Units: vx]
|
||||
void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h);
|
||||
|
||||
// Creates and initializes new heightfield.
|
||||
// Params:
|
||||
// hf - (in/out) heightfield to initialize.
|
||||
// width - (in) width of the heightfield.
|
||||
// height - (in) height of the heightfield.
|
||||
// bmin, bmax - (in) bounding box of the heightfield
|
||||
// cs - (in) grid cell size
|
||||
// ch - (in) grid cell height
|
||||
bool rcCreateHeightfield(rcHeightfield& hf, int width, int height,
|
||||
/// Initializes a new heightfield.
|
||||
/// @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] width The width of the field along the x-axis. [Limit: >= 0] [Units: vx]
|
||||
/// @param[in] height The height of the field along the z-axis. [Limit: >= 0] [Units: vx]
|
||||
/// @param[in] bmin The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu]
|
||||
/// @param[in] bmax The maximum 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] ch The y-axis cell size to use for field. [Limit: > 0] [Units: wu]
|
||||
bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
|
||||
const float* bmin, const float* bmax,
|
||||
float cs, float ch);
|
||||
|
||||
// Sets the WALKABLE flag for every triangle whose slope is below
|
||||
// the maximun walkable slope angle.
|
||||
// Params:
|
||||
// walkableSlopeAngle - (in) maximun slope angle in degrees.
|
||||
// verts - (in) array of vertices
|
||||
// nv - (in) vertex count
|
||||
// tris - (in) array of triangle vertex indices
|
||||
// nt - (in) triangle count
|
||||
// flags - (out) array of triangle flags
|
||||
void rcMarkWalkableTriangles(const float walkableSlopeAngle,
|
||||
const float* verts, int nv,
|
||||
const int* tris, int nt,
|
||||
unsigned char* flags);
|
||||
/// Sets the area id of all triangles with a slope below the specified value
|
||||
/// to #RC_WALKABLE_AREA.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. [Limits: 0 <= value < 90]
|
||||
/// [Units: Degrees]
|
||||
/// @param[in] verts The vertices. [(x, y, z) * @p nv]
|
||||
/// @param[in] nv The number of vertices.
|
||||
/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt]
|
||||
/// @param[in] nt The number of triangles.
|
||||
/// @param[out] areas The triangle area ids. [Length: >= @p nt]
|
||||
void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
|
||||
const int* tris, int nt, unsigned char* areas);
|
||||
|
||||
// Rasterizes a triangle into heightfield spans.
|
||||
// Params:
|
||||
// v0,v1,v2 - (in) the vertices of the triangle.
|
||||
// flags - (in) triangle flags (uses WALKABLE)
|
||||
// solid - (in) heighfield where the triangle is rasterized
|
||||
void rcRasterizeTriangle(const float* v0, const float* v1, const float* v2,
|
||||
unsigned char flags, rcHeightfield& solid);
|
||||
/// Sets the area id of all triangles with a slope greater than or equal to the specified value to #RC_NULL_AREA.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. [Limits: 0 <= value < 90]
|
||||
/// [Units: Degrees]
|
||||
/// @param[in] verts The vertices. [(x, y, z) * @p nv]
|
||||
/// @param[in] nv The number of vertices.
|
||||
/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt]
|
||||
/// @param[in] nt The number of triangles.
|
||||
/// @param[out] areas The triangle area ids. [Length: >= @p nt]
|
||||
void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
|
||||
const int* tris, int nt, unsigned char* areas);
|
||||
|
||||
// Rasterizes the triangles into heightfield spans.
|
||||
// Params:
|
||||
// verts - (in) array of vertices
|
||||
// nv - (in) vertex count
|
||||
// tris - (in) array of triangle vertex indices
|
||||
// norms - (in) array of triangle normals
|
||||
// flags - (in) array of triangle flags (uses WALKABLE)
|
||||
// nt - (in) triangle count
|
||||
// solid - (in) heighfield where the triangles are rasterized
|
||||
void rcRasterizeTriangles(const float* verts, int nv,
|
||||
const int* tris, const unsigned char* flags, int nt,
|
||||
rcHeightfield& solid);
|
||||
/// Adds a span to the specified heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in,out] hf An initialized heightfield.
|
||||
/// @param[in] x The width index where the span is to be added.
|
||||
/// [Limits: 0 <= value < rcHeightfield::width]
|
||||
/// @param[in] y The height index where the span is to be added.
|
||||
/// [Limits: 0 <= value < rcHeightfield::height]
|
||||
/// @param[in] smin The minimum height of the span. [Limit: < @p smax] [Units: vx]
|
||||
/// @param[in] smax 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] flagMergeThr The merge theshold. [Limit: >= 0] [Units: vx]
|
||||
void 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);
|
||||
|
||||
// Removes WALKABLE flag from all spans that are at ledges. This filtering
|
||||
// removes possible overestimation of the conservative voxelization so that
|
||||
// the resulting mesh will not have regions hanging in air over ledges.
|
||||
// Params:
|
||||
// walkableHeight - (in) minimum height where the agent can still walk
|
||||
// walkableClimb - (in) maximum height between grid cells the agent can climb
|
||||
// solid - (in/out) heightfield describing the solid space
|
||||
void rcFilterLedgeSpans(const int walkableHeight,
|
||||
const int walkableClimb,
|
||||
rcHeightfield& solid);
|
||||
/// Rasterizes a triangle into the specified heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] v0 Triangle vertex 0 [(x, y, z)]
|
||||
/// @param[in] v1 Triangle vertex 1 [(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, out] solid An initialized heightfield.
|
||||
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag.
|
||||
/// [Limit: >= 0] [Units: vx]
|
||||
void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char area, rcHeightfield& solid,
|
||||
const int flagMergeThr = 1);
|
||||
|
||||
// Removes WALKABLE flag from all spans which have smaller than
|
||||
// 'walkableHeight' clearane above them.
|
||||
// Params:
|
||||
// walkableHeight - (in) minimum height where the agent can still walk
|
||||
// solid - (in/out) heightfield describing the solid space
|
||||
void rcFilterWalkableLowHeightSpans(int walkableHeight,
|
||||
rcHeightfield& solid);
|
||||
/// Rasterizes an indexed triangle mesh into the specified heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] verts The vertices. [(x, y, z) * @p nv]
|
||||
/// @param[in] nv The number of vertices.
|
||||
/// @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] nt The number of triangles.
|
||||
/// @param[in, out] solid An initialized heightfield.
|
||||
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag.
|
||||
/// [Limit: >= 0] [Units: vx]
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
|
||||
const int* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr = 1);
|
||||
|
||||
// Marks spans which are reachable from any of the topmost spans.
|
||||
// Params:
|
||||
// walkableHeight - (in) minimum height where the agent can still walk
|
||||
// walkableClimb - (in) maximum height between grid cells the agent can climb
|
||||
// solid - (in/out) heightfield describing the solid space
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcMarkReachableSpans(const int walkableHeight,
|
||||
const int walkableClimb,
|
||||
rcHeightfield& solid);
|
||||
/// Rasterizes an indexed triangle mesh into the specified heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] verts The vertices. [(x, y, z) * @p nv]
|
||||
/// @param[in] nv The number of vertices.
|
||||
/// @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] nt The number of triangles.
|
||||
/// @param[in, out] solid An initialized heightfield.
|
||||
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag.
|
||||
/// [Limit: >= 0] [Units: vx]
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
|
||||
const unsigned short* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr = 1);
|
||||
|
||||
// Builds compact representation of the heightfield.
|
||||
// Params:
|
||||
// walkableHeight - (in) minimum height where the agent can still walk
|
||||
// walkableClimb - (in) maximum height between grid cells the agent can climb
|
||||
// hf - (in) heightfield to be compacted
|
||||
// chf - (out) compact heightfield representing the open space.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb,
|
||||
unsigned char flags,
|
||||
rcHeightfield& hf,
|
||||
rcCompactHeightfield& chf);
|
||||
/// Rasterizes triangles into the specified heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx 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] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt]
|
||||
/// @param[in] nt The number of triangles.
|
||||
/// @param[in, out] solid An initialized heightfield.
|
||||
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag.
|
||||
/// [Limit: >= 0] [Units: vx]
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr = 1);
|
||||
|
||||
// Builds distance field and stores it into the combat heightfield.
|
||||
// Params:
|
||||
// chf - (in/out) compact heightfield representing the open space.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildDistanceField(rcCompactHeightfield& chf);
|
||||
/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neihbor.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable.
|
||||
/// [Limit: >=0] [Units: vx]
|
||||
/// @param[in,out] solid A fully built heightfield. (All spans have been added.)
|
||||
void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid);
|
||||
|
||||
// Divides the walkable heighfied into simple regions.
|
||||
// Each region has only one contour and no overlaps.
|
||||
// The regions are stored in the compact heightfield 'reg' field.
|
||||
// The regions will be shrinked by the radius of the agent.
|
||||
// The process sometimes creates small regions. The parameter
|
||||
// 'minRegionSize' specifies the smallest allowed regions size.
|
||||
// If the area of a regions is smaller than allowed, the regions is
|
||||
// removed or merged to neighbour region.
|
||||
// Params:
|
||||
// chf - (in/out) compact heightfield representing the open space.
|
||||
// walkableRadius - (in) the radius of the agent.
|
||||
// minRegionSize - (in) the smallest allowed regions size.
|
||||
// maxMergeRegionSize - (in) the largest allowed regions size which can be merged.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildRegions(rcCompactHeightfield& chf,
|
||||
int walkableRadius, int borderSize,
|
||||
int minRegionSize, int mergeRegionSize);
|
||||
/// Marks spans that are ledges as not-walkable.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to
|
||||
/// be considered walkable. [Limit: >= 3] [Units: vx]
|
||||
/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable.
|
||||
/// [Limit: >=0] [Units: vx]
|
||||
/// @param[in,out] solid A fully built heightfield. (All spans have been added.)
|
||||
void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight,
|
||||
const int walkableClimb, rcHeightfield& solid);
|
||||
|
||||
// Builds simplified contours from the regions outlines.
|
||||
// Params:
|
||||
// chf - (in) compact heightfield which has regions set.
|
||||
// maxError - (in) maximum allowed distance between simplified countour and cells.
|
||||
// maxEdgeLen - (in) maximum allowed contour edge length in cells.
|
||||
// cset - (out) Resulting contour set.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildContours(rcCompactHeightfield& chf,
|
||||
/// Marks walkable spans as not walkable if the clearence above the span is less than the specified height.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to
|
||||
/// be considered walkable. [Limit: >= 3] [Units: vx]
|
||||
/// @param[in,out] solid A fully built heightfield. (All spans have been added.)
|
||||
void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid);
|
||||
|
||||
/// Returns the number of spans contained in the specified heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] hf An initialized heightfield.
|
||||
/// @returns The number of spans in the heightfield.
|
||||
int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf);
|
||||
|
||||
/// @}
|
||||
/// @name Compact Heightfield Functions
|
||||
/// @see rcCompactHeightfield
|
||||
/// @{
|
||||
|
||||
/// Builds a compact heightfield representing open space, from a heightfield representing solid space.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area
|
||||
/// to be considered walkable. [Limit: >= 3] [Units: vx]
|
||||
/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable.
|
||||
/// [Limit: >=0] [Units: vx]
|
||||
/// @param[in] hf The heightfield to be compacted.
|
||||
/// @param[out] chf The resulting compact heightfield. (Must be pre-allocated.)
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
||||
rcHeightfield& hf, rcCompactHeightfield& chf);
|
||||
|
||||
/// Erodes the walkable area within the heightfield by the specified radius.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] radius The radius of erosion. [Limits: 0 < value < 255] [Units: vx]
|
||||
/// @param[in,out] chf The populated compact heightfield to erode.
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf);
|
||||
|
||||
/// Applies a median filter to walkable area types (based on area id), removing noise.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in,out] chf A populated compact heightfield.
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf);
|
||||
|
||||
/// Applies an area id to all spans within the specified bounding box. (AABB)
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] bmin The minimum of the bounding box. [(x, y, z)]
|
||||
/// @param[in] bmax The maximum of the bounding box. [(x, y, z)]
|
||||
/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA]
|
||||
/// @param[in,out] chf A populated compact heightfield.
|
||||
void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf);
|
||||
|
||||
/// Applies the area id to the all spans within the specified convex polygon.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] verts The vertices of the polygon [Fomr: (x, y, z) * @p nverts]
|
||||
/// @param[in] nverts The number of vertices in the polygon.
|
||||
/// @param[in] hmin The height of the base of the polygon.
|
||||
/// @param[in] hmax The height of the top of the polygon.
|
||||
/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA]
|
||||
/// @param[in,out] chf A populated compact heightfield.
|
||||
void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
|
||||
const float hmin, const float hmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf);
|
||||
|
||||
/// Applies the area id to all spans within the specified cylinder.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] pos The center of the base of the cylinder. [Form: (x, y, z)]
|
||||
/// @param[in] r The radius of the cylinder.
|
||||
/// @param[in] h The height of the cylinder.
|
||||
/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA]
|
||||
/// @param[in,out] chf A populated compact heightfield.
|
||||
void rcMarkCylinderArea(rcContext* ctx, const float* pos,
|
||||
const float r, const float h, unsigned char areaId,
|
||||
rcCompactHeightfield& chf);
|
||||
|
||||
/// Builds the distance field for the specified compact heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in,out] chf A populated compact heightfield.
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
|
||||
|
||||
/// Builds region data for the heightfield using watershed partitioning.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in,out] chf A populated compact heightfield.
|
||||
/// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0] [Units: vx]
|
||||
/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. [Limit: >=0]
|
||||
/// [Units: vx].
|
||||
/// @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]
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int minRegionArea, const int mergeRegionArea);
|
||||
|
||||
/// Builds region data for the heightfield using simple monotone partitioning.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in,out] chf A populated compact heightfield.
|
||||
/// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0] [Units: vx]
|
||||
/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. [Limit: >=0]
|
||||
/// [Units: vx].
|
||||
/// @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]
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int minRegionArea, const int mergeRegionArea);
|
||||
|
||||
|
||||
/// Sets the neighbor connection data for the specified direction.
|
||||
/// @param[in] s The span to update.
|
||||
/// @param[in] dir The direction to set. [Limits: 0 <= value < 4]
|
||||
/// @param[in] i The index of the neighbor span.
|
||||
inline void rcSetCon(rcCompactSpan& s, int dir, int i)
|
||||
{
|
||||
const unsigned int shift = (unsigned int)dir*6;
|
||||
unsigned int con = s.con;
|
||||
s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift);
|
||||
}
|
||||
|
||||
/// Gets neighbor connection data for the specified direction.
|
||||
/// @param[in] s The span to check.
|
||||
/// @param[in] dir The direction to check. [Limits: 0 <= value < 4]
|
||||
/// @return The neighbor connection data for the specified direction,
|
||||
/// or #RC_NOT_CONNECTED if there is no connection.
|
||||
inline int rcGetCon(const rcCompactSpan& s, int dir)
|
||||
{
|
||||
const unsigned int shift = (unsigned int)dir*6;
|
||||
return (s.con >> shift) & 0x3f;
|
||||
}
|
||||
|
||||
/// Gets the standard width (x-axis) offset for the specified direction.
|
||||
/// @param[in] dir The direction. [Limits: 0 <= value < 4]
|
||||
/// @return The width offset to apply to the current cell position to move
|
||||
/// in the direction.
|
||||
inline int rcGetDirOffsetX(int dir)
|
||||
{
|
||||
const int offset[4] = { -1, 0, 1, 0, };
|
||||
return offset[dir&0x03];
|
||||
}
|
||||
|
||||
/// Gets the standard height (z-axis) offset for the specified direction.
|
||||
/// @param[in] dir The direction. [Limits: 0 <= value < 4]
|
||||
/// @return The height offset to apply to the current cell position to move
|
||||
/// in the direction.
|
||||
inline int rcGetDirOffsetY(int dir)
|
||||
{
|
||||
const int offset[4] = { 0, 1, 0, -1 };
|
||||
return offset[dir&0x03];
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// @name Layer, Contour, Polymesh, and Detail Mesh Functions
|
||||
/// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail
|
||||
/// @{
|
||||
|
||||
/// Builds a layer set from the specified compact heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] chf A fully built compact heightfield.
|
||||
/// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0]
|
||||
/// [Units: vx]
|
||||
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area
|
||||
/// to be considered walkable. [Limit: >= 3] [Units: vx]
|
||||
/// @param[out] lset The resulting layer set. (Must be pre-allocated.)
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int walkableHeight,
|
||||
rcHeightfieldLayerSet& lset);
|
||||
|
||||
/// Builds a contour set from the region outlines in the provided compact heightfield.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] chf A fully built compact heightfield.
|
||||
/// @param[in] maxError The maximum distance a simplfied contour's border edges should deviate
|
||||
/// the original raw contour. [Limit: >=0] [Units: wu]
|
||||
/// @param[in] maxEdgeLen The maximum allowed length for contour edges along the border of the mesh.
|
||||
/// [Limit: >=0] [Units: vx]
|
||||
/// @param[out] cset The resulting contour set. (Must be pre-allocated.)
|
||||
/// @param[in] buildFlags The build flags. (See: #rcBuildContoursFlags)
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const float maxError, const int maxEdgeLen,
|
||||
rcContourSet& cset);
|
||||
rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES);
|
||||
|
||||
// Builds connected convex polygon mesh from contour polygons.
|
||||
// Params:
|
||||
// cset - (in) contour set.
|
||||
// nvp - (in) maximum number of vertices per polygon.
|
||||
// mesh - (out) poly mesh.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh);
|
||||
/// Builds a polygon mesh from the provided contours.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] cset A fully built contour set.
|
||||
/// @param[in] nvp The maximum number of vertices allowed for polygons generated during the
|
||||
/// contour to polygon conversion process. [Limit: >= 3]
|
||||
/// @param[out] mesh The resulting polygon mesh. (Must be re-allocated.)
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh);
|
||||
|
||||
bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh);
|
||||
/// Merges multiple polygon meshes into a single mesh.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] meshes An array of polygon meshes to merge. [Size: @p nmeshes]
|
||||
/// @param[in] nmeshes The number of polygon meshes in the meshes array.
|
||||
/// @param[in] mesh The resulting polygon mesh. (Must be pre-allocated.)
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh);
|
||||
|
||||
// Builds detail triangle mesh for each polygon in the poly mesh.
|
||||
// Params:
|
||||
// mesh - (in) poly mesh to detail.
|
||||
// chf - (in) compacy height field, used to query height for new vertices.
|
||||
// sampleDist - (in) spacing between height samples used to generate more detail into mesh.
|
||||
// sampleMaxError - (in) maximum allowed distance between simplified detail mesh and height sample.
|
||||
// pmdtl - (out) detail mesh.
|
||||
// Returns false if operation ran out of memory.
|
||||
bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
|
||||
/// Builds a detail mesh from the provided polygon mesh.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] mesh A fully built 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] sampleMaxError The maximum distance the detail mesh surface should deviate from
|
||||
/// heightfield data. [Limit: >=0] [Units: wu]
|
||||
/// @param[out] dmesh The resulting detail mesh. (Must be pre-allocated.)
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
|
||||
const float sampleDist, const float sampleMaxError,
|
||||
rcPolyMeshDetail& dmesh);
|
||||
|
||||
bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh);
|
||||
/// Merges multiple detail meshes into a single detail mesh.
|
||||
/// @ingroup recast
|
||||
/// @param[in,out] ctx The build context to use during the operation.
|
||||
/// @param[in] meshes An array of detail meshes to merge. [Size: @p nmeshes]
|
||||
/// @param[in] nmeshes The number of detail meshes in the meshes array.
|
||||
/// @param[out] mesh The resulting detail mesh. (Must be pre-allocated.)
|
||||
/// @returns True if the operation completed successfully.
|
||||
bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh);
|
||||
|
||||
bool buildMeshAdjacency(unsigned short* polys, const int npolys, const int nverts, const int vertsPerPoly);
|
||||
|
||||
/// @}
|
||||
|
||||
#endif // RECAST_H
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Due to the large amount of detail documentation for this file,
|
||||
// the content normally located at the end of the header file has been separated
|
||||
// out to a file in /Docs/Extern.
|
||||
|
||||
122
extern/recastnavigation/Recast/Include/RecastAlloc.h
vendored
Normal file
122
extern/recastnavigation/Recast/Include/RecastAlloc.h
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECASTALLOC_H
|
||||
#define RECASTALLOC_H
|
||||
|
||||
/// Provides hint values to the memory allocator on how long the
|
||||
/// memory is expected to be used.
|
||||
enum rcAllocHint
|
||||
{
|
||||
RC_ALLOC_PERM, ///< Memory will persist after a function call.
|
||||
RC_ALLOC_TEMP ///< Memory used temporarily within a function.
|
||||
};
|
||||
|
||||
/// A memory allocation function.
|
||||
// @param[in] size The size, in bytes of memory, to allocate.
|
||||
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
|
||||
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see rcAllocSetCustom
|
||||
typedef void* (rcAllocFunc)(int size, rcAllocHint hint);
|
||||
|
||||
/// A memory deallocation function.
|
||||
/// @see rcAllocSetCustom
|
||||
// @param[in] ptr
|
||||
typedef void (rcFreeFunc)(void* ptr);
|
||||
|
||||
/// Sets the base custom allocation functions to be used by Recast.
|
||||
/// @param[in] allocFunc The memory allocation function to be used by #rcAlloc
|
||||
/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree
|
||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
|
||||
|
||||
/// Allocates a memory block.
|
||||
/// @param[in] size The size, in bytes of memory, to allocate.
|
||||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
void* rcAlloc(int size, rcAllocHint hint);
|
||||
|
||||
/// Deallocates a memory block.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
|
||||
void rcFree(void* ptr);
|
||||
|
||||
|
||||
/// A simple dynamic array of integers.
|
||||
class rcIntArray
|
||||
{
|
||||
int* m_data;
|
||||
int m_size, m_cap;
|
||||
inline rcIntArray(const rcIntArray&);
|
||||
inline rcIntArray& operator=(const rcIntArray&);
|
||||
public:
|
||||
|
||||
/// Constructs an instance with an initial array size of zero.
|
||||
inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
|
||||
|
||||
/// Constructs an instance initialized to the specified size.
|
||||
/// @param[in] n The initial size of the integer array.
|
||||
inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
|
||||
inline ~rcIntArray() { rcFree(m_data); }
|
||||
|
||||
/// Specifies the new size of the integer array.
|
||||
/// @param[in] n The new size of the integer array.
|
||||
void resize(int n);
|
||||
|
||||
/// Push the specified integer onto the end of the array and increases the size by one.
|
||||
/// @param[in] item The new value.
|
||||
inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
|
||||
|
||||
/// Returns the value at the end of the array and reduces the size by one.
|
||||
/// @return The value at the end of the array.
|
||||
inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
|
||||
|
||||
/// The value at the specified array index.
|
||||
/// @warning Does not provide overflow protection.
|
||||
/// @param[in] i The index of the value.
|
||||
inline const int& operator[](int i) const { return m_data[i]; }
|
||||
|
||||
/// The value at the specified array index.
|
||||
/// @warning Does not provide overflow protection.
|
||||
/// @param[in] i The index of the value.
|
||||
inline int& operator[](int i) { return m_data[i]; }
|
||||
|
||||
/// The current size of the integer array.
|
||||
inline int size() const { return m_size; }
|
||||
};
|
||||
|
||||
/// A simple helper class used to delete an array when it goes out of scope.
|
||||
/// @note This class is rarely if ever used by the end user.
|
||||
template<class T> class rcScopedDelete
|
||||
{
|
||||
T* ptr;
|
||||
inline T* operator=(T* p);
|
||||
public:
|
||||
|
||||
/// Constructs an instance with a null pointer.
|
||||
inline rcScopedDelete() : ptr(0) {}
|
||||
|
||||
/// Constructs an instance with the specified pointer.
|
||||
/// @param[in] p An pointer to an allocated array.
|
||||
inline rcScopedDelete(T* p) : ptr(p) {}
|
||||
inline ~rcScopedDelete() { rcFree(ptr); }
|
||||
|
||||
/// The root array pointer.
|
||||
/// @return The root array pointer.
|
||||
inline operator T*() { return ptr; }
|
||||
};
|
||||
|
||||
#endif
|
||||
33
extern/recastnavigation/Recast/Include/RecastAssert.h
vendored
Normal file
33
extern/recastnavigation/Recast/Include/RecastAssert.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef 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
|
||||
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
||||
# define rcAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false)
|
||||
#else
|
||||
# include <assert.h>
|
||||
# define rcAssert assert
|
||||
#endif
|
||||
|
||||
#endif // RECASTASSERT_H
|
||||
337
extern/recastnavigation/Recast/Source/Recast.cpp
vendored
337
extern/recastnavigation/Recast/Source/Recast.cpp
vendored
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -22,35 +22,178 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastLog.h"
|
||||
#include "RecastTimer.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
void rcIntArray::resize(int n)
|
||||
float rcSqrt(float x)
|
||||
{
|
||||
if (n > m_cap)
|
||||
return sqrtf(x);
|
||||
}
|
||||
|
||||
/// @class rcContext
|
||||
/// @par
|
||||
///
|
||||
/// This class does not provide logging or timer functionality on its
|
||||
/// own. Both must be provided by a concrete implementation
|
||||
/// by overriding the protected member functions. Also, this class does not
|
||||
/// provide an interface for extracting log messages. (Only adding them.)
|
||||
/// So concrete implementations must provide one.
|
||||
///
|
||||
/// If no logging or timers are required, just pass an instance of this
|
||||
/// class through the Recast build process.
|
||||
///
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Example:
|
||||
/// @code
|
||||
/// // Where ctx is an instance of rcContext and filepath is a char array.
|
||||
/// ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath);
|
||||
/// @endcode
|
||||
void rcContext::log(const rcLogCategory category, const char* format, ...)
|
||||
{
|
||||
if (!m_logEnabled)
|
||||
return;
|
||||
static const int MSG_SIZE = 512;
|
||||
char msg[MSG_SIZE];
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
int len = vsnprintf(msg, MSG_SIZE, format, ap);
|
||||
if (len >= MSG_SIZE)
|
||||
{
|
||||
if (!m_cap) m_cap = 8;
|
||||
while (m_cap < n) m_cap *= 2;
|
||||
int* newData = new int[m_cap];
|
||||
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
|
||||
delete [] m_data;
|
||||
m_data = newData;
|
||||
len = MSG_SIZE-1;
|
||||
msg[MSG_SIZE-1] = '\0';
|
||||
}
|
||||
m_size = n;
|
||||
va_end(ap);
|
||||
doLog(category, msg, len);
|
||||
}
|
||||
|
||||
rcHeightfield* rcAllocHeightfield()
|
||||
{
|
||||
rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM);
|
||||
memset(hf, 0, sizeof(rcHeightfield));
|
||||
return hf;
|
||||
}
|
||||
|
||||
void rcFreeHeightField(rcHeightfield* hf)
|
||||
{
|
||||
if (!hf) return;
|
||||
// Delete span array.
|
||||
rcFree(hf->spans);
|
||||
// Delete span pools.
|
||||
while (hf->pools)
|
||||
{
|
||||
rcSpanPool* next = hf->pools->next;
|
||||
rcFree(hf->pools);
|
||||
hf->pools = next;
|
||||
}
|
||||
rcFree(hf);
|
||||
}
|
||||
|
||||
rcCompactHeightfield* rcAllocCompactHeightfield()
|
||||
{
|
||||
rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM);
|
||||
memset(chf, 0, sizeof(rcCompactHeightfield));
|
||||
return chf;
|
||||
}
|
||||
|
||||
void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
|
||||
{
|
||||
if (!chf) return;
|
||||
rcFree(chf->cells);
|
||||
rcFree(chf->spans);
|
||||
rcFree(chf->dist);
|
||||
rcFree(chf->areas);
|
||||
rcFree(chf);
|
||||
}
|
||||
|
||||
|
||||
rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
|
||||
{
|
||||
rcHeightfieldLayerSet* lset = (rcHeightfieldLayerSet*)rcAlloc(sizeof(rcHeightfieldLayerSet), RC_ALLOC_PERM);
|
||||
memset(lset, 0, sizeof(rcHeightfieldLayerSet));
|
||||
return lset;
|
||||
}
|
||||
|
||||
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset)
|
||||
{
|
||||
if (!lset) return;
|
||||
for (int i = 0; i < lset->nlayers; ++i)
|
||||
{
|
||||
rcFree(lset->layers[i].heights);
|
||||
rcFree(lset->layers[i].areas);
|
||||
rcFree(lset->layers[i].cons);
|
||||
}
|
||||
rcFree(lset->layers);
|
||||
rcFree(lset);
|
||||
}
|
||||
|
||||
|
||||
rcContourSet* rcAllocContourSet()
|
||||
{
|
||||
rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM);
|
||||
memset(cset, 0, sizeof(rcContourSet));
|
||||
return cset;
|
||||
}
|
||||
|
||||
void rcFreeContourSet(rcContourSet* cset)
|
||||
{
|
||||
if (!cset) return;
|
||||
for (int i = 0; i < cset->nconts; ++i)
|
||||
{
|
||||
rcFree(cset->conts[i].verts);
|
||||
rcFree(cset->conts[i].rverts);
|
||||
}
|
||||
rcFree(cset->conts);
|
||||
rcFree(cset);
|
||||
}
|
||||
|
||||
rcPolyMesh* rcAllocPolyMesh()
|
||||
{
|
||||
rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM);
|
||||
memset(pmesh, 0, sizeof(rcPolyMesh));
|
||||
return pmesh;
|
||||
}
|
||||
|
||||
void rcFreePolyMesh(rcPolyMesh* pmesh)
|
||||
{
|
||||
if (!pmesh) return;
|
||||
rcFree(pmesh->verts);
|
||||
rcFree(pmesh->polys);
|
||||
rcFree(pmesh->regs);
|
||||
rcFree(pmesh->flags);
|
||||
rcFree(pmesh->areas);
|
||||
rcFree(pmesh);
|
||||
}
|
||||
|
||||
rcPolyMeshDetail* rcAllocPolyMeshDetail()
|
||||
{
|
||||
rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM);
|
||||
memset(dmesh, 0, sizeof(rcPolyMeshDetail));
|
||||
return dmesh;
|
||||
}
|
||||
|
||||
void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
|
||||
{
|
||||
if (!dmesh) return;
|
||||
rcFree(dmesh->meshes);
|
||||
rcFree(dmesh->verts);
|
||||
rcFree(dmesh->tris);
|
||||
rcFree(dmesh);
|
||||
}
|
||||
|
||||
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
|
||||
{
|
||||
// Calculate bounding box.
|
||||
vcopy(bmin, verts);
|
||||
vcopy(bmax, verts);
|
||||
rcVcopy(bmin, verts);
|
||||
rcVcopy(bmax, verts);
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
const float* v = &verts[i*3];
|
||||
vmin(bmin, v);
|
||||
vmax(bmax, v);
|
||||
rcVmin(bmin, v);
|
||||
rcVmax(bmax, v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,17 +203,25 @@ void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int*
|
||||
*h = (int)((bmax[2] - bmin[2])/cs+0.5f);
|
||||
}
|
||||
|
||||
bool rcCreateHeightfield(rcHeightfield& hf, int width, int height,
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocHeightfield, rcHeightfield
|
||||
bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height,
|
||||
const float* bmin, const float* bmax,
|
||||
float cs, float ch)
|
||||
{
|
||||
// TODO: VC complains about unref formal variable, figure out a way to handle this better.
|
||||
// rcAssert(ctx);
|
||||
|
||||
hf.width = width;
|
||||
hf.height = height;
|
||||
hf.spans = new rcSpan*[hf.width*hf.height];
|
||||
vcopy(hf.bmin, bmin);
|
||||
vcopy(hf.bmax, bmax);
|
||||
rcVcopy(hf.bmin, bmin);
|
||||
rcVcopy(hf.bmax, bmax);
|
||||
hf.cs = cs;
|
||||
hf.ch = ch;
|
||||
hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM);
|
||||
if (!hf.spans)
|
||||
return false;
|
||||
memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
|
||||
@@ -80,18 +231,29 @@ bool rcCreateHeightfield(rcHeightfield& hf, int width, int height,
|
||||
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
|
||||
{
|
||||
float e0[3], e1[3];
|
||||
vsub(e0, v1, v0);
|
||||
vsub(e1, v2, v0);
|
||||
vcross(norm, e0, e1);
|
||||
vnormalize(norm);
|
||||
rcVsub(e0, v1, v0);
|
||||
rcVsub(e1, v2, v0);
|
||||
rcVcross(norm, e0, e1);
|
||||
rcVnormalize(norm);
|
||||
}
|
||||
|
||||
void rcMarkWalkableTriangles(const float walkableSlopeAngle,
|
||||
const float* verts, int nv,
|
||||
/// @par
|
||||
///
|
||||
/// Only sets the aread id's for the walkable triangles. Does not alter the
|
||||
/// area id's for unwalkable triangles.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
|
||||
void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
|
||||
const float* verts, int /*nv*/,
|
||||
const int* tris, int nt,
|
||||
unsigned char* flags)
|
||||
unsigned char* areas)
|
||||
{
|
||||
const float walkableThr = cosf(walkableSlopeAngle/180.0f*(float)M_PI);
|
||||
// TODO: VC complains about unref formal variable, figure out a way to handle this better.
|
||||
// rcAssert(ctx);
|
||||
|
||||
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
|
||||
|
||||
float norm[3];
|
||||
|
||||
@@ -101,12 +263,45 @@ void rcMarkWalkableTriangles(const float walkableSlopeAngle,
|
||||
calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
|
||||
// Check if the face is walkable.
|
||||
if (norm[1] > walkableThr)
|
||||
flags[i] |= RC_WALKABLE;
|
||||
areas[i] = RC_WALKABLE_AREA;
|
||||
}
|
||||
}
|
||||
|
||||
static int getSpanCount(unsigned char flags, rcHeightfield& hf)
|
||||
/// @par
|
||||
///
|
||||
/// Only sets the aread id's for the unwalkable triangles. Does not alter the
|
||||
/// area id's for walkable triangles.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
|
||||
void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
|
||||
const float* verts, int /*nv*/,
|
||||
const int* tris, int nt,
|
||||
unsigned char* areas)
|
||||
{
|
||||
// TODO: VC complains about unref formal variable, figure out a way to handle this better.
|
||||
// rcAssert(ctx);
|
||||
|
||||
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
|
||||
|
||||
float norm[3];
|
||||
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
const int* tri = &tris[i*3];
|
||||
calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
|
||||
// Check if the face is walkable.
|
||||
if (norm[1] <= walkableThr)
|
||||
areas[i] = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
|
||||
int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf)
|
||||
{
|
||||
// TODO: VC complains about unref formal variable, figure out a way to handle this better.
|
||||
// rcAssert(ctx);
|
||||
|
||||
const int w = hf.width;
|
||||
const int h = hf.height;
|
||||
int spanCount = 0;
|
||||
@@ -116,7 +311,7 @@ static int getSpanCount(unsigned char flags, rcHeightfield& hf)
|
||||
{
|
||||
for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next)
|
||||
{
|
||||
if (s->flags == flags)
|
||||
if (s->area != RC_NULL_AREA)
|
||||
spanCount++;
|
||||
}
|
||||
}
|
||||
@@ -124,21 +319,25 @@ static int getSpanCount(unsigned char flags, rcHeightfield& hf)
|
||||
return spanCount;
|
||||
}
|
||||
|
||||
inline void setCon(rcCompactSpan& s, int dir, int i)
|
||||
/// @par
|
||||
///
|
||||
/// This is just the beginning of the process of fully building a compact heightfield.
|
||||
/// Various filters may be applied 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)
|
||||
{
|
||||
s.con &= ~(0xf << (dir*4));
|
||||
s.con |= (i&0xf) << (dir*4);
|
||||
}
|
||||
|
||||
bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb,
|
||||
unsigned char flags, rcHeightfield& hf,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||
|
||||
const int w = hf.width;
|
||||
const int h = hf.height;
|
||||
const int spanCount = getSpanCount(flags, hf);
|
||||
const int spanCount = rcGetHeightFieldSpanCount(ctx, hf);
|
||||
|
||||
// Fill in header.
|
||||
chf.width = w;
|
||||
@@ -147,27 +346,32 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb
|
||||
chf.walkableHeight = walkableHeight;
|
||||
chf.walkableClimb = walkableClimb;
|
||||
chf.maxRegions = 0;
|
||||
vcopy(chf.bmin, hf.bmin);
|
||||
vcopy(chf.bmax, hf.bmax);
|
||||
rcVcopy(chf.bmin, hf.bmin);
|
||||
rcVcopy(chf.bmax, hf.bmax);
|
||||
chf.bmax[1] += walkableHeight*hf.ch;
|
||||
chf.cs = hf.cs;
|
||||
chf.ch = hf.ch;
|
||||
chf.cells = new rcCompactCell[w*h];
|
||||
chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM);
|
||||
if (!chf.cells)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
|
||||
return false;
|
||||
}
|
||||
memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
|
||||
chf.spans = new rcCompactSpan[spanCount];
|
||||
chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM);
|
||||
if (!chf.spans)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount);
|
||||
chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM);
|
||||
if (!chf.areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount);
|
||||
|
||||
const int MAX_HEIGHT = 0xffff;
|
||||
|
||||
@@ -185,12 +389,13 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb
|
||||
c.count = 0;
|
||||
while (s)
|
||||
{
|
||||
if (s->flags == flags)
|
||||
if (s->area != RC_NULL_AREA)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
@@ -200,6 +405,8 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb
|
||||
}
|
||||
|
||||
// Find neighbour connections.
|
||||
const int MAX_LAYERS = RC_NOT_CONNECTED-1;
|
||||
int tooHighNeighbour = 0;
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
@@ -208,14 +415,16 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
setCon(s, dir, 0xf);
|
||||
rcSetCon(s, dir, RC_NOT_CONNECTED);
|
||||
const int nx = x + rcGetDirOffsetX(dir);
|
||||
const int ny = y + rcGetDirOffsetY(dir);
|
||||
// First check that the neighbour cell is in bounds.
|
||||
if (nx < 0 || ny < 0 || nx >= w || ny >= h)
|
||||
continue;
|
||||
|
||||
// Iterate over all neighbour spans and check if any of the is
|
||||
// accessible from current cell.
|
||||
const rcCompactCell& nc = chf.cells[nx+ny*w];
|
||||
@@ -230,23 +439,34 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb
|
||||
if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
|
||||
{
|
||||
// Mark direction as walkable.
|
||||
setCon(s, dir, k - (int)nc.index);
|
||||
const int idx = k - (int)nc.index;
|
||||
if (idx < 0 || idx > MAX_LAYERS)
|
||||
{
|
||||
tooHighNeighbour = rcMax(tooHighNeighbour, idx);
|
||||
continue;
|
||||
}
|
||||
rcSetCon(s, dir, idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->buildCompact += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
if (tooHighNeighbour > MAX_LAYERS)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
|
||||
tooHighNeighbour, MAX_LAYERS);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
static int getHeightfieldMemoryUsage(const rcHeightfield& hf)
|
||||
{
|
||||
int size = 0;
|
||||
@@ -270,3 +490,4 @@ static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
|
||||
size += sizeof(rcCompactCell) * chf.width * chf.height;
|
||||
return size;
|
||||
}
|
||||
*/
|
||||
88
extern/recastnavigation/Recast/Source/RecastAlloc.cpp
vendored
Normal file
88
extern/recastnavigation/Recast/Source/RecastAlloc.cpp
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "RecastAlloc.h"
|
||||
|
||||
static void *rcAllocDefault(int size, rcAllocHint)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void rcFreeDefault(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
|
||||
static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
|
||||
|
||||
/// @see rcAlloc, rcFree
|
||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
|
||||
{
|
||||
sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
|
||||
sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
|
||||
}
|
||||
|
||||
/// @see rcAllocSetCustom
|
||||
void* rcAlloc(int size, rcAllocHint hint)
|
||||
{
|
||||
return sRecastAllocFunc(size, hint);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// @warning This function leaves the value of @p ptr unchanged. So it still
|
||||
/// points to the same (now invalid) location, and not to null.
|
||||
///
|
||||
/// @see rcAllocSetCustom
|
||||
void rcFree(void* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
sRecastFreeFunc(ptr);
|
||||
}
|
||||
|
||||
/// @class rcIntArray
|
||||
///
|
||||
/// While it is possible to pre-allocate a specific array size during
|
||||
/// construction or by using the #resize method, certain methods will
|
||||
/// automatically resize the array as needed.
|
||||
///
|
||||
/// @warning The array memory is not initialized to zero when the size is
|
||||
/// manually set during construction or when using #resize.
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Using this method ensures the array is at least large enough to hold
|
||||
/// the specified number of elements. This can improve performance by
|
||||
/// avoiding auto-resizing during use.
|
||||
void rcIntArray::resize(int n)
|
||||
{
|
||||
if (n > m_cap)
|
||||
{
|
||||
if (!m_cap) m_cap = n;
|
||||
while (m_cap < n) m_cap *= 2;
|
||||
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
|
||||
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
|
||||
rcFree(m_data);
|
||||
m_data = newData;
|
||||
}
|
||||
m_size = n;
|
||||
}
|
||||
|
||||
524
extern/recastnavigation/Recast/Source/RecastArea.cpp
vendored
Normal file
524
extern/recastnavigation/Recast/Source/RecastArea.cpp
vendored
Normal file
@@ -0,0 +1,524 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <float.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Basically, any spans that are closer to a boundary or obstruction than the specified radius
|
||||
/// are marked as unwalkable.
|
||||
///
|
||||
/// This method is usually called immediately after the heightfield has been built.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
|
||||
bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
ctx->startTimer(RC_TIMER_ERODE_AREA);
|
||||
|
||||
unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!dist)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init distance.
|
||||
memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
// Mark boundary cells.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
{
|
||||
dist[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
int nc = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int nx = x + rcGetDirOffsetX(dir);
|
||||
const int ny = y + rcGetDirOffsetY(dir);
|
||||
const int ni = (int)chf.cells[nx+ny*w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[ni] != RC_NULL_AREA)
|
||||
{
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// At least one missing neighbour.
|
||||
if (nc != 4)
|
||||
dist[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char nd;
|
||||
|
||||
// Pass 1
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (-1,0)
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
const int ay = y + rcGetDirOffsetY(0);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (-1,-1)
|
||||
if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(3);
|
||||
const int aay = ay + rcGetDirOffsetY(3);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (0,-1)
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
const int ay = y + rcGetDirOffsetY(3);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (1,-1)
|
||||
if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(2);
|
||||
const int aay = ay + rcGetDirOffsetY(2);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2
|
||||
for (int y = h-1; y >= 0; --y)
|
||||
{
|
||||
for (int x = w-1; x >= 0; --x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (1,0)
|
||||
const int ax = x + rcGetDirOffsetX(2);
|
||||
const int ay = y + rcGetDirOffsetY(2);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (1,1)
|
||||
if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(1);
|
||||
const int aay = ay + rcGetDirOffsetY(1);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (0,1)
|
||||
const int ax = x + rcGetDirOffsetX(1);
|
||||
const int ay = y + rcGetDirOffsetY(1);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (-1,1)
|
||||
if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(0);
|
||||
const int aay = ay + rcGetDirOffsetY(0);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned char thr = (unsigned char)(radius*2);
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
if (dist[i] < thr)
|
||||
chf.areas[i] = RC_NULL_AREA;
|
||||
|
||||
rcFree(dist);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_ERODE_AREA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void insertSort(unsigned char* a, const int n)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 1; i < n; i++)
|
||||
{
|
||||
const unsigned char value = a[i];
|
||||
for (j = i - 1; j >= 0 && a[j] > value; j--)
|
||||
a[j+1] = a[j];
|
||||
a[j+1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// This filter is usually applied after applying area id's using functions
|
||||
/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.
|
||||
///
|
||||
/// @see rcCompactHeightfield
|
||||
bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
ctx->startTimer(RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init distance.
|
||||
memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
{
|
||||
areas[i] = chf.areas[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned char nei[9];
|
||||
for (int j = 0; j < 9; ++j)
|
||||
nei[j] = chf.areas[i];
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[ai] != RC_NULL_AREA)
|
||||
nei[dir*2+0] = chf.areas[ai];
|
||||
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
const int dir2 = (dir+1) & 0x3;
|
||||
if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax2 = ax + rcGetDirOffsetX(dir2);
|
||||
const int ay2 = ay + rcGetDirOffsetY(dir2);
|
||||
const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
|
||||
if (chf.areas[ai2] != RC_NULL_AREA)
|
||||
nei[dir*2+1] = chf.areas[ai2];
|
||||
}
|
||||
}
|
||||
}
|
||||
insertSort(nei, 9);
|
||||
areas[i] = nei[4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
rcFree(areas);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
if (chf.areas[i] != RC_NULL_AREA)
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int pointInPoly(int nvert, const float* verts, const float* p)
|
||||
{
|
||||
int i, j, c = 0;
|
||||
for (i = 0, j = nvert-1; i < nvert; j = i++)
|
||||
{
|
||||
const float* vi = &verts[i*3];
|
||||
const float* vj = &verts[j*3];
|
||||
if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
|
||||
(p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
|
||||
c = !c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// The y-values of the polygon vertices are ignored. So the polygon is effectively
|
||||
/// projected onto the xz-plane at @p hmin, then extruded to @p hmax.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
|
||||
const float hmin, const float hmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
rcVcopy(bmin, verts);
|
||||
rcVcopy(bmax, verts);
|
||||
for (int i = 1; i < nverts; ++i)
|
||||
{
|
||||
rcVmin(bmin, &verts[i*3]);
|
||||
rcVmax(bmax, &verts[i*3]);
|
||||
}
|
||||
bmin[1] = hmin;
|
||||
bmax[1] = hmax;
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
|
||||
// TODO: Optimize.
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
float p[3];
|
||||
p[0] = chf.bmin[0] + (x+0.5f)*chf.cs;
|
||||
p[1] = 0;
|
||||
p[2] = chf.bmin[2] + (z+0.5f)*chf.cs;
|
||||
|
||||
if (pointInPoly(nverts, verts, p))
|
||||
{
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkCylinderArea(rcContext* ctx, const float* pos,
|
||||
const float r, const float h, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MARK_CYLINDER_AREA);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
bmin[0] = pos[0] - r;
|
||||
bmin[1] = pos[1];
|
||||
bmin[2] = pos[2] - r;
|
||||
bmax[0] = pos[0] + r;
|
||||
bmax[1] = pos[1] + h;
|
||||
bmax[2] = pos[2] + r;
|
||||
const float r2 = r*r;
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
const float sx = chf.bmin[0] + (x+0.5f)*chf.cs;
|
||||
const float sz = chf.bmin[2] + (z+0.5f)*chf.cs;
|
||||
const float dx = sx - pos[0];
|
||||
const float dz = sz - pos[2];
|
||||
|
||||
if (dx*dx + dz*dz < r2)
|
||||
{
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MARK_CYLINDER_AREA);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -21,8 +21,8 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastLog.h"
|
||||
#include "RecastTimer.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
static int getCornerHeight(int x, int y, int i, int dir,
|
||||
@@ -33,44 +33,46 @@ static int getCornerHeight(int x, int y, int i, int dir,
|
||||
int ch = (int)s.y;
|
||||
int dirp = (dir+1) & 0x3;
|
||||
|
||||
unsigned short regs[4] = {0,0,0,0};
|
||||
unsigned int regs[4] = {0,0,0,0};
|
||||
|
||||
regs[0] = s.reg;
|
||||
// Combine region and area codes in order to prevent
|
||||
// border vertices which are in between two areas to be removed.
|
||||
regs[0] = chf.spans[i].reg | (chf.areas[i] << 16);
|
||||
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
ch = rcMax(ch, (int)as.y);
|
||||
regs[1] = as.reg;
|
||||
if (rcGetCon(as, dirp) != 0xf)
|
||||
regs[1] = chf.spans[ai].reg | (chf.areas[ai] << 16);
|
||||
if (rcGetCon(as, dirp) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax2 = ax + rcGetDirOffsetX(dirp);
|
||||
const int ay2 = ay + rcGetDirOffsetY(dirp);
|
||||
const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp);
|
||||
const rcCompactSpan& as2 = chf.spans[ai2];
|
||||
ch = rcMax(ch, (int)as2.y);
|
||||
regs[2] = as2.reg;
|
||||
regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16);
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, dirp) != 0xf)
|
||||
if (rcGetCon(s, dirp) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dirp);
|
||||
const int ay = y + rcGetDirOffsetY(dirp);
|
||||
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
ch = rcMax(ch, (int)as.y);
|
||||
regs[3] = as.reg;
|
||||
if (rcGetCon(as, dir) != 0xf)
|
||||
regs[3] = chf.spans[ai].reg | (chf.areas[ai] << 16);
|
||||
if (rcGetCon(as, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax2 = ax + rcGetDirOffsetX(dir);
|
||||
const int ay2 = ay + rcGetDirOffsetY(dir);
|
||||
const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir);
|
||||
const rcCompactSpan& as2 = chf.spans[ai2];
|
||||
ch = rcMax(ch, (int)as2.y);
|
||||
regs[2] = as2.reg;
|
||||
regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +88,9 @@ static int getCornerHeight(int x, int y, int i, int dir,
|
||||
// followed by two interior cells and none of the regions are out of bounds.
|
||||
const bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b];
|
||||
const bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0;
|
||||
const bool intsSameArea = (regs[c]>>16) == (regs[d]>>16);
|
||||
const bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
|
||||
if (twoSameExts && twoInts && noZeros)
|
||||
if (twoSameExts && twoInts && intsSameArea && noZeros)
|
||||
{
|
||||
isBorderVertex = true;
|
||||
break;
|
||||
@@ -109,6 +112,8 @@ static void walkContour(int x, int y, int i,
|
||||
unsigned char startDir = dir;
|
||||
int starti = i;
|
||||
|
||||
const unsigned char area = chf.areas[i];
|
||||
|
||||
int iter = 0;
|
||||
while (++iter < 40000)
|
||||
{
|
||||
@@ -116,6 +121,7 @@ static void walkContour(int x, int y, int i,
|
||||
{
|
||||
// Choose the edge corner
|
||||
bool isBorderVertex = false;
|
||||
bool isAreaBorder = false;
|
||||
int px = x;
|
||||
int py = getCornerHeight(x, y, i, dir, chf, isBorderVertex);
|
||||
int pz = y;
|
||||
@@ -127,16 +133,19 @@ static void walkContour(int x, int y, int i,
|
||||
}
|
||||
int r = 0;
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
r = (int)as.reg;
|
||||
r = (int)chf.spans[ai].reg;
|
||||
if (area != chf.areas[ai])
|
||||
isAreaBorder = true;
|
||||
}
|
||||
if (isBorderVertex)
|
||||
r |= RC_BORDER_VERTEX;
|
||||
if (isAreaBorder)
|
||||
r |= RC_AREA_BORDER;
|
||||
points.push(px);
|
||||
points.push(py);
|
||||
points.push(pz);
|
||||
@@ -151,7 +160,7 @@ static void walkContour(int x, int y, int i,
|
||||
const int nx = x + rcGetDirOffsetX(dir);
|
||||
const int ny = y + rcGetDirOffsetY(dir);
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
|
||||
ni = (int)nc.index + rcGetCon(s, dir);
|
||||
@@ -174,9 +183,9 @@ static void walkContour(int x, int y, int i,
|
||||
}
|
||||
}
|
||||
|
||||
static float distancePtSeg(int x, int y, int z,
|
||||
int px, int py, int pz,
|
||||
int qx, int qy, int qz)
|
||||
static float distancePtSeg(const int x, const int z,
|
||||
const int px, const int pz,
|
||||
const int qx, const int qz)
|
||||
{
|
||||
/* float pqx = (float)(qx - px);
|
||||
float pqy = (float)(qy - py);
|
||||
@@ -218,20 +227,40 @@ static float distancePtSeg(int x, int y, int z,
|
||||
return dx*dx + dz*dz;
|
||||
}
|
||||
|
||||
static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float maxError, int maxEdgeLen)
|
||||
static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
|
||||
const float maxError, const int maxEdgeLen, const int buildFlags)
|
||||
{
|
||||
// Add initial points.
|
||||
bool noConnections = true;
|
||||
bool hasConnections = false;
|
||||
for (int i = 0; i < points.size(); i += 4)
|
||||
{
|
||||
if ((points[i+3] & 0xffff) != 0)
|
||||
if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0)
|
||||
{
|
||||
noConnections = false;
|
||||
hasConnections = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (noConnections)
|
||||
if (hasConnections)
|
||||
{
|
||||
// The contour has some portals to other regions.
|
||||
// Add a new point to every location where the region changes.
|
||||
for (int i = 0, ni = points.size()/4; i < ni; ++i)
|
||||
{
|
||||
int ii = (i+1) % ni;
|
||||
const bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK);
|
||||
const bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER);
|
||||
if (differentRegs || areaBorders)
|
||||
{
|
||||
simplified.push(points[i*4+0]);
|
||||
simplified.push(points[i*4+1]);
|
||||
simplified.push(points[i*4+2]);
|
||||
simplified.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (simplified.size() == 0)
|
||||
{
|
||||
// If there is no connections at all,
|
||||
// create some initial points for the simplification process.
|
||||
@@ -256,7 +285,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
|
||||
llz = z;
|
||||
lli = i/4;
|
||||
}
|
||||
if (x >= urx || (x == urx && z > urz))
|
||||
if (x > urx || (x == urx && z > urz))
|
||||
{
|
||||
urx = x;
|
||||
ury = y;
|
||||
@@ -274,22 +303,6 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
|
||||
simplified.push(urz);
|
||||
simplified.push(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The contour has some portals to other regions.
|
||||
// Add a new point to every location where the region changes.
|
||||
for (int i = 0, ni = points.size()/4; i < ni; ++i)
|
||||
{
|
||||
int ii = (i+1) % ni;
|
||||
if ((points[i*4+3] & 0xffff) != (points[ii*4+3] & 0xffff))
|
||||
{
|
||||
simplified.push(points[i*4+0]);
|
||||
simplified.push(points[i*4+1]);
|
||||
simplified.push(points[i*4+2]);
|
||||
simplified.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add points until all raw points are within
|
||||
// error tolerance to the simplified shape.
|
||||
@@ -298,34 +311,48 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
|
||||
{
|
||||
int ii = (i+1) % (simplified.size()/4);
|
||||
|
||||
int ax = simplified[i*4+0];
|
||||
int ay = simplified[i*4+1];
|
||||
int az = simplified[i*4+2];
|
||||
int ai = simplified[i*4+3];
|
||||
|
||||
int bx = simplified[ii*4+0];
|
||||
int by = simplified[ii*4+1];
|
||||
int bz = simplified[ii*4+2];
|
||||
int bi = simplified[ii*4+3];
|
||||
const int ax = simplified[i*4+0];
|
||||
const int az = simplified[i*4+2];
|
||||
const int ai = simplified[i*4+3];
|
||||
|
||||
const int bx = simplified[ii*4+0];
|
||||
const int bz = simplified[ii*4+2];
|
||||
const int bi = simplified[ii*4+3];
|
||||
|
||||
// Find maximum deviation from the segment.
|
||||
float maxd = 0;
|
||||
int maxi = -1;
|
||||
int ci = (ai+1) % pn;
|
||||
int ci, cinc, endi;
|
||||
|
||||
// Tesselate only outer edges.
|
||||
if ((points[ci*4+3] & 0xffff) == 0)
|
||||
// Traverse the segment in lexilogical order so that the
|
||||
// max deviation is calculated similarly when traversing
|
||||
// opposite segments.
|
||||
if (bx > ax || (bx == ax && bz > az))
|
||||
{
|
||||
while (ci != bi)
|
||||
cinc = 1;
|
||||
ci = (ai+cinc) % pn;
|
||||
endi = bi;
|
||||
}
|
||||
else
|
||||
{
|
||||
cinc = pn-1;
|
||||
ci = (bi+cinc) % pn;
|
||||
endi = ai;
|
||||
}
|
||||
|
||||
// Tessellate only outer edges or edges between areas.
|
||||
if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 ||
|
||||
(points[ci*4+3] & RC_AREA_BORDER))
|
||||
{
|
||||
while (ci != endi)
|
||||
{
|
||||
float d = distancePtSeg(points[ci*4+0], points[ci*4+1]/4, points[ci*4+2],
|
||||
ax, ay/4, az, bx, by/4, bz);
|
||||
float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz);
|
||||
if (d > maxd)
|
||||
{
|
||||
maxd = d;
|
||||
maxi = ci;
|
||||
}
|
||||
ci = (ci+1) % pn;
|
||||
ci = (ci+cinc) % pn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +363,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
|
||||
{
|
||||
// Add space for the new point.
|
||||
simplified.resize(simplified.size()+4);
|
||||
int n = simplified.size()/4;
|
||||
const int n = simplified.size()/4;
|
||||
for (int j = n-1; j > i; --j)
|
||||
{
|
||||
simplified[j*4+0] = simplified[(j-1)*4+0];
|
||||
@@ -357,33 +384,52 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
|
||||
}
|
||||
|
||||
// Split too long edges.
|
||||
if (maxEdgeLen > 0)
|
||||
if (maxEdgeLen > 0 && (buildFlags & (RC_CONTOUR_TESS_WALL_EDGES|RC_CONTOUR_TESS_AREA_EDGES)) != 0)
|
||||
{
|
||||
for (int i = 0; i < simplified.size()/4; )
|
||||
{
|
||||
int ii = (i+1) % (simplified.size()/4);
|
||||
const int ii = (i+1) % (simplified.size()/4);
|
||||
|
||||
int ax = simplified[i*4+0];
|
||||
int az = simplified[i*4+2];
|
||||
int ai = simplified[i*4+3];
|
||||
|
||||
int bx = simplified[ii*4+0];
|
||||
int bz = simplified[ii*4+2];
|
||||
int bi = simplified[ii*4+3];
|
||||
const int ax = simplified[i*4+0];
|
||||
const int az = simplified[i*4+2];
|
||||
const int ai = simplified[i*4+3];
|
||||
|
||||
const int bx = simplified[ii*4+0];
|
||||
const int bz = simplified[ii*4+2];
|
||||
const int bi = simplified[ii*4+3];
|
||||
|
||||
// Find maximum deviation from the segment.
|
||||
int maxi = -1;
|
||||
int ci = (ai+1) % pn;
|
||||
|
||||
// Tessellate only outer edges or edges between areas.
|
||||
bool tess = false;
|
||||
// Wall edges.
|
||||
if ((buildFlags & RC_CONTOUR_TESS_WALL_EDGES) && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0)
|
||||
tess = true;
|
||||
// Edges between areas.
|
||||
if ((buildFlags & RC_CONTOUR_TESS_AREA_EDGES) && (points[ci*4+3] & RC_AREA_BORDER))
|
||||
tess = true;
|
||||
|
||||
// Tesselate only outer edges.
|
||||
if ((points[ci*4+3] & 0xffff) == 0)
|
||||
if (tess)
|
||||
{
|
||||
int dx = bx - ax;
|
||||
int dz = bz - az;
|
||||
if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen)
|
||||
{
|
||||
int n = bi < ai ? (bi+pn - ai) : (bi - ai);
|
||||
maxi = (ai + n/2) % pn;
|
||||
// Round based on the segments in lexilogical order so that the
|
||||
// max tesselation is consistent regardles in which direction
|
||||
// segments are traversed.
|
||||
if (bx > ax || (bx == ax && bz > az))
|
||||
{
|
||||
const int n = bi < ai ? (bi+pn - ai) : (bi - ai);
|
||||
maxi = (ai + n/2) % pn;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int n = bi < ai ? (bi+pn - ai) : (bi - ai);
|
||||
maxi = (ai + (n+1)/2) % pn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,7 +439,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
|
||||
{
|
||||
// Add space for the new point.
|
||||
simplified.resize(simplified.size()+4);
|
||||
int n = simplified.size()/4;
|
||||
const int n = simplified.size()/4;
|
||||
for (int j = n-1; j > i; --j)
|
||||
{
|
||||
simplified[j*4+0] = simplified[(j-1)*4+0];
|
||||
@@ -420,7 +466,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
|
||||
// and the neighbour region is take from the next raw point.
|
||||
const int ai = (simplified[i*4+3]+1) % pn;
|
||||
const int bi = simplified[i*4+3];
|
||||
simplified[i*4+3] = (points[ai*4+3] & 0xffff) | (points[bi*4+3] & RC_BORDER_VERTEX);
|
||||
simplified[i*4+3] = (points[ai*4+3] & RC_CONTOUR_REG_MASK) | (points[bi*4+3] & RC_BORDER_VERTEX);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -446,7 +492,7 @@ static void removeDegenerateSegments(rcIntArray& simplified)
|
||||
simplified[j*4+2] = simplified[(j+1)*4+2];
|
||||
simplified[j*4+3] = simplified[(j+1)*4+3];
|
||||
}
|
||||
simplified.pop();
|
||||
simplified.resize(simplified.size()-4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,25 +509,40 @@ static int calcAreaOfPolygon2D(const int* verts, const int nverts)
|
||||
return (area+1) / 2;
|
||||
}
|
||||
|
||||
inline bool ileft(const int* a, const int* b, const int* c)
|
||||
{
|
||||
return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0;
|
||||
}
|
||||
|
||||
static void getClosestIndices(const int* vertsa, const int nvertsa,
|
||||
const int* vertsb, const int nvertsb,
|
||||
int& ia, int& ib)
|
||||
{
|
||||
int closestDist = 0xfffffff;
|
||||
ia = -1, ib = -1;
|
||||
for (int i = 0; i < nvertsa; ++i)
|
||||
{
|
||||
const int in = (i+1) % nvertsa;
|
||||
const int ip = (i+nvertsa-1) % nvertsa;
|
||||
const int* va = &vertsa[i*4];
|
||||
const int* van = &vertsa[in*4];
|
||||
const int* vap = &vertsa[ip*4];
|
||||
|
||||
for (int j = 0; j < nvertsb; ++j)
|
||||
{
|
||||
const int* vb = &vertsb[j*4];
|
||||
const int dx = vb[0] - va[0];
|
||||
const int dz = vb[2] - va[2];
|
||||
const int d = dx*dx + dz*dz;
|
||||
if (d < closestDist)
|
||||
// vb must be "infront" of va.
|
||||
if (ileft(vap,va,vb) && ileft(va,van,vb))
|
||||
{
|
||||
ia = i;
|
||||
ib = j;
|
||||
closestDist = d;
|
||||
const int dx = vb[0] - va[0];
|
||||
const int dz = vb[2] - va[2];
|
||||
const int d = dx*dx + dz*dz;
|
||||
if (d < closestDist)
|
||||
{
|
||||
ia = i;
|
||||
ib = j;
|
||||
closestDist = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,7 +551,7 @@ static void getClosestIndices(const int* vertsa, const int nvertsa,
|
||||
static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
|
||||
{
|
||||
const int maxVerts = ca.nverts + cb.nverts + 2;
|
||||
int* verts = new int[maxVerts*4];
|
||||
int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
|
||||
if (!verts)
|
||||
return false;
|
||||
|
||||
@@ -520,47 +581,73 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
|
||||
nv++;
|
||||
}
|
||||
|
||||
delete [] ca.verts;
|
||||
rcFree(ca.verts);
|
||||
ca.verts = verts;
|
||||
ca.nverts = nv;
|
||||
|
||||
delete [] cb.verts;
|
||||
rcFree(cb.verts);
|
||||
cb.verts = 0;
|
||||
cb.nverts = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcBuildContours(rcCompactHeightfield& chf,
|
||||
/// @par
|
||||
///
|
||||
/// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen
|
||||
/// parameters control how closely the simplified contours will match the raw contours.
|
||||
///
|
||||
/// Simplified contours are generated such that the vertices for portals between areas match up.
|
||||
/// (They are considered mandatory vertices.)
|
||||
///
|
||||
/// Setting @p maxEdgeLength to zero will disabled the edge length feature.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
|
||||
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const float maxError, const int maxEdgeLen,
|
||||
rcContourSet& cset)
|
||||
rcContourSet& cset, const int buildFlags)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
const int borderSize = chf.borderSize;
|
||||
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS);
|
||||
|
||||
vcopy(cset.bmin, chf.bmin);
|
||||
vcopy(cset.bmax, chf.bmax);
|
||||
rcVcopy(cset.bmin, chf.bmin);
|
||||
rcVcopy(cset.bmax, chf.bmax);
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
const float pad = borderSize*chf.cs;
|
||||
cset.bmin[0] += pad;
|
||||
cset.bmin[2] += pad;
|
||||
cset.bmax[0] -= pad;
|
||||
cset.bmax[2] -= pad;
|
||||
}
|
||||
cset.cs = chf.cs;
|
||||
cset.ch = chf.ch;
|
||||
cset.width = chf.width - chf.borderSize*2;
|
||||
cset.height = chf.height - chf.borderSize*2;
|
||||
cset.borderSize = chf.borderSize;
|
||||
|
||||
const int maxContours = chf.maxRegions*2;
|
||||
cset.conts = new rcContour[maxContours];
|
||||
int maxContours = rcMax((int)chf.maxRegions, 8);
|
||||
cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
|
||||
if (!cset.conts)
|
||||
return false;
|
||||
cset.nconts = 0;
|
||||
|
||||
unsigned char* flags = new unsigned char[chf.spanCount];
|
||||
rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!flags)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags'.");
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
rcTimeVal traceStartTime = rcGetPerformanceTimer();
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
|
||||
// Mark boundaries.
|
||||
for (int y = 0; y < h; ++y)
|
||||
@@ -572,7 +659,7 @@ bool rcBuildContours(rcCompactHeightfield& chf,
|
||||
{
|
||||
unsigned char res = 0;
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (!s.reg || (s.reg & RC_BORDER_REG))
|
||||
if (!chf.spans[i].reg || (chf.spans[i].reg & RC_BORDER_REG))
|
||||
{
|
||||
flags[i] = 0;
|
||||
continue;
|
||||
@@ -580,15 +667,14 @@ bool rcBuildContours(rcCompactHeightfield& chf,
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
unsigned short r = 0;
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
r = as.reg;
|
||||
r = chf.spans[ai].reg;
|
||||
}
|
||||
if (r == s.reg)
|
||||
if (r == chf.spans[i].reg)
|
||||
res |= (1 << dir);
|
||||
}
|
||||
flags[i] = res ^ 0xf; // Inverse, mark non connected edges.
|
||||
@@ -596,9 +682,7 @@ bool rcBuildContours(rcCompactHeightfield& chf,
|
||||
}
|
||||
}
|
||||
|
||||
rcTimeVal traceEndTime = rcGetPerformanceTimer();
|
||||
|
||||
rcTimeVal simplifyStartTime = rcGetPerformanceTimer();
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
|
||||
rcIntArray verts(256);
|
||||
rcIntArray simplified(64);
|
||||
@@ -615,36 +699,87 @@ bool rcBuildContours(rcCompactHeightfield& chf,
|
||||
flags[i] = 0;
|
||||
continue;
|
||||
}
|
||||
unsigned short reg = chf.spans[i].reg;
|
||||
const unsigned short reg = chf.spans[i].reg;
|
||||
if (!reg || (reg & RC_BORDER_REG))
|
||||
continue;
|
||||
const unsigned char area = chf.areas[i];
|
||||
|
||||
verts.resize(0);
|
||||
simplified.resize(0);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
walkContour(x, y, i, chf, flags, verts);
|
||||
simplifyContour(verts, simplified, maxError, maxEdgeLen);
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
|
||||
simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
|
||||
removeDegenerateSegments(simplified);
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
|
||||
|
||||
|
||||
// Store region->contour remap info.
|
||||
// Create contour.
|
||||
if (simplified.size()/4 >= 3)
|
||||
{
|
||||
if (cset.nconts >= maxContours)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildContours: Too many contours %d, max %d.", cset.nconts, maxContours);
|
||||
return false;
|
||||
// Allocate more contours.
|
||||
// This can happen when there are tiny holes in the heightfield.
|
||||
const int oldMax = maxContours;
|
||||
maxContours *= 2;
|
||||
rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
|
||||
for (int j = 0; j < cset.nconts; ++j)
|
||||
{
|
||||
newConts[j] = cset.conts[j];
|
||||
// Reset source pointers to prevent data deletion.
|
||||
cset.conts[j].verts = 0;
|
||||
cset.conts[j].rverts = 0;
|
||||
}
|
||||
rcFree(cset.conts);
|
||||
cset.conts = newConts;
|
||||
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours);
|
||||
}
|
||||
|
||||
rcContour* cont = &cset.conts[cset.nconts++];
|
||||
|
||||
cont->nverts = simplified.size()/4;
|
||||
cont->verts = new int[cont->nverts*4];
|
||||
cont->verts = (int*)rcAlloc(sizeof(int)*cont->nverts*4, RC_ALLOC_PERM);
|
||||
if (!cont->verts)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' (%d).", cont->nverts);
|
||||
return false;
|
||||
}
|
||||
memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4);
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
for (int i = 0; i < cont->nverts; ++i)
|
||||
{
|
||||
int* v = &cont->verts[i*4];
|
||||
v[0] -= borderSize;
|
||||
v[2] -= borderSize;
|
||||
}
|
||||
}
|
||||
|
||||
cont->nrverts = verts.size()/4;
|
||||
cont->rverts = new int[cont->nrverts*4];
|
||||
cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM);
|
||||
if (!cont->rverts)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' (%d).", cont->nrverts);
|
||||
return false;
|
||||
}
|
||||
memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4);
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
for (int i = 0; i < cont->nrverts; ++i)
|
||||
{
|
||||
int* v = &cont->rverts[i*4];
|
||||
v[0] -= borderSize;
|
||||
v[2] -= borderSize;
|
||||
}
|
||||
}
|
||||
|
||||
/* cont->cx = cont->cy = cont->cz = 0;
|
||||
for (int i = 0; i < cont->nverts; ++i)
|
||||
@@ -658,13 +793,14 @@ bool rcBuildContours(rcCompactHeightfield& chf,
|
||||
cont->cz /= cont->nverts;*/
|
||||
|
||||
cont->reg = reg;
|
||||
cont->area = area;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check and merge droppings.
|
||||
// Sometimes the previous algorithms can fail and create several countours
|
||||
// Sometimes the previous algorithms can fail and create several contours
|
||||
// per area. This pass will try to merge the holes into the main region.
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
@@ -689,44 +825,29 @@ bool rcBuildContours(rcCompactHeightfield& chf,
|
||||
}
|
||||
if (mergeIdx == -1)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
|
||||
}
|
||||
else
|
||||
{
|
||||
rcContour& mcont = cset.conts[mergeIdx];
|
||||
// Merge by closest points.
|
||||
int ia, ib;
|
||||
int ia = 0, ib = 0;
|
||||
getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib);
|
||||
if (ia == -1 || ib == -1)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx);
|
||||
continue;
|
||||
}
|
||||
if (!mergeContours(mcont, cont, ia, ib))
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
delete [] flags;
|
||||
|
||||
rcTimeVal simplifyEndTime = rcGetPerformanceTimer();
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
// if (rcGetLog())
|
||||
// {
|
||||
// rcGetLog()->log(RC_LOG_PROGRESS, "Create contours: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
|
||||
// rcGetLog()->log(RC_LOG_PROGRESS, " - boundary: %.3f ms", rcGetDeltaTimeUsec(boundaryStartTime, boundaryEndTime)/1000.0f);
|
||||
// rcGetLog()->log(RC_LOG_PROGRESS, " - contour: %.3f ms", rcGetDeltaTimeUsec(contourStartTime, contourEndTime)/1000.0f);
|
||||
// }
|
||||
|
||||
if (rcGetBuildTimes())
|
||||
{
|
||||
rcGetBuildTimes()->buildContours += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
rcGetBuildTimes()->buildContoursTrace += rcGetDeltaTimeUsec(traceStartTime, traceEndTime);
|
||||
rcGetBuildTimes()->buildContoursSimplify += rcGetDeltaTimeUsec(simplifyStartTime, simplifyEndTime);
|
||||
}
|
||||
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -20,15 +20,73 @@
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastLog.h"
|
||||
#include "RecastTimer.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Allows the formation of walkable regions that will flow over low lying
|
||||
/// objects such as curbs, and up structures such as stairways.
|
||||
///
|
||||
/// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
|
||||
///
|
||||
/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call
|
||||
/// #rcFilterLedgeSpans after calling this filter.
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
void rcFilterLedgeSpans(const int walkableHeight,
|
||||
const int walkableClimb,
|
||||
ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
rcSpan* ps = 0;
|
||||
bool previousWalkable = false;
|
||||
unsigned char previousArea = RC_NULL_AREA;
|
||||
|
||||
for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next)
|
||||
{
|
||||
const bool walkable = s->area != RC_NULL_AREA;
|
||||
// If current span is not walkable, but there is walkable
|
||||
// span just below it, mark the span above it walkable too.
|
||||
if (!walkable && previousWalkable)
|
||||
{
|
||||
if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb)
|
||||
s->area = previousArea;
|
||||
}
|
||||
// Copy walkable flag so that it cannot propagate
|
||||
// past multiple non-walkable objects.
|
||||
previousWalkable = walkable;
|
||||
previousArea = s->area;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
|
||||
/// from the current span's maximum.
|
||||
/// This method removes the impact of the overestimation of conservative voxelization
|
||||
/// so the resulting mesh will not have regions hanging in the air over ledges.
|
||||
///
|
||||
/// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
||||
rcHeightfield& solid)
|
||||
{
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_FILTER_BORDER);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
@@ -42,15 +100,19 @@ void rcFilterLedgeSpans(const int walkableHeight,
|
||||
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
|
||||
{
|
||||
// Skip non walkable spans.
|
||||
if ((s->flags & RC_WALKABLE) == 0)
|
||||
if (s->area == RC_NULL_AREA)
|
||||
continue;
|
||||
|
||||
const int bot = (int)s->smax;
|
||||
const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
|
||||
const int bot = (int)(s->smax);
|
||||
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
|
||||
|
||||
// Find neighbours minimum height.
|
||||
int minh = MAX_HEIGHT;
|
||||
|
||||
// Min and max height of accessible neighbours.
|
||||
int asmin = s->smax;
|
||||
int asmax = s->smax;
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
int dx = x + rcGetDirOffsetX(dir);
|
||||
@@ -77,30 +139,49 @@ void rcFilterLedgeSpans(const int walkableHeight,
|
||||
ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT;
|
||||
// Skip neightbour if the gap between the spans is too small.
|
||||
if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
|
||||
{
|
||||
minh = rcMin(minh, nbot - bot);
|
||||
|
||||
// Find min/max accessible neighbour height.
|
||||
if (rcAbs(nbot - bot) <= walkableClimb)
|
||||
{
|
||||
if (nbot < asmin) asmin = nbot;
|
||||
if (nbot > asmax) asmax = nbot;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The current span is close to a ledge if the drop to any
|
||||
// neighbour span is less than the walkableClimb.
|
||||
if (minh < -walkableClimb)
|
||||
s->flags &= ~RC_WALKABLE;
|
||||
|
||||
s->area = RC_NULL_AREA;
|
||||
|
||||
// If the difference between all neighbours is too large,
|
||||
// we are at steep slope, mark the span as ledge.
|
||||
if ((asmax - asmin) > walkableClimb)
|
||||
{
|
||||
s->area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
// if (rcGetLog())
|
||||
// rcGetLog()->log(RC_LOG_PROGRESS, "Filter border: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->filterBorder += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
ctx->stopTimer(RC_TIMER_FILTER_BORDER);
|
||||
}
|
||||
|
||||
void rcFilterWalkableLowHeightSpans(int walkableHeight,
|
||||
rcHeightfield& solid)
|
||||
/// @par
|
||||
///
|
||||
/// For this filter, the clearance above the span is the distance from the span's
|
||||
/// maximum to the next higher span's minimum. (Same grid column.)
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid)
|
||||
{
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_FILTER_WALKABLE);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
@@ -114,136 +195,13 @@ void rcFilterWalkableLowHeightSpans(int walkableHeight,
|
||||
{
|
||||
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
|
||||
{
|
||||
const int bot = (int)s->smax;
|
||||
const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
|
||||
const int bot = (int)(s->smax);
|
||||
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
|
||||
if ((top - bot) <= walkableHeight)
|
||||
s->flags &= ~RC_WALKABLE;
|
||||
s->area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
// if (rcGetLog())
|
||||
// rcGetLog()->log(RC_LOG_PROGRESS, "Filter walkable: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->filterWalkable += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
}
|
||||
|
||||
|
||||
struct rcReachableSeed
|
||||
{
|
||||
inline void set(int ix, int iy, rcSpan* is)
|
||||
{
|
||||
x = (unsigned short)ix;
|
||||
y = (unsigned short)iy;
|
||||
s = is;
|
||||
}
|
||||
unsigned short x, y;
|
||||
rcSpan* s;
|
||||
};
|
||||
|
||||
bool rcMarkReachableSpans(const int walkableHeight,
|
||||
const int walkableClimb,
|
||||
rcHeightfield& solid)
|
||||
{
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
const int MAX_HEIGHT = 0xffff;
|
||||
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
|
||||
// Build navigable space.
|
||||
const int MAX_SEEDS = w*h;
|
||||
rcReachableSeed* stack = new rcReachableSeed[MAX_SEEDS];
|
||||
if (!stack)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcMarkReachableSpans: Out of memory 'stack' (%d).", MAX_SEEDS);
|
||||
return false;
|
||||
}
|
||||
int stackSize = 0;
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
rcSpan* topSpan = solid.spans[x + y*w];
|
||||
if (!topSpan)
|
||||
continue;
|
||||
while (topSpan->next)
|
||||
topSpan = topSpan->next;
|
||||
|
||||
// If the span is not walkable, skip it.
|
||||
if ((topSpan->flags & RC_WALKABLE) == 0)
|
||||
continue;
|
||||
// If the span has been visited already, skip it.
|
||||
if (topSpan->flags & RC_REACHABLE)
|
||||
continue;
|
||||
|
||||
// Start flood fill.
|
||||
topSpan->flags |= RC_REACHABLE;
|
||||
stackSize = 0;
|
||||
stack[stackSize].set(x, y, topSpan);
|
||||
stackSize++;
|
||||
|
||||
while (stackSize)
|
||||
{
|
||||
// Pop a seed from the stack.
|
||||
stackSize--;
|
||||
rcReachableSeed cur = stack[stackSize];
|
||||
|
||||
const int bot = (int)cur.s->smax;
|
||||
const int top = cur.s->next ? (int)cur.s->next->smin : MAX_HEIGHT;
|
||||
|
||||
// Visit neighbours in all 4 directions.
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
int dx = (int)cur.x + rcGetDirOffsetX(dir);
|
||||
int dy = (int)cur.y + rcGetDirOffsetY(dir);
|
||||
// Skip neighbour which are out of bounds.
|
||||
if (dx < 0 || dy < 0 || dx >= w || dy >= h)
|
||||
continue;
|
||||
for (rcSpan* ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
|
||||
{
|
||||
// Skip neighbour if it is not walkable.
|
||||
if ((ns->flags & RC_WALKABLE) == 0)
|
||||
continue;
|
||||
// Skip the neighbour if it has been visited already.
|
||||
if (ns->flags & RC_REACHABLE)
|
||||
continue;
|
||||
|
||||
const int nbot = (int)ns->smax;
|
||||
const int ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT;
|
||||
// Skip neightbour if the gap between the spans is too small.
|
||||
if (rcMin(top,ntop) - rcMax(bot,nbot) < walkableHeight)
|
||||
continue;
|
||||
// Skip neightbour if the climb height to the neighbour is too high.
|
||||
if (rcAbs(nbot - bot) >= walkableClimb)
|
||||
continue;
|
||||
|
||||
// This neighbour has not been visited yet.
|
||||
// Mark it as reachable and add it to the seed stack.
|
||||
ns->flags |= RC_REACHABLE;
|
||||
if (stackSize < MAX_SEEDS)
|
||||
{
|
||||
stack[stackSize].set(dx, dy, ns);
|
||||
stackSize++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete [] stack;
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
// if (rcGetLog())
|
||||
// rcGetLog()->log(RC_LOG_PROGRESS, "Mark reachable: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->filterMarkReachable += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
|
||||
return true;
|
||||
ctx->stopTimer(RC_TIMER_FILTER_WALKABLE);
|
||||
}
|
||||
|
||||
620
extern/recastnavigation/Recast/Source/RecastLayers.cpp
vendored
Normal file
620
extern/recastnavigation/Recast/Source/RecastLayers.cpp
vendored
Normal file
@@ -0,0 +1,620 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <float.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
static const int RC_MAX_LAYERS = RC_NOT_CONNECTED;
|
||||
static const int RC_MAX_NEIS = 16;
|
||||
|
||||
struct rcLayerRegion
|
||||
{
|
||||
unsigned char layers[RC_MAX_LAYERS];
|
||||
unsigned char neis[RC_MAX_NEIS];
|
||||
unsigned short ymin, ymax;
|
||||
unsigned char layerId; // Layer ID
|
||||
unsigned char nlayers; // Layer count
|
||||
unsigned char nneis; // Neighbour count
|
||||
unsigned char base; // Flag indicating if the region is hte base of merged regions.
|
||||
};
|
||||
|
||||
|
||||
static void addUnique(unsigned char* a, unsigned char& an, unsigned char v)
|
||||
{
|
||||
const int n = (int)an;
|
||||
for (int i = 0; i < n; ++i)
|
||||
if (a[i] == v)
|
||||
return;
|
||||
a[an] = v;
|
||||
an++;
|
||||
}
|
||||
|
||||
static bool contains(const unsigned char* a, const unsigned char an, const unsigned char v)
|
||||
{
|
||||
const int n = (int)an;
|
||||
for (int i = 0; i < n; ++i)
|
||||
if (a[i] == v)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool overlapRange(const unsigned short amin, const unsigned short amax,
|
||||
const unsigned short bmin, const unsigned short bmax)
|
||||
{
|
||||
return (amin > bmax || amax < bmin) ? false : true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct rcLayerSweepSpan
|
||||
{
|
||||
unsigned short ns; // number samples
|
||||
unsigned char id; // region id
|
||||
unsigned char nei; // neighbour id
|
||||
};
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
|
||||
bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int walkableHeight,
|
||||
rcHeightfieldLayerSet& lset)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_LAYERS);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
rcScopedDelete<unsigned char> srcReg = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!srcReg)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
const int nsweeps = chf.width;
|
||||
rcScopedDelete<rcLayerSweepSpan> sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP);
|
||||
if (!sweeps)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Partition walkable area into monotone regions.
|
||||
int prevCount[256];
|
||||
unsigned char regId = 0;
|
||||
|
||||
for (int y = borderSize; y < h-borderSize; ++y)
|
||||
{
|
||||
memset(prevCount,0,sizeof(int)*regId);
|
||||
unsigned char sweepId = 0;
|
||||
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA) continue;
|
||||
|
||||
unsigned char sid = 0xff;
|
||||
|
||||
// -x
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
const int ay = y + rcGetDirOffsetY(0);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff)
|
||||
sid = srcReg[ai];
|
||||
}
|
||||
|
||||
if (sid == 0xff)
|
||||
{
|
||||
sid = sweepId++;
|
||||
sweeps[sid].nei = 0xff;
|
||||
sweeps[sid].ns = 0;
|
||||
}
|
||||
|
||||
// -y
|
||||
if (rcGetCon(s,3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
const int ay = y + rcGetDirOffsetY(3);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
const unsigned char nr = srcReg[ai];
|
||||
if (nr != 0xff)
|
||||
{
|
||||
// Set neighbour when first valid neighbour is encoutered.
|
||||
if (sweeps[sid].ns == 0)
|
||||
sweeps[sid].nei = nr;
|
||||
|
||||
if (sweeps[sid].nei == nr)
|
||||
{
|
||||
// Update existing neighbour
|
||||
sweeps[sid].ns++;
|
||||
prevCount[nr]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is hit if there is nore than one neighbour.
|
||||
// Invalidate the neighbour.
|
||||
sweeps[sid].nei = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcReg[i] = sid;
|
||||
}
|
||||
}
|
||||
|
||||
// Create unique ID.
|
||||
for (int i = 0; i < sweepId; ++i)
|
||||
{
|
||||
// If the neighbour is set and there is only one continuous connection to it,
|
||||
// the sweep will be merged with the previous one, else new region is created.
|
||||
if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns)
|
||||
{
|
||||
sweeps[i].id = sweeps[i].nei;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (regId == 255)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow.");
|
||||
return false;
|
||||
}
|
||||
sweeps[i].id = regId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Remap local sweep ids to region ids.
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (srcReg[i] != 0xff)
|
||||
srcReg[i] = sweeps[srcReg[i]].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate and init layer regions.
|
||||
const int nregs = (int)regId;
|
||||
rcScopedDelete<rcLayerRegion> regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP);
|
||||
if (!regs)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs);
|
||||
return false;
|
||||
}
|
||||
memset(regs, 0, sizeof(rcLayerRegion)*nregs);
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
regs[i].layerId = 0xff;
|
||||
regs[i].ymin = 0xffff;
|
||||
regs[i].ymax = 0;
|
||||
}
|
||||
|
||||
// Find region neighbours and overlapping regions.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
unsigned char lregs[RC_MAX_LAYERS];
|
||||
int nlregs = 0;
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
const unsigned char ri = srcReg[i];
|
||||
if (ri == 0xff) continue;
|
||||
|
||||
regs[ri].ymin = rcMin(regs[ri].ymin, s.y);
|
||||
regs[ri].ymax = rcMax(regs[ri].ymax, s.y);
|
||||
|
||||
// Collect all region layers.
|
||||
if (nlregs < RC_MAX_LAYERS)
|
||||
lregs[nlregs++] = ri;
|
||||
|
||||
// Update neighbours
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
const unsigned char rai = srcReg[ai];
|
||||
if (rai != 0xff && rai != ri)
|
||||
addUnique(regs[ri].neis, regs[ri].nneis, rai);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update overlapping regions.
|
||||
for (int i = 0; i < nlregs-1; ++i)
|
||||
{
|
||||
for (int j = i+1; j < nlregs; ++j)
|
||||
{
|
||||
if (lregs[i] != lregs[j])
|
||||
{
|
||||
rcLayerRegion& ri = regs[lregs[i]];
|
||||
rcLayerRegion& rj = regs[lregs[j]];
|
||||
addUnique(ri.layers, ri.nlayers, lregs[j]);
|
||||
addUnique(rj.layers, rj.nlayers, lregs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Create 2D layers from regions.
|
||||
unsigned char layerId = 0;
|
||||
|
||||
static const int MAX_STACK = 64;
|
||||
unsigned char stack[MAX_STACK];
|
||||
int nstack = 0;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion& root = regs[i];
|
||||
// Skip alreadu visited.
|
||||
if (root.layerId != 0xff)
|
||||
continue;
|
||||
|
||||
// Start search.
|
||||
root.layerId = layerId;
|
||||
root.base = 1;
|
||||
|
||||
nstack = 0;
|
||||
stack[nstack++] = (unsigned char)i;
|
||||
|
||||
while (nstack)
|
||||
{
|
||||
// Pop front
|
||||
rcLayerRegion& reg = regs[stack[0]];
|
||||
nstack--;
|
||||
for (int j = 0; j < nstack; ++j)
|
||||
stack[j] = stack[j+1];
|
||||
|
||||
const int nneis = (int)reg.nneis;
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
{
|
||||
const unsigned char nei = reg.neis[j];
|
||||
rcLayerRegion& regn = regs[nei];
|
||||
// Skip already visited.
|
||||
if (regn.layerId != 0xff)
|
||||
continue;
|
||||
// Skip if the neighbour is overlapping root region.
|
||||
if (contains(root.layers, root.nlayers, nei))
|
||||
continue;
|
||||
// Skip if the height range would become too large.
|
||||
const int ymin = rcMin(root.ymin, regn.ymin);
|
||||
const int ymax = rcMin(root.ymax, regn.ymax);
|
||||
if ((ymax - ymin) >= 255)
|
||||
continue;
|
||||
|
||||
if (nstack < MAX_STACK)
|
||||
{
|
||||
// Deepen
|
||||
stack[nstack++] = (unsigned char)nei;
|
||||
|
||||
// Mark layer id
|
||||
regn.layerId = layerId;
|
||||
// Merge current layers to root.
|
||||
for (int k = 0; k < regn.nlayers; ++k)
|
||||
addUnique(root.layers, root.nlayers, regn.layers[k]);
|
||||
root.ymin = rcMin(root.ymin, regn.ymin);
|
||||
root.ymax = rcMax(root.ymax, regn.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layerId++;
|
||||
}
|
||||
|
||||
// Merge non-overlapping regions that are close in height.
|
||||
const unsigned short mergeHeight = (unsigned short)walkableHeight * 4;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion& ri = regs[i];
|
||||
if (!ri.base) continue;
|
||||
|
||||
unsigned char newId = ri.layerId;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unsigned char oldId = 0xff;
|
||||
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (i == j) continue;
|
||||
rcLayerRegion& rj = regs[j];
|
||||
if (!rj.base) continue;
|
||||
|
||||
// Skip if teh regions are not close to each other.
|
||||
if (!overlapRange(ri.ymin,ri.ymax+mergeHeight, rj.ymin,rj.ymax+mergeHeight))
|
||||
continue;
|
||||
// Skip if the height range would become too large.
|
||||
const int ymin = rcMin(ri.ymin, rj.ymin);
|
||||
const int ymax = rcMin(ri.ymax, rj.ymax);
|
||||
if ((ymax - ymin) >= 255)
|
||||
continue;
|
||||
|
||||
// Make sure that there is no overlap when mergin 'ri' and 'rj'.
|
||||
bool overlap = false;
|
||||
// Iterate over all regions which have the same layerId as 'rj'
|
||||
for (int k = 0; k < nregs; ++k)
|
||||
{
|
||||
if (regs[k].layerId != rj.layerId)
|
||||
continue;
|
||||
// Check if region 'k' is overlapping region 'ri'
|
||||
// Index to 'regs' is the same as region id.
|
||||
if (contains(ri.layers,ri.nlayers, (unsigned char)k))
|
||||
{
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Cannot merge of regions overlap.
|
||||
if (overlap)
|
||||
continue;
|
||||
|
||||
// Can merge i and j.
|
||||
oldId = rj.layerId;
|
||||
break;
|
||||
}
|
||||
|
||||
// Could not find anything to merge with, stop.
|
||||
if (oldId == 0xff)
|
||||
break;
|
||||
|
||||
// Merge
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
rcLayerRegion& rj = regs[j];
|
||||
if (rj.layerId == oldId)
|
||||
{
|
||||
rj.base = 0;
|
||||
// Remap layerIds.
|
||||
rj.layerId = newId;
|
||||
// Add overlaid layers from 'rj' to 'ri'.
|
||||
for (int k = 0; k < rj.nlayers; ++k)
|
||||
addUnique(ri.layers, ri.nlayers, rj.layers[k]);
|
||||
// Update heigh bounds.
|
||||
ri.ymin = rcMin(ri.ymin, rj.ymin);
|
||||
ri.ymax = rcMax(ri.ymax, rj.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compact layerIds
|
||||
unsigned char remap[256];
|
||||
memset(remap, 0, 256);
|
||||
|
||||
// Find number of unique layers.
|
||||
layerId = 0;
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
remap[regs[i].layerId] = 1;
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
if (remap[i])
|
||||
remap[i] = layerId++;
|
||||
else
|
||||
remap[i] = 0xff;
|
||||
}
|
||||
// Remap ids.
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
regs[i].layerId = remap[regs[i].layerId];
|
||||
|
||||
// No layers, return empty.
|
||||
if (layerId == 0)
|
||||
{
|
||||
ctx->stopTimer(RC_TIMER_BUILD_LAYERS);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create layers.
|
||||
rcAssert(lset.layers == 0);
|
||||
|
||||
const int lw = w - borderSize*2;
|
||||
const int lh = h - borderSize*2;
|
||||
|
||||
// Build contracted bbox for layers.
|
||||
float bmin[3], bmax[3];
|
||||
rcVcopy(bmin, chf.bmin);
|
||||
rcVcopy(bmax, chf.bmax);
|
||||
bmin[0] += borderSize*chf.cs;
|
||||
bmin[2] += borderSize*chf.cs;
|
||||
bmax[0] -= borderSize*chf.cs;
|
||||
bmax[2] -= borderSize*chf.cs;
|
||||
|
||||
lset.nlayers = (int)layerId;
|
||||
|
||||
lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM);
|
||||
if (!lset.layers)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' (%d).", lset.nlayers);
|
||||
return false;
|
||||
}
|
||||
memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers);
|
||||
|
||||
|
||||
// Store layers.
|
||||
for (int i = 0; i < lset.nlayers; ++i)
|
||||
{
|
||||
unsigned char curId = (unsigned char)i;
|
||||
|
||||
// Allocate memory for the current layer.
|
||||
rcHeightfieldLayer* layer = &lset.layers[i];
|
||||
memset(layer, 0, sizeof(rcHeightfieldLayer));
|
||||
|
||||
const int gridSize = sizeof(unsigned char)*lw*lh;
|
||||
|
||||
layer->heights = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->heights)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->heights, 0xff, gridSize);
|
||||
|
||||
layer->areas = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->areas, 0, gridSize);
|
||||
|
||||
layer->cons = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->cons)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->cons, 0, gridSize);
|
||||
|
||||
// Find layer height bounds.
|
||||
int hmin = 0, hmax = 0;
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (regs[j].base && regs[j].layerId == curId)
|
||||
{
|
||||
hmin = (int)regs[j].ymin;
|
||||
hmax = (int)regs[j].ymax;
|
||||
}
|
||||
}
|
||||
|
||||
layer->width = lw;
|
||||
layer->height = lh;
|
||||
layer->cs = chf.cs;
|
||||
layer->ch = chf.ch;
|
||||
|
||||
// Adjust the bbox to fit the heighfield.
|
||||
rcVcopy(layer->bmin, bmin);
|
||||
rcVcopy(layer->bmax, bmax);
|
||||
layer->bmin[1] = bmin[1] + hmin*chf.ch;
|
||||
layer->bmax[1] = bmin[1] + hmax*chf.ch;
|
||||
layer->hmin = hmin;
|
||||
layer->hmax = hmax;
|
||||
|
||||
// Update usable data region.
|
||||
layer->minx = layer->width;
|
||||
layer->maxx = 0;
|
||||
layer->miny = layer->height;
|
||||
layer->maxy = 0;
|
||||
|
||||
// Copy height and area from compact heighfield.
|
||||
for (int y = 0; y < lh; ++y)
|
||||
{
|
||||
for (int x = 0; x < lw; ++x)
|
||||
{
|
||||
const int cx = borderSize+x;
|
||||
const int cy = borderSize+y;
|
||||
const rcCompactCell& c = chf.cells[cx+cy*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
// Skip unassigned regions.
|
||||
if (srcReg[i] == 0xff)
|
||||
continue;
|
||||
// Skip of does nto belong to current layer.
|
||||
unsigned char lid = regs[srcReg[i]].layerId;
|
||||
if (lid != curId)
|
||||
continue;
|
||||
|
||||
// Update data bounds.
|
||||
layer->minx = rcMin(layer->minx, x);
|
||||
layer->maxx = rcMax(layer->maxx, x);
|
||||
layer->miny = rcMin(layer->miny, y);
|
||||
layer->maxy = rcMax(layer->maxy, y);
|
||||
|
||||
// Store height and area type.
|
||||
const int idx = x+y*lw;
|
||||
layer->heights[idx] = (unsigned char)(s.y - hmin);
|
||||
layer->areas[idx] = chf.areas[i];
|
||||
|
||||
// Check connection.
|
||||
unsigned char portal = 0;
|
||||
unsigned char con = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = cx + rcGetDirOffsetX(dir);
|
||||
const int ay = cy + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
unsigned char alid = srcReg[ai] != 0xff ? regs[srcReg[ai]].layerId : 0xff;
|
||||
// Portal mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid != alid)
|
||||
{
|
||||
portal |= (unsigned char)(1<<dir);
|
||||
// Update height so that it matches on both sides of the portal.
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
if (as.y > hmin)
|
||||
layer->heights[idx] = rcMax(layer->heights[idx], (unsigned char)(as.y - hmin));
|
||||
}
|
||||
// Valid connection mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid == alid)
|
||||
{
|
||||
const int nx = ax - borderSize;
|
||||
const int ny = ay - borderSize;
|
||||
if (nx >= 0 && ny >= 0 && nx < lw && ny < lh)
|
||||
con |= (unsigned char)(1<<dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer->cons[idx] = (portal << 4) | con;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layer->minx > layer->maxx)
|
||||
layer->minx = layer->maxx = 0;
|
||||
if (layer->miny > layer->maxy)
|
||||
layer->miny = layer->maxy = 0;
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_LAYERS);
|
||||
|
||||
return true;
|
||||
}
|
||||
713
extern/recastnavigation/Recast/Source/RecastMesh.cpp
vendored
713
extern/recastnavigation/Recast/Source/RecastMesh.cpp
vendored
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -21,9 +21,8 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastLog.h"
|
||||
#include "RecastTimer.h"
|
||||
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
struct rcEdge
|
||||
{
|
||||
@@ -32,36 +31,37 @@ struct rcEdge
|
||||
unsigned short poly[2];
|
||||
};
|
||||
|
||||
/*static */bool buildMeshAdjacency(unsigned short* polys, const int npolys,
|
||||
/*static*/ bool buildMeshAdjacency(unsigned short* polys, const int npolys,
|
||||
const int nverts, const int vertsPerPoly)
|
||||
{
|
||||
// Based on code by Eric Lengyel from:
|
||||
// http://www.terathon.com/code/edges.php
|
||||
|
||||
int maxEdgeCount = npolys*vertsPerPoly;
|
||||
unsigned short* firstEdge = new unsigned short[nverts + maxEdgeCount];
|
||||
unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP);
|
||||
if (!firstEdge)
|
||||
return false;
|
||||
unsigned short* nextEdge = firstEdge + nverts;
|
||||
int edgeCount = 0;
|
||||
|
||||
rcEdge* edges = new rcEdge[maxEdgeCount];
|
||||
rcEdge* edges = (rcEdge*)rcAlloc(sizeof(rcEdge)*maxEdgeCount, RC_ALLOC_TEMP);
|
||||
if (!edges)
|
||||
{
|
||||
rcFree(firstEdge);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nverts; i++)
|
||||
firstEdge[i] = 0xffff;
|
||||
|
||||
// Invalida indices are marked as 0xffff, the following code
|
||||
// handles them just fine.
|
||||
firstEdge[i] = RC_MESH_NULL_IDX;
|
||||
|
||||
for (int i = 0; i < npolys; ++i)
|
||||
{
|
||||
unsigned short* t = &polys[i*vertsPerPoly*2];
|
||||
for (int j = 0; j < vertsPerPoly; ++j)
|
||||
{
|
||||
if (t[j] == RC_MESH_NULL_IDX) break;
|
||||
unsigned short v0 = t[j];
|
||||
unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == 0xffff) ? t[0] : t[j+1];
|
||||
unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1];
|
||||
if (v0 < v1)
|
||||
{
|
||||
rcEdge& edge = edges[edgeCount];
|
||||
@@ -73,7 +73,7 @@ struct rcEdge
|
||||
edge.polyEdge[1] = 0;
|
||||
// Insert edge
|
||||
nextEdge[edgeCount] = firstEdge[v0];
|
||||
firstEdge[v0] = edgeCount;
|
||||
firstEdge[v0] = (unsigned short)edgeCount;
|
||||
edgeCount++;
|
||||
}
|
||||
}
|
||||
@@ -84,11 +84,12 @@ struct rcEdge
|
||||
unsigned short* t = &polys[i*vertsPerPoly*2];
|
||||
for (int j = 0; j < vertsPerPoly; ++j)
|
||||
{
|
||||
if (t[j] == RC_MESH_NULL_IDX) break;
|
||||
unsigned short v0 = t[j];
|
||||
unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == 0xffff) ? t[0] : t[j+1];
|
||||
unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1];
|
||||
if (v0 > v1)
|
||||
{
|
||||
for (unsigned short e = firstEdge[v1]; e != 0xffff; e = nextEdge[e])
|
||||
for (unsigned short e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = nextEdge[e])
|
||||
{
|
||||
rcEdge& edge = edges[e];
|
||||
if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1])
|
||||
@@ -115,8 +116,8 @@ struct rcEdge
|
||||
}
|
||||
}
|
||||
|
||||
delete [] firstEdge;
|
||||
delete [] edges;
|
||||
rcFree(firstEdge);
|
||||
rcFree(edges);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -133,8 +134,8 @@ inline int computeVertexHash(int x, int y, int z)
|
||||
return (int)(n & (VERTEX_BUCKET_COUNT-1));
|
||||
}
|
||||
|
||||
static int addVertex(unsigned short x, unsigned short y, unsigned short z,
|
||||
unsigned short* verts, int* firstVert, int* nextVert, int& nv)
|
||||
static unsigned short addVertex(unsigned short x, unsigned short y, unsigned short z,
|
||||
unsigned short* verts, int* firstVert, int* nextVert, int& nv)
|
||||
{
|
||||
int bucket = computeVertexHash(x, 0, z);
|
||||
int i = firstVert[bucket];
|
||||
@@ -143,7 +144,7 @@ static int addVertex(unsigned short x, unsigned short y, unsigned short z,
|
||||
{
|
||||
const unsigned short* v = &verts[i*3];
|
||||
if (v[0] == x && (rcAbs(v[1] - y) <= 2) && v[2] == z)
|
||||
return i;
|
||||
return (unsigned short)i;
|
||||
i = nextVert[i]; // next
|
||||
}
|
||||
|
||||
@@ -156,7 +157,7 @@ static int addVertex(unsigned short x, unsigned short y, unsigned short z,
|
||||
nextVert[i] = firstVert[bucket];
|
||||
firstVert[bucket] = i;
|
||||
|
||||
return i;
|
||||
return (unsigned short)i;
|
||||
}
|
||||
|
||||
inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
|
||||
@@ -196,7 +197,7 @@ inline bool collinear(const int* a, const int* b, const int* c)
|
||||
// Returns true iff ab properly intersects cd: they share
|
||||
// a point interior to both segments. The properness of the
|
||||
// intersection is ensured by using strict leftness.
|
||||
bool intersectProp(const int* a, const int* b, const int* c, const int* d)
|
||||
static bool intersectProp(const int* a, const int* b, const int* c, const int* d)
|
||||
{
|
||||
// Eliminate improper cases.
|
||||
if (collinear(a,b,c) || collinear(a,b,d) ||
|
||||
@@ -287,7 +288,7 @@ static bool diagonal(int i, int j, int n, const int* verts, int* indices)
|
||||
return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
|
||||
}
|
||||
|
||||
int triangulate(int n, const int* verts, int* indices, int* tris)
|
||||
static int triangulate(int n, const int* verts, int* indices, int* tris)
|
||||
{
|
||||
int ntris = 0;
|
||||
int* dst = tris;
|
||||
@@ -328,8 +329,6 @@ int triangulate(int n, const int* verts, int* indices, int* tris)
|
||||
if (mini == -1)
|
||||
{
|
||||
// Should not happen.
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "triangulate: Failed to triangulate polygon.");
|
||||
/* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
@@ -379,7 +378,7 @@ int triangulate(int n, const int* verts, int* indices, int* tris)
|
||||
static int countPolyVerts(const unsigned short* p, const int nvp)
|
||||
{
|
||||
for (int i = 0; i < nvp; ++i)
|
||||
if (p[i] == 0xffff)
|
||||
if (p[i] == RC_MESH_NULL_IDX)
|
||||
return i;
|
||||
return nvp;
|
||||
}
|
||||
@@ -454,8 +453,7 @@ static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
|
||||
return dx*dx + dy*dy;
|
||||
}
|
||||
|
||||
static void mergePolys(unsigned short* pa, unsigned short* pb,
|
||||
const unsigned short* verts, int ea, int eb,
|
||||
static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb,
|
||||
unsigned short* tmp, const int nvp)
|
||||
{
|
||||
const int na = countPolyVerts(pa, nvp);
|
||||
@@ -474,6 +472,7 @@ static void mergePolys(unsigned short* pa, unsigned short* pb,
|
||||
memcpy(pa, tmp, sizeof(unsigned short)*nvp);
|
||||
}
|
||||
|
||||
|
||||
static void pushFront(int v, int* arr, int& an)
|
||||
{
|
||||
an++;
|
||||
@@ -487,59 +486,157 @@ static void pushBack(int v, int* arr, int& an)
|
||||
an++;
|
||||
}
|
||||
|
||||
static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int maxTris)
|
||||
static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem)
|
||||
{
|
||||
unsigned short* tmpPoly;
|
||||
int ntris;
|
||||
|
||||
static const int nvp = mesh.nvp;
|
||||
|
||||
int* edges = 0;
|
||||
int nedges = 0;
|
||||
int* hole = 0;
|
||||
int nhole = 0;
|
||||
int* hreg = 0;
|
||||
int nhreg = 0;
|
||||
int* tris = 0;
|
||||
int* tverts = 0;
|
||||
int* thole = 0;
|
||||
unsigned short* polys = 0;
|
||||
unsigned short* pregs = 0;
|
||||
int npolys = 0;
|
||||
|
||||
const int nvp = mesh.nvp;
|
||||
|
||||
// Count number of polygons to remove.
|
||||
int nrem = 0;
|
||||
int numRemovedVerts = 0;
|
||||
int numTouchedVerts = 0;
|
||||
int numRemainingEdges = 0;
|
||||
for (int i = 0; i < mesh.npolys; ++i)
|
||||
{
|
||||
unsigned short* p = &mesh.polys[i*nvp*2];
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
if (p[j] == rem) { nrem++; break; }
|
||||
}
|
||||
|
||||
edges = new int[nrem*nvp*3];
|
||||
if (!edges)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", nrem*nvp*3);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
hole = new int[nrem*nvp];
|
||||
if (!hole)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", nrem*nvp);
|
||||
goto failure;
|
||||
}
|
||||
hreg = new int[nrem*nvp];
|
||||
if (!hreg)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", nrem*nvp);
|
||||
goto failure;
|
||||
const int nv = countPolyVerts(p, nvp);
|
||||
int numRemoved = 0;
|
||||
int numVerts = 0;
|
||||
for (int j = 0; j < nv; ++j)
|
||||
{
|
||||
if (p[j] == rem)
|
||||
{
|
||||
numTouchedVerts++;
|
||||
numRemoved++;
|
||||
}
|
||||
numVerts++;
|
||||
}
|
||||
if (numRemoved)
|
||||
{
|
||||
numRemovedVerts += numRemoved;
|
||||
numRemainingEdges += numVerts-(numRemoved+1);
|
||||
}
|
||||
}
|
||||
|
||||
// There would be too few edges remaining to create a polygon.
|
||||
// This can happen for example when a tip of a triangle is marked
|
||||
// as deletion, but there are no other polys that share the vertex.
|
||||
// In this case, the vertex should not be removed.
|
||||
if (numRemainingEdges <= 2)
|
||||
return false;
|
||||
|
||||
// Find edges which share the removed vertex.
|
||||
const int maxEdges = numTouchedVerts*2;
|
||||
int nedges = 0;
|
||||
rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP);
|
||||
if (!edges)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mesh.npolys; ++i)
|
||||
{
|
||||
unsigned short* p = &mesh.polys[i*nvp*2];
|
||||
const int nv = countPolyVerts(p, nvp);
|
||||
|
||||
// Collect edges which touches the removed vertex.
|
||||
for (int j = 0, k = nv-1; j < nv; k = j++)
|
||||
{
|
||||
if (p[j] == rem || p[k] == rem)
|
||||
{
|
||||
// Arrange edge so that a=rem.
|
||||
int a = p[j], b = p[k];
|
||||
if (b == rem)
|
||||
rcSwap(a,b);
|
||||
|
||||
// Check if the edge exists
|
||||
bool exists = false;
|
||||
for (int k = 0; k < nedges; ++k)
|
||||
{
|
||||
int* e = &edges[k*3];
|
||||
if (e[1] == b)
|
||||
{
|
||||
// Exists, increment vertex share count.
|
||||
e[2]++;
|
||||
exists = true;
|
||||
}
|
||||
}
|
||||
// Add new edge.
|
||||
if (!exists)
|
||||
{
|
||||
int* e = &edges[nedges*3];
|
||||
e[0] = a;
|
||||
e[1] = b;
|
||||
e[2] = 1;
|
||||
nedges++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There should be no more than 2 open edges.
|
||||
// This catches the case that two non-adjacent polygons
|
||||
// share the removed vertex. In that case, do not remove the vertex.
|
||||
int numOpenEdges = 0;
|
||||
for (int i = 0; i < nedges; ++i)
|
||||
{
|
||||
if (edges[i*3+2] < 2)
|
||||
numOpenEdges++;
|
||||
}
|
||||
if (numOpenEdges > 2)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem, const int maxTris)
|
||||
{
|
||||
const int nvp = mesh.nvp;
|
||||
|
||||
// Count number of polygons to remove.
|
||||
int numRemovedVerts = 0;
|
||||
for (int i = 0; i < mesh.npolys; ++i)
|
||||
{
|
||||
unsigned short* p = &mesh.polys[i*nvp*2];
|
||||
const int nv = countPolyVerts(p, nvp);
|
||||
for (int j = 0; j < nv; ++j)
|
||||
{
|
||||
if (p[j] == rem)
|
||||
numRemovedVerts++;
|
||||
}
|
||||
}
|
||||
|
||||
int nedges = 0;
|
||||
rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP);
|
||||
if (!edges)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4);
|
||||
return false;
|
||||
}
|
||||
|
||||
int nhole = 0;
|
||||
rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
|
||||
if (!hole)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp);
|
||||
return false;
|
||||
}
|
||||
|
||||
int nhreg = 0;
|
||||
rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
|
||||
if (!hreg)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp);
|
||||
return false;
|
||||
}
|
||||
|
||||
int nharea = 0;
|
||||
rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
|
||||
if (!harea)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mesh.npolys; ++i)
|
||||
{
|
||||
unsigned short* p = &mesh.polys[i*nvp*2];
|
||||
@@ -554,17 +651,20 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
{
|
||||
if (p[j] != rem && p[k] != rem)
|
||||
{
|
||||
int* e = &edges[nedges*3];
|
||||
int* e = &edges[nedges*4];
|
||||
e[0] = p[k];
|
||||
e[1] = p[j];
|
||||
e[2] = mesh.regs[i];
|
||||
e[3] = mesh.areas[i];
|
||||
nedges++;
|
||||
}
|
||||
}
|
||||
// Remove the polygon.
|
||||
unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2];
|
||||
memcpy(p,p2,sizeof(unsigned short)*nvp);
|
||||
memset(p+nvp,0xff,sizeof(unsigned short)*nvp);
|
||||
mesh.regs[i] = mesh.regs[mesh.npolys-1];
|
||||
mesh.areas[i] = mesh.areas[mesh.npolys-1];
|
||||
mesh.npolys--;
|
||||
--i;
|
||||
}
|
||||
@@ -589,16 +689,18 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
}
|
||||
for (int i = 0; i < nedges; ++i)
|
||||
{
|
||||
if (edges[i*3+0] > rem) edges[i*3+0]--;
|
||||
if (edges[i*3+1] > rem) edges[i*3+1]--;
|
||||
if (edges[i*4+0] > rem) edges[i*4+0]--;
|
||||
if (edges[i*4+1] > rem) edges[i*4+1]--;
|
||||
}
|
||||
|
||||
if (nedges == 0)
|
||||
return true;
|
||||
|
||||
hole[nhole] = edges[0];
|
||||
hreg[nhole] = edges[2];
|
||||
nhole++;
|
||||
// Start with one vertex, keep appending connected
|
||||
// segments to the start and end of the hole.
|
||||
pushBack(edges[0], hole, nhole);
|
||||
pushBack(edges[2], hreg, nhreg);
|
||||
pushBack(edges[3], harea, nharea);
|
||||
|
||||
while (nedges)
|
||||
{
|
||||
@@ -606,28 +708,34 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
|
||||
for (int i = 0; i < nedges; ++i)
|
||||
{
|
||||
const int ea = edges[i*3+0];
|
||||
const int eb = edges[i*3+1];
|
||||
const int r = edges[i*3+2];
|
||||
const int ea = edges[i*4+0];
|
||||
const int eb = edges[i*4+1];
|
||||
const int r = edges[i*4+2];
|
||||
const int a = edges[i*4+3];
|
||||
bool add = false;
|
||||
if (hole[0] == eb)
|
||||
{
|
||||
// The segment matches the beginning of the hole boundary.
|
||||
pushFront(ea, hole, nhole);
|
||||
pushFront(r, hreg, nhreg);
|
||||
pushFront(a, harea, nharea);
|
||||
add = true;
|
||||
}
|
||||
else if (hole[nhole-1] == ea)
|
||||
{
|
||||
// The segment matches the end of the hole boundary.
|
||||
pushBack(eb, hole, nhole);
|
||||
pushBack(r, hreg, nhreg);
|
||||
pushBack(a, harea, nharea);
|
||||
add = true;
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
// Remove edge.
|
||||
edges[i*3+0] = edges[(nedges-1)*3+0];
|
||||
edges[i*3+1] = edges[(nedges-1)*3+1];
|
||||
edges[i*3+2] = edges[(nedges-1)*3+2];
|
||||
// The edge segment was added, remove it.
|
||||
edges[i*4+0] = edges[(nedges-1)*4+0];
|
||||
edges[i*4+1] = edges[(nedges-1)*4+1];
|
||||
edges[i*4+2] = edges[(nedges-1)*4+2];
|
||||
edges[i*4+3] = edges[(nedges-1)*4+3];
|
||||
--nedges;
|
||||
match = true;
|
||||
--i;
|
||||
@@ -638,28 +746,25 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
break;
|
||||
}
|
||||
|
||||
tris = new int[nhole*3];
|
||||
rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP);
|
||||
if (!tris)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
|
||||
return false;
|
||||
}
|
||||
|
||||
tverts = new int[nhole*4];
|
||||
rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP);
|
||||
if (!tverts)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
|
||||
return false;
|
||||
}
|
||||
|
||||
thole = new int[nhole];
|
||||
rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP);
|
||||
if (!tverts)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate temp vertex array for triangulation.
|
||||
@@ -674,27 +779,37 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
}
|
||||
|
||||
// Triangulate the hole.
|
||||
ntris = triangulate(nhole, &tverts[0], &thole[0], tris);
|
||||
|
||||
// Merge the hole triangles back to polygons.
|
||||
polys = new unsigned short[(ntris+1)*nvp];
|
||||
if (!polys)
|
||||
int ntris = triangulate(nhole, &tverts[0], &thole[0], tris);
|
||||
if (ntris < 0)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
|
||||
goto failure;
|
||||
}
|
||||
pregs = new unsigned short[ntris];
|
||||
if (!pregs)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'pregs' (%d).", ntris);
|
||||
goto failure;
|
||||
ntris = -ntris;
|
||||
ctx->log(RC_LOG_WARNING, "removeVertex: triangulate() returned bad results.");
|
||||
}
|
||||
|
||||
tmpPoly = &polys[ntris*nvp];
|
||||
// Merge the hole triangles back to polygons.
|
||||
rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP);
|
||||
if (!polys)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
|
||||
return false;
|
||||
}
|
||||
rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP);
|
||||
if (!pregs)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris);
|
||||
return false;
|
||||
}
|
||||
rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP);
|
||||
if (!pregs)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned short* tmpPoly = &polys[ntris*nvp];
|
||||
|
||||
// Build initial polygons.
|
||||
int npolys = 0;
|
||||
memset(polys, 0xff, ntris*nvp*sizeof(unsigned short));
|
||||
for (int j = 0; j < ntris; ++j)
|
||||
{
|
||||
@@ -704,7 +819,8 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
polys[npolys*nvp+0] = (unsigned short)hole[t[0]];
|
||||
polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
|
||||
polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
|
||||
pregs[npolys] = hreg[t[0]];
|
||||
pregs[npolys] = (unsigned short)hreg[t[0]];
|
||||
pareas[npolys] = (unsigned char)harea[t[0]];
|
||||
npolys++;
|
||||
}
|
||||
}
|
||||
@@ -714,11 +830,11 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
// Merge polygons.
|
||||
if (nvp > 3)
|
||||
{
|
||||
while (true)
|
||||
for (;;)
|
||||
{
|
||||
// Find best polygons to merge.
|
||||
int bestMergeVal = 0;
|
||||
int bestPa, bestPb, bestEa, bestEb;
|
||||
int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
|
||||
|
||||
for (int j = 0; j < npolys-1; ++j)
|
||||
{
|
||||
@@ -744,9 +860,10 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
// Found best, merge.
|
||||
unsigned short* pa = &polys[bestPa*nvp];
|
||||
unsigned short* pb = &polys[bestPb*nvp];
|
||||
mergePolys(pa, pb, mesh.verts, bestEa, bestEb, tmpPoly, nvp);
|
||||
mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
|
||||
memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
|
||||
pregs[bestPb] = pregs[npolys-1];
|
||||
pareas[bestPb] = pareas[npolys-1];
|
||||
npolys--;
|
||||
}
|
||||
else
|
||||
@@ -766,50 +883,43 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
p[j] = polys[i*nvp+j];
|
||||
mesh.regs[mesh.npolys] = pregs[i];
|
||||
mesh.areas[mesh.npolys] = pareas[i];
|
||||
mesh.npolys++;
|
||||
if (mesh.npolys > maxTris)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "removeVertex: Too many polygons %d (max:%d).", mesh.npolys, maxTris);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] edges;
|
||||
delete [] hole;
|
||||
delete [] hreg;
|
||||
delete [] tris;
|
||||
delete [] thole;
|
||||
delete [] tverts;
|
||||
delete [] polys;
|
||||
delete [] pregs;
|
||||
|
||||
return true;
|
||||
|
||||
failure:
|
||||
delete [] edges;
|
||||
delete [] hole;
|
||||
delete [] hreg;
|
||||
delete [] tris;
|
||||
delete [] thole;
|
||||
delete [] tverts;
|
||||
delete [] polys;
|
||||
delete [] pregs;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
/// @par
|
||||
///
|
||||
/// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper
|
||||
/// limit must be retricted to <= #DT_VERTS_PER_POLYGON.
|
||||
///
|
||||
/// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig
|
||||
bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh)
|
||||
{
|
||||
unsigned short* tmpPoly;
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcTimeVal endTime;
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_POLYMESH);
|
||||
|
||||
vcopy(mesh.bmin, cset.bmin);
|
||||
vcopy(mesh.bmax, cset.bmax);
|
||||
rcVcopy(mesh.bmin, cset.bmin);
|
||||
rcVcopy(mesh.bmax, cset.bmax);
|
||||
mesh.cs = cset.cs;
|
||||
mesh.ch = cset.ch;
|
||||
mesh.borderSize = cset.borderSize;
|
||||
|
||||
int maxVertices = 0;
|
||||
int maxTris = 0;
|
||||
int maxVertsPerCont = 0;
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
// Skip null contours.
|
||||
if (cset.conts[i].nverts < 3) continue;
|
||||
maxVertices += cset.conts[i].nverts;
|
||||
maxTris += cset.conts[i].nverts - 2;
|
||||
maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts);
|
||||
@@ -817,103 +927,95 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
|
||||
if (maxVertices >= 0xfffe)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char* vflags = 0;
|
||||
int* nextVert = 0;
|
||||
int* firstVert = 0;
|
||||
int* indices = 0;
|
||||
int* tris = 0;
|
||||
unsigned short* polys = 0;
|
||||
|
||||
vflags = new unsigned char[maxVertices];
|
||||
|
||||
rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP);
|
||||
if (!vflags)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
|
||||
return false;
|
||||
}
|
||||
memset(vflags, 0, maxVertices);
|
||||
|
||||
mesh.verts = new unsigned short[maxVertices*3];
|
||||
mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertices*3, RC_ALLOC_PERM);
|
||||
if (!mesh.verts)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
|
||||
return false;
|
||||
}
|
||||
mesh.polys = new unsigned short[maxTris*nvp*2];
|
||||
mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2, RC_ALLOC_PERM);
|
||||
if (!mesh.polys)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2);
|
||||
return false;
|
||||
}
|
||||
mesh.regs = new unsigned short[maxTris];
|
||||
mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris, RC_ALLOC_PERM);
|
||||
if (!mesh.regs)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris);
|
||||
return false;
|
||||
}
|
||||
mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris, RC_ALLOC_PERM);
|
||||
if (!mesh.areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' (%d).", maxTris);
|
||||
return false;
|
||||
}
|
||||
|
||||
mesh.nverts = 0;
|
||||
mesh.npolys = 0;
|
||||
mesh.nvp = nvp;
|
||||
mesh.maxpolys = maxTris;
|
||||
|
||||
memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3);
|
||||
memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2);
|
||||
memset(mesh.regs, 0, sizeof(unsigned short)*maxTris);
|
||||
memset(mesh.areas, 0, sizeof(unsigned char)*maxTris);
|
||||
|
||||
nextVert = new int[maxVertices];
|
||||
rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP);
|
||||
if (!nextVert)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
|
||||
return false;
|
||||
}
|
||||
memset(nextVert, 0, sizeof(int)*maxVertices);
|
||||
|
||||
firstVert = new int[VERTEX_BUCKET_COUNT];
|
||||
rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
|
||||
if (!firstVert)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
|
||||
firstVert[i] = -1;
|
||||
|
||||
indices = new int[maxVertsPerCont];
|
||||
rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP);
|
||||
if (!indices)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
|
||||
return false;
|
||||
}
|
||||
tris = new int[maxVertsPerCont*3];
|
||||
rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP);
|
||||
if (!tris)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
|
||||
return false;
|
||||
}
|
||||
polys = new unsigned short[(maxVertsPerCont+1)*nvp];
|
||||
rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP);
|
||||
if (!polys)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
|
||||
return false;
|
||||
}
|
||||
tmpPoly = &polys[maxVertsPerCont*nvp];
|
||||
unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp];
|
||||
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
rcContour& cont = cset.conts[i];
|
||||
|
||||
// Skip empty contours.
|
||||
// Skip null contours.
|
||||
if (cont.nverts < 3)
|
||||
continue;
|
||||
|
||||
@@ -925,20 +1027,20 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
if (ntris <= 0)
|
||||
{
|
||||
// Bad triangulation, should not happen.
|
||||
/* for (int k = 0; k < cont.nverts; ++k)
|
||||
/* printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]);
|
||||
printf("\tconst float cs = %ff;\n", cset.cs);
|
||||
printf("\tconst float ch = %ff;\n", cset.ch);
|
||||
printf("\tconst int verts[] = {\n");
|
||||
for (int k = 0; k < cont.nverts; ++k)
|
||||
{
|
||||
const int* v = &cont.verts[k*4];
|
||||
printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]);
|
||||
if (nBadPos < 100)
|
||||
{
|
||||
badPos[nBadPos*3+0] = v[0];
|
||||
badPos[nBadPos*3+1] = v[1];
|
||||
badPos[nBadPos*3+2] = v[2];
|
||||
nBadPos++;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/
|
||||
ctx->log(RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour %d.", i);
|
||||
ntris = -ntris;
|
||||
}
|
||||
|
||||
// Add and merge vertices.
|
||||
for (int j = 0; j < cont.nverts; ++j)
|
||||
{
|
||||
@@ -972,11 +1074,11 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
// Merge polygons.
|
||||
if (nvp > 3)
|
||||
{
|
||||
while (true)
|
||||
for(;;)
|
||||
{
|
||||
// Find best polygons to merge.
|
||||
int bestMergeVal = 0;
|
||||
int bestPa, bestPb, bestEa, bestEb;
|
||||
int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
|
||||
|
||||
for (int j = 0; j < npolys-1; ++j)
|
||||
{
|
||||
@@ -1002,7 +1104,7 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
// Found best, merge.
|
||||
unsigned short* pa = &polys[bestPa*nvp];
|
||||
unsigned short* pb = &polys[bestPb*nvp];
|
||||
mergePolys(pa, pb, mesh.verts, bestEa, bestEb, tmpPoly, nvp);
|
||||
mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
|
||||
memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
|
||||
npolys--;
|
||||
}
|
||||
@@ -1014,7 +1116,6 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Store polygons.
|
||||
for (int j = 0; j < npolys; ++j)
|
||||
{
|
||||
@@ -1023,7 +1124,13 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
for (int k = 0; k < nvp; ++k)
|
||||
p[k] = q[k];
|
||||
mesh.regs[mesh.npolys] = cont.reg;
|
||||
mesh.areas[mesh.npolys] = cont.area;
|
||||
mesh.npolys++;
|
||||
if (mesh.npolys > maxTris)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons %d (max:%d).", mesh.npolys, maxTris);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1033,131 +1140,174 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
|
||||
{
|
||||
if (vflags[i])
|
||||
{
|
||||
if (!removeVertex(mesh, i, maxTris))
|
||||
goto failure;
|
||||
for (int j = i; j < mesh.nverts-1; ++j)
|
||||
if (!canRemoveVertex(ctx, mesh, (unsigned short)i))
|
||||
continue;
|
||||
if (!removeVertex(ctx, mesh, (unsigned short)i, maxTris))
|
||||
{
|
||||
// Failed to remove vertex
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex %d.", i);
|
||||
return false;
|
||||
}
|
||||
// Remove vertex
|
||||
// Note: mesh.nverts is already decremented inside removeVertex()!
|
||||
for (int j = i; j < mesh.nverts; ++j)
|
||||
vflags[j] = vflags[j+1];
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] vflags;
|
||||
delete [] firstVert;
|
||||
delete [] nextVert;
|
||||
delete [] indices;
|
||||
delete [] tris;
|
||||
|
||||
// Calculate adjacency.
|
||||
if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp))
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed.");
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
endTime = rcGetPerformanceTimer();
|
||||
// Find portal edges
|
||||
if (mesh.borderSize > 0)
|
||||
{
|
||||
const int w = cset.width;
|
||||
const int h = cset.height;
|
||||
for (int i = 0; i < mesh.npolys; ++i)
|
||||
{
|
||||
unsigned short* p = &mesh.polys[i*2*nvp];
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == RC_MESH_NULL_IDX) break;
|
||||
// Skip connected edges.
|
||||
if (p[nvp+j] != RC_MESH_NULL_IDX)
|
||||
continue;
|
||||
int nj = j+1;
|
||||
if (nj >= nvp || p[nj] == RC_MESH_NULL_IDX) nj = 0;
|
||||
const unsigned short* va = &mesh.verts[p[j]*3];
|
||||
const unsigned short* vb = &mesh.verts[p[nj]*3];
|
||||
|
||||
if ((int)va[0] == 0 && (int)vb[0] == 0)
|
||||
p[nvp+j] = 0x8000 | 0;
|
||||
else if ((int)va[2] == h && (int)vb[2] == h)
|
||||
p[nvp+j] = 0x8000 | 1;
|
||||
else if ((int)va[0] == w && (int)vb[0] == w)
|
||||
p[nvp+j] = 0x8000 | 2;
|
||||
else if ((int)va[2] == 0 && (int)vb[2] == 0)
|
||||
p[nvp+j] = 0x8000 | 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Just allocate the mesh flags array. The user is resposible to fill it.
|
||||
mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM);
|
||||
if (!mesh.flags)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' (%d).", mesh.npolys);
|
||||
return false;
|
||||
}
|
||||
memset(mesh.flags, 0, sizeof(unsigned short) * mesh.npolys);
|
||||
|
||||
// if (rcGetLog())
|
||||
// rcGetLog()->log(RC_LOG_PROGRESS, "Build polymesh: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->buildPolymesh += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
if (mesh.nverts > 0xffff)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
|
||||
}
|
||||
if (mesh.npolys > 0xffff)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_POLYMESH);
|
||||
|
||||
return true;
|
||||
|
||||
failure:
|
||||
delete [] vflags;
|
||||
delete [] tmpPoly;
|
||||
delete [] firstVert;
|
||||
delete [] nextVert;
|
||||
delete [] indices;
|
||||
delete [] tris;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
|
||||
/// @see rcAllocPolyMesh, rcPolyMesh
|
||||
bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
if (!nmeshes || !meshes)
|
||||
return true;
|
||||
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcTimeVal endTime;
|
||||
|
||||
int* nextVert = 0;
|
||||
int* firstVert = 0;
|
||||
unsigned short* vremap = 0;
|
||||
ctx->startTimer(RC_TIMER_MERGE_POLYMESH);
|
||||
|
||||
mesh.nvp = meshes[0]->nvp;
|
||||
mesh.cs = meshes[0]->cs;
|
||||
mesh.ch = meshes[0]->ch;
|
||||
vcopy(mesh.bmin, meshes[0]->bmin);
|
||||
vcopy(mesh.bmax, meshes[0]->bmax);
|
||||
rcVcopy(mesh.bmin, meshes[0]->bmin);
|
||||
rcVcopy(mesh.bmax, meshes[0]->bmax);
|
||||
|
||||
int maxVerts = 0;
|
||||
int maxPolys = 0;
|
||||
int maxVertsPerMesh = 0;
|
||||
for (int i = 0; i < nmeshes; ++i)
|
||||
{
|
||||
vmin(mesh.bmin, meshes[i]->bmin);
|
||||
vmax(mesh.bmax, meshes[i]->bmax);
|
||||
rcVmin(mesh.bmin, meshes[i]->bmin);
|
||||
rcVmax(mesh.bmax, meshes[i]->bmax);
|
||||
maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts);
|
||||
maxVerts += meshes[i]->nverts;
|
||||
maxPolys += meshes[i]->npolys;
|
||||
}
|
||||
|
||||
mesh.nverts = 0;
|
||||
mesh.verts = new unsigned short[maxVerts*3];
|
||||
mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVerts*3, RC_ALLOC_PERM);
|
||||
if (!mesh.verts)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3);
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3);
|
||||
return false;
|
||||
}
|
||||
|
||||
mesh.npolys = 0;
|
||||
mesh.polys = new unsigned short[maxPolys*2*mesh.nvp];
|
||||
mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM);
|
||||
if (!mesh.polys)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp);
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp);
|
||||
return false;
|
||||
}
|
||||
memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp);
|
||||
|
||||
mesh.regs = new unsigned short[maxPolys];
|
||||
mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM);
|
||||
if (!mesh.regs)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys);
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys);
|
||||
return false;
|
||||
}
|
||||
memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys);
|
||||
|
||||
mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxPolys, RC_ALLOC_PERM);
|
||||
if (!mesh.areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' (%d).", maxPolys);
|
||||
return false;
|
||||
}
|
||||
memset(mesh.areas, 0, sizeof(unsigned char)*maxPolys);
|
||||
|
||||
mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM);
|
||||
if (!mesh.flags)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' (%d).", maxPolys);
|
||||
return false;
|
||||
}
|
||||
memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys);
|
||||
|
||||
nextVert = new int[maxVerts];
|
||||
rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP);
|
||||
if (!nextVert)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts);
|
||||
return false;
|
||||
}
|
||||
memset(nextVert, 0, sizeof(int)*maxVerts);
|
||||
|
||||
firstVert = new int[VERTEX_BUCKET_COUNT];
|
||||
rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
|
||||
if (!firstVert)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
|
||||
firstVert[i] = -1;
|
||||
|
||||
vremap = new unsigned short[maxVertsPerMesh];
|
||||
rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM);
|
||||
if (!vremap)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh);
|
||||
return false;
|
||||
}
|
||||
memset(nextVert, 0, sizeof(int)*maxVerts);
|
||||
|
||||
@@ -1172,7 +1322,7 @@ bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
|
||||
{
|
||||
unsigned short* v = &pmesh->verts[j*3];
|
||||
vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz,
|
||||
mesh.verts, firstVert, nextVert, mesh.nverts);
|
||||
mesh.verts, firstVert, nextVert, mesh.nverts);
|
||||
}
|
||||
|
||||
for (int j = 0; j < pmesh->npolys; ++j)
|
||||
@@ -1180,10 +1330,12 @@ bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
|
||||
unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp];
|
||||
unsigned short* src = &pmesh->polys[j*2*mesh.nvp];
|
||||
mesh.regs[mesh.npolys] = pmesh->regs[j];
|
||||
mesh.areas[mesh.npolys] = pmesh->areas[j];
|
||||
mesh.flags[mesh.npolys] = pmesh->flags[j];
|
||||
mesh.npolys++;
|
||||
for (int k = 0; k < mesh.nvp; ++k)
|
||||
{
|
||||
if (src[k] == 0xffff) break;
|
||||
if (src[k] == RC_MESH_NULL_IDX) break;
|
||||
tgt[k] = vremap[src[k]];
|
||||
}
|
||||
}
|
||||
@@ -1192,27 +1344,20 @@ bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
|
||||
// Calculate adjacency.
|
||||
if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp))
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed.");
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
delete [] firstVert;
|
||||
delete [] nextVert;
|
||||
delete [] vremap;
|
||||
if (mesh.nverts > 0xffff)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
|
||||
}
|
||||
if (mesh.npolys > 0xffff)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
|
||||
}
|
||||
|
||||
endTime = rcGetPerformanceTimer();
|
||||
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->mergePolyMesh += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
ctx->stopTimer(RC_TIMER_MERGE_POLYMESH);
|
||||
|
||||
return true;
|
||||
|
||||
failure:
|
||||
delete [] firstVert;
|
||||
delete [] nextVert;
|
||||
delete [] vremap;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -23,248 +23,75 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastLog.h"
|
||||
#include "RecastTimer.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
static const unsigned RC_UNSET_HEIGHT = 0xffff;
|
||||
|
||||
struct rcHeightPatch
|
||||
{
|
||||
inline rcHeightPatch() : data(0) {}
|
||||
inline ~rcHeightPatch() { delete [] data; }
|
||||
inline rcHeightPatch() : data(0), xmin(0), ymin(0), width(0), height(0) {}
|
||||
inline ~rcHeightPatch() { rcFree(data); }
|
||||
unsigned short* data;
|
||||
int xmin, ymin, width, height;
|
||||
};
|
||||
|
||||
|
||||
static int circumCircle(const float xp, const float yp,
|
||||
const float x1, const float y1,
|
||||
const float x2, const float y2,
|
||||
const float x3, const float y3,
|
||||
float& xc, float& yc, float& rsqr)
|
||||
{
|
||||
static const float EPSILON = 1e-6f;
|
||||
|
||||
const float fabsy1y2 = rcAbs(y1-y2);
|
||||
const float fabsy2y3 = rcAbs(y2-y3);
|
||||
|
||||
/* Check for coincident points */
|
||||
if (fabsy1y2 < EPSILON && fabsy2y3 < EPSILON)
|
||||
return 0;
|
||||
|
||||
if (fabsy1y2 < EPSILON)
|
||||
{
|
||||
const float m2 = - (x3-x2) / (y3-y2);
|
||||
const float mx2 = (x2 + x3) / 2.0f;
|
||||
const float my2 = (y2 + y3) / 2.0f;
|
||||
xc = (x2 + x1) / 2.0f;
|
||||
yc = m2 * (xc - mx2) + my2;
|
||||
}
|
||||
else if (fabsy2y3 < EPSILON)
|
||||
{
|
||||
const float m1 = - (x2-x1) / (y2-y1);
|
||||
const float mx1 = (x1 + x2) / 2.0f;
|
||||
const float my1 = (y1 + y2) / 2.0f;
|
||||
xc = (x3 + x2) / 2.0f;
|
||||
yc = m1 * (xc - mx1) + my1;
|
||||
}
|
||||
else
|
||||
{
|
||||
const float m1 = - (x2-x1) / (y2-y1);
|
||||
const float m2 = - (x3-x2) / (y3-y2);
|
||||
const float mx1 = (x1 + x2) / 2.0f;
|
||||
const float mx2 = (x2 + x3) / 2.0f;
|
||||
const float my1 = (y1 + y2) / 2.0f;
|
||||
const float my2 = (y2 + y3) / 2.0f;
|
||||
xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
|
||||
if (fabsy1y2 > fabsy2y3)
|
||||
yc = m1 * (xc - mx1) + my1;
|
||||
else
|
||||
yc = m2 * (xc - mx2) + my2;
|
||||
}
|
||||
|
||||
float dx,dy;
|
||||
|
||||
dx = x2 - xc;
|
||||
dy = y2 - yc;
|
||||
rsqr = dx*dx + dy*dy;
|
||||
|
||||
dx = xp - xc;
|
||||
dy = yp - yc;
|
||||
const float drsqr = dx*dx + dy*dy;
|
||||
|
||||
return (drsqr <= rsqr) ? 1 : 0;
|
||||
}
|
||||
|
||||
static float *_qsort_verts;
|
||||
static int ptcmp(const void *v1, const void *v2)
|
||||
{
|
||||
const float* p1 = &_qsort_verts[(*(const int*)v1)*3];
|
||||
const float* p2 = &_qsort_verts[(*(const int*)v2)*3];
|
||||
if (p1[0] < p2[0])
|
||||
return -1;
|
||||
else if (p1[0] > p2[0])
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Based on Paul Bourke's triangulate.c
|
||||
// http://astronomy.swin.edu.au/~pbourke/terrain/triangulate/triangulate.c
|
||||
static void delaunay(const int nv, float *verts, rcIntArray& idx, rcIntArray& tris, rcIntArray& edges)
|
||||
{
|
||||
// Sort vertices
|
||||
idx.resize(nv);
|
||||
for (int i = 0; i < nv; ++i)
|
||||
idx[i] = i;
|
||||
_qsort_verts = verts;
|
||||
qsort(&idx[0], idx.size(), sizeof(int), ptcmp);
|
||||
|
||||
// Find the maximum and minimum vertex bounds.
|
||||
// This is to allow calculation of the bounding triangle
|
||||
float xmin = verts[0];
|
||||
float ymin = verts[2];
|
||||
float xmax = xmin;
|
||||
float ymax = ymin;
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
xmin = rcMin(xmin, verts[i*3+0]);
|
||||
xmax = rcMax(xmax, verts[i*3+0]);
|
||||
ymin = rcMin(ymin, verts[i*3+2]);
|
||||
ymax = rcMax(ymax, verts[i*3+2]);
|
||||
}
|
||||
float dx = xmax - xmin;
|
||||
float dy = ymax - ymin;
|
||||
float dmax = (dx > dy) ? dx : dy;
|
||||
float xmid = (xmax + xmin) / 2.0f;
|
||||
float ymid = (ymax + ymin) / 2.0f;
|
||||
|
||||
// Set up the supertriangle
|
||||
// This is a triangle which encompasses all the sample points.
|
||||
// The supertriangle coordinates are added to the end of the
|
||||
// vertex list. The supertriangle is the first triangle in
|
||||
// the triangle list.
|
||||
float sv[3*3];
|
||||
|
||||
sv[0] = xmid - 20 * dmax;
|
||||
sv[1] = 0;
|
||||
sv[2] = ymid - dmax;
|
||||
|
||||
sv[3] = xmid;
|
||||
sv[4] = 0;
|
||||
sv[5] = ymid + 20 * dmax;
|
||||
|
||||
sv[6] = xmid + 20 * dmax;
|
||||
sv[7] = 0;
|
||||
sv[8] = ymid - dmax;
|
||||
|
||||
tris.push(-3);
|
||||
tris.push(-2);
|
||||
tris.push(-1);
|
||||
tris.push(0); // not completed
|
||||
|
||||
for (int i = 0; i < nv; ++i)
|
||||
{
|
||||
const float xp = verts[idx[i]*3+0];
|
||||
const float yp = verts[idx[i]*3+2];
|
||||
|
||||
edges.resize(0);
|
||||
|
||||
// Set up the edge buffer.
|
||||
// If the point (xp,yp) lies inside the circumcircle then the
|
||||
// three edges of that triangle are added to the edge buffer
|
||||
// and that triangle is removed.
|
||||
for (int j = 0; j < tris.size()/4; ++j)
|
||||
{
|
||||
int* t = &tris[j*4];
|
||||
if (t[3]) // completed?
|
||||
continue;
|
||||
const float* v1 = t[0] < 0 ? &sv[(t[0]+3)*3] : &verts[idx[t[0]]*3];
|
||||
const float* v2 = t[1] < 0 ? &sv[(t[1]+3)*3] : &verts[idx[t[1]]*3];
|
||||
const float* v3 = t[2] < 0 ? &sv[(t[2]+3)*3] : &verts[idx[t[2]]*3];
|
||||
float xc,yc,rsqr;
|
||||
int inside = circumCircle(xp,yp, v1[0],v1[2], v2[0],v2[2], v3[0],v3[2], xc,yc,rsqr);
|
||||
if (xc < xp && rcSqr(xp-xc) > rsqr)
|
||||
t[3] = 1;
|
||||
if (inside)
|
||||
{
|
||||
// Collect triangle edges.
|
||||
edges.push(t[0]);
|
||||
edges.push(t[1]);
|
||||
edges.push(t[1]);
|
||||
edges.push(t[2]);
|
||||
edges.push(t[2]);
|
||||
edges.push(t[0]);
|
||||
// Remove triangle j.
|
||||
t[0] = tris[tris.size()-4];
|
||||
t[1] = tris[tris.size()-3];
|
||||
t[2] = tris[tris.size()-2];
|
||||
t[3] = tris[tris.size()-1];
|
||||
tris.resize(tris.size()-4);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicate edges.
|
||||
const int ne = edges.size()/2;
|
||||
for (int j = 0; j < ne-1; ++j)
|
||||
{
|
||||
for (int k = j+1; k < ne; ++k)
|
||||
{
|
||||
// Dupe?, make null.
|
||||
if ((edges[j*2+0] == edges[k*2+1]) && (edges[j*2+1] == edges[k*2+0]))
|
||||
{
|
||||
edges[j*2+0] = 0;
|
||||
edges[j*2+1] = 0;
|
||||
edges[k*2+0] = 0;
|
||||
edges[k*2+1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Form new triangles for the current point
|
||||
// Skipping over any null.
|
||||
// All edges are arranged in clockwise order.
|
||||
for (int j = 0; j < ne; ++j)
|
||||
{
|
||||
if (edges[j*2+0] == edges[j*2+1]) continue;
|
||||
tris.push(edges[j*2+0]);
|
||||
tris.push(edges[j*2+1]);
|
||||
tris.push(i);
|
||||
tris.push(0); // not completed
|
||||
}
|
||||
}
|
||||
|
||||
// Remove triangles with supertriangle vertices
|
||||
// These are triangles which have a vertex number greater than nv
|
||||
for (int i = 0; i < tris.size()/4; ++i)
|
||||
{
|
||||
int* t = &tris[i*4];
|
||||
if (t[0] < 0 || t[1] < 0 || t[2] < 0)
|
||||
{
|
||||
t[0] = tris[tris.size()-4];
|
||||
t[1] = tris[tris.size()-3];
|
||||
t[2] = tris[tris.size()-2];
|
||||
t[3] = tris[tris.size()-1];
|
||||
tris.resize(tris.size()-4);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
// Triangle vertices are pointing to sorted vertices, remap indices.
|
||||
for (int i = 0; i < tris.size(); ++i)
|
||||
tris[i] = idx[tris[i]];
|
||||
}
|
||||
|
||||
inline float vdot2(const float* a, const float* b)
|
||||
{
|
||||
return a[0]*b[0] + a[2]*b[2];
|
||||
}
|
||||
|
||||
inline float vdistSq2(const float* p, const float* q)
|
||||
{
|
||||
const float dx = q[0] - p[0];
|
||||
const float dy = q[2] - p[2];
|
||||
return dx*dx + dy*dy;
|
||||
}
|
||||
|
||||
inline float vdist2(const float* p, const float* q)
|
||||
{
|
||||
return sqrtf(vdistSq2(p,q));
|
||||
}
|
||||
|
||||
inline float vcross2(const float* p1, const float* p2, const float* p3)
|
||||
{
|
||||
const float u1 = p2[0] - p1[0];
|
||||
const float v1 = p2[2] - p1[2];
|
||||
const float u2 = p3[0] - p1[0];
|
||||
const float v2 = p3[2] - p1[2];
|
||||
return u1 * v2 - v1 * u2;
|
||||
}
|
||||
|
||||
static bool circumCircle(const float* p1, const float* p2, const float* p3,
|
||||
float* c, float& r)
|
||||
{
|
||||
static const float EPS = 1e-6f;
|
||||
|
||||
const float cp = vcross2(p1, p2, p3);
|
||||
if (fabsf(cp) > EPS)
|
||||
{
|
||||
const float p1Sq = vdot2(p1,p1);
|
||||
const float p2Sq = vdot2(p2,p2);
|
||||
const float p3Sq = vdot2(p3,p3);
|
||||
c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp);
|
||||
c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp);
|
||||
r = vdist2(c, p1);
|
||||
return true;
|
||||
}
|
||||
|
||||
c[0] = p1[0];
|
||||
c[2] = p1[2];
|
||||
r = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static float distPtTri(const float* p, const float* a, const float* b, const float* c)
|
||||
{
|
||||
float v0[3], v1[3], v2[3];
|
||||
vsub(v0, c,a);
|
||||
vsub(v1, b,a);
|
||||
vsub(v2, p,a);
|
||||
rcVsub(v0, c,a);
|
||||
rcVsub(v1, b,a);
|
||||
rcVsub(v2, p,a);
|
||||
|
||||
const float dot00 = vdot2(v0, v0);
|
||||
const float dot01 = vdot2(v0, v1);
|
||||
@@ -273,15 +100,15 @@ static float distPtTri(const float* p, const float* a, const float* b, const flo
|
||||
const float dot12 = vdot2(v1, v2);
|
||||
|
||||
// Compute barycentric coordinates
|
||||
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
|
||||
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
|
||||
const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||
|
||||
// If point lies inside the triangle, return interpolated y-coord.
|
||||
static const float EPS = 1e-4f;
|
||||
if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
|
||||
{
|
||||
float y = a[1] + v0[1]*u + v1[1]*v;
|
||||
const float y = a[1] + v0[1]*u + v1[1]*v;
|
||||
return fabsf(y-p[1]);
|
||||
}
|
||||
return FLT_MAX;
|
||||
@@ -332,7 +159,7 @@ static float distancePtSeg2d(const float* pt, const float* p, const float* q)
|
||||
return dx*dx + dz*dz;
|
||||
}
|
||||
|
||||
static float distToTriMesh(const float* p, const float* verts, int nverts, const int* tris, int ntris)
|
||||
static float distToTriMesh(const float* p, const float* verts, const int /*nverts*/, const int* tris, const int ntris)
|
||||
{
|
||||
float dmin = FLT_MAX;
|
||||
for (int i = 0; i < ntris; ++i)
|
||||
@@ -366,35 +193,335 @@ static float distToPoly(int nvert, const float* verts, const float* p)
|
||||
}
|
||||
|
||||
|
||||
static unsigned short getHeight(const float* pos, const float* bmin, const float ics, const rcHeightPatch& hp)
|
||||
static unsigned short getHeight(const float fx, const float fy, const float fz,
|
||||
const float /*cs*/, const float ics, const float ch,
|
||||
const rcHeightPatch& hp)
|
||||
{
|
||||
int ix = (int)floorf((pos[0]-bmin[0])*ics + 0.01f);
|
||||
int iz = (int)floorf((pos[2]-bmin[2])*ics + 0.01f);
|
||||
int ix = (int)floorf(fx*ics + 0.01f);
|
||||
int iz = (int)floorf(fz*ics + 0.01f);
|
||||
ix = rcClamp(ix-hp.xmin, 0, hp.width);
|
||||
iz = rcClamp(iz-hp.ymin, 0, hp.height);
|
||||
unsigned short h = hp.data[ix+iz*hp.width];
|
||||
if (h == RC_UNSET_HEIGHT)
|
||||
{
|
||||
// Special case when data might be bad.
|
||||
// Find nearest neighbour pixel which has valid height.
|
||||
const int off[8*2] = { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1};
|
||||
float dmin = FLT_MAX;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
const int nx = ix+off[i*2+0];
|
||||
const int nz = iz+off[i*2+1];
|
||||
if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue;
|
||||
const unsigned short nh = hp.data[nx+nz*hp.width];
|
||||
if (nh == RC_UNSET_HEIGHT) continue;
|
||||
|
||||
const float d = fabsf(nh*ch - fy);
|
||||
if (d < dmin)
|
||||
{
|
||||
h = nh;
|
||||
dmin = d;
|
||||
}
|
||||
|
||||
/* const float dx = (nx+0.5f)*cs - fx;
|
||||
const float dz = (nz+0.5f)*cs - fz;
|
||||
const float d = dx*dx+dz*dz;
|
||||
if (d < dmin)
|
||||
{
|
||||
h = nh;
|
||||
dmin = d;
|
||||
} */
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static bool buildPolyDetail(const float* in, const int nin, unsigned short reg,
|
||||
|
||||
enum EdgeValues
|
||||
{
|
||||
UNDEF = -1,
|
||||
HULL = -2,
|
||||
};
|
||||
|
||||
static int findEdge(const int* edges, int nedges, int s, int t)
|
||||
{
|
||||
for (int i = 0; i < nedges; i++)
|
||||
{
|
||||
const int* e = &edges[i*4];
|
||||
if ((e[0] == s && e[1] == t) || (e[0] == t && e[1] == s))
|
||||
return i;
|
||||
}
|
||||
return UNDEF;
|
||||
}
|
||||
|
||||
static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, int s, int t, int l, int r)
|
||||
{
|
||||
if (nedges >= maxEdges)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "addEdge: Too many edges (%d/%d).", nedges, maxEdges);
|
||||
return UNDEF;
|
||||
}
|
||||
|
||||
// Add edge if not already in the triangulation.
|
||||
int e = findEdge(edges, nedges, s, t);
|
||||
if (e == UNDEF)
|
||||
{
|
||||
int* e = &edges[nedges*4];
|
||||
e[0] = s;
|
||||
e[1] = t;
|
||||
e[2] = l;
|
||||
e[3] = r;
|
||||
return nedges++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
static void updateLeftFace(int* e, int s, int t, int f)
|
||||
{
|
||||
if (e[0] == s && e[1] == t && e[2] == UNDEF)
|
||||
e[2] = f;
|
||||
else if (e[1] == s && e[0] == t && e[3] == UNDEF)
|
||||
e[3] = f;
|
||||
}
|
||||
|
||||
static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d)
|
||||
{
|
||||
const float a1 = vcross2(a, b, d);
|
||||
const float a2 = vcross2(a, b, c);
|
||||
if (a1*a2 < 0.0f)
|
||||
{
|
||||
float a3 = vcross2(c, d, a);
|
||||
float a4 = a3 + a2 - a1;
|
||||
if (a3 * a4 < 0.0f)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, int t1)
|
||||
{
|
||||
for (int i = 0; i < nedges; ++i)
|
||||
{
|
||||
const int s0 = edges[i*4+0];
|
||||
const int t0 = edges[i*4+1];
|
||||
// Same or connected edges do not overlap.
|
||||
if (s0 == s1 || s0 == t1 || t0 == s1 || t0 == t1)
|
||||
continue;
|
||||
if (overlapSegSeg2d(&pts[s0*3],&pts[t0*3], &pts[s1*3],&pts[t1*3]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e)
|
||||
{
|
||||
static const float EPS = 1e-5f;
|
||||
|
||||
int* edge = &edges[e*4];
|
||||
|
||||
// Cache s and t.
|
||||
int s,t;
|
||||
if (edge[2] == UNDEF)
|
||||
{
|
||||
s = edge[0];
|
||||
t = edge[1];
|
||||
}
|
||||
else if (edge[3] == UNDEF)
|
||||
{
|
||||
s = edge[1];
|
||||
t = edge[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Edge already completed.
|
||||
return;
|
||||
}
|
||||
|
||||
// Find best point on left of edge.
|
||||
int pt = npts;
|
||||
float c[3] = {0,0,0};
|
||||
float r = -1;
|
||||
for (int u = 0; u < npts; ++u)
|
||||
{
|
||||
if (u == s || u == t) continue;
|
||||
if (vcross2(&pts[s*3], &pts[t*3], &pts[u*3]) > EPS)
|
||||
{
|
||||
if (r < 0)
|
||||
{
|
||||
// The circle is not updated yet, do it now.
|
||||
pt = u;
|
||||
circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
|
||||
continue;
|
||||
}
|
||||
const float d = vdist2(c, &pts[u*3]);
|
||||
const float tol = 0.001f;
|
||||
if (d > r*(1+tol))
|
||||
{
|
||||
// Outside current circumcircle, skip.
|
||||
continue;
|
||||
}
|
||||
else if (d < r*(1-tol))
|
||||
{
|
||||
// Inside safe circumcircle, update circle.
|
||||
pt = u;
|
||||
circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inside epsilon circum circle, do extra tests to make sure the edge is valid.
|
||||
// s-u and t-u cannot overlap with s-pt nor t-pt if they exists.
|
||||
if (overlapEdges(pts, edges, nedges, s,u))
|
||||
continue;
|
||||
if (overlapEdges(pts, edges, nedges, t,u))
|
||||
continue;
|
||||
// Edge is valid.
|
||||
pt = u;
|
||||
circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new triangle or update edge info if s-t is on hull.
|
||||
if (pt < npts)
|
||||
{
|
||||
// Update face information of edge being completed.
|
||||
updateLeftFace(&edges[e*4], s, t, nfaces);
|
||||
|
||||
// Add new edge or update face info of old edge.
|
||||
e = findEdge(edges, nedges, pt, s);
|
||||
if (e == UNDEF)
|
||||
addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF);
|
||||
else
|
||||
updateLeftFace(&edges[e*4], pt, s, nfaces);
|
||||
|
||||
// Add new edge or update face info of old edge.
|
||||
e = findEdge(edges, nedges, t, pt);
|
||||
if (e == UNDEF)
|
||||
addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF);
|
||||
else
|
||||
updateLeftFace(&edges[e*4], t, pt, nfaces);
|
||||
|
||||
nfaces++;
|
||||
}
|
||||
else
|
||||
{
|
||||
updateLeftFace(&edges[e*4], s, t, HULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void delaunayHull(rcContext* ctx, const int npts, const float* pts,
|
||||
const int nhull, const int* hull,
|
||||
rcIntArray& tris, rcIntArray& edges)
|
||||
{
|
||||
int nfaces = 0;
|
||||
int nedges = 0;
|
||||
const int maxEdges = npts*10;
|
||||
edges.resize(maxEdges*4);
|
||||
|
||||
for (int i = 0, j = nhull-1; i < nhull; j=i++)
|
||||
addEdge(ctx, &edges[0], nedges, maxEdges, hull[j],hull[i], HULL, UNDEF);
|
||||
|
||||
int currentEdge = 0;
|
||||
while (currentEdge < nedges)
|
||||
{
|
||||
if (edges[currentEdge*4+2] == UNDEF)
|
||||
completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
|
||||
if (edges[currentEdge*4+3] == UNDEF)
|
||||
completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
|
||||
currentEdge++;
|
||||
}
|
||||
|
||||
// Create tris
|
||||
tris.resize(nfaces*4);
|
||||
for (int i = 0; i < nfaces*4; ++i)
|
||||
tris[i] = -1;
|
||||
|
||||
for (int i = 0; i < nedges; ++i)
|
||||
{
|
||||
const int* e = &edges[i*4];
|
||||
if (e[3] >= 0)
|
||||
{
|
||||
// Left face
|
||||
int* t = &tris[e[3]*4];
|
||||
if (t[0] == -1)
|
||||
{
|
||||
t[0] = e[0];
|
||||
t[1] = e[1];
|
||||
}
|
||||
else if (t[0] == e[1])
|
||||
t[2] = e[0];
|
||||
else if (t[1] == e[0])
|
||||
t[2] = e[1];
|
||||
}
|
||||
if (e[2] >= 0)
|
||||
{
|
||||
// Right
|
||||
int* t = &tris[e[2]*4];
|
||||
if (t[0] == -1)
|
||||
{
|
||||
t[0] = e[1];
|
||||
t[1] = e[0];
|
||||
}
|
||||
else if (t[0] == e[0])
|
||||
t[2] = e[1];
|
||||
else if (t[1] == e[1])
|
||||
t[2] = e[0];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < tris.size()/4; ++i)
|
||||
{
|
||||
int* t = &tris[i*4];
|
||||
if (t[0] == -1 || t[1] == -1 || t[2] == -1)
|
||||
{
|
||||
ctx->log(RC_LOG_WARNING, "delaunayHull: Removing dangling face %d [%d,%d,%d].", i, t[0],t[1],t[2]);
|
||||
t[0] = tris[tris.size()-4];
|
||||
t[1] = tris[tris.size()-3];
|
||||
t[2] = tris[tris.size()-2];
|
||||
t[3] = tris[tris.size()-1];
|
||||
tris.resize(tris.size()-4);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline float getJitterX(const int i)
|
||||
{
|
||||
return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
|
||||
}
|
||||
|
||||
inline float getJitterY(const int i)
|
||||
{
|
||||
return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
|
||||
}
|
||||
|
||||
static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
|
||||
const float sampleDist, const float sampleMaxError,
|
||||
const rcCompactHeightfield& chf, const rcHeightPatch& hp,
|
||||
float* verts, int& nverts, rcIntArray& tris,
|
||||
rcIntArray& edges, rcIntArray& idx, rcIntArray& samples)
|
||||
rcIntArray& edges, rcIntArray& samples)
|
||||
{
|
||||
static const int MAX_VERTS = 256;
|
||||
static const int MAX_EDGE = 64;
|
||||
float edge[(MAX_EDGE+1)*3];
|
||||
static const int MAX_VERTS = 127;
|
||||
static const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts).
|
||||
static const int MAX_VERTS_PER_EDGE = 32;
|
||||
float edge[(MAX_VERTS_PER_EDGE+1)*3];
|
||||
int hull[MAX_VERTS];
|
||||
int nhull = 0;
|
||||
|
||||
nverts = 0;
|
||||
|
||||
for (int i = 0; i < nin; ++i)
|
||||
vcopy(&verts[i*3], &in[i*3]);
|
||||
rcVcopy(&verts[i*3], &in[i*3]);
|
||||
nverts = nin;
|
||||
|
||||
const float ics = 1.0f/chf.cs;
|
||||
const float cs = chf.cs;
|
||||
const float ics = 1.0f/cs;
|
||||
|
||||
// Tesselate outlines.
|
||||
// Tessellate outlines.
|
||||
// This is done in separate pass in order to ensure
|
||||
// seamless height values across the ply boundaries.
|
||||
if (sampleDist > 0)
|
||||
@@ -403,17 +530,24 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg,
|
||||
{
|
||||
const float* vj = &in[j*3];
|
||||
const float* vi = &in[i*3];
|
||||
bool swapped = false;
|
||||
// Make sure the segments are always handled in same order
|
||||
// using lexological sort or else there will be seams.
|
||||
if (fabsf(vj[0]-vi[0]) < 1e-6f)
|
||||
{
|
||||
if (vj[2] > vi[2])
|
||||
{
|
||||
rcSwap(vj,vi);
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vj[0] > vi[0])
|
||||
{
|
||||
rcSwap(vj,vi);
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
// Create samples along the edge.
|
||||
float dx = vi[0] - vj[0];
|
||||
@@ -421,9 +555,10 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg,
|
||||
float dz = vi[2] - vj[2];
|
||||
float d = sqrtf(dx*dx + dz*dz);
|
||||
int nn = 1 + (int)floorf(d/sampleDist);
|
||||
if (nn > MAX_EDGE) nn = MAX_EDGE;
|
||||
if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1;
|
||||
if (nverts+nn >= MAX_VERTS)
|
||||
nn = MAX_VERTS-1-nverts;
|
||||
|
||||
for (int k = 0; k <= nn; ++k)
|
||||
{
|
||||
float u = (float)k/(float)nn;
|
||||
@@ -431,10 +566,10 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg,
|
||||
pos[0] = vj[0] + dx*u;
|
||||
pos[1] = vj[1] + dy*u;
|
||||
pos[2] = vj[2] + dz*u;
|
||||
pos[1] = chf.bmin[1] + getHeight(pos, chf.bmin, ics, hp)*chf.ch;
|
||||
pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, hp)*chf.ch;
|
||||
}
|
||||
// Simplify samples.
|
||||
int idx[MAX_EDGE] = {0,nn};
|
||||
int idx[MAX_VERTS_PER_EDGE] = {0,nn};
|
||||
int nidx = 2;
|
||||
for (int k = 0; k < nidx-1; )
|
||||
{
|
||||
@@ -468,31 +603,61 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg,
|
||||
++k;
|
||||
}
|
||||
}
|
||||
|
||||
hull[nhull++] = j;
|
||||
// Add new vertices.
|
||||
for (int k = 1; k < nidx-1; ++k)
|
||||
if (swapped)
|
||||
{
|
||||
vcopy(&verts[nverts*3], &edge[idx[k]*3]);
|
||||
nverts++;
|
||||
for (int k = nidx-2; k > 0; --k)
|
||||
{
|
||||
rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
|
||||
hull[nhull++] = nverts;
|
||||
nverts++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int k = 1; k < nidx-1; ++k)
|
||||
{
|
||||
rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
|
||||
hull[nhull++] = nverts;
|
||||
nverts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tesselate the base mesh.
|
||||
|
||||
// Tessellate the base mesh.
|
||||
edges.resize(0);
|
||||
tris.resize(0);
|
||||
idx.resize(0);
|
||||
delaunay(nverts, verts, idx, tris, edges);
|
||||
|
||||
delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
|
||||
|
||||
if (tris.size() == 0)
|
||||
{
|
||||
// Could not triangulate the poly, make sure there is some valid data there.
|
||||
ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data.");
|
||||
for (int i = 2; i < nverts; ++i)
|
||||
{
|
||||
tris.push(0);
|
||||
tris.push(i-1);
|
||||
tris.push(i);
|
||||
tris.push(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sampleDist > 0)
|
||||
{
|
||||
// Create sample locations in a grid.
|
||||
float bmin[3], bmax[3];
|
||||
vcopy(bmin, in);
|
||||
vcopy(bmax, in);
|
||||
rcVcopy(bmin, in);
|
||||
rcVcopy(bmax, in);
|
||||
for (int i = 1; i < nin; ++i)
|
||||
{
|
||||
vmin(bmin, &in[i*3]);
|
||||
vmax(bmax, &in[i*3]);
|
||||
rcVmin(bmin, &in[i*3]);
|
||||
rcVmax(bmax, &in[i*3]);
|
||||
}
|
||||
int x0 = (int)floorf(bmin[0]/sampleDist);
|
||||
int x1 = (int)ceilf(bmax[0]/sampleDist);
|
||||
@@ -505,56 +670,71 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg,
|
||||
{
|
||||
float pt[3];
|
||||
pt[0] = x*sampleDist;
|
||||
pt[1] = (bmax[1]+bmin[1])*0.5f;
|
||||
pt[2] = z*sampleDist;
|
||||
// Make sure the samples are not too close to the edges.
|
||||
if (distToPoly(nin,in,pt) > -sampleDist/2) continue;
|
||||
samples.push(x);
|
||||
samples.push(getHeight(pt, chf.bmin, ics, hp));
|
||||
samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp));
|
||||
samples.push(z);
|
||||
samples.push(0); // Not added
|
||||
}
|
||||
}
|
||||
|
||||
// Add the samples starting from the one that has the most
|
||||
// error. The procedure stops when all samples are added
|
||||
// or when the max error is within treshold.
|
||||
const int nsamples = samples.size()/3;
|
||||
const int nsamples = samples.size()/4;
|
||||
for (int iter = 0; iter < nsamples; ++iter)
|
||||
{
|
||||
if (nverts >= MAX_VERTS)
|
||||
break;
|
||||
|
||||
// Find sample with most error.
|
||||
float bestpt[3];
|
||||
float bestpt[3] = {0,0,0};
|
||||
float bestd = 0;
|
||||
int besti = -1;
|
||||
for (int i = 0; i < nsamples; ++i)
|
||||
{
|
||||
const int* s = &samples[i*4];
|
||||
if (s[3]) continue; // skip added.
|
||||
float pt[3];
|
||||
pt[0] = samples[i*3+0]*sampleDist;
|
||||
pt[1] = chf.bmin[1] + samples[i*3+1]*chf.ch;
|
||||
pt[2] = samples[i*3+2]*sampleDist;
|
||||
// The sample location is jittered to get rid of some bad triangulations
|
||||
// which are cause by symmetrical data from the grid structure.
|
||||
pt[0] = s[0]*sampleDist + getJitterX(i)*cs*0.1f;
|
||||
pt[1] = s[1]*chf.ch;
|
||||
pt[2] = s[2]*sampleDist + getJitterY(i)*cs*0.1f;
|
||||
float d = distToTriMesh(pt, verts, nverts, &tris[0], tris.size()/4);
|
||||
if (d < 0) continue; // did not hit the mesh.
|
||||
if (d > bestd)
|
||||
{
|
||||
bestd = d;
|
||||
vcopy(bestpt,pt);
|
||||
besti = i;
|
||||
rcVcopy(bestpt,pt);
|
||||
}
|
||||
}
|
||||
// If the max error is within accepted threshold, stop tesselating.
|
||||
if (bestd <= sampleMaxError)
|
||||
if (bestd <= sampleMaxError || besti == -1)
|
||||
break;
|
||||
|
||||
// Mark sample as added.
|
||||
samples[besti*4+3] = 1;
|
||||
// Add the new sample point.
|
||||
vcopy(&verts[nverts*3],bestpt);
|
||||
rcVcopy(&verts[nverts*3],bestpt);
|
||||
nverts++;
|
||||
|
||||
// Create new triangulation.
|
||||
// TODO: Incremental add instead of full rebuild.
|
||||
edges.resize(0);
|
||||
tris.resize(0);
|
||||
idx.resize(0);
|
||||
delaunay(nverts, verts, idx, tris, edges);
|
||||
delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
|
||||
}
|
||||
}
|
||||
|
||||
if (nverts >= MAX_VERTS)
|
||||
break;
|
||||
}
|
||||
const int ntris = tris.size()/4;
|
||||
if (ntris > MAX_TRIS)
|
||||
{
|
||||
tris.resize(MAX_TRIS*4);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -562,82 +742,178 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg,
|
||||
|
||||
static void getHeightData(const rcCompactHeightfield& chf,
|
||||
const unsigned short* poly, const int npoly,
|
||||
const unsigned short* verts,
|
||||
const unsigned short* verts, const int bs,
|
||||
rcHeightPatch& hp, rcIntArray& stack)
|
||||
{
|
||||
// Floodfill the heightfield to get 2D height data,
|
||||
// starting at vertex locations as seeds.
|
||||
|
||||
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
|
||||
|
||||
// Note: Reads to the compact heightfield are offset by border size (bs)
|
||||
// since border size offset is already removed from the polymesh vertices.
|
||||
|
||||
memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
|
||||
|
||||
stack.resize(0);
|
||||
|
||||
static const int offset[9*2] =
|
||||
{
|
||||
0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0,
|
||||
};
|
||||
|
||||
// Use poly vertices as seed points for the flood fill.
|
||||
for (int j = 0; j < npoly; ++j)
|
||||
{
|
||||
const int ax = (int)verts[poly[j]*3+0];
|
||||
const int ay = (int)verts[poly[j]*3+1];
|
||||
const int az = (int)verts[poly[j]*3+2];
|
||||
if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
|
||||
az < hp.ymin || az >= hp.ymin+hp.height)
|
||||
continue;
|
||||
|
||||
const rcCompactCell& c = chf.cells[ax+az*chf.width];
|
||||
int dmin = 0xffff;
|
||||
int ai = -1;
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
int cx = 0, cz = 0, ci =-1;
|
||||
int dmin = RC_UNSET_HEIGHT;
|
||||
for (int k = 0; k < 9; ++k)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
int d = rcAbs(ay - (int)s.y);
|
||||
if (d < dmin)
|
||||
const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
|
||||
const int ay = (int)verts[poly[j]*3+1];
|
||||
const int az = (int)verts[poly[j]*3+2] + offset[k*2+1];
|
||||
if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
|
||||
az < hp.ymin || az >= hp.ymin+hp.height)
|
||||
continue;
|
||||
|
||||
const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
ai = i;
|
||||
dmin = d;
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
int d = rcAbs(ay - (int)s.y);
|
||||
if (d < dmin)
|
||||
{
|
||||
cx = ax;
|
||||
cz = az;
|
||||
ci = i;
|
||||
dmin = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ai != -1)
|
||||
if (ci != -1)
|
||||
{
|
||||
stack.push(ax);
|
||||
stack.push(az);
|
||||
stack.push(ai);
|
||||
stack.push(cx);
|
||||
stack.push(cz);
|
||||
stack.push(ci);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Find center of the polygon using flood fill.
|
||||
int pcx = 0, pcz = 0;
|
||||
for (int j = 0; j < npoly; ++j)
|
||||
{
|
||||
pcx += (int)verts[poly[j]*3+0];
|
||||
pcz += (int)verts[poly[j]*3+2];
|
||||
}
|
||||
pcx /= npoly;
|
||||
pcz /= npoly;
|
||||
|
||||
for (int i = 0; i < stack.size(); i += 3)
|
||||
{
|
||||
int cx = stack[i+0];
|
||||
int cy = stack[i+1];
|
||||
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
|
||||
hp.data[idx] = 1;
|
||||
}
|
||||
|
||||
while (stack.size() > 0)
|
||||
{
|
||||
int ci = stack.pop();
|
||||
int cy = stack.pop();
|
||||
int cx = stack.pop();
|
||||
|
||||
// Skip already visited locations.
|
||||
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
|
||||
if (hp.data[idx] != 0xffff)
|
||||
continue;
|
||||
|
||||
// Check if close to center of the polygon.
|
||||
if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1)
|
||||
{
|
||||
stack.resize(0);
|
||||
stack.push(cx);
|
||||
stack.push(cy);
|
||||
stack.push(ci);
|
||||
break;
|
||||
}
|
||||
|
||||
const rcCompactSpan& cs = chf.spans[ci];
|
||||
hp.data[idx] = cs.y;
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(cs, dir) == 0xf) continue;
|
||||
if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
|
||||
|
||||
const int ax = cx + rcGetDirOffsetX(dir);
|
||||
const int ay = cy + rcGetDirOffsetY(dir);
|
||||
|
||||
|
||||
if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
|
||||
ay < hp.ymin || ay >= (hp.ymin+hp.height))
|
||||
continue;
|
||||
|
||||
if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0xffff)
|
||||
|
||||
if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0)
|
||||
continue;
|
||||
|
||||
const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);
|
||||
|
||||
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir);
|
||||
int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
|
||||
hp.data[idx] = 1;
|
||||
|
||||
stack.push(ax);
|
||||
stack.push(ay);
|
||||
stack.push(ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
|
||||
|
||||
// Mark start locations.
|
||||
for (int i = 0; i < stack.size(); i += 3)
|
||||
{
|
||||
int cx = stack[i+0];
|
||||
int cy = stack[i+1];
|
||||
int ci = stack[i+2];
|
||||
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
|
||||
const rcCompactSpan& cs = chf.spans[ci];
|
||||
hp.data[idx] = cs.y;
|
||||
}
|
||||
|
||||
static const int RETRACT_SIZE = 256;
|
||||
int head = 0;
|
||||
|
||||
while (head*3 < stack.size())
|
||||
{
|
||||
int cx = stack[head*3+0];
|
||||
int cy = stack[head*3+1];
|
||||
int ci = stack[head*3+2];
|
||||
head++;
|
||||
if (head >= RETRACT_SIZE)
|
||||
{
|
||||
head = 0;
|
||||
if (stack.size() > RETRACT_SIZE*3)
|
||||
memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3));
|
||||
stack.resize(stack.size()-RETRACT_SIZE*3);
|
||||
}
|
||||
|
||||
const rcCompactSpan& cs = chf.spans[ci];
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
|
||||
|
||||
const int ax = cx + rcGetDirOffsetX(dir);
|
||||
const int ay = cy + rcGetDirOffsetY(dir);
|
||||
|
||||
if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
|
||||
ay < hp.ymin || ay >= (hp.ymin+hp.height))
|
||||
continue;
|
||||
|
||||
if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT)
|
||||
continue;
|
||||
|
||||
const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);
|
||||
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
|
||||
hp.data[idx] = as.y;
|
||||
|
||||
stack.push(ax);
|
||||
stack.push(ay);
|
||||
stack.push(ai);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static unsigned char getEdgeFlags(const float* va, const float* vb,
|
||||
@@ -664,51 +940,48 @@ static unsigned char getTriFlags(const float* va, const float* vb, const float*
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocPolyMeshDetail, rcPolyMesh, rcCompactHeightfield, rcPolyMeshDetail, rcConfig
|
||||
bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
|
||||
const float sampleDist, const float sampleMaxError,
|
||||
rcPolyMeshDetail& dmesh)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
|
||||
|
||||
if (mesh.nverts == 0 || mesh.npolys == 0)
|
||||
return true;
|
||||
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcTimeVal endTime;
|
||||
|
||||
int vcap;
|
||||
int tcap;
|
||||
|
||||
|
||||
const int nvp = mesh.nvp;
|
||||
const float cs = mesh.cs;
|
||||
const float ch = mesh.ch;
|
||||
const float* orig = mesh.bmin;
|
||||
const int borderSize = mesh.borderSize;
|
||||
|
||||
rcIntArray edges(64);
|
||||
rcIntArray tris(512);
|
||||
rcIntArray idx(512);
|
||||
rcIntArray stack(512);
|
||||
rcIntArray samples(512);
|
||||
float verts[256*3];
|
||||
float* poly = 0;
|
||||
int* bounds = 0;
|
||||
rcHeightPatch hp;
|
||||
int nPolyVerts = 0;
|
||||
int maxhw = 0, maxhh = 0;
|
||||
|
||||
bounds = new int[mesh.npolys*4];
|
||||
rcScopedDelete<int> bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP);
|
||||
if (!bounds)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4);
|
||||
return false;
|
||||
}
|
||||
poly = new float[nvp*3];
|
||||
if (!bounds)
|
||||
rcScopedDelete<float> poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP);
|
||||
if (!poly)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find max size for a polygon area.
|
||||
@@ -725,7 +998,7 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c
|
||||
ymax = 0;
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if(p[j] == 0xffff) break;
|
||||
if(p[j] == RC_MESH_NULL_IDX) break;
|
||||
const unsigned short* v = &mesh.verts[p[j]*3];
|
||||
xmin = rcMin(xmin, (int)v[0]);
|
||||
xmax = rcMax(xmax, (int)v[0]);
|
||||
@@ -742,58 +1015,54 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c
|
||||
maxhh = rcMax(maxhh, ymax-ymin);
|
||||
}
|
||||
|
||||
hp.data = new unsigned short[maxhw*maxhh];
|
||||
hp.data = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxhw*maxhh, RC_ALLOC_TEMP);
|
||||
if (!hp.data)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
dmesh.nmeshes = mesh.npolys;
|
||||
dmesh.nverts = 0;
|
||||
dmesh.ntris = 0;
|
||||
dmesh.meshes = new unsigned short[dmesh.nmeshes*4];
|
||||
dmesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*dmesh.nmeshes*4, RC_ALLOC_PERM);
|
||||
if (!dmesh.meshes)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4);
|
||||
return false;
|
||||
}
|
||||
|
||||
vcap = nPolyVerts+nPolyVerts/2;
|
||||
tcap = vcap*2;
|
||||
int vcap = nPolyVerts+nPolyVerts/2;
|
||||
int tcap = vcap*2;
|
||||
|
||||
dmesh.nverts = 0;
|
||||
dmesh.verts = new float[vcap*3];
|
||||
dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
|
||||
if (!dmesh.verts)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3);
|
||||
return false;
|
||||
}
|
||||
dmesh.ntris = 0;
|
||||
dmesh.tris = new unsigned char[tcap*4];
|
||||
dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM);
|
||||
if (!dmesh.tris)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mesh.npolys; ++i)
|
||||
{
|
||||
const unsigned short* p = &mesh.polys[i*nvp*2];
|
||||
|
||||
// Find polygon bounding box.
|
||||
// Store polygon vertices for processing.
|
||||
int npoly = 0;
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if(p[j] == 0xffff) break;
|
||||
if(p[j] == RC_MESH_NULL_IDX) break;
|
||||
const unsigned short* v = &mesh.verts[p[j]*3];
|
||||
poly[j*3+0] = orig[0] + v[0]*cs;
|
||||
poly[j*3+1] = orig[1] + v[1]*ch;
|
||||
poly[j*3+2] = orig[2] + v[2]*cs;
|
||||
poly[j*3+0] = v[0]*cs;
|
||||
poly[j*3+1] = v[1]*ch;
|
||||
poly[j*3+2] = v[2]*cs;
|
||||
npoly++;
|
||||
}
|
||||
|
||||
@@ -802,29 +1071,40 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c
|
||||
hp.ymin = bounds[i*4+2];
|
||||
hp.width = bounds[i*4+1]-bounds[i*4+0];
|
||||
hp.height = bounds[i*4+3]-bounds[i*4+2];
|
||||
getHeightData(chf, p, npoly, mesh.verts, hp, stack);
|
||||
getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack);
|
||||
|
||||
// Build detail mesh.
|
||||
int nverts = 0;
|
||||
if (!buildPolyDetail(poly, npoly, mesh.regs[i],
|
||||
if (!buildPolyDetail(ctx, poly, npoly,
|
||||
sampleDist, sampleMaxError,
|
||||
chf, hp, verts, nverts, tris,
|
||||
edges, idx, samples))
|
||||
edges, samples))
|
||||
{
|
||||
goto failure;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Offset detail vertices, unnecassary?
|
||||
// Move detail verts to world space.
|
||||
for (int j = 0; j < nverts; ++j)
|
||||
verts[j*3+1] += chf.ch;
|
||||
{
|
||||
verts[j*3+0] += orig[0];
|
||||
verts[j*3+1] += orig[1] + chf.ch; // Is this offset necessary?
|
||||
verts[j*3+2] += orig[2];
|
||||
}
|
||||
// Offset poly too, will be used to flag checking.
|
||||
for (int j = 0; j < npoly; ++j)
|
||||
{
|
||||
poly[j*3+0] += orig[0];
|
||||
poly[j*3+1] += orig[1];
|
||||
poly[j*3+2] += orig[2];
|
||||
}
|
||||
|
||||
// Store detail submesh.
|
||||
const int ntris = tris.size()/4;
|
||||
|
||||
dmesh.meshes[i*4+0] = dmesh.nverts;
|
||||
dmesh.meshes[i*4+1] = (unsigned short)nverts;
|
||||
dmesh.meshes[i*4+2] = dmesh.ntris;
|
||||
dmesh.meshes[i*4+3] = (unsigned short)ntris;
|
||||
|
||||
dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts;
|
||||
dmesh.meshes[i*4+1] = (unsigned int)nverts;
|
||||
dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris;
|
||||
dmesh.meshes[i*4+3] = (unsigned int)ntris;
|
||||
|
||||
// Store vertices, allocate more memory if necessary.
|
||||
if (dmesh.nverts+nverts > vcap)
|
||||
@@ -832,16 +1112,15 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c
|
||||
while (dmesh.nverts+nverts > vcap)
|
||||
vcap += 256;
|
||||
|
||||
float* newv = new float[vcap*3];
|
||||
float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
|
||||
if (!newv)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3);
|
||||
return false;
|
||||
}
|
||||
if (dmesh.nverts)
|
||||
memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts);
|
||||
delete [] dmesh.verts;
|
||||
rcFree(dmesh.verts);
|
||||
dmesh.verts = newv;
|
||||
}
|
||||
for (int j = 0; j < nverts; ++j)
|
||||
@@ -857,16 +1136,15 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c
|
||||
{
|
||||
while (dmesh.ntris+ntris > tcap)
|
||||
tcap += 256;
|
||||
unsigned char* newt = new unsigned char[tcap*4];
|
||||
unsigned char* newt = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM);
|
||||
if (!newt)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4);
|
||||
goto failure;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4);
|
||||
return false;
|
||||
}
|
||||
if (dmesh.ntris)
|
||||
memcpy(newt, dmesh.tris, sizeof(unsigned char)*4*dmesh.ntris);
|
||||
delete [] dmesh.tris;
|
||||
rcFree(dmesh.tris);
|
||||
dmesh.tris = newt;
|
||||
}
|
||||
for (int j = 0; j < ntris; ++j)
|
||||
@@ -879,29 +1157,19 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c
|
||||
dmesh.ntris++;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] bounds;
|
||||
delete [] poly;
|
||||
|
||||
endTime = rcGetPerformanceTimer();
|
||||
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->buildDetailMesh += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
|
||||
|
||||
return true;
|
||||
|
||||
failure:
|
||||
|
||||
delete [] bounds;
|
||||
delete [] poly;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh)
|
||||
/// @see rcAllocPolyMeshDetail, rcPolyMeshDetail
|
||||
bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh)
|
||||
{
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
|
||||
|
||||
int maxVerts = 0;
|
||||
int maxTris = 0;
|
||||
int maxMeshes = 0;
|
||||
@@ -915,29 +1183,26 @@ bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPoly
|
||||
}
|
||||
|
||||
mesh.nmeshes = 0;
|
||||
mesh.meshes = new unsigned short[maxMeshes*4];
|
||||
mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM);
|
||||
if (!mesh.meshes)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4);
|
||||
return false;
|
||||
}
|
||||
|
||||
mesh.ntris = 0;
|
||||
mesh.tris = new unsigned char[maxTris*4];
|
||||
mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM);
|
||||
if (!mesh.tris)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4);
|
||||
return false;
|
||||
}
|
||||
|
||||
mesh.nverts = 0;
|
||||
mesh.verts = new float[maxVerts*3];
|
||||
mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM);
|
||||
if (!mesh.verts)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3);
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -948,18 +1213,18 @@ bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPoly
|
||||
if (!dm) continue;
|
||||
for (int j = 0; j < dm->nmeshes; ++j)
|
||||
{
|
||||
unsigned short* dst = &mesh.meshes[mesh.nmeshes*4];
|
||||
unsigned short* src = &dm->meshes[j*4];
|
||||
dst[0] = mesh.nverts+src[0];
|
||||
unsigned int* dst = &mesh.meshes[mesh.nmeshes*4];
|
||||
unsigned int* src = &dm->meshes[j*4];
|
||||
dst[0] = (unsigned int)mesh.nverts+src[0];
|
||||
dst[1] = src[1];
|
||||
dst[2] = mesh.ntris+src[2];
|
||||
dst[2] = (unsigned int)mesh.ntris+src[2];
|
||||
dst[3] = src[3];
|
||||
mesh.nmeshes++;
|
||||
}
|
||||
|
||||
for (int k = 0; k < dm->nverts; ++k)
|
||||
{
|
||||
vcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]);
|
||||
rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]);
|
||||
mesh.nverts++;
|
||||
}
|
||||
for (int k = 0; k < dm->ntris; ++k)
|
||||
@@ -972,10 +1237,7 @@ bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPoly
|
||||
}
|
||||
}
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->mergePolyMeshDetail += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -20,8 +20,8 @@
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastTimer.h"
|
||||
#include "RecastLog.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax)
|
||||
{
|
||||
@@ -48,8 +48,7 @@ static rcSpan* allocSpan(rcHeightfield& hf)
|
||||
{
|
||||
// Create new page.
|
||||
// Allocate memory for the new pool.
|
||||
const int size = (sizeof(rcSpanPool)-sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL;
|
||||
rcSpanPool* pool = reinterpret_cast<rcSpanPool*>(new unsigned char[size]);
|
||||
rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
|
||||
if (!pool) return 0;
|
||||
pool->next = 0;
|
||||
// Add the pool into the list of pools.
|
||||
@@ -83,16 +82,17 @@ static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
|
||||
hf.freelist = ptr;
|
||||
}
|
||||
|
||||
static void addSpan(rcHeightfield& hf, int x, int y,
|
||||
unsigned short smin, unsigned short smax,
|
||||
unsigned short flags)
|
||||
static void addSpan(rcHeightfield& hf, const int x, const int y,
|
||||
const unsigned short smin, const unsigned short smax,
|
||||
const unsigned char area, const int flagMergeThr)
|
||||
{
|
||||
|
||||
int idx = x + y*hf.width;
|
||||
|
||||
rcSpan* s = allocSpan(hf);
|
||||
s->smin = smin;
|
||||
s->smax = smax;
|
||||
s->flags = flags;
|
||||
s->area = area;
|
||||
s->next = 0;
|
||||
|
||||
// Empty cell, add he first span.
|
||||
@@ -127,9 +127,8 @@ static void addSpan(rcHeightfield& hf, int x, int y,
|
||||
s->smax = cur->smax;
|
||||
|
||||
// Merge flags.
|
||||
// if (s->smax == cur->smax)
|
||||
if (rcAbs((int)s->smax - (int)cur->smax) <= 1)
|
||||
s->flags |= cur->flags;
|
||||
if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr)
|
||||
s->area = rcMax(s->area, cur->area);
|
||||
|
||||
// Remove current span.
|
||||
rcSpan* next = cur->next;
|
||||
@@ -155,6 +154,21 @@ static void addSpan(rcHeightfield& hf, int x, int y,
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The span addition can be set to favor flags. If the span is merged to
|
||||
/// another span and the new @p smax is within @p flagMergeThr units
|
||||
/// from the existing span, the span flags are merged.
|
||||
///
|
||||
/// @see rcHeightfield, rcSpan.
|
||||
void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
|
||||
const unsigned short smin, const unsigned short smax,
|
||||
const unsigned char area, const int flagMergeThr)
|
||||
{
|
||||
// rcAssert(ctx);
|
||||
addSpan(hf, x,y, smin, smax, area, flagMergeThr);
|
||||
}
|
||||
|
||||
static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd)
|
||||
{
|
||||
float d[12];
|
||||
@@ -186,9 +200,10 @@ static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, fl
|
||||
}
|
||||
|
||||
static void rasterizeTri(const float* v0, const float* v1, const float* v2,
|
||||
unsigned char flags, rcHeightfield& hf,
|
||||
const unsigned char area, rcHeightfield& hf,
|
||||
const float* bmin, const float* bmax,
|
||||
const float cs, const float ics, const float ich)
|
||||
const float cs, const float ics, const float ich,
|
||||
const int flagMergeThr)
|
||||
{
|
||||
const int w = hf.width;
|
||||
const int h = hf.height;
|
||||
@@ -196,12 +211,12 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
|
||||
const float by = bmax[1] - bmin[1];
|
||||
|
||||
// Calculate the bounding box of the triangle.
|
||||
vcopy(tmin, v0);
|
||||
vcopy(tmax, v0);
|
||||
vmin(tmin, v1);
|
||||
vmin(tmin, v2);
|
||||
vmax(tmax, v1);
|
||||
vmax(tmax, v2);
|
||||
rcVcopy(tmin, v0);
|
||||
rcVcopy(tmax, v0);
|
||||
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))
|
||||
@@ -223,9 +238,9 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
|
||||
for (int y = y0; y <= y1; ++y)
|
||||
{
|
||||
// Clip polygon to row.
|
||||
vcopy(&in[0], v0);
|
||||
vcopy(&in[1*3], v1);
|
||||
vcopy(&in[2*3], v2);
|
||||
rcVcopy(&in[0], v0);
|
||||
rcVcopy(&in[1*3], v1);
|
||||
rcVcopy(&in[2*3], v2);
|
||||
int nvrow = 3;
|
||||
const float cz = bmin[2] + y*cs;
|
||||
nvrow = clipPoly(in, nvrow, out, 0, 1, -cz);
|
||||
@@ -256,38 +271,50 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
|
||||
if (smax < 0.0f) continue;
|
||||
if (smin > by) continue;
|
||||
// Clamp the span to the heightfield bbox.
|
||||
if (smin < 0.0f) smin = bmin[1];
|
||||
if (smax > by) smax = bmax[1];
|
||||
if (smin < 0.0f) smin = 0;
|
||||
if (smax > by) smax = by;
|
||||
|
||||
// Snap the span to the heightfield height grid.
|
||||
unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, 0x7fff);
|
||||
unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), 0, 0x7fff);
|
||||
unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
|
||||
unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
|
||||
|
||||
addSpan(hf, x, y, ismin, ismax, flags);
|
||||
addSpan(hf, x, y, ismin, ismax, area, flagMergeThr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcRasterizeTriangle(const float* v0, const float* v1, const float* v2,
|
||||
unsigned char flags, rcHeightfield& solid)
|
||||
/// @par
|
||||
///
|
||||
/// No spans will be added if the triangle does not overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char area, rcHeightfield& solid,
|
||||
const int flagMergeThr)
|
||||
{
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
rasterizeTri(v0, v1, v2, flags, solid, solid.bmin, solid.bmax, solid.cs, ics, ich);
|
||||
rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->rasterizeTriangles += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
void rcRasterizeTriangles(const float* verts, int nv,
|
||||
const int* tris, const unsigned char* flags, int nt,
|
||||
rcHeightfield& solid)
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
||||
const int* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
@@ -298,11 +325,63 @@ void rcRasterizeTriangles(const float* verts, int nv,
|
||||
const float* v1 = &verts[tris[i*3+1]*3];
|
||||
const float* v2 = &verts[tris[i*3+2]*3];
|
||||
// Rasterize.
|
||||
rasterizeTri(v0, v1, v2, flags[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich);
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
if (rcGetBuildTimes())
|
||||
rcGetBuildTimes()->rasterizeTriangles += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
||||
const unsigned short* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(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.
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
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.
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2009 Mikko Mononen memon@inside.org
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -23,13 +23,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastLog.h"
|
||||
#include "RecastTimer.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
#include <new>
|
||||
|
||||
|
||||
static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
unsigned short* src, unsigned short* dst,
|
||||
unsigned short& maxDist)
|
||||
static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist)
|
||||
{
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
@@ -47,11 +46,19 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
const unsigned char area = chf.areas[i];
|
||||
|
||||
int nc = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
nc++;
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
if (area == chf.areas[ai])
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
if (nc != 4)
|
||||
src[i] = 0;
|
||||
@@ -59,6 +66,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Pass 1
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
@@ -69,7 +77,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 0) != 0xf)
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (-1,0)
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
@@ -80,7 +88,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
src[i] = src[ai]+2;
|
||||
|
||||
// (-1,-1)
|
||||
if (rcGetCon(as, 3) != 0xf)
|
||||
if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(3);
|
||||
const int aay = ay + rcGetDirOffsetY(3);
|
||||
@@ -89,7 +97,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
src[i] = src[aai]+3;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 3) != 0xf)
|
||||
if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (0,-1)
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
@@ -100,7 +108,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
src[i] = src[ai]+2;
|
||||
|
||||
// (1,-1)
|
||||
if (rcGetCon(as, 2) != 0xf)
|
||||
if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(2);
|
||||
const int aay = ay + rcGetDirOffsetY(2);
|
||||
@@ -123,7 +131,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 2) != 0xf)
|
||||
if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (1,0)
|
||||
const int ax = x + rcGetDirOffsetX(2);
|
||||
@@ -134,7 +142,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
src[i] = src[ai]+2;
|
||||
|
||||
// (1,1)
|
||||
if (rcGetCon(as, 1) != 0xf)
|
||||
if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(1);
|
||||
const int aay = ay + rcGetDirOffsetY(1);
|
||||
@@ -143,7 +151,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
src[i] = src[aai]+3;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 1) != 0xf)
|
||||
if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (0,1)
|
||||
const int ax = x + rcGetDirOffsetX(1);
|
||||
@@ -154,7 +162,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
src[i] = src[ai]+2;
|
||||
|
||||
// (-1,1)
|
||||
if (rcGetCon(as, 0) != 0xf)
|
||||
if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(0);
|
||||
const int aay = ay + rcGetDirOffsetY(0);
|
||||
@@ -171,8 +179,6 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf,
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
maxDist = rcMax(src[i], maxDist);
|
||||
|
||||
return src;
|
||||
|
||||
}
|
||||
|
||||
static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr,
|
||||
@@ -191,17 +197,17 @@ static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr,
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
int cd = (int)src[i];
|
||||
const unsigned short cd = src[i];
|
||||
if (cd <= thr)
|
||||
{
|
||||
dst[i] = cd;
|
||||
continue;
|
||||
}
|
||||
|
||||
int d = cd;
|
||||
int d = (int)cd;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
@@ -210,7 +216,7 @@ static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr,
|
||||
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
const int dir2 = (dir+1) & 0x3;
|
||||
if (rcGetCon(as, dir2) != 0xf)
|
||||
if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax2 = ax + rcGetDirOffsetX(dir2);
|
||||
const int ay2 = ay + rcGetDirOffsetY(dir2);
|
||||
@@ -236,22 +242,24 @@ static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr,
|
||||
|
||||
|
||||
static bool floodRegion(int x, int y, int i,
|
||||
unsigned short level, unsigned short minLevel, unsigned short r,
|
||||
unsigned short level, unsigned short r,
|
||||
rcCompactHeightfield& chf,
|
||||
unsigned short* src,
|
||||
unsigned short* srcReg, unsigned short* srcDist,
|
||||
rcIntArray& stack)
|
||||
{
|
||||
const int w = chf.width;
|
||||
|
||||
const unsigned char area = chf.areas[i];
|
||||
|
||||
// Flood fill mark region.
|
||||
stack.resize(0);
|
||||
stack.push((int)x);
|
||||
stack.push((int)y);
|
||||
stack.push((int)i);
|
||||
src[i*2] = r;
|
||||
src[i*2+1] = 0;
|
||||
srcReg[i] = r;
|
||||
srcDist[i] = 0;
|
||||
|
||||
unsigned short lev = level >= minLevel+2 ? level-2 : minLevel;
|
||||
unsigned short lev = level >= 2 ? level-2 : 0;
|
||||
int count = 0;
|
||||
|
||||
while (stack.size() > 0)
|
||||
@@ -267,25 +275,30 @@ static bool floodRegion(int x, int y, int i,
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
// 8 connected
|
||||
if (rcGetCon(cs, dir) != 0xf)
|
||||
if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = cx + rcGetDirOffsetX(dir);
|
||||
const int ay = cy + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
|
||||
unsigned short nr = src[ai*2];
|
||||
if (chf.areas[ai] != area)
|
||||
continue;
|
||||
unsigned short nr = srcReg[ai];
|
||||
if (nr & RC_BORDER_REG) // Do not take borders into account.
|
||||
continue;
|
||||
if (nr != 0 && nr != r)
|
||||
ar = nr;
|
||||
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
|
||||
const int dir2 = (dir+1) & 0x3;
|
||||
if (rcGetCon(as, dir2) != 0xf)
|
||||
if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax2 = ax + rcGetDirOffsetX(dir2);
|
||||
const int ay2 = ay + rcGetDirOffsetY(dir2);
|
||||
const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
|
||||
|
||||
unsigned short nr = src[ai2*2];
|
||||
if (chf.areas[ai2] != area)
|
||||
continue;
|
||||
unsigned short nr = srcReg[ai2];
|
||||
if (nr != 0 && nr != r)
|
||||
ar = nr;
|
||||
}
|
||||
@@ -293,7 +306,7 @@ static bool floodRegion(int x, int y, int i,
|
||||
}
|
||||
if (ar != 0)
|
||||
{
|
||||
src[ci*2] = 0;
|
||||
srcReg[ci] = 0;
|
||||
continue;
|
||||
}
|
||||
count++;
|
||||
@@ -301,21 +314,20 @@ static bool floodRegion(int x, int y, int i,
|
||||
// Expand neighbours.
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(cs, dir) != 0xf)
|
||||
if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = cx + rcGetDirOffsetX(dir);
|
||||
const int ay = cy + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
|
||||
if (chf.spans[ai].dist >= lev)
|
||||
if (chf.areas[ai] != area)
|
||||
continue;
|
||||
if (chf.dist[ai] >= lev && srcReg[ai] == 0)
|
||||
{
|
||||
if (src[ai*2] == 0)
|
||||
{
|
||||
src[ai*2] = r;
|
||||
src[ai*2+1] = 0;
|
||||
stack.push(ax);
|
||||
stack.push(ay);
|
||||
stack.push(ai);
|
||||
}
|
||||
srcReg[ai] = r;
|
||||
srcDist[ai] = 0;
|
||||
stack.push(ax);
|
||||
stack.push(ay);
|
||||
stack.push(ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,8 +338,8 @@ static bool floodRegion(int x, int y, int i,
|
||||
|
||||
static unsigned short* expandRegions(int maxIter, unsigned short level,
|
||||
rcCompactHeightfield& chf,
|
||||
unsigned short* src,
|
||||
unsigned short* dst,
|
||||
unsigned short* srcReg, unsigned short* srcDist,
|
||||
unsigned short* dstReg, unsigned short* dstDist,
|
||||
rcIntArray& stack)
|
||||
{
|
||||
const int w = chf.width;
|
||||
@@ -342,7 +354,7 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (chf.spans[i].dist >= level && src[i*2] == 0)
|
||||
if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
|
||||
{
|
||||
stack.push(x);
|
||||
stack.push(y);
|
||||
@@ -357,7 +369,8 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
|
||||
{
|
||||
int failed = 0;
|
||||
|
||||
memcpy(dst, src, sizeof(unsigned short)*chf.spanCount*2);
|
||||
memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount);
|
||||
memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount);
|
||||
|
||||
for (int j = 0; j < stack.size(); j += 3)
|
||||
{
|
||||
@@ -370,29 +383,31 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned short r = src[i*2];
|
||||
unsigned short r = srcReg[i];
|
||||
unsigned short d2 = 0xffff;
|
||||
const unsigned char area = chf.areas[i];
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) == 0xf) continue;
|
||||
if (rcGetCon(s, dir) == RC_NOT_CONNECTED) continue;
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
if (src[ai*2] > 0 && (src[ai*2] & RC_BORDER_REG) == 0)
|
||||
if (chf.areas[ai] != area) continue;
|
||||
if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0)
|
||||
{
|
||||
if ((int)src[ai*2+1]+2 < (int)d2)
|
||||
if ((int)srcDist[ai]+2 < (int)d2)
|
||||
{
|
||||
r = src[ai*2];
|
||||
d2 = src[ai*2+1]+2;
|
||||
r = srcReg[ai];
|
||||
d2 = srcDist[ai]+2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (r)
|
||||
{
|
||||
stack[j+2] = -1; // mark as used
|
||||
dst[i*2] = r;
|
||||
dst[i*2+1] = d2;
|
||||
dstReg[i] = r;
|
||||
dstDist[i] = d2;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -401,7 +416,8 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
|
||||
}
|
||||
|
||||
// rcSwap source and dest.
|
||||
rcSwap(src, dst);
|
||||
rcSwap(srcReg, dstReg);
|
||||
rcSwap(srcDist, dstDist);
|
||||
|
||||
if (failed*3 == stack.size())
|
||||
break;
|
||||
@@ -414,17 +430,25 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
return srcReg;
|
||||
}
|
||||
|
||||
|
||||
struct rcRegion
|
||||
{
|
||||
inline rcRegion() : count(0), id(0), remap(false) {}
|
||||
inline rcRegion(unsigned short i) :
|
||||
spanCount(0),
|
||||
id(i),
|
||||
areaType(0),
|
||||
remap(false),
|
||||
visited(false)
|
||||
{}
|
||||
|
||||
int count;
|
||||
unsigned short id;
|
||||
int spanCount; // Number of spans belonging to this region
|
||||
unsigned short id; // ID of the region
|
||||
unsigned char areaType; // Are type.
|
||||
bool remap;
|
||||
bool visited;
|
||||
rcIntArray connections;
|
||||
rcIntArray floors;
|
||||
};
|
||||
@@ -467,25 +491,27 @@ static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short
|
||||
removeAdjacentNeighbours(reg);
|
||||
}
|
||||
|
||||
static bool canMergeWithRegion(rcRegion& reg, unsigned short id)
|
||||
static bool canMergeWithRegion(const rcRegion& rega, const rcRegion& regb)
|
||||
{
|
||||
if (rega.areaType != regb.areaType)
|
||||
return false;
|
||||
int n = 0;
|
||||
for (int i = 0; i < reg.connections.size(); ++i)
|
||||
for (int i = 0; i < rega.connections.size(); ++i)
|
||||
{
|
||||
if (reg.connections[i] == id)
|
||||
if (rega.connections[i] == regb.id)
|
||||
n++;
|
||||
}
|
||||
if (n > 1)
|
||||
return false;
|
||||
for (int i = 0; i < reg.floors.size(); ++i)
|
||||
for (int i = 0; i < rega.floors.size(); ++i)
|
||||
{
|
||||
if (reg.floors[i] == id)
|
||||
if (rega.floors[i] == regb.id)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void addUniqueFloorRegion(rcRegion& reg, unsigned short n)
|
||||
static void addUniqueFloorRegion(rcRegion& reg, int n)
|
||||
{
|
||||
for (int i = 0; i < reg.floors.size(); ++i)
|
||||
if (reg.floors[i] == n)
|
||||
@@ -543,8 +569,8 @@ static bool mergeRegions(rcRegion& rega, rcRegion& regb)
|
||||
|
||||
for (int j = 0; j < regb.floors.size(); ++j)
|
||||
addUniqueFloorRegion(rega, regb.floors[j]);
|
||||
rega.count += regb.count;
|
||||
regb.count = 0;
|
||||
rega.spanCount += regb.spanCount;
|
||||
regb.spanCount = 0;
|
||||
regb.connections.resize(0);
|
||||
|
||||
return true;
|
||||
@@ -562,26 +588,26 @@ static bool isRegionConnectedToBorder(const rcRegion& reg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* src,
|
||||
static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
|
||||
int x, int y, int i, int dir)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
unsigned short r = 0;
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
|
||||
r = src[ai*2];
|
||||
r = srcReg[ai];
|
||||
}
|
||||
if (r == src[i*2])
|
||||
if (r == srcReg[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void walkContour(int x, int y, int i, int dir,
|
||||
rcCompactHeightfield& chf,
|
||||
unsigned short* src,
|
||||
unsigned short* srcReg,
|
||||
rcIntArray& cont)
|
||||
{
|
||||
int startDir = dir;
|
||||
@@ -589,12 +615,12 @@ static void walkContour(int x, int y, int i, int dir,
|
||||
|
||||
const rcCompactSpan& ss = chf.spans[i];
|
||||
unsigned short curReg = 0;
|
||||
if (rcGetCon(ss, dir) != 0xf)
|
||||
if (rcGetCon(ss, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir);
|
||||
curReg = src[ai*2];
|
||||
curReg = srcReg[ai];
|
||||
}
|
||||
cont.push(curReg);
|
||||
|
||||
@@ -603,16 +629,16 @@ static void walkContour(int x, int y, int i, int dir,
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (isSolidEdge(chf, src, x, y, i, dir))
|
||||
if (isSolidEdge(chf, srcReg, x, y, i, dir))
|
||||
{
|
||||
// Choose the edge corner
|
||||
unsigned short r = 0;
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
|
||||
r = src[ai*2];
|
||||
r = srcReg[ai];
|
||||
}
|
||||
if (r != curReg)
|
||||
{
|
||||
@@ -627,7 +653,7 @@ static void walkContour(int x, int y, int i, int dir,
|
||||
int ni = -1;
|
||||
const int nx = x + rcGetDirOffsetX(dir);
|
||||
const int ny = y + rcGetDirOffsetY(dir);
|
||||
if (rcGetCon(s, dir) != 0xf)
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
|
||||
ni = (int)nc.index + rcGetCon(s, dir);
|
||||
@@ -667,26 +693,26 @@ static void walkContour(int x, int y, int i, int dir,
|
||||
}
|
||||
}
|
||||
|
||||
static bool filterSmallRegions(int minRegionSize, int mergeRegionSize,
|
||||
static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
|
||||
unsigned short& maxRegionId,
|
||||
rcCompactHeightfield& chf,
|
||||
unsigned short* src)
|
||||
unsigned short* srcReg)
|
||||
{
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
int nreg = maxRegionId+1;
|
||||
rcRegion* regions = new rcRegion[nreg];
|
||||
|
||||
const int nreg = maxRegionId+1;
|
||||
rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
|
||||
if (!regions)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
|
||||
ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nreg; ++i)
|
||||
regions[i].id = (unsigned short)i;
|
||||
|
||||
// Construct regions
|
||||
for (int i = 0; i < nreg; ++i)
|
||||
new(®ions[i]) rcRegion((unsigned short)i);
|
||||
|
||||
// Find edge of a region and find connections around the contour.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
@@ -695,19 +721,19 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize,
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
unsigned short r = src[i*2];
|
||||
unsigned short r = srcReg[i];
|
||||
if (r == 0 || r >= nreg)
|
||||
continue;
|
||||
|
||||
rcRegion& reg = regions[r];
|
||||
reg.count++;
|
||||
reg.spanCount++;
|
||||
|
||||
|
||||
|
||||
// Update floors.
|
||||
for (int j = (int)c.index; j < ni; ++j)
|
||||
{
|
||||
if (i == j) continue;
|
||||
unsigned short floorId = src[j*2];
|
||||
unsigned short floorId = srcReg[j];
|
||||
if (floorId == 0 || floorId >= nreg)
|
||||
continue;
|
||||
addUniqueFloorRegion(reg, floorId);
|
||||
@@ -717,11 +743,13 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize,
|
||||
if (reg.connections.size() > 0)
|
||||
continue;
|
||||
|
||||
reg.areaType = chf.areas[i];
|
||||
|
||||
// Check if this cell is next to a border.
|
||||
int ndir = -1;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (isSolidEdge(chf, src, x, y, i, dir))
|
||||
if (isSolidEdge(chf, srcReg, x, y, i, dir))
|
||||
{
|
||||
ndir = dir;
|
||||
break;
|
||||
@@ -732,33 +760,78 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize,
|
||||
{
|
||||
// The cell is at border.
|
||||
// Walk around the contour to find all the neighbours.
|
||||
walkContour(x, y, i, ndir, chf, src, reg.connections);
|
||||
walkContour(x, y, i, ndir, chf, srcReg, reg.connections);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove too small unconnected regions.
|
||||
|
||||
// Remove too small regions.
|
||||
rcIntArray stack(32);
|
||||
rcIntArray trace(32);
|
||||
for (int i = 0; i < nreg; ++i)
|
||||
{
|
||||
rcRegion& reg = regions[i];
|
||||
if (reg.id == 0 || (reg.id & RC_BORDER_REG))
|
||||
continue;
|
||||
if (reg.count == 0)
|
||||
continue;
|
||||
if (reg.spanCount == 0)
|
||||
continue;
|
||||
if (reg.visited)
|
||||
continue;
|
||||
|
||||
if (reg.connections.size() == 1 && reg.connections[0] == 0)
|
||||
// Count the total size of all the connected regions.
|
||||
// Also keep track of the regions connects to a tile border.
|
||||
bool connectsToBorder = false;
|
||||
int spanCount = 0;
|
||||
stack.resize(0);
|
||||
trace.resize(0);
|
||||
|
||||
reg.visited = true;
|
||||
stack.push(i);
|
||||
|
||||
while (stack.size())
|
||||
{
|
||||
if (reg.count < minRegionSize)
|
||||
// Pop
|
||||
int ri = stack.pop();
|
||||
|
||||
rcRegion& creg = regions[ri];
|
||||
|
||||
spanCount += creg.spanCount;
|
||||
trace.push(ri);
|
||||
|
||||
for (int j = 0; j < creg.connections.size(); ++j)
|
||||
{
|
||||
// Non-connected small region, remove.
|
||||
reg.count = 0;
|
||||
reg.id = 0;
|
||||
if (creg.connections[j] & RC_BORDER_REG)
|
||||
{
|
||||
connectsToBorder = true;
|
||||
continue;
|
||||
}
|
||||
rcRegion& nreg = regions[creg.connections[j]];
|
||||
if (nreg.visited)
|
||||
continue;
|
||||
if (nreg.id == 0 || (nreg.id & RC_BORDER_REG))
|
||||
continue;
|
||||
// Visit
|
||||
stack.push(nreg.id);
|
||||
nreg.visited = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the accumulated regions size is too small, remove it.
|
||||
// Do not remove areas which connect to tile borders
|
||||
// as their size cannot be estimated correctly and removing them
|
||||
// can potentially remove necessary areas.
|
||||
if (spanCount < minRegionArea && !connectsToBorder)
|
||||
{
|
||||
// Kill all visited regions.
|
||||
for (int j = 0; j < trace.size(); ++j)
|
||||
{
|
||||
regions[trace[j]].spanCount = 0;
|
||||
regions[trace[j]].id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Merge too small regions to neighbour regions.
|
||||
int mergeCount = 0 ;
|
||||
do
|
||||
@@ -768,14 +841,14 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize,
|
||||
{
|
||||
rcRegion& reg = regions[i];
|
||||
if (reg.id == 0 || (reg.id & RC_BORDER_REG))
|
||||
continue;
|
||||
if (reg.count == 0)
|
||||
continue;
|
||||
if (reg.spanCount == 0)
|
||||
continue;
|
||||
|
||||
|
||||
// Check to see if the region should be merged.
|
||||
if (reg.count > mergeRegionSize && isRegionConnectedToBorder(reg))
|
||||
if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg))
|
||||
continue;
|
||||
|
||||
|
||||
// Small region with more than 1 connection.
|
||||
// Or region which is not connected to a border at all.
|
||||
// Find smallest neighbour region that connects to this one.
|
||||
@@ -786,11 +859,11 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize,
|
||||
if (reg.connections[j] & RC_BORDER_REG) continue;
|
||||
rcRegion& mreg = regions[reg.connections[j]];
|
||||
if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue;
|
||||
if (mreg.count < smallest &&
|
||||
canMergeWithRegion(reg, mreg.id) &&
|
||||
canMergeWithRegion(mreg, reg.id))
|
||||
if (mreg.spanCount < smallest &&
|
||||
canMergeWithRegion(reg, mreg) &&
|
||||
canMergeWithRegion(mreg, reg))
|
||||
{
|
||||
smallest = mreg.count;
|
||||
smallest = mreg.spanCount;
|
||||
mergeId = mreg.id;
|
||||
}
|
||||
}
|
||||
@@ -821,16 +894,16 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize,
|
||||
}
|
||||
}
|
||||
while (mergeCount > 0);
|
||||
|
||||
|
||||
// Compress region Ids.
|
||||
for (int i = 0; i < nreg; ++i)
|
||||
{
|
||||
regions[i].remap = false;
|
||||
if (regions[i].id == 0) continue; // Skip nil regions.
|
||||
if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions.
|
||||
if (regions[i].id == 0) continue; // Skip nil regions.
|
||||
if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions.
|
||||
regions[i].remap = true;
|
||||
}
|
||||
|
||||
|
||||
unsigned short regIdGen = 0;
|
||||
for (int i = 0; i < nreg; ++i)
|
||||
{
|
||||
@@ -848,91 +921,88 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize,
|
||||
}
|
||||
}
|
||||
maxRegionId = regIdGen;
|
||||
|
||||
|
||||
// Remap regions.
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
{
|
||||
if ((src[i*2] & RC_BORDER_REG) == 0)
|
||||
src[i*2] = regions[src[i*2]].id;
|
||||
if ((srcReg[i] & RC_BORDER_REG) == 0)
|
||||
srcReg[i] = regions[srcReg[i]].id;
|
||||
}
|
||||
|
||||
delete [] regions;
|
||||
for (int i = 0; i < nreg; ++i)
|
||||
regions[i].~rcRegion();
|
||||
rcFree(regions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rcBuildDistanceField(rcCompactHeightfield& chf)
|
||||
/// @par
|
||||
///
|
||||
/// This is usually the second to the last step in creating a fully built
|
||||
/// compact heightfield. This step is required before regions are built
|
||||
/// using #rcBuildRegions or #rcBuildRegionsMonotone.
|
||||
///
|
||||
/// After this step, the distance data is available via the rcCompactHeightfield::maxDistance
|
||||
/// and rcCompactHeightfield::dist fields.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcBuildRegions, rcBuildRegionsMonotone
|
||||
bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
rcAssert(ctx);
|
||||
|
||||
unsigned short* dist0 = new unsigned short[chf.spanCount];
|
||||
if (!dist0)
|
||||
ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD);
|
||||
|
||||
if (chf.dist)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dist0' (%d).", chf.spanCount);
|
||||
rcFree(chf.dist);
|
||||
chf.dist = 0;
|
||||
}
|
||||
|
||||
unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!src)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
unsigned short* dist1 = new unsigned short[chf.spanCount];
|
||||
if (!dist1)
|
||||
unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!dst)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dist1' (%d).", chf.spanCount);
|
||||
delete [] dist0;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount);
|
||||
rcFree(src);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned short* src = dist0;
|
||||
unsigned short* dst = dist1;
|
||||
|
||||
unsigned short maxDist = 0;
|
||||
|
||||
rcTimeVal distStartTime = rcGetPerformanceTimer();
|
||||
|
||||
if (calculateDistanceField(chf, src, dst, maxDist) != src)
|
||||
rcSwap(src, dst);
|
||||
ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
|
||||
|
||||
calculateDistanceField(chf, src, maxDist);
|
||||
chf.maxDistance = maxDist;
|
||||
|
||||
rcTimeVal distEndTime = rcGetPerformanceTimer();
|
||||
ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
|
||||
|
||||
rcTimeVal blurStartTime = rcGetPerformanceTimer();
|
||||
ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
|
||||
|
||||
// Blur
|
||||
if (boxBlur(chf, 1, src, dst) != src)
|
||||
rcSwap(src, dst);
|
||||
|
||||
// Store distance.
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
chf.spans[i].dist = src[i];
|
||||
chf.dist = src;
|
||||
|
||||
rcTimeVal blurEndTime = rcGetPerformanceTimer();
|
||||
ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD);
|
||||
|
||||
delete [] dist0;
|
||||
delete [] dist1;
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
/* if (rcGetLog())
|
||||
{
|
||||
rcGetLog()->log(RC_LOG_PROGRESS, "Build distance field: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
|
||||
rcGetLog()->log(RC_LOG_PROGRESS, " - dist: %.3f ms", rcGetDeltaTimeUsec(distStartTime, distEndTime)/1000.0f);
|
||||
rcGetLog()->log(RC_LOG_PROGRESS, " - blur: %.3f ms", rcGetDeltaTimeUsec(blurStartTime, blurEndTime)/1000.0f);
|
||||
}*/
|
||||
if (rcGetBuildTimes())
|
||||
{
|
||||
rcGetBuildTimes()->buildDistanceField += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
rcGetBuildTimes()->buildDistanceFieldDist += rcGetDeltaTimeUsec(distStartTime, distEndTime);
|
||||
rcGetBuildTimes()->buildDistanceFieldBlur += rcGetDeltaTimeUsec(blurStartTime, blurEndTime);
|
||||
}
|
||||
rcFree(dst);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void paintRectRegion(int minx, int maxx, int miny, int maxy,
|
||||
unsigned short regId, unsigned short minLevel,
|
||||
rcCompactHeightfield& chf, unsigned short* src)
|
||||
static void paintRectRegion(int minx, int maxx, int miny, int maxy, unsigned short regId,
|
||||
rcCompactHeightfield& chf, unsigned short* srcReg)
|
||||
{
|
||||
const int w = chf.width;
|
||||
const int w = chf.width;
|
||||
for (int y = miny; y < maxy; ++y)
|
||||
{
|
||||
for (int x = minx; x < maxx; ++x)
|
||||
@@ -940,77 +1010,283 @@ static void paintRectRegion(int minx, int maxx, int miny, int maxy,
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (chf.spans[i].dist >= minLevel)
|
||||
src[i*2] = regId;
|
||||
if (chf.areas[i] != RC_NULL_AREA)
|
||||
srcReg[i] = regId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool rcBuildRegions(rcCompactHeightfield& chf,
|
||||
int walkableRadius, int borderSize,
|
||||
int minRegionSize, int mergeRegionSize)
|
||||
|
||||
static const unsigned short RC_NULL_NEI = 0xffff;
|
||||
|
||||
struct rcSweepSpan
|
||||
{
|
||||
rcTimeVal startTime = rcGetPerformanceTimer();
|
||||
unsigned short rid; // row id
|
||||
unsigned short id; // region id
|
||||
unsigned short ns; // number samples
|
||||
unsigned short nei; // neighbour id
|
||||
};
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour.
|
||||
/// Contours will form simple polygons.
|
||||
///
|
||||
/// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be
|
||||
/// re-assigned to the zero (null) region.
|
||||
///
|
||||
/// Partitioning can result in smaller than necessary regions. @p mergeRegionArea helps
|
||||
/// reduce unecessarily small regions.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// The region data will be available via the rcCompactHeightfield::maxRegions
|
||||
/// and rcCompactSpan::reg fields.
|
||||
///
|
||||
/// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig
|
||||
bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int minRegionArea, const int mergeRegionArea)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_REGIONS);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
unsigned short id = 1;
|
||||
|
||||
rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!srcReg)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
|
||||
|
||||
const int nsweeps = rcMax(chf.width,chf.height);
|
||||
rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP);
|
||||
if (!sweeps)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Mark border regions.
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// Make sure border will not overflow.
|
||||
const int bw = rcMin(w, borderSize);
|
||||
const int bh = rcMin(h, borderSize);
|
||||
// Paint regions
|
||||
paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
|
||||
paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
|
||||
paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++;
|
||||
paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++;
|
||||
|
||||
chf.borderSize = borderSize;
|
||||
}
|
||||
|
||||
rcIntArray prev(256);
|
||||
|
||||
// Sweep one line at a time.
|
||||
for (int y = borderSize; y < h-borderSize; ++y)
|
||||
{
|
||||
// Collect spans from this row.
|
||||
prev.resize(id+1);
|
||||
memset(&prev[0],0,sizeof(int)*id);
|
||||
unsigned short rid = 1;
|
||||
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA) continue;
|
||||
|
||||
// -x
|
||||
unsigned short previd = 0;
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
const int ay = y + rcGetDirOffsetY(0);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
|
||||
previd = srcReg[ai];
|
||||
}
|
||||
|
||||
if (!previd)
|
||||
{
|
||||
previd = rid++;
|
||||
sweeps[previd].rid = previd;
|
||||
sweeps[previd].ns = 0;
|
||||
sweeps[previd].nei = 0;
|
||||
}
|
||||
|
||||
// -y
|
||||
if (rcGetCon(s,3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
const int ay = y + rcGetDirOffsetY(3);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
|
||||
{
|
||||
unsigned short nr = srcReg[ai];
|
||||
if (!sweeps[previd].nei || sweeps[previd].nei == nr)
|
||||
{
|
||||
sweeps[previd].nei = nr;
|
||||
sweeps[previd].ns++;
|
||||
prev[nr]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
sweeps[previd].nei = RC_NULL_NEI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcReg[i] = previd;
|
||||
}
|
||||
}
|
||||
|
||||
// Create unique ID.
|
||||
for (int i = 1; i < rid; ++i)
|
||||
{
|
||||
if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 &&
|
||||
prev[sweeps[i].nei] == (int)sweeps[i].ns)
|
||||
{
|
||||
sweeps[i].id = sweeps[i].nei;
|
||||
}
|
||||
else
|
||||
{
|
||||
sweeps[i].id = id++;
|
||||
}
|
||||
}
|
||||
|
||||
// Remap IDs
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (srcReg[i] > 0 && srcReg[i] < rid)
|
||||
srcReg[i] = sweeps[srcReg[i]].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
|
||||
|
||||
// Filter out small regions.
|
||||
chf.maxRegions = id;
|
||||
if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
|
||||
return false;
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
|
||||
|
||||
// Store the result out.
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
chf.spans[i].reg = srcReg[i];
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour.
|
||||
/// Contours will form simple polygons.
|
||||
///
|
||||
/// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be
|
||||
/// re-assigned to the zero (null) region.
|
||||
///
|
||||
/// Watershed partitioning can result in smaller than necessary regions, especially in diagonal corridors.
|
||||
/// @p mergeRegionArea helps reduce unecessarily small regions.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// The region data will be available via the rcCompactHeightfield::maxRegions
|
||||
/// and rcCompactSpan::reg fields.
|
||||
///
|
||||
/// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig
|
||||
bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int minRegionArea, const int mergeRegionArea)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_REGIONS);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
unsigned short* tmp1 = new unsigned short[chf.spanCount*2];
|
||||
if (!tmp1)
|
||||
rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP);
|
||||
if (!buf)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'tmp1' (%d).", chf.spanCount*2);
|
||||
return false;
|
||||
}
|
||||
unsigned short* tmp2 = new unsigned short[chf.spanCount*2];
|
||||
if (!tmp2)
|
||||
{
|
||||
if (rcGetLog())
|
||||
rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'tmp2' (%d).", chf.spanCount*2);
|
||||
delete [] tmp1;
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4);
|
||||
return false;
|
||||
}
|
||||
|
||||
rcTimeVal regStartTime = rcGetPerformanceTimer();
|
||||
ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
|
||||
|
||||
rcIntArray stack(1024);
|
||||
rcIntArray visited(1024);
|
||||
|
||||
unsigned short* src = tmp1;
|
||||
unsigned short* dst = tmp2;
|
||||
unsigned short* srcReg = buf;
|
||||
unsigned short* srcDist = buf+chf.spanCount;
|
||||
unsigned short* dstReg = buf+chf.spanCount*2;
|
||||
unsigned short* dstDist = buf+chf.spanCount*3;
|
||||
|
||||
memset(src, 0, sizeof(unsigned short) * chf.spanCount*2);
|
||||
memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount);
|
||||
memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount);
|
||||
|
||||
unsigned short regionId = 1;
|
||||
unsigned short level = (chf.maxDistance+1) & ~1;
|
||||
|
||||
unsigned short minLevel = (unsigned short)(walkableRadius*2);
|
||||
|
||||
const int expandIters = 4 + walkableRadius * 2;
|
||||
|
||||
// Mark border regions.
|
||||
paintRectRegion(0, borderSize, 0, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++;
|
||||
paintRectRegion(w-borderSize, w, 0, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++;
|
||||
paintRectRegion(0, w, 0, borderSize, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++;
|
||||
paintRectRegion(0, w, h-borderSize, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++;
|
||||
// TODO: Figure better formula, expandIters defines how much the
|
||||
// watershed "overflows" and simplifies the regions. Tying it to
|
||||
// agent radius was usually good indication how greedy it could be.
|
||||
// const int expandIters = 4 + walkableRadius * 2;
|
||||
const int expandIters = 8;
|
||||
|
||||
rcTimeVal expTime = 0;
|
||||
rcTimeVal floodTime = 0;
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// Make sure border will not overflow.
|
||||
const int bw = rcMin(w, borderSize);
|
||||
const int bh = rcMin(h, borderSize);
|
||||
// Paint regions
|
||||
paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
|
||||
paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
|
||||
paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
|
||||
paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
|
||||
|
||||
chf.borderSize = borderSize;
|
||||
}
|
||||
|
||||
while (level > minLevel)
|
||||
while (level > 0)
|
||||
{
|
||||
level = level >= 2 ? level-2 : 0;
|
||||
|
||||
rcTimeVal expStartTime = rcGetPerformanceTimer();
|
||||
ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
|
||||
|
||||
// Expand current regions until no empty connected cells found.
|
||||
if (expandRegions(expandIters, level, chf, src, dst, stack) != src)
|
||||
rcSwap(src, dst);
|
||||
if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
|
||||
{
|
||||
rcSwap(srcReg, dstReg);
|
||||
rcSwap(srcDist, dstDist);
|
||||
}
|
||||
|
||||
expTime += rcGetPerformanceTimer() - expStartTime;
|
||||
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
|
||||
|
||||
rcTimeVal floodStartTime = rcGetPerformanceTimer();
|
||||
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
|
||||
|
||||
// Mark new regions with IDs.
|
||||
for (int y = 0; y < h; ++y)
|
||||
@@ -1020,61 +1296,41 @@ bool rcBuildRegions(rcCompactHeightfield& chf,
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (chf.spans[i].dist < level || src[i*2] != 0)
|
||||
if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
|
||||
if (floodRegion(x, y, i, minLevel, level, regionId, chf, src, stack))
|
||||
if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
|
||||
regionId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
floodTime += rcGetPerformanceTimer() - floodStartTime;
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
|
||||
}
|
||||
|
||||
// Expand current regions until no empty connected cells found.
|
||||
if (expandRegions(expandIters*8, minLevel, chf, src, dst, stack) != src)
|
||||
rcSwap(src, dst);
|
||||
if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
|
||||
{
|
||||
rcSwap(srcReg, dstReg);
|
||||
rcSwap(srcDist, dstDist);
|
||||
}
|
||||
|
||||
rcTimeVal regEndTime = rcGetPerformanceTimer();
|
||||
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
|
||||
|
||||
rcTimeVal filterStartTime = rcGetPerformanceTimer();
|
||||
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
|
||||
|
||||
// Filter out small regions.
|
||||
chf.maxRegions = regionId;
|
||||
if (!filterSmallRegions(minRegionSize, mergeRegionSize, chf.maxRegions, chf, src))
|
||||
if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
|
||||
return false;
|
||||
|
||||
rcTimeVal filterEndTime = rcGetPerformanceTimer();
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
|
||||
|
||||
// Write the result out.
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
chf.spans[i].reg = src[i*2];
|
||||
chf.spans[i].reg = srcReg[i];
|
||||
|
||||
delete [] tmp1;
|
||||
delete [] tmp2;
|
||||
ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
|
||||
|
||||
rcTimeVal endTime = rcGetPerformanceTimer();
|
||||
|
||||
/* if (rcGetLog())
|
||||
{
|
||||
rcGetLog()->log(RC_LOG_PROGRESS, "Build regions: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
|
||||
rcGetLog()->log(RC_LOG_PROGRESS, " - reg: %.3f ms", rcGetDeltaTimeUsec(regStartTime, regEndTime)/1000.0f);
|
||||
rcGetLog()->log(RC_LOG_PROGRESS, " - exp: %.3f ms", rcGetDeltaTimeUsec(0, expTime)/1000.0f);
|
||||
rcGetLog()->log(RC_LOG_PROGRESS, " - flood: %.3f ms", rcGetDeltaTimeUsec(0, floodTime)/1000.0f);
|
||||
rcGetLog()->log(RC_LOG_PROGRESS, " - filter: %.3f ms", rcGetDeltaTimeUsec(filterStartTime, filterEndTime)/1000.0f);
|
||||
}
|
||||
*/
|
||||
if (rcGetBuildTimes())
|
||||
{
|
||||
rcGetBuildTimes()->buildRegions += rcGetDeltaTimeUsec(startTime, endTime);
|
||||
rcGetBuildTimes()->buildRegionsReg += rcGetDeltaTimeUsec(regStartTime, regEndTime);
|
||||
rcGetBuildTimes()->buildRegionsExp += rcGetDeltaTimeUsec(0, expTime);
|
||||
rcGetBuildTimes()->buildRegionsFlood += rcGetDeltaTimeUsec(0, floodTime);
|
||||
rcGetBuildTimes()->buildRegionsFilter += rcGetDeltaTimeUsec(filterStartTime, filterEndTime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
211
extern/recastnavigation/recast-capi.cpp
vendored
211
extern/recastnavigation/recast-capi.cpp
vendored
@@ -30,6 +30,11 @@
|
||||
#include <math.h>
|
||||
#include "Recast.h"
|
||||
|
||||
static rcContext *sctx;
|
||||
|
||||
#define INIT_SCTX() \
|
||||
if (sctx == NULL) sctx = new rcContext(false)
|
||||
|
||||
int recast_buildMeshAdjacency(unsigned short* polys, const int npolys,
|
||||
const int nverts, const int vertsPerPoly)
|
||||
{
|
||||
@@ -48,109 +53,123 @@ void recast_calcGridSize(const float *bmin, const float *bmax, float cs, int *w,
|
||||
|
||||
struct recast_heightfield *recast_newHeightfield(void)
|
||||
{
|
||||
return (struct recast_heightfield *) (new rcHeightfield);
|
||||
return (struct recast_heightfield *) rcAllocHeightfield();
|
||||
}
|
||||
|
||||
void recast_destroyHeightfield(struct recast_heightfield *heightfield)
|
||||
{
|
||||
delete (rcHeightfield *) heightfield;
|
||||
rcFreeHeightField((rcHeightfield *) heightfield);
|
||||
}
|
||||
|
||||
int recast_createHeightfield(struct recast_heightfield *hf, int width, int height,
|
||||
const float *bmin, const float* bmax, float cs, float ch)
|
||||
{
|
||||
return rcCreateHeightfield(*(rcHeightfield *)hf, width, height, bmin, bmax, cs, ch);
|
||||
INIT_SCTX();
|
||||
return rcCreateHeightfield(sctx, *(rcHeightfield *)hf, width, height, bmin, bmax, cs, ch);
|
||||
}
|
||||
|
||||
void recast_markWalkableTriangles(const float walkableSlopeAngle,const float *verts, int nv,
|
||||
const int *tris, int nt, unsigned char *flags)
|
||||
{
|
||||
rcMarkWalkableTriangles(walkableSlopeAngle, verts, nv, tris, nt, flags);
|
||||
INIT_SCTX();
|
||||
rcMarkWalkableTriangles(sctx, walkableSlopeAngle, verts, nv, tris, nt, flags);
|
||||
}
|
||||
|
||||
void recast_rasterizeTriangles(const float *verts, int nv, const int *tris,
|
||||
const unsigned char *flags, int nt, struct recast_heightfield *solid)
|
||||
{
|
||||
rcRasterizeTriangles(verts, nv, tris, flags, nt, *(rcHeightfield *) solid);
|
||||
INIT_SCTX();
|
||||
rcRasterizeTriangles(sctx, verts, nv, tris, flags, nt, *(rcHeightfield *) solid);
|
||||
}
|
||||
|
||||
void recast_filterLedgeSpans(const int walkableHeight, const int walkableClimb,
|
||||
struct recast_heightfield *solid)
|
||||
{
|
||||
rcFilterLedgeSpans(walkableHeight, walkableClimb, *(rcHeightfield *) solid);
|
||||
INIT_SCTX();
|
||||
rcFilterLedgeSpans(sctx, walkableHeight, walkableClimb, *(rcHeightfield *) solid);
|
||||
}
|
||||
|
||||
void recast_filterWalkableLowHeightSpans(int walkableHeight, struct recast_heightfield *solid)
|
||||
{
|
||||
rcFilterWalkableLowHeightSpans(walkableHeight, *(rcHeightfield *) solid);
|
||||
INIT_SCTX();
|
||||
rcFilterWalkableLowHeightSpans(sctx, walkableHeight, *(rcHeightfield *) solid);
|
||||
}
|
||||
|
||||
void recast_filterLowHangingWalkableObstacles(const int walkableClimb, struct recast_heightfield *solid)
|
||||
{
|
||||
INIT_SCTX();
|
||||
rcFilterLowHangingWalkableObstacles(sctx, walkableClimb, *(rcHeightfield *) solid);
|
||||
}
|
||||
|
||||
struct recast_compactHeightfield *recast_newCompactHeightfield(void)
|
||||
{
|
||||
return (struct recast_compactHeightfield *) (new rcCompactHeightfield);
|
||||
return (struct recast_compactHeightfield *) rcAllocCompactHeightfield();
|
||||
}
|
||||
|
||||
void recast_destroyCompactHeightfield(struct recast_compactHeightfield *compactHeightfield)
|
||||
{
|
||||
delete (rcCompactHeightfield *) compactHeightfield;
|
||||
rcFreeCompactHeightfield( (rcCompactHeightfield *) compactHeightfield);
|
||||
}
|
||||
|
||||
int recast_buildCompactHeightfield(const int walkableHeight, const int walkableClimb,
|
||||
unsigned char flags, struct recast_heightfield *hf, struct recast_compactHeightfield *chf)
|
||||
struct recast_heightfield *hf, struct recast_compactHeightfield *chf)
|
||||
{
|
||||
int rcFlags = 0;
|
||||
INIT_SCTX();
|
||||
return rcBuildCompactHeightfield(sctx, walkableHeight, walkableClimb,
|
||||
*(rcHeightfield *) hf, *(rcCompactHeightfield *) chf);
|
||||
}
|
||||
|
||||
if(flags & RECAST_WALKABLE)
|
||||
rcFlags |= RC_WALKABLE;
|
||||
|
||||
if(flags & RECAST_REACHABLE)
|
||||
rcFlags |= RC_REACHABLE;
|
||||
|
||||
return rcBuildCompactHeightfield(walkableHeight, walkableClimb, rcFlags,
|
||||
*(rcHeightfield *) hf, *(rcCompactHeightfield *) chf);
|
||||
int recast_erodeWalkableArea(int radius, struct recast_compactHeightfield *chf)
|
||||
{
|
||||
INIT_SCTX();
|
||||
return rcErodeWalkableArea(sctx, radius, *(rcCompactHeightfield *) chf);
|
||||
}
|
||||
|
||||
int recast_buildDistanceField(struct recast_compactHeightfield *chf)
|
||||
{
|
||||
return rcBuildDistanceField(*(rcCompactHeightfield *) chf);
|
||||
INIT_SCTX();
|
||||
return rcBuildDistanceField(sctx, *(rcCompactHeightfield *) chf);
|
||||
}
|
||||
|
||||
int recast_buildRegions(struct recast_compactHeightfield *chf, int walkableRadius, int borderSize,
|
||||
int recast_buildRegions(struct recast_compactHeightfield *chf, int borderSize,
|
||||
int minRegionSize, int mergeRegionSize)
|
||||
{
|
||||
return rcBuildRegions(*(rcCompactHeightfield *) chf, walkableRadius, borderSize,
|
||||
INIT_SCTX();
|
||||
return rcBuildRegions(sctx, *(rcCompactHeightfield *) chf, borderSize,
|
||||
minRegionSize, mergeRegionSize);
|
||||
}
|
||||
|
||||
struct recast_contourSet *recast_newContourSet(void)
|
||||
{
|
||||
return (struct recast_contourSet *) (new rcContourSet);
|
||||
return (struct recast_contourSet *) rcAllocContourSet();
|
||||
}
|
||||
|
||||
void recast_destroyContourSet(struct recast_contourSet *contourSet)
|
||||
{
|
||||
delete (rcContourSet *) contourSet;
|
||||
rcFreeContourSet((rcContourSet *) contourSet);
|
||||
}
|
||||
|
||||
int recast_buildContours(struct recast_compactHeightfield *chf,
|
||||
const float maxError, const int maxEdgeLen, struct recast_contourSet *cset)
|
||||
{
|
||||
return rcBuildContours(*(rcCompactHeightfield *) chf, maxError, maxEdgeLen, *(rcContourSet *) cset);
|
||||
INIT_SCTX();
|
||||
return rcBuildContours(sctx, *(rcCompactHeightfield *) chf, maxError, maxEdgeLen, *(rcContourSet *) cset);
|
||||
}
|
||||
|
||||
struct recast_polyMesh *recast_newPolyMesh(void)
|
||||
{
|
||||
return (recast_polyMesh *) (new rcPolyMesh);
|
||||
return (recast_polyMesh *) rcAllocPolyMesh();
|
||||
}
|
||||
|
||||
void recast_destroyPolyMesh(struct recast_polyMesh *polyMesh)
|
||||
{
|
||||
delete (rcPolyMesh *) polyMesh;
|
||||
rcFreePolyMesh((rcPolyMesh *) polyMesh);
|
||||
}
|
||||
|
||||
int recast_buildPolyMesh(struct recast_contourSet *cset, int nvp, struct recast_polyMesh *mesh)
|
||||
{
|
||||
return rcBuildPolyMesh(*(rcContourSet *) cset, nvp, * (rcPolyMesh *) mesh);
|
||||
INIT_SCTX();
|
||||
return rcBuildPolyMesh(sctx, *(rcContourSet *) cset, nvp, * (rcPolyMesh *) mesh);
|
||||
}
|
||||
|
||||
unsigned short *recast_polyMeshGetVerts(struct recast_polyMesh *mesh, int *nverts)
|
||||
@@ -206,18 +225,19 @@ unsigned short *recast_polyMeshGetPolys(struct recast_polyMesh *mesh, int *npoly
|
||||
|
||||
struct recast_polyMeshDetail *recast_newPolyMeshDetail(void)
|
||||
{
|
||||
return (struct recast_polyMeshDetail *) (new rcPolyMeshDetail);
|
||||
return (struct recast_polyMeshDetail *) rcAllocPolyMeshDetail();
|
||||
}
|
||||
|
||||
void recast_destroyPolyMeshDetail(struct recast_polyMeshDetail *polyMeshDetail)
|
||||
{
|
||||
delete (rcPolyMeshDetail *) polyMeshDetail;
|
||||
rcFreePolyMeshDetail((rcPolyMeshDetail *) polyMeshDetail);
|
||||
}
|
||||
|
||||
int recast_buildPolyMeshDetail(const struct recast_polyMesh *mesh, const struct recast_compactHeightfield *chf,
|
||||
const float sampleDist, const float sampleMaxError, struct recast_polyMeshDetail *dmesh)
|
||||
{
|
||||
return rcBuildPolyMeshDetail(*(rcPolyMesh *) mesh, *(rcCompactHeightfield *) chf,
|
||||
INIT_SCTX();
|
||||
return rcBuildPolyMeshDetail(sctx, *(rcPolyMesh *) mesh, *(rcCompactHeightfield *) chf,
|
||||
sampleDist, sampleMaxError, *(rcPolyMeshDetail *) dmesh);
|
||||
}
|
||||
|
||||
@@ -241,7 +261,7 @@ unsigned char *recast_polyMeshDetailGetTris(struct recast_polyMeshDetail *mesh,
|
||||
return dmesh->tris;
|
||||
}
|
||||
|
||||
unsigned short *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mesh, int *nmeshes)
|
||||
unsigned int *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mesh, int *nmeshes)
|
||||
{
|
||||
rcPolyMeshDetail *dmesh = (rcPolyMeshDetail *)mesh;
|
||||
|
||||
@@ -250,3 +270,130 @@ unsigned short *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mes
|
||||
|
||||
return dmesh->meshes;
|
||||
}
|
||||
|
||||
// qsort based on FreeBSD source (libkern\qsort.c)
|
||||
typedef int cmp_t(void *, const void *, const void *);
|
||||
static inline char *med3(char *, char *, char *, cmp_t *, void *);
|
||||
static inline void swapfunc(char *, char *, int, int);
|
||||
|
||||
#define min(a, b) (a) < (b) ? a : b
|
||||
#define swapcode(TYPE, parmi, parmj, n) \
|
||||
{ \
|
||||
long i = (n) / sizeof (TYPE); \
|
||||
TYPE *pi = (TYPE *) (parmi); \
|
||||
TYPE *pj = (TYPE *) (parmj); \
|
||||
do { \
|
||||
TYPE t = *pi; \
|
||||
*pi++ = *pj; \
|
||||
*pj++ = t; \
|
||||
} while (--i > 0); \
|
||||
}
|
||||
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
|
||||
es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
|
||||
|
||||
static inline void swapfunc(char* a, char* b, int n, int swaptype)
|
||||
{
|
||||
if(swaptype <= 1)
|
||||
swapcode(long, a, b, n)
|
||||
else
|
||||
swapcode(char, a, b, n)
|
||||
}
|
||||
|
||||
#define swap(a, b) \
|
||||
if (swaptype == 0) { \
|
||||
long t = *(long *)(a); \
|
||||
*(long *)(a) = *(long *)(b);\
|
||||
*(long *)(b) = t; \
|
||||
} else \
|
||||
swapfunc(a, b, es, swaptype)
|
||||
|
||||
#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
|
||||
#define CMP(t, x, y) (cmp((t), (x), (y)))
|
||||
|
||||
static inline char * med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk)
|
||||
{
|
||||
return CMP(thunk, a, b) < 0 ?
|
||||
(CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a ))
|
||||
:(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c ));
|
||||
}
|
||||
|
||||
void recast_qsort(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp)
|
||||
{
|
||||
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
|
||||
int d, r, swaptype, swap_cnt;
|
||||
|
||||
loop:
|
||||
SWAPINIT(a, es);
|
||||
swap_cnt = 0;
|
||||
if (n < 7) {
|
||||
for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
|
||||
for (pl = pm;
|
||||
pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
|
||||
pl -= es)
|
||||
swap(pl, pl - es);
|
||||
return;
|
||||
}
|
||||
pm = (char *)a + (n / 2) * es;
|
||||
if (n > 7) {
|
||||
pl = (char *)a;
|
||||
pn = (char *)a + (n - 1) * es;
|
||||
if (n > 40) {
|
||||
d = (n / 8) * es;
|
||||
pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk);
|
||||
pm = med3(pm - d, pm, pm + d, cmp, thunk);
|
||||
pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk);
|
||||
}
|
||||
pm = med3(pl, pm, pn, cmp, thunk);
|
||||
}
|
||||
swap((char *)a, pm);
|
||||
pa = pb = (char *)a + es;
|
||||
|
||||
pc = pd = (char *)a + (n - 1) * es;
|
||||
for (;;) {
|
||||
while (pb <= pc && (r = CMP(thunk, pb, a)) <= 0) {
|
||||
if (r == 0) {
|
||||
swap_cnt = 1;
|
||||
swap(pa, pb);
|
||||
pa += es;
|
||||
}
|
||||
pb += es;
|
||||
}
|
||||
while (pb <= pc && (r = CMP(thunk, pc, a)) >= 0) {
|
||||
if (r == 0) {
|
||||
swap_cnt = 1;
|
||||
swap(pc, pd);
|
||||
pd -= es;
|
||||
}
|
||||
pc -= es;
|
||||
}
|
||||
if (pb > pc)
|
||||
break;
|
||||
swap(pb, pc);
|
||||
swap_cnt = 1;
|
||||
pb += es;
|
||||
pc -= es;
|
||||
}
|
||||
if (swap_cnt == 0) { /* Switch to insertion sort */
|
||||
for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
|
||||
for (pl = pm;
|
||||
pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
|
||||
pl -= es)
|
||||
swap(pl, pl - es);
|
||||
return;
|
||||
}
|
||||
|
||||
pn = (char *)a + n * es;
|
||||
r = min(pa - (char *)a, pb - pa);
|
||||
vecswap((char *)a, pb - r, r);
|
||||
r = min(pd - pc, pn - pd - es);
|
||||
vecswap(pb, pn - r, r);
|
||||
if ((r = pb - pa) > es)
|
||||
recast_qsort(a, r / es, es, thunk, cmp);
|
||||
if ((r = pd - pc) > es) {
|
||||
/* Iterate rather than recurse to save stack space */
|
||||
a = pn - r;
|
||||
n = r / es;
|
||||
goto loop;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
extern/recastnavigation/recast-capi.h
vendored
18
extern/recastnavigation/recast-capi.h
vendored
@@ -28,6 +28,9 @@
|
||||
#ifndef RECAST_C_API_H
|
||||
#define RECAST_C_API_H
|
||||
|
||||
// for size_t
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -69,16 +72,20 @@ void recast_filterLedgeSpans(const int walkableHeight, const int walkableClimb,
|
||||
|
||||
void recast_filterWalkableLowHeightSpans(int walkableHeight, struct recast_heightfield *solid);
|
||||
|
||||
void recast_filterLowHangingWalkableObstacles(const int walkableClimb, struct recast_heightfield *solid);
|
||||
|
||||
struct recast_compactHeightfield *recast_newCompactHeightfield(void);
|
||||
|
||||
void recast_destroyCompactHeightfield(struct recast_compactHeightfield *compactHeightfield);
|
||||
|
||||
int recast_buildCompactHeightfield(const int walkableHeight, const int walkableClimb,
|
||||
unsigned char flags, struct recast_heightfield *hf, struct recast_compactHeightfield *chf);
|
||||
struct recast_heightfield *hf, struct recast_compactHeightfield *chf);
|
||||
|
||||
int recast_erodeWalkableArea(int radius, struct recast_compactHeightfield *chf);
|
||||
|
||||
int recast_buildDistanceField(struct recast_compactHeightfield *chf);
|
||||
|
||||
int recast_buildRegions(struct recast_compactHeightfield *chf, int walkableRadius, int borderSize,
|
||||
int recast_buildRegions(struct recast_compactHeightfield *chf, int borderSize,
|
||||
int minRegionSize, int mergeRegionSize);
|
||||
|
||||
/* Contour set */
|
||||
@@ -119,7 +126,12 @@ float *recast_polyMeshDetailGetVerts(struct recast_polyMeshDetail *mesh, int *nv
|
||||
|
||||
unsigned char *recast_polyMeshDetailGetTris(struct recast_polyMeshDetail *mesh, int *ntris);
|
||||
|
||||
unsigned short *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mesh, int *nmeshes);
|
||||
unsigned int *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mesh, int *nmeshes);
|
||||
|
||||
/* utility function: quick sort reentrant */
|
||||
typedef int recast_cmp_t(void *ctx, const void *a, const void *b);
|
||||
|
||||
void recast_qsort(void *a, size_t n, size_t es, void *thunk, recast_cmp_t *cmp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -328,14 +328,10 @@ struct SortContext
|
||||
const int* trisToFacesMap;
|
||||
};
|
||||
|
||||
/* XXX: not thread-safe, but it's called only from modifiers stack
|
||||
which isn't threaded. Anyway, better to avoid this in the future */
|
||||
static struct SortContext *_qsort_context;
|
||||
|
||||
static int compareByData(const void * a, const void * b)
|
||||
static int compareByData(void *ctx, const void * a, const void * b)
|
||||
{
|
||||
return ( _qsort_context->recastData[_qsort_context->trisToFacesMap[*(int*)a]] -
|
||||
_qsort_context->recastData[_qsort_context->trisToFacesMap[*(int*)b]] );
|
||||
return (((struct SortContext *)ctx)->recastData[((struct SortContext *)ctx)->trisToFacesMap[*(int*)a]] -
|
||||
((struct SortContext *)ctx)->recastData[((struct SortContext *)ctx)->trisToFacesMap[*(int*)b]] );
|
||||
}
|
||||
|
||||
int buildNavMeshData(const int nverts, const float* verts,
|
||||
@@ -367,8 +363,7 @@ int buildNavMeshData(const int nverts, const float* verts,
|
||||
trisMapping[i]=i;
|
||||
context.recastData = recastData;
|
||||
context.trisToFacesMap = trisToFacesMap;
|
||||
_qsort_context = &context;
|
||||
qsort(trisMapping, ntris, sizeof(int), compareByData);
|
||||
recast_qsort(trisMapping, ntris, sizeof(int), &context, compareByData);
|
||||
|
||||
//search first valid triangle - triangle of convex polygon
|
||||
validTriStart = -1;
|
||||
|
||||
@@ -526,7 +526,7 @@ Scene *add_scene(const char *name)
|
||||
sce->gm.recastData.agentradius = 0.6f;
|
||||
sce->gm.recastData.edgemaxlen = 12.0f;
|
||||
sce->gm.recastData.edgemaxerror = 1.3f;
|
||||
sce->gm.recastData.regionminsize = 50.f;
|
||||
sce->gm.recastData.regionminsize = 8.f;
|
||||
sce->gm.recastData.regionmergesize = 20.f;
|
||||
sce->gm.recastData.vertsperpoly = 6;
|
||||
sce->gm.recastData.detailsampledist = 6.0f;
|
||||
|
||||
@@ -12101,7 +12101,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
|
||||
if(sce->gm.recastData.edgemaxerror == 0.0f)
|
||||
sce->gm.recastData.edgemaxerror = 1.3f;
|
||||
if(sce->gm.recastData.regionminsize == 0.0f)
|
||||
sce->gm.recastData.regionminsize = 50.f;
|
||||
sce->gm.recastData.regionminsize = 8.f;
|
||||
if(sce->gm.recastData.regionmergesize == 0.0f)
|
||||
sce->gm.recastData.regionmergesize = 20.f;
|
||||
if(sce->gm.recastData.vertsperpoly<3)
|
||||
|
||||
@@ -174,7 +174,7 @@ static int buildNavMesh(const RecastData *recastParams, int nverts, float *verts
|
||||
struct recast_compactHeightfield* chf;
|
||||
struct recast_contourSet *cset;
|
||||
int width, height, walkableHeight, walkableClimb, walkableRadius;
|
||||
int minRegionSize, mergeRegionSize, maxEdgeLen;
|
||||
int minRegionArea, mergeRegionArea, maxEdgeLen;
|
||||
float detailSampleDist, detailSampleMaxError;
|
||||
|
||||
recast_calcBounds(verts, nverts, bmin, bmax);
|
||||
@@ -183,8 +183,8 @@ static int buildNavMesh(const RecastData *recastParams, int nverts, float *verts
|
||||
walkableHeight= (int)ceilf(recastParams->agentheight/ recastParams->cellheight);
|
||||
walkableClimb= (int)floorf(recastParams->agentmaxclimb / recastParams->cellheight);
|
||||
walkableRadius= (int)ceilf(recastParams->agentradius / recastParams->cellsize);
|
||||
minRegionSize= (int)(recastParams->regionminsize * recastParams->regionminsize);
|
||||
mergeRegionSize= (int)(recastParams->regionmergesize * recastParams->regionmergesize);
|
||||
minRegionArea= (int)(recastParams->regionminsize * recastParams->regionminsize);
|
||||
mergeRegionArea= (int)(recastParams->regionmergesize * recastParams->regionmergesize);
|
||||
maxEdgeLen= (int)(recastParams->edgemaxlen/recastParams->cellsize);
|
||||
detailSampleDist= recastParams->detailsampledist< 0.9f ? 0 :
|
||||
recastParams->cellsize * recastParams->detailsampledist;
|
||||
@@ -212,13 +212,14 @@ static int buildNavMesh(const RecastData *recastParams, int nverts, float *verts
|
||||
MEM_freeN(triflags);
|
||||
|
||||
/* ** Step 3: Filter walkables surfaces ** */
|
||||
recast_filterLowHangingWalkableObstacles(walkableClimb, solid);
|
||||
recast_filterLedgeSpans(walkableHeight, walkableClimb, solid);
|
||||
recast_filterWalkableLowHeightSpans(walkableHeight, solid);
|
||||
|
||||
/* ** Step 4: Partition walkable surface to simple regions ** */
|
||||
|
||||
chf= recast_newCompactHeightfield();
|
||||
if(!recast_buildCompactHeightfield(walkableHeight, walkableClimb, RECAST_WALKABLE, solid, chf)) {
|
||||
if(!recast_buildCompactHeightfield(walkableHeight, walkableClimb, solid, chf)) {
|
||||
recast_destroyHeightfield(solid);
|
||||
recast_destroyCompactHeightfield(chf);
|
||||
|
||||
@@ -226,6 +227,13 @@ static int buildNavMesh(const RecastData *recastParams, int nverts, float *verts
|
||||
}
|
||||
|
||||
recast_destroyHeightfield(solid);
|
||||
solid = NULL;
|
||||
|
||||
if (!recast_erodeWalkableArea(walkableRadius, chf)) {
|
||||
recast_destroyCompactHeightfield(chf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepare for region partitioning, by calculating distance field along the walkable surface */
|
||||
if(!recast_buildDistanceField(chf)) {
|
||||
@@ -235,7 +243,7 @@ static int buildNavMesh(const RecastData *recastParams, int nverts, float *verts
|
||||
}
|
||||
|
||||
/* Partition the walkable surface into simple regions without holes */
|
||||
if(!recast_buildRegions(chf, walkableRadius, 0, minRegionSize, mergeRegionSize)) {
|
||||
if(!recast_buildRegions(chf, 0, minRegionArea, mergeRegionArea)) {
|
||||
recast_destroyCompactHeightfield(chf);
|
||||
|
||||
return 0;
|
||||
@@ -293,7 +301,8 @@ static Object* createRepresentation(bContext *C, struct recast_polyMesh *pmesh,
|
||||
Object* obedit;
|
||||
int createob= base==NULL;
|
||||
int nverts, nmeshes, nvp;
|
||||
unsigned short *verts, *meshes, *polys;
|
||||
unsigned short *verts, *polys;
|
||||
unsigned int *meshes;
|
||||
float bmin[3], cs, ch, *dverts;
|
||||
unsigned char *tris;
|
||||
ModifierData *md;
|
||||
@@ -348,7 +357,7 @@ static Object* createRepresentation(bContext *C, struct recast_polyMesh *pmesh,
|
||||
|
||||
for(i= 0; i<nmeshes; i++) {
|
||||
int uniquevbase= em->totvert;
|
||||
unsigned short vbase= meshes[4*i+0];
|
||||
unsigned int vbase= meshes[4*i+0];
|
||||
unsigned short ndv= meshes[4*i+1];
|
||||
unsigned short tribase= meshes[4*i+2];
|
||||
unsigned short trinum= meshes[4*i+3];
|
||||
|
||||
@@ -518,7 +518,7 @@ void KX_NavMeshObject::DrawNavMesh(NavMeshRenderMode renderMode)
|
||||
else
|
||||
v = m_navMesh->getDetailVertex(pd->vbase+(t[k]-p->nv));
|
||||
float pos[3];
|
||||
vcopy(pos, v);
|
||||
rcVcopy(pos, v);
|
||||
flipAxes(pos);
|
||||
tri[k].setValue(pos);
|
||||
}
|
||||
|
||||
@@ -325,9 +325,9 @@ inline float vdot2(const float* a, const float* b)
|
||||
static bool barDistSqPointToTri(const float* p, const float* a, const float* b, const float* c)
|
||||
{
|
||||
float v0[3], v1[3], v2[3];
|
||||
vsub(v0, c,a);
|
||||
vsub(v1, b,a);
|
||||
vsub(v2, p,a);
|
||||
rcVsub(v0, c,a);
|
||||
rcVsub(v1, b,a);
|
||||
rcVsub(v2, p,a);
|
||||
|
||||
const float dot00 = vdot2(v0, v0);
|
||||
const float dot01 = vdot2(v0, v1);
|
||||
|
||||
Reference in New Issue
Block a user