diff --git a/src/Runtime/Ghost.Graphics/Ghost.Graphics.csproj b/src/Runtime/Ghost.Graphics/Ghost.Graphics.csproj
index fe61d22..e9b6b34 100644
--- a/src/Runtime/Ghost.Graphics/Ghost.Graphics.csproj
+++ b/src/Runtime/Ghost.Graphics/Ghost.Graphics.csproj
@@ -30,6 +30,7 @@
+
diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodBoundsHelper.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodBoundsHelper.cs
index 6204dd1..f1edd8e 100644
--- a/src/Runtime/Ghost.Graphics/Meshlet/ClodBoundsHelper.cs
+++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodBoundsHelper.cs
@@ -1,52 +1,48 @@
using System;
using System.Numerics;
using Ghost.MeshOptimizer;
-using Misaki.HighPerformance;
+using Misaki.HighPerformance.LowLevel.Buffer;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Meshlet;
internal static class ClodBoundsHelper
{
- public static ClodBounds ComputeBounds(ClodMesh mesh, UnsafeList indices, float error)
+ public static unsafe ClodBounds ComputeBounds(ClodMesh mesh, UnsafeList indices, float error)
{
- var bounds = MeshOptApi.ComputeClusterBounds(indices.GetUnsafePtr(), (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]);
- result.radius = bounds.radius;
- result.error = error;
- return result;
+ var bounds = MeshOptApi.ComputeClusterBounds((uint*)indices.GetUnsafePtr(), (nuint)indices.Count, mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
+ return new ClodBounds
+ {
+ center = new Vector3(bounds.center[0], bounds.center[1], bounds.center[2]),
+ radius = bounds.radius,
+ error = error
+ };
}
- public static ClodBounds MergeBounds(UnsafeList clusters, UnsafeList group)
+ public static unsafe ClodBounds MergeBounds(UnsafeList clusters, UnsafeList group)
{
- // Use Temp for the bounds list to support mega-meshes without stack overflow
- var boundsList = new UnsafeList((nuint)group.Length, Allocator.Temp);
-
- for (int j = 0; j < (int)group.Length; j++)
- {
+ var boundsList = new UnsafeList(group.Count, Allocator.Temp);
+ for (int j = 0; j < group.Count; j++)
boundsList.Add(clusters[group[j]].bounds);
- }
var merged = MeshOptApi.ComputeSphereBounds(
(float*)boundsList.GetUnsafePtr(),
- (nuint)group.Length,
+ (nuint)group.Count,
(nuint)sizeof(ClodBounds),
- (float*)boundsList.GetUnsafePtr() + 3, // offset to radius field
+ (float*)boundsList.GetUnsafePtr() + 3,
(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++)
- {
- result.error = Math.Max(result.error, clusters[group[j]].bounds.error);
- }
+ float maxError = 0.0f;
+ for (int j = 0; j < group.Count; j++)
+ maxError = Math.Max(maxError, clusters[group[j]].bounds.error);
boundsList.Dispose();
- return result;
+ return new ClodBounds
+ {
+ center = new Vector3(merged.center[0], merged.center[1], merged.center[2]),
+ radius = merged.radius,
+ error = maxError
+ };
}
}
diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodBuilder.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodBuilder.cs
index 0bf5bce..f082234 100644
--- a/src/Runtime/Ghost.Graphics/Meshlet/ClodBuilder.cs
+++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodBuilder.cs
@@ -1,8 +1,8 @@
using System;
using System.Diagnostics;
-using System.Numerics;
using Ghost.MeshOptimizer;
-using Misaki.HighPerformance;
+using Misaki.HighPerformance.LowLevel.Buffer;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Meshlet;
@@ -21,78 +21,69 @@ public unsafe static class ClodBuilder
{
Debug.Assert(mesh.vertexAttributesStride % (nuint)sizeof(float) == 0, "vertexAttributesStride must be a multiple of sizeof(float)");
- // Use Persistent or Temp for large mesh data to avoid stack overflow
- var locks = new UnsafeList(mesh.vertexCount, Allocator.Temp);
+ var locks = new UnsafeList((int)mesh.vertexCount, Allocator.Temp);
locks.AsSpan().Fill(0);
- // Generate position-only remap
- var remap = new UnsafeList(mesh.vertexCount, Allocator.Temp);
- MeshOptApi.GeneratePositionRemap(remap.GetUnsafePtr(), mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
+ var remap = new UnsafeList((int)mesh.vertexCount, Allocator.Temp);
+ remap.Resize((int)mesh.vertexCount);
+ MeshOptApi.GeneratePositionRemap((uint*)remap.GetUnsafePtr(), mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride);
- // Set up protect bits on UV seams
if (mesh.attributeProtectMask != 0)
{
nuint maxAttributes = mesh.vertexAttributesStride / sizeof(float);
for (nuint i = 0; i < mesh.vertexCount; i++)
{
- uint r = remap[(int)i];
+ uint r = ((uint*)remap.GetUnsafePtr())[(int)i];
for (nuint j = 0; j < maxAttributes; j++)
{
if ((r != i) && ((mesh.attributeProtectMask & (1u << (int)j)) != 0))
{
if (mesh.vertexAttributes[i * maxAttributes + j] != mesh.vertexAttributes[r * maxAttributes + j])
{
- locks[(int)i] |= (byte)MeshOptApi.SimplifyVertex_Protect;
+ ((byte*)locks.GetUnsafePtr())[(int)i] |= (byte)(Api.meshopt_SimplifyVertex_Protect & 0xFF);
}
}
}
}
}
- // Initial clusterization
var clusters = ClodInternal.Clusterize(config, mesh, mesh.indices, mesh.indexCount, Allocator.Persistent);
- // Compute initial bounds
- for (int i = 0; i < (int)clusters.Length; i++)
+ for (int i = 0; i < clusters.Count; i++)
{
clusters[i].bounds = ClodBoundsHelper.ComputeBounds(mesh, clusters[i].indices, 0.0f);
}
- var pending = new UnsafeList(clusters.Length, Allocator.Temp);
- for (int i = 0; i < (int)clusters.Length; i++)
+ var pending = new UnsafeList(clusters.Count, Allocator.Temp);
+ for (int i = 0; i < clusters.Count; i++)
pending.Add(i);
int depth = 0;
- while (pending.Length > 1)
+ while (pending.Count > 1)
{
- // Partition results are temporary but returned, using Temp allocator
var groups = ClodPartition.Partition(config, mesh, clusters, pending, remap, Allocator.Temp);
-
pending.Clear();
- // Lock boundaries
ClodBoundary.LockBoundary(locks, groups, clusters, remap, mesh.vertexLock);
- for (int i = 0; i < (int)groups.Length; i++)
+ for (int i = 0; i < groups.Count; i++)
{
- // Merged indices for a group of clusters
- var merged = new UnsafeList((nuint)groups[i].Length * config.maxTriangles * 3, Allocator.Temp);
- for (int j = 0; j < (int)groups[i].Length; j++)
+ var merged = new UnsafeList(groups[i].Count * (int)config.maxTriangles * 3, Allocator.Temp);
+ for (int j = 0; j < groups[i].Count; j++)
{
var clusterIndices = clusters[groups[i][j]].indices;
- for (int k = 0; k < (int)clusterIndices.Length; k++)
+ for (int k = 0; k < clusterIndices.Count; k++)
merged.Add(clusterIndices[k]);
}
- nuint targetSize = ((nuint)merged.Length / 3) * (nuint)config.simplifyRatio * 3;
-
+ nuint targetSize = ((nuint)merged.Count / 3) * (nuint)config.simplifyRatio * 3;
var bounds = ClodBoundsHelper.MergeBounds(clusters, groups[i]);
float error = 0.0f;
var simplified = ClodSimplify.Simplify(config, mesh, merged, locks, targetSize, &error);
- if (simplified.Length > (nuint)(merged.Length * config.simplifyThreshold))
+ if ((nuint)simplified.Count > (nuint)(merged.Count * config.simplifyThreshold))
{
bounds.error = float.MaxValue;
OutputGroup(config, mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback);
@@ -104,49 +95,41 @@ public unsafe static class ClodBuilder
int refined = OutputGroup(config, mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback);
- // Discard old clusters
- for (int j = 0; j < (int)groups[i].Length; j++)
- {
+ for (int j = 0; j < groups[i].Count; j++)
clusters[groups[i][j]].indices.Dispose();
- }
- // Clusterize simplified mesh
- var split = ClodInternal.Clusterize(config, mesh, simplified.GetUnsafePtr(), simplified.Length, Allocator.Persistent);
- for (int j = 0; j < (int)split.Length; j++)
+ var split = ClodInternal.Clusterize(config, mesh, (uint*)simplified.GetUnsafePtr(), (nuint)simplified.Count, Allocator.Persistent);
+ for (int j = 0; j < split.Count; j++)
{
split[j].refined = refined;
split[j].bounds = bounds;
clusters.Add(split[j]);
- pending.Add((int)clusters.Length - 1);
+ pending.Add(clusters.Count - 1);
}
split.Dispose();
merged.Dispose();
}
- // Cleanup groups
- for (int i = 0; i < (int)groups.Length; i++)
+ for (int i = 0; i < groups.Count; i++)
groups[i].Dispose();
groups.Dispose();
depth++;
}
- if (pending.Length > 0)
+ if (pending.Count > 0)
{
- var cluster = clusters[pending[0]];
- var bounds = cluster.bounds;
+ var bounds = clusters[pending[0]].bounds;
bounds.error = float.MaxValue;
OutputGroup(config, mesh, clusters, pending, bounds, depth, outputContext, outputCallback);
}
- nuint finalClusterCount = clusters.Length;
+ nuint finalClusterCount = (nuint)clusters.Count;
- // Cleanup
- for (int i = 0; i < (int)clusters.Length; i++)
+ for (int i = 0; i < clusters.Count; i++)
clusters[i].indices.Dispose();
clusters.Dispose();
-
locks.Dispose();
remap.Dispose();
pending.Dispose();
@@ -165,28 +148,26 @@ public unsafe static class ClodBuilder
ClodOutputDelegate outputCallback
)
{
- // Use Temp for the output array to avoid stack pressure
- var groupClusters = new UnsafeList(group.Length, Allocator.Temp);
+ var groupClusters = new UnsafeList(group.Count, Allocator.Temp);
- for (int i = 0; i < (int)group.Length; i++)
+ for (int i = 0; i < group.Count; i++)
{
ref var srcCluster = ref clusters[group[i]];
- var dstCluster = new ClodCluster
+ groupClusters.Add(new ClodCluster
{
refined = srcCluster.refined,
bounds = (config.optimizeBounds && srcCluster.refined != -1)
? ClodBoundsHelper.ComputeBounds(mesh, srcCluster.indices, srcCluster.bounds.error)
: srcCluster.bounds,
- indices = srcCluster.indices.GetUnsafePtr(),
- indexCount = (nuint)srcCluster.indices.Length,
+ indices = (uint*)srcCluster.indices.GetUnsafePtr(),
+ indexCount = (nuint)srcCluster.indices.Count,
vertexCount = srcCluster.vertices
- };
- groupClusters.Add(dstCluster);
+ });
}
var clodGroup = new ClodGroup { depth = depth, simplified = simplified };
int result = outputCallback != null
- ? outputCallback(outputContext, clodGroup, (ClodCluster*)groupClusters.GetUnsafePtr(), (nuint)groupClusters.Length)
+ ? outputCallback(outputContext, clodGroup, (ClodCluster*)groupClusters.GetUnsafePtr(), (nuint)groupClusters.Count)
: -1;
groupClusters.Dispose();
diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal.cs
index 88d8035..7184073 100644
--- a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal.cs
+++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal.cs
@@ -1,92 +1,79 @@
using System;
using Ghost.MeshOptimizer;
-using Misaki.HighPerformance;
+using Misaki.HighPerformance.LowLevel.Buffer;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Meshlet;
internal static class ClodInternal
{
- public static UnsafeList Clusterize(ClodConfig config, ClodMesh mesh, uint* indices, nuint indexCount, Allocator allocator)
+ public static unsafe UnsafeList Clusterize(ClodConfig config, ClodMesh mesh, uint* indices, nuint indexCount, Allocator allocator)
{
nuint maxMeshlets = MeshOptApi.BuildMeshletsBound(indexCount, config.maxVertices, config.minTriangles);
- var meshlets = new UnsafeList(maxMeshlets, allocator);
- var meshletVertices = new UnsafeList(indexCount, allocator);
- var meshletTriangles = new UnsafeList(indexCount, allocator);
+ var meshlets = new UnsafeList((int)maxMeshlets, Allocator.Temp);
+ meshlets.Resize((int)maxMeshlets);
+ var meshletVertices = new UnsafeList((int)indexCount, Allocator.Temp);
+ meshletVertices.Resize((int)indexCount);
+ var meshletTriangles = new UnsafeList((int)indexCount, Allocator.Temp);
+ meshletTriangles.Resize((int)indexCount);
+
+ meshopt_Meshlet* pMeshlets = (meshopt_Meshlet*)meshlets.GetUnsafePtr();
+ uint* pMeshletVertices = (uint*)meshletVertices.GetUnsafePtr();
+ byte* pMeshletTriangles = (byte*)meshletTriangles.GetUnsafePtr();
- meshlets.Resize(maxMeshlets);
-
nuint meshletCount;
if (config.clusterSpatial)
{
- meshletCount = MeshOptApi.BuildMeshletsSpatial(
- meshlets.GetUnsafePtr(),
- meshletVertices.GetUnsafePtr(),
- meshletTriangles.GetUnsafePtr(),
- indices,
- indexCount,
- mesh.vertexPositions,
- mesh.vertexCount,
- mesh.vertexPositionsStride,
- config.maxVertices,
- config.minTriangles,
- config.maxTriangles,
+ meshletCount = pMeshlets[0].BuildsSpatial(
+ pMeshletVertices, pMeshletTriangles,
+ indices, indexCount,
+ mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride,
+ config.maxVertices, config.minTriangles, config.maxTriangles,
config.clusterFillWeight
);
}
else
{
- meshletCount = MeshOptApi.BuildMeshletsFlex(
- meshlets.GetUnsafePtr(),
- meshletVertices.GetUnsafePtr(),
- meshletTriangles.GetUnsafePtr(),
- indices,
- indexCount,
- mesh.vertexPositions,
- mesh.vertexCount,
- mesh.vertexPositionsStride,
- config.maxVertices,
- config.minTriangles,
- config.maxTriangles,
- 0.0f,
- config.clusterSplitFactor
+ meshletCount = pMeshlets[0].BuildsFlex(
+ pMeshletVertices, pMeshletTriangles,
+ indices, indexCount,
+ mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride,
+ config.maxVertices, config.minTriangles, config.maxTriangles,
+ 0.0f, config.clusterSplitFactor
);
}
- meshlets.Resize(meshletCount);
- var clusters = new UnsafeList(meshletCount, allocator);
-
+ var clusters = new UnsafeList((int)meshletCount, allocator);
+
for (nuint i = 0; i < meshletCount; i++)
{
- ref var meshlet = ref meshlets[i];
+ ref var meshlet = ref pMeshlets[i];
if (config.optimizeClusters)
{
MeshOptApi.OptimizeMeshlet(
- meshletVertices.GetUnsafePtr() + meshlet.vertexOffset,
- meshletTriangles.GetUnsafePtr() + meshlet.triangleOffset,
- meshlet.triangleCount,
- meshlet.vertexCount
+ pMeshletVertices + meshlet.vertex_offset,
+ pMeshletTriangles + meshlet.triangle_offset,
+ meshlet.triangle_count,
+ meshlet.vertex_count
);
}
var cluster = new Cluster
{
- vertices = meshlet.vertexCount,
- indices = new UnsafeList(meshlet.triangleCount * 3, allocator),
+ vertices = meshlet.vertex_count,
+ indices = new UnsafeList((int)(meshlet.triangle_count * 3), allocator),
group = -1,
refined = -1
};
- for (nuint j = 0; j < meshlet.triangleCount * 3; j++)
- {
- cluster.indices.Add(meshletVertices[(int)(meshlet.vertexOffset + meshletTriangles[(int)(meshlet.triangleOffset + j)])]);
- }
+ for (nuint j = 0; j < meshlet.triangle_count * 3; j++)
+ cluster.indices.Add(pMeshletVertices[meshlet.vertex_offset + pMeshletTriangles[meshlet.triangle_offset + j]]);
clusters.Add(cluster);
}
- // Cleanup
meshlets.Dispose();
meshletVertices.Dispose();
meshletTriangles.Dispose();
diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Boundary.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Boundary.cs
index 70b64d1..2715395 100644
--- a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Boundary.cs
+++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Boundary.cs
@@ -1,48 +1,48 @@
using Ghost.MeshOptimizer;
-using Misaki.HighPerformance;
+using Misaki.HighPerformance.LowLevel.Buffer;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Meshlet;
internal static class ClodBoundary
{
- public static void LockBoundary(UnsafeList locks, UnsafeList> groups, UnsafeList clusters, UnsafeList remap, byte* vertexLock)
+ public static unsafe void LockBoundary(UnsafeList locks, UnsafeList> groups, UnsafeList clusters, UnsafeList remap, byte* vertexLock)
{
- for (int i = 0; i < (int)locks.Length; i++)
- {
- locks[i] &= ~((byte)((1 << 0) | (1 << 7)));
- }
+ byte* pLocks = (byte*)locks.GetUnsafePtr();
+ uint* pRemap = (uint*)remap.GetUnsafePtr();
- for (int i = 0; i < (int)groups.Length; i++)
+ for (int i = 0; i < locks.Count; i++)
+ pLocks[i] = unchecked((byte)(pLocks[i] & ~((1 << 0) | (1 << 7))));
+
+ for (int i = 0; i < groups.Count; i++)
{
- for (int j = 0; j < (int)groups[i].Length; j++)
+ for (int j = 0; j < groups[i].Count; j++)
{
var cluster = clusters[groups[i][j]];
- for (int k = 0; k < (int)cluster.indices.Length; k++)
+ for (int k = 0; k < cluster.indices.Count; k++)
{
- uint v = cluster.indices[k];
- uint r = remap[(int)v];
- locks[(int)r] |= (byte)(locks[(int)r] >> 7);
+ uint r = pRemap[(int)cluster.indices[k]];
+ pLocks[r] |= (byte)(pLocks[r] >> 7);
}
}
- for (int j = 0; j < (int)groups[i].Length; j++)
+ for (int j = 0; j < groups[i].Count; j++)
{
var cluster = clusters[groups[i][j]];
- for (int k = 0; k < (int)cluster.indices.Length; k++)
+ for (int k = 0; k < cluster.indices.Count; k++)
{
- uint v = cluster.indices[k];
- uint r = remap[(int)v];
- locks[(int)r] |= (byte)(1 << 7);
+ uint r = pRemap[(int)cluster.indices[k]];
+ pLocks[r] |= (byte)(1 << 7);
}
}
}
- for (int i = 0; i < (int)locks.Length; i++)
+ for (int i = 0; i < locks.Count; i++)
{
- uint r = remap[i];
- locks[i] = (byte)((locks[(int)r] & 1) | (locks[i] & (byte)MeshOptApi.meshopt_SimplifyVertex_Protect));
+ uint r = pRemap[i];
+ pLocks[i] = (byte)((pLocks[r] & 1) | (pLocks[i] & (byte)(Api.meshopt_SimplifyVertex_Protect & 0xFF)));
if (vertexLock != null)
- locks[i] |= vertexLock[i];
+ pLocks[i] |= vertexLock[i];
}
}
}
diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Partition.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Partition.cs
index 55bf8b3..b0423fe 100644
--- a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Partition.cs
+++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Partition.cs
@@ -1,71 +1,59 @@
using System;
using Ghost.MeshOptimizer;
-using Misaki.HighPerformance;
+using Misaki.HighPerformance.LowLevel.Buffer;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Meshlet;
internal static class ClodPartition
{
- public static UnsafeList> Partition(ClodConfig config, ClodMesh mesh, UnsafeList clusters, UnsafeList pending, UnsafeList remap, Allocator allocator)
+ public static unsafe UnsafeList> Partition(ClodConfig config, ClodMesh mesh, UnsafeList clusters, UnsafeList pending, UnsafeList remap, Allocator allocator)
{
- if (pending.Length <= (int)config.partitionSize)
+ if (pending.Count <= (int)config.partitionSize)
{
- var partitions = new UnsafeList>(1, allocator);
- partitions.Add(pending);
- return partitions;
+ var single = new UnsafeList>(1, allocator);
+ single.Add(pending);
+ return single;
}
- // Internal counters/indices can stay on Temp to avoid stack overflow for huge meshes
- var clusterIndices = new UnsafeList(1024, Allocator.Temp);
- var clusterCounts = new UnsafeList((nuint)pending.Length, Allocator.Temp);
-
nuint totalIndexCount = 0;
- for (int i = 0; i < pending.Length; i++)
- {
- var cluster = clusters[pending[i]];
- totalIndexCount += cluster.indices.Length;
- }
+ for (int i = 0; i < pending.Count; i++)
+ totalIndexCount += (nuint)clusters[pending[i]].indices.Count;
- clusterIndices.Resize(totalIndexCount);
+ var clusterIndices = new UnsafeList((int)totalIndexCount, Allocator.Temp);
+ var clusterCounts = new UnsafeList(pending.Count, Allocator.Temp);
nuint offset = 0;
- for (int i = 0; i < pending.Length; i++)
+ for (int i = 0; i < pending.Count; i++)
{
var cluster = clusters[pending[i]];
- clusterCounts.Add((uint)cluster.indices.Length);
-
- for (int j = 0; j < (int)cluster.indices.Length; j++)
- {
- clusterIndices[(int)offset + j] = remap[(int)cluster.indices[j]];
- }
- offset += (nuint)cluster.indices.Length;
+ clusterCounts.Add((uint)cluster.indices.Count);
+ for (int j = 0; j < cluster.indices.Count; j++)
+ clusterIndices.Add(((uint*)remap.GetUnsafePtr())[(int)cluster.indices[j]]);
+ offset += (nuint)cluster.indices.Count;
}
- var clusterPart = new UnsafeList((nuint)pending.Length, Allocator.Temp);
- clusterPart.Resize((nuint)pending.Length);
+ var clusterPart = new UnsafeList(pending.Count, Allocator.Temp);
+ clusterPart.Resize(pending.Count);
nuint partitionCount = MeshOptApi.PartitionClusters(
- clusterPart.GetUnsafePtr(),
- clusterIndices.GetUnsafePtr(),
+ (uint*)clusterPart.GetUnsafePtr(),
+ (uint*)clusterIndices.GetUnsafePtr(),
totalIndexCount,
- clusterCounts.GetUnsafePtr(),
- (nuint)pending.Length,
+ (uint*)clusterCounts.GetUnsafePtr(),
+ (nuint)pending.Count,
config.partitionSpatial ? mesh.vertexPositions : null,
- remap.Length,
+ (nuint)remap.Count,
mesh.vertexPositionsStride,
config.partitionSize
);
- var partitions = new UnsafeList>(partitionCount, allocator);
+ var partitions = new UnsafeList>((int)partitionCount, allocator);
for (nuint i = 0; i < partitionCount; i++)
- {
- partitions.Add(new UnsafeList((nuint)(config.partitionSize + config.partitionSize / 3), allocator));
- }
+ partitions.Add(new UnsafeList((int)(config.partitionSize + config.partitionSize / 3), allocator));
- for (int i = 0; i < pending.Length; i++)
- {
- partitions[(int)clusterPart[i]].Add(pending[i]);
- }
+ for (int i = 0; i < pending.Count; i++)
+ partitions[(int)((uint*)clusterPart.GetUnsafePtr())[i]].Add(pending[i]);
clusterIndices.Dispose();
clusterCounts.Dispose();
diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodMesh.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodMesh.cs
index e552d89..3d6869b 100644
--- a/src/Runtime/Ghost.Graphics/Meshlet/ClodMesh.cs
+++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodMesh.cs
@@ -1,6 +1,8 @@
using System;
using Ghost.MeshOptimizer;
-using Misaki.HighPerformance;
+using Misaki.HighPerformance.LowLevel.Collections;
+using Misaki.HighPerformance.LowLevel;
+using Misaki.HighPerformance.LowLevel.Buffer;
namespace Ghost.Graphics.Meshlet;
diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodSimplify.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodSimplify.cs
index 09e9f86..600d410 100644
--- a/src/Runtime/Ghost.Graphics/Meshlet/ClodSimplify.cs
+++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodSimplify.cs
@@ -1,12 +1,13 @@
using System;
using Ghost.MeshOptimizer;
-using Misaki.HighPerformance;
+using Misaki.HighPerformance.LowLevel.Buffer;
+using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Meshlet;
internal static class ClodSimplify
{
- public static UnsafeList Simplify(
+ public static unsafe UnsafeList Simplify(
ClodConfig config,
ClodMesh mesh,
UnsafeList indices,
@@ -15,25 +16,22 @@ internal static class ClodSimplify
float* error
)
{
- if (targetCount > (nuint)indices.Length)
- {
+ if (targetCount >= (nuint)indices.Count)
return indices;
- }
- // Use Allocator.Temp for LOD results to avoid stack overflow on mega-meshes
- var lod = new UnsafeList((nuint)indices.Length, Allocator.Temp);
- lod.Resize((nuint)indices.Length);
+ var lod = new UnsafeList(indices.Count, Allocator.Temp);
+ lod.Resize(indices.Count);
- uint options = MeshOptApi.SimplifySparse | MeshOptApi.SimplifyErrorAbsolute;
+ uint options = (uint)(Api.meshopt_SimplifySparse | Api.meshopt_SimplifyErrorAbsolute);
if (config.simplifyPermissive)
- options |= MeshOptApi.SimplifyPermissive;
+ options |= (uint)Api.meshopt_SimplifyPermissive;
if (config.simplifyRegularize)
- options |= MeshOptApi.SimplifyRegularize;
+ options |= (uint)Api.meshopt_SimplifyRegularize;
nuint resultSize = MeshOptApi.SimplifyWithAttributes(
- lod.GetUnsafePtr(),
- indices.GetUnsafePtr(),
- (nuint)indices.Length,
+ (uint*)lod.GetUnsafePtr(),
+ (uint*)indices.GetUnsafePtr(),
+ (nuint)indices.Count,
mesh.vertexPositions,
mesh.vertexCount,
mesh.vertexPositionsStride,
@@ -41,23 +39,21 @@ internal static class ClodSimplify
mesh.vertexAttributesStride,
mesh.attributeWeights,
mesh.attributeCount,
- locks.GetUnsafePtr(),
+ (byte*)locks.GetUnsafePtr(),
targetCount,
float.MaxValue,
options,
error
);
+ lod.Resize((int)resultSize);
- lod.Resize(resultSize);
-
- // Fallback to permissive if needed
- if (lod.Length > targetCount && config.simplifyFallbackPermissive && !config.simplifyPermissive)
+ if ((nuint)lod.Count > targetCount && config.simplifyFallbackPermissive && !config.simplifyPermissive)
{
- options |= MeshOptApi.SimplifyPermissive;
+ options |= (uint)Api.meshopt_SimplifyPermissive;
resultSize = MeshOptApi.SimplifyWithAttributes(
- lod.GetUnsafePtr(),
- indices.GetUnsafePtr(),
- (nuint)indices.Length,
+ (uint*)lod.GetUnsafePtr(),
+ (uint*)indices.GetUnsafePtr(),
+ (nuint)indices.Count,
mesh.vertexPositions,
mesh.vertexCount,
mesh.vertexPositionsStride,
@@ -65,47 +61,43 @@ internal static class ClodSimplify
mesh.vertexAttributesStride,
mesh.attributeWeights,
mesh.attributeCount,
- locks.GetUnsafePtr(),
+ (byte*)locks.GetUnsafePtr(),
targetCount,
float.MaxValue,
options,
error
);
- lod.Resize(resultSize);
+ lod.Resize((int)resultSize);
}
- // Sloppy fallback
- if (lod.Length > targetCount && config.simplifyFallbackSloppy)
+ if ((nuint)lod.Count > 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)
- {
- uint a = indices[i], b = indices[i + 1], c = indices[i + 2];
-
- int posStride = (int)(mesh.vertexPositionsStride / sizeof(float));
- float* va = mesh.vertexPositions + (a * posStride);
- float* vb = mesh.vertexPositions + (b * posStride);
- float* vc = mesh.vertexPositions + (c * posStride);
+ uint* pIdx = (uint*)indices.GetUnsafePtr();
+ int posStride = (int)(mesh.vertexPositionsStride / sizeof(float));
- float dx = va[0] - vb[0], dy = va[1] - vb[1], dz = va[2] - vb[2];
+ for (int i = 0; i < indices.Count; i += 3)
+ {
+ uint a = pIdx[i], b = pIdx[i + 1], c = pIdx[i + 2];
+ float* va = mesh.vertexPositions + (a * (uint)posStride);
+ float* vb = mesh.vertexPositions + (b * (uint)posStride);
+ float* vc = mesh.vertexPositions + (c * (uint)posStride);
+
+ float dx, dy, dz;
+ 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));
}
@@ -114,16 +106,4 @@ internal static class ClodSimplify
return lod;
}
-
- private static void SimplifyFallback(
- UnsafeList lod,
- ClodMesh mesh,
- UnsafeList indices,
- UnsafeList locks,
- nuint targetCount,
- float* error
- )
- {
- // Placeholder for sloppy simplification fallback logic
- }
}