feat(ufbx): switch to native ufbx_vec/quat/matrix types
Replaces all Misaki.HighPerformance.Mathematics vector, quaternion, and matrix types in Ghost.Ufbx bindings with new native ufbx_vec2, ufbx_vec3, ufbx_vec4, ufbx_quat, and ufbx_matrix structs. Updates all interop code, struct fields, and API signatures accordingly. Adds struct definitions for the new types and provides matrix operations as struct methods. Removes unnecessary math package reference. Also includes minor fixes to system attributes, meshlet LOD logic, and mesh utility. BREAKING CHANGE: All Ufbx-related APIs now use ufbx_* types instead of Misaki.HighPerformance.Mathematics types. Existing code using the old types will require updates.
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.3.1" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.3.3" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.5" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.6" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -19,9 +19,13 @@ public static class MathUtility
|
||||
{
|
||||
var R = new float3x3(rotation);
|
||||
return new float4x4(
|
||||
R[0][0] * scale.x, R[0][1] * scale.y, R[0][2] * scale.z, position.x,
|
||||
R[1][0] * scale.x, R[1][1] * scale.y, R[1][2] * scale.z, position.y,
|
||||
R[2][0] * scale.x, R[2][1] * scale.y, R[2][2] * scale.z, position.z,
|
||||
// Row 0: Right.x, Up.x, Forward.x, Pos.x
|
||||
R[0][0] * scale.x, R[1][0] * scale.y, R[2][0] * scale.z, position.x,
|
||||
// Row 1: Right.y, Up.y, Forward.y, Pos.y
|
||||
R[0][1] * scale.x, R[1][1] * scale.y, R[2][1] * scale.z, position.y,
|
||||
// Row 2: Right.z, Up.z, Forward.z, Pos.z
|
||||
R[0][2] * scale.x, R[1][2] * scale.y, R[2][2] * scale.z, position.z,
|
||||
// Row 3: 0, 0, 0, 1
|
||||
0f, 0f, 0f, 1f
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,26 +113,26 @@ public abstract class SystemBase : ISystem
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
|
||||
public class UpdateAfterAttribute : Attribute
|
||||
public abstract class UpdateAfterAttribute : Attribute
|
||||
{
|
||||
public Type SystemType { get; }
|
||||
public abstract Type SystemType { get; }
|
||||
}
|
||||
|
||||
public UpdateAfterAttribute(Type systemType)
|
||||
{
|
||||
SystemType = systemType;
|
||||
}
|
||||
public abstract class UpdateBeforeAttribute : Attribute
|
||||
{
|
||||
public abstract Type SystemType { get; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
|
||||
public class UpdateBeforeAttribute : Attribute
|
||||
public class UpdateAfterAttribute<T> : UpdateAfterAttribute
|
||||
{
|
||||
public Type SystemType { get; }
|
||||
public override Type SystemType => typeof(T);
|
||||
}
|
||||
|
||||
public UpdateBeforeAttribute(Type systemType)
|
||||
{
|
||||
SystemType = systemType;
|
||||
}
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
|
||||
public class UpdateBeforeAttribute<T> : UpdateBeforeAttribute
|
||||
{
|
||||
public override Type SystemType => typeof(T);
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TerraFX.Interop.D3D12MemoryAllocator" Version="3.0.1" />
|
||||
<PackageReference Include="TerraFX.Interop.D3D12MemoryAllocator" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -15,10 +15,12 @@ namespace Ghost.Graphics.Core;
|
||||
public struct Meshlet
|
||||
{
|
||||
public SphereBounds boundingSphere; // 16 bytes
|
||||
public SphereBounds parentBoundingSphere; // 16 bytes
|
||||
public AABB boundingBox; // 24 bytes
|
||||
public uint vertexOffset; // offset into meshlet vertex index array
|
||||
public uint triangleOffset; // offset into packed triangle array
|
||||
public uint groupIndex; // owning group
|
||||
public float clusterError; // geometric error of this meshlet/cluster
|
||||
public float parentError; // geometric refinement error carried into runtime LOD tests
|
||||
public byte vertexCount; // max 64
|
||||
public byte triangleCount; // max 124
|
||||
@@ -319,13 +321,15 @@ public struct Mesh : IResourceReleasable
|
||||
var meshlet = new Meshlet
|
||||
{
|
||||
boundingSphere = new SphereBounds(cluster.bounds.center, cluster.bounds.radius),
|
||||
parentBoundingSphere = new SphereBounds(group.simplified.center, group.simplified.radius),
|
||||
boundingBox = new AABB(cluster.bounds.center - cluster.bounds.radius, cluster.bounds.center + cluster.bounds.radius),
|
||||
vertexCount = (byte)cluster.vertexCount,
|
||||
triangleCount = (byte)(cluster.localIndexCount / 3),
|
||||
vertexOffset = (uint)data.meshletVertices.Count,
|
||||
triangleOffset = (uint)data.meshletTriangles.Count,
|
||||
groupIndex = (uint)data.groups.Count - 1,
|
||||
parentError = cluster.bounds.error,
|
||||
clusterError = cluster.bounds.error,
|
||||
parentError = group.simplified.error,
|
||||
localMaterialIndex = 0, // TODO: support multiple materials
|
||||
lodLevel = (byte)group.depth,
|
||||
};
|
||||
|
||||
@@ -13,11 +13,13 @@ struct Vertex
|
||||
struct Meshlet
|
||||
{
|
||||
float4 boundingSphere;
|
||||
float4 parentBoundingSphere;
|
||||
float3 boundingBoxMin;
|
||||
float3 boundingBoxMax;
|
||||
uint vertexOffset;
|
||||
uint triangleOffset;
|
||||
uint groupIndex;
|
||||
float clusterError;
|
||||
float parentError;
|
||||
uint packedCounts; // byte vertexCount, byte triangleCount, byte localMaterialIndex, byte lodLevel
|
||||
};
|
||||
|
||||
@@ -46,19 +46,59 @@ shader "MyShader/Standard"
|
||||
|
||||
groupshared ASPayload s_Payload;
|
||||
|
||||
// computes approximate (perspective) projection error of a cluster in screen space
|
||||
float BoundsError(float3 center, float radius, float error, float3 cameraPos, float cameraProj, float cameraZNear)
|
||||
{
|
||||
float d = length(center - cameraPos) - radius;
|
||||
return error / max(d, cameraZNear) * (cameraProj * 0.5f);
|
||||
}
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void ASMain(uint3 groupID : SV_GroupID)
|
||||
{
|
||||
InstanceData instanceData = LoadData<InstanceData>(g_PushConstantData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
||||
|
||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
||||
Meshlet meshlet = meshletBuffer.Load<Meshlet>(groupID.x * sizeof(Meshlet));
|
||||
|
||||
uint meshletIndex = groupID.x;
|
||||
s_Payload.meshletIndex = groupID.x;
|
||||
|
||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
||||
Meshlet meshlet = meshletBuffer.Load<Meshlet>(meshletIndex * sizeof(Meshlet));
|
||||
|
||||
ViewData viewData = LoadData<ViewData>(g_PushConstantData.viewBuffer, 0);
|
||||
|
||||
// Assume uniform scale
|
||||
float scale = length(float3(instanceData.localToWorld[0][0], instanceData.localToWorld[1][0], instanceData.localToWorld[2][0]));
|
||||
|
||||
// Nanite projection metric: cot(fovy / 2) -> projectionMatrix[1][1]
|
||||
// Multiply by screenSize.y to get error in pixels.
|
||||
float cameraProj = viewData.projectionMatrix[1][1] * viewData.screenSize.y;
|
||||
|
||||
float threshold = 1.0f;
|
||||
|
||||
// Calculate THIS cluster's error using ITS OWN bounding sphere
|
||||
float3 clusterCenter = mul(instanceData.localToWorld, float4(meshlet.boundingSphere.xyz, 1.0f)).xyz;
|
||||
float clusterRadius = meshlet.boundingSphere.w * scale;
|
||||
float clusterError = meshlet.clusterError * scale;
|
||||
float clusterProjError = BoundsError(clusterCenter, clusterRadius, clusterError, viewData.cameraPosition, cameraProj, viewData.nearClip);
|
||||
|
||||
// Calculate the PARENT'S error using the PARENT'S bounding sphere
|
||||
float3 parentCenter = mul(instanceData.localToWorld, float4(meshlet.parentBoundingSphere.xyz, 1.0f)).xyz;
|
||||
float parentRadius = meshlet.parentBoundingSphere.w * scale;
|
||||
float parentError = meshlet.parentError * scale;
|
||||
float parentProjError = BoundsError(parentCenter, parentRadius, parentError, viewData.cameraPosition, cameraProj, viewData.nearClip);
|
||||
|
||||
bool isRoot = meshlet.parentError >= 3.402823466e+38F; // FLT_MAX
|
||||
uint emitMeshlet = ((parentProjError > threshold || isRoot) && clusterProjError <= threshold) ? 1u : 0u;
|
||||
|
||||
// Failsafe: if we are at the finest LOD (LOD 0) and the error is STILL too high,
|
||||
// we must render it anyway because there is no more detailed geometry to fall back on.
|
||||
uint lodLevel = (meshlet.packedCounts >> 24) & 0xFFu;
|
||||
uint emitMeshlet = lodLevel == 0u ? 1u : 0u;
|
||||
if (lodLevel == 0u && parentProjError > threshold)
|
||||
{
|
||||
emitMeshlet = 1u;
|
||||
}
|
||||
|
||||
DispatchMesh(emitMeshlet, 1u, 1u, s_Payload);
|
||||
}
|
||||
|
||||
@@ -140,6 +180,9 @@ shader "MyShader/Standard"
|
||||
float b = float((hash & 0x0000FFu)) / 255.0;
|
||||
|
||||
return float4(r, g, b, 1.0);
|
||||
|
||||
// InstanceData instanceData = LoadData<InstanceData>(g_PushConstantData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
// return mul(instanceData.localToWorld, float4(input.normal, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user