Changed `List<DtStraightPath>` to `Span<DtStraightPath>` for enhanced memory efficiency

This commit is contained in:
ikpil 2024-05-23 02:16:03 +09:00 committed by Ikpil
parent 5b6905bd8f
commit 99224251dc
13 changed files with 179 additions and 167 deletions

View File

@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Changed `DtTileCacheLayerHeaderReader` to a static class - Changed `DtTileCacheLayerHeaderReader` to a static class
- Changed `Dictionary<int, List<DtMeshTile>>` to `DtMeshTile[]` to optimize memory usage - Changed `Dictionary<int, List<DtMeshTile>>` to `DtMeshTile[]` to optimize memory usage
- Changed `MAX_STEER_POINTS` from class constant to local. - Changed `MAX_STEER_POINTS` from class constant to local.
- Changed `List<DtStraightPath>` to `Span<DtStraightPath>` for enhanced memory efficiency
### Removed ### Removed
- Nothing - Nothing

View File

@ -944,13 +944,13 @@ namespace DotRecast.Detour.Crowd
} }
// Find corners for steering // Find corners for steering
ag.corridor.FindCorners(ref ag.corners, DtCrowdConst.DT_CROWDAGENT_MAX_CORNERS, _navQuery, _filters[ag.option.queryFilterType]); ag.ncorners = ag.corridor.FindCorners(ag.corners, DtCrowdConst.DT_CROWDAGENT_MAX_CORNERS, _navQuery, _filters[ag.option.queryFilterType]);
// Check to see if the corner after the next corner is directly visible, // Check to see if the corner after the next corner is directly visible,
// and short cut to there. // and short cut to there.
if ((ag.option.updateFlags & DtCrowdAgentUpdateFlags.DT_CROWD_OPTIMIZE_VIS) != 0 && ag.corners.Count > 0) if ((ag.option.updateFlags & DtCrowdAgentUpdateFlags.DT_CROWD_OPTIMIZE_VIS) != 0 && ag.ncorners > 0)
{ {
RcVec3f target = ag.corners[Math.Min(1, ag.corners.Count - 1)].pos; RcVec3f target = ag.corners[Math.Min(1, ag.ncorners - 1)].pos;
ag.corridor.OptimizePathVisibility(target, ag.option.pathOptimizationRange, _navQuery, ag.corridor.OptimizePathVisibility(target, ag.option.pathOptimizationRange, _navQuery,
_filters[ag.option.queryFilterType]); _filters[ag.option.queryFilterType]);
@ -1000,7 +1000,7 @@ namespace DotRecast.Detour.Crowd
// Adjust the path over the off-mesh connection. // Adjust the path over the off-mesh connection.
long[] refs = new long[2]; long[] refs = new long[2];
if (ag.corridor.MoveOverOffmeshConnection(ag.corners[ag.corners.Count - 1].refs, refs, ref anim.startPos, if (ag.corridor.MoveOverOffmeshConnection(ag.corners[ag.ncorners - 1].refs, refs, ref anim.startPos,
ref anim.endPos, _navQuery)) ref anim.endPos, _navQuery))
{ {
anim.initPos = ag.npos; anim.initPos = ag.npos;
@ -1010,7 +1010,7 @@ namespace DotRecast.Detour.Crowd
anim.tmax = (RcVecUtils.Dist2D(anim.startPos, anim.endPos) / ag.option.maxSpeed) * 0.5f; anim.tmax = (RcVecUtils.Dist2D(anim.startPos, anim.endPos) / ag.option.maxSpeed) * 0.5f;
ag.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_OFFMESH; ag.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_OFFMESH;
ag.corners.Clear(); ag.ncorners = 0;
ag.neis.Clear(); ag.neis.Clear();
continue; continue;
} }

View File

@ -61,7 +61,10 @@ namespace DotRecast.Detour.Crowd
public DtCrowdAgentParams option; public DtCrowdAgentParams option;
/// The local path corridor corners for the agent. /// The local path corridor corners for the agent.
public List<DtStraightPath> corners = new List<DtStraightPath>(); public DtStraightPath[] corners = new DtStraightPath[DtCrowdConst.DT_CROWDAGENT_MAX_CORNERS];
/// The number of corners.
public int ncorners;
public DtMoveRequestState targetState; // < State of the movement request. public DtMoveRequestState targetState; // < State of the movement request.
public long targetRef; // < Target polyref of the movement request. public long targetRef; // < Target polyref of the movement request.
@ -100,16 +103,16 @@ namespace DotRecast.Detour.Crowd
public bool OverOffmeshConnection(float radius) public bool OverOffmeshConnection(float radius)
{ {
if (0 == corners.Count) if (0 == ncorners)
return false; return false;
bool offMeshConnection = ((corners[corners.Count - 1].flags bool offMeshConnection = ((corners[ncorners - 1].flags
& DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
? true ? true
: false; : false;
if (offMeshConnection) if (offMeshConnection)
{ {
float distSq = RcVecUtils.Dist2DSqr(npos, corners[corners.Count - 1].pos); float distSq = RcVecUtils.Dist2DSqr(npos, corners[ncorners - 1].pos);
if (distSq < radius * radius) if (distSq < radius * radius)
return true; return true;
} }
@ -119,12 +122,12 @@ namespace DotRecast.Detour.Crowd
public float GetDistanceToGoal(float range) public float GetDistanceToGoal(float range)
{ {
if (0 == corners.Count) if (0 == ncorners)
return range; return range;
bool endOfPath = ((corners[corners.Count - 1].flags & DtStraightPathFlags.DT_STRAIGHTPATH_END) != 0) ? true : false; bool endOfPath = ((corners[ncorners - 1].flags & DtStraightPathFlags.DT_STRAIGHTPATH_END) != 0) ? true : false;
if (endOfPath) if (endOfPath)
return Math.Min(RcVecUtils.Dist2D(npos, corners[corners.Count - 1].pos), range); return Math.Min(RcVecUtils.Dist2D(npos, corners[ncorners - 1].pos), range);
return range; return range;
} }
@ -132,10 +135,10 @@ namespace DotRecast.Detour.Crowd
public RcVec3f CalcSmoothSteerDirection() public RcVec3f CalcSmoothSteerDirection()
{ {
RcVec3f dir = new RcVec3f(); RcVec3f dir = new RcVec3f();
if (0 < corners.Count) if (0 < ncorners)
{ {
int ip0 = 0; int ip0 = 0;
int ip1 = Math.Min(1, corners.Count - 1); int ip1 = Math.Min(1, ncorners - 1);
var p0 = corners[ip0].pos; var p0 = corners[ip0].pos;
var p1 = corners[ip1].pos; var p1 = corners[ip1].pos;
@ -161,7 +164,7 @@ namespace DotRecast.Detour.Crowd
public RcVec3f CalcStraightSteerDirection() public RcVec3f CalcStraightSteerDirection()
{ {
RcVec3f dir = new RcVec3f(); RcVec3f dir = new RcVec3f();
if (0 < corners.Count) if (0 < ncorners)
{ {
dir = RcVec3f.Subtract(corners[0].pos, npos); dir = RcVec3f.Subtract(corners[0].pos, npos);
dir.Y = 0; dir.Y = 0;

View File

@ -133,42 +133,42 @@ namespace DotRecast.Detour.Crowd
/// @param[in] navquery The query object used to build the corridor. /// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation. /// @param[in] filter The filter to apply to the operation.
/// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners] /// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners]
public int FindCorners(ref List<DtStraightPath> corners, int maxCorners, DtNavMeshQuery navquery, IDtQueryFilter filter) public int FindCorners(Span<DtStraightPath> corners, int maxCorners, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
const float MIN_TARGET_DIST = 0.01f; const float MIN_TARGET_DIST = 0.01f;
var result = navquery.FindStraightPath(m_pos, m_target, m_path, m_npath, ref corners, maxCorners, 0); int ncorners = 0;
if (result.Succeeded()) navquery.FindStraightPath(m_pos, m_target, m_path, m_npath, corners, out ncorners, maxCorners, 0);
// Prune points in the beginning of the path which are too close.
while (0 < ncorners)
{ {
// Prune points in the beginning of the path which are too close. if ((corners[0].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 ||
int start = 0; RcVecUtils.Dist2DSqr(corners[0].pos, m_pos) > RcMath.Sqr(MIN_TARGET_DIST))
foreach (DtStraightPath spi in corners)
{ {
if ((spi.flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 break;
|| RcVecUtils.Dist2DSqr(spi.pos, m_pos) > RcMath.Sqr(MIN_TARGET_DIST))
{
break;
}
start++;
} }
int end = corners.Count; ncorners--;
// Prune points after an off-mesh connection. if (0 < ncorners)
for (int i = start; i < corners.Count; i++)
{ {
DtStraightPath spi = corners[i]; RcSpans.Move(corners, 1, 0, 3);
if ((spi.flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
{
end = i + 1;
break;
}
} }
corners = corners.GetRange(start, end - start);
} }
return corners.Count;
// Prune points after an off-mesh connection.
for (int i = 0; i < ncorners; ++i)
{
if ((corners[i].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
{
ncorners = i + 1;
break;
}
}
return ncorners;
} }
/** /**

View File

@ -20,9 +20,7 @@ freely, subject to the following restrictions:
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Collections;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
@ -1485,24 +1483,27 @@ namespace DotRecast.Detour
return DtStatus.DT_SUCCESS | details; return DtStatus.DT_SUCCESS | details;
} }
protected DtStatus AppendVertex(RcVec3f pos, int flags, long refs, ref List<DtStraightPath> straightPath, protected DtStatus AppendVertex(RcVec3f pos, int flags, long refs, Span<DtStraightPath> straightPath, ref int straightPathCount, int maxStraightPath)
int maxStraightPath)
{ {
if (straightPath.Count > 0 && DtUtils.VEqual(straightPath[straightPath.Count - 1].pos, pos)) if (straightPathCount > 0 && DtUtils.VEqual(straightPath[straightPathCount - 1].pos, pos))
{ {
// The vertices are equal, update flags and poly. // The vertices are equal, update flags and poly.
straightPath[straightPath.Count - 1] = new DtStraightPath(straightPath[straightPath.Count - 1].pos, flags, refs); straightPath[straightPathCount - 1] = new DtStraightPath(straightPath[straightPathCount - 1].pos, flags, refs);
} }
else else
{ {
if (straightPath.Count < maxStraightPath) // Append new vertex.
straightPath[straightPathCount] = new DtStraightPath(pos, flags, refs);
straightPathCount++;
// If there is no space to append more vertices, return.
if (straightPathCount >= maxStraightPath)
{ {
// Append new vertex. return DtStatus.DT_SUCCESS | DtStatus.DT_BUFFER_TOO_SMALL;
straightPath.Add(new DtStraightPath(pos, flags, refs));
} }
// If reached end of path or there is no space to append more vertices, return. // If reached end of path, return.
if (flags == DtStraightPathFlags.DT_STRAIGHTPATH_END || straightPath.Count >= maxStraightPath) if (flags == DtStraightPathFlags.DT_STRAIGHTPATH_END)
{ {
return DtStatus.DT_SUCCESS; return DtStatus.DT_SUCCESS;
} }
@ -1512,9 +1513,9 @@ namespace DotRecast.Detour
} }
protected DtStatus AppendPortals(int startIdx, int endIdx, RcVec3f endPos, List<long> path, protected DtStatus AppendPortals(int startIdx, int endIdx, RcVec3f endPos, List<long> path,
ref List<DtStraightPath> straightPath, int maxStraightPath, int options) Span<DtStraightPath> straightPath, ref int straightPathCount, int maxStraightPath, int options)
{ {
var startPos = straightPath[straightPath.Count - 1].pos; var startPos = straightPath[straightPathCount - 1].pos;
// Append or update last vertex // Append or update last vertex
DtStatus stat; DtStatus stat;
for (int i = startIdx; i < endIdx; i++) for (int i = startIdx; i < endIdx; i++)
@ -1553,7 +1554,7 @@ namespace DotRecast.Detour
if (DtUtils.IntersectSegSeg2D(startPos, endPos, left, right, out var _, out var t)) if (DtUtils.IntersectSegSeg2D(startPos, endPos, left, right, out var _, out var t))
{ {
var pt = RcVec3f.Lerp(left, right, t); var pt = RcVec3f.Lerp(left, right, t);
stat = AppendVertex(pt, 0, path[i + 1], ref straightPath, maxStraightPath); stat = AppendVertex(pt, 0, path[i + 1], straightPath, ref straightPathCount, maxStraightPath);
if (!stat.InProgress()) if (!stat.InProgress())
{ {
return stat; return stat;
@ -1590,17 +1591,22 @@ namespace DotRecast.Detour
/// @param[in] maxStraightPath The maximum number of points the straight path arrays can hold. [Limit: > 0] /// @param[in] maxStraightPath The maximum number of points the straight path arrays can hold. [Limit: > 0]
/// @param[in] options Query options. (see: #dtStraightPathOptions) /// @param[in] options Query options. (see: #dtStraightPathOptions)
/// @returns The status flags for the query. /// @returns The status flags for the query.
public virtual DtStatus FindStraightPath(RcVec3f startPos, RcVec3f endPos, List<long> path, int pathSize, public virtual DtStatus FindStraightPath(RcVec3f startPos, RcVec3f endPos,
ref List<DtStraightPath> straightPath, List<long> path, int pathSize,
int maxStraightPath, int options) Span<DtStraightPath> straightPath, out int straightPathCount, int maxStraightPath,
int options)
{ {
if (!startPos.IsFinite() || !endPos.IsFinite() || null == straightPath straightPathCount = 0;
|| null == path || pathSize <= 0 || path[0] == 0 || maxStraightPath <= 0)
if (!startPos.IsFinite() || !endPos.IsFinite() ||
null == straightPath ||
null == path || pathSize <= 0 || path[0] == 0
|| maxStraightPath <= 0)
{ {
return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM;
} }
straightPath.Clear(); DtStatus stat = DtStatus.DT_STATUS_NOTHING;
// TODO: Should this be callers responsibility? // TODO: Should this be callers responsibility?
var closestStartPosRes = ClosestPointOnPolyBoundary(path[0], startPos, out var closestStartPos); var closestStartPosRes = ClosestPointOnPolyBoundary(path[0], startPos, out var closestStartPos);
@ -1616,7 +1622,7 @@ namespace DotRecast.Detour
} }
// Add start point. // Add start point.
DtStatus stat = AppendVertex(closestStartPos, DtStraightPathFlags.DT_STRAIGHTPATH_START, path[0], ref straightPath, maxStraightPath); stat = AppendVertex(closestStartPos, DtStraightPathFlags.DT_STRAIGHTPATH_START, path[0], straightPath, ref straightPathCount, maxStraightPath);
if (!stat.InProgress()) if (!stat.InProgress())
{ {
return stat; return stat;
@ -1663,13 +1669,13 @@ namespace DotRecast.Detour
if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0) if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0)
{ {
// Ignore status return value as we're just about to return anyway. // Ignore status return value as we're just about to return anyway.
AppendPortals(apexIndex, i, closestEndPos, path, ref straightPath, maxStraightPath, options); AppendPortals(apexIndex, i, closestEndPos, path, straightPath, ref straightPathCount, maxStraightPath, options);
} }
// Ignore status return value as we're just about to return anyway. // Ignore status return value as we're just about to return anyway.
AppendVertex(closestEndPos, 0, path[i], ref straightPath, maxStraightPath); AppendVertex(closestEndPos, 0, path[i], straightPath, ref straightPathCount, maxStraightPath);
return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM | (straightPath.Count >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING); return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM | (straightPathCount >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING);
} }
// If starting really close the portal, advance. // If starting really close the portal, advance.
@ -1705,7 +1711,7 @@ namespace DotRecast.Detour
// Append portals along the current straight path segment. // Append portals along the current straight path segment.
if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0) if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0)
{ {
stat = AppendPortals(apexIndex, leftIndex, portalLeft, path, ref straightPath, maxStraightPath, options); stat = AppendPortals(apexIndex, leftIndex, portalLeft, path, straightPath, ref straightPathCount, maxStraightPath, options);
if (!stat.InProgress()) if (!stat.InProgress())
{ {
return stat; return stat;
@ -1728,7 +1734,7 @@ namespace DotRecast.Detour
long refs = leftPolyRef; long refs = leftPolyRef;
// Append or update vertex // Append or update vertex
stat = AppendVertex(portalApex, flags, refs, ref straightPath, maxStraightPath); stat = AppendVertex(portalApex, flags, refs, straightPath, ref straightPathCount, maxStraightPath);
if (!stat.InProgress()) if (!stat.InProgress())
{ {
return stat; return stat;
@ -1761,7 +1767,7 @@ namespace DotRecast.Detour
// Append portals along the current straight path segment. // Append portals along the current straight path segment.
if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0) if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0)
{ {
stat = AppendPortals(apexIndex, rightIndex, portalRight, path, ref straightPath, maxStraightPath, options); stat = AppendPortals(apexIndex, rightIndex, portalRight, path, straightPath, ref straightPathCount, maxStraightPath, options);
if (!stat.InProgress()) if (!stat.InProgress())
{ {
return stat; return stat;
@ -1784,7 +1790,7 @@ namespace DotRecast.Detour
long refs = rightPolyRef; long refs = rightPolyRef;
// Append or update vertex // Append or update vertex
stat = AppendVertex(portalApex, flags, refs, ref straightPath, maxStraightPath); stat = AppendVertex(portalApex, flags, refs, straightPath, ref straightPathCount, maxStraightPath);
if (!stat.InProgress()) if (!stat.InProgress())
{ {
return stat; return stat;
@ -1806,7 +1812,7 @@ namespace DotRecast.Detour
// Append portals along the current straight path segment. // Append portals along the current straight path segment.
if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0) if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0)
{ {
stat = AppendPortals(apexIndex, pathSize - 1, closestEndPos, path, ref straightPath, maxStraightPath, options); stat = AppendPortals(apexIndex, pathSize - 1, closestEndPos, path, straightPath, ref straightPathCount, maxStraightPath, options);
if (!stat.InProgress()) if (!stat.InProgress())
{ {
return stat; return stat;
@ -1815,8 +1821,8 @@ namespace DotRecast.Detour
} }
// Ignore status return value as we're just about to return anyway. // Ignore status return value as we're just about to return anyway.
AppendVertex(closestEndPos, DtStraightPathFlags.DT_STRAIGHTPATH_END, 0, ref straightPath, maxStraightPath); AppendVertex(closestEndPos, DtStraightPathFlags.DT_STRAIGHTPATH_END, 0, straightPath, ref straightPathCount, maxStraightPath);
return DtStatus.DT_SUCCESS | (straightPath.Count >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING); return DtStatus.DT_SUCCESS | (straightPathCount >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING);
} }
/// @par /// @par

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using DotRecast.Core.Numerics;
namespace DotRecast.Detour
{
public class DtNavMeshQueryMock : DtNavMeshQuery
{
private readonly DtStraightPath[] _straightPath;
private readonly DtStatus _status;
public DtNavMeshQueryMock(DtStraightPath[] straightPath, DtStatus status)
: base(null)
{
_straightPath = straightPath;
_status = status;
}
public override DtStatus FindStraightPath(RcVec3f startPos, RcVec3f endPos,
List<long> path, int pathSize,
Span<DtStraightPath> straightPath, out int straightPathCount, int maxStraightPath,
int options)
{
straightPathCount = 0;
for (int i = 0; i < _straightPath.Length && i < maxStraightPath; ++i)
{
straightPath[i] = _straightPath[i];
straightPathCount += 1;
}
return _status;
}
}
}

View File

@ -40,8 +40,8 @@ namespace DotRecast.Detour
steerPosRef = 0; steerPosRef = 0;
// Find steer target. // Find steer target.
var straightPath = new List<DtStraightPath>(MAX_STEER_POINTS); Span<DtStraightPath> straightPath = stackalloc DtStraightPath[MAX_STEER_POINTS];
var result = navQuery.FindStraightPath(startPos, endPos, path, pathSize, ref straightPath, MAX_STEER_POINTS, 0); var result = navQuery.FindStraightPath(startPos, endPos, path, pathSize, straightPath, out var nsteerPath, MAX_STEER_POINTS, 0);
if (result.Failed()) if (result.Failed())
{ {
return false; return false;
@ -49,7 +49,7 @@ namespace DotRecast.Detour
// Find vertex far enough to steer to. // Find vertex far enough to steer to.
int ns = 0; int ns = 0;
while (ns < straightPath.Count) while (ns < nsteerPath)
{ {
// Stop at Off-Mesh link or when point is further than slop away. // Stop at Off-Mesh link or when point is further than slop away.
if (((straightPath[ns].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) if (((straightPath[ns].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
@ -59,7 +59,7 @@ namespace DotRecast.Detour
} }
// Failed to find good point to steer to. // Failed to find good point to steer to.
if (ns >= straightPath.Count) if (ns >= nsteerPath)
return false; return false;
steerPos = straightPath[ns].pos; steerPos = straightPath[ns].pos;

View File

@ -251,10 +251,10 @@ public class CrowdSampleTool : ISampleTool
if (_showCorners) if (_showCorners)
{ {
if (0 < ag.corners.Count) if (0 < ag.ncorners)
{ {
dd.Begin(LINES, 2.0f); dd.Begin(LINES, 2.0f);
for (int j = 0; j < ag.corners.Count; ++j) for (int j = 0; j < ag.ncorners; ++j)
{ {
RcVec3f va = j == 0 ? pos : ag.corners[j - 1].pos; RcVec3f va = j == 0 ? pos : ag.corners[j - 1].pos;
RcVec3f vb = ag.corners[j].pos; RcVec3f vb = ag.corners[j].pos;
@ -262,10 +262,10 @@ public class CrowdSampleTool : ISampleTool
dd.Vertex(vb.X, vb.Y + radius, vb.Z, DuRGBA(128, 0, 0, 192)); dd.Vertex(vb.X, vb.Y + radius, vb.Z, DuRGBA(128, 0, 0, 192));
} }
if ((ag.corners[ag.corners.Count - 1].flags if ((ag.corners[ag.ncorners - 1].flags
& DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
{ {
RcVec3f v = ag.corners[ag.corners.Count - 1].pos; RcVec3f v = ag.corners[ag.ncorners - 1].pos;
dd.Vertex(v.X, v.Y, v.Z, DuRGBA(192, 0, 0, 192)); dd.Vertex(v.X, v.Y, v.Z, DuRGBA(192, 0, 0, 192));
dd.Vertex(v.X, v.Y + radius * 2, v.Z, DuRGBA(192, 0, 0, 192)); dd.Vertex(v.X, v.Y + radius * 2, v.Z, DuRGBA(192, 0, 0, 192));
} }

View File

@ -57,7 +57,8 @@ public class TestNavmeshSampleTool : ISampleTool
private bool m_hitResult; private bool m_hitResult;
private float m_distanceToWall; private float m_distanceToWall;
private List<DtStraightPath> m_straightPath; private DtStraightPath[] m_straightPath;
private int m_straightPathCount;
private List<long> m_polys; private List<long> m_polys;
private List<long> m_parent; private List<long> m_parent;
private float m_neighbourhoodRadius; private float m_neighbourhoodRadius;
@ -77,6 +78,8 @@ public class TestNavmeshSampleTool : ISampleTool
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED, SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED,
new float[] { 1f, 1f, 1f, 1f, 2f, 1.5f } new float[] { 1f, 1f, 1f, 1f, 2f, 1.5f }
); );
m_straightPath = new DtStraightPath[MAX_POLYS];
m_straightPathCount = 0;
} }
public void Layout() public void Layout()
@ -284,7 +287,7 @@ public class TestNavmeshSampleTool : ISampleTool
int spathCol = DuRGBA(64, 16, 0, 220); int spathCol = DuRGBA(64, 16, 0, 220);
int offMeshCol = DuRGBA(128, 96, 0, 220); int offMeshCol = DuRGBA(128, 96, 0, 220);
dd.Begin(LINES, 2.0f); dd.Begin(LINES, 2.0f);
for (int i = 0; i < m_straightPath.Count - 1; ++i) for (int i = 0; i < m_straightPathCount - 1; ++i)
{ {
DtStraightPath straightPathItem = m_straightPath[i]; DtStraightPath straightPathItem = m_straightPath[i];
DtStraightPath straightPathItem2 = m_straightPath[i + 1]; DtStraightPath straightPathItem2 = m_straightPath[i + 1];
@ -304,7 +307,7 @@ public class TestNavmeshSampleTool : ISampleTool
dd.End(); dd.End();
dd.Begin(POINTS, 6.0f); dd.Begin(POINTS, 6.0f);
for (int i = 0; i < m_straightPath.Count; ++i) for (int i = 0; i < m_straightPathCount; ++i)
{ {
DtStraightPath straightPathItem = m_straightPath[i]; DtStraightPath straightPathItem = m_straightPath[i];
int col; int col;
@ -349,7 +352,7 @@ public class TestNavmeshSampleTool : ISampleTool
dd.DepthMask(false); dd.DepthMask(false);
int spathCol = m_hitResult ? DuRGBA(64, 16, 0, 220) : DuRGBA(240, 240, 240, 220); int spathCol = m_hitResult ? DuRGBA(64, 16, 0, 220) : DuRGBA(240, 240, 240, 220);
dd.Begin(LINES, 2.0f); dd.Begin(LINES, 2.0f);
for (int i = 0; i < m_straightPath.Count - 1; ++i) for (int i = 0; i < m_straightPathCount - 1; ++i)
{ {
DtStraightPath straightPathItem = m_straightPath[i]; DtStraightPath straightPathItem = m_straightPath[i];
DtStraightPath straightPathItem2 = m_straightPath[i + 1]; DtStraightPath straightPathItem2 = m_straightPath[i + 1];
@ -359,7 +362,7 @@ public class TestNavmeshSampleTool : ISampleTool
dd.End(); dd.End();
dd.Begin(POINTS, 4.0f); dd.Begin(POINTS, 4.0f);
for (int i = 0; i < m_straightPath.Count; ++i) for (int i = 0; i < m_straightPathCount; ++i)
{ {
DtStraightPath straightPathItem = m_straightPath[i]; DtStraightPath straightPathItem = m_straightPath[i];
dd.Vertex(straightPathItem.pos.X, straightPathItem.pos.Y + 0.4f, straightPathItem.pos.Z, spathCol); dd.Vertex(straightPathItem.pos.X, straightPathItem.pos.Y + 0.4f, straightPathItem.pos.Z, spathCol);
@ -666,18 +669,18 @@ public class TestNavmeshSampleTool : ISampleTool
else if (_mode == RcTestNavmeshToolMode.PATHFIND_STRAIGHT) else if (_mode == RcTestNavmeshToolMode.PATHFIND_STRAIGHT)
{ {
_tool.FindStraightPath(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast, _tool.FindStraightPath(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast,
ref m_polys, ref m_straightPath, _straightPathOption); ref m_polys, m_straightPath, out m_straightPathCount, MAX_POLYS, _straightPathOption);
} }
else if (_mode == RcTestNavmeshToolMode.PATHFIND_SLICED) else if (_mode == RcTestNavmeshToolMode.PATHFIND_SLICED)
{ {
m_polys?.Clear(); m_polys?.Clear();
m_straightPath?.Clear(); m_straightPathCount = 0;
m_pathFindStatus = _tool.InitSlicedFindPath(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast); m_pathFindStatus = _tool.InitSlicedFindPath(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast);
} }
else if (_mode == RcTestNavmeshToolMode.RAYCAST) else if (_mode == RcTestNavmeshToolMode.RAYCAST)
{ {
_tool.Raycast(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _tool.Raycast(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter,
ref m_polys, ref m_straightPath, ref m_hitPos, ref m_hitNormal, ref m_hitResult); ref m_polys, m_straightPath, out m_straightPathCount, MAX_POLYS, ref m_hitPos, ref m_hitNormal, ref m_hitResult);
} }
else if (_mode == RcTestNavmeshToolMode.DISTANCE_TO_WALL) else if (_mode == RcTestNavmeshToolMode.DISTANCE_TO_WALL)
{ {
@ -712,7 +715,7 @@ public class TestNavmeshSampleTool : ISampleTool
if (m_pathFindStatus.InProgress()) if (m_pathFindStatus.InProgress())
{ {
m_pathFindStatus = _tool.UpdateSlicedFindPath(navQuery, 1, m_endRef, m_spos, m_epos, ref m_polys, ref m_straightPath); m_pathFindStatus = _tool.UpdateSlicedFindPath(navQuery, 1, m_endRef, m_spos, m_epos, ref m_polys, m_straightPath, out m_straightPathCount, MAX_POLYS);
} }
} }
} }

View File

@ -11,7 +11,6 @@ namespace DotRecast.Recast.Toolset.Tools
public const int MAX_POLYS = 256; public const int MAX_POLYS = 256;
public const int MAX_SMOOTH = 2048; public const int MAX_SMOOTH = 2048;
public RcTestNavMeshTool() public RcTestNavMeshTool()
{ {
} }
@ -172,15 +171,15 @@ namespace DotRecast.Recast.Toolset.Tools
} }
public DtStatus FindStraightPath(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPt, RcVec3f endPt, IDtQueryFilter filter, bool enableRaycast, public DtStatus FindStraightPath(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPt, RcVec3f endPt, IDtQueryFilter filter, bool enableRaycast,
ref List<long> polys, ref List<DtStraightPath> straightPath, int straightPathOptions) ref List<long> polys, Span<DtStraightPath> straightPath, out int straightPathCount, int maxStraightPath, int straightPathOptions)
{ {
straightPathCount = 0;
if (startRef == 0 || endRef == 0) if (startRef == 0 || endRef == 0)
{ {
return DtStatus.DT_FAILURE; return DtStatus.DT_FAILURE;
} }
polys ??= new List<long>(); polys ??= new List<long>();
straightPath ??= new List<DtStraightPath>();
polys.Clear(); polys.Clear();
straightPath.Clear(); straightPath.Clear();
@ -202,7 +201,7 @@ namespace DotRecast.Recast.Toolset.Tools
} }
} }
navQuery.FindStraightPath(startPt, epos, polys, polys.Count, ref straightPath, MAX_POLYS, straightPathOptions); navQuery.FindStraightPath(startPt, epos, polys, polys.Count, straightPath, out straightPathCount, maxStraightPath, straightPathOptions);
return DtStatus.DT_SUCCESS; return DtStatus.DT_SUCCESS;
} }
@ -221,8 +220,9 @@ namespace DotRecast.Recast.Toolset.Tools
} }
public DtStatus UpdateSlicedFindPath(DtNavMeshQuery navQuery, int maxIter, long endRef, RcVec3f startPos, RcVec3f endPos, public DtStatus UpdateSlicedFindPath(DtNavMeshQuery navQuery, int maxIter, long endRef, RcVec3f startPos, RcVec3f endPos,
ref List<long> path, ref List<DtStraightPath> straightPath) ref List<long> path, Span<DtStraightPath> straightPath, out int straightPathCount, int maxStraightPath)
{ {
straightPathCount = 0;
var status = navQuery.UpdateSlicedFindPath(maxIter, out _); var status = navQuery.UpdateSlicedFindPath(maxIter, out _);
if (!status.Succeeded()) if (!status.Succeeded())
@ -232,7 +232,6 @@ namespace DotRecast.Recast.Toolset.Tools
navQuery.FinalizeSlicedFindPath(ref path); navQuery.FinalizeSlicedFindPath(ref path);
straightPath?.Clear();
if (path != null) if (path != null)
{ {
// In case of partial path, make sure the end point is clamped to the last polygon. // In case of partial path, make sure the end point is clamped to the last polygon.
@ -246,8 +245,7 @@ namespace DotRecast.Recast.Toolset.Tools
} }
} }
straightPath = new List<DtStraightPath>(MAX_POLYS); navQuery.FindStraightPath(startPos, epos, path, path.Count, straightPath, out straightPathCount, maxStraightPath, DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS);
navQuery.FindStraightPath(startPos, epos, path, path.Count, ref straightPath, MAX_POLYS, DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS);
} }
return DtStatus.DT_SUCCESS; return DtStatus.DT_SUCCESS;
@ -255,13 +253,12 @@ namespace DotRecast.Recast.Toolset.Tools
public DtStatus Raycast(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter, public DtStatus Raycast(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter,
ref List<long> polys, ref List<DtStraightPath> straightPath, ref RcVec3f hitPos, ref RcVec3f hitNormal, ref bool hitResult) ref List<long> polys, Span<DtStraightPath> straightPath, out int straightPathCount, int maxStraightPath, ref RcVec3f hitPos, ref RcVec3f hitNormal, ref bool hitResult)
{ {
straightPathCount = 0;
if (startRef == 0 || endRef == 0) if (startRef == 0 || endRef == 0)
{ {
polys?.Clear(); polys?.Clear();
straightPath?.Clear();
return DtStatus.DT_FAILURE; return DtStatus.DT_FAILURE;
} }
@ -299,10 +296,8 @@ namespace DotRecast.Recast.Toolset.Tools
} }
} }
straightPath ??= new List<DtStraightPath>(); straightPath[straightPathCount++] = new DtStraightPath(startPos, 0, 0);
straightPath.Clear(); straightPath[straightPathCount++] = new DtStraightPath(hitPos, 0, 0);
straightPath.Add(new DtStraightPath(startPos, 0, 0));
straightPath.Add(new DtStraightPath(hitPos, 0, 0));
return status; return status;
} }

View File

@ -17,15 +17,13 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using Moq;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Detour.Crowd.Test; namespace DotRecast.Detour.Crowd.Test;
public class DtPathCorridorTest public class DtPathCorridorTest
{ {
private readonly DtPathCorridor corridor = new DtPathCorridor(); private readonly DtPathCorridor corridor = new DtPathCorridor();
@ -41,63 +39,35 @@ public class DtPathCorridorTest
[Test] [Test]
public void ShouldKeepOriginalPathInFindCornersWhenNothingCanBePruned() public void ShouldKeepOriginalPathInFindCornersWhenNothingCanBePruned()
{ {
List<DtStraightPath> straightPath = new(); var straightPath = new DtStraightPath[4];
straightPath.Add(new DtStraightPath(new RcVec3f(11, 20, 30.00001f), 0, 0)); straightPath[0] = new DtStraightPath(new RcVec3f(11, 20, 30.00001f), 0, 0);
straightPath.Add(new DtStraightPath(new RcVec3f(12, 20, 30.00002f), 0, 0)); straightPath[1] = new DtStraightPath(new RcVec3f(12, 20, 30.00002f), 0, 0);
straightPath.Add(new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0)); straightPath[2] = new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0);
straightPath.Add(new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0)); straightPath[3] = new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0);
var mockQuery = new Mock<DtNavMeshQuery>(It.IsAny<DtNavMesh>()); var query = new DtNavMeshQueryMock(straightPath, DtStatus.DT_SUCCESS);
mockQuery.Setup(q => q.FindStraightPath(
It.IsAny<RcVec3f>(),
It.IsAny<RcVec3f>(),
It.IsAny<List<long>>(),
It.IsAny<int>(),
ref It.Ref<List<DtStraightPath>>.IsAny,
It.IsAny<int>(),
It.IsAny<int>())
)
.Callback((RcVec3f startPos, RcVec3f endPos, List<long> path, int pathSize,
ref List<DtStraightPath> refStraightPath, int maxStraightPath, int options) =>
{
refStraightPath = straightPath;
})
.Returns(() => DtStatus.DT_SUCCESS);
var path = new List<DtStraightPath>(); Span<DtStraightPath> path = stackalloc DtStraightPath[8];
corridor.FindCorners(ref path, int.MaxValue, mockQuery.Object, filter); var npath = corridor.FindCorners(path, 8, query, filter);
Assert.That(path.Count, Is.EqualTo(4)); Assert.That(npath, Is.EqualTo(4));
Assert.That(path, Is.EqualTo(straightPath)); Assert.That(path.Slice(0, npath).ToArray(), Is.EqualTo(straightPath));
} }
[Test] [Test]
public void ShouldPrunePathInFindCorners() public void ShouldPrunePathInFindCorners()
{ {
List<DtStraightPath> straightPath = new(); DtStraightPath[] straightPath = new DtStraightPath[5];
straightPath.Add(new DtStraightPath(new RcVec3f(10, 20, 30.00001f), 0, 0)); // too close straightPath[0] = (new DtStraightPath(new RcVec3f(10, 20, 30.00001f), 0, 0)); // too close
straightPath.Add(new DtStraightPath(new RcVec3f(10, 20, 30.00002f), 0, 0)); // too close straightPath[1] = (new DtStraightPath(new RcVec3f(10, 20, 30.00002f), 0, 0)); // too close
straightPath.Add(new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0)); straightPath[2] = (new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0));
straightPath.Add(new DtStraightPath(new RcVec3f(12f, 22, 33f), DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION, 0)); // offmesh straightPath[3] = (new DtStraightPath(new RcVec3f(12f, 22, 33f), DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION, 0)); // offmesh
straightPath.Add(new DtStraightPath(new RcVec3f(11f, 21, 32f), DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION, 0)); // offmesh straightPath[4] = (new DtStraightPath(new RcVec3f(11f, 21, 32f), DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION, 0)); // offmesh
var mockQuery = new Mock<DtNavMeshQuery>(It.IsAny<DtNavMesh>()); var query = new DtNavMeshQueryMock(straightPath, DtStatus.DT_SUCCESS);
mockQuery.Setup(q => q.FindStraightPath(
It.IsAny<RcVec3f>(),
It.IsAny<RcVec3f>(),
It.IsAny<List<long>>(),
It.IsAny<int>(),
ref It.Ref<List<DtStraightPath>>.IsAny,
It.IsAny<int>(),
It.IsAny<int>())
).Callback((RcVec3f startPos, RcVec3f endPos, List<long> path, int pathSize,
ref List<DtStraightPath> refStraightPath, int maxStraightPath, int options) =>
{
refStraightPath = straightPath;
})
.Returns(() => DtStatus.DT_SUCCESS);
var path = new List<DtStraightPath>(); Span<DtStraightPath> path = stackalloc DtStraightPath[8];
corridor.FindCorners(ref path, int.MaxValue, mockQuery.Object, filter); int npath = corridor.FindCorners(path, 8, query, filter);
Assert.That(path.Count, Is.EqualTo(2)); Assert.That(npath, Is.EqualTo(2));
Assert.That(path, Is.EqualTo(new List<DtStraightPath> { straightPath[2], straightPath[3] })); Assert.That(path.Slice(0, npath).ToArray(), Is.EqualTo(new DtStraightPath[] { straightPath[2], straightPath[3] }));
} }
} }

View File

@ -16,13 +16,13 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
public class FindPathTest : AbstractDetourTest public class FindPathTest : AbstractDetourTest
{ {
private static readonly DtStatus[] STATUSES = private static readonly DtStatus[] STATUSES =
@ -184,6 +184,7 @@ public class FindPathTest : AbstractDetourTest
{ {
IDtQueryFilter filter = new DtQueryDefaultFilter(); IDtQueryFilter filter = new DtQueryDefaultFilter();
var path = new List<long>(); var path = new List<long>();
Span<DtStraightPath> straightPath = stackalloc DtStraightPath[256];
for (int i = 0; i < STRAIGHT_PATHS.Length; i++) for (int i = 0; i < STRAIGHT_PATHS.Length; i++)
{ {
// startRefs.Length; i++) { // startRefs.Length; i++) {
@ -192,9 +193,8 @@ public class FindPathTest : AbstractDetourTest
var startPos = startPoss[i]; var startPos = startPoss[i];
var endPos = endPoss[i]; var endPos = endPoss[i];
var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption); var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption);
var straightPath = new List<DtStraightPath>(); query.FindStraightPath(startPos, endPos, path, path.Count, straightPath, out var nstraightPath, 256, 0);
query.FindStraightPath(startPos, endPos, path, path.Count, ref straightPath, int.MaxValue, 0); Assert.That(nstraightPath, Is.EqualTo(STRAIGHT_PATHS[i].Length));
Assert.That(straightPath.Count, Is.EqualTo(STRAIGHT_PATHS[i].Length));
for (int j = 0; j < STRAIGHT_PATHS[i].Length; j++) for (int j = 0; j < STRAIGHT_PATHS[i].Length; j++)
{ {
Assert.That(straightPath[j].refs, Is.EqualTo(STRAIGHT_PATHS[i][j].refs)); Assert.That(straightPath[j].refs, Is.EqualTo(STRAIGHT_PATHS[i][j].refs));

View File

@ -18,6 +18,7 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using DotRecast.Core; using DotRecast.Core;
@ -29,7 +30,6 @@ using NUnit.Framework;
namespace DotRecast.Detour.TileCache.Test; namespace DotRecast.Detour.TileCache.Test;
public class TileCacheFindPathTest : AbstractTileCacheTest public class TileCacheFindPathTest : AbstractTileCacheTest
{ {
private readonly RcVec3f start = new RcVec3f(39.44734f, 9.998177f, -0.784811f); private readonly RcVec3f start = new RcVec3f(39.44734f, 9.998177f, -0.784811f);
@ -56,11 +56,11 @@ public class TileCacheFindPathTest : AbstractTileCacheTest
var path = new List<long>(); var path = new List<long>();
var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption); var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption);
int maxStraightPath = 256; const int maxStraightPath = 256;
int options = 0; int options = 0;
var pathStr = new List<DtStraightPath>(); Span<DtStraightPath> pathStr = stackalloc DtStraightPath[maxStraightPath];
query.FindStraightPath(startPos, endPos, path, path.Count, ref pathStr, maxStraightPath, options); query.FindStraightPath(startPos, endPos, path, path.Count, pathStr, out var npathStr, maxStraightPath, options);
Assert.That(pathStr.Count, Is.EqualTo(8)); Assert.That(npathStr, Is.EqualTo(8));
} }
} }