Some cleanup and using new features / intellisense recommendations.

This commit is contained in:
Ottermandias 2024-01-06 18:37:52 +01:00
parent 51bb9cf7cd
commit 677c9bd801
5 changed files with 117 additions and 128 deletions

View file

@ -13,24 +13,17 @@ namespace Penumbra.Import.Models.Export;
public class MeshExporter
{
public class Mesh
public class Mesh(IEnumerable<IMeshBuilder<MaterialBuilder>> meshes, NodeBuilder[]? joints)
{
private IMeshBuilder<MaterialBuilder>[] _meshes;
private NodeBuilder[]? _joints;
public Mesh(IMeshBuilder<MaterialBuilder>[] meshes, NodeBuilder[]? joints)
{
_meshes = meshes;
_joints = joints;
}
public void AddToScene(SceneBuilder scene)
{
foreach (var mesh in _meshes)
if (_joints == null)
foreach (var mesh in meshes)
{
if (joints == null)
scene.AddRigidMesh(mesh, Matrix4x4.Identity);
else
scene.AddSkinnedMesh(mesh, Matrix4x4.Identity, _joints);
scene.AddSkinnedMesh(mesh, Matrix4x4.Identity, joints);
}
}
}
@ -43,9 +36,11 @@ public class MeshExporter
private const byte MaximumMeshBufferStreams = 3;
private readonly MdlFile _mdl;
private readonly byte _lod;
private readonly ushort _meshIndex;
private MdlStructs.MeshStruct XivMesh => _mdl.Meshes[_meshIndex];
private readonly byte _lod;
private readonly ushort _meshIndex;
private MdlStructs.MeshStruct XivMesh
=> _mdl.Meshes[_meshIndex];
private readonly Dictionary<ushort, int>? _boneIndexMap;
@ -53,10 +48,10 @@ public class MeshExporter
private readonly Type _materialType;
private readonly Type _skinningType;
private MeshExporter(MdlFile mdl, byte lod, ushort meshIndex, Dictionary<string, int>? boneNameMap)
private MeshExporter(MdlFile mdl, byte lod, ushort meshIndex, IReadOnlyDictionary<string, int>? boneNameMap)
{
_mdl = mdl;
_lod = lod;
_mdl = mdl;
_lod = lod;
_meshIndex = meshIndex;
if (boneNameMap != null)
@ -76,11 +71,12 @@ public class MeshExporter
if (_skinningType != typeof(VertexEmpty) && _boneIndexMap == null)
Penumbra.Log.Warning($"Mesh {meshIndex} has skinned vertex usages but no bone information was provided.");
Penumbra.Log.Debug($"Mesh {meshIndex} using vertex types geometry: {_geometryType.Name}, material: {_materialType.Name}, skinning: {_skinningType.Name}");
Penumbra.Log.Debug(
$"Mesh {meshIndex} using vertex types geometry: {_geometryType.Name}, material: {_materialType.Name}, skinning: {_skinningType.Name}");
}
/// <summary> Build a mapping between indices in this mesh's bone table (if any), and the glTF joint indices provdied. </summary>
private Dictionary<ushort, int>? BuildBoneIndexMap(Dictionary<string, int> boneNameMap)
/// <summary> Build a mapping between indices in this mesh's bone table (if any), and the glTF joint indices provided. </summary>
private Dictionary<ushort, int>? BuildBoneIndexMap(IReadOnlyDictionary<string, int> boneNameMap)
{
// A BoneTableIndex of 255 means that this mesh is not skinned.
if (XivMesh.BoneTableIndex == 255)
@ -105,11 +101,10 @@ public class MeshExporter
/// <summary> Build glTF meshes for this XIV mesh. </summary>
private IMeshBuilder<MaterialBuilder>[] BuildMeshes()
{
var indices = BuildIndices();
var indices = BuildIndices();
var vertices = BuildVertices();
// NOTE: Index indices are specified relative to the LOD's 0, but we're reading chunks for each mesh, so we're specifying the index base relative to the mesh's base.
if (XivMesh.SubMeshCount == 0)
return [BuildMesh($"mesh {_meshIndex}", indices, vertices, 0, (int)XivMesh.IndexCount)];
@ -117,7 +112,8 @@ public class MeshExporter
.Skip(XivMesh.SubMeshIndex)
.Take(XivMesh.SubMeshCount)
.WithIndex()
.Select(submesh => BuildMesh($"mesh {_meshIndex}.{submesh.Index}", indices, vertices, (int)(submesh.Value.IndexOffset - XivMesh.StartIndex), (int)submesh.Value.IndexCount))
.Select(subMesh => BuildMesh($"mesh {_meshIndex}.{subMesh.Index}", indices, vertices,
(int)(subMesh.Value.IndexOffset - XivMesh.StartIndex), (int)subMesh.Value.IndexCount))
.ToArray();
}
@ -161,7 +157,7 @@ public class MeshExporter
}
var primitiveVertices = meshBuilder.Primitives.First().Vertices;
var shapeNames = new List<string>();
var shapeNames = new List<string>();
foreach (var shape in _mdl.Shapes)
{
@ -177,24 +173,28 @@ public class MeshExporter
)
.Where(shapeValue =>
shapeValue.BaseIndicesIndex >= indexBase
&& shapeValue.BaseIndicesIndex < indexBase + indexCount
&& shapeValue.BaseIndicesIndex < indexBase + indexCount
)
.ToList();
if (shapeValues.Count == 0) continue;
if (shapeValues.Count == 0)
continue;
var morphBuilder = meshBuilder.UseMorphTarget(shapeNames.Count);
shapeNames.Add(shape.ShapeName);
foreach (var shapeValue in shapeValues)
{
morphBuilder.SetVertex(
primitiveVertices[gltfIndices[shapeValue.BaseIndicesIndex - indexBase]].GetGeometry(),
vertices[shapeValue.ReplacingVertexIndex].GetGeometry()
);
}
}
meshBuilder.Extras = JsonContent.CreateFrom(new Dictionary<string, object>() {
{"targetNames", shapeNames}
meshBuilder.Extras = JsonContent.CreateFrom(new Dictionary<string, object>()
{
{ "targetNames", shapeNames },
});
return meshBuilder;
@ -249,23 +249,25 @@ public class MeshExporter
}
/// <summary> Read a vertex attribute of the specified type from a vertex buffer stream. </summary>
private object ReadVertexAttribute(MdlFile.VertexType type, BinaryReader reader)
private static object ReadVertexAttribute(MdlFile.VertexType type, BinaryReader reader)
{
return type switch
{
MdlFile.VertexType.Single3 => new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
MdlFile.VertexType.Single4 => new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
MdlFile.VertexType.UInt => reader.ReadBytes(4),
MdlFile.VertexType.ByteFloat4 => new Vector4(reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f),
MdlFile.VertexType.UInt => reader.ReadBytes(4),
MdlFile.VertexType.ByteFloat4 => new Vector4(reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f,
reader.ReadByte() / 255f),
MdlFile.VertexType.Half2 => new Vector2((float)reader.ReadHalf(), (float)reader.ReadHalf()),
MdlFile.VertexType.Half4 => new Vector4((float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf()),
MdlFile.VertexType.Half4 => new Vector4((float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf(),
(float)reader.ReadHalf()),
_ => throw new ArgumentOutOfRangeException()
_ => throw new ArgumentOutOfRangeException(),
};
}
/// <summary> Get the vertex geometry type for this mesh's vertex usages. </summary>
private Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
private static Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
{
if (!usages.ContainsKey(MdlFile.VertexUsage.Position))
throw new Exception("Mesh does not contain position vertex elements.");
@ -304,16 +306,16 @@ public class MeshExporter
}
/// <summary> Get the vertex material type for this mesh's vertex usages. </summary>
private Type GetMaterialType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
private static Type GetMaterialType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
{
var uvCount = 0;
if (usages.TryGetValue(MdlFile.VertexUsage.UV, out var type))
uvCount = type switch
{
MdlFile.VertexType.Half2 => 1,
MdlFile.VertexType.Half4 => 2,
MdlFile.VertexType.Half2 => 1,
MdlFile.VertexType.Half4 => 2,
MdlFile.VertexType.Single4 => 2,
_ => throw new Exception($"Unexpected UV vertex type {type}.")
_ => throw new Exception($"Unexpected UV vertex type {type}."),
};
var materialUsages = (
@ -323,11 +325,11 @@ public class MeshExporter
return materialUsages switch
{
(2, true) => typeof(VertexColor1Texture2),
(2, true) => typeof(VertexColor1Texture2),
(2, false) => typeof(VertexTexture2),
(1, true) => typeof(VertexColor1Texture1),
(1, true) => typeof(VertexColor1Texture1),
(1, false) => typeof(VertexTexture1),
(0, true) => typeof(VertexColor1),
(0, true) => typeof(VertexColor1),
(0, false) => typeof(VertexEmpty),
_ => throw new Exception("Unreachable."),
@ -377,7 +379,7 @@ public class MeshExporter
}
/// <summary> Get the vertex skinning type for this mesh's vertex usages. </summary>
private Type GetSkinningType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
private static Type GetSkinningType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
{
if (usages.ContainsKey(MdlFile.VertexUsage.BlendWeights) && usages.ContainsKey(MdlFile.VertexUsage.BlendIndices))
return typeof(VertexJoints4);
@ -400,7 +402,8 @@ public class MeshExporter
var weights = ToVector4(attributes[MdlFile.VertexUsage.BlendWeights]);
var bindings = Enumerable.Range(0, 4)
.Select(bindingIndex => {
.Select(bindingIndex =>
{
// NOTE: I've not seen any files that throw this error that aren't completely broken.
var xivBoneIndex = indices[bindingIndex];
if (!_boneIndexMap.TryGetValue(xivBoneIndex, out var jointIndex))
@ -417,44 +420,44 @@ public class MeshExporter
/// <summary> Clamps any tangent W value other than 1 to -1. </summary>
/// <remarks> Some XIV models seemingly store -1 as 0, this patches over that. </remarks>
private Vector4 FixTangentVector(Vector4 tangent)
private static Vector4 FixTangentVector(Vector4 tangent)
=> tangent with { W = tangent.W == 1 ? 1 : -1 };
/// <summary> Convert a vertex attribute value to a Vector2. Supported inputs are Vector2, Vector3, and Vector4. </summary>
private Vector2 ToVector2(object data)
private static Vector2 ToVector2(object data)
=> data switch
{
Vector2 v2 => v2,
Vector3 v3 => new Vector2(v3.X, v3.Y),
Vector4 v4 => new Vector2(v4.X, v4.Y),
_ => throw new ArgumentOutOfRangeException($"Invalid Vector2 input {data}")
_ => throw new ArgumentOutOfRangeException($"Invalid Vector2 input {data}"),
};
/// <summary> Convert a vertex attribute value to a Vector3. Supported inputs are Vector2, Vector3, and Vector4. </summary>
private Vector3 ToVector3(object data)
private static Vector3 ToVector3(object data)
=> data switch
{
Vector2 v2 => new Vector3(v2.X, v2.Y, 0),
Vector3 v3 => v3,
Vector4 v4 => new Vector3(v4.X, v4.Y, v4.Z),
_ => throw new ArgumentOutOfRangeException($"Invalid Vector3 input {data}")
_ => throw new ArgumentOutOfRangeException($"Invalid Vector3 input {data}"),
};
/// <summary> Convert a vertex attribute value to a Vector4. Supported inputs are Vector2, Vector3, and Vector4. </summary>
private Vector4 ToVector4(object data)
private static Vector4 ToVector4(object data)
=> data switch
{
Vector2 v2 => new Vector4(v2.X, v2.Y, 0, 0),
Vector2 v2 => new Vector4(v2.X, v2.Y, 0, 0),
Vector3 v3 => new Vector4(v3.X, v3.Y, v3.Z, 1),
Vector4 v4 => v4,
_ => throw new ArgumentOutOfRangeException($"Invalid Vector3 input {data}")
_ => throw new ArgumentOutOfRangeException($"Invalid Vector3 input {data}"),
};
/// <summary> Convert a vertex attribute value to a byte array. </summary>
private byte[] ToByteArray(object data)
private static byte[] ToByteArray(object data)
=> data switch
{
byte[] value => value,
_ => throw new ArgumentOutOfRangeException($"Invalid byte[] input {data}")
_ => throw new ArgumentOutOfRangeException($"Invalid byte[] input {data}"),
};
}