forked from mirror/DotRecast
Support for saving and loading dynamic nav meshes @ppiastucki
[Upstream] from recast4j 506b503 - chore: Support for saving and loading dynamic nav meshes (fixes #200) (#209)
This commit is contained in:
parent
36795dc909
commit
62f9cfe034
|
@ -23,6 +23,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DotRecast.Core;
|
using DotRecast.Core;
|
||||||
|
using DotRecast.Core.Collections;
|
||||||
using DotRecast.Detour.Dynamic.Colliders;
|
using DotRecast.Detour.Dynamic.Colliders;
|
||||||
using DotRecast.Detour.Dynamic.Io;
|
using DotRecast.Detour.Dynamic.Io;
|
||||||
using DotRecast.Recast;
|
using DotRecast.Recast;
|
||||||
|
@ -230,6 +231,8 @@ namespace DotRecast.Detour.Dynamic
|
||||||
{
|
{
|
||||||
if (_dirty)
|
if (_dirty)
|
||||||
{
|
{
|
||||||
|
_dirty = false;
|
||||||
|
|
||||||
DtNavMesh navMesh = new DtNavMesh();
|
DtNavMesh navMesh = new DtNavMesh();
|
||||||
navMesh.Init(navMeshParams, MAX_VERTS_PER_POLY);
|
navMesh.Init(navMeshParams, MAX_VERTS_PER_POLY);
|
||||||
|
|
||||||
|
@ -239,7 +242,6 @@ namespace DotRecast.Detour.Dynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
_navMesh = navMesh;
|
_navMesh = navMesh;
|
||||||
_dirty = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,5 +269,21 @@ namespace DotRecast.Detour.Dynamic
|
||||||
{
|
{
|
||||||
return _tiles.Values.Select(t => t.recastResult).ToList();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -189,5 +189,10 @@ namespace DotRecast.Detour.Dynamic
|
||||||
id = 0;
|
id = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetMeshData(DtMeshData data)
|
||||||
|
{
|
||||||
|
this.meshData = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -6,6 +7,7 @@ using DotRecast.Core.Numerics;
|
||||||
using DotRecast.Detour.Dynamic.Colliders;
|
using DotRecast.Detour.Dynamic.Colliders;
|
||||||
using DotRecast.Detour.Dynamic.Io;
|
using DotRecast.Detour.Dynamic.Io;
|
||||||
using DotRecast.Detour.Dynamic.Test.Io;
|
using DotRecast.Detour.Dynamic.Test.Io;
|
||||||
|
using DotRecast.Detour.Io;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace DotRecast.Detour.Dynamic.Test;
|
namespace DotRecast.Detour.Dynamic.Test;
|
||||||
|
@ -78,4 +80,101 @@ public class DynamicNavMeshTest
|
||||||
// path length should be back to the initial value
|
// path length should be back to the initial value
|
||||||
Assert.That(path.Count, Is.EqualTo(16));
|
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…
Reference in New Issue