add DtTileCacheLayerBuilder

This commit is contained in:
ikpil 2023-08-19 12:48:47 +09:00
parent 53505fe13d
commit 6450704a8f
10 changed files with 217 additions and 217 deletions

View File

@ -1,78 +0,0 @@
/*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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.
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DotRecast.Core;
namespace DotRecast.Detour.TileCache
{
public abstract class AbstractTileLayersBuilder
{
protected List<byte[]> Build(RcByteOrder order, bool cCompatibility, int threads, int tw, int th)
{
if (threads == 1)
{
return BuildSingleThread(order, cCompatibility, tw, th);
}
return BuildMultiThread(order, cCompatibility, tw, th, threads);
}
private List<byte[]> BuildSingleThread(RcByteOrder order, bool cCompatibility, int tw, int th)
{
List<byte[]> layers = new List<byte[]>();
for (int y = 0; y < th; ++y)
{
for (int x = 0; x < tw; ++x)
{
layers.AddRange(Build(x, y, order, cCompatibility));
}
}
return layers;
}
private List<byte[]> BuildMultiThread(RcByteOrder order, bool cCompatibility, int tw, int th, int threads)
{
var results = new List<DtTileCacheBuildResult>();
for (int y = 0; y < th; ++y)
{
for (int x = 0; x < tw; ++x)
{
int tx = x;
int ty = y;
var task = Task.Run(() => Build(tx, ty, order, cCompatibility));
results.Add(new DtTileCacheBuildResult(tx, ty, task));
}
}
return results
.SelectMany(x => x.task.Result)
.ToList();
}
protected abstract List<byte[]> Build(int tx, int ty, RcByteOrder order, bool cCompatibility);
}
}

View File

@ -1935,11 +1935,11 @@ namespace DotRecast.Detour.TileCache
public byte[] CompressTileCacheLayer(DtTileCacheLayerHeader header, int[] heights, int[] areas, int[] cons, RcByteOrder order, bool cCompatibility, IRcCompressor comp)
{
using var ms = new MemoryStream();
using var baos = new BinaryWriter(ms);
using var bw = new BinaryWriter(ms);
DtTileCacheLayerHeaderWriter hw = new DtTileCacheLayerHeaderWriter();
try
{
hw.Write(baos, header, order, cCompatibility);
hw.Write(bw, header, order, cCompatibility);
int gridSize = header.width * header.height;
byte[] buffer = new byte[gridSize * 3];
for (int i = 0; i < gridSize; i++)
@ -1950,7 +1950,7 @@ namespace DotRecast.Detour.TileCache
}
var compressed = comp.Compress(buffer);
baos.Write(compressed);
bw.Write(compressed);
return ms.ToArray();
}
catch (IOException e)

View File

@ -0,0 +1,139 @@
/*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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.
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DotRecast.Core;
using DotRecast.Detour.TileCache.Io.Compress;
using DotRecast.Recast;
using DotRecast.Recast.Geom;
namespace DotRecast.Detour.TileCache
{
public class DtTileCacheLayerBuilder
{
private IDtTileCacheCompressorFactory _compFactory;
public DtTileCacheLayerBuilder(IDtTileCacheCompressorFactory compFactory)
{
_compFactory = compFactory;
}
public List<byte[]> Build(IInputGeomProvider geom, RcConfig cfg, RcByteOrder order, bool cCompatibility, int threads, int tw, int th)
{
if (threads == 1)
{
return BuildSingleThread(geom, cfg, order, cCompatibility, tw, th);
}
return BuildMultiThread(geom, cfg, order, cCompatibility, tw, th, threads);
}
private List<byte[]> BuildSingleThread(IInputGeomProvider geom, RcConfig cfg, RcByteOrder order, bool cCompatibility, int tw, int th)
{
List<byte[]> layers = new List<byte[]>();
for (int y = 0; y < th; ++y)
{
for (int x = 0; x < tw; ++x)
{
var list = BuildTileCacheLayer(geom, cfg, x, y, order, cCompatibility);
layers.AddRange(list);
}
}
return layers;
}
private List<byte[]> BuildMultiThread(IInputGeomProvider geom, RcConfig cfg, RcByteOrder order, bool cCompatibility, int tw, int th, int threads)
{
var results = new List<DtTileCacheBuildResult>();
for (int y = 0; y < th; ++y)
{
for (int x = 0; x < tw; ++x)
{
int tx = x;
int ty = y;
var task = Task.Run(() => BuildTileCacheLayer(geom, cfg, tx, ty, order, cCompatibility));
results.Add(new DtTileCacheBuildResult(tx, ty, task));
}
}
return results
.SelectMany(x => x.task.Result)
.ToList();
}
protected virtual RcHeightfieldLayerSet BuildHeightfieldLayerSet(IInputGeomProvider geom, RcConfig cfg, int tx, int ty)
{
RecastBuilder rcBuilder = new RecastBuilder();
RcVec3f bmin = geom.GetMeshBoundsMin();
RcVec3f bmax = geom.GetMeshBoundsMax();
RecastBuilderConfig builderCfg = new RecastBuilderConfig(cfg, bmin, bmax, tx, ty);
RcHeightfieldLayerSet lset = rcBuilder.BuildLayers(geom, builderCfg);
return lset;
}
protected virtual List<byte[]> BuildTileCacheLayer(IInputGeomProvider geom, RcConfig cfg, int tx, int ty, RcByteOrder order, bool cCompatibility)
{
RcHeightfieldLayerSet lset = BuildHeightfieldLayerSet(geom, cfg, tx, ty);
List<byte[]> result = new List<byte[]>();
if (lset != null)
{
DtTileCacheBuilder builder = new DtTileCacheBuilder();
for (int i = 0; i < lset.layers.Length; ++i)
{
RcHeightfieldLayer layer = lset.layers[i];
// Store header
DtTileCacheLayerHeader header = new DtTileCacheLayerHeader();
header.magic = DtTileCacheLayerHeader.DT_TILECACHE_MAGIC;
header.version = DtTileCacheLayerHeader.DT_TILECACHE_VERSION;
// Tile layer location in the navmesh.
header.tx = tx;
header.ty = ty;
header.tlayer = i;
header.bmin = layer.bmin;
header.bmax = layer.bmax;
// Tile info.
header.width = layer.width;
header.height = layer.height;
header.minx = layer.minx;
header.maxx = layer.maxx;
header.miny = layer.miny;
header.maxy = layer.maxy;
header.hmin = layer.hmin;
header.hmax = layer.hmax;
var comp = _compFactory.Get(cCompatibility);
var bytes = builder.CompressTileCacheLayer(header, layer.heights, layer.areas, layer.cons, order, cCompatibility, comp);
result.Add(bytes);
}
}
return result;
}
}
}

View File

@ -24,27 +24,17 @@ namespace DotRecast.Detour.TileCache
{
public class DtTileCacheLayerHeader
{
public const int DT_TILECACHE_MAGIC = 'D' << 24 | 'T' << 16 | 'L' << 8 | 'R';
/// < 'DTLR';
public const int DT_TILECACHE_MAGIC = 'D' << 24 | 'T' << 16 | 'L' << 8 | 'R'; // < 'DTLR';
public const int DT_TILECACHE_VERSION = 1;
public int magic;
/// < Data magic
public int version;
/// < Data version
public int magic; // < Data magic
public int version; // < Data version
public int tx, ty, tlayer;
public RcVec3f bmin = new RcVec3f();
public RcVec3f bmax = new RcVec3f();
public int hmin, hmax;
/// < Height min/max range
public int width, height;
/// < Dimension of the layer.
public int minx, maxx, miny, maxy; /// < Usable sub-region.
public int hmin, hmax; // < Height min/max range
public int width, height; // < Dimension of the layer.
public int minx, maxx, miny, maxy; // < Usable sub-region.
}
}

View File

@ -45,54 +45,54 @@ namespace DotRecast.Recast
public readonly float WalkableSlopeAngle;
/**
* Minimum floor to 'ceiling' height that will still allow the floor area to be considered walkable. [Limit: &gt;= 3]
* [Units: vx]
**/
* Minimum floor to 'ceiling' height that will still allow the floor area to be considered walkable. [Limit: &gt;= 3]
* [Units: vx]
**/
public readonly int WalkableHeight;
/** Maximum ledge height that is considered to still be traversable. [Limit: &gt;=0] [Units: vx] **/
public readonly int WalkableClimb;
/**
* The distance to erode/shrink the walkable area of the heightfield away from obstructions. [Limit: &gt;=0] [Units:
* vx]
**/
* The distance to erode/shrink the walkable area of the heightfield away from obstructions. [Limit: &gt;=0] [Units:
* vx]
**/
public readonly int WalkableRadius;
/** The maximum allowed length for contour edges along the border of the mesh. [Limit: &gt;=0] [Units: vx] **/
public readonly int MaxEdgeLen;
/**
* The maximum distance a simplfied contour's border edges should deviate the original raw contour. [Limit: &gt;=0]
* [Units: vx]
**/
* The maximum distance a simplfied contour's border edges should deviate the original raw contour. [Limit: &gt;=0]
* [Units: vx]
**/
public readonly float MaxSimplificationError;
/** The minimum number of cells allowed to form isolated island areas. [Limit: &gt;=0] [Units: vx] **/
public readonly int MinRegionArea;
/**
* Any regions with a span count smaller than this value will, if possible, be merged with larger regions. [Limit:
* &gt;=0] [Units: vx]
**/
* Any regions with a span count smaller than this value will, if possible, be merged with larger regions. [Limit:
* &gt;=0] [Units: vx]
**/
public readonly int MergeRegionArea;
/**
* The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process.
* [Limit: &gt;= 3]
**/
* The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process.
* [Limit: &gt;= 3]
**/
public readonly int MaxVertsPerPoly;
/**
* Sets the sampling distance to use when generating the detail mesh. (For height detail only.) [Limits: 0 or >=
* 0.9] [Units: wu]
**/
* Sets the sampling distance to use when generating the detail mesh. (For height detail only.) [Limits: 0 or >=
* 0.9] [Units: wu]
**/
public readonly float DetailSampleDist;
/**
* The maximum distance the detail mesh surface should deviate from heightfield data. (For height detail only.)
* [Limit: &gt;=0] [Units: wu]
**/
* The maximum distance the detail mesh surface should deviate from heightfield data. (For height detail only.)
* [Limit: &gt;=0] [Units: wu]
**/
public readonly float DetailSampleMaxError;
public readonly RcAreaModification WalkableAreaMod;
@ -116,8 +116,8 @@ namespace DotRecast.Recast
public readonly float MaxEdgeLenWorld;
/**
* Non-tiled build configuration
*/
* Non-tiled build configuration
*/
public RcConfig(RcPartition partitionType, float cellSize, float cellHeight, float agentHeight, float agentRadius,
float agentMaxClimb, float agentMaxSlope, int regionMinSize, int regionMergeSize, float edgeMaxLen,
float edgeMaxError, int vertsPerPoly, float detailSampleDist, float detailSampleMaxError,
@ -128,8 +128,8 @@ namespace DotRecast.Recast
}
/**
* Non-tiled build configuration
*/
* Non-tiled build configuration
*/
public RcConfig(RcPartition partitionType, float cellSize, float cellHeight, float agentMaxSlope,
bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans, float agentHeight,
float agentRadius, float agentMaxClimb, int regionMinSize, int regionMergeSize, float edgeMaxLen, float edgeMaxError,

View File

@ -27,6 +27,6 @@ namespace DotRecast.Recast
/// @see rcAllocHeightfieldLayerSet, rcFreeHeightfieldLayerSet
public class RcHeightfieldLayerSet
{
public RcHeightfieldLayer[] layers; /// < The layers in the set. [Size: #nlayers]
public RcHeightfieldLayer[] layers; // < The layers in the set. [Size: #nlayers]
}
}

