7
7
using Penumbra . GameData . Files . ModelStructs ;
8
8
using SharpGLTF . Geometry ;
9
9
using SharpGLTF . Geometry . VertexTypes ;
10
- using SharpGLTF . IO ;
11
10
using SharpGLTF . Materials ;
12
11
using SharpGLTF . Scenes ;
13
12
@@ -84,9 +83,12 @@ private MeshExporter(in ExportConfig config, MdlFile mdl, byte lod, ushort meshI
84
83
_boneIndexMap = BuildBoneIndexMap ( skeleton . Value ) ;
85
84
86
85
var usages = _mdl . VertexDeclarations [ _meshIndex ] . VertexElements
86
+ . GroupBy ( ele => ( MdlFile . VertexUsage ) ele . Usage , ele => ele )
87
87
. 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 ( )
90
92
) ;
91
93
92
94
_geometryType = GetGeometryType ( usages ) ;
@@ -112,6 +114,7 @@ private MeshExporter(in ExportConfig config, MdlFile mdl, byte lod, ushort meshI
112
114
113
115
var indexMap = new Dictionary < ushort , int > ( ) ;
114
116
// #TODO @ackwell maybe fix for V6 Models, I think this works fine.
117
+
115
118
foreach ( var ( xivBoneIndex , tableIndex ) in xivBoneTable . BoneIndex . Take ( ( int ) xivBoneTable . BoneCount ) . WithIndex ( ) )
116
119
{
117
120
var boneName = _mdl . Bones [ xivBoneIndex ] ;
@@ -278,18 +281,22 @@ private IReadOnlyList<IVertexBuilder> BuildVertices()
278
281
279
282
var sortedElements = _mdl . VertexDeclarations [ _meshIndex ] . VertexElements
280
283
. OrderBy ( element => element . Offset )
281
- . Select ( element => ( ( MdlFile . VertexUsage ) element . Usage , element ) )
282
284
. ToList ( ) ;
283
-
284
285
var vertices = new List < IVertexBuilder > ( ) ;
285
286
286
- var attributes = new Dictionary < MdlFile . VertexUsage , object > ( ) ;
287
+ var attributes = new Dictionary < MdlFile . VertexUsage , List < object > > ( ) ;
287
288
for ( var vertexIndex = 0 ; vertexIndex < XivMesh . VertexCount ; vertexIndex ++ )
288
289
{
289
290
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
+
293
300
294
301
var vertexGeometry = BuildVertexGeometry ( attributes ) ;
295
302
var vertexMaterial = BuildVertexMaterial ( attributes ) ;
@@ -320,7 +327,7 @@ private object ReadVertexAttribute(MdlFile.VertexType type, BinaryReader reader)
320
327
}
321
328
322
329
/// <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 )
324
331
{
325
332
if ( ! usages . ContainsKey ( MdlFile . VertexUsage . Position ) )
326
333
throw _notifier . Exception ( "Mesh does not contain position vertex elements." ) ;
@@ -335,28 +342,28 @@ private Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.Ve
335
342
}
336
343
337
344
/// <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 )
339
346
{
340
347
if ( _geometryType == typeof ( VertexPosition ) )
341
348
return new VertexPosition (
342
- ToVector3 ( attributes [ MdlFile . VertexUsage . Position ] )
349
+ ToVector3 ( GetFirstSafe ( attributes , MdlFile . VertexUsage . Position ) )
343
350
) ;
344
351
345
352
if ( _geometryType == typeof ( VertexPositionNormal ) )
346
353
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 ) )
349
356
) ;
350
357
351
358
if ( _geometryType == typeof ( VertexPositionNormalTangent ) )
352
359
{
353
360
// (Bi)tangents are universally stored as ByteFloat4, which uses 0..1 to represent the full -1..1 range.
354
361
// 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 ;
356
363
357
364
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 ) ) ,
360
367
bitangent
361
368
) ;
362
369
}
@@ -365,18 +372,23 @@ private IVertexGeometry BuildVertexGeometry(IReadOnlyDictionary<MdlFile.VertexUs
365
372
}
366
373
367
374
/// <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 )
369
376
{
370
377
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 )
373
381
{
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
+ }
380
392
381
393
var materialUsages = (
382
394
uvCount ,
@@ -385,40 +397,42 @@ private Type GetMaterialType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.Ve
385
397
386
398
return materialUsages switch
387
399
{
400
+ ( 3 , true ) => typeof ( VertexTexture3ColorFfxiv ) ,
401
+ ( 3 , false ) => typeof ( VertexTexture3 ) ,
388
402
( 2 , true ) => typeof ( VertexTexture2ColorFfxiv ) ,
389
403
( 2 , false ) => typeof ( VertexTexture2 ) ,
390
404
( 1 , true ) => typeof ( VertexTexture1ColorFfxiv ) ,
391
405
( 1 , false ) => typeof ( VertexTexture1 ) ,
392
406
( 0 , true ) => typeof ( VertexColorFfxiv ) ,
393
407
( 0 , false ) => typeof ( VertexEmpty ) ,
394
408
395
- _ => throw new Exception ( "Unreachable .") ,
409
+ _ => throw _notifier . Exception ( $ "Unhandled UV count of { uvCount } encountered .") ,
396
410
} ;
397
411
}
398
412
399
413
/// <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 )
401
415
{
402
416
if ( _materialType == typeof ( VertexEmpty ) )
403
417
return new VertexEmpty ( ) ;
404
418
405
419
if ( _materialType == typeof ( VertexColorFfxiv ) )
406
- return new VertexColorFfxiv ( ToVector4 ( attributes [ MdlFile . VertexUsage . Color ] ) ) ;
420
+ return new VertexColorFfxiv ( ToVector4 ( GetFirstSafe ( attributes , MdlFile . VertexUsage . Color ) ) ) ;
407
421
408
422
if ( _materialType == typeof ( VertexTexture1 ) )
409
- return new VertexTexture1 ( ToVector2 ( attributes [ MdlFile . VertexUsage . UV ] ) ) ;
423
+ return new VertexTexture1 ( ToVector2 ( GetFirstSafe ( attributes , MdlFile . VertexUsage . UV ) ) ) ;
410
424
411
425
if ( _materialType == typeof ( VertexTexture1ColorFfxiv ) )
412
426
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 ) )
415
429
) ;
416
430
417
431
// XIV packs two UVs into a single vec4 attribute.
418
432
419
433
if ( _materialType == typeof ( VertexTexture2 ) )
420
434
{
421
- var uv = ToVector4 ( attributes [ MdlFile . VertexUsage . UV ] ) ;
435
+ var uv = ToVector4 ( GetFirstSafe ( attributes , MdlFile . VertexUsage . UV ) ) ;
422
436
return new VertexTexture2 (
423
437
new Vector2 ( uv . X , uv . Y ) ,
424
438
new Vector2 ( uv . Z , uv . W )
@@ -427,37 +441,55 @@ private IVertexMaterial BuildVertexMaterial(IReadOnlyDictionary<MdlFile.VertexUs
427
441
428
442
if ( _materialType == typeof ( VertexTexture2ColorFfxiv ) )
429
443
{
430
- var uv = ToVector4 ( attributes [ MdlFile . VertexUsage . UV ] ) ;
444
+ var uv = ToVector4 ( GetFirstSafe ( attributes , MdlFile . VertexUsage . UV ) ) ;
431
445
return new VertexTexture2ColorFfxiv (
432
446
new Vector2 ( uv . X , uv . Y ) ,
433
447
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 ) )
435
472
) ;
436
473
}
437
474
438
475
throw _notifier . Exception ( $ "Unknown material type { _skinningType } ") ;
439
476
}
440
477
441
478
/// <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 )
443
480
{
444
481
if ( usages . ContainsKey ( MdlFile . VertexUsage . BlendWeights ) && usages . ContainsKey ( MdlFile . VertexUsage . BlendIndices ) )
445
482
{
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 ) ;
454
486
}
455
487
456
488
return typeof ( VertexEmpty ) ;
457
489
}
458
490
459
491
/// <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 )
461
493
{
462
494
if ( _skinningType == typeof ( VertexEmpty ) )
463
495
return new VertexEmpty ( ) ;
@@ -467,8 +499,8 @@ private IVertexSkinning BuildVertexSkinning(IReadOnlyDictionary<MdlFile.VertexUs
467
499
if ( _boneIndexMap == null )
468
500
throw _notifier . Exception ( "Tried to build skinned vertex but no bone mappings are available." ) ;
469
501
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 ) ;
472
504
var indices = ToByteArray ( indiciesData ) ;
473
505
var weights = ToFloatArray ( weightsData ) ;
474
506
@@ -495,6 +527,17 @@ private IVertexSkinning BuildVertexSkinning(IReadOnlyDictionary<MdlFile.VertexUs
495
527
throw _notifier . Exception ( $ "Unknown skinning type { _skinningType } ") ;
496
528
}
497
529
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
+
498
541
/// <summary> Convert a vertex attribute value to a Vector2. Supported inputs are Vector2, Vector3, and Vector4. </summary>
499
542
private static Vector2 ToVector2 ( object data )
500
543
=> data switch
0 commit comments