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

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;
}