Changed `Dictionary<int, List<DtMeshTile>>` to `DtMeshTile[]` to optimize memory usage

This commit is contained in:
ikpil 2024-05-22 01:33:15 +09:00 committed by Ikpil
parent 9a03ade6c9
commit c7f03d00ff
8 changed files with 205 additions and 129 deletions

View File

@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Changed `PolyQueryInvoker` to `DtActionPolyQuery` - Changed `PolyQueryInvoker` to `DtActionPolyQuery`
- Changed `DtTileCacheBuilder` to a static class - Changed `DtTileCacheBuilder` to a static class
- Changed `DtTileCacheLayerHeaderReader` to a static class - Changed `DtTileCacheLayerHeaderReader` to a static class
- Changed `Dictionary<int, List<DtMeshTile>>` to `DtMeshTile[]` to optimize memory usage
### Removed ### Removed
- Nothing - Nothing

View File

@ -33,6 +33,7 @@ namespace DotRecast.Detour
public DtLink[] links; // The tile links. [Size: dtMeshHeader::maxLinkCount] public DtLink[] links; // The tile links. [Size: dtMeshHeader::maxLinkCount]
public int flags; //< Tile flags. (See: #dtTileFlags) public int flags; //< Tile flags. (See: #dtTileFlags)
public DtMeshTile next; //< The next free tile, or the next tile in the spatial grid.
public DtMeshTile(int index) public DtMeshTile(int index)
{ {

View File

@ -39,8 +39,8 @@ namespace DotRecast.Detour
private int m_tileLutSize; //< Tile hash lookup size (must be pot). private int m_tileLutSize; //< Tile hash lookup size (must be pot).
private int m_tileLutMask; // < Tile hash lookup mask. private int m_tileLutMask; // < Tile hash lookup mask.
private Dictionary<int, List<DtMeshTile>> m_posLookup; //< Tile hash lookup. private DtMeshTile[] m_posLookup; //< Tile hash lookup.
private LinkedList<DtMeshTile> m_nextFree; //< Freelist of tiles. private DtMeshTile m_nextFree; //< Freelist of tiles.
private DtMeshTile[] m_tiles; //< List of tiles. private DtMeshTile[] m_tiles; //< List of tiles.
/** The maximum number of vertices per navigation polygon. */ /** The maximum number of vertices per navigation polygon. */
@ -62,15 +62,16 @@ namespace DotRecast.Detour
if (0 == m_tileLutSize) if (0 == m_tileLutSize)
m_tileLutSize = 1; m_tileLutSize = 1;
m_tileLutMask = m_tileLutSize - 1; m_tileLutMask = m_tileLutSize - 1;
m_tiles = new DtMeshTile[m_maxTiles]; m_tiles = new DtMeshTile[m_maxTiles];
m_posLookup = new Dictionary<int, List<DtMeshTile>>(); m_posLookup = new DtMeshTile[m_tileLutSize];
m_nextFree = new LinkedList<DtMeshTile>(); m_nextFree = null;
for (int i = 0; i < m_maxTiles; i++) for (int i = m_maxTiles-1; i >= 0; --i)
{ {
m_tiles[i] = new DtMeshTile(i); m_tiles[i] = new DtMeshTile(i);
m_tiles[i].salt = 1; m_tiles[i].salt = 1;
m_nextFree.AddLast(m_tiles[i]); m_tiles[i].next = m_nextFree;
m_nextFree = m_tiles[i];
} }
return DtStatus.DT_SUCCESS; return DtStatus.DT_SUCCESS;
@ -384,15 +385,13 @@ namespace DotRecast.Detour
DtMeshTile tile = null; DtMeshTile tile = null;
if (lastRef == 0) if (lastRef == 0)
{ {
// Make sure we could allocate a tile. if (null != m_nextFree)
if (0 == m_nextFree.Count)
{ {
throw new Exception("Could not allocate a tile"); tile = m_nextFree;
m_nextFree = tile.next;
tile.next = null;
m_tileCount++;
} }
tile = m_nextFree.First?.Value;
m_nextFree.RemoveFirst();
m_tileCount++;
} }
else else
{ {
@ -405,14 +404,25 @@ namespace DotRecast.Detour
// Try to find the specific tile id from the free list. // Try to find the specific tile id from the free list.
DtMeshTile target = m_tiles[tileIndex]; DtMeshTile target = m_tiles[tileIndex];
// Remove from freelist DtMeshTile prev = null;
if (!m_nextFree.Remove(target)) tile = m_nextFree;
while (null != tile && tile != target)
{ {
// Could not find the correct location. prev = tile;
return DtStatus.DT_FAILURE | DtStatus.DT_OUT_OF_MEMORY; tile = tile.next;
} }
tile = target; // Could not find the correct location.
if (tile != target)
return DtStatus.DT_FAILURE | DtStatus.DT_OUT_OF_MEMORY;
// Remove from freelist
if (null == prev)
m_nextFree = tile.next;
else
prev.next = tile.next;
// Restore salt. // Restore salt.
tile.salt = DecodePolyIdSalt(lastRef); tile.salt = DecodePolyIdSalt(lastRef);
} }
@ -424,7 +434,10 @@ namespace DotRecast.Detour
} }
// Insert tile into the position lut. // Insert tile into the position lut.
GetTileListByPos(header.x, header.y).Add(tile); int h = ComputeTileHash(header.x, header.y, m_tileLutMask);
tile.next = m_posLookup[h];
m_posLookup[h] = tile;
// Patch header pointers. // Patch header pointers.
tile.data = data; tile.data = data;
@ -450,13 +463,19 @@ namespace DotRecast.Detour
tile.flags = flags; tile.flags = flags;
ConnectIntLinks(tile); ConnectIntLinks(tile);
// Base off-mesh connections to their starting polygons and connect connections inside the tile. // Base off-mesh connections to their starting polygons and connect connections inside the tile.
BaseOffMeshLinks(tile); BaseOffMeshLinks(tile);
ConnectExtOffMeshLinks(tile, tile, -1); ConnectExtOffMeshLinks(tile, tile, -1);
// Create connections with neighbour tiles.
const int MAX_NEIS = 32;
DtMeshTile[] neis = new DtMeshTile[MAX_NEIS];
int nneis;
// Connect with layers in current tile. // Connect with layers in current tile.
List<DtMeshTile> neis = GetTilesAt(header.x, header.y); nneis = GetTilesAt(header.x, header.y, neis, MAX_NEIS);
for (int j = 0; j < neis.Count; ++j) for (int j = 0; j < nneis; ++j)
{ {
if (neis[j] == tile) if (neis[j] == tile)
{ {
@ -472,8 +491,8 @@ namespace DotRecast.Detour
// Connect with neighbour tiles. // Connect with neighbour tiles.
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
neis = GetNeighbourTilesAt(header.x, header.y, i); nneis = GetNeighbourTilesAt(header.x, header.y, i, neis, MAX_NEIS);
for (int j = 0; j < neis.Count; ++j) for (int j = 0; j < nneis; ++j)
{ {
ConnectExtLinks(tile, neis[j], i); ConnectExtLinks(tile, neis[j], i);
ConnectExtLinks(neis[j], tile, DtUtils.OppositeTile(i)); ConnectExtLinks(neis[j], tile, DtUtils.OppositeTile(i));
@ -516,30 +535,44 @@ namespace DotRecast.Detour
} }
// Remove tile from hash lookup. // Remove tile from hash lookup.
GetTileListByPos(tile.data.header.x, tile.data.header.y).Remove(tile); int h = ComputeTileHash(tile.data.header.x, tile.data.header.y, m_tileLutMask);
DtMeshTile prev = null;
// Remove connections to neighbour tiles. DtMeshTile cur = m_posLookup[h];
// Create connections with neighbour tiles. while (null != cur)
// Disconnect from other layers in current tile.
List<DtMeshTile> nneis = GetTilesAt(tile.data.header.x, tile.data.header.y);
foreach (DtMeshTile j in nneis)
{ {
if (j == tile) if (cur == tile)
{ {
continue; if (null != prev)
prev.next = cur.next;
else
m_posLookup[h] = cur.next;
break;
} }
UnconnectLinks(j, tile); prev = cur;
cur = cur.next;
}
// Remove connections to neighbour tiles.
const int MAX_NEIS = 32;
DtMeshTile[] neis = new DtMeshTile[MAX_NEIS];
int nneis = 0;
// Disconnect from other layers in current tile.
nneis = GetTilesAt(tile.data.header.x, tile.data.header.y, neis, MAX_NEIS);
for (int j = 0; j < nneis; ++j)
{
if (neis[j] == tile) continue;
UnconnectLinks(neis[j], tile);
} }
// Disconnect from neighbour tiles. // Disconnect from neighbour tiles.
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
nneis = GetNeighbourTilesAt(tile.data.header.x, tile.data.header.y, i); nneis = GetNeighbourTilesAt(tile.data.header.x, tile.data.header.y, i, neis, MAX_NEIS);
foreach (DtMeshTile j in nneis) for (int j = 0; j < nneis; ++j)
{ {
UnconnectLinks(j, tile); UnconnectLinks(neis[j], tile);
} }
} }
@ -557,7 +590,8 @@ namespace DotRecast.Detour
} }
// Add to free list. // Add to free list.
m_nextFree.AddFirst(tile); tile.next = m_nextFree;
m_nextFree = tile;
m_tileCount--; m_tileCount--;
return GetTileRef(tile); return GetTileRef(tile);
} }
@ -1270,19 +1304,27 @@ namespace DotRecast.Detour
DtMeshTile GetTileAt(int x, int y, int layer) DtMeshTile GetTileAt(int x, int y, int layer)
{ {
foreach (DtMeshTile tile in GetTileListByPos(x, y)) // Find tile based on hash.
int h = ComputeTileHash(x, y, m_tileLutMask);
DtMeshTile tile = m_posLookup[h];
while (null != tile)
{ {
if (tile.data.header != null && tile.data.header.x == x && tile.data.header.y == y if (null != tile.data &&
&& tile.data.header.layer == layer) null != tile.data.header &&
tile.data.header.x == x &&
tile.data.header.y == y &&
tile.data.header.layer == layer)
{ {
return tile; return tile;
} }
tile = tile.next;
} }
return null; return null;
} }
List<DtMeshTile> GetNeighbourTilesAt(int x, int y, int side) int GetNeighbourTilesAt(int x, int y, int side, DtMeshTile[] tiles, int maxTiles)
{ {
int nx = x, ny = y; int nx = x, ny = y;
switch (side) switch (side)
@ -1317,21 +1359,31 @@ namespace DotRecast.Detour
break; break;
} }
return GetTilesAt(nx, ny); return GetTilesAt(nx, ny, tiles, maxTiles);
} }
public List<DtMeshTile> GetTilesAt(int x, int y) public int GetTilesAt(int x, int y, DtMeshTile[] tiles, int maxTiles)
{ {
List<DtMeshTile> tiles = new List<DtMeshTile>(); int n = 0;
foreach (DtMeshTile tile in GetTileListByPos(x, y))
// Find tile based on hash.
int h = ComputeTileHash(x, y, m_tileLutMask);
DtMeshTile tile = m_posLookup[h];
while (null != tile)
{ {
if (tile.data.header != null && tile.data.header.x == x && tile.data.header.y == y) if (null != tile.data &&
null != tile.data.header &&
tile.data.header.x == x &&
tile.data.header.y == y)
{ {
tiles.Add(tile); if (n < maxTiles)
tiles[n++] = tile;
} }
tile = tile.next;
} }
return tiles; return n;
} }
public long GetTileRefAt(int x, int y, int layer) public long GetTileRefAt(int x, int y, int layer)
@ -1453,9 +1505,9 @@ namespace DotRecast.Detour
return m_tileCount; return m_tileCount;
} }
public int GetAvailableTileCount() public bool IsAvailableTileCount()
{ {
return m_nextFree.Count; return null != m_nextFree;
} }
public DtStatus SetPolyFlags(long refs, int flags) public DtStatus SetPolyFlags(long refs, int flags)
@ -1613,18 +1665,6 @@ namespace DotRecast.Detour
return center; return center;
} }
private List<DtMeshTile> GetTileListByPos(int x, int z)
{
var tileHash = ComputeTileHash(x, z, m_tileLutMask);
if (!m_posLookup.TryGetValue(tileHash, out var tiles))
{
tiles = new List<DtMeshTile>();
m_posLookup.Add(tileHash, tiles);
}
return tiles;
}
public void ComputeBounds(out RcVec3f bmin, out RcVec3f bmax) public void ComputeBounds(out RcVec3f bmin, out RcVec3f bmax)
{ {
bmin = new RcVec3f(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); bmin = new RcVec3f(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);

View File

@ -764,39 +764,27 @@ namespace DotRecast.Detour
// Find tiles the query touches. // Find tiles the query touches.
RcVec3f bmin = RcVec3f.Subtract(center, halfExtents); RcVec3f bmin = RcVec3f.Subtract(center, halfExtents);
RcVec3f bmax = RcVec3f.Add(center, halfExtents); RcVec3f bmax = RcVec3f.Add(center, halfExtents);
foreach (var t in QueryTiles(center, halfExtents))
{
QueryPolygonsInTile(t, bmin, bmax, filter, query);
}
return DtStatus.DT_SUCCESS; // Find tiles the query touches.
}
/**
* Finds tiles that overlap the search box.
*/
public IList<DtMeshTile> QueryTiles(RcVec3f center, RcVec3f halfExtents)
{
if (!center.IsFinite() || !halfExtents.IsFinite())
{
return RcImmutableArray<DtMeshTile>.Empty;
}
RcVec3f bmin = RcVec3f.Subtract(center, halfExtents);
RcVec3f bmax = RcVec3f.Add(center, halfExtents);
m_nav.CalcTileLoc(bmin, out var minx, out var miny); m_nav.CalcTileLoc(bmin, out var minx, out var miny);
m_nav.CalcTileLoc(bmax, out var maxx, out var maxy); m_nav.CalcTileLoc(bmax, out var maxx, out var maxy);
List<DtMeshTile> tiles = new List<DtMeshTile>(); const int MAX_NEIS = 32;
DtMeshTile[] neis = new DtMeshTile[MAX_NEIS];
for (int y = miny; y <= maxy; ++y) for (int y = miny; y <= maxy; ++y)
{ {
for (int x = minx; x <= maxx; ++x) for (int x = minx; x <= maxx; ++x)
{ {
tiles.AddRange(m_nav.GetTilesAt(x, y)); int nneis = m_nav.GetTilesAt(x, y, neis, MAX_NEIS);
for (int j = 0; j < nneis; ++j)
{
QueryPolygonsInTile(neis[j], bmin, bmax, filter, query);
}
} }
} }
return tiles; return DtStatus.DT_SUCCESS;
} }
/// @par /// @par

View File

@ -68,8 +68,8 @@ namespace DotRecast.Recast.Toolset.Tools
tileTriCount = 0; // ... tileTriCount = 0; // ...
tileMemUsage = 0; // ... tileMemUsage = 0; // ...
var availableTileCount = navMesh.GetAvailableTileCount(); var availableTile = navMesh.IsAvailableTileCount();
if (0 >= availableTileCount) if (!availableTile)
{ {
return false; return false;
} }

View File

@ -24,7 +24,6 @@ using NUnit.Framework;
namespace DotRecast.Detour.Test.Io; namespace DotRecast.Detour.Test.Io;
public class MeshSetReaderTest public class MeshSetReaderTest
{ {
private readonly DtMeshSetReader reader = new DtMeshSetReader(); private readonly DtMeshSetReader reader = new DtMeshSetReader();
@ -39,20 +38,28 @@ public class MeshSetReaderTest
Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128));
Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000));
Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f));
List<DtMeshTile> tiles = mesh.GetTilesAt(4, 7);
Assert.That(tiles.Count, Is.EqualTo(1)); const int MAX_NEIS = 32;
DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS];
int nneis = 0;
nneis = mesh.GetTilesAt(4, 7, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(7)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(7));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(22 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(22 * 3));
tiles = mesh.GetTilesAt(1, 6);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(1, 6, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(7)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(7));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(26 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(26 * 3));
tiles = mesh.GetTilesAt(6, 2);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(6, 2, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(1));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(4 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(4 * 3));
tiles = mesh.GetTilesAt(7, 6);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(7, 6, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(8)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(8));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(24 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(24 * 3));
} }
@ -68,20 +75,28 @@ public class MeshSetReaderTest
Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128));
Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000));
Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f));
List<DtMeshTile> tiles = mesh.GetTilesAt(6, 9);
Assert.That(tiles.Count, Is.EqualTo(1)); const int MAX_NEIS = 32;
DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS];
int nneis = 0;
nneis = mesh.GetTilesAt(6, 9, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3));
tiles = mesh.GetTilesAt(2, 9);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(2, 9, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3));
tiles = mesh.GetTilesAt(4, 3);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(4, 3, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3));
tiles = mesh.GetTilesAt(2, 8);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(2, 8, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3));
} }
@ -97,21 +112,29 @@ public class MeshSetReaderTest
Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128));
Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000));
Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f));
List<DtMeshTile> tiles = mesh.GetTilesAt(6, 9);
Assert.That(tiles.Count, Is.EqualTo(1)); const int MAX_NEIS = 32;
DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS];
int nneis = 0;
nneis = mesh.GetTilesAt(6, 9, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3));
tiles = mesh.GetTilesAt(2, 9);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(2, 9, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3));
tiles = mesh.GetTilesAt(4, 3);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(4, 3, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3));
tiles = mesh.GetTilesAt(2, 8);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(2, 8, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3));
} }
} }

