Fix dynamic mesh bounds calculation #77

recast4j
- https://github.com/recast4j/recast4j/issues/203
- https://github.com/recast4j/recast4j/pull/204
This commit is contained in:
ikpil 2024-09-21 02:54:01 +09:00
parent e9a05843da
commit 2d0e2f8525
14 changed files with 98 additions and 40 deletions

View File

@ -12,6 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed
- Fix raycast shortcuts ([@Sarofc](https://github.com/Sarofc)) [#72](https://github.com/ikpil/DotRecast/issues/72)
- Fix dynamic mesh bounds calculation ([@ppiastucki](https://github.com/ppiastucki)) [#77](https://github.com/ikpil/DotRecast/issues/77)
- issuer : [@OhJeongrok](https://github.com/OhJeongrok)
### Changed
- Changed data structure of 'neis' from List<byte> to byte[] for optimized memory usage and improved access speed in `DtLayerMonotoneRegion`

View File

@ -123,18 +123,18 @@ Global
{FFE40BBF-843B-41FA-8504-F4ABD166762E} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{38933A87-4568-40A5-A3DA-E2445E8C2B99} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{C19E4BFA-63A0-4815-9815-869A9DC52DBC} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{88754FE2-A05A-4D4D-A81A-90418AD32362} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{554CB5BD-D58A-4856-BFE1-666A62C9BEA3} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{FA7EF26A-BA47-43FD-86F8-0A33CFDF643F} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{F9C5B52E-C01D-4514-94E9-B1A6895352E2} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{53AF87DA-37F8-4504-B623-B2113F4438CA} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{67C68B34-118A-439C-88E1-D6D1ED78DC59} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{17E4F2F0-FC27-416E-9CB6-9F2CAAC49C9D} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{7BAA69B2-EDC7-4603-B16F-BC7B24353F81} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{DEB16B90-CCD4-497E-A2E9-4CC66FD7EF47} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{3CAA7306-088E-4373-A406-99755CC2B605} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{023E1E6A-4895-4573-89AE-3D5D8E0B39C8} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{DF987948-8C23-4337-AF83-D87D6407518D} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{88754FE2-A05A-4D4D-A81A-90418AD32362} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{554CB5BD-D58A-4856-BFE1-666A62C9BEA3} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{F9C5B52E-C01D-4514-94E9-B1A6895352E2} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{67C68B34-118A-439C-88E1-D6D1ED78DC59} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{7BAA69B2-EDC7-4603-B16F-BC7B24353F81} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{3CAA7306-088E-4373-A406-99755CC2B605} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{10395C8F-DFBD-4263-8A20-EA3500A6E55A} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
{D1EFC625-D095-4208-98A2-112B73CB40B0} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73}
EndGlobalSection

View File

@ -188,17 +188,17 @@ namespace DotRecast.Detour.Dynamic
return _tiles.Values;
}
int minx = (int)MathF.Floor((bounds[0] - navMeshParams.orig.X) / navMeshParams.tileWidth);
int minz = (int)MathF.Floor((bounds[2] - navMeshParams.orig.Z) / navMeshParams.tileHeight);
int maxx = (int)MathF.Floor((bounds[3] - navMeshParams.orig.X) / navMeshParams.tileWidth);
int maxz = (int)MathF.Floor((bounds[5] - navMeshParams.orig.Z) / navMeshParams.tileHeight);
int minx = (int)MathF.Floor((bounds[0] - navMeshParams.orig.X) / navMeshParams.tileWidth) - 1;
int minz = (int)MathF.Floor((bounds[2] - navMeshParams.orig.Z) / navMeshParams.tileHeight) - 1;
int maxx = (int)MathF.Floor((bounds[3] - navMeshParams.orig.X) / navMeshParams.tileWidth) + 1;
int maxz = (int)MathF.Floor((bounds[5] - navMeshParams.orig.Z) / navMeshParams.tileHeight) + 1;
List<DtDynamicTile> tiles = new List<DtDynamicTile>();
for (int z = minz; z <= maxz; ++z)
{
for (int x = minx; x <= maxx; ++x)
{
DtDynamicTile tile = GetTileAt(x, z);
if (tile != null)
if (tile != null && IntersectsXZ(tile, bounds))
{
tiles.Add(tile);
}
@ -208,6 +208,12 @@ namespace DotRecast.Detour.Dynamic
return tiles;
}
private bool IntersectsXZ(DtDynamicTile tile, float[] bounds)
{
return tile.voxelTile.boundsMin.X <= bounds[3] && tile.voxelTile.boundsMax.X >= bounds[0] &&
tile.voxelTile.boundsMin.Z <= bounds[5] && tile.voxelTile.boundsMax.Z >= bounds[2];
}
private List<DtDynamicTile> GetTilesByCollider(long cid)
{
return _tiles.Values.Where(t => t.ContainsCollider(cid)).ToList();

View File

@ -78,7 +78,7 @@ namespace DotRecast.Detour.Dynamic.Io
walkbableAreaMod, buildMeshDetail);
}
public static DtVoxelFile From(RcConfig config, List<RcBuilderResult> results)
public static DtVoxelFile From(RcConfig config, IList<RcBuilderResult> results)
{
DtVoxelFile f = new DtVoxelFile();
f.version = 1;
@ -109,13 +109,14 @@ namespace DotRecast.Detour.Dynamic.Io
};
foreach (RcBuilderResult r in results)
{
float pad = r.SolidHeightfiled.borderSize * r.SolidHeightfiled.cs;
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[0] = Math.Min(f.bounds[0], r.SolidHeightfiled.bmin.X + pad);
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[2] = Math.Min(f.bounds[2], r.SolidHeightfiled.bmin.Z + pad);
f.bounds[3] = Math.Max(f.bounds[3], r.SolidHeightfiled.bmax.X - pad);
f.bounds[4] = Math.Max(f.bounds[4], r.SolidHeightfiled.bmax.Y);
f.bounds[5] = Math.Max(f.bounds[5], r.SolidHeightfiled.bmax.Z);
f.bounds[5] = Math.Max(f.bounds[5], r.SolidHeightfiled.bmax.Z - pad);
}
return f;
@ -155,12 +156,13 @@ namespace DotRecast.Detour.Dynamic.Io
{
RcHeightfield heightfield = vt.Heightfield();
f.tiles.Add(new DtVoxelTile(vt.tileX, vt.tileZ, heightfield));
f.bounds[0] = Math.Min(f.bounds[0], vt.boundsMin.X);
float pad = vt.borderSize * vt.cellSize;
f.bounds[0] = Math.Min(f.bounds[0], vt.boundsMin.X + pad);
f.bounds[1] = Math.Min(f.bounds[1], vt.boundsMin.Y);
f.bounds[2] = Math.Min(f.bounds[2], vt.boundsMin.Z);
f.bounds[3] = Math.Max(f.bounds[3], vt.boundsMax.X);
f.bounds[2] = Math.Min(f.bounds[2], vt.boundsMin.Z + pad);
f.bounds[3] = Math.Max(f.bounds[3], vt.boundsMax.X - pad);
f.bounds[4] = Math.Max(f.bounds[4], vt.boundsMax.Y);
f.bounds[5] = Math.Max(f.bounds[5], vt.boundsMax.Z);
f.bounds[5] = Math.Max(f.bounds[5], vt.boundsMax.Z - pad);
}
return f;

View File

@ -32,6 +32,7 @@ namespace DotRecast.Recast.Demo
private DtNavMesh _navMesh;
private DtNavMeshQuery _navMeshQuery;
private readonly RcNavMeshBuildSettings _settings;
private RcConfig _cfg;
private IList<RcBuilderResult> _recastResults;
private bool _changed;
@ -56,6 +57,11 @@ namespace DotRecast.Recast.Demo
return _geom;
}
public RcConfig GetRecastConfig()
{
return _cfg;
}
public IList<RcBuilderResult> GetRecastResults()
{
return _recastResults;
@ -86,9 +92,10 @@ namespace DotRecast.Recast.Demo
_changed = changed;
}
public void Update(DemoInputGeomProvider geom, IList<RcBuilderResult> recastResults, DtNavMesh navMesh)
public void Update(DemoInputGeomProvider geom, RcConfig cfg, IList<RcBuilderResult> recastResults, DtNavMesh navMesh)
{
_geom = geom;
_cfg = cfg;
_recastResults = recastResults;
_navMesh = navMesh;
SetQuery(navMesh);

View File

@ -320,7 +320,7 @@ public class RecastDemo : IRecastDemoChannel
if (null != mesh)
{
_sample.Update(_sample.GetInputGeom(), ImmutableArray<RcBuilderResult>.Empty, mesh);
_sample.Update(_sample.GetInputGeom(), _sample.GetRecastConfig(), ImmutableArray<RcBuilderResult>.Empty, mesh);
_toolsetView.SetEnabled(true);
}
}
@ -684,7 +684,7 @@ public class RecastDemo : IRecastDemoChannel
{
var geom = LoadInputMesh(args.FilePath);
_sample.Update(geom, ImmutableArray<RcBuilderResult>.Empty, null);
_sample.Update(geom, null, ImmutableArray<RcBuilderResult>.Empty, null);
}
private void OnNavMeshBuildBegan(NavMeshBuildBeganEvent args)
@ -719,7 +719,7 @@ public class RecastDemo : IRecastDemoChannel
return;
}
_sample.Update(_sample.GetInputGeom(), buildResult.RecastBuilderResults, buildResult.NavMesh);
_sample.Update(_sample.GetInputGeom(), buildResult.Cfg, buildResult.RecastBuilderResults, buildResult.NavMesh);
_sample.SetChanged(false);
settingsView.SetBuildTime((RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond);
//settingsUI.SetBuildTelemetry(buildResult.Item1.Select(x => x.GetTelemetry()).ToList());

View File

@ -25,6 +25,7 @@ using DotRecast.Core;
using DotRecast.Core.Collections;
using DotRecast.Core.Numerics;
using DotRecast.Detour.Dynamic;
using DotRecast.Detour.Dynamic.Io;
using DotRecast.Recast.Toolset;
using DotRecast.Recast.Toolset.Tools;
using DotRecast.Recast.Demo.Draw;
@ -119,6 +120,14 @@ public class DynamicUpdateSampleTool : ISampleTool
const string loadVoxelPopupStrId = "Load Voxels Popup";
bool isLoadVoxelPopup = true;
if (_sample.GetRecastResults() != null && _sample.GetRecastConfig() != null)
{
if (ImGui.Button("Import Voxels"))
{
Copy();
}
}
if (ImGui.Button("Load Voxels..."))
{
ImGui.OpenPopup(loadVoxelPopupStrId);
@ -410,7 +419,7 @@ public class DynamicUpdateSampleTool : ISampleTool
{
buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond;
var dynaMesh = _tool.GetDynamicNavMesh();
_sample.Update(null, dynaMesh.RecastResults(), dynaMesh.NavMesh());
_sample.Update(null, null, dynaMesh.RecastResults(), dynaMesh.NavMesh());
_sample.SetChanged(false);
}
}
@ -420,6 +429,15 @@ public class DynamicUpdateSampleTool : ISampleTool
}
}
private void Copy()
{
if (_sample.GetRecastResults() != null && _sample.GetRecastConfig() != null)
{
var dynaMesh = _tool.Copy(_sample.GetRecastConfig(), _sample.GetRecastResults());
UpdateFrom(dynaMesh.config);
BuildDynaMesh();
}
}
private void Load(string filename)
{
@ -457,7 +475,7 @@ public class DynamicUpdateSampleTool : ISampleTool
}
buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond;
_sample.Update(null, dynaMesh.RecastResults(), dynaMesh.NavMesh());
_sample.Update(null, null, dynaMesh.RecastResults(), dynaMesh.NavMesh());
}
private void UpdateTo(DtDynamicNavMeshConfig config)

