You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
4.1 KiB
96 lines
4.1 KiB
using System.Numerics; |
|
using Elements.Assets; |
|
using SharpGLTF.Geometry; |
|
using SharpGLTF.Geometry.VertexTypes; |
|
using SharpGLTF.Materials; |
|
using SharpGLTF.Schema2; |
|
|
|
namespace Res2tf; |
|
|
|
public class MeshAsset(MeshX meshX, ModelRoot model) |
|
{ |
|
public MeshX MeshX { get; } = meshX; |
|
public Mesh Mesh { get; } = model.CreateMesh(Build(meshX)); |
|
|
|
static IMeshBuilder<MaterialBuilder> Build(MeshX meshX) |
|
=> (meshX.HasNormals, meshX.HasTangents) switch { |
|
(false, false) => Build<VertexPosition>(meshX), |
|
( true, false) => Build<VertexPositionNormal>(meshX), |
|
( true, true) => Build<VertexPositionNormalTangent>(meshX), |
|
_ => throw new InvalidOperationException(), |
|
}; |
|
|
|
static IMeshBuilder<MaterialBuilder> Build<G>(MeshX meshX) |
|
where G : struct, IVertexGeometry |
|
=> (meshX.HasColors, meshX.UV_ChannelCount) switch { |
|
(false, 0) => Build<G, VertexEmpty>(meshX), |
|
(false, 1) => Build<G, VertexTexture1>(meshX), |
|
(false, 2) => Build<G, VertexTexture2>(meshX), |
|
(false, 3) => Build<G, VertexTexture3>(meshX), |
|
(false, 4) => Build<G, VertexTexture4>(meshX), |
|
( true, 0) => Build<G, VertexColor1>(meshX), |
|
( true, 1) => Build<G, VertexColor1Texture1>(meshX), |
|
( true, 2) => Build<G, VertexColor1Texture2>(meshX), |
|
( true, 3) => Build<G, VertexColor1Texture3>(meshX), |
|
( true, 4) => Build<G, VertexColor1Texture4>(meshX), |
|
_ => throw new InvalidOperationException(), |
|
}; |
|
|
|
static IMeshBuilder<MaterialBuilder> Build<G, M>(MeshX meshX) |
|
where G : struct, IVertexGeometry |
|
where M : struct, IVertexMaterial |
|
=> meshX.HasBoneBindings |
|
? Build<G, M, VertexJoints4>(meshX) |
|
: Build<G, M, VertexEmpty>(meshX); |
|
|
|
static MeshBuilder<G, M, S> Build<G, M, S>(MeshX meshX) |
|
where G : struct, IVertexGeometry |
|
where M : struct, IVertexMaterial |
|
where S : struct, IVertexSkinning |
|
{ |
|
var mesh = new MeshBuilder<G, M, S>(); |
|
|
|
foreach (var subMeshX in meshX.Submeshes) { |
|
var prim = mesh.UsePrimitive(new()); // placeholder materials |
|
|
|
VertexBuilder<G, M, S> BuildVertex(Vertex v) { |
|
var vertex = new VertexBuilder<G, M, S> { Position = v.Position }; |
|
if (meshX.HasNormals ) vertex.Geometry.SetNormal(v.Normal); |
|
if (meshX.HasTangents) vertex.Geometry.SetTangent(v.Tangent4); |
|
if (meshX.HasColors ) vertex.Material.SetColor(0, new(v.Color.r, v.Color.g, v.Color.b, v.Color.a)); |
|
for (var i = 0; i < meshX.UV_ChannelCount; i++) vertex.Material.SetTexCoord(i, v.GetUV(i)); |
|
if (meshX.HasBoneBindings) { var b = v.BoneBinding; vertex.Skinning.SetBindings( |
|
(b.boneIndex0, b.weight0), (b.boneIndex1, b.weight1), (b.boneIndex2, b.weight2), (b.boneIndex3, b.weight3)); } |
|
return vertex; |
|
} |
|
|
|
foreach (var t in subMeshX.RawIndicies.Chunk(3)) |
|
prim.AddTriangle(BuildVertex(meshX.GetVertex(t[0])), |
|
BuildVertex(meshX.GetVertex(t[1])), |
|
BuildVertex(meshX.GetVertex(t[2]))); |
|
} |
|
|
|
return mesh; |
|
} |
|
|
|
public IEnumerable<Matrix4x4> GetInverseBindMatrices() |
|
=> MeshX.Bones |
|
.Select(b => b.BindPose) |
|
.Select(ResoniteToNumerics) |
|
.Select(NormalizeIdentityColumn); |
|
|
|
|
|
static Matrix4x4 ResoniteToNumerics(Elements.Core.float4x4 m) |
|
=> new(m.m00, m.m10, m.m20, m.m30, |
|
m.m01, m.m11, m.m21, m.m31, |
|
m.m02, m.m12, m.m22, m.m32, |
|
m.m03, m.m13, m.m23, m.m33); |
|
|
|
// glTF is very sensitive about the identity column being exactly (0, 0, 0, 1). |
|
static Matrix4x4 NormalizeIdentityColumn(Matrix4x4 m) |
|
=> (IsApproxEqual(m.M14, 0) && IsApproxEqual(m.M24, 0) && IsApproxEqual(m.M34, 0) && IsApproxEqual(m.M44, 1)) |
|
? m with { M14 = 0, M24 = 0, M34 = 0, M44 = 1 } : m; |
|
|
|
static bool IsApproxEqual(float a, float b, float epsilon = 0.00001f) |
|
=> MathF.Abs(a - b) < epsilon; |
|
}
|
|
|