refactor: ObjImporter, InputGeomProvider

This commit is contained in:
ikpil 2023-09-24 18:42:41 +09:00
parent c294275da4
commit 229ab3f9f5
20 changed files with 56 additions and 69 deletions

View File

@ -1,5 +1,6 @@
/* /*
recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -17,24 +18,16 @@ freely, subject to the following restrictions:
*/ */
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using DotRecast.Recast.Geom;
namespace DotRecast.Recast namespace DotRecast.Core
{ {
public static class ObjImporter public static class RcObjImporter
{ {
public static IInputGeomProvider Load(byte[] chunk) public static RcObjImporterContext LoadContext(byte[] chunk)
{ {
var context = LoadContext(chunk); RcObjImporterContext context = new RcObjImporterContext();
return new SimpleInputGeomProvider(context.vertexPositions, context.meshFaces);
}
public static ObjImporterContext LoadContext(byte[] chunk)
{
ObjImporterContext context = new ObjImporterContext();
try try
{ {
using StreamReader reader = new StreamReader(new MemoryStream(chunk)); using StreamReader reader = new StreamReader(new MemoryStream(chunk));
@ -54,7 +47,7 @@ namespace DotRecast.Recast
} }
public static void ReadLine(string line, ObjImporterContext context) public static void ReadLine(string line, RcObjImporterContext context)
{ {
if (line.StartsWith("v")) if (line.StartsWith("v"))
{ {
@ -66,7 +59,7 @@ namespace DotRecast.Recast
} }
} }
private static void ReadVertex(string line, ObjImporterContext context) private static void ReadVertex(string line, RcObjImporterContext context)
{ {
if (line.StartsWith("v ")) if (line.StartsWith("v "))
{ {
@ -89,13 +82,13 @@ namespace DotRecast.Recast
// fix - https://github.com/ikpil/DotRecast/issues/7 // fix - https://github.com/ikpil/DotRecast/issues/7
return new float[] return new float[]
{ {
float.Parse(v[1], CultureInfo.InvariantCulture), float.Parse(v[1], CultureInfo.InvariantCulture),
float.Parse(v[2], CultureInfo.InvariantCulture), float.Parse(v[2], CultureInfo.InvariantCulture),
float.Parse(v[3], CultureInfo.InvariantCulture) float.Parse(v[3], CultureInfo.InvariantCulture)
}; };
} }
private static void ReadFace(string line, ObjImporterContext context) private static void ReadFace(string line, RcObjImporterContext context)
{ {
string[] v = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); string[] v = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (v.Length < 4) if (v.Length < 4)
@ -113,7 +106,7 @@ namespace DotRecast.Recast
} }
} }
private static int ReadFaceVertex(string face, ObjImporterContext context) private static int ReadFaceVertex(string face, RcObjImporterContext context)
{ {
string[] v = face.Split("/"); string[] v = face.Split("/");
return GetIndex(int.Parse(v[0]), context.vertexPositions.Count); return GetIndex(int.Parse(v[0]), context.vertexPositions.Count);

View File

@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Recast namespace DotRecast.Core
{ {
public class ObjImporterContext public class RcObjImporterContext
{ {
public List<float> vertexPositions = new List<float>(); public List<float> vertexPositions = new List<float>();
public List<int> meshFaces = new List<int>(); public List<int> meshFaces = new List<int>();

View File

@ -43,7 +43,6 @@ using DotRecast.Recast.Toolset.Geom;
using DotRecast.Recast.Demo.Tools; using DotRecast.Recast.Demo.Tools;
using DotRecast.Recast.Demo.UI; using DotRecast.Recast.Demo.UI;
using DotRecast.Recast.Toolset; using DotRecast.Recast.Toolset;
using MouseButton = Silk.NET.Input.MouseButton; using MouseButton = Silk.NET.Input.MouseButton;
using Window = Silk.NET.Windowing.Window; using Window = Silk.NET.Windowing.Window;
@ -298,9 +297,7 @@ public class RecastDemo : IRecastDemoChannel
private DemoInputGeomProvider LoadInputMesh(string filename) private DemoInputGeomProvider LoadInputMesh(string filename)
{ {
var bytes = RcResources.Load(filename); DemoInputGeomProvider geom = DemoInputGeomProvider.LoadFile(filename);
DemoInputGeomProvider geom = DemoObjImporter.Load(bytes);
_lastGeomFileName = filename; _lastGeomFileName = filename;
return geom; return geom;
} }

View File

@ -30,6 +30,7 @@ using DotRecast.Recast.Toolset.Gizmos;
using DotRecast.Recast.Toolset.Tools; using DotRecast.Recast.Toolset.Tools;
using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Demo.Draw;
using DotRecast.Recast.Demo.UI; using DotRecast.Recast.Demo.UI;
using DotRecast.Recast.Toolset.Geom;
using ImGuiNET; using ImGuiNET;
using Serilog; using Serilog;
using static DotRecast.Recast.Demo.Draw.DebugDraw; using static DotRecast.Recast.Demo.Draw.DebugDraw;
@ -88,9 +89,9 @@ public class DynamicUpdateSampleTool : ISampleTool
public DynamicUpdateSampleTool() public DynamicUpdateSampleTool()
{ {
var bridgeGeom = DemoObjImporter.Load(RcResources.Load("bridge.obj")); var bridgeGeom = DemoInputGeomProvider.LoadFile("bridge.obj");
var houseGeom = DemoObjImporter.Load(RcResources.Load("house.obj")); var houseGeom = DemoInputGeomProvider.LoadFile("house.obj");
var convexGeom = DemoObjImporter.Load(RcResources.Load("convex.obj")); var convexGeom = DemoInputGeomProvider.LoadFile("convex.obj");
_tool = new(Random.Shared, bridgeGeom, houseGeom, convexGeom); _tool = new(Random.Shared, bridgeGeom, houseGeom, convexGeom);
executor = Task.Factory; executor = Task.Factory;
} }

View File

@ -1,15 +0,0 @@
using System;
using System.IO;
using DotRecast.Recast.Toolset.Geom;
namespace DotRecast.Recast.Toolset
{
public static class DemoObjImporter
{
public static DemoInputGeomProvider Load(byte[] chunk)
{
var context = ObjImporter.LoadContext(chunk);
return new DemoInputGeomProvider(context.vertexPositions, context.meshFaces);
}
}
}

View File

@ -37,6 +37,13 @@ namespace DotRecast.Recast.Toolset.Geom
private readonly List<RcOffMeshConnection> _offMeshConnections = new List<RcOffMeshConnection>(); private readonly List<RcOffMeshConnection> _offMeshConnections = new List<RcOffMeshConnection>();
private readonly RcTriMesh _mesh; private readonly RcTriMesh _mesh;
public static DemoInputGeomProvider LoadFile(string objFilePath)
{
byte[] chunk = RcResources.Load(objFilePath);
var context = RcObjImporter.LoadContext(chunk);
return new DemoInputGeomProvider(context.vertexPositions, context.meshFaces);
}
public DemoInputGeomProvider(List<float> vertexPositions, List<int> meshFaces) : public DemoInputGeomProvider(List<float> vertexPositions, List<int> meshFaces) :
this(MapVertices(vertexPositions), MapFaces(meshFaces)) this(MapVertices(vertexPositions), MapFaces(meshFaces))
{ {

View File

@ -35,6 +35,13 @@ namespace DotRecast.Recast.Geom
private readonly List<RcConvexVolume> volumes = new List<RcConvexVolume>(); private readonly List<RcConvexVolume> volumes = new List<RcConvexVolume>();
private readonly RcTriMesh _mesh; private readonly RcTriMesh _mesh;
public static SimpleInputGeomProvider LoadFile(string objFilePath)
{
byte[] chunk = RcResources.Load(objFilePath);
var context = RcObjImporter.LoadContext(chunk);
return new SimpleInputGeomProvider(context.vertexPositions, context.meshFaces);
}
public SimpleInputGeomProvider(List<float> vertexPositions, List<int> meshFaces) public SimpleInputGeomProvider(List<float> vertexPositions, List<int> meshFaces)
: this(MapVertices(vertexPositions), MapFaces(meshFaces)) : this(MapVertices(vertexPositions), MapFaces(meshFaces))
{ {

View File

@ -1,6 +1,6 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public interface IRecastBuilderProgressListener public interface IRcBuilderProgressListener
{ {
void OnProgress(int completed, int total); void OnProgress(int completed, int total);
} }

View File

@ -32,16 +32,16 @@ namespace DotRecast.Recast
public class RcBuilder public class RcBuilder
{ {
private readonly IRecastBuilderProgressListener progressListener; private readonly IRcBuilderProgressListener _progressListener;
public RcBuilder() public RcBuilder()
{ {
progressListener = null; _progressListener = null;
} }
public RcBuilder(IRecastBuilderProgressListener progressListener) public RcBuilder(IRcBuilderProgressListener progressListener)
{ {
this.progressListener = progressListener; _progressListener = progressListener;
} }
public List<RcBuilderResult> BuildTiles(IInputGeomProvider geom, RcConfig cfg, TaskFactory taskFactory) public List<RcBuilderResult> BuildTiles(IInputGeomProvider geom, RcConfig cfg, TaskFactory taskFactory)
@ -150,9 +150,9 @@ namespace DotRecast.Recast
int ty, RcAtomicInteger counter, int total) int ty, RcAtomicInteger counter, int total)
{ {
RcBuilderResult result = Build(geom, new RcBuilderConfig(cfg, bmin, bmax, tx, ty)); RcBuilderResult result = Build(geom, new RcBuilderConfig(cfg, bmin, bmax, tx, ty));
if (progressListener != null) if (_progressListener != null)
{ {
progressListener.OnProgress(counter.IncrementAndGet(), total); _progressListener.OnProgress(counter.IncrementAndGet(), total);
} }
return result; return result;
@ -237,8 +237,7 @@ namespace DotRecast.Recast
// //
// Create contours. // Create contours.
RcContourSet cset = RcContours.BuildContours(ctx, chf, cfg.MaxSimplificationError, cfg.MaxEdgeLen, RcContourSet cset = RcContours.BuildContours(ctx, chf, cfg.MaxSimplificationError, cfg.MaxEdgeLen, RcConstants.RC_CONTOUR_TESS_WALL_EDGES);
RcConstants.RC_CONTOUR_TESS_WALL_EDGES);
// //
// Step 6. Build polygons mesh from contours. // Step 6. Build polygons mesh from contours.
@ -283,8 +282,7 @@ namespace DotRecast.Recast
/* /*
* Step 3. Partition walkable surface to simple regions. * Step 3. Partition walkable surface to simple regions.
*/ */
private RcCompactHeightfield BuildCompactHeightfield(IInputGeomProvider geom, RcConfig cfg, RcTelemetry ctx, private RcCompactHeightfield BuildCompactHeightfield(IInputGeomProvider geom, RcConfig cfg, RcTelemetry ctx, RcHeightfield solid)
RcHeightfield solid)
{ {
// Compact the heightfield so that it is faster to handle from now on. // Compact the heightfield so that it is faster to handle from now on.
// This will result more cache coherent data as well as the neighbours // This will result more cache coherent data as well as the neighbours

View File

@ -40,7 +40,7 @@ public class RecastTestMeshBuilder
public const float m_detailSampleMaxError = 1.0f; public const float m_detailSampleMaxError = 1.0f;
public RecastTestMeshBuilder() public RecastTestMeshBuilder()
: this(ObjImporter.Load(RcResources.Load("dungeon.obj")), : this(SimpleInputGeomProvider.LoadFile("dungeon.obj"),
RcPartition.WATERSHED, RcPartition.WATERSHED,
m_cellSize, m_cellHeight, m_cellSize, m_cellHeight,
m_agentMaxSlope, m_agentHeight, m_agentRadius, m_agentMaxClimb, m_agentMaxSlope, m_agentHeight, m_agentRadius, m_agentMaxClimb,

View File

@ -54,7 +54,7 @@ public class MeshSetReaderWriterTest
[Test] [Test]
public void Test() public void Test()
{ {
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load("dungeon.obj")); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj");
NavMeshSetHeader header = new NavMeshSetHeader(); NavMeshSetHeader header = new NavMeshSetHeader();
header.magic = NavMeshSetHeader.NAVMESHSET_MAGIC; header.magic = NavMeshSetHeader.NAVMESHSET_MAGIC;

View File

@ -40,7 +40,7 @@ public class RecastTestMeshBuilder
private const float m_detailSampleMaxError = 1.0f; private const float m_detailSampleMaxError = 1.0f;
public RecastTestMeshBuilder() public RecastTestMeshBuilder()
: this(ObjImporter.Load(RcResources.Load("dungeon.obj")), : this(SimpleInputGeomProvider.LoadFile("dungeon.obj"),
RcPartition.WATERSHED, RcPartition.WATERSHED,
m_cellSize, m_cellHeight, m_cellSize, m_cellHeight,
m_agentMaxSlope, m_agentHeight, m_agentRadius, m_agentMaxClimb, m_agentMaxSlope, m_agentHeight, m_agentRadius, m_agentMaxClimb,

View File

@ -45,7 +45,7 @@ public class TestTiledNavMeshBuilder
private const int m_tileSize = 32; private const int m_tileSize = 32;
public TestTiledNavMeshBuilder() : public TestTiledNavMeshBuilder() :
this(ObjImporter.Load(RcResources.Load("dungeon.obj")), this(SimpleInputGeomProvider.LoadFile("dungeon.obj"),
RcPartition.WATERSHED, m_cellSize, m_cellHeight, m_agentHeight, m_agentRadius, m_agentMaxClimb, m_agentMaxSlope, RcPartition.WATERSHED, m_cellSize, m_cellHeight, m_agentHeight, m_agentRadius, m_agentMaxClimb, m_agentMaxSlope,
m_regionMinSize, m_regionMergeSize, m_edgeMaxLen, m_edgeMaxError, m_vertsPerPoly, m_detailSampleDist, m_regionMinSize, m_regionMergeSize, m_edgeMaxLen, m_edgeMaxError, m_vertsPerPoly, m_detailSampleDist,
m_detailSampleMaxError, m_tileSize) m_detailSampleMaxError, m_tileSize)

View File

@ -51,7 +51,7 @@ public class TileCacheReaderWriterTest : AbstractTileCacheTest
private void TestDungeon(bool cCompatibility) private void TestDungeon(bool cCompatibility)
{ {
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load("dungeon.obj")); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj");
TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom); TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom);
List<byte[]> layers = layerBuilder.Build(RcByteOrder.LITTLE_ENDIAN, cCompatibility, 1); List<byte[]> layers = layerBuilder.Build(RcByteOrder.LITTLE_ENDIAN, cCompatibility, 1);
DtTileCache tc = GetTileCache(geom, RcByteOrder.LITTLE_ENDIAN, cCompatibility); DtTileCache tc = GetTileCache(geom, RcByteOrder.LITTLE_ENDIAN, cCompatibility);

View File

@ -34,7 +34,7 @@ public class TempObstaclesTest : AbstractTileCacheTest
public void TestDungeon() public void TestDungeon()
{ {
bool cCompatibility = true; bool cCompatibility = true;
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load("dungeon.obj")); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj");
TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom); TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom);
List<byte[]> layers = layerBuilder.Build(RcByteOrder.LITTLE_ENDIAN, cCompatibility, 1); List<byte[]> layers = layerBuilder.Build(RcByteOrder.LITTLE_ENDIAN, cCompatibility, 1);
DtTileCache tc = GetTileCache(geom, RcByteOrder.LITTLE_ENDIAN, cCompatibility); DtTileCache tc = GetTileCache(geom, RcByteOrder.LITTLE_ENDIAN, cCompatibility);
@ -68,7 +68,7 @@ public class TempObstaclesTest : AbstractTileCacheTest
public void TestDungeonBox() public void TestDungeonBox()
{ {
bool cCompatibility = true; bool cCompatibility = true;
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load("dungeon.obj")); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj");
TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom); TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom);
List<byte[]> layers = layerBuilder.Build(RcByteOrder.LITTLE_ENDIAN, cCompatibility, 1); List<byte[]> layers = layerBuilder.Build(RcByteOrder.LITTLE_ENDIAN, cCompatibility, 1);
DtTileCache tc = GetTileCache(geom, RcByteOrder.LITTLE_ENDIAN, cCompatibility); DtTileCache tc = GetTileCache(geom, RcByteOrder.LITTLE_ENDIAN, cCompatibility);

View File

@ -55,7 +55,7 @@ public class TileCacheNavigationTest : AbstractTileCacheTest
public void SetUp() public void SetUp()
{ {
bool cCompatibility = true; bool cCompatibility = true;
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load("dungeon.obj")); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj");
TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom); TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom);
List<byte[]> layers = layerBuilder.Build(RcByteOrder.LITTLE_ENDIAN, cCompatibility, 1); List<byte[]> layers = layerBuilder.Build(RcByteOrder.LITTLE_ENDIAN, cCompatibility, 1);
DtTileCache tc = GetTileCache(geom, RcByteOrder.LITTLE_ENDIAN, cCompatibility); DtTileCache tc = GetTileCache(geom, RcByteOrder.LITTLE_ENDIAN, cCompatibility);

View File

@ -59,7 +59,7 @@ public class TileCacheTest : AbstractTileCacheTest
private void TestDungeon(RcByteOrder order, bool cCompatibility) private void TestDungeon(RcByteOrder order, bool cCompatibility)
{ {
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load("dungeon.obj")); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj");
DtTileCache tc = GetTileCache(geom, order, cCompatibility); DtTileCache tc = GetTileCache(geom, order, cCompatibility);
TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom); TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom);
List<byte[]> layers = layerBuilder.Build(order, cCompatibility, 1); List<byte[]> layers = layerBuilder.Build(order, cCompatibility, 1);
@ -155,7 +155,7 @@ public class TileCacheTest : AbstractTileCacheTest
private void Test(RcByteOrder order, bool cCompatibility) private void Test(RcByteOrder order, bool cCompatibility)
{ {
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load("nav_test.obj")); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("nav_test.obj");
DtTileCache tc = GetTileCache(geom, order, cCompatibility); DtTileCache tc = GetTileCache(geom, order, cCompatibility);
TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom); TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom);
List<byte[]> layers = layerBuilder.Build(order, cCompatibility, 1); List<byte[]> layers = layerBuilder.Build(order, cCompatibility, 1);
@ -182,8 +182,7 @@ public class TileCacheTest : AbstractTileCacheTest
RcByteOrder order = RcByteOrder.LITTLE_ENDIAN; RcByteOrder order = RcByteOrder.LITTLE_ENDIAN;
bool cCompatibility = false; bool cCompatibility = false;
var objBytes = RcResources.Load("dungeon.obj"); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj");
IInputGeomProvider geom = ObjImporter.Load(objBytes);
TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom); TestTileLayerBuilder layerBuilder = new TestTileLayerBuilder(geom);
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {

View File

@ -145,7 +145,7 @@ public class RecastLayersTest
private RcHeightfieldLayerSet Build(string filename, int x, int y) private RcHeightfieldLayerSet Build(string filename, int x, int y)
{ {
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load(filename)); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile(filename);
RcBuilder builder = new RcBuilder(); RcBuilder builder = new RcBuilder();
RcConfig cfg = new RcConfig(true, m_tileSize, m_tileSize, RcConfig cfg = new RcConfig(true, m_tileSize, m_tileSize,
RcConfig.CalcBorder(m_agentRadius, m_cellSize), RcConfig.CalcBorder(m_agentRadius, m_cellSize),

View File

@ -97,7 +97,7 @@ public class RecastSoloMeshTest
int expContours, int expVerts, int expPolys, int expDetMeshes, int expDetVerts, int expDetTris) int expContours, int expVerts, int expPolys, int expDetMeshes, int expDetVerts, int expDetTris)
{ {
m_partitionType = partitionType; m_partitionType = partitionType;
IInputGeomProvider geomProvider = ObjImporter.Load(RcResources.Load(filename)); IInputGeomProvider geomProvider = SimpleInputGeomProvider.LoadFile(filename);
long time = RcFrequency.Ticks; long time = RcFrequency.Ticks;
RcVec3f bmin = geomProvider.GetMeshBoundsMin(); RcVec3f bmin = geomProvider.GetMeshBoundsMin();
RcVec3f bmax = geomProvider.GetMeshBoundsMax(); RcVec3f bmax = geomProvider.GetMeshBoundsMax();

View File

@ -58,7 +58,7 @@ public class RecastTileMeshTest
public void TestBuild(string filename) public void TestBuild(string filename)
{ {
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load(filename)); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile(filename);
RcBuilder builder = new RcBuilder(); RcBuilder builder = new RcBuilder();
RcConfig cfg = new RcConfig( RcConfig cfg = new RcConfig(
true, m_tileSize, m_tileSize, RcConfig.CalcBorder(m_agentRadius, m_cellSize), true, m_tileSize, m_tileSize, RcConfig.CalcBorder(m_agentRadius, m_cellSize),
@ -100,7 +100,7 @@ public class RecastTileMeshTest
[Test] [Test]
public void TestPerformance() public void TestPerformance()
{ {
IInputGeomProvider geom = ObjImporter.Load(RcResources.Load("dungeon.obj")); IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj");
RcBuilder builder = new RcBuilder(); RcBuilder builder = new RcBuilder();
RcConfig cfg = new RcConfig( RcConfig cfg = new RcConfig(
true, m_tileSize, m_tileSize, true, m_tileSize, m_tileSize,