View File

@ -20,6 +20,7 @@ freely, subject to the following restrictions:
using System.Collections.Generic;
using DotRecast.Core;
using DotRecast.Detour.TileCache.Test.Io;
using DotRecast.Recast;
using DotRecast.Recast.Geom;
using NUnit.Framework;

View File

@ -20,110 +20,56 @@ freely, subject to the following restrictions:
using System.Collections.Generic;
using DotRecast.Core;
using DotRecast.Detour.TileCache.Io.Compress;
using DotRecast.Detour.TileCache.Test.Io;
using DotRecast.Recast;
using DotRecast.Recast.Geom;
namespace DotRecast.Detour.TileCache.Test;
public class TestTileLayerBuilder : AbstractTileLayersBuilder
public class TestTileLayerBuilder : DtTileCacheLayerBuilder
{
private const float m_cellSize = 0.3f;
private const float m_cellHeight = 0.2f;
private const float m_agentHeight = 2.0f;
private const float m_agentRadius = 0.6f;
private const float m_agentMaxClimb = 0.9f;
private const float m_agentMaxSlope = 45.0f;
private const int m_regionMinSize = 8;
private const int m_regionMergeSize = 20;
private const float m_regionMinArea = m_regionMinSize * m_regionMinSize * m_cellSize * m_cellSize;
private const float m_regionMergeArea = m_regionMergeSize * m_regionMergeSize * m_cellSize * m_cellSize;
private const float m_edgeMaxLen = 12.0f;
private const float m_edgeMaxError = 1.3f;
private const int m_vertsPerPoly = 6;
private const float m_detailSampleDist = 6.0f;
private const float m_detailSampleMaxError = 1.0f;
private readonly RcConfig rcConfig;
private const int m_tileSize = 48;
protected readonly IInputGeomProvider geom;
private readonly int tw;
private readonly int th;
private const float CellSize = 0.3f;
private const float CellHeight = 0.2f;
public TestTileLayerBuilder(IInputGeomProvider geom)
private const float AgentHeight = 2.0f;
private const float AgentRadius = 0.6f;
private const float AgentMaxClimb = 0.9f;
private const float AgentMaxSlope = 45.0f;
private const int RegionMinSize = 8;
private const int RegionMergeSize = 20;
private const float RegionMinArea = RegionMinSize * RegionMinSize * CellSize * CellSize;
private const float RegionMergeArea = RegionMergeSize * RegionMergeSize * CellSize * CellSize;
private const float EdgeMaxLen = 12.0f;
private const float EdgeMaxError = 1.3f;
private const int VertsPerPoly = 6;
private const float DetailSampleDist = 6.0f;
private const float DetailSampleMaxError = 1.0f;
private readonly RcConfig _cfg;
private const int m_tileSize = 48;
private readonly IInputGeomProvider _geom;
public readonly int tw;
public readonly int th;
public TestTileLayerBuilder(IInputGeomProvider geom) : base(DtTileCacheCompressorForTestFactory.Shared)
{
this.geom = geom;
rcConfig = new RcConfig(true, m_tileSize, m_tileSize, RcConfig.CalcBorder(m_agentRadius, m_cellSize),
RcPartition.WATERSHED, m_cellSize, m_cellHeight, m_agentMaxSlope, true, true, true, m_agentHeight,
m_agentRadius, m_agentMaxClimb, m_regionMinArea, m_regionMergeArea, m_edgeMaxLen, m_edgeMaxError, m_vertsPerPoly,
true, m_detailSampleDist, m_detailSampleMaxError, SampleAreaModifications.SAMPLE_AREAMOD_GROUND);
_geom = geom;
_cfg = new RcConfig(true, m_tileSize, m_tileSize, RcConfig.CalcBorder(AgentRadius, CellSize),
RcPartition.WATERSHED, CellSize, CellHeight, AgentMaxSlope, true, true, true, AgentHeight,
AgentRadius, AgentMaxClimb, RegionMinArea, RegionMergeArea, EdgeMaxLen, EdgeMaxError, VertsPerPoly,
true, DetailSampleDist, DetailSampleMaxError, SampleAreaModifications.SAMPLE_AREAMOD_GROUND);
RcVec3f bmin = geom.GetMeshBoundsMin();
RcVec3f bmax = geom.GetMeshBoundsMax();
Recast.RcUtils.CalcTileCount(bmin, bmax, m_cellSize, m_tileSize, m_tileSize, out tw, out th);
RcUtils.CalcTileCount(bmin, bmax, CellSize, m_tileSize, m_tileSize, out tw, out th);
}
public List<byte[]> Build(RcByteOrder order, bool cCompatibility, int threads)
{
return Build(order, cCompatibility, threads, tw, th);
}
public int GetTw()
{
return tw;
}
public int GetTh()
{
return th;
}
protected override List<byte[]> Build(int tx, int ty, RcByteOrder order, bool cCompatibility)
{
RcHeightfieldLayerSet lset = GetHeightfieldSet(tx, ty);
List<byte[]> result = new();
if (lset != null)
{
DtTileCacheBuilder builder = new DtTileCacheBuilder();
for (int i = 0; i < lset.layers.Length; ++i)
{
RcHeightfieldLayer layer = lset.layers[i];
// Store header
DtTileCacheLayerHeader header = new DtTileCacheLayerHeader();
header.magic = DtTileCacheLayerHeader.DT_TILECACHE_MAGIC;
header.version = DtTileCacheLayerHeader.DT_TILECACHE_VERSION;
// Tile layer location in the navmesh.
header.tx = tx;
header.ty = ty;
header.tlayer = i;
header.bmin = layer.bmin;
header.bmax = layer.bmax;
// Tile info.
header.width = layer.width;
header.height = layer.height;
header.minx = layer.minx;
header.maxx = layer.maxx;
header.miny = layer.miny;
header.maxy = layer.maxy;
header.hmin = layer.hmin;
header.hmax = layer.hmax;
var comp = DtTileCacheCompressorForTestFactory.Shared.Get(cCompatibility);
result.Add(builder.CompressTileCacheLayer(header, layer.heights, layer.areas, layer.cons, order, cCompatibility, comp));
}
}
return result;
}
protected RcHeightfieldLayerSet GetHeightfieldSet(int tx, int ty)
{
RecastBuilder rcBuilder = new RecastBuilder();
RcVec3f bmin = geom.GetMeshBoundsMin();
RcVec3f bmax = geom.GetMeshBoundsMax();
RecastBuilderConfig cfg = new RecastBuilderConfig(rcConfig, bmin, bmax, tx, ty);
RcHeightfieldLayerSet lset = rcBuilder.BuildLayers(geom, cfg);
return lset;
return Build(_geom, _cfg, order, cCompatibility, threads, tw, th);
}
}

View File

@ -64,9 +64,9 @@ public class TileCacheNavigationTest : AbstractTileCacheTest
tc.AddTile(data, 0);
}
for (int y = 0; y < layerBuilder.GetTh(); ++y)
for (int y = 0; y < layerBuilder.th; ++y)
{
for (int x = 0; x < layerBuilder.GetTw(); ++x)
for (int x = 0; x < layerBuilder.tw; ++x)
{
foreach (long refs in tc.GetTilesAt(x, y))
{

View File

@ -178,10 +178,12 @@ public class TileCacheTest : AbstractTileCacheTest
[Test]
public void TestPerformance()
{
int threads = 4;
int threads = Environment.ProcessorCount;
RcByteOrder order = RcByteOrder.LITTLE_ENDIAN;
bool cCompatibility = false;
IInputGeomProvider geom = ObjImporter.Load(Loader.ToBytes("dungeon.obj"));
var objBytes = Loader.ToBytes("dungeon.obj");
IInputGeomProvider geom = ObjImporter.Load(objBytes);
TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom);
for (int i = 0; i < 4; i++)
{