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:
2026-03-16 23:55:19 +00:00
parent f2b68955b1
commit 2ba60c4bae
8 changed files with 173 additions and 211 deletions

View File

@@ -12,8 +12,7 @@ internal static class ClodSimplify
UnsafeList<uint> indices,
UnsafeList<byte> 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<uint>(indices.Length, allocator);
using var scope = AllocationManager.CreateStackScope();
var lod = new UnsafeList<uint>(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<uint> indices,
UnsafeList<byte> 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
}
}