forked from bit/DotRecastNetSim
[Upstream] Cleanup filter code and improved documentation (https://github.com/recastnavigation/recastnavigation/pull/683)
- https://github.com/recastnavigation/recastnavigation/pull/683 This mostly just changes variable names and adds some comments to make the code more clear. It also has a few small fixup changes to the unit tests.
This commit is contained in:
parent
652b8d751a
commit
be73850965
|
@ -33,7 +33,12 @@ namespace DotRecast.Recast
|
||||||
private const int MAX_LAYERS = RC_NOT_CONNECTED - 1;
|
private const int MAX_LAYERS = RC_NOT_CONNECTED - 1;
|
||||||
private const int MAX_HEIGHT = RcConstants.SPAN_MAX_HEIGHT;
|
private const int MAX_HEIGHT = RcConstants.SPAN_MAX_HEIGHT;
|
||||||
|
|
||||||
/// @par
|
/// @}
|
||||||
|
/// @name Compact Heightfield Functions
|
||||||
|
/// @see rcCompactHeightfield
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Builds a compact heightfield representing open space, from a heightfield representing solid space.
|
||||||
///
|
///
|
||||||
/// This is just the beginning of the process of fully building a compact heightfield.
|
/// This is just the beginning of the process of fully building a compact heightfield.
|
||||||
/// Various filters may be applied, then the distance field and regions built.
|
/// Various filters may be applied, then the distance field and regions built.
|
||||||
|
@ -42,28 +47,38 @@ namespace DotRecast.Recast
|
||||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||||
///
|
///
|
||||||
/// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig
|
/// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig
|
||||||
public static RcCompactHeightfield BuildCompactHeightfield(RcTelemetry ctx, int walkableHeight, int walkableClimb, RcHeightfield hf)
|
/// @ingroup recast
|
||||||
|
///
|
||||||
|
/// @param[in,out] context The build context to use during the operation.
|
||||||
|
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area
|
||||||
|
/// to 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] heightfield The heightfield to be compacted.
|
||||||
|
/// @param[out] compactHeightfield The resulting compact heightfield. (Must be pre-allocated.)
|
||||||
|
/// @returns True if the operation completed successfully.
|
||||||
|
public static RcCompactHeightfield BuildCompactHeightfield(RcTelemetry context, int walkableHeight, int walkableClimb, RcHeightfield heightfield)
|
||||||
{
|
{
|
||||||
using var timer = ctx.ScopedTimer(RcTimerLabel.RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
using var timer = context.ScopedTimer(RcTimerLabel.RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||||
|
|
||||||
RcCompactHeightfield chf = new RcCompactHeightfield();
|
RcCompactHeightfield chf = new RcCompactHeightfield();
|
||||||
int w = hf.width;
|
int w = heightfield.width;
|
||||||
int h = hf.height;
|
int h = heightfield.height;
|
||||||
int spanCount = GetHeightFieldSpanCount(hf);
|
int spanCount = GetHeightFieldSpanCount(context, heightfield);
|
||||||
|
|
||||||
// Fill in header.
|
// Fill in header.
|
||||||
chf.width = w;
|
chf.width = w;
|
||||||
chf.height = h;
|
chf.height = h;
|
||||||
chf.borderSize = hf.borderSize;
|
chf.borderSize = heightfield.borderSize;
|
||||||
chf.spanCount = spanCount;
|
chf.spanCount = spanCount;
|
||||||
chf.walkableHeight = walkableHeight;
|
chf.walkableHeight = walkableHeight;
|
||||||
chf.walkableClimb = walkableClimb;
|
chf.walkableClimb = walkableClimb;
|
||||||
chf.maxRegions = 0;
|
chf.maxRegions = 0;
|
||||||
chf.bmin = hf.bmin;
|
chf.bmin = heightfield.bmin;
|
||||||
chf.bmax = hf.bmax;
|
chf.bmax = heightfield.bmax;
|
||||||
chf.bmax.Y += walkableHeight * hf.ch;
|
chf.bmax.Y += walkableHeight * heightfield.ch;
|
||||||
chf.cs = hf.cs;
|
chf.cs = heightfield.cs;
|
||||||
chf.ch = hf.ch;
|
chf.ch = heightfield.ch;
|
||||||
chf.cells = new RcCompactCell[w * h];
|
chf.cells = new RcCompactCell[w * h];
|
||||||
//chf.spans = new RcCompactSpan[spanCount];
|
//chf.spans = new RcCompactSpan[spanCount];
|
||||||
chf.areas = new int[spanCount];
|
chf.areas = new int[spanCount];
|
||||||
|
@ -79,7 +94,7 @@ namespace DotRecast.Recast
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
for (int x = 0; x < w; ++x)
|
||||||
{
|
{
|
||||||
RcSpan s = hf.spans[x + y * w];
|
RcSpan s = heightfield.spans[x + y * w];
|
||||||
// If there are no spans at this cell, just leave the data to index=0, count=0.
|
// If there are no spans at this cell, just leave the data to index=0, count=0.
|
||||||
if (s == null)
|
if (s == null)
|
||||||
continue;
|
continue;
|
||||||
|
@ -167,16 +182,21 @@ namespace DotRecast.Recast
|
||||||
return chf;
|
return chf;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetHeightFieldSpanCount(RcHeightfield hf)
|
/// Returns the number of spans contained in the specified heightfield.
|
||||||
|
/// @ingroup recast
|
||||||
|
/// @param[in,out] context The build context to use during the operation.
|
||||||
|
/// @param[in] heightfield An initialized heightfield.
|
||||||
|
/// @returns The number of spans in the heightfield.
|
||||||
|
private static int GetHeightFieldSpanCount(RcTelemetry context, RcHeightfield heightfield)
|
||||||
{
|
{
|
||||||
int w = hf.width;
|
int w = heightfield.width;
|
||||||
int h = hf.height;
|
int h = heightfield.height;
|
||||||
int spanCount = 0;
|
int spanCount = 0;
|
||||||
for (int y = 0; y < h; ++y)
|
for (int y = 0; y < h; ++y)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
for (int x = 0; x < w; ++x)
|
||||||
{
|
{
|
||||||
for (RcSpan s = hf.spans[x + y * w]; s != null; s = s.next)
|
for (RcSpan s = heightfield.spans[x + y * w]; s != null; s = s.next)
|
||||||
{
|
{
|
||||||
if (s.area != RC_NULL_AREA)
|
if (s.area != RC_NULL_AREA)
|
||||||
spanCount++;
|
spanCount++;
|
||||||
|
|
|
@ -28,150 +28,175 @@ namespace DotRecast.Recast
|
||||||
|
|
||||||
public static class RcFilters
|
public static class RcFilters
|
||||||
{
|
{
|
||||||
/// @par
|
/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimb of the span below them.
|
||||||
///
|
///
|
||||||
/// Allows the formation of walkable regions that will flow over low lying
|
/// This removes small obstacles that the agent would be able to walk over such as curbs, and also allows agents to move up structures such as stairs.
|
||||||
/// objects such as curbs, and up structures such as stairways.
|
/// This removes small obstacles and rasterization artifacts that the agent would be able to walk over
|
||||||
///
|
/// such as curbs. It also allows agents to move up terraced structures like stairs.
|
||||||
/// Two neighboring spans are walkable if: <tt>RcAbs(currentSpan.smax - neighborSpan.smax) < walkableClimb</tt>
|
///
|
||||||
///
|
/// Obstacle spans are marked walkable if: <tt>obstacleSpan.smax - walkableSpan.smax < walkableClimb</tt>
|
||||||
/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call
|
///
|
||||||
/// #rcFilterLedgeSpans after calling this filter.
|
/// @warning Will override the effect of #rcFilterLedgeSpans. If both filters are used, call #rcFilterLedgeSpans only after applying this filter.
|
||||||
///
|
///
|
||||||
/// @see rcHeightfield, rcConfig
|
/// @see rcHeightfield, rcConfig
|
||||||
public static void FilterLowHangingWalkableObstacles(RcTelemetry ctx, int walkableClimb, RcHeightfield solid)
|
///
|
||||||
|
/// @ingroup recast
|
||||||
|
/// @param[in,out] context The build context to use during the operation.
|
||||||
|
/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable.
|
||||||
|
/// [Limit: >=0] [Units: vx]
|
||||||
|
/// @param[in,out] heightfield A fully built heightfield. (All spans have been added.)
|
||||||
|
public static void FilterLowHangingWalkableObstacles(RcTelemetry context, int walkableClimb, RcHeightfield heightfield)
|
||||||
{
|
{
|
||||||
using var timer = ctx.ScopedTimer(RcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);
|
using var timer = context.ScopedTimer(RcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||||
|
|
||||||
int w = solid.width;
|
int xSize = heightfield.width;
|
||||||
int h = solid.height;
|
int zSize = heightfield.height;
|
||||||
|
|
||||||
for (int y = 0; y < h; ++y)
|
for (int z = 0; z < zSize; ++z)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
for (int x = 0; x < xSize; ++x)
|
||||||
{
|
{
|
||||||
RcSpan ps = null;
|
RcSpan previousSpan = null;
|
||||||
bool previousWalkable = false;
|
bool previousWasWalkable = false;
|
||||||
int previousArea = RC_NULL_AREA;
|
int previousAreaID = RC_NULL_AREA;
|
||||||
|
|
||||||
for (RcSpan s = solid.spans[x + y * w]; s != null; ps = s, s = s.next)
|
// For each span in the column...
|
||||||
|
for (RcSpan span = heightfield.spans[x + z * xSize]; span != null; previousSpan = span, span = span.next)
|
||||||
{
|
{
|
||||||
bool walkable = s.area != RC_NULL_AREA;
|
bool walkable = span.area != RC_NULL_AREA;
|
||||||
// If current span is not walkable, but there is walkable
|
// If current span is not walkable, but there is walkable span just below it and the height difference
|
||||||
// span just below it, mark the span above it walkable too.
|
// is small enough for the agent to walk over, mark the current span as walkable too.
|
||||||
if (!walkable && previousWalkable)
|
if (!walkable && previousWasWalkable && span.smax - previousSpan.smax <= walkableClimb)
|
||||||
{
|
{
|
||||||
if (MathF.Abs(s.smax - ps.smax) <= walkableClimb)
|
span.area = previousAreaID;
|
||||||
s.area = previousArea;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy walkable flag so that it cannot propagate
|
// Copy the original walkable value regardless of whether we changed it.
|
||||||
// past multiple non-walkable objects.
|
// This prevents multiple consecutive non-walkable spans from being erroneously marked as walkable.
|
||||||
previousWalkable = walkable;
|
previousWasWalkable = walkable;
|
||||||
previousArea = s.area;
|
previousAreaID = span.area;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
/// Marks spans that are ledges as not-walkable.
|
||||||
///
|
///
|
||||||
/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
|
/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
|
||||||
/// from the current span's maximum.
|
/// from the current span's maximum.
|
||||||
/// This method removes the impact of the overestimation of conservative voxelization
|
/// 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.
|
/// 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>
|
/// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
|
||||||
///
|
///
|
||||||
/// @see rcHeightfield, rcConfig
|
/// @see rcHeightfield, rcConfig
|
||||||
public static void FilterLedgeSpans(RcTelemetry ctx, int walkableHeight, int walkableClimb, RcHeightfield heightfield)
|
///
|
||||||
|
/// @ingroup recast
|
||||||
|
/// @param[in,out] context The build context to use during the operation.
|
||||||
|
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to
|
||||||
|
/// 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] heightfield A fully built heightfield. (All spans have been added.)
|
||||||
|
public static void FilterLedgeSpans(RcTelemetry context, int walkableHeight, int walkableClimb, RcHeightfield heightfield)
|
||||||
{
|
{
|
||||||
using var timer = ctx.ScopedTimer(RcTimerLabel.RC_TIMER_FILTER_BORDER);
|
using var timer = context.ScopedTimer(RcTimerLabel.RC_TIMER_FILTER_BORDER);
|
||||||
|
|
||||||
int xSize = heightfield.width;
|
int xSize = heightfield.width;
|
||||||
int zSize = heightfield.height;
|
int zSize = heightfield.height;
|
||||||
|
|
||||||
// Mark border spans.
|
// Mark spans that are adjacent to a ledge as unwalkable..
|
||||||
for (int z = 0; z < zSize; ++z)
|
for (int z = 0; z < zSize; ++z)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < xSize; ++x)
|
for (int x = 0; x < xSize; ++x)
|
||||||
{
|
{
|
||||||
for (RcSpan span = heightfield.spans[x + z * xSize]; span != null; span = span.next)
|
for (RcSpan span = heightfield.spans[x + z * xSize]; span != null; span = span.next)
|
||||||
{
|
{
|
||||||
// Skip non walkable spans.
|
// Skip non-walkable spans.
|
||||||
if (span.area == RC_NULL_AREA)
|
if (span.area == RC_NULL_AREA)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bot = (span.smax);
|
int floor = (span.smax);
|
||||||
int top = span.next != null ? span.next.smin : SPAN_MAX_HEIGHT;
|
int ceiling = span.next != null ? span.next.smin : SPAN_MAX_HEIGHT;
|
||||||
|
|
||||||
// Find neighbours minimum height.
|
// The difference between this walkable area and the lowest neighbor walkable area.
|
||||||
int minNeighborHeight = SPAN_MAX_HEIGHT;
|
// This is the difference between the current span and all neighbor spans that have
|
||||||
|
// enough space for an agent to move between, but not accounting at all for surface slope.
|
||||||
|
int lowestNeighborFloorDifference = SPAN_MAX_HEIGHT;
|
||||||
|
|
||||||
// Min and max height of accessible neighbours.
|
// Min and max height of accessible neighbours.
|
||||||
int accessibleNeighborMinHeight = span.smax;
|
int lowestTraversableNeighborFloor = span.smax;
|
||||||
int accessibleNeighborMaxHeight = span.smax;
|
int highestTraversableNeighborFloor = span.smax;
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
for (int direction = 0; direction < 4; ++direction)
|
||||||
{
|
{
|
||||||
int dx = x + GetDirOffsetX(direction);
|
int neighborX = x + GetDirOffsetX(direction);
|
||||||
int dz = z + GetDirOffsetY(direction);
|
int neighborZ = z + GetDirOffsetY(direction);
|
||||||
|
|
||||||
// Skip neighbours which are out of bounds.
|
// Skip neighbours which are out of bounds.
|
||||||
if (dx < 0 || dz < 0 || dx >= xSize || dz >= zSize)
|
if (neighborX < 0 || neighborZ < 0 || neighborX >= xSize || neighborZ >= zSize)
|
||||||
{
|
{
|
||||||
minNeighborHeight = (-walkableClimb - 1);
|
lowestNeighborFloorDifference = (-walkableClimb - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// From minus infinity to the first span.
|
RcSpan neighborSpan = heightfield.spans[neighborX + neighborZ * xSize];
|
||||||
RcSpan neighborSpan = heightfield.spans[dx + dz * xSize];
|
|
||||||
int neighborTop = neighborSpan != null ? neighborSpan.smin : SPAN_MAX_HEIGHT;
|
// The most we can step down to the neighbor is the walkableClimb distance.
|
||||||
|
// Start with the area under the neighbor span
|
||||||
|
int neighborCeiling = neighborSpan != null ? neighborSpan.smin : SPAN_MAX_HEIGHT;
|
||||||
|
|
||||||
// Skip neightbour if the gap between the spans is too small.
|
// Skip neightbour if the gap between the spans is too small.
|
||||||
if (Math.Min(top, neighborTop) - bot >= walkableHeight)
|
if (Math.Min(ceiling, neighborCeiling) - floor >= walkableHeight)
|
||||||
{
|
{
|
||||||
minNeighborHeight = (-walkableClimb - 1);
|
lowestNeighborFloorDifference = (-walkableClimb - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rest of the spans.
|
// For each span in the neighboring column...
|
||||||
for (neighborSpan = heightfield.spans[dx + dz * xSize]; neighborSpan != null; neighborSpan = neighborSpan.next)
|
for (; neighborSpan != null; neighborSpan = neighborSpan.next)
|
||||||
{
|
{
|
||||||
int neighborBot = neighborSpan.smax;
|
int neighborFloor = neighborSpan.smax;
|
||||||
neighborTop = neighborSpan.next != null ? neighborSpan.next.smin : SPAN_MAX_HEIGHT;
|
neighborCeiling = neighborSpan.next != null ? neighborSpan.next.smin : SPAN_MAX_HEIGHT;
|
||||||
|
|
||||||
// Skip neightbour if the gap between the spans is too small.
|
|
||||||
if (Math.Min(top, neighborTop) - Math.Max(bot, neighborBot) >= walkableHeight)
|
|
||||||
{
|
|
||||||
int accessibleNeighbourHeight = neighborBot - bot;
|
|
||||||
minNeighborHeight = Math.Min(minNeighborHeight, accessibleNeighbourHeight);
|
|
||||||
|
|
||||||
// Find min/max accessible neighbour height.
|
// Only consider neighboring areas that have enough overlap to be potentially traversable.
|
||||||
if (MathF.Abs(accessibleNeighbourHeight) <= walkableClimb)
|
if (Math.Min(ceiling, neighborCeiling) - Math.Max(floor, neighborFloor) < walkableHeight)
|
||||||
{
|
{
|
||||||
if (neighborBot < accessibleNeighborMinHeight) accessibleNeighborMinHeight = neighborBot;
|
// No space to traverse between them.
|
||||||
if (neighborBot > accessibleNeighborMaxHeight) accessibleNeighborMaxHeight = neighborBot;
|
continue;
|
||||||
}
|
}
|
||||||
else if (accessibleNeighbourHeight < -walkableClimb)
|
|
||||||
{
|
int neighborFloorDifference = neighborFloor - floor;
|
||||||
break;
|
lowestNeighborFloorDifference = Math.Min(lowestNeighborFloorDifference, neighborFloorDifference);
|
||||||
}
|
|
||||||
|
// Find min/max accessible neighbor height.
|
||||||
|
// Only consider neighbors that are at most walkableClimb away.
|
||||||
|
if (MathF.Abs(neighborFloorDifference) <= walkableClimb)
|
||||||
|
{
|
||||||
|
// There is space to move to the neighbor cell and the slope isn't too much.
|
||||||
|
lowestTraversableNeighborFloor = Math.Min(lowestTraversableNeighborFloor, neighborFloor);
|
||||||
|
highestTraversableNeighborFloor = Math.Max(highestTraversableNeighborFloor, neighborFloor);
|
||||||
|
}
|
||||||
|
else if (neighborFloorDifference < -walkableClimb)
|
||||||
|
{
|
||||||
|
// We already know this will be considered a ledge span so we can early-out
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The current span is close to a ledge if the drop to any
|
// The current span is close to a ledge if the magnitude of the drop to any neighbour span is greater than the walkableClimb distance.
|
||||||
// neighbour span is less than the walkableClimb.
|
// That is, there is a gap that is large enough to let an agent move between them, but the drop (surface slope) is too large to allow it.
|
||||||
if (minNeighborHeight < -walkableClimb)
|
// (If this is the case, then biggestNeighborStepDown will be negative, so compare against the negative walkableClimb as a means of checking
|
||||||
|
// the magnitude of the delta)
|
||||||
|
if (lowestNeighborFloorDifference < -walkableClimb)
|
||||||
{
|
{
|
||||||
span.area = RC_NULL_AREA;
|
span.area = RC_NULL_AREA;
|
||||||
}
|
}
|
||||||
|
// If the difference between all neighbor floors is too large, this is a steep slope, so mark the span as an unwalkable ledge.
|
||||||
// If the difference between all neighbours is too large,
|
else if ((highestTraversableNeighborFloor - lowestTraversableNeighborFloor) > walkableClimb)
|
||||||
// we are at steep slope, mark the span as ledge.
|
|
||||||
if ((accessibleNeighborMaxHeight - accessibleNeighborMinHeight) > walkableClimb)
|
|
||||||
{
|
{
|
||||||
span.area = RC_NULL_AREA;
|
span.area = RC_NULL_AREA;
|
||||||
}
|
}
|
||||||
|
@ -180,31 +205,41 @@ namespace DotRecast.Recast
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @par
|
/// Marks walkable spans as not walkable if the clearance above the span is less than the specified walkableHeight.
|
||||||
///
|
///
|
||||||
/// For this filter, the clearance above the span is the distance from the span's
|
/// 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.)
|
/// maximum to the minimum of the next higher span in the same column.
|
||||||
///
|
/// If there is no higher span in the column, the clearance is computed as the
|
||||||
|
/// distance from the top of the span to the maximum heightfield height.
|
||||||
|
///
|
||||||
/// @see rcHeightfield, rcConfig
|
/// @see rcHeightfield, rcConfig
|
||||||
public static void FilterWalkableLowHeightSpans(RcTelemetry ctx, int walkableHeight, RcHeightfield solid)
|
/// @ingroup recast
|
||||||
|
///
|
||||||
|
/// @param[in,out] context The build context to use during the operation.
|
||||||
|
/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to
|
||||||
|
/// be considered walkable. [Limit: >= 3] [Units: vx]
|
||||||
|
/// @param[in,out] heightfield A fully built heightfield. (All spans have been added.)
|
||||||
|
public static void FilterWalkableLowHeightSpans(RcTelemetry context, int walkableHeight, RcHeightfield heightfield)
|
||||||
{
|
{
|
||||||
using var timer = ctx.ScopedTimer(RcTimerLabel.RC_TIMER_FILTER_WALKABLE);
|
using var timer = context.ScopedTimer(RcTimerLabel.RC_TIMER_FILTER_WALKABLE);
|
||||||
|
|
||||||
int w = solid.width;
|
int xSize = heightfield.width;
|
||||||
int h = solid.height;
|
int zSize = heightfield.height;
|
||||||
|
|
||||||
// Remove walkable flag from spans which do not have enough
|
// Remove walkable flag from spans which do not have enough
|
||||||
// space above them for the agent to stand there.
|
// space above them for the agent to stand there.
|
||||||
for (int y = 0; y < h; ++y)
|
for (int z = 0; z < zSize; ++z)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < w; ++x)
|
for (int x = 0; x < xSize; ++x)
|
||||||
{
|
{
|
||||||
for (RcSpan s = solid.spans[x + y * w]; s != null; s = s.next)
|
for (RcSpan span = heightfield.spans[x + z * xSize]; span != null; span = span.next)
|
||||||
{
|
{
|
||||||
int bot = (s.smax);
|
int floor = (span.smax);
|
||||||
int top = s.next != null ? s.next.smin : SPAN_MAX_HEIGHT;
|
int ceiling = span.next != null ? span.next.smin : SPAN_MAX_HEIGHT;
|
||||||
if ((top - bot) < walkableHeight)
|
if ((ceiling - floor) < walkableHeight)
|
||||||
s.area = RC_NULL_AREA;
|
{
|
||||||
|
span.area = RC_NULL_AREA;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue