diff --git a/Penumbra/Import/Models/Export/MeshExporter.cs b/Penumbra/Import/Models/Export/MeshExporter.cs
index 6d65e152..aa0811d7 100644
--- a/Penumbra/Import/Models/Export/MeshExporter.cs
+++ b/Penumbra/Import/Models/Export/MeshExporter.cs
@@ -7,7 +7,6 @@ using Penumbra.GameData.Files;
using Penumbra.GameData.Files.ModelStructs;
using SharpGLTF.Geometry;
using SharpGLTF.Geometry.VertexTypes;
-using SharpGLTF.IO;
using SharpGLTF.Materials;
using SharpGLTF.Scenes;
@@ -347,24 +346,24 @@ public class MeshExporter
{
if (_geometryType == typeof(VertexPosition))
return new VertexPosition(
- ToVector3(attributes[MdlFile.VertexUsage.Position].First())
+ ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Position))
);
if (_geometryType == typeof(VertexPositionNormal))
return new VertexPositionNormal(
- ToVector3(attributes[MdlFile.VertexUsage.Position].First()),
- ToVector3(attributes[MdlFile.VertexUsage.Normal].First())
+ ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Position)),
+ ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Normal))
);
if (_geometryType == typeof(VertexPositionNormalTangent))
{
// (Bi)tangents are universally stored as ByteFloat4, which uses 0..1 to represent the full -1..1 range.
// TODO: While this assumption is safe, it would be sensible to actually check.
- var bitangent = ToVector4(attributes[MdlFile.VertexUsage.Tangent1].First()) * 2 - Vector4.One;
+ var bitangent = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Tangent1)) * 2 - Vector4.One;
return new VertexPositionNormalTangent(
- ToVector3(attributes[MdlFile.VertexUsage.Position].First()),
- ToVector3(attributes[MdlFile.VertexUsage.Normal].First()),
+ ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Position)),
+ ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Normal)),
bitangent
);
}
@@ -418,22 +417,22 @@ public class MeshExporter
return new VertexEmpty();
if (_materialType == typeof(VertexColorFfxiv))
- return new VertexColorFfxiv(ToVector4(attributes[MdlFile.VertexUsage.Color].First()));
+ return new VertexColorFfxiv(ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Color)));
if (_materialType == typeof(VertexTexture1))
- return new VertexTexture1(ToVector2(attributes[MdlFile.VertexUsage.UV].First()));
+ return new VertexTexture1(ToVector2(GetFirstSafe(attributes, MdlFile.VertexUsage.UV)));
if (_materialType == typeof(VertexTexture1ColorFfxiv))
return new VertexTexture1ColorFfxiv(
- ToVector2(attributes[MdlFile.VertexUsage.UV].First()),
- ToVector4(attributes[MdlFile.VertexUsage.Color].First())
+ ToVector2(GetFirstSafe(attributes, MdlFile.VertexUsage.UV)),
+ ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Color))
);
// XIV packs two UVs into a single vec4 attribute.
if (_materialType == typeof(VertexTexture2))
{
- var uv = ToVector4(attributes[MdlFile.VertexUsage.UV].First());
+ var uv = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.UV));
return new VertexTexture2(
new Vector2(uv.X, uv.Y),
new Vector2(uv.Z, uv.W)
@@ -442,11 +441,11 @@ public class MeshExporter
if (_materialType == typeof(VertexTexture2ColorFfxiv))
{
- var uv = ToVector4(attributes[MdlFile.VertexUsage.UV].First());
+ var uv = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.UV));
return new VertexTexture2ColorFfxiv(
new Vector2(uv.X, uv.Y),
new Vector2(uv.Z, uv.W),
- ToVector4(attributes[MdlFile.VertexUsage.Color].First())
+ ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Color))
);
}
if (_materialType == typeof(VertexTexture3))
@@ -469,7 +468,7 @@ public class MeshExporter
new Vector2(uv0.X, uv0.Y),
new Vector2(uv0.Z, uv0.W),
new Vector2(uv1.X, uv1.Y),
- ToVector4(attributes[MdlFile.VertexUsage.Color].First())
+ ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Color))
);
}
@@ -477,18 +476,13 @@ public class MeshExporter
}
/// Get the vertex skinning type for this mesh's vertex usages.
- private static Type GetSkinningType(IReadOnlyDictionary> usages)
+ private Type GetSkinningType(IReadOnlyDictionary> usages)
{
if (usages.ContainsKey(MdlFile.VertexUsage.BlendWeights) && usages.ContainsKey(MdlFile.VertexUsage.BlendIndices))
{
- if (usages[MdlFile.VertexUsage.BlendWeights].First() == MdlFile.VertexType.UShort4)
- {
- return typeof(VertexJoints8);
- }
- else
- {
- return typeof(VertexJoints4);
- }
+ return GetFirstSafe(usages, MdlFile.VertexUsage.BlendWeights) == MdlFile.VertexType.UShort4
+ ? typeof(VertexJoints8)
+ : typeof(VertexJoints4);
}
return typeof(VertexEmpty);
@@ -505,8 +499,8 @@ public class MeshExporter
if (_boneIndexMap == null)
throw _notifier.Exception("Tried to build skinned vertex but no bone mappings are available.");
- var indiciesData = attributes[MdlFile.VertexUsage.BlendIndices].First();
- var weightsData = attributes[MdlFile.VertexUsage.BlendWeights].First();
+ var indiciesData = GetFirstSafe(attributes, MdlFile.VertexUsage.BlendIndices);
+ var weightsData = GetFirstSafe(attributes, MdlFile.VertexUsage.BlendWeights);
var indices = ToByteArray(indiciesData);
var weights = ToFloatArray(weightsData);
@@ -533,6 +527,17 @@ public class MeshExporter
throw _notifier.Exception($"Unknown skinning type {_skinningType}");
}
+ /// Check that the list has length 1 for any case where this is expected and return the one entry.
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private T GetFirstSafe(IReadOnlyDictionary> attributes, MdlFile.VertexUsage usage)
+ {
+ var list = attributes[usage];
+ if (list.Count != 1)
+ throw _notifier.Exception($"Multiple usage indices encountered for {usage}.");
+
+ return list[0];
+ }
+
/// Convert a vertex attribute value to a Vector2. Supported inputs are Vector2, Vector3, and Vector4.
private static Vector2 ToVector2(object data)
=> data switch