@@ -1423,25 +1423,158 @@ public static void CalculateTangents(TTModel model, Action<bool, string> logging
1423
1423
continue ;
1424
1424
}
1425
1425
1426
+
1427
+ // Compile lists of connected vertices.
1428
+ Dictionary < int , HashSet < int > > connectedVertices = new Dictionary < int , HashSet < int > > ( ) ;
1429
+ for ( int i = 0 ; i < p . TriangleIndices . Count ; i += 3 )
1430
+ {
1431
+ var t0 = p . TriangleIndices [ i ] ;
1432
+ var t1 = p . TriangleIndices [ i + 1 ] ;
1433
+ var t2 = p . TriangleIndices [ i + 2 ] ;
1434
+
1435
+ if ( ! connectedVertices . ContainsKey ( t0 ) )
1436
+ {
1437
+ connectedVertices . Add ( t0 , new HashSet < int > ( ) ) ;
1438
+ }
1439
+ if ( ! connectedVertices . ContainsKey ( t1 ) )
1440
+ {
1441
+ connectedVertices . Add ( t1 , new HashSet < int > ( ) ) ;
1442
+ }
1443
+ if ( ! connectedVertices . ContainsKey ( t2 ) )
1444
+ {
1445
+ connectedVertices . Add ( t2 , new HashSet < int > ( ) ) ;
1446
+ }
1447
+
1448
+ connectedVertices [ t0 ] . Add ( t1 ) ;
1449
+ connectedVertices [ t0 ] . Add ( t2 ) ;
1450
+ connectedVertices [ t1 ] . Add ( t0 ) ;
1451
+ connectedVertices [ t1 ] . Add ( t2 ) ;
1452
+ connectedVertices [ t2 ] . Add ( t0 ) ;
1453
+ connectedVertices [ t2 ] . Add ( t1 ) ;
1454
+ }
1455
+
1456
+
1457
+ // Compute the welded vertices list and the translation table.
1458
+ List < TTVertex > tempVertices = new List < TTVertex > ( ) ;
1459
+
1460
+ // Original Vertex ID => Temp Vertex ID
1461
+ Dictionary < int , int > vertTranslation = new Dictionary < int , int > ( ) ;
1462
+
1463
+ // Welded Groups
1464
+ Dictionary < int , List < int > > weldedVerts = new Dictionary < int , List < int > > ( ) ;
1465
+
1466
+
1467
+ for ( int oIdx = 0 ; oIdx < p . Vertices . Count ; oIdx ++ )
1468
+ {
1469
+ var oVertex = p . Vertices [ oIdx ] ;
1470
+ var idx = - 1 ;
1471
+ for ( int nIdx = 0 ; nIdx < tempVertices . Count ; nIdx ++ ) {
1472
+ var nVertex = tempVertices [ nIdx ] ;
1473
+
1474
+
1475
+ // The only things that matter in tangent calculation are the
1476
+ // UV1, Pos, Normal
1477
+ // So if the point was only split due to a UV2 or vColor difference, we want to re-weld them (unless they are a mirror-seam).
1478
+ if ( nVertex . Position == oVertex . Position
1479
+ && nVertex . UV1 == oVertex . UV1
1480
+ && nVertex . Normal == oVertex . Normal
1481
+ && ( nVertex . UV2 != oVertex . UV2
1482
+ || nVertex . VertexColor != oVertex . VertexColor ) )
1483
+ {
1484
+
1485
+ // Calculate the set of already connected vertices.
1486
+ var alreadyMergedVerts = weldedVerts [ nIdx ] ;
1487
+ HashSet < int > alreadyConnectedOldVerts = new HashSet < int > ( ) ;
1488
+ foreach ( var amIdx in alreadyMergedVerts )
1489
+ {
1490
+ foreach ( var cv in connectedVertices [ amIdx ] )
1491
+ {
1492
+ alreadyConnectedOldVerts . Add ( cv ) ;
1493
+ }
1494
+ }
1495
+
1496
+ // Get my connected vertices.
1497
+ var myConnectedVerts = connectedVertices [ oIdx ] ;
1498
+
1499
+ // If this vertex is a mirror point along a UV seam we can't merge them.
1500
+ // Mirror-point check involves looking at the connected vertices of the two
1501
+ // points to be welded, and investigating if any point has an identical UV, but differing position.
1502
+
1503
+ // Note - Under certain circumstances where you have n-poles in the model at the same point where you have
1504
+ // a mirror seam and a UV2 or VColor mirror seam, it's possible this could still fail depending on the exact order
1505
+ // of the indices/vertices, however, this case should be exceedingly rare, and easily fixable from a modeling standpoint.
1506
+
1507
+ // Further addendum - Should Tangents be calculated with the entire mesh group welded together, as that is most likely
1508
+ // how SE Handles it?
1509
+
1510
+ bool isMirror = false ;
1511
+ foreach ( var weldedConnection in alreadyConnectedOldVerts )
1512
+ {
1513
+ var wcVert = p . Vertices [ weldedConnection ] ;
1514
+ foreach ( var newConnection in myConnectedVerts )
1515
+ {
1516
+ var ncVert = p . Vertices [ newConnection ] ;
1517
+
1518
+ if ( ncVert . UV1 == wcVert . UV1 &&
1519
+ ncVert . Position != wcVert . Position )
1520
+ {
1521
+ isMirror = true ;
1522
+ break ;
1523
+ }
1524
+ }
1525
+ if ( isMirror )
1526
+ {
1527
+ break ;
1528
+ }
1529
+ }
1530
+
1531
+ if ( ! isMirror )
1532
+ {
1533
+ idx = nIdx ;
1534
+ break ;
1535
+ }
1536
+ }
1537
+ }
1538
+
1539
+ if ( idx == - 1 )
1540
+ {
1541
+ tempVertices . Add ( oVertex ) ;
1542
+ idx = tempVertices . Count - 1 ;
1543
+ weldedVerts . Add ( idx , new List < int > ( ) ) ;
1544
+ }
1545
+
1546
+ weldedVerts [ idx ] . Add ( oIdx ) ;
1547
+ vertTranslation . Add ( oIdx , idx ) ;
1548
+ }
1549
+
1550
+ // Compute the new triangle indices using the translation table
1551
+ List < int > tempIndices = new List < int > ( ) ;
1552
+ for ( int i = 0 ; i < p . TriangleIndices . Count ; i ++ )
1553
+ {
1554
+ var oldVert = p . TriangleIndices [ i ] ;
1555
+ var newVert = vertTranslation [ oldVert ] ;
1556
+ tempIndices . Add ( newVert ) ;
1557
+ }
1558
+
1426
1559
// Interim arrays for calculations
1427
- var tangents = new List < Vector3 > ( p . Vertices . Count ) ;
1428
- tangents . AddRange ( Enumerable . Repeat ( Vector3 . Zero , p . Vertices . Count ) ) ;
1429
- var bitangents = new List < Vector3 > ( p . Vertices . Count ) ;
1430
- bitangents . AddRange ( Enumerable . Repeat ( Vector3 . Zero , p . Vertices . Count ) ) ;
1560
+ var tangents = new List < Vector3 > ( tempVertices . Count ) ;
1561
+ tangents . AddRange ( Enumerable . Repeat ( Vector3 . Zero , tempVertices . Count ) ) ;
1562
+ var bitangents = new List < Vector3 > ( tempVertices . Count ) ;
1563
+ bitangents . AddRange ( Enumerable . Repeat ( Vector3 . Zero , tempVertices . Count ) ) ;
1431
1564
1432
1565
// Calculate Tangent, Bitangent/Binormal and Handedness.
1433
1566
1434
1567
// This loops for each TRI, building up the sum
1435
1568
// tangent/bitangent angles at each VERTEX.
1436
- for ( var a = 0 ; a < p . TriangleIndices . Count ; a += 3 )
1569
+ for ( var a = 0 ; a < tempIndices . Count ; a += 3 )
1437
1570
{
1438
- var vertexId1 = p . TriangleIndices [ a ] ;
1439
- var vertexId2 = p . TriangleIndices [ a + 1 ] ;
1440
- var vertexId3 = p . TriangleIndices [ a + 2 ] ;
1571
+ var vertexId1 = tempIndices [ a ] ;
1572
+ var vertexId2 = tempIndices [ a + 1 ] ;
1573
+ var vertexId3 = tempIndices [ a + 2 ] ;
1441
1574
1442
- var vertex1 = p . Vertices [ vertexId1 ] ;
1443
- var vertex2 = p . Vertices [ vertexId2 ] ;
1444
- var vertex3 = p . Vertices [ vertexId3 ] ;
1575
+ var vertex1 = tempVertices [ vertexId1 ] ;
1576
+ var vertex2 = tempVertices [ vertexId2 ] ;
1577
+ var vertex3 = tempVertices [ vertexId3 ] ;
1445
1578
1446
1579
var deltaX1 = vertex2 . Position . X - vertex1 . Position . X ;
1447
1580
var deltaX2 = vertex3 . Position . X - vertex1 . Position . X ;
@@ -1472,15 +1605,17 @@ public static void CalculateTangents(TTModel model, Action<bool, string> logging
1472
1605
}
1473
1606
1474
1607
// Loop the VERTEXES now to calculate the end tangent/bitangents based on the summed data for each VERTEX
1475
- for ( var vertexId = 0 ; vertexId < p . Vertices . Count ; ++ vertexId )
1608
+ for ( var vertexId = 0 ; vertexId < tempVertices . Count ; ++ vertexId )
1476
1609
{
1477
1610
// Reference: https://marti.works/posts/post-calculating-tangents-for-your-mesh/post/
1478
1611
// We were already doing these calculations to establish handedness, but we weren't actually
1479
1612
// using the other results before. Better to kill the previous computations and use these numbers
1480
1613
// for everything to avoid minor differences causing errors.
1481
1614
1482
1615
//var posIdx = vDict[a];
1483
- var vertex = p . Vertices [ vertexId ] ;
1616
+ var vertex = tempVertices [ vertexId ] ;
1617
+ //List<TTVertex> oVertices = new List<TTVertex>();
1618
+ var oVertices = vertTranslation . Where ( x => x . Value == vertexId ) . Select ( x => x . Key ) . ToList ( ) ;
1484
1619
1485
1620
var n = vertex . Normal ;
1486
1621
@@ -1500,13 +1635,17 @@ public static void CalculateTangents(TTModel model, Action<bool, string> logging
1500
1635
1501
1636
// Apply handedness
1502
1637
binormal *= handedness ;
1638
+ // FFXIV actually tracks BINORMAL handedness, not TANGENT handeness, so we have to reverse this.
1639
+ var boolHandedness = ! ( handedness < 0 ? true : false ) ;
1503
1640
1504
- vertex . Tangent = tangent ;
1505
- vertex . Binormal = binormal ;
1506
- vertex . Handedness = handedness < 0 ? true : false ;
1641
+ foreach ( var vIdx in oVertices )
1642
+ {
1643
+ var v = p . Vertices [ vIdx ] ;
1644
+ v . Tangent = tangent ;
1645
+ v . Binormal = binormal ;
1646
+ v . Handedness = boolHandedness ;
1647
+ }
1507
1648
1508
- // FFXIV actually tracks BINORMAL handedness, not TANGENT handeness, so we have to reverse this.
1509
- vertex . Handedness = ! vertex . Handedness ;
1510
1649
}
1511
1650
}
1512
1651
}
0 commit comments