Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
410 changes: 364 additions & 46 deletions CadRevealFbxProvider.Tests/BatchUtils/ScaffoldOptimizerTests.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
namespace CadRevealFbxProvider.Tests.BatchUtils.ScaffoldPartOptimizers;

using System.Numerics;
using CadRevealComposer.Primitives;
using CadRevealComposer.Tessellation;
using CadRevealFbxProvider.BatchUtils.ScaffoldPartOptimizers;

public abstract class ScaffoldPartOptimizerTest : IScaffoldPartOptimizer
{
public abstract string Name { get; }
public abstract Mesh[] Optimize(Mesh mesh);
public abstract IScaffoldOptimizerResult[] Optimize(
APrimitive basePrimitive,
Mesh mesh,
Func<ulong, int, ulong> requestChildPartInstanceId
);
public abstract string[] GetPartNameTriggerKeywords();

public abstract List<Vector3> GetVerticesTruth();
public abstract List<uint> GetIndicesTruth();
public abstract List<List<Vector3>> GetVerticesTruth();
public abstract List<List<uint>> GetIndicesTruth();
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
namespace CadRevealFbxProvider.Tests.BatchUtils.ScaffoldPartOptimizers;

using System.Numerics;
using CadRevealComposer.Primitives;
using CadRevealComposer.Tessellation;
using CadRevealFbxProvider.BatchUtils.ScaffoldPartOptimizers;

public class ScaffoldPartOptimizerTestPartA : ScaffoldPartOptimizerTest
{
public override List<Vector3> GetVerticesTruth()
public override List<List<Vector3>> GetVerticesTruth()
{
return [new Vector3(1.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f)];
return
[
[new Vector3(1.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f)]
];
}

public override List<uint> GetIndicesTruth()
public override List<List<uint>> GetIndicesTruth()
{
return [1, 0, 2];
return
[
[1, 0, 2]
];
}

public override string Name
{
get { return "Part A test optimizer"; }
}

public override Mesh[] Optimize(Mesh mesh)
public override IScaffoldOptimizerResult[] Optimize(
APrimitive basePrimitive,
Mesh mesh,
Func<ulong, int, ulong> requestChildPartInstanceId
)
{
return [new Mesh(GetVerticesTruth().ToArray(), GetIndicesTruth().ToArray(), mesh.Error)];
return
[
new ScaffoldOptimizerResult(
basePrimitive,
new Mesh(GetVerticesTruth()[0].ToArray(), GetIndicesTruth()[0].ToArray(), mesh.Error),
0,
requestChildPartInstanceId
)
];
}

public override string[] GetPartNameTriggerKeywords()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
namespace CadRevealFbxProvider.Tests.BatchUtils.ScaffoldPartOptimizers;

using System.Numerics;
using CadRevealComposer.Primitives;
using CadRevealComposer.Tessellation;
using CadRevealFbxProvider.BatchUtils.ScaffoldPartOptimizers;

public class ScaffoldPartOptimizerTestPartB : ScaffoldPartOptimizerTest
{
public override List<Vector3> GetVerticesTruth()
public override List<List<Vector3>> GetVerticesTruth()
{
return [new Vector3(3.0f, 0.0f, 0.0f), new Vector3(0.0f, 4.0f, 0.0f), new Vector3(0.0f, 0.0f, 5.0f)];
return
[
[new Vector3(3.0f, 0.0f, 0.0f), new Vector3(0.0f, 4.0f, 0.0f), new Vector3(0.0f, 0.0f, 5.0f)]
];
}

public override List<uint> GetIndicesTruth()
public override List<List<uint>> GetIndicesTruth()
{
return [2, 0, 1];
return
[
[2, 0, 1]
];
}

public override string Name
{
get { return "Part A test optimizer"; }
}

public override Mesh[] Optimize(Mesh mesh)
public override IScaffoldOptimizerResult[] Optimize(
APrimitive basePrimitive,
Mesh mesh,
Func<ulong, int, ulong> requestChildPartInstanceId
)
{
return [new Mesh(GetVerticesTruth().ToArray(), GetIndicesTruth().ToArray(), mesh.Error)];
return
[
new ScaffoldOptimizerResult(
basePrimitive,
new Mesh(GetVerticesTruth()[0].ToArray(), GetIndicesTruth()[0].ToArray(), mesh.Error),
0,
requestChildPartInstanceId
)
];
}

public override string[] GetPartNameTriggerKeywords()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
namespace CadRevealFbxProvider.Tests.BatchUtils.ScaffoldPartOptimizers;

using System.Drawing;
using System.Numerics;
using CadRevealComposer;
using CadRevealComposer.Primitives;
using CadRevealComposer.Tessellation;
using CadRevealFbxProvider.BatchUtils.ScaffoldPartOptimizers;

public class ScaffoldPartOptimizerTestPartC : ScaffoldPartOptimizerTest
{
public override List<List<Vector3>> GetVerticesTruth()
{
return
[
[],
[new Vector3(3.0f, 0.0f, 0.0f), new Vector3(0.0f, 4.0f, 0.0f), new Vector3(0.0f, 0.0f, 5.0f)],
[new Vector3(3.0f, 0.0f, 0.0f), new Vector3(0.0f, 4.0f, 0.0f), new Vector3(0.0f, 0.0f, 5.0f)],
[],
[new Vector3(3.0f, 0.0f, 0.0f), new Vector3(0.0f, 4.0f, 0.0f), new Vector3(0.0f, 0.0f, 5.0f)],
[new Vector3(3.0f, 0.0f, 0.0f), new Vector3(0.0f, 4.0f, 0.0f), new Vector3(0.0f, 0.0f, 5.0f)]
];
}

public override List<List<uint>> GetIndicesTruth()
{
return
[
[],
[2, 0, 1],
[2, 0, 1],
[],
[2, 0, 1],
[2, 0, 1]
];
}

public override string Name
{
get { return "Part C test optimizer"; }
}

public override IScaffoldOptimizerResult[] Optimize(
APrimitive basePrimitive,
Mesh mesh,
Func<ulong, int, ulong> requestChildPartInstanceId
)
{
return
[
new ScaffoldOptimizerResult(
new Circle(
Matrix4x4.Identity,
new Vector3(0, 0, 1),
0,
Color.Black,
new BoundingBox(new Vector3(0, 0, 0), new Vector3(1, 1, 1))
)
),
new ScaffoldOptimizerResult(
basePrimitive,
new Mesh(GetVerticesTruth()[1].ToArray(), GetIndicesTruth()[1].ToArray(), mesh.Error),
0,
requestChildPartInstanceId
),
new ScaffoldOptimizerResult(
basePrimitive,
new Mesh(GetVerticesTruth()[2].ToArray(), GetIndicesTruth()[2].ToArray(), mesh.Error),
1,
requestChildPartInstanceId
),
new ScaffoldOptimizerResult(
new Circle(
Matrix4x4.Identity,
new Vector3(0, 0, 1),
0,
Color.Black,
new BoundingBox(new Vector3(0, 0, 0), new Vector3(1, 1, 1))
)
),
new ScaffoldOptimizerResult(
new TriangleMesh(
new Mesh(GetVerticesTruth()[4].ToArray(), GetIndicesTruth()[4].ToArray(), mesh.Error),
0,
Color.Black,
new BoundingBox(new Vector3(0, 0, 0), new Vector3(1, 1, 1))
)
),
new ScaffoldOptimizerResult(
basePrimitive,
new Mesh(GetVerticesTruth()[5].ToArray(), GetIndicesTruth()[5].ToArray(), mesh.Error),
2,
requestChildPartInstanceId
)
];
}

public override string[] GetPartNameTriggerKeywords()
{
return ["Test C"];
}
}
79 changes: 51 additions & 28 deletions CadRevealFbxProvider/BatchUtils/ScaffoldOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,62 +13,83 @@ public void AddPartOptimizer(IScaffoldPartOptimizer optimizer)
_partOptimizers.Add(optimizer);
}

public void OptimizeNode(CadRevealNode node)
public void OptimizeNode(CadRevealNode node, Func<ulong> requestNewInstanceId)
{
var name = node.Name;
var partName = node.Name;

var newGeometries = new List<APrimitive>();
foreach (APrimitive primitive in node.Geometries)
{
APrimitive[]? newPrimitiveList = OptimizePrimitive(primitive, name);
APrimitive[]? newPrimitiveList = OptimizePrimitive(primitive, partName, requestNewInstanceId);
newGeometries.AddRange(newPrimitiveList ?? [primitive]);
}

node.Geometries = newGeometries.ToArray();
}

private APrimitive[]? OptimizePrimitive(APrimitive primitive, string name)
private APrimitive[]? OptimizePrimitive(APrimitive primitive, string partName, Func<ulong> requestNewInstanceId)
{
// Handle only primitives that have their own Mesh objects, then pull out the mesh and optimize. These are the ones that need to be optimized.
var primitiveList = new List<APrimitive>();
switch (primitive)
{
case InstancedMesh instancedMesh:
{
Mesh[] meshes = OptimizeMesh(instancedMesh.TemplateMesh, name);
primitiveList.AddRange(
meshes.Select(mesh => new InstancedMesh(
instancedMesh.InstanceId,
mesh,
instancedMesh.InstanceMatrix,
instancedMesh.TreeIndex,
instancedMesh.Color,
instancedMesh.AxisAlignedBoundingBox
))
);
OptimizeMeshAndAddResult(instancedMesh.TemplateMesh);
break;
}
case TriangleMesh triangleMesh:
{
Mesh[] meshes = OptimizeMesh(triangleMesh.Mesh, name);
primitiveList.AddRange(
meshes.Select(mesh => new TriangleMesh(
mesh,
triangleMesh.TreeIndex,
triangleMesh.Color,
triangleMesh.AxisAlignedBoundingBox
))
);
OptimizeMeshAndAddResult(triangleMesh.Mesh);
break;
}
}

return primitiveList.Count > 0 ? primitiveList.ToArray() : null;

void OptimizeMeshAndAddResult(Mesh mesh)
{
IScaffoldOptimizerResult[] results = OptimizeMesh(primitive, mesh, partName, requestNewInstanceId);
primitiveList.AddRange(results.Select(result => result.Get()));
}
}

private Mesh[] OptimizeMesh(Mesh mesh, string name)
private ulong OnRequestChildMeshInstanceId(
ulong instanceIdParentMesh,
int indexChildMesh,
Func<ulong> requestNewInstanceId
)
{
Mesh[] optimizedMesh = [mesh];
if (_childMeshInstanceIdLookup.TryGetValue(instanceIdParentMesh, out Dictionary<int, ulong>? retInstanceIdDict))
{
if (retInstanceIdDict.TryGetValue(indexChildMesh, out ulong retInstanceId))
{
return retInstanceId;
}

_childMeshInstanceIdLookup[instanceIdParentMesh].Add(indexChildMesh, requestNewInstanceId());
return _childMeshInstanceIdLookup[instanceIdParentMesh][indexChildMesh];
}

_childMeshInstanceIdLookup.Add(instanceIdParentMesh, new Dictionary<int, ulong>());
_childMeshInstanceIdLookup[instanceIdParentMesh].Add(indexChildMesh, requestNewInstanceId());
return _childMeshInstanceIdLookup[instanceIdParentMesh][indexChildMesh];
}

private IScaffoldOptimizerResult[] OptimizeMesh(
APrimitive basePrimitive,
Mesh mesh,
string name,
Func<ulong> requestNewInstanceId
)
{
var onRequestChildMeshInstanceId = (ulong instanceIdParentMesh, int indexChildMesh) =>
OnRequestChildMeshInstanceId(instanceIdParentMesh, indexChildMesh, requestNewInstanceId);

IScaffoldOptimizerResult[] optimizedResult =
[
new ScaffoldOptimizerResult(basePrimitive, mesh, 0, onRequestChildMeshInstanceId)
];
var triggeredOptimizers = new List<IScaffoldPartOptimizer>();
foreach (IScaffoldPartOptimizer partOptimizer in _partOptimizers)
{
Expand All @@ -85,7 +106,7 @@ private Mesh[] OptimizeMesh(Mesh mesh, string name)
{
if (triggeredOptimizers.Count == 0)
{
optimizedMesh = partOptimizer.Optimize(mesh);
optimizedResult = partOptimizer.Optimize(basePrimitive, mesh, onRequestChildMeshInstanceId);
}
triggeredOptimizers.Add(partOptimizer);
}
Expand All @@ -107,9 +128,11 @@ private Mesh[] OptimizeMesh(Mesh mesh, string name)
}
}

return optimizedMesh;
return optimizedResult;
}

private readonly Dictionary<ulong, Dictionary<int, ulong>> _childMeshInstanceIdLookup =
new Dictionary<ulong, Dictionary<int, ulong>>();
private readonly List<IScaffoldPartOptimizer> _partOptimizers =
[
// :TODO: Fill in the available part optimizers here
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace CadRevealFbxProvider.BatchUtils.ScaffoldPartOptimizers;

using CadRevealComposer.Primitives;

public interface IScaffoldOptimizerResult
{
public APrimitive Get();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
namespace CadRevealFbxProvider.BatchUtils.ScaffoldPartOptimizers;

using CadRevealComposer.Primitives;
using CadRevealComposer.Tessellation;

public interface IScaffoldPartOptimizer
{
public string Name { get; }
public Mesh[] Optimize(Mesh mesh);
public IScaffoldOptimizerResult[] Optimize(
APrimitive basePrimitive, // Primitive that contains the mesh
Mesh mesh, // The mesh
Func<ulong, int, ulong> requestChildPartInstanceId // Function will be called when the optimizer needs an instance ID parameters are (index of child mesh, instance ID of base primitive)
);
public string[] GetPartNameTriggerKeywords();
}
Loading