Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for saving and loading dynamic nav meshes @ppiastucki #79

Merged
merged 1 commit into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/DotRecast.Detour.Dynamic/DtDynamicNavMesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 3. This notice may not be removed or altered from any source distribution.
using System.Linq;
using System.Threading.Tasks;
using DotRecast.Core;
using DotRecast.Core.Collections;
using DotRecast.Detour.Dynamic.Colliders;
using DotRecast.Detour.Dynamic.Io;
using DotRecast.Recast;
Expand Down Expand Up @@ -63,7 +64,7 @@ public DtDynamicNavMesh(DtVoxelFile voxelFile)
navMeshParams.orig.Y = voxelFile.bounds[1];
navMeshParams.orig.Z = voxelFile.bounds[2];
navMeshParams.tileWidth = voxelFile.useTiles ? voxelFile.cellSize * voxelFile.tileSizeX : voxelFile.bounds[3] - voxelFile.bounds[0];
navMeshParams.tileHeight = voxelFile.useTiles ? voxelFile.cellSize * voxelFile.tileSizeZ: voxelFile.bounds[5] - voxelFile.bounds[2];
navMeshParams.tileHeight = voxelFile.useTiles ? voxelFile.cellSize * voxelFile.tileSizeZ : voxelFile.bounds[5] - voxelFile.bounds[2];
navMeshParams.maxTiles = voxelFile.tiles.Count;
navMeshParams.maxPolys = 0x8000;
foreach (var t in voxelFile.tiles)
Expand Down Expand Up @@ -230,6 +231,8 @@ private bool UpdateNavMesh()
{
if (_dirty)
{
_dirty = false;

DtNavMesh navMesh = new DtNavMesh();
navMesh.Init(navMeshParams, MAX_VERTS_PER_POLY);

Expand All @@ -239,7 +242,6 @@ private bool UpdateNavMesh()
}

_navMesh = navMesh;
_dirty = false;
return true;
}

Expand Down Expand Up @@ -267,5 +269,21 @@ public List<RcBuilderResult> RecastResults()
{
return _tiles.Values.Select(t => t.recastResult).ToList();
}

public void NavMesh(DtNavMesh mesh)
{
_tiles.Values.ForEach(t =>
{
const int MAX_NEIS = 32;
DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS];
int nneis = mesh.GetTilesAt(t.voxelTile.tileX, t.voxelTile.tileZ, tiles, MAX_NEIS);
if (0 < nneis)
{
t.SetMeshData(tiles[0].data);
}
});
_navMesh = mesh;
_dirty = false;
}
}
}
5 changes: 5 additions & 0 deletions src/DotRecast.Detour.Dynamic/DtDynamicTile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,5 +189,10 @@ public void AddTo(DtNavMesh navMesh)
id = 0;
}
}

public void SetMeshData(DtMeshData data)
{
this.meshData = data;
}
}
}
99 changes: 99 additions & 0 deletions test/DotRecast.Detour.Dynamic.Test/DynamicNavMeshTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
Expand All @@ -6,6 +7,7 @@
using DotRecast.Detour.Dynamic.Colliders;
using DotRecast.Detour.Dynamic.Io;
using DotRecast.Detour.Dynamic.Test.Io;
using DotRecast.Detour.Io;
using NUnit.Framework;

namespace DotRecast.Detour.Dynamic.Test;
Expand Down Expand Up @@ -78,4 +80,101 @@ public void E2eTest()
// path length should be back to the initial value
Assert.That(path.Count, Is.EqualTo(16));
}


[Test]
public void ShouldSaveAndLoadDynamicNavMesh()
{
using var writerMs = new MemoryStream();
using var bw = new BinaryWriter(writerMs);


int maxVertsPerPoly = 6;
// load voxels from file

{
byte[] bytes = RcIO.ReadFileIfFound("test_tiles.voxels");
using var readMs = new MemoryStream(bytes);
using var br = new BinaryReader(readMs);

DtVoxelFileReader reader = new DtVoxelFileReader(DtVoxelTileLZ4ForTestCompressor.Shared);
DtVoxelFile f = reader.Read(br);

// create dynamic navmesh
DtDynamicNavMesh mesh = new DtDynamicNavMesh(f);

// build navmesh asynchronously using multiple threads
mesh.Build(Task.Factory);

// Save the resulting nav mesh and re-use it
new DtMeshSetWriter().Write(bw, mesh.NavMesh(), RcByteOrder.LITTLE_ENDIAN, true);
maxVertsPerPoly = mesh.NavMesh().GetMaxVertsPerPoly();
}

{
byte[] bytes = RcIO.ReadFileIfFound("test_tiles.voxels");
using var readMs = new MemoryStream(bytes);
using var br = new BinaryReader(readMs);

// load voxels from file
DtVoxelFileReader reader = new DtVoxelFileReader(DtVoxelTileLZ4ForTestCompressor.Shared);
DtVoxelFile f = reader.Read(br);

// create dynamic navmesh
DtDynamicNavMesh mesh = new DtDynamicNavMesh(f);
// use the saved nav mesh instead of building from scratch
DtNavMesh navMesh = new DtMeshSetReader().Read(new RcByteBuffer(writerMs.ToArray()), maxVertsPerPoly);
mesh.NavMesh(navMesh);

DtNavMeshQuery query = new DtNavMeshQuery(mesh.NavMesh());
IDtQueryFilter filter = new DtQueryDefaultFilter();

// find path
_ = query.FindNearestPoly(START_POS, EXTENT, filter, out var startNearestRef, out var startNearestPos, out var _);
_ = query.FindNearestPoly(END_POS, EXTENT, filter, out var endNearestRef, out var endNearestPos, out var _);

List<long> path = new List<long>();
query.FindPath(startNearestRef, endNearestRef, startNearestPos, endNearestPos, filter, ref path, DtFindPathOption.AnyAngle);

// check path length without any obstacles
Assert.That(path.Count, Is.EqualTo(16));

// place obstacle
DtCollider colldier = new DtSphereCollider(SPHERE_POS, 20, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND, 0.1f);
long colliderId = mesh.AddCollider(colldier);

// update navmesh asynchronously
mesh.Update(Task.Factory);

// create new query
query = new DtNavMeshQuery(mesh.NavMesh());

// find path again
_ = query.FindNearestPoly(START_POS, EXTENT, filter, out startNearestRef, out startNearestPos, out var _);
_ = query.FindNearestPoly(END_POS, EXTENT, filter, out endNearestRef, out endNearestPos, out var _);

path = new List<long>();
query.FindPath(startNearestRef, endNearestRef, startNearestPos, endNearestPos, filter, ref path, DtFindPathOption.AnyAngle);

// check path length with obstacles
Assert.That(path.Count, Is.EqualTo(19));

// remove obstacle
mesh.RemoveCollider(colliderId);
// update navmesh asynchronously
mesh.Update(Task.Factory);

// create new query
query = new DtNavMeshQuery(mesh.NavMesh());
// find path one more time
_ = query.FindNearestPoly(START_POS, EXTENT, filter, out startNearestRef, out startNearestPos, out var _);
_ = query.FindNearestPoly(END_POS, EXTENT, filter, out endNearestRef, out endNearestPos, out var _);

path = new List<long>();
query.FindPath(startNearestRef, endNearestRef, startNearestPos, endNearestPos, filter, ref path, DtFindPathOption.AnyAngle);

// path length should be back to the initial value
Assert.That(path.Count, Is.EqualTo(16));
}
}
}
Loading