mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-26 18:39:28 +01:00
Some cleanup and using new features / intellisense recommendations.
This commit is contained in:
parent
51bb9cf7cd
commit
677c9bd801
5 changed files with 117 additions and 128 deletions
|
|
@ -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}"),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue