From 2ba60c4bae19b9022d7c6acbeaa4285a20aa53c2 Mon Sep 17 00:00:00 2001 From: Julian Date: Mon, 16 Mar 2026 23:55:19 +0000 Subject: [PATCH] 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 --- .../Ghost.Graphics/Meshlet/ClodBounds.cs | 6 +- .../Meshlet/ClodBoundsHelper.cs | 69 +++---- .../Ghost.Graphics/Meshlet/ClodBuilder.cs | 23 +-- .../Ghost.Graphics/Meshlet/ClodConfig.cs | 22 --- .../Ghost.Graphics/Meshlet/ClodInternal.cs | 28 +-- .../Meshlet/ClodInternal_Boundary.cs | 12 +- .../Meshlet/ClodInternal_Partition.cs | 45 +++-- .../Ghost.Graphics/Meshlet/ClodSimplify.cs | 179 ++++++++---------- 8 files changed, 173 insertions(+), 211 deletions(-) diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodBounds.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodBounds.cs index fab5797..8a6514c 100644 --- a/src/Runtime/Ghost.Graphics/Meshlet/ClodBounds.cs +++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodBounds.cs @@ -1,8 +1,10 @@ +using System.Numerics; + namespace Ghost.Graphics.Meshlet; -public unsafe struct ClodBounds +public struct ClodBounds { - public fixed float center[3]; + public Vector3 center; public float radius; public float error; } diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodBoundsHelper.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodBoundsHelper.cs index 93ff4f0..23e26b1 100644 --- a/src/Runtime/Ghost.Graphics/Meshlet/ClodBoundsHelper.cs +++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodBoundsHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using Ghost.MeshOptimizer; using Misaki.HighPerformance; @@ -8,56 +9,44 @@ internal static class ClodBoundsHelper { public static ClodBounds ComputeBounds(ClodMesh mesh, UnsafeList indices, float error) { - fixed (uint* pIndices = new uint[(int)indices.Length]) - { - 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[0] = bounds.center[0]; - result.center[1] = bounds.center[1]; - result.center[2] = bounds.center[2]; - result.radius = bounds.radius; - result.error = error; - return result; - } + var bounds = MeshOptApi.meshopt_computeClusterBounds(indices.Ptr, (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; } public static ClodBounds MergeBounds(UnsafeList clusters, UnsafeList group) { - var boundsList = new ClodBounds[group.Length]; + using var scope = AllocationManager.CreateStackScope(); + var boundsList = new UnsafeList(group.Length, scope.AllocationHandle); + boundsList.Resize((nuint)group.Length); + for (int j = 0; j < (int)group.Length; j++) { 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( - &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; + result.error = Math.Max(result.error, clusters[group[j]].bounds.error); } + + return result; } } diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodBuilder.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodBuilder.cs index d89a59a..5e9563f 100644 --- a/src/Runtime/Ghost.Graphics/Meshlet/ClodBuilder.cs +++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using Ghost.MeshOptimizer; using Misaki.HighPerformance; @@ -34,7 +35,7 @@ public unsafe static class ClodBuilder // Generate position-only remap var remap = new UnsafeList((int)mesh.vertexCount, allocator); 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 if (mesh.attributeProtectMask != 0) @@ -49,7 +50,7 @@ public unsafe static class ClodBuilder { 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) { - var groups = ClodInternal.Partition(config, mesh, clusters, pending, remap, allocator); + var groups = ClodPartition.Partition(config, mesh, clusters, pending, remap); pending.Clear(); // 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++) { - var merged = new UnsafeList(groups[i].Length * (int)config.MaxTriangles * 3, allocator); + var merged = new UnsafeList(groups[i].Length * (int)config.maxTriangles * 3, allocator); for (int j = 0; j < (int)groups[i].Length; j++) { var clusterIndices = clusters[groups[i][j]].indices; @@ -91,14 +92,14 @@ public unsafe static class ClodBuilder 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]); 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; OutputGroup(config, mesh, clusters, groups[i], bounds, depth, outputContext, outputCallback, allocator); @@ -107,7 +108,7 @@ public unsafe static class ClodBuilder 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); @@ -171,7 +172,7 @@ public unsafe static class ClodBuilder Allocator allocator ) { - var groupClusters = new UnsafeList((int)group.Length, allocator); + var groupClusters = new UnsafeList(group.Length, allocator); groupClusters.Resize((nuint)group.Length); for (int i = 0; i < (int)group.Length; i++) @@ -180,7 +181,7 @@ public unsafe static class ClodBuilder ref var dstCluster = ref groupClusters[i]; 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) : srcCluster.bounds; dstCluster.indices = srcCluster.indices.Ptr; diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodConfig.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodConfig.cs index b3de792..0384b72 100644 --- a/src/Runtime/Ghost.Graphics/Meshlet/ClodConfig.cs +++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodConfig.cs @@ -34,26 +34,4 @@ public struct ClodConfig public bool optimizeBounds; 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; } } diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal.cs index e1f67d4..dea7452 100644 --- a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal.cs +++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal.cs @@ -8,7 +8,7 @@ internal static class ClodInternal { public static UnsafeList 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(maxMeshlets, allocator); var meshletVertices = new UnsafeList(indexCount, allocator); @@ -17,9 +17,9 @@ internal static class ClodInternal meshlets.Resize(maxMeshlets); nuint meshletCount; - if (config.ClusterSpatial) + if (config.clusterSpatial) { - meshletCount = Api.meshopt_buildMeshletsSpatial( + meshletCount = MeshOptApi.meshopt_buildMeshletsSpatial( meshlets.Ptr, meshletVertices.Ptr, meshletTriangles.Ptr, @@ -28,15 +28,15 @@ internal static class ClodInternal mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride, - config.MaxVertices, - config.MinTriangles, - config.MaxTriangles, - config.ClusterFillWeight + config.maxVertices, + config.minTriangles, + config.maxTriangles, + config.clusterFillWeight ); } else { - meshletCount = Api.meshopt_buildMeshletsFlex( + meshletCount = MeshOptApi.meshopt_buildMeshletsFlex( meshlets.Ptr, meshletVertices.Ptr, meshletTriangles.Ptr, @@ -45,11 +45,11 @@ internal static class ClodInternal mesh.vertexPositions, mesh.vertexCount, mesh.vertexPositionsStride, - config.MaxVertices, - config.MinTriangles, - config.MaxTriangles, + config.maxVertices, + config.minTriangles, + config.maxTriangles, 0.0f, - config.ClusterSplitFactor + config.clusterSplitFactor ); } meshlets.Resize(meshletCount); @@ -60,9 +60,9 @@ internal static class ClodInternal { ref var meshlet = ref meshlets[i]; - if (config.OptimizeClusters) + if (config.optimizeClusters) { - Api.meshopt_optimizeMeshlet( + MeshOptApi.meshopt_optimizeMeshlet( meshletVertices.Ptr + meshlet.vertexOffset, meshletTriangles.Ptr + meshlet.triangleOffset, meshlet.triangleCount, diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Boundary.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Boundary.cs index 0fc8e7f..70b64d1 100644 --- a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Boundary.cs +++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Boundary.cs @@ -1,3 +1,10 @@ +using Ghost.MeshOptimizer; +using Misaki.HighPerformance; + +namespace Ghost.Graphics.Meshlet; + +internal static class ClodBoundary +{ public static void LockBoundary(UnsafeList locks, UnsafeList> groups, UnsafeList clusters, UnsafeList remap, byte* vertexLock) { for (int i = 0; i < (int)locks.Length; i++) @@ -7,7 +14,6 @@ for (int i = 0; i < (int)groups.Length; i++) { - // Mark remapped vertices for (int j = 0; j < (int)groups[i].Length; j++) { var cluster = clusters[groups[i][j]]; @@ -19,7 +25,6 @@ } } - // Mark seen for (int j = 0; j < (int)groups[i].Length; j++) { var cluster = clusters[groups[i][j]]; @@ -35,8 +40,9 @@ for (int i = 0; i < (int)locks.Length; 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) locks[i] |= vertexLock[i]; } } +} diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Partition.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Partition.cs index 209ddb4..a6d75d9 100644 --- a/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Partition.cs +++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodInternal_Partition.cs @@ -1,14 +1,24 @@ - public static UnsafeList> Partition(ClodConfig config, ClodMesh mesh, UnsafeList clusters, UnsafeList pending, UnsafeList remap, Allocator allocator) +using System; +using Ghost.MeshOptimizer; +using Misaki.HighPerformance; + +namespace Ghost.Graphics.Meshlet; + +internal static class ClodPartition +{ + public static UnsafeList> Partition(ClodConfig config, ClodMesh mesh, UnsafeList clusters, UnsafeList pending, UnsafeList remap) { - if (pending.Length <= (int)config.PartitionSize) + if (pending.Length <= (int)config.partitionSize) { - var partitions = new UnsafeList>(1, allocator); + using var scope = AllocationManager.CreateStackScope(); + var partitions = new UnsafeList>(1, scope.AllocationHandle); partitions.Add(pending); return partitions; } - var clusterIndices = new UnsafeList(1024, allocator); // Initial guess - var clusterCounts = new UnsafeList(pending.Length, allocator); + using var stackScope = AllocationManager.CreateStackScope(); + var clusterIndices = new UnsafeList(1024, stackScope.AllocationHandle); + var clusterCounts = new UnsafeList(pending.Length, stackScope.AllocationHandle); nuint totalIndexCount = 0; for (int i = 0; i < pending.Length; i++) @@ -32,33 +42,25 @@ offset += (nuint)cluster.indices.Length; } - var clusterPart = new UnsafeList(pending.Length, allocator); + var clusterPart = new UnsafeList(pending.Length, stackScope.AllocationHandle); clusterPart.Resize((nuint)pending.Length); - nuint partitionCount = Api.meshopt_partitionClusters( + nuint partitionCount = MeshOptApi.meshopt_partitionClusters( clusterPart.Ptr, clusterIndices.Ptr, totalIndexCount, clusterCounts.Ptr, (nuint)pending.Length, - config.PartitionSpatial ? mesh.vertexPositions : null, + config.partitionSpatial ? mesh.vertexPositions : null, remap.Length, mesh.vertexPositionsStride, - config.PartitionSize + config.partitionSize ); - var partitions = new UnsafeList>(partitionCount, allocator); + var partitions = new UnsafeList>(partitionCount, stackScope.AllocationHandle); for (nuint i = 0; i < partitionCount; i++) { - partitions.Add(new UnsafeList((nuint)(config.PartitionSize + config.PartitionSize / 3), allocator)); - } - - // 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. + partitions.Add(new UnsafeList((nuint)(config.partitionSize + config.partitionSize / 3), stackScope.AllocationHandle)); } for (int i = 0; i < pending.Length; i++) @@ -66,9 +68,6 @@ partitions[(int)clusterPart[i]].Add(pending[i]); } - clusterIndices.Dispose(); - clusterCounts.Dispose(); - clusterPart.Dispose(); - return partitions; } +} diff --git a/src/Runtime/Ghost.Graphics/Meshlet/ClodSimplify.cs b/src/Runtime/Ghost.Graphics/Meshlet/ClodSimplify.cs index db557d8..fe39f25 100644 --- a/src/Runtime/Ghost.Graphics/Meshlet/ClodSimplify.cs +++ b/src/Runtime/Ghost.Graphics/Meshlet/ClodSimplify.cs @@ -12,8 +12,7 @@ internal static class ClodSimplify UnsafeList indices, UnsafeList locks, nuint targetCount, - float* error, - Allocator allocator + float* error ) { if (targetCount > (nuint)indices.Length) @@ -21,106 +20,96 @@ internal static class ClodSimplify return indices; } - var lod = new UnsafeList(indices.Length, allocator); + using var scope = AllocationManager.CreateStackScope(); + var lod = new UnsafeList(indices.Length, scope.AllocationHandle); lod.Resize((nuint)indices.Length); - uint options = Api.meshopt_SimplifySparse | Api.meshopt_SimplifyErrorAbsolute; - if (config.SimplifyPermissive) - options |= Api.meshopt_SimplifyPermissive; - if (config.SimplifyRegularize) - options |= Api.meshopt_SimplifyRegularize; + uint options = MeshOptApi.meshopt_SimplifySparse | MeshOptApi.meshopt_SimplifyErrorAbsolute; + if (config.simplifyPermissive) + options |= MeshOptApi.meshopt_SimplifyPermissive; + if (config.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++) - pIndices[i] = indices[i]; - for (int i = 0; i < (int)locks.Length; i++) - pLocks[i] = locks[i]; + 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); - nuint 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 - ); + 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; - lod.Resize(resultSize); + float emax = Math.Max(Math.Max(eab, eac), ebc); + float emin = Math.Min(Math.Min(eab, eac), ebc); - // Fallback to permissive if needed - if (lod.Length > targetCount && config.SimplifyFallbackPermissive && !config.SimplifyPermissive) - { - 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 - if (lod.Length > targetCount && config.SimplifyFallbackSloppy) - { - SimplifyFallback(lod, mesh, indices, locks, targetCount, error, allocator); - *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); - - 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); - } + maxEdgeSq = Math.Max(maxEdgeSq, Math.Max(emin, emax / 4)); } + + *error = Math.Min(*error, (float)Math.Sqrt(maxEdgeSq) * config.simplifyErrorEdgeLimit); } return lod; @@ -132,12 +121,10 @@ internal static class ClodSimplify UnsafeList indices, UnsafeList locks, nuint targetCount, - float* error, - Allocator allocator + float* error ) { // Simplified version - deindex and use sloppy simplification // Implementation details would involve creating a subset for sparse simplification - // For now, this is a placeholder } }