View File

@ -107,20 +107,28 @@ public class MeshSetReaderWriterTest
Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128));
Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000));
Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f));
List<DtMeshTile> tiles = mesh.GetTilesAt(6, 9);
Assert.That(tiles.Count, Is.EqualTo(1)); const int MAX_NEIS = 32;
DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS];
int nneis = 0;
nneis = mesh.GetTilesAt(6, 9, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3));
tiles = mesh.GetTilesAt(2, 9);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(2, 9, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3));
tiles = mesh.GetTilesAt(4, 3);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(4, 3, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3));
tiles = mesh.GetTilesAt(2, 8);
Assert.That(tiles.Count, Is.EqualTo(1)); nneis = mesh.GetTilesAt(2, 8, tiles, MAX_NEIS);
Assert.That(nneis, Is.EqualTo(1));
Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3));
} }

View File

@ -26,7 +26,6 @@ using NUnit.Framework;
namespace DotRecast.Detour.TileCache.Test; namespace DotRecast.Detour.TileCache.Test;
public class TempObstaclesTest : AbstractTileCacheTest public class TempObstaclesTest : AbstractTileCacheTest
{ {
[Test] [Test]
@ -43,21 +42,29 @@ public class TempObstaclesTest : AbstractTileCacheTest
tc.BuildNavMeshTile(refs); tc.BuildNavMeshTile(refs);
} }
List<DtMeshTile> tiles = tc.GetNavMesh().GetTilesAt(1, 4); const int MAX_NEIS = 32;
DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS];
int nneis = 0;
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
DtMeshTile tile = tiles[0]; DtMeshTile tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.vertCount, Is.EqualTo(16));
Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6));
long o = tc.AddObstacle(new RcVec3f(-1.815208f, 9.998184f, -20.307983f), 1f, 2f); long o = tc.AddObstacle(new RcVec3f(-1.815208f, 9.998184f, -20.307983f), 1f, 2f);
bool upToDate = tc.Update(); bool upToDate = tc.Update();
Assert.That(upToDate, Is.True); Assert.That(upToDate, Is.True);
tiles = tc.GetNavMesh().GetTilesAt(1, 4);
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
tile = tiles[0]; tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(22)); Assert.That(tile.data.header.vertCount, Is.EqualTo(22));
Assert.That(tile.data.header.polyCount, Is.EqualTo(11)); Assert.That(tile.data.header.polyCount, Is.EqualTo(11));
tc.RemoveObstacle(o); tc.RemoveObstacle(o);
upToDate = tc.Update(); upToDate = tc.Update();
Assert.That(upToDate, Is.True); Assert.That(upToDate, Is.True);
tiles = tc.GetNavMesh().GetTilesAt(1, 4);
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
tile = tiles[0]; tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.vertCount, Is.EqualTo(16));
Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6));
@ -77,24 +84,32 @@ public class TempObstaclesTest : AbstractTileCacheTest
tc.BuildNavMeshTile(refs); tc.BuildNavMeshTile(refs);
} }
List<DtMeshTile> tiles = tc.GetNavMesh().GetTilesAt(1, 4); const int MAX_NEIS = 32;
DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS];
int nneis = 0;
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
DtMeshTile tile = tiles[0]; DtMeshTile tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.vertCount, Is.EqualTo(16));
Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6));
long o = tc.AddBoxObstacle( long o = tc.AddBoxObstacle(
new RcVec3f(-2.315208f, 9.998184f, -20.807983f), new RcVec3f(-2.315208f, 9.998184f, -20.807983f),
new RcVec3f(-1.315208f, 11.998184f, -19.807983f) new RcVec3f(-1.315208f, 11.998184f, -19.807983f)
); );
bool upToDate = tc.Update(); bool upToDate = tc.Update();
Assert.That(upToDate, Is.True); Assert.That(upToDate, Is.True);
tiles = tc.GetNavMesh().GetTilesAt(1, 4);
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
tile = tiles[0]; tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(22)); Assert.That(tile.data.header.vertCount, Is.EqualTo(22));
Assert.That(tile.data.header.polyCount, Is.EqualTo(11)); Assert.That(tile.data.header.polyCount, Is.EqualTo(11));
tc.RemoveObstacle(o); tc.RemoveObstacle(o);
upToDate = tc.Update(); upToDate = tc.Update();
Assert.That(upToDate, Is.True); Assert.That(upToDate, Is.True);
tiles = tc.GetNavMesh().GetTilesAt(1, 4);
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
tile = tiles[0]; tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.vertCount, Is.EqualTo(16));
Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6));