diff --git a/src/DotRecast.Detour.TileCache/AbstractTileLayersBuilder.cs b/src/DotRecast.Detour.TileCache/AbstractTileLayersBuilder.cs deleted file mode 100644 index 112a592..0000000 --- a/src/DotRecast.Detour.TileCache/AbstractTileLayersBuilder.cs +++ /dev/null @@ -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 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 BuildSingleThread(RcByteOrder order, bool cCompatibility, int tw, int th) - { - List layers = new List(); - 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 BuildMultiThread(RcByteOrder order, bool cCompatibility, int tw, int th, int threads) - { - var results = new List(); - 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 Build(int tx, int ty, RcByteOrder order, bool cCompatibility); - } -} \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheBuilder.cs b/src/DotRecast.Detour.TileCache/DtTileCacheBuilder.cs index 6169a94..a5bfc06 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheBuilder.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheBuilder.cs @@ -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) diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuilder.cs b/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuilder.cs new file mode 100644 index 0000000..5f2a50b --- /dev/null +++ b/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuilder.cs @@ -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 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 BuildSingleThread(IInputGeomProvider geom, RcConfig cfg, RcByteOrder order, bool cCompatibility, int tw, int th) + { + List layers = new List(); + 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 BuildMultiThread(IInputGeomProvider geom, RcConfig cfg, RcByteOrder order, bool cCompatibility, int tw, int th, int threads) + { + var results = new List(); + 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 BuildTileCacheLayer(IInputGeomProvider geom, RcConfig cfg, int tx, int ty, RcByteOrder order, bool cCompatibility) + { + RcHeightfieldLayerSet lset = BuildHeightfieldLayerSet(geom, cfg, tx, ty); + List result = new List(); + 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; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheLayerHeader.cs b/src/DotRecast.Detour.TileCache/DtTileCacheLayerHeader.cs index 818f4e4..fd4a5c0 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheLayerHeader.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheLayerHeader.cs @@ -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. } } \ No newline at end of file diff --git a/src/DotRecast.Recast/RcConfig.cs b/src/DotRecast.Recast/RcConfig.cs index 30baf8a..da80445 100644 --- a/src/DotRecast.Recast/RcConfig.cs +++ b/src/DotRecast.Recast/RcConfig.cs @@ -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: >= 3] - * [Units: vx] - **/ + * Minimum floor to 'ceiling' height that will still allow the floor area to be considered walkable. [Limit: >= 3] + * [Units: vx] + **/ public readonly int WalkableHeight; /** Maximum ledge height that is considered to still be traversable. [Limit: >=0] [Units: vx] **/ public readonly int WalkableClimb; /** - * The distance to erode/shrink the walkable area of the heightfield away from obstructions. [Limit: >=0] [Units: - * vx] - **/ + * The distance to erode/shrink the walkable area of the heightfield away from obstructions. [Limit: >=0] [Units: + * vx] + **/ public readonly int WalkableRadius; /** The maximum allowed length for contour edges along the border of the mesh. [Limit: >=0] [Units: vx] **/ public readonly int MaxEdgeLen; /** - * The maximum distance a simplfied contour's border edges should deviate the original raw contour. [Limit: >=0] - * [Units: vx] - **/ + * The maximum distance a simplfied contour's border edges should deviate the original raw contour. [Limit: >=0] + * [Units: vx] + **/ public readonly float MaxSimplificationError; /** The minimum number of cells allowed to form isolated island areas. [Limit: >=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: - * >=0] [Units: vx] - **/ + * Any regions with a span count smaller than this value will, if possible, be merged with larger regions. [Limit: + * >=0] [Units: vx] + **/ public readonly int MergeRegionArea; /** - * The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process. - * [Limit: >= 3] - **/ + * The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process. + * [Limit: >= 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: >=0] [Units: wu] - **/ + * The maximum distance the detail mesh surface should deviate from heightfield data. (For height detail only.) + * [Limit: >=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, diff --git a/src/DotRecast.Recast/RcHeightfieldLayerSet.cs b/src/DotRecast.Recast/RcHeightfieldLayerSet.cs index 27797d5..1f60102 100644 --- a/src/DotRecast.Recast/RcHeightfieldLayerSet.cs +++ b/src/DotRecast.Recast/RcHeightfieldLayerSet.cs @@ -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] } } \ No newline at end of file diff --git a/test/DotRecast.Detour.TileCache.Test/TempObstaclesTest.cs b/test/DotRecast.Detour.TileCache.Test/TempObstaclesTest.cs index 18206d3..24c984d 100644 --- a/test/DotRecast.Detour.TileCache.Test/TempObstaclesTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/TempObstaclesTest.cs @@ -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; diff --git a/test/DotRecast.Detour.TileCache.Test/TestTileLayerBuilder.cs b/test/DotRecast.Detour.TileCache.Test/TestTileLayerBuilder.cs index 0a42fe9..52e01f4 100644 --- a/test/DotRecast.Detour.TileCache.Test/TestTileLayerBuilder.cs +++ b/test/DotRecast.Detour.TileCache.Test/TestTileLayerBuilder.cs @@ -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 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 Build(int tx, int ty, RcByteOrder order, bool cCompatibility) - { - RcHeightfieldLayerSet lset = GetHeightfieldSet(tx, ty); - List 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); } } \ No newline at end of file diff --git a/test/DotRecast.Detour.TileCache.Test/TileCacheNavigationTest.cs b/test/DotRecast.Detour.TileCache.Test/TileCacheNavigationTest.cs index f7eb38f..bf08352 100644 --- a/test/DotRecast.Detour.TileCache.Test/TileCacheNavigationTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/TileCacheNavigationTest.cs @@ -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)) { diff --git a/test/DotRecast.Detour.TileCache.Test/TileCacheTest.cs b/test/DotRecast.Detour.TileCache.Test/TileCacheTest.cs index c334e39..bcf7d56 100644 --- a/test/DotRecast.Detour.TileCache.Test/TileCacheTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/TileCacheTest.cs @@ -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++) {