Compare commits

..

19 Commits

Author SHA1 Message Date
ikpil f49f9eb558 addedd AllocSpan/FreeSpan API 2024-04-22 23:57:13 +09:00
ikpil 6b2bd27b71 added RcSpanPool class 2024-04-22 23:52:01 +09:00
ikpil 50ea674cce used option `keepInterResults` to save memory
- https://github.com/ikpil/DotRecast/issues/66
- https://github.com/ikpil/DotRecast/issues/61
2024-04-20 12:59:34 +09:00
ikpil 8fe46a6450 Keep Itermediate Results in JumpLinkBuilderSampleTools 2024-04-18 00:10:06 +09:00
ikpil 80e07ebb3c Tick 'Keep Intermediate Results' option. 2024-04-17 23:50:48 +09:00
ikpil 97ffaf8700 add comment "Tick 'Keep Itermediate Results'" 2024-04-16 23:24:48 +09:00
ikpil 17c1c18372 added "Keep Itermediate Results" flag 2024-04-15 23:42:55 +09:00
ikpil 9210eb58fe changed to use `CompactHeightfield` instead of `Heightfield` when finding bmin/bmax 2024-04-14 00:43:03 +09:00
ikpil da82d6ba4d reduced usability of SolidHeightfield in recast build results 2024-04-13 12:53:58 +09:00
ikpil 26a1dfddeb fix bug where return value of dynamic update result is reversed 2024-04-13 12:43:54 +09:00
ikpil 668ebd0128 update comment 2024-04-12 02:27:07 +09:00
ikpil 402b25a436 fixed an issue where await deadlock could occur in various platform environments. 2024-04-12 00:08:06 +09:00
ikpil 7874b4403c Renamed RcBuilderResult class to C# style naming 2024-04-10 20:39:06 +09:00
ikpil 82027dffd7 Limiting the number of tasks during build 2024-04-10 19:52:58 +09:00
ikpil 573386c473 update library K4os.Compression.LZ4 1.3.8, NUnit.Analyzers 4.1.0, coverlet.collector 6.0.2 2024-03-22 23:12:55 +09:00
ikpil daf826f5f4 modify .editorconfig 2024-03-13 23:18:18 +09:00
wreng b67ebeaec3 Added stackalloc where it's acceptable 2024-03-06 01:12:11 +09:00
Andrew Gilewsky 7dfac39408 Optimization: reduce number of allocations on hot path. 2024-03-06 01:09:32 +09:00
ikpil 4b8cd8e31b NUnit 4.1.0 2024-02-29 13:54:36 +09:00
43 changed files with 468 additions and 367 deletions

View File

@ -20,6 +20,7 @@ dotnet_sort_system_directives_first = true
csharp_preserve_single_line_statements = false csharp_preserve_single_line_statements = false
csharp_preserve_single_line_blocks = true csharp_preserve_single_line_blocks = true
# # ReSharper properties
resharper_csharp_wrap_lines = false
resharper_csharp_space_before_trailing_comment = true resharper_csharp_space_before_trailing_comment = true
resharper_csharp_space_after_operator_keyword = true resharper_csharp_space_after_operator_keyword = true

View File

@ -3,5 +3,7 @@
public interface IRcRand public interface IRcRand
{ {
float Next(); float Next();
double NextDouble();
int NextInt32();
} }
} }

View File

@ -6,9 +6,13 @@ namespace DotRecast.Core
{ {
private readonly Random _r; private readonly Random _r;
public RcRand() public RcRand() : this(new Random())
{ {
_r = new Random(); }
public RcRand(Random r)
{
_r = r;
} }
public RcRand(long seed) public RcRand(long seed)
@ -21,6 +25,11 @@ namespace DotRecast.Core
return (float)_r.NextDouble(); return (float)_r.NextDouble();
} }
public double NextDouble()
{
return _r.NextDouble();
}
public int NextInt32() public int NextInt32()
{ {
return _r.Next(); return _r.Next();

View File

@ -40,7 +40,7 @@ namespace DotRecast.Detour.Dynamic
private readonly BlockingCollection<IDtDaynmicTileJob> updateQueue = new BlockingCollection<IDtDaynmicTileJob>(); private readonly BlockingCollection<IDtDaynmicTileJob> updateQueue = new BlockingCollection<IDtDaynmicTileJob>();
private readonly RcAtomicLong currentColliderId = new RcAtomicLong(0); private readonly RcAtomicLong currentColliderId = new RcAtomicLong(0);
private DtNavMesh _navMesh; private DtNavMesh _navMesh;
private bool dirty = true; private bool _dirty = true;
public DtDynamicNavMesh(DtVoxelFile voxelFile) public DtDynamicNavMesh(DtVoxelFile voxelFile)
{ {
@ -105,29 +105,6 @@ namespace DotRecast.Detour.Dynamic
updateQueue.Add(new DtDynamicTileColliderRemovalJob(colliderId, GetTilesByCollider(colliderId))); updateQueue.Add(new DtDynamicTileColliderRemovalJob(colliderId, GetTilesByCollider(colliderId)));
} }
/**
* Perform full build of the nav mesh
*/
public void Build()
{
ProcessQueue();
Rebuild(_tiles.Values);
}
/**
* Perform incremental update of the nav mesh
*/
public bool Update()
{
return Rebuild(ProcessQueue());
}
private bool Rebuild(ICollection<DtDynamicTile> stream)
{
foreach (var dynamicTile in stream)
Rebuild(dynamicTile);
return UpdateNavMesh();
}
private HashSet<DtDynamicTile> ProcessQueue() private HashSet<DtDynamicTile> ProcessQueue()
{ {
@ -159,27 +136,49 @@ namespace DotRecast.Detour.Dynamic
} }
} }
/** // Perform full build of the navmesh
* Perform full build concurrently using the given {@link ExecutorService} public void Build()
*/ {
public Task<bool> Build(TaskFactory executor) ProcessQueue();
Rebuild(_tiles.Values);
}
// Perform full build concurrently using the given {@link ExecutorService}
public bool Build(TaskFactory executor)
{ {
ProcessQueue(); ProcessQueue();
return Rebuild(_tiles.Values, executor); return Rebuild(_tiles.Values, executor);
} }
/**
* Perform incremental update concurrently using the given {@link ExecutorService} // Perform incremental update of the navmesh
*/ public bool Update()
public Task<bool> Update(TaskFactory executor) {
return Rebuild(ProcessQueue());
}
// Perform incremental update concurrently using the given {@link ExecutorService}
public bool Update(TaskFactory executor)
{ {
return Rebuild(ProcessQueue(), executor); return Rebuild(ProcessQueue(), executor);
} }
private Task<bool> Rebuild(ICollection<DtDynamicTile> tiles, TaskFactory executor) private bool Rebuild(ICollection<DtDynamicTile> tiles)
{ {
var tasks = tiles.Select(tile => executor.StartNew(() => Rebuild(tile))).ToArray(); foreach (var tile in tiles)
return Task.WhenAll(tasks).ContinueWith(k => UpdateNavMesh()); Rebuild(tile);
return UpdateNavMesh();
}
private bool Rebuild(ICollection<DtDynamicTile> tiles, TaskFactory executor)
{
var tasks = tiles
.Select(tile => executor.StartNew(() => Rebuild(tile)))
.ToArray();
Task.WaitAll(tasks);
return UpdateNavMesh();
} }
private ICollection<DtDynamicTile> GetTiles(float[] bounds) private ICollection<DtDynamicTile> GetTiles(float[] bounds)
@ -218,19 +217,19 @@ namespace DotRecast.Detour.Dynamic
{ {
DtNavMeshCreateParams option = new DtNavMeshCreateParams(); DtNavMeshCreateParams option = new DtNavMeshCreateParams();
option.walkableHeight = config.walkableHeight; option.walkableHeight = config.walkableHeight;
dirty = dirty | tile.Build(builder, config, _context); _dirty = _dirty | tile.Build(builder, config, _context);
} }
private bool UpdateNavMesh() private bool UpdateNavMesh()
{ {
if (dirty) if (_dirty)
{ {
DtNavMesh navMesh = new DtNavMesh(navMeshParams, MAX_VERTS_PER_POLY); DtNavMesh navMesh = new DtNavMesh(navMeshParams, MAX_VERTS_PER_POLY);
foreach (var t in _tiles.Values) foreach (var t in _tiles.Values)
t.AddTo(navMesh); t.AddTo(navMesh);
this._navMesh = navMesh; _navMesh = navMesh;
dirty = false; _dirty = false;
return true; return true;
} }

View File

@ -132,8 +132,8 @@ namespace DotRecast.Detour.Dynamic
private DtNavMeshCreateParams NavMeshCreateParams(int tilex, int tileZ, float cellSize, float cellHeight, private DtNavMeshCreateParams NavMeshCreateParams(int tilex, int tileZ, float cellSize, float cellHeight,
DtDynamicNavMeshConfig config, RcBuilderResult rcResult) DtDynamicNavMeshConfig config, RcBuilderResult rcResult)
{ {
RcPolyMesh m_pmesh = rcResult.GetMesh(); RcPolyMesh m_pmesh = rcResult.Mesh;
RcPolyMeshDetail m_dmesh = rcResult.GetMeshDetail(); RcPolyMeshDetail m_dmesh = rcResult.MeshDetail;
DtNavMeshCreateParams option = new DtNavMeshCreateParams(); DtNavMeshCreateParams option = new DtNavMeshCreateParams();
for (int i = 0; i < m_pmesh.npolys; ++i) for (int i = 0; i < m_pmesh.npolys; ++i)
{ {

View File

@ -109,13 +109,13 @@ namespace DotRecast.Detour.Dynamic.Io
}; };
foreach (RcBuilderResult r in results) foreach (RcBuilderResult r in results)
{ {
f.tiles.Add(new DtVoxelTile(r.tileX, r.tileZ, r.GetSolidHeightfield())); f.tiles.Add(new DtVoxelTile(r.TileX, r.TileZ, r.SolidHeightfiled));
f.bounds[0] = Math.Min(f.bounds[0], r.GetSolidHeightfield().bmin.X); f.bounds[0] = Math.Min(f.bounds[0], r.SolidHeightfiled.bmin.X);
f.bounds[1] = Math.Min(f.bounds[1], r.GetSolidHeightfield().bmin.Y); f.bounds[1] = Math.Min(f.bounds[1], r.SolidHeightfiled.bmin.Y);
f.bounds[2] = Math.Min(f.bounds[2], r.GetSolidHeightfield().bmin.Z); f.bounds[2] = Math.Min(f.bounds[2], r.SolidHeightfiled.bmin.Z);
f.bounds[3] = Math.Max(f.bounds[3], r.GetSolidHeightfield().bmax.X); f.bounds[3] = Math.Max(f.bounds[3], r.SolidHeightfiled.bmax.X);
f.bounds[4] = Math.Max(f.bounds[4], r.GetSolidHeightfield().bmax.Y); f.bounds[4] = Math.Max(f.bounds[4], r.SolidHeightfiled.bmax.Y);
f.bounds[5] = Math.Max(f.bounds[5], r.GetSolidHeightfield().bmax.Z); f.bounds[5] = Math.Max(f.bounds[5], r.SolidHeightfiled.bmax.Z);
} }
return f; return f;

View File

@ -21,7 +21,7 @@ namespace DotRecast.Detour.Extras.Jumplink
public JumpLinkBuilder(IList<RcBuilderResult> results) public JumpLinkBuilder(IList<RcBuilderResult> results)
{ {
this.results = results; this.results = results;
edges = results.Select(r => edgeExtractor.ExtractEdges(r.GetMesh())).ToList(); edges = results.Select(r => edgeExtractor.ExtractEdges(r.Mesh)).ToList();
} }
public List<JumpLink> Build(JumpLinkBuilderConfig acfg, JumpLinkType type) public List<JumpLink> Build(JumpLinkBuilderConfig acfg, JumpLinkType type)
@ -43,7 +43,7 @@ namespace DotRecast.Detour.Extras.Jumplink
{ {
EdgeSampler es = edgeSamplerFactory.Get(acfg, type, edge); EdgeSampler es = edgeSamplerFactory.Get(acfg, type, edge);
groundSampler.Sample(acfg, result, es); groundSampler.Sample(acfg, result, es);
trajectorySampler.Sample(acfg, result.GetSolidHeightfield(), es); trajectorySampler.Sample(acfg, result.SolidHeightfiled, es);
JumpSegment[] jumpSegments = jumpSegmentBuilder.Build(acfg, es); JumpSegment[] jumpSegments = jumpSegmentBuilder.Build(acfg, es);
return BuildJumpLinks(acfg, es, jumpSegments); return BuildJumpLinks(acfg, es, jumpSegments);
} }

View File

@ -16,25 +16,25 @@ namespace DotRecast.Detour.Extras.Jumplink
private DtNavMeshQuery CreateNavMesh(RcBuilderResult r, float agentRadius, float agentHeight, float agentClimb) private DtNavMeshQuery CreateNavMesh(RcBuilderResult r, float agentRadius, float agentHeight, float agentClimb)
{ {
DtNavMeshCreateParams option = new DtNavMeshCreateParams(); DtNavMeshCreateParams option = new DtNavMeshCreateParams();
option.verts = r.GetMesh().verts; option.verts = r.Mesh.verts;
option.vertCount = r.GetMesh().nverts; option.vertCount = r.Mesh.nverts;
option.polys = r.GetMesh().polys; option.polys = r.Mesh.polys;
option.polyAreas = r.GetMesh().areas; option.polyAreas = r.Mesh.areas;
option.polyFlags = r.GetMesh().flags; option.polyFlags = r.Mesh.flags;
option.polyCount = r.GetMesh().npolys; option.polyCount = r.Mesh.npolys;
option.nvp = r.GetMesh().nvp; option.nvp = r.Mesh.nvp;
option.detailMeshes = r.GetMeshDetail().meshes; option.detailMeshes = r.MeshDetail.meshes;
option.detailVerts = r.GetMeshDetail().verts; option.detailVerts = r.MeshDetail.verts;
option.detailVertsCount = r.GetMeshDetail().nverts; option.detailVertsCount = r.MeshDetail.nverts;
option.detailTris = r.GetMeshDetail().tris; option.detailTris = r.MeshDetail.tris;
option.detailTriCount = r.GetMeshDetail().ntris; option.detailTriCount = r.MeshDetail.ntris;
option.walkableRadius = agentRadius; option.walkableRadius = agentRadius;
option.walkableHeight = agentHeight; option.walkableHeight = agentHeight;
option.walkableClimb = agentClimb; option.walkableClimb = agentClimb;
option.bmin = r.GetMesh().bmin; option.bmin = r.Mesh.bmin;
option.bmax = r.GetMesh().bmax; option.bmax = r.Mesh.bmax;
option.cs = r.GetMesh().cs; option.cs = r.Mesh.cs;
option.ch = r.GetMesh().ch; option.ch = r.Mesh.ch;
option.buildBvTree = true; option.buildBvTree = true;
return new DtNavMeshQuery(new DtNavMesh(DtNavMeshBuilder.CreateNavMeshData(option), option.nvp, 0)); return new DtNavMeshQuery(new DtNavMesh(DtNavMeshBuilder.CreateNavMeshData(option), option.nvp, 0));
} }

View File

@ -358,8 +358,8 @@ namespace DotRecast.Detour
var tbmax = tile.data.header.bmax; var tbmax = tile.data.header.bmax;
float qfac = tile.data.header.bvQuantFactor; float qfac = tile.data.header.bvQuantFactor;
// Calculate quantized box // Calculate quantized box
int[] bmin = new int[3]; Span<int> bmin = stackalloc int[3];
int[] bmax = new int[3]; Span<int> bmax = stackalloc int[3];
// dtClamp query box to world box. // dtClamp query box to world box.
float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X; float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X;
float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y;

View File

@ -583,8 +583,8 @@ namespace DotRecast.Detour
var tbmax = tile.data.header.bmax; var tbmax = tile.data.header.bmax;
float qfac = tile.data.header.bvQuantFactor; float qfac = tile.data.header.bvQuantFactor;
// Calculate quantized box // Calculate quantized box
int[] bmin = new int[3]; Span<int> bmin = stackalloc int[3];
int[] bmax = new int[3]; Span<int> bmax = stackalloc int[3];
// dtClamp query box to world box. // dtClamp query box to world box.
float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X; float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X;
float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y;
@ -1824,6 +1824,9 @@ namespace DotRecast.Detour
float[] verts = new float[m_nav.GetMaxVertsPerPoly() * 3]; float[] verts = new float[m_nav.GetMaxVertsPerPoly() * 3];
const int MAX_NEIS = 8;
Span<long> neis = stackalloc long[MAX_NEIS];
while (0 < stack.Count) while (0 < stack.Count)
{ {
// Pop front. // Pop front.
@ -1854,9 +1857,7 @@ namespace DotRecast.Detour
for (int i = 0, j = curPoly.vertCount - 1; i < curPoly.vertCount; j = i++) for (int i = 0, j = curPoly.vertCount - 1; i < curPoly.vertCount; j = i++)
{ {
// Find links to neighbours. // Find links to neighbours.
int MAX_NEIS = 8;
int nneis = 0; int nneis = 0;
long[] neis = new long[MAX_NEIS];
if ((curPoly.neis[j] & DtNavMesh.DT_EXT_LINK) != 0) if ((curPoly.neis[j] & DtNavMesh.DT_EXT_LINK) != 0)
{ {

View File

@ -64,7 +64,7 @@ namespace DotRecast.Detour
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)] /// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
/// @return True if the two AABB's overlap. /// @return True if the two AABB's overlap.
/// @see dtOverlapBounds /// @see dtOverlapBounds
public static bool OverlapQuantBounds(int[] amin, int[] amax, int[] bmin, int[] bmax) public static bool OverlapQuantBounds(Span<int> amin, Span<int> amax, Span<int> bmin, Span<int> bmax)
{ {
bool overlap = true; bool overlap = true;
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;

View File

@ -26,7 +26,7 @@
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0"/> <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0"/>
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" /> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/>
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.6"/> <PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageReference Include="Silk.NET" Version="2.20.0" /> <PackageReference Include="Silk.NET" Version="2.20.0" />
<PackageReference Include="Silk.NET.OpenGL.Extensions.ImGui" Version="2.20.0" /> <PackageReference Include="Silk.NET.OpenGL.Extensions.ImGui" Version="2.20.0" />
</ItemGroup> </ItemGroup>

View File

@ -123,80 +123,80 @@ public class NavMeshRenderer
foreach (RcBuilderResult rcBuilderResult in rcBuilderResults) foreach (RcBuilderResult rcBuilderResult in rcBuilderResults)
{ {
if (rcBuilderResult.GetCompactHeightfield() != null && drawMode == DrawMode.DRAWMODE_COMPACT) if (rcBuilderResult.CompactHeightfield != null && drawMode == DrawMode.DRAWMODE_COMPACT)
{ {
_debugDraw.DebugDrawCompactHeightfieldSolid(rcBuilderResult.GetCompactHeightfield()); _debugDraw.DebugDrawCompactHeightfieldSolid(rcBuilderResult.CompactHeightfield);
} }
if (rcBuilderResult.GetCompactHeightfield() != null && drawMode == DrawMode.DRAWMODE_COMPACT_DISTANCE) if (rcBuilderResult.CompactHeightfield != null && drawMode == DrawMode.DRAWMODE_COMPACT_DISTANCE)
{ {
_debugDraw.DebugDrawCompactHeightfieldDistance(rcBuilderResult.GetCompactHeightfield()); _debugDraw.DebugDrawCompactHeightfieldDistance(rcBuilderResult.CompactHeightfield);
} }
if (rcBuilderResult.GetCompactHeightfield() != null && drawMode == DrawMode.DRAWMODE_COMPACT_REGIONS) if (rcBuilderResult.CompactHeightfield != null && drawMode == DrawMode.DRAWMODE_COMPACT_REGIONS)
{ {
_debugDraw.DebugDrawCompactHeightfieldRegions(rcBuilderResult.GetCompactHeightfield()); _debugDraw.DebugDrawCompactHeightfieldRegions(rcBuilderResult.CompactHeightfield);
} }
if (rcBuilderResult.GetSolidHeightfield() != null && drawMode == DrawMode.DRAWMODE_VOXELS) if (rcBuilderResult.SolidHeightfiled != null && drawMode == DrawMode.DRAWMODE_VOXELS)
{ {
_debugDraw.Fog(true); _debugDraw.Fog(true);
_debugDraw.DebugDrawHeightfieldSolid(rcBuilderResult.GetSolidHeightfield()); _debugDraw.DebugDrawHeightfieldSolid(rcBuilderResult.SolidHeightfiled);
_debugDraw.Fog(false); _debugDraw.Fog(false);
} }
if (rcBuilderResult.GetSolidHeightfield() != null && drawMode == DrawMode.DRAWMODE_VOXELS_WALKABLE) if (rcBuilderResult.SolidHeightfiled != null && drawMode == DrawMode.DRAWMODE_VOXELS_WALKABLE)
{ {
_debugDraw.Fog(true); _debugDraw.Fog(true);
_debugDraw.DebugDrawHeightfieldWalkable(rcBuilderResult.GetSolidHeightfield()); _debugDraw.DebugDrawHeightfieldWalkable(rcBuilderResult.SolidHeightfiled);
_debugDraw.Fog(false); _debugDraw.Fog(false);
} }
if (rcBuilderResult.GetContourSet() != null && drawMode == DrawMode.DRAWMODE_RAW_CONTOURS) if (rcBuilderResult.ContourSet != null && drawMode == DrawMode.DRAWMODE_RAW_CONTOURS)
{ {
_debugDraw.DepthMask(false); _debugDraw.DepthMask(false);
_debugDraw.DebugDrawRawContours(rcBuilderResult.GetContourSet(), 1f); _debugDraw.DebugDrawRawContours(rcBuilderResult.ContourSet, 1f);
_debugDraw.DepthMask(true); _debugDraw.DepthMask(true);
} }
if (rcBuilderResult.GetContourSet() != null && drawMode == DrawMode.DRAWMODE_BOTH_CONTOURS) if (rcBuilderResult.ContourSet != null && drawMode == DrawMode.DRAWMODE_BOTH_CONTOURS)
{ {
_debugDraw.DepthMask(false); _debugDraw.DepthMask(false);
_debugDraw.DebugDrawRawContours(rcBuilderResult.GetContourSet(), 0.5f); _debugDraw.DebugDrawRawContours(rcBuilderResult.ContourSet, 0.5f);
_debugDraw.DebugDrawContours(rcBuilderResult.GetContourSet()); _debugDraw.DebugDrawContours(rcBuilderResult.ContourSet);
_debugDraw.DepthMask(true); _debugDraw.DepthMask(true);
} }
if (rcBuilderResult.GetContourSet() != null && drawMode == DrawMode.DRAWMODE_CONTOURS) if (rcBuilderResult.ContourSet != null && drawMode == DrawMode.DRAWMODE_CONTOURS)
{ {
_debugDraw.DepthMask(false); _debugDraw.DepthMask(false);
_debugDraw.DebugDrawContours(rcBuilderResult.GetContourSet()); _debugDraw.DebugDrawContours(rcBuilderResult.ContourSet);
_debugDraw.DepthMask(true); _debugDraw.DepthMask(true);
} }
if (rcBuilderResult.GetCompactHeightfield() != null && drawMode == DrawMode.DRAWMODE_REGION_CONNECTIONS) if (rcBuilderResult.CompactHeightfield != null && drawMode == DrawMode.DRAWMODE_REGION_CONNECTIONS)
{ {
_debugDraw.DebugDrawCompactHeightfieldRegions(rcBuilderResult.GetCompactHeightfield()); _debugDraw.DebugDrawCompactHeightfieldRegions(rcBuilderResult.CompactHeightfield);
_debugDraw.DepthMask(false); _debugDraw.DepthMask(false);
if (rcBuilderResult.GetContourSet() != null) if (rcBuilderResult.ContourSet != null)
{ {
_debugDraw.DebugDrawRegionConnections(rcBuilderResult.GetContourSet()); _debugDraw.DebugDrawRegionConnections(rcBuilderResult.ContourSet);
} }
_debugDraw.DepthMask(true); _debugDraw.DepthMask(true);
} }
if (rcBuilderResult.GetMesh() != null && drawMode == DrawMode.DRAWMODE_POLYMESH) if (rcBuilderResult.Mesh != null && drawMode == DrawMode.DRAWMODE_POLYMESH)
{ {
_debugDraw.DepthMask(false); _debugDraw.DepthMask(false);
_debugDraw.DebugDrawPolyMesh(rcBuilderResult.GetMesh()); _debugDraw.DebugDrawPolyMesh(rcBuilderResult.Mesh);
_debugDraw.DepthMask(true); _debugDraw.DepthMask(true);
} }
if (rcBuilderResult.GetMeshDetail() != null && drawMode == DrawMode.DRAWMODE_POLYMESH_DETAIL) if (rcBuilderResult.MeshDetail != null && drawMode == DrawMode.DRAWMODE_POLYMESH_DETAIL)
{ {
_debugDraw.DepthMask(false); _debugDraw.DepthMask(false);
_debugDraw.DebugDrawPolyMeshDetail(rcBuilderResult.GetMeshDetail()); _debugDraw.DebugDrawPolyMeshDetail(rcBuilderResult.MeshDetail);
_debugDraw.DepthMask(true); _debugDraw.DepthMask(true);
} }
} }

View File

@ -368,7 +368,7 @@ public class RecastDemo : IRecastDemoChannel
var scale = (float)_resolution.X / 1920; var scale = (float)_resolution.X / 1920;
int fontSize = Math.Max(10, (int)(16 * scale)); int fontSize = Math.Max(10, (int)(16 * scale));
// for windows : Microsoft Visual C++ Redistributable Package // for windows : Microsoft Visual C++ Redistributable Package
// link - https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist // link - https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist
var imGuiFontConfig = new ImGuiFontConfig(Path.Combine("resources\\fonts", "DroidSans.ttf"), fontSize, null); var imGuiFontConfig = new ImGuiFontConfig(Path.Combine("resources\\fonts", "DroidSans.ttf"), fontSize, null);
@ -379,7 +379,7 @@ public class RecastDemo : IRecastDemoChannel
DemoInputGeomProvider geom = LoadInputMesh("nav_test.obj"); DemoInputGeomProvider geom = LoadInputMesh("nav_test.obj");
_sample = new DemoSample(geom, ImmutableArray<RcBuilderResult>.Empty, null); _sample = new DemoSample(geom, ImmutableArray<RcBuilderResult>.Empty, null);
_menuView = new RcMenuView(); _menuView = new RcMenuView();
settingsView = new RcSettingsView(this); settingsView = new RcSettingsView(this);
settingsView.SetSample(_sample); settingsView.SetSample(_sample);
@ -537,6 +537,7 @@ public class RecastDemo : IRecastDemoChannel
bool hasBound = false; bool hasBound = false;
RcVec3f bminN = RcVec3f.Zero; RcVec3f bminN = RcVec3f.Zero;
RcVec3f bmaxN = RcVec3f.Zero; RcVec3f bmaxN = RcVec3f.Zero;
if (_sample.GetInputGeom() != null) if (_sample.GetInputGeom() != null)
{ {
bminN = _sample.GetInputGeom().GetMeshBoundsMin(); bminN = _sample.GetInputGeom().GetMeshBoundsMin();
@ -552,7 +553,7 @@ public class RecastDemo : IRecastDemoChannel
{ {
foreach (RcBuilderResult result in _sample.GetRecastResults()) foreach (RcBuilderResult result in _sample.GetRecastResults())
{ {
if (result.GetSolidHeightfield() != null) if (result.CompactHeightfield != null)
{ {
if (!hasBound) if (!hasBound)
{ {
@ -561,15 +562,15 @@ public class RecastDemo : IRecastDemoChannel
} }
bminN = new RcVec3f( bminN = new RcVec3f(
Math.Min(bminN.X, result.GetSolidHeightfield().bmin.X), Math.Min(bminN.X, result.CompactHeightfield.bmin.X),
Math.Min(bminN.Y, result.GetSolidHeightfield().bmin.Y), Math.Min(bminN.Y, result.CompactHeightfield.bmin.Y),
Math.Min(bminN.Z, result.GetSolidHeightfield().bmin.Z) Math.Min(bminN.Z, result.CompactHeightfield.bmin.Z)
); );
bmaxN = new RcVec3f( bmaxN = new RcVec3f(
Math.Max(bmaxN.X, result.GetSolidHeightfield().bmax.X), Math.Max(bmaxN.X, result.CompactHeightfield.bmax.X),
Math.Max(bmaxN.Y, result.GetSolidHeightfield().bmax.Y), Math.Max(bmaxN.Y, result.CompactHeightfield.bmax.Y),
Math.Max(bmaxN.Z, result.GetSolidHeightfield().bmax.Z) Math.Max(bmaxN.Z, result.CompactHeightfield.bmax.Z)
); );
hasBound = true; hasBound = true;
@ -577,12 +578,15 @@ public class RecastDemo : IRecastDemoChannel
} }
} }
// Reset camera and fog to match the mesh bounds.
if (hasBound) if (hasBound)
{ {
RcVec3f bmin = bminN; RcVec3f bmin = bminN;
RcVec3f bmax = bmaxN; RcVec3f bmax = bmaxN;
camr = (float)(Math.Sqrt(RcMath.Sqr(bmax.X - bmin.X) + RcMath.Sqr(bmax.Y - bmin.Y) + RcMath.Sqr(bmax.Z - bmin.Z)) / 2); camr = (float)(Math.Sqrt(RcMath.Sqr(bmax.X - bmin.X) +
RcMath.Sqr(bmax.Y - bmin.Y) +
RcMath.Sqr(bmax.Z - bmin.Z)) / 2);
cameraPos.X = (bmax.X + bmin.X) / 2 + camr; cameraPos.X = (bmax.X + bmin.X) / 2 + camr;
cameraPos.Y = (bmax.Y + bmin.Y) / 2 + camr; cameraPos.Y = (bmax.Y + bmin.Y) / 2 + camr;
cameraPos.Z = (bmax.Z + bmin.Z) / 2 + camr; cameraPos.Z = (bmax.Z + bmin.Z) / 2 + camr;
@ -688,14 +692,15 @@ public class RecastDemo : IRecastDemoChannel
NavMeshBuildResult buildResult; NavMeshBuildResult buildResult;
var geom = _sample.GetInputGeom();
var settings = _sample.GetSettings(); var settings = _sample.GetSettings();
if (settings.tiled) if (settings.tiled)
{ {
buildResult = tileNavMeshBuilder.Build(_sample.GetInputGeom(), settings); buildResult = tileNavMeshBuilder.Build(geom, settings);
} }
else else
{ {
buildResult = soloNavMeshBuilder.Build(_sample.GetInputGeom(), settings); buildResult = soloNavMeshBuilder.Build(geom, settings);
} }
if (!buildResult.Success) if (!buildResult.Success)
@ -713,7 +718,7 @@ public class RecastDemo : IRecastDemoChannel
Logger.Information($"build times"); Logger.Information($"build times");
Logger.Information($"-----------------------------------------"); Logger.Information($"-----------------------------------------");
var telemetries = buildResult.RecastBuilderResults var telemetries = buildResult.RecastBuilderResults
.Select(x => x.GetTelemetry()) .Select(x => x.Context)
.SelectMany(x => x.ToList()) .SelectMany(x => x.ToList())
.GroupBy(x => x.Key) .GroupBy(x => x.Key)
.ToImmutableSortedDictionary(x => x.Key, x => x.Sum(y => y.Millis)); .ToImmutableSortedDictionary(x => x.Key, x => x.Sum(y => y.Millis));

View File

@ -91,7 +91,7 @@ public class DynamicUpdateSampleTool : ISampleTool
var bridgeGeom = DemoInputGeomProvider.LoadFile("bridge.obj"); var bridgeGeom = DemoInputGeomProvider.LoadFile("bridge.obj");
var houseGeom = DemoInputGeomProvider.LoadFile("house.obj"); var houseGeom = DemoInputGeomProvider.LoadFile("house.obj");
var convexGeom = DemoInputGeomProvider.LoadFile("convex.obj"); var convexGeom = DemoInputGeomProvider.LoadFile("convex.obj");
_tool = new(Random.Shared, bridgeGeom, houseGeom, convexGeom); _tool = new(new RcRand(Random.Shared), bridgeGeom, houseGeom, convexGeom);
executor = Task.Factory; executor = Task.Factory;
} }
@ -406,7 +406,7 @@ public class DynamicUpdateSampleTool : ISampleTool
long t = RcFrequency.Ticks; long t = RcFrequency.Ticks;
try try
{ {
bool updated = _tool.UpdateDynaMesh(executor); bool updated = _tool.Update(executor);
if (updated) if (updated)
{ {
buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond; buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond;
@ -450,7 +450,7 @@ public class DynamicUpdateSampleTool : ISampleTool
long t = RcFrequency.Ticks; long t = RcFrequency.Ticks;
try try
{ {
var _ = dynaMesh.Build(executor).Result; var _ = dynaMesh.Build(executor);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -16,6 +16,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.Linq;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Detour.Extras.Jumplink; using DotRecast.Detour.Extras.Jumplink;
using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Demo.Draw;
@ -96,13 +97,25 @@ public class JumpLinkBuilderSampleTool : ISampleTool
if (build || _cfg.buildOffMeshConnections) if (build || _cfg.buildOffMeshConnections)
{ {
if (0 < _sample.GetRecastResults().Count) do
{ {
if (0 >= _sample.GetRecastResults().Count)
{
Logger.Error("build navmesh");
break;
}
if (_sample.GetRecastResults().Any(x => null == x.SolidHeightfiled))
{
Logger.Error("Tick 'Keep Itermediate Results' option");
break;
}
var geom = _sample.GetInputGeom(); var geom = _sample.GetInputGeom();
var settings = _sample.GetSettings(); var settings = _sample.GetSettings();
_tool.Build(geom, settings, _sample.GetRecastResults(), _cfg); _tool.Build(geom, settings, _sample.GetRecastResults(), _cfg);
} } while (false);
} }
ImGui.NewLine(); ImGui.NewLine();

View File

@ -166,6 +166,10 @@ public class RcSettingsView : IRcView
ImGui.SliderFloat("Max Sample Error", ref settings.detailSampleMaxError, 0f, 16f, "%.1f"); ImGui.SliderFloat("Max Sample Error", ref settings.detailSampleMaxError, 0f, 16f, "%.1f");
ImGui.NewLine(); ImGui.NewLine();
ImGui.Checkbox("Keep Itermediate Results", ref settings.keepInterResults);
ImGui.Checkbox("Build All Tiles", ref settings.buildAll);
ImGui.NewLine();
ImGui.Text("Tiling"); ImGui.Text("Tiling");
ImGui.Separator(); ImGui.Separator();
ImGui.Checkbox("Enable", ref settings.tiled); ImGui.Checkbox("Enable", ref settings.tiled);
@ -228,6 +232,12 @@ public class RcSettingsView : IRcView
DrawMode.Values.ForEach(dm => { ImGui.RadioButton(dm.Text, ref drawMode, dm.Idx); }); DrawMode.Values.ForEach(dm => { ImGui.RadioButton(dm.Text, ref drawMode, dm.Idx); });
ImGui.NewLine(); ImGui.NewLine();
ImGui.Separator();
ImGui.Text("Tick 'Keep Itermediate Results'");
ImGui.Text("rebuild some tiles to see");
ImGui.Text("more debug mode options.");
ImGui.NewLine();
ImGui.End(); ImGui.End();
} }
@ -262,4 +272,4 @@ public class RcSettingsView : IRcView
{ {
this.maxPolys = maxPolys; this.maxPolys = maxPolys;
} }
} }

View File

@ -10,8 +10,8 @@ namespace DotRecast.Recast.Toolset.Builder
float cellHeight, float agentHeight, float agentRadius, float agentMaxClimb, float cellHeight, float agentHeight, float agentRadius, float agentMaxClimb,
RcBuilderResult rcResult) RcBuilderResult rcResult)
{ {
RcPolyMesh pmesh = rcResult.GetMesh(); RcPolyMesh pmesh = rcResult.Mesh;
RcPolyMeshDetail dmesh = rcResult.GetMeshDetail(); RcPolyMeshDetail dmesh = rcResult.MeshDetail;
DtNavMeshCreateParams option = new DtNavMeshCreateParams(); DtNavMeshCreateParams option = new DtNavMeshCreateParams();
for (int i = 0; i < pmesh.npolys; ++i) for (int i = 0; i < pmesh.npolys; ++i)
{ {

View File

@ -34,7 +34,8 @@ namespace DotRecast.Recast.Toolset.Builder
settings.edgeMaxLen, settings.edgeMaxError, settings.edgeMaxLen, settings.edgeMaxError,
settings.vertsPerPoly, settings.vertsPerPoly,
settings.detailSampleDist, settings.detailSampleMaxError, settings.detailSampleDist, settings.detailSampleMaxError,
settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans); settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans,
settings.keepInterResults);
} }
public NavMeshBuildResult Build(DemoInputGeomProvider geom, public NavMeshBuildResult Build(DemoInputGeomProvider geom,
@ -45,7 +46,8 @@ namespace DotRecast.Recast.Toolset.Builder
float edgeMaxLen, float edgeMaxError, float edgeMaxLen, float edgeMaxError,
int vertsPerPoly, int vertsPerPoly,
float detailSampleDist, float detailSampleMaxError, float detailSampleDist, float detailSampleMaxError,
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans) bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
bool keepInterResults)
{ {
RcConfig cfg = new RcConfig( RcConfig cfg = new RcConfig(
partitionType, partitionType,
@ -58,7 +60,7 @@ namespace DotRecast.Recast.Toolset.Builder
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans, filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true); SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
RcBuilderResult rcResult = BuildRecastResult(geom, cfg); RcBuilderResult rcResult = BuildRecastResult(geom, cfg, keepInterResults);
var meshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, rcResult); var meshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, rcResult);
if (null == meshData) if (null == meshData)
{ {
@ -74,11 +76,26 @@ namespace DotRecast.Recast.Toolset.Builder
return new DtNavMesh(meshData, vertsPerPoly, 0); return new DtNavMesh(meshData, vertsPerPoly, 0);
} }
private RcBuilderResult BuildRecastResult(DemoInputGeomProvider geom, RcConfig cfg) private RcBuilderResult BuildRecastResult(DemoInputGeomProvider geom, RcConfig cfg, bool keepInterResults)
{ {
RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax()); RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax());
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
return rcBuilder.Build(geom, bcfg); var result = rcBuilder.Build(geom, bcfg);
if (!keepInterResults)
{
return new RcBuilderResult(
result.TileX,
result.TileZ,
null,
null,
null,
result.Mesh,
result.MeshDetail,
result.Context
);
}
return result;
} }
public DtMeshData BuildMeshData(DemoInputGeomProvider geom, public DtMeshData BuildMeshData(DemoInputGeomProvider geom,

View File

@ -41,7 +41,8 @@ namespace DotRecast.Recast.Toolset.Builder
settings.minRegionSize, settings.mergedRegionSize, settings.minRegionSize, settings.mergedRegionSize,
settings.edgeMaxLen, settings.edgeMaxError, settings.edgeMaxLen, settings.edgeMaxError,
settings.vertsPerPoly, settings.detailSampleDist, settings.detailSampleMaxError, settings.vertsPerPoly, settings.detailSampleDist, settings.detailSampleMaxError,
settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans); settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans,
settings.keepInterResults, settings.buildAll);
} }
public NavMeshBuildResult Build(IInputGeomProvider geom, public NavMeshBuildResult Build(IInputGeomProvider geom,
@ -53,7 +54,8 @@ namespace DotRecast.Recast.Toolset.Builder
float edgeMaxLen, float edgeMaxError, float edgeMaxLen, float edgeMaxError,
int vertsPerPoly, int vertsPerPoly,
float detailSampleDist, float detailSampleMaxError, float detailSampleDist, float detailSampleMaxError,
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans) bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
bool keepInterResults, bool buildAll)
{ {
List<RcBuilderResult> results = BuildRecastResult( List<RcBuilderResult> results = BuildRecastResult(
geom, geom,
@ -65,7 +67,8 @@ namespace DotRecast.Recast.Toolset.Builder
edgeMaxLen, edgeMaxError, edgeMaxLen, edgeMaxError,
vertsPerPoly, vertsPerPoly,
detailSampleDist, detailSampleMaxError, detailSampleDist, detailSampleMaxError,
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
keepInterResults, buildAll
); );
var tileMeshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, results); var tileMeshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, results);
@ -82,7 +85,8 @@ namespace DotRecast.Recast.Toolset.Builder
float edgeMaxLen, float edgeMaxError, float edgeMaxLen, float edgeMaxError,
int vertsPerPoly, int vertsPerPoly,
float detailSampleDist, float detailSampleMaxError, float detailSampleDist, float detailSampleMaxError,
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans) bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
bool keepInterResults, bool buildAll)
{ {
RcConfig cfg = new RcConfig(true, tileSize, tileSize, RcConfig cfg = new RcConfig(true, tileSize, tileSize,
@ -97,7 +101,7 @@ namespace DotRecast.Recast.Toolset.Builder
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans, filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true); SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
return rcBuilder.BuildTiles(geom, cfg, Task.Factory); return rcBuilder.BuildTiles(geom, cfg, keepInterResults, buildAll, Environment.ProcessorCount + 1, Task.Factory);
} }
public DtNavMesh BuildNavMesh(IInputGeomProvider geom, List<DtMeshData> meshData, float cellSize, int tileSize, int vertsPerPoly) public DtNavMesh BuildNavMesh(IInputGeomProvider geom, List<DtMeshData> meshData, float cellSize, int tileSize, int vertsPerPoly)
@ -123,10 +127,9 @@ namespace DotRecast.Recast.Toolset.Builder
List<DtMeshData> meshData = new List<DtMeshData>(); List<DtMeshData> meshData = new List<DtMeshData>();
foreach (RcBuilderResult result in results) foreach (RcBuilderResult result in results)
{ {
int x = result.tileX; int x = result.TileX;
int z = result.tileZ; int z = result.TileZ;
DtNavMeshCreateParams option = DemoNavMeshBuilder DtNavMeshCreateParams option = DemoNavMeshBuilder.GetNavMeshCreateParams(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, result);
.GetNavMeshCreateParams(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, result);
option.tileX = x; option.tileX = x;
option.tileZ = z; option.tileZ = z;

View File

@ -31,5 +31,8 @@
public bool tiled = false; public bool tiled = false;
public int tileSize = 32; public int tileSize = 32;
public bool keepInterResults = false;
public bool buildAll = true;
} }
} }

View File

@ -18,12 +18,12 @@ namespace DotRecast.Recast.Toolset.Tools
private DtDynamicNavMesh dynaMesh; private DtDynamicNavMesh dynaMesh;
private readonly Dictionary<long, RcGizmo> colliderGizmos; private readonly Dictionary<long, RcGizmo> colliderGizmos;
private readonly Random random; private readonly IRcRand random;
private readonly DemoInputGeomProvider bridgeGeom; private readonly DemoInputGeomProvider bridgeGeom;
private readonly DemoInputGeomProvider houseGeom; private readonly DemoInputGeomProvider houseGeom;
private readonly DemoInputGeomProvider convexGeom; private readonly DemoInputGeomProvider convexGeom;
public RcDynamicUpdateTool(Random rand, DemoInputGeomProvider bridgeGeom, DemoInputGeomProvider houseGeom, DemoInputGeomProvider convexGeom) public RcDynamicUpdateTool(IRcRand rand, DemoInputGeomProvider bridgeGeom, DemoInputGeomProvider houseGeom, DemoInputGeomProvider convexGeom)
{ {
this.colliderGizmos = new Dictionary<long, RcGizmo>(); this.colliderGizmos = new Dictionary<long, RcGizmo>();
this.random = rand; this.random = rand;
@ -339,20 +339,14 @@ namespace DotRecast.Recast.Toolset.Tools
return resultvector; return resultvector;
} }
public bool UpdateDynaMesh(TaskFactory executor) public bool Update(TaskFactory executor)
{ {
if (dynaMesh == null) if (dynaMesh == null)
{ {
return false; return false;
} }
bool updated = dynaMesh.Update(executor).Result; return dynaMesh.Update(executor);
if (updated)
{
return false;
}
return true;
} }
public bool Raycast(RcVec3f spos, RcVec3f epos, out float hitPos, out RcVec3f raycastHitPos) public bool Raycast(RcVec3f spos, RcVec3f epos, out float hitPos, out RcVec3f raycastHitPos)

View File

@ -94,7 +94,7 @@ namespace DotRecast.Recast.Toolset.Tools
var beginTick = RcFrequency.Ticks; var beginTick = RcFrequency.Ticks;
var rb = new RcBuilder(); var rb = new RcBuilder();
var result = rb.BuildTile(geom, cfg, bmin, bmax, tx, ty, new RcAtomicInteger(0), 1); var result = rb.BuildTile(geom, cfg, bmin, bmax, tx, ty, new RcAtomicInteger(0), 1, settings.keepInterResults);
var tb = new TileNavMeshBuilder(); var tb = new TileNavMeshBuilder();
var meshData = tb.BuildMeshData(geom, settings.cellSize, settings.cellHeight, settings.agentHeight, settings.agentRadius, settings.agentMaxClimb, RcImmutableArray.Create(result) var meshData = tb.BuildMeshData(geom, settings.cellSize, settings.cellHeight, settings.agentHeight, settings.agentRadius, settings.agentMaxClimb, RcImmutableArray.Create(result)

View File

@ -19,7 +19,9 @@ freely, subject to the following restrictions:
*/ */
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotRecast.Core; using DotRecast.Core;
@ -45,129 +47,123 @@ namespace DotRecast.Recast
_progressListener = progressListener; _progressListener = progressListener;
} }
public List<RcBuilderResult> BuildTiles(IInputGeomProvider geom, RcConfig cfg, TaskFactory taskFactory) public List<RcBuilderResult> BuildTiles(IInputGeomProvider geom, RcConfig cfg, bool keepInterResults, bool buildAll,
int threads = 0, TaskFactory taskFactory = null, CancellationToken cancellation = default)
{ {
RcVec3f bmin = geom.GetMeshBoundsMin(); RcVec3f bmin = geom.GetMeshBoundsMin();
RcVec3f bmax = geom.GetMeshBoundsMax(); RcVec3f bmax = geom.GetMeshBoundsMax();
CalcTileCount(bmin, bmax, cfg.Cs, cfg.TileSizeX, cfg.TileSizeZ, out var tw, out var th); CalcTileCount(bmin, bmax, cfg.Cs, cfg.TileSizeX, cfg.TileSizeZ, out var tw, out var th);
List<RcBuilderResult> results = new List<RcBuilderResult>();
if (null != taskFactory) if (1 < threads)
{ {
BuildMultiThreadAsync(geom, cfg, bmin, bmax, tw, th, results, taskFactory, default); return BuildMultiThread(geom, cfg, bmin, bmax, tw, th, threads, taskFactory ?? Task.Factory, cancellation, keepInterResults, buildAll);
} }
else
return BuildSingleThread(geom, cfg, bmin, bmax, tw, th, keepInterResults, buildAll);
}
private List<RcBuilderResult> BuildSingleThread(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tw, int th,
bool keepInterResults, bool buildAll)
{
var results = new List<RcBuilderResult>(th * tw);
RcAtomicInteger counter = new RcAtomicInteger(0);
for (int y = 0; y < th; ++y)
{ {
BuildSingleThreadAsync(geom, cfg, bmin, bmax, tw, th, results); for (int x = 0; x < tw; ++x)
{
var result = BuildTile(geom, cfg, bmin, bmax, x, y, counter, tw * th, keepInterResults);
results.Add(result);
}
} }
return results; return results;
} }
private List<RcBuilderResult> BuildMultiThread(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tw, int th,
public Task BuildTilesAsync(IInputGeomProvider geom, RcConfig cfg, int threads, List<RcBuilderResult> results, TaskFactory taskFactory, CancellationToken cancellationToken) int threads, TaskFactory taskFactory, CancellationToken cancellation,
bool keepInterResults, bool buildAll)
{ {
RcVec3f bmin = geom.GetMeshBoundsMin(); var results = new ConcurrentQueue<RcBuilderResult>();
RcVec3f bmax = geom.GetMeshBoundsMax(); RcAtomicInteger progress = new RcAtomicInteger(0);
CalcTileCount(bmin, bmax, cfg.Cs, cfg.TileSizeX, cfg.TileSizeZ, out var tw, out var th);
Task task;
if (1 < threads)
{
task = BuildMultiThreadAsync(geom, cfg, bmin, bmax, tw, th, results, taskFactory, cancellationToken);
}
else
{
task = BuildSingleThreadAsync(geom, cfg, bmin, bmax, tw, th, results);
}
return task;
}
private Task BuildSingleThreadAsync(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax,
int tw, int th, List<RcBuilderResult> results)
{
RcAtomicInteger counter = new RcAtomicInteger(0);
for (int y = 0; y < th; ++y)
{
for (int x = 0; x < tw; ++x)
{
results.Add(BuildTile(geom, cfg, bmin, bmax, x, y, counter, tw * th));
}
}
return Task.CompletedTask;
}
private Task BuildMultiThreadAsync(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax,
int tw, int th, List<RcBuilderResult> results, TaskFactory taskFactory, CancellationToken cancellationToken)
{
RcAtomicInteger counter = new RcAtomicInteger(0);
CountdownEvent latch = new CountdownEvent(tw * th);
List<Task> tasks = new List<Task>();
List<Task> limits = new List<Task>(threads);
for (int x = 0; x < tw; ++x) for (int x = 0; x < tw; ++x)
{ {
for (int y = 0; y < th; ++y) for (int y = 0; y < th; ++y)
{ {
int tx = x; int tx = x;
int ty = y; int ty = y;
var task = taskFactory.StartNew(() => var task = taskFactory.StartNew(state =>
{ {
if (cancellationToken.IsCancellationRequested) if (cancellation.IsCancellationRequested)
return; return;
try try
{ {
RcBuilderResult tile = BuildTile(geom, cfg, bmin, bmax, tx, ty, counter, tw * th); RcBuilderResult result = BuildTile(geom, cfg, bmin, bmax, tx, ty, progress, tw * th, keepInterResults);
lock (results) results.Enqueue(result);
{
results.Add(tile);
}
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e); Console.WriteLine(e);
} }
}, null, cancellation);
limits.Add(task);
latch.Signal(); if (threads <= limits.Count)
}, cancellationToken); {
Task.WaitAll(limits.ToArray());
tasks.Add(task); limits.Clear();
}
} }
} }
try if (0 < limits.Count)
{
latch.Wait();
}
catch (ThreadInterruptedException)
{ {
Task.WaitAll(limits.ToArray());
limits.Clear();
} }
return Task.WhenAll(tasks.ToArray()); var list = results.ToList();
return list;
} }
public RcBuilderResult BuildTile(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tx, public RcBuilderResult BuildTile(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tx, int ty, RcAtomicInteger progress, int total, bool keepInterResults)
int ty, RcAtomicInteger counter, int total)
{ {
RcBuilderResult result = Build(geom, new RcBuilderConfig(cfg, bmin, bmax, tx, ty)); var bcfg = new RcBuilderConfig(cfg, bmin, bmax, tx, ty);
RcBuilderResult result = Build(geom, bcfg);
if (_progressListener != null) if (_progressListener != null)
{ {
_progressListener.OnProgress(counter.IncrementAndGet(), total); _progressListener.OnProgress(progress.IncrementAndGet(), total);
}
if (!keepInterResults)
{
return new RcBuilderResult(
result.TileX,
result.TileZ,
null,
null,
null,
result.Mesh,
result.MeshDetail,
result.Context
);
} }
return result; return result;
} }
public RcBuilderResult Build(IInputGeomProvider geom, RcBuilderConfig builderCfg) public RcBuilderResult Build(IInputGeomProvider geom, RcBuilderConfig bcfg)
{ {
RcConfig cfg = builderCfg.cfg; RcConfig cfg = bcfg.cfg;
RcContext ctx = new RcContext(); RcContext ctx = new RcContext();
// //
// Step 1. Rasterize input polygon soup. // Step 1. Rasterize input polygon soup.
// //
RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, builderCfg); RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, bcfg);
return Build(ctx, builderCfg.tileX, builderCfg.tileZ, geom, cfg, solid); return Build(ctx, bcfg.tileX, bcfg.tileZ, geom, cfg, solid);
} }
public RcBuilderResult Build(RcContext ctx, int tileX, int tileZ, IInputGeomProvider geom, RcConfig cfg, RcHeightfield solid) public RcBuilderResult Build(RcContext ctx, int tileX, int tileZ, IInputGeomProvider geom, RcConfig cfg, RcHeightfield solid)
@ -205,7 +201,7 @@ namespace DotRecast.Recast
{ {
// Prepare for region partitioning, by calculating distance field along the walkable surface. // Prepare for region partitioning, by calculating distance field along the walkable surface.
RcRegions.BuildDistanceField(ctx, chf); RcRegions.BuildDistanceField(ctx, chf);
// Partition the walkable surface into simple regions without holes. // Partition the walkable surface into simple regions without holes.
RcRegions.BuildRegions(ctx, chf, cfg.MinRegionArea, cfg.MergeRegionArea); RcRegions.BuildRegions(ctx, chf, cfg.MinRegionArea, cfg.MergeRegionArea);
} }
@ -298,7 +294,7 @@ namespace DotRecast.Recast
RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, builderCfg); RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, builderCfg);
FilterHeightfield(ctx, solid, builderCfg.cfg); FilterHeightfield(ctx, solid, builderCfg.cfg);
RcCompactHeightfield chf = BuildCompactHeightfield(ctx, geom, builderCfg.cfg, solid); RcCompactHeightfield chf = BuildCompactHeightfield(ctx, geom, builderCfg.cfg, solid);
RcLayers.BuildHeightfieldLayers(ctx, chf, builderCfg.cfg.BorderSize, builderCfg.cfg.WalkableHeight, out var lset); RcLayers.BuildHeightfieldLayers(ctx, chf, builderCfg.cfg.BorderSize, builderCfg.cfg.WalkableHeight, out var lset);
return lset; return lset;
} }

View File

@ -4,56 +4,26 @@ namespace DotRecast.Recast
{ {
public class RcBuilderResult public class RcBuilderResult
{ {
public readonly int tileX; public readonly int TileX;
public readonly int tileZ; public readonly int TileZ;
private readonly RcCompactHeightfield chf;
private readonly RcContourSet cs;
private readonly RcPolyMesh pmesh;
private readonly RcPolyMeshDetail dmesh;
private readonly RcHeightfield solid;
private readonly RcContext _context;
public RcBuilderResult(int tileX, int tileZ, RcHeightfield solid, RcCompactHeightfield chf, RcContourSet cs, RcPolyMesh pmesh, RcPolyMeshDetail dmesh, RcContext ctx) public readonly RcHeightfield SolidHeightfiled;
{ public readonly RcCompactHeightfield CompactHeightfield;
this.tileX = tileX; public readonly RcContourSet ContourSet;
this.tileZ = tileZ; public readonly RcPolyMesh Mesh;
this.solid = solid; public readonly RcPolyMeshDetail MeshDetail;
this.chf = chf; public readonly RcContext Context;
this.cs = cs;
this.pmesh = pmesh;
this.dmesh = dmesh;
_context = ctx;
}
public RcPolyMesh GetMesh() public RcBuilderResult(int tileX, int tileZ, RcHeightfield solidHeightfiled, RcCompactHeightfield compactHeightfield, RcContourSet contourSet, RcPolyMesh mesh, RcPolyMeshDetail meshDetail, RcContext ctx)
{ {
return pmesh; TileX = tileX;
} TileZ = tileZ;
SolidHeightfiled = solidHeightfiled;
public RcPolyMeshDetail GetMeshDetail() CompactHeightfield = compactHeightfield;
{ ContourSet = contourSet;
return dmesh; Mesh = mesh;
} MeshDetail = meshDetail;
Context = ctx;
public RcCompactHeightfield GetCompactHeightfield()
{
return chf;
}
public RcContourSet GetContourSet()
{
return cs;
}
public RcHeightfield GetSolidHeightfield()
{
return solid;
}
public RcContext GetTelemetry()
{
return _context;
} }
} }
} }

View File

@ -22,29 +22,21 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
/** Represents a heightfield layer within a layer set. */ /// A dynamic heightfield representing obstructed space.
/// @ingroup recast
public class RcHeightfield public class RcHeightfield
{ {
/** The width of the heightfield. (Along the x-axis in cell units.) */ public readonly int width; //< The width of the heightfield. (Along the x-axis in cell units.)
public readonly int width; public readonly int height; //< The height of the heightfield. (Along the z-axis in cell units.)
public readonly RcVec3f bmin; //< The minimum bounds in world space. [(x, y, z)]
public RcVec3f bmax; //< The maximum bounds in world space. [(x, y, z)]
public readonly float cs; //< The size of each cell. (On the xz-plane.)
public readonly float ch; //< The height of each cell. (The minimum increment along the y-axis.)
public readonly RcSpan[] spans; //< Heightfield of spans (width*height).
/** The height of the heightfield. (Along the z-axis in cell units.) */ // memory pool for rcSpan instances.
public readonly int height; public RcSpanPool pools; //< Linked list of span pools.
public RcSpan freelist; //< The next free span.
/** The minimum bounds in world space. [(x, y, z)] */
public readonly RcVec3f bmin;
/** The maximum bounds in world space. [(x, y, z)] */
public RcVec3f bmax;
/** The size of each cell. (On the xz-plane.) */
public readonly float cs;
/** The height of each cell. (The minimum increment along the y-axis.) */
public readonly float ch;
/** Heightfield of spans (width*height). */
public readonly RcSpan[] spans;
/** Border size in cell units */ /** Border size in cell units */
public readonly int borderSize; public readonly int borderSize;

View File

@ -30,9 +30,9 @@ namespace DotRecast.Recast
hitTime = 0.0f; hitTime = 0.0f;
foreach (RcBuilderResult result in results) foreach (RcBuilderResult result in results)
{ {
if (result.GetMeshDetail() != null) if (result.MeshDetail != null)
{ {
if (Raycast(result.GetMesh(), result.GetMeshDetail(), src, dst, out hitTime)) if (Raycast(result.Mesh, result.MeshDetail, src, dst, out hitTime))
{ {
return true; return true;
} }

View File

@ -41,6 +41,63 @@ namespace DotRecast.Recast
aMin.Y <= bMax.Y && aMax.Y >= bMin.Y && aMin.Y <= bMax.Y && aMax.Y >= bMin.Y &&
aMin.Z <= bMax.Z && aMax.Z >= bMin.Z; aMin.Z <= bMax.Z && aMax.Z >= bMin.Z;
} }
/// Allocates a new span in the heightfield.
/// Use a memory pool and free list to minimize actual allocations.
///
/// @param[in] heightfield The heightfield
/// @returns A pointer to the allocated or re-used span memory.
private static RcSpan AllocSpan(RcHeightfield heightfield)
{
// If necessary, allocate new page and update the freelist.
if (heightfield.freelist == null || heightfield.freelist.next == null)
{
// Create new page.
// Allocate memory for the new pool.
RcSpanPool spanPool = new RcSpanPool();
if (spanPool == null)
{
return null;
}
// Add the pool into the list of pools.
spanPool.next = heightfield.pools;
heightfield.pools = spanPool;
// Add new spans to the free list.
RcSpan freeList = heightfield.freelist;
int head = 0;
int it = RC_SPANS_PER_POOL;
do
{
--it;
spanPool.items[it].next = freeList;
freeList = spanPool.items[it];
} while (it != head);
heightfield.freelist = spanPool.items[it];
}
// Pop item from the front of the free list.
RcSpan newSpan = heightfield.freelist;
heightfield.freelist = heightfield.freelist.next;
return newSpan;
}
/// Releases the memory used by the span back to the heightfield, so it can be re-used for new spans.
/// @param[in] heightfield The heightfield.
/// @param[in] span A pointer to the span to free
private static void FreeSpan(RcHeightfield heightfield, RcSpan span)
{
if (span == null)
{
return;
}
// Add the span to the front of the free list.
span.next = heightfield.freelist;
heightfield.freelist = span;
}
/// Adds a span to the heightfield. If the new span overlaps existing spans, /// Adds a span to the heightfield. If the new span overlaps existing spans,
@ -156,7 +213,7 @@ namespace DotRecast.Recast
float axisOffset, int axis) float axisOffset, int axis)
{ {
// How far positive or negative away from the separating axis is each vertex. // How far positive or negative away from the separating axis is each vertex.
float[] inVertAxisDelta = new float[12]; Span<float> inVertAxisDelta = stackalloc float[12];
for (int inVert = 0; inVert < inVertsCount; ++inVert) for (int inVert = 0; inVert < inVertsCount; ++inVert)
{ {
inVertAxisDelta[inVert] = axisOffset - inVerts[inVertsOffset + inVert * 3 + axis]; inVertAxisDelta[inVert] = axisOffset - inVerts[inVertsOffset + inVert * 3 + axis];

View File

@ -0,0 +1,38 @@
/*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com
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.
*/
namespace DotRecast.Recast
{
/// A memory pool used for quick allocation of spans within a heightfield.
/// @see rcHeightfield
public class RcSpanPool
{
public RcSpanPool next; //< The next span pool.
public readonly RcSpan[] items; //< Array of spans in the pool.
public RcSpanPool()
{
items = new RcSpan[RcConstants.RC_SPANS_PER_POOL];
for (int i = 0; i < items.Length; ++i)
{
items[i] = new RcSpan();
}
}
}
}

View File

@ -9,13 +9,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="4.0.1" /> <PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
<PackageReference Include="NUnit.Analyzers" Version="4.0.1"> <PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.1"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -9,13 +9,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="4.0.1" /> <PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
<PackageReference Include="NUnit.Analyzers" Version="4.0.1"> <PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.1"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -73,13 +73,13 @@ public class RecastTestMeshBuilder
RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax()); RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax());
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg); RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg);
RcPolyMesh m_pmesh = rcResult.GetMesh(); RcPolyMesh m_pmesh = rcResult.Mesh;
for (int i = 0; i < m_pmesh.npolys; ++i) for (int i = 0; i < m_pmesh.npolys; ++i)
{ {
m_pmesh.flags[i] = 1; m_pmesh.flags[i] = 1;
} }
RcPolyMeshDetail m_dmesh = rcResult.GetMeshDetail(); RcPolyMeshDetail m_dmesh = rcResult.MeshDetail;
DtNavMeshCreateParams option = new DtNavMeshCreateParams(); DtNavMeshCreateParams option = new DtNavMeshCreateParams();
option.verts = m_pmesh.verts; option.verts = m_pmesh.verts;
option.vertCount = m_pmesh.nverts; option.vertCount = m_pmesh.nverts;

View File

@ -9,17 +9,17 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="4.0.1" /> <PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
<PackageReference Include="NUnit.Analyzers" Version="4.0.1"> <PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.1"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.6" /> <PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -10,7 +10,6 @@ using NUnit.Framework;
namespace DotRecast.Detour.Dynamic.Test; namespace DotRecast.Detour.Dynamic.Test;
public class DynamicNavMeshTest public class DynamicNavMeshTest
{ {
private static readonly RcVec3f START_POS = new RcVec3f(70.87453f, 0.0010070801f, 86.69021f); private static readonly RcVec3f START_POS = new RcVec3f(70.87453f, 0.0010070801f, 86.69021f);
@ -32,9 +31,7 @@ public class DynamicNavMeshTest
// create dynamic navmesh // create dynamic navmesh
DtDynamicNavMesh mesh = new DtDynamicNavMesh(f); DtDynamicNavMesh mesh = new DtDynamicNavMesh(f);
// build navmesh asynchronously using multiple threads // build navmesh asynchronously using multiple threads
Task<bool> future = mesh.Build(Task.Factory); mesh.Build(Task.Factory);
// wait for build to complete
bool _ = future.Result;
// create new query // create new query
DtNavMeshQuery query = new DtNavMeshQuery(mesh.NavMesh()); DtNavMeshQuery query = new DtNavMeshQuery(mesh.NavMesh());
@ -54,9 +51,8 @@ public class DynamicNavMeshTest
long colliderId = mesh.AddCollider(colldier); long colliderId = mesh.AddCollider(colldier);
// update navmesh asynchronously // update navmesh asynchronously
future = mesh.Update(Task.Factory); mesh.Update(Task.Factory);
// wait for update to complete
_ = future.Result;
// create new query // create new query
query = new DtNavMeshQuery(mesh.NavMesh()); query = new DtNavMeshQuery(mesh.NavMesh());
@ -70,9 +66,7 @@ public class DynamicNavMeshTest
// remove obstacle // remove obstacle
mesh.RemoveCollider(colliderId); mesh.RemoveCollider(colliderId);
// update navmesh asynchronously // update navmesh asynchronously
future = mesh.Update(Task.Factory); mesh.Update(Task.Factory);
// wait for update to complete
_ = future.Result;
// create new query // create new query
query = new DtNavMeshQuery(mesh.NavMesh()); query = new DtNavMeshQuery(mesh.NavMesh());

View File

@ -31,7 +31,6 @@ using NUnit.Framework;
namespace DotRecast.Detour.Dynamic.Test; namespace DotRecast.Detour.Dynamic.Test;
public class VoxelQueryTest public class VoxelQueryTest
{ {
private const int TILE_WIDTH = 100; private const int TILE_WIDTH = 100;
@ -101,12 +100,12 @@ public class VoxelQueryTest
// load voxels from file // load voxels from file
DtVoxelFileReader reader = new DtVoxelFileReader(DtVoxelTileLZ4ForTestCompressor.Shared); DtVoxelFileReader reader = new DtVoxelFileReader(DtVoxelTileLZ4ForTestCompressor.Shared);
DtVoxelFile f = reader.Read(br); DtVoxelFile f = reader.Read(br);
// create dynamic navmesh // create dynamic navmesh
var mesh = new DtDynamicNavMesh(f); var mesh = new DtDynamicNavMesh(f);
// build navmesh asynchronously using multiple threads // build navmesh asynchronously using multiple threads
Task<bool> future = mesh.Build(Task.Factory); mesh.Build(Task.Factory);
// wait for build to complete
var _ = future.Result;
return mesh; return mesh;
} }
} }

View File

@ -9,13 +9,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="4.0.1" /> <PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
<PackageReference Include="NUnit.Analyzers" Version="4.0.1"> <PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.1"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -9,13 +9,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="4.0.1" /> <PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
<PackageReference Include="NUnit.Analyzers" Version="4.0.1"> <PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.1"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -73,13 +73,13 @@ public class RecastTestMeshBuilder
RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax()); RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax());
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg); RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg);
RcPolyMesh m_pmesh = rcResult.GetMesh(); RcPolyMesh m_pmesh = rcResult.Mesh;
for (int i = 0; i < m_pmesh.npolys; ++i) for (int i = 0; i < m_pmesh.npolys; ++i)
{ {
m_pmesh.flags[i] = 1; m_pmesh.flags[i] = 1;
} }
RcPolyMeshDetail m_dmesh = rcResult.GetMeshDetail(); RcPolyMeshDetail m_dmesh = rcResult.MeshDetail;
DtNavMeshCreateParams option = new DtNavMeshCreateParams(); DtNavMeshCreateParams option = new DtNavMeshCreateParams();
option.verts = m_pmesh.verts; option.verts = m_pmesh.verts;
option.vertCount = m_pmesh.nverts; option.vertCount = m_pmesh.nverts;

View File

@ -28,7 +28,7 @@ public class TestDetourBuilder : DetourBuilder
{ {
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
RcBuilderResult rcResult = rcBuilder.Build(geom, rcConfig); RcBuilderResult rcResult = rcBuilder.Build(geom, rcConfig);
RcPolyMesh pmesh = rcResult.GetMesh(); RcPolyMesh pmesh = rcResult.Mesh;
if (applyRecastDemoFlags) if (applyRecastDemoFlags)
{ {
@ -58,7 +58,7 @@ public class TestDetourBuilder : DetourBuilder
} }
} }
RcPolyMeshDetail dmesh = rcResult.GetMeshDetail(); RcPolyMeshDetail dmesh = rcResult.MeshDetail;
DtNavMeshCreateParams option = GetNavMeshCreateParams(rcConfig.cfg, pmesh, dmesh, agentHeight, agentRadius, DtNavMeshCreateParams option = GetNavMeshCreateParams(rcConfig.cfg, pmesh, dmesh, agentHeight, agentRadius,
agentMaxClimb); agentMaxClimb);
return Build(option, x, y); return Build(option, x, y);

View File

@ -78,13 +78,13 @@ public class TestTiledNavMeshBuilder
true, true, true, true, true, true,
SampleAreaModifications.SAMPLE_AREAMOD_GROUND, true); SampleAreaModifications.SAMPLE_AREAMOD_GROUND, true);
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
List<RcBuilderResult> rcResult = rcBuilder.BuildTiles(geom, cfg, null); List<RcBuilderResult> rcResult = rcBuilder.BuildTiles(geom, cfg, false, true);
// Add tiles to nav mesh // Add tiles to nav mesh
foreach (RcBuilderResult result in rcResult) foreach (RcBuilderResult result in rcResult)
{ {
RcPolyMesh pmesh = result.GetMesh(); RcPolyMesh pmesh = result.Mesh;
if (pmesh.npolys == 0) if (pmesh.npolys == 0)
{ {
continue; continue;
@ -103,7 +103,7 @@ public class TestTiledNavMeshBuilder
option.polyFlags = pmesh.flags; option.polyFlags = pmesh.flags;
option.polyCount = pmesh.npolys; option.polyCount = pmesh.npolys;
option.nvp = pmesh.nvp; option.nvp = pmesh.nvp;
RcPolyMeshDetail dmesh = result.GetMeshDetail(); RcPolyMeshDetail dmesh = result.MeshDetail;
option.detailMeshes = dmesh.meshes; option.detailMeshes = dmesh.meshes;
option.detailVerts = dmesh.verts; option.detailVerts = dmesh.verts;
option.detailVertsCount = dmesh.nverts; option.detailVertsCount = dmesh.nverts;
@ -116,8 +116,8 @@ public class TestTiledNavMeshBuilder
option.bmax = pmesh.bmax; option.bmax = pmesh.bmax;
option.cs = cellSize; option.cs = cellSize;
option.ch = cellHeight; option.ch = cellHeight;
option.tileX = result.tileX; option.tileX = result.TileX;
option.tileZ = result.tileZ; option.tileZ = result.TileZ;
option.buildBvTree = true; option.buildBvTree = true;
navMesh.AddTile(DtNavMeshBuilder.CreateNavMeshData(option), 0, 0); navMesh.AddTile(DtNavMeshBuilder.CreateNavMeshData(option), 0, 0);
} }

View File

@ -9,17 +9,17 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="4.0.1" /> <PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
<PackageReference Include="NUnit.Analyzers" Version="4.0.1"> <PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.1"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.6" /> <PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -9,13 +9,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="4.0.1" /> <PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
<PackageReference Include="NUnit.Analyzers" Version="4.0.1"> <PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.1"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -27,7 +27,6 @@ using NUnit.Framework;
namespace DotRecast.Recast.Test; namespace DotRecast.Recast.Test;
public class RecastTileMeshTest public class RecastTileMeshTest
{ {
private const float m_cellSize = 0.3f; private const float m_cellSize = 0.3f;
@ -71,28 +70,28 @@ public class RecastTileMeshTest
SampleAreaModifications.SAMPLE_AREAMOD_GROUND, true); SampleAreaModifications.SAMPLE_AREAMOD_GROUND, true);
RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 7, 8); RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 7, 8);
RcBuilderResult rcResult = builder.Build(geom, bcfg); RcBuilderResult rcResult = builder.Build(geom, bcfg);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(1)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(1));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(5)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(5));
bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 6, 9); bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 6, 9);
rcResult = builder.Build(geom, bcfg); rcResult = builder.Build(geom, bcfg);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(2)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(2));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(7)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(7));
bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 2, 9); bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 2, 9);
rcResult = builder.Build(geom, bcfg); rcResult = builder.Build(geom, bcfg);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(2)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(2));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(9)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(9));
bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 4, 3); bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 4, 3);
rcResult = builder.Build(geom, bcfg); rcResult = builder.Build(geom, bcfg);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(3)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(3));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(6)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(6));
bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 2, 8); bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 2, 8);
rcResult = builder.Build(geom, bcfg); rcResult = builder.Build(geom, bcfg);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(5)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(5));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(17)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(17));
bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 0, 8); bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), 0, 8);
rcResult = builder.Build(geom, bcfg); rcResult = builder.Build(geom, bcfg);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(6)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(6));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(15)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(15));
} }
[Test] [Test]
@ -138,28 +137,27 @@ public class RecastTileMeshTest
private void Build(IInputGeomProvider geom, RcBuilder builder, RcConfig cfg, int threads, bool validate) private void Build(IInputGeomProvider geom, RcBuilder builder, RcConfig cfg, int threads, bool validate)
{ {
CancellationTokenSource cts = new CancellationTokenSource(); CancellationTokenSource cts = new CancellationTokenSource();
List<RcBuilderResult> tiles = new(); List<RcBuilderResult> tiles = builder.BuildTiles(geom, cfg, false, true, threads, Task.Factory, cts.Token);
var task = builder.BuildTilesAsync(geom, cfg, threads, tiles, Task.Factory, cts.Token);
if (validate) if (validate)
{ {
RcBuilderResult rcResult = GetTile(tiles, 7, 8); RcBuilderResult rcResult = GetTile(tiles, 7, 8);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(1)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(1));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(5)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(5));
rcResult = GetTile(tiles, 6, 9); rcResult = GetTile(tiles, 6, 9);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(2)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(2));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(7)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(7));
rcResult = GetTile(tiles, 2, 9); rcResult = GetTile(tiles, 2, 9);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(2)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(2));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(9)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(9));
rcResult = GetTile(tiles, 4, 3); rcResult = GetTile(tiles, 4, 3);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(3)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(3));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(6)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(6));
rcResult = GetTile(tiles, 2, 8); rcResult = GetTile(tiles, 2, 8);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(5)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(5));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(17)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(17));
rcResult = GetTile(tiles, 0, 8); rcResult = GetTile(tiles, 0, 8);
Assert.That(rcResult.GetMesh().npolys, Is.EqualTo(6)); Assert.That(rcResult.Mesh.npolys, Is.EqualTo(6));
Assert.That(rcResult.GetMesh().nverts, Is.EqualTo(15)); Assert.That(rcResult.Mesh.nverts, Is.EqualTo(15));
} }
try try
@ -175,6 +173,6 @@ public class RecastTileMeshTest
private RcBuilderResult GetTile(List<RcBuilderResult> tiles, int x, int z) private RcBuilderResult GetTile(List<RcBuilderResult> tiles, int x, int z)
{ {
return tiles.FirstOrDefault(tile => tile.tileX == x && tile.tileZ == z); return tiles.FirstOrDefault(tile => tile.TileX == x && tile.TileZ == z);
} }
} }