View File

@ -32,7 +32,7 @@ public class ObstacleSampleTool : ISampleTool
var buildResult = _tool.Build(geom, settings, RcByteOrder.LITTLE_ENDIAN, true);
if (buildResult.Success)
{
_sample.Update(_sample.GetInputGeom(), buildResult.RecastBuilderResults, buildResult.NavMesh);
_sample.Update(_sample.GetInputGeom(), buildResult.Cfg, buildResult.RecastBuilderResults, buildResult.NavMesh);
}
}

View File

@ -7,6 +7,7 @@ namespace DotRecast.Recast.Toolset.Builder
public class NavMeshBuildResult
{
public readonly bool Success;
public readonly RcConfig Cfg;
public readonly IList<RcBuilderResult> RecastBuilderResults;
public readonly DtNavMesh NavMesh;
@ -17,11 +18,22 @@ namespace DotRecast.Recast.Toolset.Builder
NavMesh = null;
}
public NavMeshBuildResult(IList<RcBuilderResult> recastBuilderResults, DtNavMesh navMesh)
// for solo
public NavMeshBuildResult(RcConfig cfg, IList<RcBuilderResult> recastBuilderResults, DtNavMesh navMesh)
{
Success = true;
Cfg = cfg;
RecastBuilderResults = recastBuilderResults;
NavMesh = navMesh;
}
// for tiles
public NavMeshBuildResult(RcConfig cfg, IList<RcBuilderResult> recastBuilderResults)
{
Success = true;
Cfg = cfg;
RecastBuilderResults = recastBuilderResults;
NavMesh = null;
}
}
}

View File

@ -69,7 +69,7 @@ namespace DotRecast.Recast.Toolset.Builder
}
var navMesh = BuildNavMesh(meshData, vertsPerPoly);
return new NavMeshBuildResult(RcImmutableArray.Create(rcResult), navMesh);
return new NavMeshBuildResult(cfg, RcImmutableArray.Create(rcResult), navMesh);
}
private DtNavMesh BuildNavMesh(DtMeshData meshData, int vertsPerPoly)

View File

@ -58,7 +58,7 @@ namespace DotRecast.Recast.Toolset.Builder
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
bool keepInterResults, bool buildAll)
{
List<RcBuilderResult> results = BuildRecastResult(
NavMeshBuildResult result = BuildRecastResult(
geom,
tileSize,
partitionType,
@ -72,12 +72,12 @@ namespace DotRecast.Recast.Toolset.Builder
keepInterResults, buildAll
);
var tileMeshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, results);
var tileMeshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, result.RecastBuilderResults);
var tileNavMesh = BuildNavMesh(geom, tileMeshData, cellSize, tileSize, vertsPerPoly);
return new NavMeshBuildResult(results, tileNavMesh);
return new NavMeshBuildResult(result.Cfg, result.RecastBuilderResults, tileNavMesh);
}
public List<RcBuilderResult> BuildRecastResult(IInputGeomProvider geom,
public NavMeshBuildResult BuildRecastResult(IInputGeomProvider geom,
int tileSize,
RcPartition partitionType,
float cellSize, float cellHeight,
@ -102,7 +102,8 @@ namespace DotRecast.Recast.Toolset.Builder
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
RcBuilder rcBuilder = new RcBuilder();
return rcBuilder.BuildTiles(geom, cfg, keepInterResults, buildAll, Environment.ProcessorCount + 1, Task.Factory);
var results = rcBuilder.BuildTiles(geom, cfg, keepInterResults, buildAll, Environment.ProcessorCount + 1, Task.Factory);
return new NavMeshBuildResult(cfg, results);
}
public DtNavMesh BuildNavMesh(IInputGeomProvider geom, List<DtMeshData> meshData, float cellSize, int tileSize, int vertsPerPoly)

View File

@ -32,7 +32,7 @@ namespace DotRecast.Recast.Toolset
public bool tiled = false;
public int tileSize = 32;
public bool keepInterResults = false;
public bool keepInterResults = true; // full memory
public bool buildAll = true;
}
}

View File

@ -141,6 +141,16 @@ namespace DotRecast.Recast.Toolset.Tools
return colliderWithGizmo;
}
public DtDynamicNavMesh Copy(RcConfig cfg, IList<RcBuilderResult> results)
{
var voxelFile = DtVoxelFile.From(cfg, results);
dynaMesh = new DtDynamicNavMesh(voxelFile);
dynaMesh.config.keepIntermediateResults = true;
colliderGizmos.Clear();
return dynaMesh;
}
public DtDynamicNavMesh Load(string filename, IRcCompressor compressor)
{
using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);

View File

@ -78,7 +78,7 @@ namespace DotRecast.Recast.Toolset.Tools
_tc.BuildNavMeshTile(refs);
}
return new NavMeshBuildResult(RcImmutableArray<RcBuilderResult>.Empty, _tc.GetNavMesh());
return new NavMeshBuildResult(cfg, RcImmutableArray<RcBuilderResult>.Empty, _tc.GetNavMesh());
}
public void ClearAllTempObstacles()