Compare commits

..

25 Commits

Author SHA1 Message Date
wrenge 005ab583f7 Merge remote-tracking branch 'refs/remotes/upstream/pr/fix-small-object-heap-issue' into pr/fix-small-object-heap-issue
# Conflicts:
#	src/DotRecast.Detour/DtNavMeshQuery.cs
2024-04-27 09:01:27 +03:00
ikpil 94e66ed318 fix: SOH issue (#41)
fix: SOH issue step2 (#41)

https://github.com/ikpil/DotRecast/issues/41

fix: SOH issue step3 (#41)

- https://github.com/ikpil/DotRecast/issues/41

fix : SOH issue (#41)

- https://github.com/ikpil/DotRecast/issues/41#issuecomment-1908359895

fix: SOH issue (#41)

- https://github.com/ikpil/DotRecast/issues/41#issuecomment-1908365394

fix: SOH issue(#41)

https://github.com/ikpil/DotRecast/issues/41#issuecomment-1908367226

array benchmark

benchmark array test step2

test

ss
2024-04-26 00:34:34 +09:00
ikpil 97777511a7 Added the keepInterResults option to RcBuilder.Build()
- https://github.com/ikpil/DotRecast/issues/66
2024-04-25 01:18:04 +09:00
ikpil f22ec94038 support C# 10, 11 in DotRecast.Recast.Demo 2024-04-25 00:59:02 +09:00
ikpil 4a80473e2f fix workflow 2024-04-25 00:02:27 +09:00
ikpil bf6ee495d2 upgrade Silk.NET 2.20.0 -> 2.21.0 2024-04-24 23:40:11 +09:00
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
48 changed files with 483 additions and 391 deletions

View File

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

View File

@ -49,7 +49,7 @@ jobs:
uses: actions/checkout@v4
- name: Set up .NET 8.0
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.x

View File

@ -37,7 +37,7 @@ jobs:
fetch-depth: 0 # Get all history to allow automatic versioning using MinVer
- name: Setup .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}.x

View File

@ -33,7 +33,7 @@ jobs:
fetch-depth: 0 # Get all history to allow automatic versioning using MinVer
- name: Setup Dotnet
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.x'

View File

@ -15,7 +15,7 @@ jobs:
fetch-depth: 0 # Get all history to allow automatic versioning using MinVer
- name: Setup Dotnet
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.x'

View File

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

View File

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

View File

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

View File

@ -100,7 +100,7 @@ namespace DotRecast.Detour.Dynamic
Math.Min(DtDynamicNavMesh.MAX_VERTS_PER_POLY, config.vertsPerPoly),
config.detailSampleDistance, config.detailSampleMaxError,
true, true, true, default, true);
RcBuilderResult r = builder.Build(context, vt.tileX, vt.tileZ, null, rcConfig, heightfield);
RcBuilderResult r = builder.Build(context, vt.tileX, vt.tileZ, null, rcConfig, heightfield, false);
if (config.keepIntermediateResults)
{
recastResult = r;
@ -132,8 +132,8 @@ namespace DotRecast.Detour.Dynamic
private DtNavMeshCreateParams NavMeshCreateParams(int tilex, int tileZ, float cellSize, float cellHeight,
DtDynamicNavMeshConfig config, RcBuilderResult rcResult)
{
RcPolyMesh m_pmesh = rcResult.GetMesh();
RcPolyMeshDetail m_dmesh = rcResult.GetMeshDetail();
RcPolyMesh m_pmesh = rcResult.Mesh;
RcPolyMeshDetail m_dmesh = rcResult.MeshDetail;
DtNavMeshCreateParams option = new DtNavMeshCreateParams();
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)
{
f.tiles.Add(new DtVoxelTile(r.tileX, r.tileZ, r.GetSolidHeightfield()));
f.bounds[0] = Math.Min(f.bounds[0], r.GetSolidHeightfield().bmin.X);
f.bounds[1] = Math.Min(f.bounds[1], r.GetSolidHeightfield().bmin.Y);
f.bounds[2] = Math.Min(f.bounds[2], r.GetSolidHeightfield().bmin.Z);
f.bounds[3] = Math.Max(f.bounds[3], r.GetSolidHeightfield().bmax.X);
f.bounds[4] = Math.Max(f.bounds[4], r.GetSolidHeightfield().bmax.Y);
f.bounds[5] = Math.Max(f.bounds[5], r.GetSolidHeightfield().bmax.Z);
f.tiles.Add(new DtVoxelTile(r.TileX, r.TileZ, r.SolidHeightfiled));
f.bounds[0] = Math.Min(f.bounds[0], r.SolidHeightfiled.bmin.X);
f.bounds[1] = Math.Min(f.bounds[1], r.SolidHeightfiled.bmin.Y);
f.bounds[2] = Math.Min(f.bounds[2], r.SolidHeightfiled.bmin.Z);
f.bounds[3] = Math.Max(f.bounds[3], r.SolidHeightfiled.bmax.X);
f.bounds[4] = Math.Max(f.bounds[4], r.SolidHeightfiled.bmax.Y);
f.bounds[5] = Math.Max(f.bounds[5], r.SolidHeightfiled.bmax.Z);
}
return f;

View File

@ -21,7 +21,7 @@ namespace DotRecast.Detour.Extras.Jumplink
public JumpLinkBuilder(IList<RcBuilderResult> 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)
@ -43,7 +43,7 @@ namespace DotRecast.Detour.Extras.Jumplink
{
EdgeSampler es = edgeSamplerFactory.Get(acfg, type, edge);
groundSampler.Sample(acfg, result, es);
trajectorySampler.Sample(acfg, result.GetSolidHeightfield(), es);
trajectorySampler.Sample(acfg, result.SolidHeightfiled, es);
JumpSegment[] jumpSegments = jumpSegmentBuilder.Build(acfg, es);
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)
{
DtNavMeshCreateParams option = new DtNavMeshCreateParams();
option.verts = r.GetMesh().verts;
option.vertCount = r.GetMesh().nverts;
option.polys = r.GetMesh().polys;
option.polyAreas = r.GetMesh().areas;
option.polyFlags = r.GetMesh().flags;
option.polyCount = r.GetMesh().npolys;
option.nvp = r.GetMesh().nvp;
option.detailMeshes = r.GetMeshDetail().meshes;
option.detailVerts = r.GetMeshDetail().verts;
option.detailVertsCount = r.GetMeshDetail().nverts;
option.detailTris = r.GetMeshDetail().tris;
option.detailTriCount = r.GetMeshDetail().ntris;
option.verts = r.Mesh.verts;
option.vertCount = r.Mesh.nverts;
option.polys = r.Mesh.polys;
option.polyAreas = r.Mesh.areas;
option.polyFlags = r.Mesh.flags;
option.polyCount = r.Mesh.npolys;
option.nvp = r.Mesh.nvp;
option.detailMeshes = r.MeshDetail.meshes;
option.detailVerts = r.MeshDetail.verts;
option.detailVertsCount = r.MeshDetail.nverts;
option.detailTris = r.MeshDetail.tris;
option.detailTriCount = r.MeshDetail.ntris;
option.walkableRadius = agentRadius;
option.walkableHeight = agentHeight;
option.walkableClimb = agentClimb;
option.bmin = r.GetMesh().bmin;
option.bmax = r.GetMesh().bmax;
option.cs = r.GetMesh().cs;
option.ch = r.GetMesh().ch;
option.bmin = r.Mesh.bmin;
option.bmax = r.Mesh.bmax;
option.cs = r.Mesh.cs;
option.ch = r.Mesh.ch;
option.buildBvTree = true;
return new DtNavMeshQuery(new DtNavMesh(DtNavMeshBuilder.CreateNavMeshData(option), option.nvp, 0));
}

View File

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

View File

@ -584,8 +584,8 @@ namespace DotRecast.Detour
var tbmax = tile.data.header.bmax;
float qfac = tile.data.header.bvQuantFactor;
// Calculate quantized box
int[] bmin = new int[3];
int[] bmax = new int[3];
Span<int> bmin = stackalloc int[3];
Span<int> bmax = stackalloc int[3];
// dtClamp query box to world box.
float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X;
float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y;
@ -1827,6 +1827,9 @@ namespace DotRecast.Detour
using var verts = RcRentedArray.Rent<float>(m_nav.GetMaxVertsPerPoly() * 3);
const int MAX_NEIS = 8;
Span<long> neis = stackalloc long[MAX_NEIS];
while (0 < stack.Count)
{
// Pop front.
@ -1857,9 +1860,7 @@ namespace DotRecast.Detour
for (int i = 0, j = curPoly.vertCount - 1; i < curPoly.vertCount; j = i++)
{
// Find links to neighbours.
int MAX_NEIS = 8;
int nneis = 0;
using var neis = RcRentedArray.Rent<long>(MAX_NEIS);
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)]
/// @return True if the two AABB's overlap.
/// @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;
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;

View File

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

View File

@ -32,15 +32,18 @@ public class GLCheckerTexture
_gl = gl;
}
public void Release()
public unsafe void Release()
{
if (m_texId != 0)
{
_gl.DeleteTextures(1, m_texId);
fixed (uint* p = &m_texId)
{
_gl.DeleteTextures(1, p);
}
}
}
public void Bind()
public unsafe void Bind()
{
if (m_texId == 0)
{
@ -50,7 +53,11 @@ public class GLCheckerTexture
uint TSIZE = 64;
int[] data = new int[TSIZE * TSIZE];
_gl.GenTextures(1, out m_texId);
fixed (uint* p = &m_texId)
{
_gl.GenTextures(1, p);
}
_gl.BindTexture(GLEnum.Texture2D, m_texId);
int level = 0;
@ -70,8 +77,10 @@ public class GLCheckerTexture
level++;
}
_gl.TexParameterI(GLEnum.Texture2D, GLEnum.TextureMinFilter, (uint)GLEnum.LinearMipmapNearest);
_gl.TexParameterI(GLEnum.Texture2D, GLEnum.TextureMagFilter, (uint)GLEnum.Linear);
uint linearMipmapNearest = (uint)GLEnum.LinearMipmapNearest;
uint linear = (uint)GLEnum.Linear;
_gl.TexParameterI(GLEnum.Texture2D, GLEnum.TextureMinFilter, &linearMipmapNearest);
_gl.TexParameterI(GLEnum.Texture2D, GLEnum.TextureMagFilter, &linear);
}
else
{

View File

@ -123,80 +123,80 @@ public class NavMeshRenderer
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.DebugDrawHeightfieldSolid(rcBuilderResult.GetSolidHeightfield());
_debugDraw.DebugDrawHeightfieldSolid(rcBuilderResult.SolidHeightfiled);
_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.DebugDrawHeightfieldWalkable(rcBuilderResult.GetSolidHeightfield());
_debugDraw.DebugDrawHeightfieldWalkable(rcBuilderResult.SolidHeightfiled);
_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.DebugDrawRawContours(rcBuilderResult.GetContourSet(), 1f);
_debugDraw.DebugDrawRawContours(rcBuilderResult.ContourSet, 1f);
_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.DebugDrawRawContours(rcBuilderResult.GetContourSet(), 0.5f);
_debugDraw.DebugDrawContours(rcBuilderResult.GetContourSet());
_debugDraw.DebugDrawRawContours(rcBuilderResult.ContourSet, 0.5f);
_debugDraw.DebugDrawContours(rcBuilderResult.ContourSet);
_debugDraw.DepthMask(true);
}
if (rcBuilderResult.GetContourSet() != null && drawMode == DrawMode.DRAWMODE_CONTOURS)
if (rcBuilderResult.ContourSet != null && drawMode == DrawMode.DRAWMODE_CONTOURS)
{
_debugDraw.DepthMask(false);
_debugDraw.DebugDrawContours(rcBuilderResult.GetContourSet());
_debugDraw.DebugDrawContours(rcBuilderResult.ContourSet);
_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);
if (rcBuilderResult.GetContourSet() != null)
if (rcBuilderResult.ContourSet != null)
{
_debugDraw.DebugDrawRegionConnections(rcBuilderResult.GetContourSet());
_debugDraw.DebugDrawRegionConnections(rcBuilderResult.ContourSet);
}
_debugDraw.DepthMask(true);
}
if (rcBuilderResult.GetMesh() != null && drawMode == DrawMode.DRAWMODE_POLYMESH)
if (rcBuilderResult.Mesh != null && drawMode == DrawMode.DRAWMODE_POLYMESH)
{
_debugDraw.DepthMask(false);
_debugDraw.DebugDrawPolyMesh(rcBuilderResult.GetMesh());
_debugDraw.DebugDrawPolyMesh(rcBuilderResult.Mesh);
_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.DebugDrawPolyMeshDetail(rcBuilderResult.GetMeshDetail());
_debugDraw.DebugDrawPolyMeshDetail(rcBuilderResult.MeshDetail);
_debugDraw.DepthMask(true);
}
}

View File

@ -368,7 +368,7 @@ public class RecastDemo : IRecastDemoChannel
var scale = (float)_resolution.X / 1920;
int fontSize = Math.Max(10, (int)(16 * scale));
// for windows : Microsoft Visual C++ Redistributable Package
// 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);
@ -379,7 +379,7 @@ public class RecastDemo : IRecastDemoChannel
DemoInputGeomProvider geom = LoadInputMesh("nav_test.obj");
_sample = new DemoSample(geom, ImmutableArray<RcBuilderResult>.Empty, null);
_menuView = new RcMenuView();
settingsView = new RcSettingsView(this);
settingsView.SetSample(_sample);
@ -537,6 +537,7 @@ public class RecastDemo : IRecastDemoChannel
bool hasBound = false;
RcVec3f bminN = RcVec3f.Zero;
RcVec3f bmaxN = RcVec3f.Zero;
if (_sample.GetInputGeom() != null)
{
bminN = _sample.GetInputGeom().GetMeshBoundsMin();
@ -552,7 +553,7 @@ public class RecastDemo : IRecastDemoChannel
{
foreach (RcBuilderResult result in _sample.GetRecastResults())
{
if (result.GetSolidHeightfield() != null)
if (result.CompactHeightfield != null)
{
if (!hasBound)
{
@ -561,15 +562,15 @@ public class RecastDemo : IRecastDemoChannel
}
bminN = new RcVec3f(
Math.Min(bminN.X, result.GetSolidHeightfield().bmin.X),
Math.Min(bminN.Y, result.GetSolidHeightfield().bmin.Y),
Math.Min(bminN.Z, result.GetSolidHeightfield().bmin.Z)
Math.Min(bminN.X, result.CompactHeightfield.bmin.X),
Math.Min(bminN.Y, result.CompactHeightfield.bmin.Y),
Math.Min(bminN.Z, result.CompactHeightfield.bmin.Z)
);
bmaxN = new RcVec3f(
Math.Max(bmaxN.X, result.GetSolidHeightfield().bmax.X),
Math.Max(bmaxN.Y, result.GetSolidHeightfield().bmax.Y),
Math.Max(bmaxN.Z, result.GetSolidHeightfield().bmax.Z)
Math.Max(bmaxN.X, result.CompactHeightfield.bmax.X),
Math.Max(bmaxN.Y, result.CompactHeightfield.bmax.Y),
Math.Max(bmaxN.Z, result.CompactHeightfield.bmax.Z)
);
hasBound = true;
@ -577,12 +578,15 @@ public class RecastDemo : IRecastDemoChannel
}
}
// Reset camera and fog to match the mesh bounds.
if (hasBound)
{
RcVec3f bmin = bminN;
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.Y = (bmax.Y + bmin.Y) / 2 + camr;
cameraPos.Z = (bmax.Z + bmin.Z) / 2 + camr;
@ -688,14 +692,15 @@ public class RecastDemo : IRecastDemoChannel
NavMeshBuildResult buildResult;
var geom = _sample.GetInputGeom();
var settings = _sample.GetSettings();
if (settings.tiled)
{
buildResult = tileNavMeshBuilder.Build(_sample.GetInputGeom(), settings);
buildResult = tileNavMeshBuilder.Build(geom, settings);
}
else
{
buildResult = soloNavMeshBuilder.Build(_sample.GetInputGeom(), settings);
buildResult = soloNavMeshBuilder.Build(geom, settings);
}
if (!buildResult.Success)
@ -713,7 +718,7 @@ public class RecastDemo : IRecastDemoChannel
Logger.Information($"build times");
Logger.Information($"-----------------------------------------");
var telemetries = buildResult.RecastBuilderResults
.Select(x => x.GetTelemetry())
.Select(x => x.Context)
.SelectMany(x => x.ToList())
.GroupBy(x => x.Key)
.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 houseGeom = DemoInputGeomProvider.LoadFile("house.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;
}
@ -406,7 +406,7 @@ public class DynamicUpdateSampleTool : ISampleTool
long t = RcFrequency.Ticks;
try
{
bool updated = _tool.UpdateDynaMesh(executor);
bool updated = _tool.Update(executor);
if (updated)
{
buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond;
@ -450,7 +450,7 @@ public class DynamicUpdateSampleTool : ISampleTool
long t = RcFrequency.Ticks;
try
{
var _ = dynaMesh.Build(executor).Result;
var _ = dynaMesh.Build(executor);
}
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.
*/
using System.Linq;
using DotRecast.Core.Numerics;
using DotRecast.Detour.Extras.Jumplink;
using DotRecast.Recast.Demo.Draw;
@ -96,13 +97,25 @@ public class JumpLinkBuilderSampleTool : ISampleTool
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 settings = _sample.GetSettings();
_tool.Build(geom, settings, _sample.GetRecastResults(), _cfg);
}
} while (false);
}
ImGui.NewLine();

View File

@ -166,6 +166,10 @@ public class RcSettingsView : IRcView
ImGui.SliderFloat("Max Sample Error", ref settings.detailSampleMaxError, 0f, 16f, "%.1f");
ImGui.NewLine();
ImGui.Checkbox("Keep Itermediate Results", ref settings.keepInterResults);
ImGui.Checkbox("Build All Tiles", ref settings.buildAll);
ImGui.NewLine();
ImGui.Text("Tiling");
ImGui.Separator();
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); });
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();
}
@ -262,4 +272,4 @@ public class RcSettingsView : IRcView
{
this.maxPolys = maxPolys;
}
}
}

