@@ -51,6 +51,8 @@ public class ModelImportOptions : ICloneable
51
51
52
52
public bool ClearEmptyMeshData { get ; set ; }
53
53
54
+ public bool AutoAssignHeels { get ; set ; }
55
+
54
56
/// <summary>
55
57
/// Logging output function.
56
58
/// </summary>
@@ -83,6 +85,7 @@ public ModelImportOptions()
83
85
SourceApplication = "Unknown" ;
84
86
ReferenceItem = null ;
85
87
ClearEmptyMeshData = false ;
88
+ AutoAssignHeels = true ;
86
89
}
87
90
88
91
@@ -177,6 +180,11 @@ public async Task Apply(TTModel ttModel, XivMdl currentMdl = null, XivMdl origin
177
180
}
178
181
}
179
182
183
+ if ( AutoAssignHeels )
184
+ {
185
+ ModelModifiers . AssignHeelAttribute ( ttModel , LoggingFunction ) ;
186
+ }
187
+
180
188
// Ensure shape data is updated with our various changes.
181
189
ttModel . UpdateShapeData ( ) ;
182
190
}
@@ -290,14 +298,14 @@ public static void AutoScaleModel(TTModel ttModel, TTModel originalModel, double
290
298
0.00254D ,
291
299
} ;
292
300
293
- foreach ( var conversion in possibleConversions )
301
+ foreach ( var conversion in possibleConversions )
294
302
{
295
303
var nSize = NewModelSize * conversion ;
296
304
var diff = ( OldModelSize - nSize ) / OldModelSize ;
297
305
298
- if ( Math . Abs ( diff ) < tolerance )
306
+ if ( Math . Abs ( diff ) < tolerance )
299
307
{
300
- if ( conversion != 1.0D )
308
+ if ( conversion != 1.0D )
301
309
{
302
310
loggingFunction ( true , "Correcting Scaling Error: Rescaling model by " + conversion ) ;
303
311
ScaleModel ( ttModel , conversion , loggingFunction ) ;
@@ -325,7 +333,7 @@ public static void ScaleModel(TTModel ttModel, double scale, Action<bool, string
325
333
326
334
foreach ( var m in ttModel . MeshGroups )
327
335
{
328
- foreach ( var p in m . Parts )
336
+ foreach ( var p in m . Parts )
329
337
{
330
338
foreach ( var v in p . Vertices )
331
339
{
@@ -349,7 +357,7 @@ public static void MergeMeshTypes(TTModel ttModel, XivMdl rawMdl, Action<bool, s
349
357
var meshIdx = 0 ;
350
358
foreach ( var baseMesh in rawMdl . LoDList [ 0 ] . MeshDataList )
351
359
{
352
- if ( meshIdx >= ttModel . MeshGroups . Count )
360
+ if ( meshIdx >= ttModel . MeshGroups . Count )
353
361
{
354
362
continue ;
355
363
}
@@ -374,7 +382,7 @@ public static void MergeGeometryData(TTModel ttModel, XivMdl rawMdl, Action<bool
374
382
375
383
var meshIdx = 0 ;
376
384
var totalPartIdx = 0 ;
377
- foreach ( var baseMesh in rawMdl . LoDList [ 0 ] . MeshDataList )
385
+ foreach ( var baseMesh in rawMdl . LoDList [ 0 ] . MeshDataList )
378
386
{
379
387
var ttMesh = new TTMeshGroup ( ) ;
380
388
ttModel . MeshGroups . Add ( ttMesh ) ;
@@ -409,7 +417,7 @@ public static void MergeGeometryData(TTModel ttModel, XivMdl rawMdl, Action<bool
409
417
totalParts = 1 ;
410
418
}
411
419
412
- for ( var pi = 0 ; pi < totalParts ; pi ++ )
420
+ for ( var pi = 0 ; pi < totalParts ; pi ++ )
413
421
{
414
422
415
423
var ttPart = new TTMeshPart ( ) ;
@@ -426,7 +434,7 @@ public static void MergeGeometryData(TTModel ttModel, XivMdl rawMdl, Action<bool
426
434
// Get the Vertices unique to this part.
427
435
var uniqueVertexIdSet = new SortedSet < int > ( indices ) ; // Maximum possible amount is # of indices, though likely it is less.
428
436
429
- foreach ( var ind in indices )
437
+ foreach ( var ind in indices )
430
438
{
431
439
uniqueVertexIdSet . Add ( ind ) ;
432
440
}
@@ -439,7 +447,7 @@ public static void MergeGeometryData(TTModel ttModel, XivMdl rawMdl, Action<bool
439
447
440
448
// Now we need to loop through, copy over the vertex data, keeping track of the new vertex IDs.
441
449
ttPart . Vertices = new List < TTVertex > ( uniqueVertexIds . Count ) ;
442
- for ( var i = 0 ; i < uniqueVertexIds . Count ; i ++ )
450
+ for ( var i = 0 ; i < uniqueVertexIds . Count ; i ++ )
443
451
{
444
452
var oldVertexId = uniqueVertexIds [ i ] ;
445
453
var ttVert = new TTVertex ( ) ;
@@ -499,7 +507,7 @@ public static void MergeGeometryData(TTModel ttModel, XivMdl rawMdl, Action<bool
499
507
ttVert . UV2 . X = 0 ;
500
508
}
501
509
502
- if ( float . IsNaN ( ttVert . UV2 . Y ) )
510
+ if ( float . IsNaN ( ttVert . UV2 . Y ) )
503
511
{
504
512
ttVert . UV2 . Y = 0 ;
505
513
}
@@ -523,11 +531,11 @@ public static void MergeGeometryData(TTModel ttModel, XivMdl rawMdl, Action<bool
523
531
524
532
var vertexBoneArrayLength = baseMesh . VertexBoneArraySize ;
525
533
// Now for the fun part, establishing bones.
526
- for ( var bIdx = 0 ; bIdx < vertexBoneArrayLength ; bIdx ++ )
534
+ for ( var bIdx = 0 ; bIdx < vertexBoneArrayLength ; bIdx ++ )
527
535
{
528
536
// Vertex doesn't have weights.
529
537
if ( baseMesh . VertexData . BoneWeights . Count <= oldVertexId ) break ;
530
-
538
+
531
539
// No more weights for this vertex.
532
540
if ( baseMesh . VertexData . BoneIndices [ oldVertexId ] . Length <= bIdx ) break ;
533
541
@@ -541,8 +549,8 @@ public static void MergeGeometryData(TTModel ttModel, XivMdl rawMdl, Action<bool
541
549
// These seem to actually be irrelevant, and the bone ID is just routed directly to the mesh level identifier.
542
550
// var partBoneSet = rawMdl.PartBoneSets.BoneIndices.GetRange(basePart.BoneStartOffset, basePart.BoneCount);
543
551
544
- ttVert . BoneIds [ bIdx ] = ( byte ) boneId ;
545
- ttVert . Weights [ bIdx ] = ( byte ) Math . Round ( weight * 255 ) ;
552
+ ttVert . BoneIds [ bIdx ] = ( byte ) boneId ;
553
+ ttVert . Weights [ bIdx ] = ( byte ) Math . Round ( weight * 255 ) ;
546
554
}
547
555
548
556
ttPart . Vertices . Add ( ttVert ) ;
@@ -732,7 +740,7 @@ public static void MergeShapeData(TTModel ttModel, XivMdl ogMdl, Action<bool, st
732
740
badPart = true ;
733
741
break ;
734
742
}
735
-
743
+
736
744
vertexReplacements . Add ( ogGroup . VertexData . Indices [ d . BaseIndex ] , vId ) ;
737
745
738
746
var vert = new TTVertex ( ) ;
@@ -780,18 +788,18 @@ public static void MergeShapeData(TTModel ttModel, XivMdl ogMdl, Action<bool, st
780
788
vertices . Add ( vId , vert ) ;
781
789
}
782
790
783
- if ( badPart )
791
+ if ( badPart )
784
792
{
785
793
continue ;
786
794
}
787
795
788
796
// Now we need to go through and create the shape part objects for each part.
789
797
Dictionary < int , TTShapePart > shapeParts = new Dictionary < int , TTShapePart > ( ) ;
790
- foreach ( var kv in vertexReplacements )
798
+ foreach ( var kv in vertexReplacements )
791
799
{
792
800
// For every vertex which was replaced, we need to identify what part owned it.
793
801
var info = ttMesh . GetPartRelevantVertexInformation ( kv . Key ) ;
794
- if ( ! shapeParts . ContainsKey ( info . PartId ) )
802
+ if ( ! shapeParts . ContainsKey ( info . PartId ) )
795
803
{
796
804
var tempShp = new TTShapePart ( ) ;
797
805
tempShp . Name = shp . ShapeName ;
@@ -807,10 +815,10 @@ public static void MergeShapeData(TTModel ttModel, XivMdl ogMdl, Action<bool, st
807
815
}
808
816
809
817
// Now just add the shapes to the associated TTParts
810
- foreach ( var kv in shapeParts )
818
+ foreach ( var kv in shapeParts )
811
819
{
812
820
if ( kv . Key == - 1 ) continue ;
813
- if ( ttMesh . Parts [ kv . Key ] . ShapeParts . Count == 0 )
821
+ if ( ttMesh . Parts [ kv . Key ] . ShapeParts . Count == 0 )
814
822
{
815
823
// Pretty janky, but a simple enough way to guarantee we can always
816
824
// restore back to the original shape.
@@ -860,9 +868,9 @@ public static void ForceUVQuadrant(TTModel model, Action<bool, string> loggingFu
860
868
}
861
869
862
870
loggingFunction ( false , "Forcing UV1 to [1,-1]..." ) ;
863
- foreach ( var m in model . MeshGroups )
871
+ foreach ( var m in model . MeshGroups )
864
872
{
865
- foreach ( var p in m . Parts )
873
+ foreach ( var p in m . Parts )
866
874
{
867
875
bool anyNegativeX = p . Vertices . Any ( x => x . UV1 . X < 0 ) ;
868
876
bool anyPositiveY = p . Vertices . Any ( x => x . UV1 . Y > 0 ) ;
@@ -883,7 +891,7 @@ public static void ForceUVQuadrant(TTModel model, Action<bool, string> loggingFu
883
891
// The extra [anyPositive/negative] values check is to avoid potentially
884
892
// shifting values at exactly 0 if 0 is effectively the "top" of the
885
893
// used UV space.
886
-
894
+
887
895
// The goal here is to allow the user to have used any exact quadrant in the [-1 - 1, -1 - 1] range
888
896
// and maintain the UV correctly, even if they used exactly [1,1] as a coordinate, for example.
889
897
@@ -1076,7 +1084,7 @@ private static void UpdateShapeParts(TTMeshPart p)
1076
1084
{
1077
1085
var shpVertex = shpKv . Value . Vertices [ vKv . Value ] ;
1078
1086
var pVertex = p . Vertices [ vKv . Key ] ;
1079
- var newVert = ( TTVertex ) pVertex . Clone ( ) ;
1087
+ var newVert = ( TTVertex ) pVertex . Clone ( ) ;
1080
1088
newVert . Position = shpVertex . Position ;
1081
1089
shpKv . Value . Vertices [ vKv . Value ] = newVert ;
1082
1090
}
@@ -1135,7 +1143,7 @@ public static async Task RaceConvertRecursive(TTModel model, XivRace targetRace,
1135
1143
await CalculateTangents ( model , loggingFunction , true ) ;
1136
1144
}
1137
1145
private static async Task INTERNAL_RaceConvertRecursive ( TTModel model , XivRace targetRace , XivRace originalRace , Action < bool , string > loggingFunction = null , ModTransaction tx = null )
1138
- {
1146
+ {
1139
1147
try
1140
1148
{
1141
1149
if ( originalRace . IsDirectParentOf ( targetRace ) )
@@ -1250,12 +1258,12 @@ public static async Task ApplyRacialDeform(TTModel model, XivRace targetRace, bo
1250
1258
var parent = dict . FirstOrDefault ( x => x . Value . BoneNumber == dict [ bone ] . BoneParent ) . Value ;
1251
1259
1252
1260
// Walk up the tree until we find a parent with a deform.
1253
- while ( parent != null && ! def . Deformations . ContainsKey ( parent . BoneName ) )
1261
+ while ( parent != null && ! def . Deformations . ContainsKey ( parent . BoneName ) )
1254
1262
{
1255
1263
parent = dict . FirstOrDefault ( x => x . Value . BoneNumber == parent . BoneParent ) . Value ;
1256
1264
}
1257
1265
1258
- if ( parent != null )
1266
+ if ( parent != null )
1259
1267
{
1260
1268
// Found a parent? use that bone's deforms.
1261
1269
def . Deformations [ bone ] = def . Deformations [ parent . BoneName ] ;
@@ -1426,7 +1434,7 @@ public static async Task ApplyRacialDeform(TTModel model, XivRace targetRace, bo
1426
1434
}
1427
1435
}
1428
1436
}
1429
- catch ( Exception ex )
1437
+ catch ( Exception ex )
1430
1438
{
1431
1439
throw ;
1432
1440
}
@@ -1444,9 +1452,9 @@ public static async Task ApplyRacialDeform(TTModel model, XivRace targetRace, bo
1444
1452
private static Vector3 MatrixTransform ( Vector3 vector , Matrix transform )
1445
1453
{
1446
1454
var result = new Vector3 (
1447
- ( vector . X * transform [ 0 ] ) + ( vector . Y * transform [ 1 ] ) + ( vector . Z * transform [ 2 ] ) + ( 1.0f * transform [ 3 ] ) ,
1448
- ( vector . X * transform [ 4 ] ) + ( vector . Y * transform [ 5 ] ) + ( vector . Z * transform [ 6 ] ) + ( 1.0f * transform [ 7 ] ) ,
1449
- ( vector . X * transform [ 8 ] ) + ( vector . Y * transform [ 9 ] ) + ( vector . Z * transform [ 10 ] ) + ( 1.0f * transform [ 11 ] ) ) ;
1455
+ ( vector . X * transform [ 0 ] ) + ( vector . Y * transform [ 1 ] ) + ( vector . Z * transform [ 2 ] ) + ( 1.0f * transform [ 3 ] ) ,
1456
+ ( vector . X * transform [ 4 ] ) + ( vector . Y * transform [ 5 ] ) + ( vector . Z * transform [ 6 ] ) + ( 1.0f * transform [ 7 ] ) ,
1457
+ ( vector . X * transform [ 8 ] ) + ( vector . Y * transform [ 9 ] ) + ( vector . Z * transform [ 10 ] ) + ( 1.0f * transform [ 11 ] ) ) ;
1450
1458
1451
1459
return result ;
1452
1460
}
@@ -1494,7 +1502,7 @@ public static bool CleanWeight(TTVertex v, int maxWeights = 4, Action<bool, stri
1494
1502
1495
1503
int boneSum = 0 ;
1496
1504
var sum = v . Weights . Select ( x => ( int ) x ) . Aggregate ( ( sum , x ) => sum + x ) ;
1497
- if ( sum == 255 )
1505
+ if ( sum == 255 )
1498
1506
{
1499
1507
return false ;
1500
1508
}
@@ -1589,7 +1597,7 @@ public static void CleanWeights(TTModel model, Action<bool, string> loggingFunct
1589
1597
foreach ( var v in p . Vertices )
1590
1598
{
1591
1599
bool majorCorrection = false ;
1592
- if ( usage . NeedsEightWeights )
1600
+ if ( usage . NeedsEightWeights )
1593
1601
{
1594
1602
majorCorrection = CleanWeight ( v , 8 , loggingFunction ) ;
1595
1603
} else
@@ -1673,13 +1681,13 @@ internal static async Task ConvertFlowData(TTModel model, Action<bool, string> l
1673
1681
1674
1682
1675
1683
var tasks = new List < Task > ( ) ;
1676
- foreach ( var m in model . MeshGroups )
1684
+ foreach ( var m in model . MeshGroups )
1677
1685
{
1678
- foreach ( var p in m . Parts )
1686
+ foreach ( var p in m . Parts )
1679
1687
{
1680
1688
tasks . Add ( Task . Run ( ( ) =>
1681
1689
{
1682
- foreach ( var v in p . Vertices )
1690
+ foreach ( var v in p . Vertices )
1683
1691
{
1684
1692
var f = new float [ 3 ] ;
1685
1693
@@ -1739,6 +1747,60 @@ internal static void MakeExportReady(TTModel model, bool shiftUv = true, Action<
1739
1747
model . UVState = TTModel . UVAddressingSpace . Standard ;
1740
1748
}
1741
1749
1750
+ public static string AssignHeelAttribute ( TTModel model , Action < bool , string > loggingFunction = null )
1751
+ {
1752
+ if ( model == null || ! model . HasPath )
1753
+ {
1754
+ return "" ;
1755
+ }
1756
+ loggingFunction ??= NoOp ;
1757
+
1758
+ if ( ! model . Source . EndsWith ( "_top.mdl" )
1759
+ && ! model . Source . EndsWith ( "_dwn.mdl" )
1760
+ && ! model . Source . EndsWith ( "_sho.mdl" ) ) {
1761
+ return "" ;
1762
+ }
1763
+ const string _Prefix = "heels_offset=" ;
1764
+
1765
+ loggingFunction ? . Invoke ( false , "Assigning Heel Attributes..." ) ;
1766
+
1767
+ var max = float . MaxValue ;
1768
+ TTMeshPart first = null ;
1769
+ foreach ( var m in model . MeshGroups )
1770
+ {
1771
+ foreach ( var p in m . Parts )
1772
+ {
1773
+ foreach ( var v in p . Vertices )
1774
+ {
1775
+ if ( v . Position . Y < max )
1776
+ {
1777
+ max = v . Position . Y ;
1778
+ }
1779
+ }
1780
+
1781
+ first ??= p ;
1782
+ }
1783
+ }
1784
+
1785
+ if ( max < 0 )
1786
+ {
1787
+ foreach ( var m in model . MeshGroups )
1788
+ {
1789
+ foreach ( var p in m . Parts )
1790
+ {
1791
+ p . Attributes . RemoveWhere ( x => x . StartsWith ( _Prefix ) ) ;
1792
+ }
1793
+ }
1794
+
1795
+ // Offset is inverted, since it's bringing the character to 0.
1796
+ max *= - 1 ;
1797
+ var atr = _Prefix + max . ToString ( "0.0000" ) ;
1798
+ first . Attributes . Add ( atr ) ;
1799
+ return atr ;
1800
+ }
1801
+ return "" ;
1802
+ }
1803
+
1742
1804
/// <summary>
1743
1805
/// Convenience function for calculating tangent data for a TTModel.
1744
1806
/// </summary>
0 commit comments