Skip to content

Commit b2860c1

Browse files
committedFeb 20, 2025·
Merge branch 'refs/heads/adamm789/model-export'
2 parents a73dee8 + 1f172b4 commit b2860c1

File tree

3 files changed

+201
-73
lines changed

3 files changed

+201
-73
lines changed
 

‎Penumbra.GameData

‎Penumbra/Import/Models/Export/MeshExporter.cs

+90-47
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Penumbra.GameData.Files.ModelStructs;
88
using SharpGLTF.Geometry;
99
using SharpGLTF.Geometry.VertexTypes;
10-
using SharpGLTF.IO;
1110
using SharpGLTF.Materials;
1211
using SharpGLTF.Scenes;
1312

@@ -84,9 +83,12 @@ private MeshExporter(in ExportConfig config, MdlFile mdl, byte lod, ushort meshI
8483
_boneIndexMap = BuildBoneIndexMap(skeleton.Value);
8584

8685
var usages = _mdl.VertexDeclarations[_meshIndex].VertexElements
86+
.GroupBy(ele => (MdlFile.VertexUsage)ele.Usage, ele => ele)
8787
.ToImmutableDictionary(
88-
element => (MdlFile.VertexUsage)element.Usage,
89-
element => (MdlFile.VertexType)element.Type
88+
g => g.Key,
89+
g => g.OrderBy(ele => ele.UsageIndex) // OrderBy UsageIndex is probably unnecessary as they're probably already be in order
90+
.Select(ele => (MdlFile.VertexType)ele.Type)
91+
.ToList()
9092
);
9193

9294
_geometryType = GetGeometryType(usages);
@@ -112,6 +114,7 @@ private MeshExporter(in ExportConfig config, MdlFile mdl, byte lod, ushort meshI
112114

113115
var indexMap = new Dictionary<ushort, int>();
114116
// #TODO @ackwell maybe fix for V6 Models, I think this works fine.
117+
115118
foreach (var (xivBoneIndex, tableIndex) in xivBoneTable.BoneIndex.Take((int)xivBoneTable.BoneCount).WithIndex())
116119
{
117120
var boneName = _mdl.Bones[xivBoneIndex];
@@ -278,18 +281,22 @@ private IReadOnlyList<IVertexBuilder> BuildVertices()
278281

279282
var sortedElements = _mdl.VertexDeclarations[_meshIndex].VertexElements
280283
.OrderBy(element => element.Offset)
281-
.Select(element => ((MdlFile.VertexUsage)element.Usage, element))
282284
.ToList();
283-
284285
var vertices = new List<IVertexBuilder>();
285286

286-
var attributes = new Dictionary<MdlFile.VertexUsage, object>();
287+
var attributes = new Dictionary<MdlFile.VertexUsage, List<object>>();
287288
for (var vertexIndex = 0; vertexIndex < XivMesh.VertexCount; vertexIndex++)
288289
{
289290
attributes.Clear();
290-
291-
foreach (var (usage, element) in sortedElements)
292-
attributes[usage] = ReadVertexAttribute((MdlFile.VertexType)element.Type, streams[element.Stream]);
291+
attributes = sortedElements
292+
.GroupBy(element => element.Usage)
293+
.ToDictionary(
294+
x => (MdlFile.VertexUsage)x.Key,
295+
x => x.OrderBy(ele => ele.UsageIndex) // Once again, OrderBy UsageIndex is probably unnecessary
296+
.Select(ele => ReadVertexAttribute((MdlFile.VertexType)ele.Type, streams[ele.Stream]))
297+
.ToList()
298+
);
299+
293300

294301
var vertexGeometry = BuildVertexGeometry(attributes);
295302
var vertexMaterial = BuildVertexMaterial(attributes);
@@ -320,7 +327,7 @@ private object ReadVertexAttribute(MdlFile.VertexType type, BinaryReader reader)
320327
}
321328

322329
/// <summary> Get the vertex geometry type for this mesh's vertex usages. </summary>
323-
private Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
330+
private Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, List<MdlFile.VertexType>> usages)
324331
{
325332
if (!usages.ContainsKey(MdlFile.VertexUsage.Position))
326333
throw _notifier.Exception("Mesh does not contain position vertex elements.");
@@ -335,28 +342,28 @@ private Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.Ve
335342
}
336343

337344
/// <summary> Build a geometry vertex from a vertex's attributes. </summary>
338-
private IVertexGeometry BuildVertexGeometry(IReadOnlyDictionary<MdlFile.VertexUsage, object> attributes)
345+
private IVertexGeometry BuildVertexGeometry(IReadOnlyDictionary<MdlFile.VertexUsage, List<object>> attributes)
339346
{
340347
if (_geometryType == typeof(VertexPosition))
341348
return new VertexPosition(
342-
ToVector3(attributes[MdlFile.VertexUsage.Position])
349+
ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Position))
343350
);
344351

345352
if (_geometryType == typeof(VertexPositionNormal))
346353
return new VertexPositionNormal(
347-
ToVector3(attributes[MdlFile.VertexUsage.Position]),
348-
ToVector3(attributes[MdlFile.VertexUsage.Normal])
354+
ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Position)),
355+
ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Normal))
349356
);
350357

351358
if (_geometryType == typeof(VertexPositionNormalTangent))
352359
{
353360
// (Bi)tangents are universally stored as ByteFloat4, which uses 0..1 to represent the full -1..1 range.
354361
// TODO: While this assumption is safe, it would be sensible to actually check.
355-
var bitangent = ToVector4(attributes[MdlFile.VertexUsage.Tangent1]) * 2 - Vector4.One;
362+
var bitangent = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Tangent1)) * 2 - Vector4.One;
356363

357364
return new VertexPositionNormalTangent(
358-
ToVector3(attributes[MdlFile.VertexUsage.Position]),
359-
ToVector3(attributes[MdlFile.VertexUsage.Normal]),
365+
ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Position)),
366+
ToVector3(GetFirstSafe(attributes, MdlFile.VertexUsage.Normal)),
360367
bitangent
361368
);
362369
}
@@ -365,18 +372,23 @@ private IVertexGeometry BuildVertexGeometry(IReadOnlyDictionary<MdlFile.VertexUs
365372
}
366373

367374
/// <summary> Get the vertex material type for this mesh's vertex usages. </summary>
368-
private Type GetMaterialType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
375+
private Type GetMaterialType(IReadOnlyDictionary<MdlFile.VertexUsage, List<MdlFile.VertexType>> usages)
369376
{
370377
var uvCount = 0;
371-
if (usages.TryGetValue(MdlFile.VertexUsage.UV, out var type))
372-
uvCount = type switch
378+
if (usages.TryGetValue(MdlFile.VertexUsage.UV, out var list))
379+
{
380+
foreach (var type in list)
373381
{
374-
MdlFile.VertexType.Half2 => 1,
375-
MdlFile.VertexType.Half4 => 2,
376-
MdlFile.VertexType.Single2 => 1,
377-
MdlFile.VertexType.Single4 => 2,
378-
_ => throw _notifier.Exception($"Unexpected UV vertex type {type}."),
379-
};
382+
uvCount += type switch
383+
{
384+
MdlFile.VertexType.Half2 => 1,
385+
MdlFile.VertexType.Half4 => 2,
386+
MdlFile.VertexType.Single2 => 1,
387+
MdlFile.VertexType.Single4 => 2,
388+
_ => throw _notifier.Exception($"Unexpected UV vertex type {type}."),
389+
};
390+
}
391+
}
380392

381393
var materialUsages = (
382394
uvCount,
@@ -385,40 +397,42 @@ private Type GetMaterialType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.Ve
385397

386398
return materialUsages switch
387399
{
400+
(3, true) => typeof(VertexTexture3ColorFfxiv),
401+
(3, false) => typeof(VertexTexture3),
388402
(2, true) => typeof(VertexTexture2ColorFfxiv),
389403
(2, false) => typeof(VertexTexture2),
390404
(1, true) => typeof(VertexTexture1ColorFfxiv),
391405
(1, false) => typeof(VertexTexture1),
392406
(0, true) => typeof(VertexColorFfxiv),
393407
(0, false) => typeof(VertexEmpty),
394408

395-
_ => throw new Exception("Unreachable."),
409+
_ => throw _notifier.Exception($"Unhandled UV count of {uvCount} encountered."),
396410
};
397411
}
398412

