@@ -2372,6 +2372,11 @@ public static async Task<byte[]> MakeUncompressedMdlFile(TTModel model, string t
2372
2372
2373
2373
}
2374
2374
2375
+ // I do not know where in FFXIV's model pipeline this limit comes from,
2376
+ // but vertex buffers larger than 2^23 will overflow and wrap around in game.
2377
+ public const int _MaxVertexBufferSize = 8388608 ;
2378
+
2379
+
2375
2380
/// <summary>
2376
2381
/// Creates a new Uncompressed MDL file from the given information.
2377
2382
/// OGMdl is used to fill in gaps in data types we do not know about.
@@ -2383,6 +2388,7 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
2383
2388
{
2384
2389
var mdlVersion = ttModel . MdlVersion > 0 ? ttModel . MdlVersion : ogMdl . MdlVersion ;
2385
2390
2391
+
2386
2392
// Debug Code
2387
2393
/*
2388
2394
var root = XivCache.GetFilePathRoot(ogMdl.MdlPath);
@@ -2410,6 +2416,31 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
2410
2416
{
2411
2417
var usageInfo = ttModel . GetUsageInfo ( ) ;
2412
2418
2419
+ var vertexSize = 48 ;
2420
+ if ( usageInfo . NeedsEightWeights )
2421
+ {
2422
+ vertexSize += 8 ;
2423
+ }
2424
+ if ( usageInfo . UsesUv2 )
2425
+ {
2426
+ vertexSize += 8 ;
2427
+ }
2428
+ if ( usageInfo . UsesVColor2 )
2429
+ {
2430
+ vertexSize += 4 ;
2431
+ }
2432
+ var shapeVertCount = ttModel . MeshGroups . Sum ( m => m . Parts . Sum ( p => p . ShapeParts . Sum ( s => s . Value . Vertices . Count ) ) ) ;
2433
+ var totalVertexCount = shapeVertCount + ttModel . VertexCount ;
2434
+ var estimatedVertexBufferSize = ( vertexSize * totalVertexCount ) ;
2435
+
2436
+ if ( estimatedVertexBufferSize >= _MaxVertexBufferSize )
2437
+ {
2438
+ upgradePrecision = false ;
2439
+ }
2440
+
2441
+
2442
+
2443
+
2413
2444
ttModel . OrderMeshGroupsForImport ( ) ;
2414
2445
var rawShapeData = ttModel . GetRawShapeParts ( ) ;
2415
2446
@@ -2491,8 +2522,7 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
2491
2522
source . OrderBy ( x => ( x . DataBlock * - 1000 ) + x . DataOffset ) ;
2492
2523
vertexStreamCounts . Add ( source . Max ( x => x . DataBlock ) + 1 ) ;
2493
2524
2494
- // If we're upgrading precision on a v6 mdl, might as well add all the bells and whistles.
2495
- if ( mdlVersion >= 6 && upgradePrecision )
2525
+ if ( upgradePrecision )
2496
2526
{
2497
2527
// Add precomputed tangent data.
2498
2528
var tangentCount = source . Count ( x => x . DataUsage == VertexUsageType . Tangent ) ;
@@ -2508,23 +2538,61 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
2508
2538
DataUsage = VertexUsageType . Tangent
2509
2539
} ) ;
2510
2540
}
2541
+ } else
2542
+ {
2543
+ source . RemoveAll ( x => x . DataUsage == VertexUsageType . Tangent ) ;
2544
+ }
2511
2545
2512
- if ( usageInfo . UsesVColor2 )
2546
+ if ( usageInfo . UsesVColor2 )
2547
+ {
2548
+ // Add 2nd color channel for faux-wind simulation.
2549
+ var colorCounts = source . Count ( x => x . DataUsage == VertexUsageType . Color ) ;
2550
+ var colorIdx = source . FindIndex ( x => x . DataUsage == VertexUsageType . Color ) ;
2551
+ if ( colorCounts == 1 )
2513
2552
{
2514
- // Add 2nd color channel for faux-wind simulation.
2515
- var colorCounts = source . Count ( x => x . DataUsage == VertexUsageType . Color ) ;
2516
- var colorIdx = source . FindIndex ( x => x . DataUsage == VertexUsageType . Color ) ;
2517
- if ( colorCounts == 1 )
2553
+ source . Insert ( colorIdx + 1 , new VertexDataStruct ( )
2518
2554
{
2519
- source . Insert ( colorIdx + 1 , new VertexDataStruct ( )
2520
- {
2521
- DataBlock = 1 ,
2522
- DataOffset = 0 , // Offset doesn't matter since we recalculate it anyways.
2523
- DataType = VertexDataType . Ubyte4n ,
2524
- DataUsage = VertexUsageType . Color
2525
- } ) ;
2526
- }
2555
+ DataBlock = 1 ,
2556
+ DataOffset = 0 , // Offset doesn't matter since we recalculate it anyways.
2557
+ DataType = VertexDataType . Ubyte4n ,
2558
+ DataUsage = VertexUsageType . Color
2559
+ } ) ;
2560
+ }
2561
+ } else
2562
+ {
2563
+ source . RemoveAll ( x => x . DataUsage == VertexUsageType . Color && x . Count == 1 ) ;
2564
+ }
2565
+
2566
+ if ( ttModel . HasWeights )
2567
+ {
2568
+ // Ensure we have bone vertex structs if we need them.
2569
+ var bone = source . FirstOrDefault ( x => x . DataUsage == VertexUsageType . BoneWeight ) ;
2570
+ if ( bone == null )
2571
+ {
2572
+ source . Add ( new VertexDataStruct ( )
2573
+ {
2574
+ DataBlock = 0 ,
2575
+ DataOffset = 0 ,
2576
+ DataType = VertexDataType . Ubyte4 ,
2577
+ DataUsage = VertexUsageType . BoneWeight
2578
+ } ) ;
2579
+ }
2580
+ bone = source . FirstOrDefault ( x => x . DataUsage == VertexUsageType . BoneIndex ) ;
2581
+ if ( bone == null )
2582
+ {
2583
+ source . Add ( new VertexDataStruct ( )
2584
+ {
2585
+ DataBlock = 0 ,
2586
+ DataOffset = 0 ,
2587
+ DataType = VertexDataType . Ubyte4 ,
2588
+ DataUsage = VertexUsageType . BoneIndex
2589
+ } ) ;
2527
2590
}
2591
+ } else
2592
+ {
2593
+ // Remove bone vertex structs if they're not used.
2594
+ source . RemoveAll ( x => x . DataUsage == VertexUsageType . BoneWeight ) ;
2595
+ source . RemoveAll ( x => x . DataUsage == VertexUsageType . BoneIndex ) ;
2528
2596
}
2529
2597
2530
2598
foreach ( var vds in source )
@@ -2555,7 +2623,7 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
2555
2623
2556
2624
if ( dataUsage == VertexUsageType . BoneWeight )
2557
2625
{
2558
- if ( mdlVersion >= 6 && upgradePrecision )
2626
+ if ( usageInfo . NeedsEightWeights )
2559
2627
{
2560
2628
dataType = VertexDataType . UByte8 ;
2561
2629
}
@@ -2567,7 +2635,7 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
2567
2635
2568
2636
if ( dataUsage == VertexUsageType . BoneIndex )
2569
2637
{
2570
- if ( mdlVersion >= 6 && upgradePrecision )
2638
+ if ( usageInfo . NeedsEightWeights )
2571
2639
{
2572
2640
dataType = VertexDataType . UByte8 ;
2573
2641
}
@@ -2711,6 +2779,12 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
2711
2779
2712
2780
Dat . Pad ( indexDataBlock , 16 ) ;
2713
2781
}
2782
+
2783
+ if ( vertexDataBlock . Count > _MaxVertexBufferSize )
2784
+ {
2785
+ throw new InvalidDataException ( $ "Total Vertex buffer data size is too large, even after compression attempts:\n Total Size: { vertexDataBlock . Count } \n Max Size: { _MaxVertexBufferSize } \n \n Please reduce the total number of Vertices in the model:\n Vertices (After Unwelding): { totalVertexCount } ") ;
2786
+ }
2787
+
2714
2788
#endregion
2715
2789
2716
2790
// Path Data
0 commit comments