View File

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

View File

@ -34,7 +34,8 @@ namespace DotRecast.Recast.Toolset.Builder
settings.edgeMaxLen, settings.edgeMaxError,
settings.vertsPerPoly,
settings.detailSampleDist, settings.detailSampleMaxError,
settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans);
settings.filterLowHangingObstacles, settings.filterLedgeSpans, settings.filterWalkableLowHeightSpans,
settings.keepInterResults);
}
public NavMeshBuildResult Build(DemoInputGeomProvider geom,
@ -45,7 +46,8 @@ namespace DotRecast.Recast.Toolset.Builder
float edgeMaxLen, float edgeMaxError,
int vertsPerPoly,
float detailSampleDist, float detailSampleMaxError,
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans)
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
bool keepInterResults)
{
RcConfig cfg = new RcConfig(
partitionType,
@ -58,7 +60,7 @@ namespace DotRecast.Recast.Toolset.Builder
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
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);
if (null == meshData)
{
@ -74,11 +76,11 @@ namespace DotRecast.Recast.Toolset.Builder
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());
RcBuilder rcBuilder = new RcBuilder();
return rcBuilder.Build(geom, bcfg);
return rcBuilder.Build(geom, bcfg, keepInterResults);
}
public DtMeshData BuildMeshData(DemoInputGeomProvider geom,

View File

@ -41,7 +41,8 @@ namespace DotRecast.Recast.Toolset.Builder
settings.minRegionSize, settings.mergedRegionSize,
settings.edgeMaxLen, settings.edgeMaxError,
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,
@ -53,7 +54,8 @@ namespace DotRecast.Recast.Toolset.Builder
float edgeMaxLen, float edgeMaxError,
int vertsPerPoly,
float detailSampleDist, float detailSampleMaxError,
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans)
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
bool keepInterResults, bool buildAll)
{
List<RcBuilderResult> results = BuildRecastResult(
geom,
@ -65,7 +67,8 @@ namespace DotRecast.Recast.Toolset.Builder
edgeMaxLen, edgeMaxError,
vertsPerPoly,
detailSampleDist, detailSampleMaxError,
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
keepInterResults, buildAll
);
var tileMeshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, results);
@ -82,7 +85,8 @@ namespace DotRecast.Recast.Toolset.Builder
float edgeMaxLen, float edgeMaxError,
int vertsPerPoly,
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,
@ -97,7 +101,7 @@ namespace DotRecast.Recast.Toolset.Builder
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
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)
@ -123,10 +127,9 @@ namespace DotRecast.Recast.Toolset.Builder
List<DtMeshData> meshData = new List<DtMeshData>();
foreach (RcBuilderResult result in results)
{
int x = result.tileX;
int z = result.tileZ;
DtNavMeshCreateParams option = DemoNavMeshBuilder
.GetNavMeshCreateParams(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, result);
int x = result.TileX;
int z = result.TileZ;
DtNavMeshCreateParams option = DemoNavMeshBuilder.GetNavMeshCreateParams(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, result);
option.tileX = x;
option.tileZ = z;

View File

@ -31,5 +31,8 @@
public bool tiled = false;
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 readonly Dictionary<long, RcGizmo> colliderGizmos;
private readonly Random random;
private readonly IRcRand random;
private readonly DemoInputGeomProvider bridgeGeom;
private readonly DemoInputGeomProvider houseGeom;
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.random = rand;
@ -339,20 +339,14 @@ namespace DotRecast.Recast.Toolset.Tools
return resultvector;
}
public bool UpdateDynaMesh(TaskFactory executor)
public bool Update(TaskFactory executor)
{
if (dynaMesh == null)
{
return false;
}
bool updated = dynaMesh.Update(executor).Result;
if (updated)
{
return false;
}
return true;
return dynaMesh.Update(executor);
}
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 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 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.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DotRecast.Core;
@ -45,132 +47,113 @@ namespace DotRecast.Recast
_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 bmax = geom.GetMeshBoundsMax();
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;
}
public Task BuildTilesAsync(IInputGeomProvider geom, RcConfig cfg, int threads, List<RcBuilderResult> results, TaskFactory taskFactory, CancellationToken cancellationToken)
private List<RcBuilderResult> BuildMultiThread(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tw, int th,
int threads, TaskFactory taskFactory, CancellationToken cancellation,
bool keepInterResults, bool buildAll)
{
RcVec3f bmin = geom.GetMeshBoundsMin();
RcVec3f bmax = geom.GetMeshBoundsMax();
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>();
var results = new ConcurrentQueue<RcBuilderResult>();
RcAtomicInteger progress = new RcAtomicInteger(0);
List<Task> limits = new List<Task>(threads);
for (int x = 0; x < tw; ++x)
{
for (int y = 0; y < th; ++y)
{
int tx = x;
int ty = y;
var task = taskFactory.StartNew(() =>
var task = taskFactory.StartNew(state =>
{
if (cancellationToken.IsCancellationRequested)
if (cancellation.IsCancellationRequested)
return;
try
{
RcBuilderResult tile = BuildTile(geom, cfg, bmin, bmax, tx, ty, counter, tw * th);
lock (results)
{
results.Add(tile);
}
RcBuilderResult result = BuildTile(geom, cfg, bmin, bmax, tx, ty, progress, tw * th, keepInterResults);
results.Enqueue(result);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}, null, cancellation);
latch.Signal();
}, cancellationToken);
tasks.Add(task);
limits.Add(task);
if (threads <= limits.Count)
{
Task.WaitAll(limits.ToArray());
limits.Clear();
}
}
}
try
{
latch.Wait();
}
catch (ThreadInterruptedException)
if (0 < limits.Count)
{
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,
int ty, RcAtomicInteger counter, int total)
public RcBuilderResult BuildTile(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tx, int ty, RcAtomicInteger progress, int total, bool keepInterResults)
{
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, keepInterResults);
if (_progressListener != null)
{
_progressListener.OnProgress(counter.IncrementAndGet(), total);
_progressListener.OnProgress(progress.IncrementAndGet(), total);
}
return result;
}
public RcBuilderResult Build(IInputGeomProvider geom, RcBuilderConfig builderCfg)
public RcBuilderResult Build(IInputGeomProvider geom, RcBuilderConfig bcfg, bool keepInterResults)
{
RcConfig cfg = builderCfg.cfg;
RcConfig cfg = bcfg.cfg;
RcContext ctx = new RcContext();
//
// Step 1. Rasterize input polygon soup.
//
RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, builderCfg);
return Build(ctx, builderCfg.tileX, builderCfg.tileZ, geom, cfg, solid);
RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, bcfg);
return Build(ctx, bcfg.tileX, bcfg.tileZ, geom, cfg, solid, keepInterResults);
}
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, bool keepInterResults)
{
FilterHeightfield(ctx, solid, cfg);
RcCompactHeightfield chf = BuildCompactHeightfield(ctx, geom, cfg, solid);
@ -205,7 +188,7 @@ namespace DotRecast.Recast
{
// Prepare for region partitioning, by calculating distance field along the walkable surface.
RcRegions.BuildDistanceField(ctx, chf);
// Partition the walkable surface into simple regions without holes.
RcRegions.BuildRegions(ctx, chf, cfg.MinRegionArea, cfg.MergeRegionArea);
}
@ -241,7 +224,17 @@ namespace DotRecast.Recast
RcPolyMeshDetail dmesh = cfg.BuildMeshDetail
? RcMeshDetails.BuildPolyMeshDetail(ctx, pmesh, chf, cfg.DetailSampleDist, cfg.DetailSampleMaxError)
: null;
return new RcBuilderResult(tileX, tileZ, solid, chf, cset, pmesh, dmesh, ctx);
return new RcBuilderResult(
tileX,
tileZ,
keepInterResults ? solid : null,
keepInterResults ? chf : null,
keepInterResults ? cset : null,
pmesh,
dmesh,
ctx
);
}
/*
@ -298,7 +291,7 @@ namespace DotRecast.Recast
RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, builderCfg);
FilterHeightfield(ctx, solid, builderCfg.cfg);
RcCompactHeightfield chf = BuildCompactHeightfield(ctx, geom, builderCfg.cfg, solid);
RcLayers.BuildHeightfieldLayers(ctx, chf, builderCfg.cfg.BorderSize, builderCfg.cfg.WalkableHeight, out var lset);
return lset;
}

View File

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

View File

@ -22,29 +22,21 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Recast
{
/** Represents a heightfield layer within a layer set. */
/// A dynamic heightfield representing obstructed space.
/// @ingroup recast
public class RcHeightfield
{
/** The width of the heightfield. (Along the x-axis in cell units.) */
public readonly int width;
public readonly int width; //< The width of the heightfield. (Along the x-axis in cell units.)
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.) */
public readonly int height;
/** 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;
// memory pool for rcSpan instances.
public RcSpanPool pools; //< Linked list of span pools.
public RcSpan freelist; //< The next free span.
/** Border size in cell units */
public readonly int borderSize;

View File

@ -30,9 +30,9 @@ namespace DotRecast.Recast
hitTime = 0.0f;
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;
}

View File

@ -41,6 +41,63 @@ namespace DotRecast.Recast
aMin.Y <= bMax.Y && aMax.Y >= bMin.Y &&
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,
@ -156,7 +213,7 @@ namespace DotRecast.Recast
float axisOffset, int axis)
{
// 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)
{
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>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<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="NUnit.Analyzers" Version="4.0.1">
<PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.1">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,8 +27,8 @@ public class TestDetourBuilder : DetourBuilder
float agentMaxClimb, int x, int y, bool applyRecastDemoFlags)
{
RcBuilder rcBuilder = new RcBuilder();
RcBuilderResult rcResult = rcBuilder.Build(geom, rcConfig);
RcPolyMesh pmesh = rcResult.GetMesh();
RcBuilderResult rcResult = rcBuilder.Build(geom, rcConfig, false);
RcPolyMesh pmesh = rcResult.Mesh;
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,
agentMaxClimb);
return Build(option, x, y);

View File

@ -78,13 +78,13 @@ public class TestTiledNavMeshBuilder
true, true, true,
SampleAreaModifications.SAMPLE_AREAMOD_GROUND, true);
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
foreach (RcBuilderResult result in rcResult)
{
RcPolyMesh pmesh = result.GetMesh();
RcPolyMesh pmesh = result.Mesh;
if (pmesh.npolys == 0)
{
continue;
@ -103,7 +103,7 @@ public class TestTiledNavMeshBuilder
option.polyFlags = pmesh.flags;
option.polyCount = pmesh.npolys;
option.nvp = pmesh.nvp;
RcPolyMeshDetail dmesh = result.GetMeshDetail();
RcPolyMeshDetail dmesh = result.MeshDetail;
option.detailMeshes = dmesh.meshes;
option.detailVerts = dmesh.verts;
option.detailVertsCount = dmesh.nverts;
@ -116,8 +116,8 @@ public class TestTiledNavMeshBuilder
option.bmax = pmesh.bmax;
option.cs = cellSize;
option.ch = cellHeight;
option.tileX = result.tileX;
option.tileZ = result.tileZ;
option.tileX = result.TileX;
option.tileZ = result.TileZ;
option.buildBvTree = true;
navMesh.AddTile(DtNavMeshBuilder.CreateNavMeshData(option), 0, 0);
}

View File

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

View File

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

View File

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