399413
/// <summary> Build a material vertex from a vertex's attributes. </summary>
400-
private IVertexMaterial BuildVertexMaterial(IReadOnlyDictionary<MdlFile.VertexUsage, object> attributes)
414+
private IVertexMaterial BuildVertexMaterial(IReadOnlyDictionary<MdlFile.VertexUsage, List<object>> attributes)
401415
{
402416
if (_materialType == typeof(VertexEmpty))
403417
return new VertexEmpty();
404418

405419
if (_materialType == typeof(VertexColorFfxiv))
406-
return new VertexColorFfxiv(ToVector4(attributes[MdlFile.VertexUsage.Color]));
420+
return new VertexColorFfxiv(ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Color)));
407421

408422
if (_materialType == typeof(VertexTexture1))
409-
return new VertexTexture1(ToVector2(attributes[MdlFile.VertexUsage.UV]));
423+
return new VertexTexture1(ToVector2(GetFirstSafe(attributes, MdlFile.VertexUsage.UV)));
410424

411425
if (_materialType == typeof(VertexTexture1ColorFfxiv))
412426
return new VertexTexture1ColorFfxiv(
413-
ToVector2(attributes[MdlFile.VertexUsage.UV]),
414-
ToVector4(attributes[MdlFile.VertexUsage.Color])
427+
ToVector2(GetFirstSafe(attributes, MdlFile.VertexUsage.UV)),
428+
ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Color))
415429
);
416430

417431
// XIV packs two UVs into a single vec4 attribute.
418432

419433
if (_materialType == typeof(VertexTexture2))
420434
{
421-
var uv = ToVector4(attributes[MdlFile.VertexUsage.UV]);
435+
var uv = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.UV));
422436
return new VertexTexture2(
423437
new Vector2(uv.X, uv.Y),
424438
new Vector2(uv.Z, uv.W)
@@ -427,37 +441,55 @@ private IVertexMaterial BuildVertexMaterial(IReadOnlyDictionary<MdlFile.VertexUs
427441

428442
if (_materialType == typeof(VertexTexture2ColorFfxiv))
429443
{
430-
var uv = ToVector4(attributes[MdlFile.VertexUsage.UV]);
444+
var uv = ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.UV));
431445
return new VertexTexture2ColorFfxiv(
432446
new Vector2(uv.X, uv.Y),
433447
new Vector2(uv.Z, uv.W),
434-
ToVector4(attributes[MdlFile.VertexUsage.Color])
448+
ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Color))
449+
);
450+
}
451+
if (_materialType == typeof(VertexTexture3))
452+
{
453+
// Not 100% sure about this
454+
var uv0 = ToVector4(attributes[MdlFile.VertexUsage.UV][0]);
455+
var uv1 = ToVector4(attributes[MdlFile.VertexUsage.UV][1]);
456+
return new VertexTexture3(
457+
new Vector2(uv0.X, uv0.Y),
458+
new Vector2(uv0.Z, uv0.W),
459+
new Vector2(uv1.X, uv1.Y)
460+
);
461+
}
462+
463+
if (_materialType == typeof(VertexTexture3ColorFfxiv))
464+
{
465+
var uv0 = ToVector4(attributes[MdlFile.VertexUsage.UV][0]);
466+
var uv1 = ToVector4(attributes[MdlFile.VertexUsage.UV][1]);
467+
return new VertexTexture3ColorFfxiv(
468+
new Vector2(uv0.X, uv0.Y),
469+
new Vector2(uv0.Z, uv0.W),
470+
new Vector2(uv1.X, uv1.Y),
471+
ToVector4(GetFirstSafe(attributes, MdlFile.VertexUsage.Color))
435472
);
436473
}
437474

438475
throw _notifier.Exception($"Unknown material type {_skinningType}");
439476
}
440477

