refactor: improve unsafe collection API usage per review
- Replace float[3] with Vector3 (System.Numerics) - Use UnsafeList.GetUnsafePtr() instead of fixed blocks - Use AllocationManager.CreateStackScope() for temporary collections - Remove redundant properties in ClodConfig (public fields suffice) - Use MeshOptApi directly instead of Api alias - Fix method signatures to not require allocator for stack-scoped collections
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Meshlet;
|
namespace Ghost.Graphics.Meshlet;
|
||||||
|
|
||||||
public unsafe struct ClodBounds
|
public struct ClodBounds
|
||||||
{
|
{
|
||||||
public fixed float center[3];
|
public Vector3 center;
|
||||||
public float radius;
|
public float radius;
|
||||||
public float error;
|
public float error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
using Ghost.MeshOptimizer;
|
using Ghost.MeshOptimizer;
|
||||||
using Misaki.HighPerformance;
|
using Misaki.HighPerformance;
|
||||||
|
|
||||||
@@ -8,56 +9,44 @@ internal static class ClodBoundsHelper
|
|||||||
{
|
{
|
||||||
public static ClodBounds ComputeBounds(ClodMesh mesh, UnsafeList<uint> indices, float error)
|
public static ClodBounds ComputeBounds(ClodMesh mesh, UnsafeList<uint> indices, float error)
|
||||||
{
|
{
|
||||||
fixed (uint* pIndices = new uint[(int)indices.Length])
|
var bounds = MeshOptApi.meshopt_computeClusterBounds(indices.Ptr, (nuint)indices.Length, mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
|
||||||
{
|
|
||||||
for (int i = 0; i < (int)indices.Length; i++)
|
|
||||||
{
|
|
||||||
pIndices[i] = indices[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
var bounds = Api.meshopt_computeClusterBounds(pIndices, (nuint)indices.Length, mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
|
var result = new ClodBounds();
|
||||||
|
result.center = new Vector3(bounds.center[0], bounds.center[1], bounds.center[2]);
|
||||||
var result = new ClodBounds();
|
result.radius = bounds.radius;
|
||||||
result.center[0] = bounds.center[0];
|
result.error = error;
|
||||||
result.center[1] = bounds.center[1];
|
return result;
|
||||||
result.center[2] = bounds.center[2];
|
|
||||||
result.radius = bounds.radius;
|
|
||||||
result.error = error;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ClodBounds MergeBounds(UnsafeList<Cluster> clusters, UnsafeList<int> group)
|
public static ClodBounds MergeBounds(UnsafeList<Cluster> clusters, UnsafeList<int> group)
|
||||||
{
|
{
|
||||||
var boundsList = new ClodBounds[group.Length];
|
using var scope = AllocationManager.CreateStackScope();
|
||||||
|
var boundsList = new UnsafeList<ClodBounds>(group.Length, scope.AllocationHandle);
|
||||||
|
boundsList.Resize((nuint)group.Length);
|
||||||
|
|
||||||
for (int j = 0; j < (int)group.Length; j++)
|
for (int j = 0; j < (int)group.Length; j++)
|
||||||
{
|
{
|
||||||
boundsList[j] = clusters[group[j]].bounds;
|
boundsList[j] = clusters[group[j]].bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (ClodBounds* pBounds = boundsList)
|
var merged = MeshOptApi.meshopt_computeSphereBounds(
|
||||||
|
(float*)boundsList.Ptr,
|
||||||
|
(nuint)group.Length,
|
||||||
|
(nuint)sizeof(ClodBounds),
|
||||||
|
(float*)boundsList.Ptr + 3, // offset to radius field
|
||||||
|
(nuint)sizeof(ClodBounds)
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = new ClodBounds();
|
||||||
|
result.center = new Vector3(merged.center[0], merged.center[1], merged.center[2]);
|
||||||
|
result.radius = merged.radius;
|
||||||
|
|
||||||
|
result.error = 0.0f;
|
||||||
|
for (int j = 0; j < (int)group.Length; j++)
|
||||||
{
|
{
|
||||||
var merged = Api.meshopt_computeSphereBounds(
|
result.error = Math.Max(result.error, clusters[group[j]].bounds.error);
|
||||||
&pBounds[0].center[0],
|
|
||||||
(nuint)boundsList.Length,
|
|
||||||
(nuint)sizeof(ClodBounds),
|
|
||||||
&pBounds[0].radius,
|
|
||||||
(nuint)sizeof(ClodBounds)
|
|
||||||
);
|
|
||||||
|
|
||||||
var result = new ClodBounds();
|
|
||||||
result.center[0] = merged.center[0];
|
|
||||||
result.center[1] = merged.center[1];
|
|
||||||
result.center[2] = merged.center[2];
|
|
||||||
result.radius = merged.radius;
|
|
||||||
|
|
||||||
result.error = 0.0f;
|
|
||||||
for (int j = 0; j < (int)group.Length; j++)
|
|
||||||
{
|
|
||||||
result.error = Math.Max(result.error, clusters[group[j]].bounds.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
using Ghost.MeshOptimizer;
|
using Ghost.MeshOptimizer;
|
||||||
using Misaki.HighPerformance;
|
using Misaki.HighPerformance;
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ public unsafe static class ClodBuilder
|
|||||||
// Generate position-only remap
|
// Generate position-only remap
|
||||||
var remap = new UnsafeList<uint>((int)mesh.vertexCount, allocator);
|
var remap = new UnsafeList<uint>((int)mesh.vertexCount, allocator);
|
||||||
remap.Resize(mesh.vertexCount);
|
remap.Resize(mesh.vertexCount);
|
||||||
Api.meshopt_generatePositionRemap(remap.Ptr, mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
|
MeshOptApi.meshopt_generatePositionRemap(remap.Ptr, mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
|
||||||
|
|
||||||
// Set up protect bits on UV seams
|
// Set up protect bits on UV seams
|
||||||
if (mesh.attributeProtectMask != 0)
|
if (mesh.attributeProtectMask != 0)
|
||||||
@@ -49,7 +50,7 @@ public unsafe static class ClodBuilder
|
|||||||
{
|
{
|
||||||
if (mesh.vertexAttributes[i * maxAttributes + j] != mesh.vertexAttributes[r * maxAttributes + j])
|
if (mesh.vertexAttributes[i * maxAttributes + j] != mesh.vertexAttributes[r * maxAttributes + j])
|
||||||
{
|
{
|
||||||
locks[(int)i] |= (byte)Api.meshopt_SimplifyVertex_Protect;
|
locks[(int)i] |= (byte)MeshOptApi.meshopt_SimplifyVertex_Protect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,16 +75,16 @@ public unsafe static class ClodBuilder
|
|||||||
|
|
||||||
while (pending.Length > 1)
|
while (pending.Length > 1)
|
||||||
{
|
{
|
||||||
var groups = ClodInternal.Partition(config, mesh, clusters, pending, remap, allocator);
|
var groups = ClodPartition.Partition(config, mesh, clusters, pending, remap);
|
||||||
|
|
||||||
pending.Clear();
|
pending.Clear();
|
||||||
|
|
||||||
// Lock boundaries
|
// Lock boundaries
|
||||||
ClodInternal.LockBoundary(locks, groups, clusters, remap, mesh.vertexLock);
|
ClodBoundary.LockBoundary(locks, groups, clusters, remap, mesh.vertexLock);
|
||||||
|
|
||||||
for (int i = 0; i < (int)groups.Length; i++)
|
for (int i = 0; i < (int)groups.Length; i++)
|
||||||
{
|
{
|
||||||
var merged = new UnsafeList<uint>(groups[i].Length * (int)config.MaxTriangles * 3, allocator);
|
var merged = new UnsafeList<uint>(groups[i].Length * (int)config.maxTriangles * 3, allocator);
|
||||||
for (int j = 0; j < (int)groups[i].Length; j++)
|
for (int j = 0; j < (int)groups[i].Length; j++)
|
||||||
{
|
{
|
||||||
var clusterIndices = clusters[groups[i][j]].indices;
|
var clusterIndices = clusters[groups[i][j]].indices;
|
||||||
@@ -91,14 +92,14 @@ public unsafe static class ClodBuilder
|
|||||||
merged.Add(clusterIndices[k]);
|
merged.Add(clusterIndices[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
nuint targetSize = ((nuint)merged.Length / 3) * (nuint)config.SimplifyRatio * 3;
|
nuint targetSize = ((nuint)merged.Length / 3) * (nuint)config.simplifyRatio * 3;
|
||||||
|
|
||||||
var bounds = ClodBoundsHelper.MergeBounds(clusters, groups[i]);
|
var bounds = ClodBoundsHelper.MergeBounds(clusters, groups[i]);
|
||||||
|
|
||||||
float error = 0.0f;
|
float error = 0.0f;
|
||||||
var simplified = ClodSimplify.Simplify(config, mesh, merged, locks, targetSize, &error, allocator);
|
var simplified = ClodSimplify.Simplify(config, mesh, merged, locks, targetSize, &error);
|
||||||
|
|
||||||
if (simplified.Length > (nuint)(merged.Length * config.SimplifyThreshold))
|
if (simplified.Length > (nuint)(merged.Length * config.simplifyThreshold))
|
||||||
{
|
{
|
||||||
bounds.error = float.MaxValue;
|
bounds.error = float.MaxValue;
|
||||||
OutputGroup(config, mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback, allocator);
|
OutputGroup(config, mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback, allocator);
|
||||||
@@ -107,7 +108,7 @@ public unsafe static class ClodBuilder
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds.error = Math.Max(bounds.error * config.SimplifyErrorMergePrevious, error) + error * config.SimplifyErrorMergeAdditive;
|
bounds.error = Math.Max(bounds.error * config.simplifyErrorMergePrevious, error) + error * config.simplifyErrorMergeAdditive;
|
||||||
|
|
||||||
int refined = OutputGroup(config, mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback, allocator);
|
int refined = OutputGroup(config, mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback, allocator);
|
||||||
|
|
||||||
@@ -171,7 +172,7 @@ public unsafe static class ClodBuilder
|
|||||||
Allocator allocator
|
Allocator allocator
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var groupClusters = new UnsafeList<ClodCluster>((int)group.Length, allocator);
|
var groupClusters = new UnsafeList<ClodCluster>(group.Length, allocator);
|
||||||
groupClusters.Resize((nuint)group.Length);
|
groupClusters.Resize((nuint)group.Length);
|
||||||
|
|
||||||
for (int i = 0; i < (int)group.Length; i++)
|
for (int i = 0; i < (int)group.Length; i++)
|
||||||
@@ -180,7 +181,7 @@ public unsafe static class ClodBuilder
|
|||||||
ref var dstCluster = ref groupClusters[i];
|
ref var dstCluster = ref groupClusters[i];
|
||||||
|
|
||||||
dstCluster.refined = srcCluster.refined;
|
dstCluster.refined = srcCluster.refined;
|
||||||
dstCluster.bounds = (config.OptimizeBounds && srcCluster.refined != -1)
|
dstCluster.bounds = (config.optimizeBounds && srcCluster.refined != -1)
|
||||||
? ClodBoundsHelper.ComputeBounds(mesh, srcCluster.indices, srcCluster.bounds.error)
|
? ClodBoundsHelper.ComputeBounds(mesh, srcCluster.indices, srcCluster.bounds.error)
|
||||||
: srcCluster.bounds;
|
: srcCluster.bounds;
|
||||||
dstCluster.indices = srcCluster.indices.Ptr;
|
dstCluster.indices = srcCluster.indices.Ptr;
|
||||||
|
|||||||
@@ -34,26 +34,4 @@ public struct ClodConfig
|
|||||||
public bool optimizeBounds;
|
public bool optimizeBounds;
|
||||||
|
|
||||||
public bool optimizeClusters;
|
public bool optimizeClusters;
|
||||||
|
|
||||||
public nuint MaxVertices { get => maxVertices; set => maxVertices = value; }
|
|
||||||
public nuint MinTriangles { get => minTriangles; set => minTriangles = value; }
|
|
||||||
public nuint MaxTriangles { get => maxTriangles; set => maxTriangles = value; }
|
|
||||||
public bool PartitionSpatial { get => partitionSpatial; set => partitionSpatial = value; }
|
|
||||||
public bool PartitionSort { get => partitionSort; set => partitionSort = value; }
|
|
||||||
public nuint PartitionSize { get => partitionSize; set => partitionSize = value; }
|
|
||||||
public bool ClusterSpatial { get => clusterSpatial; set => clusterSpatial = value; }
|
|
||||||
public float ClusterFillWeight { get => clusterFillWeight; set => clusterFillWeight = value; }
|
|
||||||
public float ClusterSplitFactor { get => clusterSplitFactor; set => clusterSplitFactor = value; }
|
|
||||||
public float SimplifyRatio { get => simplifyRatio; set => simplifyRatio = value; }
|
|
||||||
public float SimplifyThreshold { get => simplifyThreshold; set => simplifyThreshold = value; }
|
|
||||||
public float SimplifyErrorMergePrevious { get => simplifyErrorMergePrevious; set => simplifyErrorMergePrevious = value; }
|
|
||||||
public float SimplifyErrorMergeAdditive { get => simplifyErrorMergeAdditive; set => simplifyErrorMergeAdditive = value; }
|
|
||||||
public float SimplifyErrorFactorSloppy { get => simplifyErrorFactorSloppy; set => simplifyErrorFactorSloppy = value; }
|
|
||||||
public float SimplifyErrorEdgeLimit { get => simplifyErrorEdgeLimit; set => simplifyErrorEdgeLimit = value; }
|
|
||||||
public bool SimplifyPermissive { get => simplifyPermissive; set => simplifyPermissive = value; }
|
|
||||||
public bool SimplifyFallbackPermissive { get => simplifyFallbackPermissive; set => simplifyFallbackPermissive = value; }
|
|
||||||
public bool SimplifyFallbackSloppy { get => simplifyFallbackSloppy; set => simplifyFallbackSloppy = value; }
|
|
||||||
public bool SimplifyRegularize { get => simplifyRegularize; set => simplifyRegularize = value; }
|
|
||||||
public bool OptimizeBounds { get => optimizeBounds; set => optimizeBounds = value; }
|
|
||||||
public bool OptimizeClusters { get => optimizeClusters; set => optimizeClusters = value; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ internal static class ClodInternal
|
|||||||
{
|
{
|
||||||
public static UnsafeList<Cluster> Clusterize(ClodConfig config, ClodMesh mesh, uint* indices, nuint indexCount, Allocator allocator)
|
public static UnsafeList<Cluster> Clusterize(ClodConfig config, ClodMesh mesh, uint* indices, nuint indexCount, Allocator allocator)
|
||||||
{
|
{
|
||||||
nuint maxMeshlets = Api.meshopt_buildMeshletsBound(indexCount, config.MaxVertices, config.MinTriangles);
|
nuint maxMeshlets = MeshOptApi.meshopt_buildMeshletsBound(indexCount, config.maxVertices, config.minTriangles);
|
||||||
|
|
||||||
var meshlets = new UnsafeList<meshopt_Meshlet>(maxMeshlets, allocator);
|
var meshlets = new UnsafeList<meshopt_Meshlet>(maxMeshlets, allocator);
|
||||||
var meshletVertices = new UnsafeList<uint>(indexCount, allocator);
|
var meshletVertices = new UnsafeList<uint>(indexCount, allocator);
|
||||||
@@ -17,9 +17,9 @@ internal static class ClodInternal
|
|||||||
meshlets.Resize(maxMeshlets);
|
meshlets.Resize(maxMeshlets);
|
||||||
|
|
||||||
nuint meshletCount;
|
nuint meshletCount;
|
||||||
if (config.ClusterSpatial)
|
if (config.clusterSpatial)
|
||||||
{
|
{
|
||||||
meshletCount = Api.meshopt_buildMeshletsSpatial(
|
meshletCount = MeshOptApi.meshopt_buildMeshletsSpatial(
|
||||||
meshlets.Ptr,
|
meshlets.Ptr,
|
||||||
meshletVertices.Ptr,
|
meshletVertices.Ptr,
|
||||||
meshletTriangles.Ptr,
|
meshletTriangles.Ptr,
|
||||||
@@ -28,15 +28,15 @@ internal static class ClodInternal
|
|||||||
mesh.vertexPositions,
|
mesh.vertexPositions,
|
||||||
mesh.vertexCount,
|
mesh.vertexCount,
|
||||||
mesh.vertexPositionsStride,
|
mesh.vertexPositionsStride,
|
||||||
config.MaxVertices,
|
config.maxVertices,
|
||||||
config.MinTriangles,
|
config.minTriangles,
|
||||||
config.MaxTriangles,
|
config.maxTriangles,
|
||||||
config.ClusterFillWeight
|
config.clusterFillWeight
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
meshletCount = Api.meshopt_buildMeshletsFlex(
|
meshletCount = MeshOptApi.meshopt_buildMeshletsFlex(
|
||||||
meshlets.Ptr,
|
meshlets.Ptr,
|
||||||
meshletVertices.Ptr,
|
meshletVertices.Ptr,
|
||||||
meshletTriangles.Ptr,
|
meshletTriangles.Ptr,
|
||||||
@@ -45,11 +45,11 @@ internal static class ClodInternal
|
|||||||
mesh.vertexPositions,
|
mesh.vertexPositions,
|
||||||
mesh.vertexCount,
|
mesh.vertexCount,
|
||||||
mesh.vertexPositionsStride,
|
mesh.vertexPositionsStride,
|
||||||
config.MaxVertices,
|
config.maxVertices,
|
||||||
config.MinTriangles,
|
config.minTriangles,
|
||||||
config.MaxTriangles,
|
config.maxTriangles,
|
||||||
0.0f,
|
0.0f,
|
||||||
config.ClusterSplitFactor
|
config.clusterSplitFactor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
meshlets.Resize(meshletCount);
|
meshlets.Resize(meshletCount);
|
||||||
@@ -60,9 +60,9 @@ internal static class ClodInternal
|
|||||||
{
|
{
|
||||||
ref var meshlet = ref meshlets[i];
|
ref var meshlet = ref meshlets[i];
|
||||||
|
|
||||||
if (config.OptimizeClusters)
|
if (config.optimizeClusters)
|
||||||
{
|
{
|
||||||
Api.meshopt_optimizeMeshlet(
|
MeshOptApi.meshopt_optimizeMeshlet(
|
||||||
meshletVertices.Ptr + meshlet.vertexOffset,
|
meshletVertices.Ptr + meshlet.vertexOffset,
|
||||||
meshletTriangles.Ptr + meshlet.triangleOffset,
|
meshletTriangles.Ptr + meshlet.triangleOffset,
|
||||||
meshlet.triangleCount,
|
meshlet.triangleCount,
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
using Ghost.MeshOptimizer;
|
||||||
|
using Misaki.HighPerformance;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.Meshlet;
|
||||||
|
|
||||||
|
internal static class ClodBoundary
|
||||||
|
{
|
||||||
public static void LockBoundary(UnsafeList<byte> locks, UnsafeList<UnsafeList<int>> groups, UnsafeList<Cluster> clusters, UnsafeList<uint> remap, byte* vertexLock)
|
public static void LockBoundary(UnsafeList<byte> locks, UnsafeList<UnsafeList<int>> groups, UnsafeList<Cluster> clusters, UnsafeList<uint> remap, byte* vertexLock)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < (int)locks.Length; i++)
|
for (int i = 0; i < (int)locks.Length; i++)
|
||||||
@@ -7,7 +14,6 @@
|
|||||||
|
|
||||||
for (int i = 0; i < (int)groups.Length; i++)
|
for (int i = 0; i < (int)groups.Length; i++)
|
||||||
{
|
{
|
||||||
// Mark remapped vertices
|
|
||||||
for (int j = 0; j < (int)groups[i].Length; j++)
|
for (int j = 0; j < (int)groups[i].Length; j++)
|
||||||
{
|
{
|
||||||
var cluster = clusters[groups[i][j]];
|
var cluster = clusters[groups[i][j]];
|
||||||
@@ -19,7 +25,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark seen
|
|
||||||
for (int j = 0; j < (int)groups[i].Length; j++)
|
for (int j = 0; j < (int)groups[i].Length; j++)
|
||||||
{
|
{
|
||||||
var cluster = clusters[groups[i][j]];
|
var cluster = clusters[groups[i][j]];
|
||||||
@@ -35,8 +40,9 @@
|
|||||||
for (int i = 0; i < (int)locks.Length; i++)
|
for (int i = 0; i < (int)locks.Length; i++)
|
||||||
{
|
{
|
||||||
uint r = remap[i];
|
uint r = remap[i];
|
||||||
locks[i] = (byte)((locks[(int)r] & 1) | (locks[i] & (byte)MeshOptimizer.Api.meshopt_SimplifyVertex_Protect));
|
locks[i] = (byte)((locks[(int)r] & 1) | (locks[i] & (byte)MeshOptApi.meshopt_SimplifyVertex_Protect));
|
||||||
if (vertexLock != null)
|
if (vertexLock != null)
|
||||||
locks[i] |= vertexLock[i];
|
locks[i] |= vertexLock[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
public static UnsafeList<UnsafeList<int>> Partition(ClodConfig config, ClodMesh mesh, UnsafeList<Cluster> clusters, UnsafeList<int> pending, UnsafeList<uint> remap, Allocator allocator)
|
using System;
|
||||||
|
using Ghost.MeshOptimizer;
|
||||||
|
using Misaki.HighPerformance;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.Meshlet;
|
||||||
|
|
||||||
|
internal static class ClodPartition
|
||||||
|
{
|
||||||
|
public static UnsafeList<UnsafeList<int>> Partition(ClodConfig config, ClodMesh mesh, UnsafeList<Cluster> clusters, UnsafeList<int> pending, UnsafeList<uint> remap)
|
||||||
{
|
{
|
||||||
if (pending.Length <= (int)config.PartitionSize)
|
if (pending.Length <= (int)config.partitionSize)
|
||||||
{
|
{
|
||||||
var partitions = new UnsafeList<UnsafeList<int>>(1, allocator);
|
using var scope = AllocationManager.CreateStackScope();
|
||||||
|
var partitions = new UnsafeList<UnsafeList<int>>(1, scope.AllocationHandle);
|
||||||
partitions.Add(pending);
|
partitions.Add(pending);
|
||||||
return partitions;
|
return partitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
var clusterIndices = new UnsafeList<uint>(1024, allocator); // Initial guess
|
using var stackScope = AllocationManager.CreateStackScope();
|
||||||
var clusterCounts = new UnsafeList<uint>(pending.Length, allocator);
|
var clusterIndices = new UnsafeList<uint>(1024, stackScope.AllocationHandle);
|
||||||
|
var clusterCounts = new UnsafeList<uint>(pending.Length, stackScope.AllocationHandle);
|
||||||
|
|
||||||
nuint totalIndexCount = 0;
|
nuint totalIndexCount = 0;
|
||||||
for (int i = 0; i < pending.Length; i++)
|
for (int i = 0; i < pending.Length; i++)
|
||||||
@@ -32,33 +42,25 @@
|
|||||||
offset += (nuint)cluster.indices.Length;
|
offset += (nuint)cluster.indices.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
var clusterPart = new UnsafeList<uint>(pending.Length, allocator);
|
var clusterPart = new UnsafeList<uint>(pending.Length, stackScope.AllocationHandle);
|
||||||
clusterPart.Resize((nuint)pending.Length);
|
clusterPart.Resize((nuint)pending.Length);
|
||||||
|
|
||||||
nuint partitionCount = Api.meshopt_partitionClusters(
|
nuint partitionCount = MeshOptApi.meshopt_partitionClusters(
|
||||||
clusterPart.Ptr,
|
clusterPart.Ptr,
|
||||||
clusterIndices.Ptr,
|
clusterIndices.Ptr,
|
||||||
totalIndexCount,
|
totalIndexCount,
|
||||||
clusterCounts.Ptr,
|
clusterCounts.Ptr,
|
||||||
(nuint)pending.Length,
|
(nuint)pending.Length,
|
||||||
config.PartitionSpatial ? mesh.vertexPositions : null,
|
config.partitionSpatial ? mesh.vertexPositions : null,
|
||||||
remap.Length,
|
remap.Length,
|
||||||
mesh.vertexPositionsStride,
|
mesh.vertexPositionsStride,
|
||||||
config.PartitionSize
|
config.partitionSize
|
||||||
);
|
);
|
||||||
|
|
||||||
var partitions = new UnsafeList<UnsafeList<int>>(partitionCount, allocator);
|
var partitions = new UnsafeList<UnsafeList<int>>(partitionCount, stackScope.AllocationHandle);
|
||||||
for (nuint i = 0; i < partitionCount; i++)
|
for (nuint i = 0; i < partitionCount; i++)
|
||||||
{
|
{
|
||||||
partitions.Add(new UnsafeList<int>((nuint)(config.PartitionSize + config.PartitionSize / 3), allocator));
|
partitions.Add(new UnsafeList<int>((nuint)(config.partitionSize + config.partitionSize / 3), stackScope.AllocationHandle));
|
||||||
}
|
|
||||||
|
|
||||||
// Handle sorting if requested
|
|
||||||
if (config.PartitionSort)
|
|
||||||
{
|
|
||||||
// Logic to sort partitions spatially using meshopt_spatialSortRemap
|
|
||||||
// For simplicity in this implementation, I will skip the complex sorting for now
|
|
||||||
// and just distribute clusters directly as per the basic meshopt example.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < pending.Length; i++)
|
for (int i = 0; i < pending.Length; i++)
|
||||||
@@ -66,9 +68,6 @@
|
|||||||
partitions[(int)clusterPart[i]].Add(pending[i]);
|
partitions[(int)clusterPart[i]].Add(pending[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterIndices.Dispose();
|
|
||||||
clusterCounts.Dispose();
|
|
||||||
clusterPart.Dispose();
|
|
||||||
|
|
||||||
return partitions;
|
return partitions;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ internal static class ClodSimplify
|
|||||||
UnsafeList<uint> indices,
|
UnsafeList<uint> indices,
|
||||||
UnsafeList<byte> locks,
|
UnsafeList<byte> locks,
|
||||||
nuint targetCount,
|
nuint targetCount,
|
||||||
float* error,
|
float* error
|
||||||
Allocator allocator
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (targetCount > (nuint)indices.Length)
|
if (targetCount > (nuint)indices.Length)
|
||||||
@@ -21,106 +20,96 @@ internal static class ClodSimplify
|
|||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lod = new UnsafeList<uint>(indices.Length, allocator);
|
using var scope = AllocationManager.CreateStackScope();
|
||||||
|
var lod = new UnsafeList<uint>(indices.Length, scope.AllocationHandle);
|
||||||
lod.Resize((nuint)indices.Length);
|
lod.Resize((nuint)indices.Length);
|
||||||
|
|
||||||
uint options = Api.meshopt_SimplifySparse | Api.meshopt_SimplifyErrorAbsolute;
|
uint options = MeshOptApi.meshopt_SimplifySparse | MeshOptApi.meshopt_SimplifyErrorAbsolute;
|
||||||
if (config.SimplifyPermissive)
|
if (config.simplifyPermissive)
|
||||||
options |= Api.meshopt_SimplifyPermissive;
|
options |= MeshOptApi.meshopt_SimplifyPermissive;
|
||||||
if (config.SimplifyRegularize)
|
if (config.simplifyRegularize)
|
||||||
options |= Api.meshopt_SimplifyRegularize;
|
options |= MeshOptApi.meshopt_SimplifyRegularize;
|
||||||
|
|
||||||
fixed (uint* pIndices = new uint[(int)indices.Length])
|
nuint resultSize = MeshOptApi.meshopt_simplifyWithAttributes(
|
||||||
|
lod.Ptr,
|
||||||
|
indices.Ptr,
|
||||||
|
(nuint)indices.Length,
|
||||||
|
mesh.vertexPositions,
|
||||||
|
mesh.vertexCount,
|
||||||
|
mesh.vertexPositionsStride,
|
||||||
|
mesh.vertexAttributes,
|
||||||
|
mesh.vertexAttributesStride,
|
||||||
|
mesh.attributeWeights,
|
||||||
|
mesh.attributeCount,
|
||||||
|
locks.Ptr,
|
||||||
|
targetCount,
|
||||||
|
float.MaxValue,
|
||||||
|
options,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
|
||||||
|
lod.Resize(resultSize);
|
||||||
|
|
||||||
|
// Fallback to permissive if needed
|
||||||
|
if (lod.Length > targetCount && config.simplifyFallbackPermissive && !config.simplifyPermissive)
|
||||||
{
|
{
|
||||||
fixed (byte* pLocks = new byte[(int)locks.Length])
|
options |= MeshOptApi.meshopt_SimplifyPermissive;
|
||||||
|
resultSize = MeshOptApi.meshopt_simplifyWithAttributes(
|
||||||
|
lod.Ptr,
|
||||||
|
indices.Ptr,
|
||||||
|
(nuint)indices.Length,
|
||||||
|
mesh.vertexPositions,
|
||||||
|
mesh.vertexCount,
|
||||||
|
mesh.vertexPositionsStride,
|
||||||
|
mesh.vertexAttributes,
|
||||||
|
mesh.vertexAttributesStride,
|
||||||
|
mesh.attributeWeights,
|
||||||
|
mesh.attributeCount,
|
||||||
|
locks.Ptr,
|
||||||
|
targetCount,
|
||||||
|
float.MaxValue,
|
||||||
|
options,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
lod.Resize(resultSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sloppy fallback
|
||||||
|
if (lod.Length > targetCount && config.simplifyFallbackSloppy)
|
||||||
|
{
|
||||||
|
SimplifyFallback(lod, mesh, indices, locks, targetCount, error);
|
||||||
|
*error *= config.simplifyErrorFactorSloppy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge limit check
|
||||||
|
if (config.simplifyErrorEdgeLimit > 0)
|
||||||
|
{
|
||||||
|
float maxEdgeSq = 0;
|
||||||
|
for (int i = 0; i < (int)indices.Length; i += 3)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < (int)indices.Length; i++)
|
uint a = indices[i], b = indices[i + 1], c = indices[i + 2];
|
||||||
pIndices[i] = indices[i];
|
|
||||||
for (int i = 0; i < (int)locks.Length; i++)
|
|
||||||
pLocks[i] = locks[i];
|
|
||||||
|
|
||||||
nuint resultSize = Api.meshopt_simplifyWithAttributes(
|
int posStride = (int)(mesh.vertexPositionsStride / sizeof(float));
|
||||||
lod.Ptr,
|
float* va = mesh.vertexPositions + (a * posStride);
|
||||||
pIndices,
|
float* vb = mesh.vertexPositions + (b * posStride);
|
||||||
(nuint)indices.Length,
|
float* vc = mesh.vertexPositions + (c * posStride);
|
||||||
mesh.vertexPositions,
|
|
||||||
mesh.vertexCount,
|
|
||||||
mesh.vertexPositionsStride,
|
|
||||||
mesh.vertexAttributes,
|
|
||||||
mesh.vertexAttributesStride,
|
|
||||||
mesh.attributeWeights,
|
|
||||||
mesh.attributeCount,
|
|
||||||
pLocks,
|
|
||||||
targetCount,
|
|
||||||
float.MaxValue,
|
|
||||||
options,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
|
|
||||||
lod.Resize(resultSize);
|
float dx = va[0] - vb[0], dy = va[1] - vb[1], dz = va[2] - vb[2];
|
||||||
|
float eab = dx * dx + dy * dy + dz * dz;
|
||||||
|
|
||||||
// Fallback to permissive if needed
|
dx = va[0] - vc[0]; dy = va[1] - vc[1]; dz = va[2] - vc[2];
|
||||||
if (lod.Length > targetCount && config.SimplifyFallbackPermissive && !config.SimplifyPermissive)
|
float eac = dx * dx + dy * dy + dz * dz;
|
||||||
{
|
|
||||||
options |= Api.meshopt_SimplifyPermissive;
|
|
||||||
resultSize = Api.meshopt_simplifyWithAttributes(
|
|
||||||
lod.Ptr,
|
|
||||||
pIndices,
|
|
||||||
(nuint)indices.Length,
|
|
||||||
mesh.vertexPositions,
|
|
||||||
mesh.vertexCount,
|
|
||||||
mesh.vertexPositionsStride,
|
|
||||||
mesh.vertexAttributes,
|
|
||||||
mesh.vertexAttributesStride,
|
|
||||||
mesh.attributeWeights,
|
|
||||||
mesh.attributeCount,
|
|
||||||
pLocks,
|
|
||||||
targetCount,
|
|
||||||
float.MaxValue,
|
|
||||||
options,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
lod.Resize(resultSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sloppy fallback
|
dx = vb[0] - vc[0]; dy = vb[1] - vc[1]; dz = vb[2] - vc[2];
|
||||||
if (lod.Length > targetCount && config.SimplifyFallbackSloppy)
|
float ebc = dx * dx + dy * dy + dz * dz;
|
||||||
{
|
|
||||||
SimplifyFallback(lod, mesh, indices, locks, targetCount, error, allocator);
|
|
||||||
*error *= config.SimplifyErrorFactorSloppy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Edge limit check
|
float emax = Math.Max(Math.Max(eab, eac), ebc);
|
||||||
if (config.SimplifyErrorEdgeLimit > 0)
|
float emin = Math.Min(Math.Min(eab, eac), ebc);
|
||||||
{
|
|
||||||
float maxEdgeSq = 0;
|
|
||||||
for (int i = 0; i < (int)indices.Length; i += 3)
|
|
||||||
{
|
|
||||||
uint a = indices[i], b = indices[i + 1], c = indices[i + 2];
|
|
||||||
|
|
||||||
int posStride = (int)(mesh.vertexPositionsStride / sizeof(float));
|
maxEdgeSq = Math.Max(maxEdgeSq, Math.Max(emin, emax / 4));
|
||||||
float* va = mesh.vertexPositions + (a * posStride);
|
|
||||||
float* vb = mesh.vertexPositions + (b * posStride);
|
|
||||||
float* vc = mesh.vertexPositions + (c * posStride);
|
|
||||||
|
|
||||||
float dx = va[0] - vb[0], dy = va[1] - vb[1], dz = va[2] - vb[2];
|
|
||||||
float eab = dx * dx + dy * dy + dz * dz;
|
|
||||||
|
|
||||||
dx = va[0] - vc[0]; dy = va[1] - vc[1]; dz = va[2] - vc[2];
|
|
||||||
float eac = dx * dx + dy * dy + dz * dz;
|
|
||||||
|
|
||||||
dx = vb[0] - vc[0]; dy = vb[1] - vc[1]; dz = vb[2] - vc[2];
|
|
||||||
float ebc = dx * dx + dy * dy + dz * dz;
|
|
||||||
|
|
||||||
float emax = Math.Max(Math.Max(eab, eac), ebc);
|
|
||||||
float emin = Math.Min(Math.Min(eab, eac), ebc);
|
|
||||||
|
|
||||||
maxEdgeSq = Math.Max(maxEdgeSq, Math.Max(emin, emax / 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
*error = Math.Min(*error, (float)Math.Sqrt(maxEdgeSq) * config.SimplifyErrorEdgeLimit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*error = Math.Min(*error, (float)Math.Sqrt(maxEdgeSq) * config.simplifyErrorEdgeLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lod;
|
return lod;
|
||||||
@@ -132,12 +121,10 @@ internal static class ClodSimplify
|
|||||||
UnsafeList<uint> indices,
|
UnsafeList<uint> indices,
|
||||||
UnsafeList<byte> locks,
|
UnsafeList<byte> locks,
|
||||||
nuint targetCount,
|
nuint targetCount,
|
||||||
float* error,
|
float* error
|
||||||
Allocator allocator
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Simplified version - deindex and use sloppy simplification
|
// Simplified version - deindex and use sloppy simplification
|
||||||
// Implementation details would involve creating a subset for sparse simplification
|
// Implementation details would involve creating a subset for sparse simplification
|
||||||
// For now, this is a placeholder
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user