From 5553d19d59f13e4db42c70e9ea34484dc324d268 Mon Sep 17 00:00:00 2001 From: Stephen Pratt Date: Mon, 22 Aug 2011 21:49:21 +0000 Subject: [PATCH] Recast: Detail API documentation for the elements declared in Recast.h. (Partial) The elements through rcPolyMesh are complete. --- Docs/Extern/Recast_api.txt | 427 +++++++++++++++++++++++++++++++++++++ Recast/Include/Recast.h | 424 ++++++++++++++++++++++++++---------- Recast/Source/Recast.cpp | 20 ++ 3 files changed, 753 insertions(+), 118 deletions(-) create mode 100644 Docs/Extern/Recast_api.txt diff --git a/Docs/Extern/Recast_api.txt b/Docs/Extern/Recast_api.txt new file mode 100644 index 0000000..bde5a03 --- /dev/null +++ b/Docs/Extern/Recast_api.txt @@ -0,0 +1,427 @@ +// This file contains the detail API documentation for +// elements defined in the Recast.h. + +/** + +@struct rcConfig +@ingroup recast +@par + +The is a convenience structure that represents an aggregation of parameters +used at different stages in the Recast build process. Some +values are derived during the build process. Not all parameters +are used for all build processes. + +Units are usually in voxels (vx) or world units (wu). The units for voxels, +grid size, and cell size are all based on the values of #cs and #ch. + +In this documentation, the term 'field' refers to heightfield and +contour data structures that define spacial information using an integer +grid. + +The upper and lower limits for the various parameters often depend on +the platform's floating point accuraccy as well as interdependencies between +the values of multiple parameters. See the individual parameter +documentation for details. + +@var rcConfig::borderSize +@par + +This value represents the the closest the walkable area of the heightfield +should come to the xz-plane AABB of the field. It does not have any +impact on the borders around internal obstructions. + +@var rcConfig::tileSize +@par + +This field is only used when building multi-tile meshes. + +@var rcConfig::cs +@par + +@p cs and #ch define voxel/grid/cell size. So their values have significant +side effects on all parameters defined in voxel units. + +The minimum value for this parameter depends on the platform's floating point +accuracy, with the practical minimum usually around 0.05. + +@var rcConfig::ch +@par + +#cs and @p ch define voxel/grid/cell size. So their values have significant +side effects on all parameters defined in voxel units. + +The minimum value for this parameter depends on the platform's floating point +accuracy, with the practical minimum usually around 0.05. + +@var rcConfig::walkableSlopeAngle +@par + +The practical upper limit for this parameter is usually around 85 degrees. + +@var rcConfig::walkableHeight +@par + +Permits detection of overhangs in the source geometry that make the geometry +below un-walkable. The value is usually set to the maximum agent height. + +@var rcConfig::walkableClimb +@par + +Allows the mesh to flow over low lying obstructions such as curbs and +up/down stairways. The value is usually set to how far up/down an agent can step. + +@var rcConfig::walkableRadius +@par + +In general, this is the closest any part of the final mesh should get to an +obstruction in the source geometry. It is usually set to the maximum +agent radius. + +While a value of zero is legal, it is not recommended and can result in +odd edge case issues. + +@var rcConfig::maxEdgeLen +@par + +Extra vertices will be inserted as needed to keep contour edges below this +length. A value of zero effectively disables this feature. + +@var rcConfig::maxSimplificationError +@par + +The effect of this parameter only applies to the xz-plane. + +@var rcConfig::minRegionArea +@par + +Any regions that are smaller than this area will be marked as unwalkable. +This is useful in removing useless regions that can sometimes form on +geometry such as table tops, box tops, etc. + +@var rcConfig::maxVertsPerPoly +@par + +If the mesh data is to be used to construct a Detour navigation mesh, then the upper limit +is limited to <= #DT_VERTS_PER_POLYGON. + + +@struct rcHeightfield +@ingroup recast +@par + +The grid of a heightfield is layed out on the xz-plane based on the +value of #cs. Spans exist within the grid columns with the span +min/max values at increments of #ch from the base of the grid. The smallest +possible span size is (#cs width) * (#cs depth) * (#ch height). (Which is a single voxel.) + +The standard process for buidling a heightfield is to allocate it using +#rcAllocHeightfield, initialize it using #rcCreateHeightfield, then +add spans using the various helper functions such as #rcRasterizeTriangle. + +Building a heightfield is one of the first steps in creating a polygon mesh +from source geometry. After it is populated, it is used to build a +rcCompactHeightfield. + +Example of iterating the spans in a heightfield: +@code +// Where hf is a reference to an heightfield object. + +const float* orig = hf.bmin; +const float cs = hf.cs; +const float ch = hf.ch; + +const int w = hf.width; +const int h = hf.height; + +for (int y = 0; y < h; ++y) +{ + for (int x = 0; x < w; ++x) + { + // Deriving the minimum corner of the grid location. + float fx = orig[0] + x*cs; + float fz = orig[2] + y*cs; + // The base span in the column. (May be null.) + const rcSpan* s = hf.spans[x + y*w]; + while (s) + { + // Detriving the minium and maximum world position of the span. + float fymin = orig[1]+s->smin*ch; + float fymax = orig[1] + s->smax*ch; + // Do other things with the span before moving up the column. + s = s->next; + } + } +} +@endcode + +@see rcAllocHeightfield, rcFreeHeightField, rcCreateHeightfield + +@struct rcCompactCell +@par + +See the rcCompactHeightfield documentation for an example of how compact cells +are used to iterate the heightfield. + +Useful instances of this type can only by obtained from a #rcCompactHeightfield object. + +@see rcCompactHeightfield + +@struct rcCompactSpan +@par + +The span represents open, unobstructed space within a compact heightfield column. +See the rcCompactHeightfield documentation for an example of iterating spans and searching +span connections. + +Useful instances of this type can only by obtained from a #rcCompactHeightfield object. + +@see rcCompactHeightfield + + +@struct rcCompactHeightfield +@ingroup recast +@par + +For this type of heightfield, the spans represent the open (unobstructed) +space above the solid surfaces of a voxel field. It is usually created from +a #rcHeightfield object. Data is stored in a compact, efficient manner, +but the structure is not condusive to adding and removing spans. + +The standard process for buidling a compact heightfield is to allocate it +using #rcAllocCompactHeightfield, build it using #rcBuildCompactHeightfield, +then run it through the various helper functions to generate neighbor +and region data. + +Connected neighbor spans form non-overlapping surfaces. When neighbor +information is generated, spans will include data that can be used to +locate axis-neighbors. Axis-neighbors are connected +spans that are offset from the current cell column as follows: +
+Direction 0 = (-1, 0)
+Direction 1 = (0, 1)
+Direction 2 = (1, 0)
+Direction 3 = (0, -1)
+
+ +Example of iterating and inspecting spans, including connected neighbors: + +@code +// Where chf is an instance of a rcCompactHeightfield. + +const float cs = chf.cs; +const float ch = chf.ch; + +for (int y = 0; y < chf.height; ++y) +{ + for (int x = 0; x < chf.width; ++x) + { + // Deriving the minimum corner of the grid location. + const float fx = chf.bmin[0] + x*cs; + const float fz = chf.bmin[2] + y*cs; + + // Get the cell for the grid location then iterate + // up the column. + const rcCompactCell& c = chf.cells[x+y*chf.width]; + for (unsigned i = c.index, ni = c.index+c.count; i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + + Deriving the minimum (floor) of the span. + const float fy = chf.bmin[1] + (s.y+1)*ch; + + // Testing the area assignment of the span. + if (chf.areas[i] == RC_WALKABLE_AREA) + { + // The span is in the default 'walkable area'. + } + else if (chf.areas[i] == RC_NULL_AREA) + { + // The surface is not considered walkable. + // E.g. It was filtered out during the build processes. + } + else + { + // Do something. (Only applicable for custom build + // build processes.) + } + + // Iterating the connected axis-neighbor spans. + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + // There is a neighbor in this direction. + const int nx = x + rcGetDirOffsetX(dir); + const int ny = y + rcGetDirOffsetY(dir); + const int ni = (int)chf.cells[nx+ny*w].index + rcGetCon(s, 0); + const rcCompactSpan& ns = chf.spans[ni]; + // Do something with the neighbor span. + } + } + } + } +} +@endcode + +@see rcAllocCompactHeightfield, rcFreeCompactHeightfield, rcBuildCompactHeightfield + +@struct rcContour +@ingroup recast +@par + +A contour only exists within the context of a #rcContourSet object. + +While the height of the contour's border may vary, the contour will always +form a simple polygon when projected onto the xz-plane. + +Example of converting vertices into world space: + +@code +// Where cset is the rcContourSet object to which the contour belongs. +float worldX = cset.bmin[0] + vertX * cset.cs; +float worldY = cset.bmin[1] + vertY * cset.ch; +float worldZ = cset.bmin[2] + vertZ * cset.cs; +@endcode + +@see rcContourSet + +@var rcContour::verts +@par + +The simplified contour is a version of the raw contour with all +'unnecessary' vertices removed. Whether a vertex is +considered unnecessary depends on the contour build process. + +The data format is as follows: (x, y, z, r) * #nverts + +A contour edge is formed by the current and next vertex. The r-value +represents region and connection information for the edge. For example: + +@code +int r = verts[i*4+3]; + +int regionId = r & RC_CONTOUR_REG_MASK; + +if (r & RC_BORDER_VERTEX) +{ + // The edge represents a solid border. +} + +if (r & RC_AREA_BORDER) +{ + // The edge represents a transition between different areas. +} +@endcode + +@var rcContour::rverts +@par + +See #verts for information on element layout. + +@struct rcContourSet +@ingroup recast +@par + +All contours within the set share the minimum bounds and cell sizes of the set. + +The standard process for building a contour set is to allocate it +using #rcAllocContourSet, then initialize is using #rcBuildContours. + +@see rcAllocContourSet, rcFreeContourSet, rcBuildContours + +@struct rcPolyMesh +@ingroup recast +@par + +A mesh of potentially overlapping convex polygons of between three +and #nvp vertices. The mesh exists within the context of an axis-aligned +bounding box (AABB) with vertices laid out in an evenly spaced grid, based +on the values of #cs and #ch. + +The standard process for building a contour set is to allocate it using +#rcAllocPolyMesh, the initialize it using #rcBuildPolyMesh + +Example of iterating the polygons: + +@code +// Where mesh is a reference to a rcPolyMesh object. + +const int nvp = mesh.nvp; +const float cs = mesh.cs; +const float ch = mesh.ch; +const float* orig = mesh.bmin; + +for (int i = 0; i < mesh.npolys; ++i) +{ + const unsigned short* p = &mesh.polys[i*nvp*2]; + + // Iterate the vertices. + unsigned short vi[3]; // The vertex indices. + for (int j = 0; j < nvp; ++j) + { + if (p[j] == RC_MESH_NULL_IDX) + break; // End of vertices. + + if (p[j + nvp] == RC_MESH_NULL_IDX) + { + // The edge beginning with this vertex is a solid border. + } + else + { + // The edge beginning with this vertex connects to + // polygon p[j + nvp]. + } + + // Convert to world space. + const unsigned short* v = &mesh.verts[p[j]*3]; + const float x = orig[0] + v[0]*cs; + const float y = orig[1] + v[1]*ch; + const float z = orig[2] + v[2]*cs; + // Do something with the vertices. + } +} +@endcode + +@see rcAllocPolyMesh, rcFreePolyMesh, rcBuildPolyMesh + +@var rcPolyMesh::verts +@par + +The values of #bmin ,#cs, and #ch are used to convert vertex coordinates +to world space as follows: + +@code +float worldX = bmin[0] + verts[i*3+0] * cs +float worldY = bmin[1] + verts[i*3+1] * ch +float worldZ = bmin[2] + verts[i*3+2] * cs +@endcode + +@var rcPolyMesh::polys +@par + +Each entry is 2 * #nvp in length. The first half of the entry +contains the indices of the polygon. The first instance of #RC_MESH_NULL_IDX +indicates the end of the indices for the entry. The second half contains +indices to neighbor polygons. A value of #RC_MESH_NULL_IDX indicates no +connection for the associated edge. (I.e. The edge is a solid border.) + +For example: +
+nvp = 6
+For the entry: (1, 3, 4, 8, RC_MESH_NULL_IDX, RC_MESH_NULL_IDX, 
+                18, RC_MESH_NULL_IDX , 21, RC_MESH_NULL_IDX, RC_MESH_NULL_IDX, RC_MESH_NULL_IDX)
+
+(1, 3, 4, 8) defines a polygon with 4 vertices.
+Edge 1->3 is shared with polygon 18.
+Edge 4->8 is shared with polygon 21.
+Edges 3->4 and 4->8 are border edges not shared with any other polygon.
+
+ +@var rcPolyMesh::areas +@par + +The standard build process assigns the value of #RC_WALKABLE_AREA to all walkable polygons. +This value can then be changed to meet user requirements. + +*/ diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h index 14fc2ea..16d72b9 100644 --- a/Recast/Include/Recast.h +++ b/Recast/Include/Recast.h @@ -19,264 +19,446 @@ #ifndef RECAST_H #define RECAST_H -// Some math headers don't have PI defined. +/** + * @defgroup recast Recast + * Elements related to path planning. + * @note This list is not yet complete. (The documentation effort is still underway.) + */ + +/// The value of PI used by Recast. static const float RC_PI = 3.14159265f; +/// Recast log categories. +/// @ingroup recast +/// @see rcContext enum rcLogCategory { - RC_LOG_PROGRESS = 1, - RC_LOG_WARNING, - RC_LOG_ERROR, + 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. +/// @ingroup recast +/// @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, - RC_TIMER_BUILD_LAYERS, + /// 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 }; -/// Build context provides several optional utilities needed for the build process, -/// such as timing, logging, and build time collecting. +/// Provides an interface for optional logging and performance tracking of the Recast +/// build process. 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; } - /// Resets log. + + /// 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 timer. + /// Enables or disables the performance timers. + /// @param[in] state TRUE if timers should be enabled. inline void enableTimer(bool state) { m_timerEnabled = state; } - /// Resets all timers. + + /// Clears all peformance timers. (Resets all to unused.) inline void resetTimers() { if (m_timerEnabled) doResetTimers(); } - /// Starts timer, used for performance timing. + + /// Starts the specified performance timer. + /// @param label The category of timer. inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); } - /// Stops timer, used for performance timing. + + /// Stops the specified performance timer. + /// @param label The category of the timer. inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); } - /// Returns time accumulated between timer start/stop. + + /// 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: - /// @name Virtual functions to override for custom implementations. + /// @name Custom implementation functions. + /// Logging and timer functionality must be provided by a concrete + /// implementation of these functions. This class does not implement these functions. ///@{ + + /// 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; }; - -/// The units of the parameters are specified in parenthesis as follows: -/// (vx) voxels, (wu) world units +/// Specifies a configuration to use when performing Recast builds. struct rcConfig { - int width, height; ///< Dimensions of the rasterized heightfield (vx) - int tileSize; ///< Width and Height of a tile (vx) - int borderSize; ///< Non-navigable Border around the heightfield (vx) - float cs, ch; ///< Grid cell size and height (wu) - float bmin[3], bmax[3]; ///< Grid bounds (wu) - float walkableSlopeAngle; ///< Maximum walkable slope angle in degrees. - int walkableHeight; ///< Minimum height where the agent can still walk (vx) - int walkableClimb; ///< Maximum height between grid cells the agent can climb (vx) - int walkableRadius; ///< Radius of the agent in cells (vx) - int maxEdgeLen; ///< Maximum contour edge length (vx) - float maxSimplificationError; ///< Maximum distance error from contour to cells (vx) - int minRegionArea; ///< Regions whose area is smaller than this threshold will be removed. (vx) - int mergeRegionArea; ///< Regions whose area is smaller than this threshold will be merged (vx) - int maxVertsPerPoly; ///< Max number of vertices per polygon - float detailSampleDist; ///< Detail mesh sample spacing. - float detailSampleMaxError; ///< Detail mesh simplification max sample error. + /// 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 regions. [Limit: >=0] [Units: vx] + int minRegionArea; + + /// Any regions with a cell 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; }; -/// Define number of bits in the above structure for smin/smax. +/// Defines number of bits in rcSpan::smin and rcSpan::smax. static const int RC_SPAN_HEIGHT_BITS = 13; -/// The max height is used for clamping rasterized values. +/// Defines the maximum value for rcSpan::smin and rcSpan::smax. static const int RC_SPAN_MAX_HEIGHT = (1<log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath); +/// @endcode void rcContext::log(const rcLogCategory category, const char* format, ...) { if (!m_logEnabled)