25
25
using xivModdingFramework . Mods ;
26
26
using xivModdingFramework . Textures . Enums ;
27
27
using static xivModdingFramework . Cache . XivCache ;
28
+ using MathNet . Numerics . LinearAlgebra ;
28
29
29
30
namespace xivModdingFramework . Models . DataContainers
30
31
{
@@ -88,6 +89,7 @@ public class TTVertex : ICloneable {
88
89
public Vector3 Normal = new Vector3 ( 0 , 0 , 0 ) ;
89
90
public Vector3 Binormal = new Vector3 ( 0 , 0 , 0 ) ;
90
91
public Vector3 Tangent = new Vector3 ( 0 , 0 , 0 ) ;
92
+ public Vector3 FlowDirection = new Vector3 ( 0 , 0 , 0 ) ;
91
93
92
94
// This is Technically BINORMAL handedness in FFXIV.
93
95
// A values of TRUE indicates we need to flip the Tangent when generated. (-1)
@@ -107,6 +109,64 @@ public class TTVertex : ICloneable {
107
109
public byte [ ] BoneIds = new byte [ _BONE_ARRAY_LENGTH ] ;
108
110
public byte [ ] Weights = new byte [ _BONE_ARRAY_LENGTH ] ;
109
111
112
+ public Vector3 GetTangentSpaceFlow ( )
113
+ {
114
+ var flow = WorldToTangent ( FlowDirection . ToArray ( ) ) ;
115
+ return new Vector3 ( flow [ 0 ] , flow [ 1 ] , 0 ) . Normalized ( ) ;
116
+ }
117
+
118
+ public float [ ] WorldToTangent ( float [ ] vector )
119
+ {
120
+ var mat = Matrix < float > . Build . Dense ( 3 , 3 ) ;
121
+
122
+ var n = Normal . Normalized ( ) ;
123
+ var b = Binormal . Normalized ( ) ;
124
+ var t = Tangent . Normalized ( ) ;
125
+
126
+ mat [ 0 , 0 ] = t [ 0 ] ;
127
+ mat [ 0 , 1 ] = t [ 1 ] ;
128
+ mat [ 0 , 2 ] = t [ 2 ] ;
129
+
130
+ mat [ 1 , 0 ] = b [ 0 ] ;
131
+ mat [ 1 , 1 ] = b [ 1 ] ;
132
+ mat [ 1 , 2 ] = b [ 2 ] ;
133
+
134
+ mat [ 2 , 0 ] = n [ 0 ] ;
135
+ mat [ 2 , 1 ] = n [ 1 ] ;
136
+ mat [ 2 , 2 ] = n [ 2 ] ;
137
+ var vec = Vector < float > . Build . Dense ( vector ) ;
138
+
139
+ var flow = mat * vec ;
140
+ return flow . AsArray ( ) ;
141
+ }
142
+
143
+ public float [ ] TangentToWorld ( float [ ] vector )
144
+ {
145
+ var mat = Matrix < float > . Build . Dense ( 3 , 3 ) ;
146
+
147
+ var n = Normal . Normalized ( ) ;
148
+ var b = Binormal . Normalized ( ) ;
149
+ var t = Tangent . Normalized ( ) ;
150
+
151
+ mat [ 0 , 0 ] = t [ 0 ] ;
152
+ mat [ 0 , 1 ] = t [ 1 ] ;
153
+ mat [ 0 , 2 ] = t [ 2 ] ;
154
+
155
+ mat [ 1 , 0 ] = b [ 0 ] ;
156
+ mat [ 1 , 1 ] = b [ 1 ] ;
157
+ mat [ 1 , 2 ] = b [ 2 ] ;
158
+
159
+ mat [ 2 , 0 ] = n [ 0 ] ;
160
+ mat [ 2 , 1 ] = n [ 1 ] ;
161
+ mat [ 2 , 2 ] = n [ 2 ] ;
162
+ var vec = Vector < float > . Build . Dense ( vector ) ;
163
+
164
+ mat = mat . Transpose ( ) ;
165
+
166
+ var flow = mat * vec ;
167
+ return flow . AsArray ( ) ;
168
+ }
169
+
110
170
public static List < TTVertex > CloneVertexList ( List < TTVertex > verts )
111
171
{
112
172
var newVerts = new List < TTVertex > ( verts . Count ) ;
@@ -124,6 +184,7 @@ public static List<TTVertex> CloneVertexList(List<TTVertex> verts)
124
184
if ( a . Normal != b . Normal ) return false ;
125
185
if ( a . Binormal != b . Binormal ) return false ;
126
186
if ( a . Handedness != b . Handedness ) return false ;
187
+ if ( a . FlowDirection != b . FlowDirection ) return false ;
127
188
if ( a . UV1 != b . UV1 ) return false ;
128
189
if ( a . UV2 != b . UV2 ) return false ;
129
190
if ( a . UV2 != b . UV3 ) return false ;
@@ -1385,7 +1446,7 @@ public uint GetAttributeBitmask(int groupNumber, int partNumber)
1385
1446
/// </summary>
1386
1447
/// <param name="filePath"></param>
1387
1448
/// <returns></returns>
1388
- public static TTModel LoadFromFile ( string filePath , Action < bool , string > loggingFunction = null , ModelImportOptions settings = null )
1449
+ public static async Task < TTModel > LoadFromFile ( string filePath , Action < bool , string > loggingFunction = null , ModelImportOptions settings = null )
1389
1450
{
1390
1451
if ( loggingFunction == null )
1391
1452
{
@@ -1583,6 +1644,10 @@ public static TTModel LoadFromFile(string filePath, Action<bool, string> logging
1583
1644
vertex . Weights [ 6 ] = ( byte ) ( Math . Round ( reader . GetFloat ( "bone_7_weight" ) * 255 ) ) ;
1584
1645
vertex . Weights [ 7 ] = ( byte ) ( Math . Round ( reader . GetFloat ( "bone_8_weight" ) * 255 ) ) ;
1585
1646
1647
+
1648
+ vertex . FlowDirection [ 0 ] = reader . GetFloat ( "flow_u" ) ;
1649
+ vertex . FlowDirection [ 1 ] = reader . GetFloat ( "flow_v" ) ;
1650
+
1586
1651
return vertex ;
1587
1652
} ) ;
1588
1653
@@ -1651,6 +1716,10 @@ public static TTModel LoadFromFile(string filePath, Action<bool, string> logging
1651
1716
// Convert the model to FFXIV's internal weirdness.
1652
1717
ModelModifiers . MakeImportReady ( model , settings . ShiftImportUV , loggingFunction ) ;
1653
1718
1719
+ await ModelModifiers . CalculateTangents ( model , loggingFunction ) ;
1720
+
1721
+ await ModelModifiers . ConvertFlowData ( model , loggingFunction ) ;
1722
+
1654
1723
ModelModifiers . CleanWeights ( model , loggingFunction ) ;
1655
1724
1656
1725
return model ;
@@ -1662,6 +1731,7 @@ private static void MigrateImportDb(SQLiteConnection db)
1662
1731
Version version = null ;
1663
1732
1664
1733
bool hasUv3 = false ;
1734
+ bool hasFlow = false ;
1665
1735
var query = @"PRAGMA table_info(vertices);" ;
1666
1736
using ( var cmd = new SQLiteCommand ( query , db ) )
1667
1737
{
@@ -1674,6 +1744,10 @@ private static void MigrateImportDb(SQLiteConnection db)
1674
1744
{
1675
1745
hasUv3 = true ;
1676
1746
}
1747
+ if ( name == "flow_u" )
1748
+ {
1749
+ hasFlow = true ;
1750
+ }
1677
1751
}
1678
1752
1679
1753
if ( ! sqlReader . IsClosed )
@@ -1697,6 +1771,20 @@ private static void MigrateImportDb(SQLiteConnection db)
1697
1771
}
1698
1772
}
1699
1773
1774
+ if ( ! hasFlow )
1775
+ {
1776
+ query = "ALTER TABLE vertices ADD COLUMN flow_u REAL NOT NULL DEFAULT 0;" ;
1777
+ using ( var cmd = new SQLiteCommand ( query , db ) )
1778
+ {
1779
+ cmd . ExecuteScalar ( ) ;
1780
+ }
1781
+ query = "ALTER TABLE vertices ADD COLUMN flow_v REAL NOT NULL DEFAULT 0;" ;
1782
+ using ( var cmd = new SQLiteCommand ( query , db ) )
1783
+ {
1784
+ cmd . ExecuteScalar ( ) ;
1785
+ }
1786
+ }
1787
+
1700
1788
}
1701
1789
1702
1790
@@ -2389,8 +2477,8 @@ public static void SaveFullToFile(string filePath, XivRace race, List<TTModel> m
2389
2477
2390
2478
private static void WriteVertex ( TTVertex v , SQLiteConnection db , int meshIdx , int partIdx , int vIdx )
2391
2479
{
2392
- var query = @"insert into vertices ( mesh, part, vertex_id, position_x, position_y, position_z, normal_x, normal_y, normal_z, binormal_x, binormal_y, binormal_z, tangent_x, tangent_y, tangent_z, color_r, color_g, color_b, color_a, color2_r, color2_g, color2_b, color2_a, uv_1_u, uv_1_v, uv_2_u, uv_2_v, bone_1_id, bone_1_weight, bone_2_id, bone_2_weight, bone_3_id, bone_3_weight, bone_4_id, bone_4_weight, bone_5_id, bone_5_weight, bone_6_id, bone_6_weight, bone_7_id, bone_7_weight, bone_8_id, bone_8_weight, uv_3_u, uv_3_v)
2393
- values ($mesh, $part, $vertex_id, $position_x, $position_y, $position_z, $normal_x, $normal_y, $normal_z, $binormal_x, $binormal_y, $binormal_z, $tangent_x, $tangent_y, $tangent_z, $color_r, $color_g, $color_b, $color_a, $color2_r, $color2_g, $color2_b, $color2_a, $uv_1_u, $uv_1_v, $uv_2_u, $uv_2_v, $bone_1_id, $bone_1_weight, $bone_2_id, $bone_2_weight, $bone_3_id, $bone_3_weight, $bone_4_id, $bone_4_weight, $bone_5_id, $bone_5_weight, $bone_6_id, $bone_6_weight, $bone_7_id, $bone_7_weight, $bone_8_id, $bone_8_weight, $uv_3_u, $uv_3_v);" ;
2480
+ var query = @"insert into vertices ( mesh, part, vertex_id, position_x, position_y, position_z, normal_x, normal_y, normal_z, binormal_x, binormal_y, binormal_z, tangent_x, tangent_y, tangent_z, color_r, color_g, color_b, color_a, color2_r, color2_g, color2_b, color2_a, uv_1_u, uv_1_v, uv_2_u, uv_2_v, bone_1_id, bone_1_weight, bone_2_id, bone_2_weight, bone_3_id, bone_3_weight, bone_4_id, bone_4_weight, bone_5_id, bone_5_weight, bone_6_id, bone_6_weight, bone_7_id, bone_7_weight, bone_8_id, bone_8_weight, uv_3_u, uv_3_v, flow_u, flow_v )
2481
+ values ($mesh, $part, $vertex_id, $position_x, $position_y, $position_z, $normal_x, $normal_y, $normal_z, $binormal_x, $binormal_y, $binormal_z, $tangent_x, $tangent_y, $tangent_z, $color_r, $color_g, $color_b, $color_a, $color2_r, $color2_g, $color2_b, $color2_a, $uv_1_u, $uv_1_v, $uv_2_u, $uv_2_v, $bone_1_id, $bone_1_weight, $bone_2_id, $bone_2_weight, $bone_3_id, $bone_3_weight, $bone_4_id, $bone_4_weight, $bone_5_id, $bone_5_weight, $bone_6_id, $bone_6_weight, $bone_7_id, $bone_7_weight, $bone_8_id, $bone_8_weight, $uv_3_u, $uv_3_v, $flow_u, $flow_v );" ;
2394
2482
using ( var cmd = new SQLiteCommand ( query , db ) )
2395
2483
{
2396
2484
cmd . Parameters . AddWithValue ( "part" , partIdx ) ;
@@ -2454,6 +2542,11 @@ private static void WriteVertex(TTVertex v, SQLiteConnection db, int meshIdx, in
2454
2542
cmd . Parameters . AddWithValue ( "bone_8_id" , v . BoneIds [ 7 ] ) ;
2455
2543
cmd . Parameters . AddWithValue ( "bone_8_weight" , v . Weights [ 7 ] / 255f ) ;
2456
2544
2545
+
2546
+ var flow = v . GetTangentSpaceFlow ( ) ;
2547
+ cmd . Parameters . AddWithValue ( "flow_u" , flow . X ) ;
2548
+ cmd . Parameters . AddWithValue ( "flow_v" , flow . Y ) ;
2549
+
2457
2550
cmd . ExecuteScalar ( ) ;
2458
2551
}
2459
2552
}
@@ -2545,7 +2638,7 @@ public void SetFullModelDBMetaData(string filePath, string fullModelName)
2545
2638
/// </summary>
2546
2639
/// <param name="rawMdl"></param>
2547
2640
/// <returns></returns>
2548
- public static TTModel FromRaw ( XivMdl rawMdl , Action < bool , string > loggingFunction = null )
2641
+ public static async Task < TTModel > FromRaw ( XivMdl rawMdl , Action < bool , string > loggingFunction = null )
2549
2642
{
2550
2643
if ( rawMdl == null )
2551
2644
{
@@ -2578,6 +2671,7 @@ public static TTModel FromRaw(XivMdl rawMdl, Action<bool, string> loggingFunctio
2578
2671
2579
2672
ttModel . UVState = UVAddressingSpace . SE_Space ;
2580
2673
2674
+ await ModelModifiers . CalculateTangents ( ttModel ) ;
2581
2675
return ttModel ;
2582
2676
}
2583
2677
0 commit comments