441478
/// <summary> Get the vertex skinning type for this mesh's vertex usages. </summary>
442-
private static Type GetSkinningType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
479+
private Type GetSkinningType(IReadOnlyDictionary<MdlFile.VertexUsage, List<MdlFile.VertexType>> usages)
443480
{
444481
if (usages.ContainsKey(MdlFile.VertexUsage.BlendWeights) && usages.ContainsKey(MdlFile.VertexUsage.BlendIndices))
445482
{
446-
if (usages[MdlFile.VertexUsage.BlendWeights] == MdlFile.VertexType.UShort4)
447-
{
448-
return typeof(VertexJoints8);
449-
}
450-
else
451-
{
452-
return typeof(VertexJoints4);
453-
}
483+
return GetFirstSafe(usages, MdlFile.VertexUsage.BlendWeights) == MdlFile.VertexType.UShort4
484+
? typeof(VertexJoints8)
485+
: typeof(VertexJoints4);
454486
}
455487

456488
return typeof(VertexEmpty);
457489
}
458490

459491
/// <summary> Build a skinning vertex from a vertex's attributes. </summary>
460-
private IVertexSkinning BuildVertexSkinning(IReadOnlyDictionary<MdlFile.VertexUsage, object> attributes)
492+
private IVertexSkinning BuildVertexSkinning(IReadOnlyDictionary<MdlFile.VertexUsage, List<object>> attributes)
461493
{
462494
if (_skinningType == typeof(VertexEmpty))
463495
return new VertexEmpty();
@@ -467,8 +499,8 @@ private IVertexSkinning BuildVertexSkinning(IReadOnlyDictionary<MdlFile.VertexUs
467499
if (_boneIndexMap == null)
468500
throw _notifier.Exception("Tried to build skinned vertex but no bone mappings are available.");
469501

470-
var indiciesData = attributes[MdlFile.VertexUsage.BlendIndices];
471-
var weightsData = attributes[MdlFile.VertexUsage.BlendWeights];
502+
var indiciesData = GetFirstSafe(attributes, MdlFile.VertexUsage.BlendIndices);
503+
var weightsData = GetFirstSafe(attributes, MdlFile.VertexUsage.BlendWeights);
472504
var indices = ToByteArray(indiciesData);
473505
var weights = ToFloatArray(weightsData);
474506

@@ -495,6 +527,17 @@ private IVertexSkinning BuildVertexSkinning(IReadOnlyDictionary<MdlFile.VertexUs
495527
throw _notifier.Exception($"Unknown skinning type {_skinningType}");
496528
}
497529

530+
/// <summary> Check that the list has length 1 for any case where this is expected and return the one entry. </summary>
531+
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
532+
private T GetFirstSafe<T>(IReadOnlyDictionary<MdlFile.VertexUsage, List<T>> attributes, MdlFile.VertexUsage usage)
533+
{
534+
var list = attributes[usage];
535+
if (list.Count != 1)
536+
throw _notifier.Exception($"Multiple usage indices encountered for {usage}.");
537+
538+
return list[0];
539+
}
540+
498541
/// <summary> Convert a vertex attribute value to a Vector2. Supported inputs are Vector2, Vector3, and Vector4. </summary>
499542
private static Vector2 ToVector2(object data)
500543
=> data switch

‎Penumbra/Import/Models/Export/VertexFragment.cs

+110-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using SharpGLTF.Geometry.VertexTypes;
32
using SharpGLTF.Memory;
43
using SharpGLTF.Schema2;
@@ -11,7 +10,7 @@ namespace Penumbra.Import.Models.Export;
1110
and there's reason to overhaul the export pipeline.
1211
*/
1312

14-
public struct VertexColorFfxiv : IVertexCustom
13+
public struct VertexColorFfxiv(Vector4 ffxivColor) : IVertexCustom
1514
{
1615
public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes()
1716
{
@@ -20,7 +19,7 @@ public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes(
2019
new AttributeFormat(DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true));
2120
}
2221

23-
public Vector4 FfxivColor;
22+
public Vector4 FfxivColor = ffxivColor;
2423

2524
public int MaxColors
2625
=> 0;
@@ -33,9 +32,6 @@ public int MaxTextCoords
3332
public IEnumerable<string> CustomAttributes
3433
=> CustomNames;
3534

36-
public VertexColorFfxiv(Vector4 ffxivColor)
37-
=> FfxivColor = ffxivColor;
38-
3935
public void Add(in VertexMaterialDelta delta)
4036
{ }
4137

@@ -88,7 +84,7 @@ public void Validate()
8884
}
8985
}
9086

91-
public struct VertexTexture1ColorFfxiv : IVertexCustom
87+
public struct VertexTexture1ColorFfxiv(Vector2 texCoord0, Vector4 ffxivColor) : IVertexCustom
9288
{
9389
public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes()
9490
{
@@ -98,9 +94,9 @@ public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes(
9894
new AttributeFormat(DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true));
9995
}
10096

101-
public Vector2 TexCoord0;
97+
public Vector2 TexCoord0 = texCoord0;
10298

103-
public Vector4 FfxivColor;
99+
public Vector4 FfxivColor = ffxivColor;
104100

105101
public int MaxColors
106102
=> 0;
@@ -113,12 +109,6 @@ public int MaxTextCoords
113109
public IEnumerable<string> CustomAttributes
114110
=> CustomNames;
115111

116-
public VertexTexture1ColorFfxiv(Vector2 texCoord0, Vector4 ffxivColor)
117-
{
118-
TexCoord0 = texCoord0;
119-
FfxivColor = ffxivColor;
120-
}
121-
122112
public void Add(in VertexMaterialDelta delta)
123113
{
124114
TexCoord0 += delta.TexCoord0Delta;
@@ -182,7 +172,7 @@ public void Validate()
182172
}
183173
}
184174

185-
public struct VertexTexture2ColorFfxiv : IVertexCustom
175+
public struct VertexTexture2ColorFfxiv(Vector2 texCoord0, Vector2 texCoord1, Vector4 ffxivColor) : IVertexCustom
186176
{
187177
public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes()
188178
{
@@ -194,9 +184,9 @@ public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes(
194184
new AttributeFormat(DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true));
195185
}
196186

197-
public Vector2 TexCoord0;
198-
public Vector2 TexCoord1;
199-
public Vector4 FfxivColor;
187+
public Vector2 TexCoord0 = texCoord0;
188+
public Vector2 TexCoord1 = texCoord1;
189+
public Vector4 FfxivColor = ffxivColor;
200190

201191
public int MaxColors
202192
=> 0;
@@ -209,17 +199,109 @@ public int MaxTextCoords
209199
public IEnumerable<string> CustomAttributes
210200
=> CustomNames;
211201

212-
public VertexTexture2ColorFfxiv(Vector2 texCoord0, Vector2 texCoord1, Vector4 ffxivColor)
202+
public void Add(in VertexMaterialDelta delta)
213203
{
214-
TexCoord0 = texCoord0;
215-
TexCoord1 = texCoord1;
216-
FfxivColor = ffxivColor;
204+
TexCoord0 += delta.TexCoord0Delta;
205+
TexCoord1 += delta.TexCoord1Delta;
217206
}
218207

208+
public VertexMaterialDelta Subtract(IVertexMaterial baseValue)
209+
=> new(Vector4.Zero, Vector4.Zero, TexCoord0 - baseValue.GetTexCoord(0), TexCoord1 - baseValue.GetTexCoord(1));
210+
211+
public Vector2 GetTexCoord(int index)
212+
=> index switch
213+
{
214+
0 => TexCoord0,
215+
1 => TexCoord1,
216+
_ => throw new ArgumentOutOfRangeException(nameof(index)),
217+
};
218+
219+
public void SetTexCoord(int setIndex, Vector2 coord)
220+
{
221+
if (setIndex == 0)
222+
TexCoord0 = coord;
223+
if (setIndex == 1)
224+
TexCoord1 = coord;
225+
if (setIndex >= 2)
226+
throw new ArgumentOutOfRangeException(nameof(setIndex));
227+
}
228+
229+
public bool TryGetCustomAttribute(string attributeName, out object? value)
230+
{
231+
switch (attributeName)
232+
{
233+
case "_FFXIV_COLOR":
234+
value = FfxivColor;
235+
return true;
236+
237+
default:
238+
value = null;
239+
return false;
240+
}
241+
}
242+
243+
public void SetCustomAttribute(string attributeName, object value)
244+
{
245+
if (attributeName == "_FFXIV_COLOR" && value is Vector4 valueVector4)
246+
FfxivColor = valueVector4;
247+
}
248+
249+
public Vector4 GetColor(int index)
250+
=> throw new ArgumentOutOfRangeException(nameof(index));
251+
252+
public void SetColor(int setIndex, Vector4 color)
253+
{ }
254+
255+
public void Validate()
256+
{
257+
var components = new[]
258+
{
259+
FfxivColor.X,
260+
FfxivColor.Y,
261+
FfxivColor.Z,
262+
FfxivColor.W,
263+
};
264+
if (components.Any(component => component < 0 || component > 1))
265+
throw new ArgumentOutOfRangeException(nameof(FfxivColor));
266+
}
267+
}
268+
269+
public struct VertexTexture3ColorFfxiv(Vector2 texCoord0, Vector2 texCoord1, Vector2 texCoord2, Vector4 ffxivColor)
270+
: IVertexCustom
271+
{
272+
public IEnumerable<KeyValuePair<string, AttributeFormat>> GetEncodingAttributes()
273+
{
274+
yield return new KeyValuePair<string, AttributeFormat>("TEXCOORD_0",
275+
new AttributeFormat(DimensionType.VEC2, EncodingType.FLOAT, false));
276+
yield return new KeyValuePair<string, AttributeFormat>("TEXCOORD_1",
277+
new AttributeFormat(DimensionType.VEC2, EncodingType.FLOAT, false));
278+
yield return new KeyValuePair<string, AttributeFormat>("TEXCOORD_2",
279+
new AttributeFormat(DimensionType.VEC2, EncodingType.FLOAT, false));
280+
yield return new KeyValuePair<string, AttributeFormat>("_FFXIV_COLOR",
281+
new AttributeFormat(DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true));
282+
}
283+
284+
public Vector2 TexCoord0 = texCoord0;
285+
public Vector2 TexCoord1 = texCoord1;
286+
public Vector2 TexCoord2 = texCoord2;
287+
public Vector4 FfxivColor = ffxivColor;
288+
289+
public int MaxColors
290+
=> 0;
291+
292+
public int MaxTextCoords
293+
=> 3;
294+
295+
private static readonly string[] CustomNames = ["_FFXIV_COLOR"];
296+
297+
public IEnumerable<string> CustomAttributes
298+
=> CustomNames;
299+
219300
public void Add(in VertexMaterialDelta delta)
220301
{
221302
TexCoord0 += delta.TexCoord0Delta;
222303
TexCoord1 += delta.TexCoord1Delta;
304+
TexCoord2 += delta.TexCoord2Delta;
223305
}
224306

225307
public VertexMaterialDelta Subtract(IVertexMaterial baseValue)
@@ -230,6 +312,7 @@ public Vector2 GetTexCoord(int index)
230312
{
231313
0 => TexCoord0,
232314
1 => TexCoord1,
315+
2 => TexCoord2,
233316
_ => throw new ArgumentOutOfRangeException(nameof(index)),
234317
};
235318

@@ -239,7 +322,9 @@ public void SetTexCoord(int setIndex, Vector2 coord)
239322
TexCoord0 = coord;
240323
if (setIndex == 1)
241324
TexCoord1 = coord;
242-
if (setIndex >= 2)
325+
if (setIndex == 2)
326+
TexCoord2 = coord;
327+
if (setIndex >= 3)
243328
throw new ArgumentOutOfRangeException(nameof(setIndex));
244329
}
245330

@@ -278,7 +363,7 @@ public void Validate()
278363
FfxivColor.Z,
279364
FfxivColor.W,
280365
};
281-
if (components.Any(component => component < 0 || component > 1))
366+
if (components.Any(component => component is < 0f or > 1f))
282367
throw new ArgumentOutOfRangeException(nameof(FfxivColor));
283368
}
284369
}

0 commit comments

Comments
 (0)
Please sign in to comment.