Skip to content

Commit db44bb2

Browse files
committed
Calculate binormal/tangents at mesh level.
1 parent 659840a commit db44bb2

File tree

1 file changed

+98
-158
lines changed

1 file changed

+98
-158
lines changed

xivModdingFramework/Models/Helpers/ModelModifiers.cs

Lines changed: 98 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1749,10 +1749,7 @@ public static void CalculateTangents(TTModel model, Action<bool, string> logging
17491749

17501750
foreach (var m in model.MeshGroups)
17511751
{
1752-
foreach (var p in m.Parts)
1753-
{
1754-
CalculateTangentsForPart(p);
1755-
}
1752+
CalculateTangentsForMesh(m);
17561753
}
17571754

17581755
if(resetShapes.Count > 0)
@@ -1802,179 +1799,113 @@ private static bool AnyMissingTangentData(TTModel model)
18021799
return false;
18031800
}
18041801

1805-
private static void CalculateTangentsForPart(TTMeshPart p)
1802+
private static (List<int> Indices, Dictionary<int, List<TTVertex>> VertexTable) GetWeldedMeshData(TTMeshGroup m)
18061803
{
1804+
List<int> indices = new List<int>(m.Parts.Sum(x => x.TriangleIndices.Count));
1805+
List<TTVertex> vertices = new List<TTVertex>(m.Parts.Sum(x => x.Vertices.Count));
18071806

1808-
// Make sure there's actually data to use...
1809-
if (p.Vertices.Count == 0 || p.TriangleIndices.Count == 0)
1810-
{
1811-
return;
1812-
}
1813-
1814-
if(p.Vertices.Any(x => x.Binormal != Vector3.Zero))
1815-
{
1816-
// Faster function.
1817-
//CalculateTangentsFromBinormalsForPart(p);
1818-
//return;
1819-
}
1820-
1821-
// Compile lists of connected vertices.
1822-
Dictionary<int, HashSet<int>> connectedVertices = new Dictionary<int, HashSet<int>>();
1823-
for (int i = 0; i < p.TriangleIndices.Count; i += 3)
1807+
// Calculate the index list and combine vertex arrays.
1808+
var offset = 0;
1809+
foreach(var p in m.Parts)
18241810
{
1825-
var t0 = p.TriangleIndices[i];
1826-
var t1 = p.TriangleIndices[i + 1];
1827-
var t2 = p.TriangleIndices[i + 2];
1828-
1829-
if (!connectedVertices.ContainsKey(t0))
1811+
foreach(var i in p.TriangleIndices)
18301812
{
1831-
connectedVertices.Add(t0, new HashSet<int>());
1813+
indices.Add(i + offset);
18321814
}
1833-
if (!connectedVertices.ContainsKey(t1))
1834-
{
1835-
connectedVertices.Add(t1, new HashSet<int>());
1836-
}
1837-
if (!connectedVertices.ContainsKey(t2))
1838-
{
1839-
connectedVertices.Add(t2, new HashSet<int>());
1840-
}
1841-
1842-
connectedVertices[t0].Add(t1);
1843-
connectedVertices[t0].Add(t2);
1844-
connectedVertices[t1].Add(t0);
1845-
connectedVertices[t1].Add(t2);
1846-
connectedVertices[t2].Add(t0);
1847-
connectedVertices[t2].Add(t1);
1815+
offset += p.Vertices.Count;
1816+
vertices.AddRange(p.Vertices);
18481817
}
18491818

1819+
Dictionary<int, int> oldToNewVertex = new Dictionary<int, int>();
1820+
List<TTVertex> newVertices = new List<TTVertex>(vertices.Count);
1821+
var vertexTable = new Dictionary<int, List<TTVertex>>();
18501822

1851-
// Compute the welded vertices list and the translation table.
1852-
List<TTVertex> tempVertices = new List<TTVertex>();
1853-
1854-
// Original Vertex ID => Temp Vertex ID
1855-
Dictionary<int, int> vertTranslation = new Dictionary<int, int>();
1856-
1857-
// Welded Groups
1858-
Dictionary<int, List<int>> weldedVerts = new Dictionary<int, List<int>>();
1859-
1860-
1861-
for (int oIdx = 0; oIdx < p.Vertices.Count; oIdx++)
1823+
// Perform vertex welding.
1824+
for (int i = 0; i < vertices.Count; i++)
18621825
{
1863-
var oVertex = (TTVertex) p.Vertices[oIdx].Clone();
1864-
var idx = -1;
1865-
for (int nIdx = 0; nIdx < tempVertices.Count; nIdx++)
1826+
var ov = vertices[i];
1827+
var found = false;
1828+
for(var ni = 0; ni < newVertices.Count; ni++)
18661829
{
1867-
var nVertex = tempVertices[nIdx];
1868-
1830+
var nv = newVertices[ni];
18691831

1870-
// The only things that matter in tangent calculation are the
1871-
// UV1, Pos, and Normal. Any other differences don't matter and we can reweld them.
1872-
if (nVertex.Position == oVertex.Position
1873-
&& nVertex.UV1 == oVertex.UV1
1874-
&& nVertex.Normal == oVertex.Normal)
1832+
if(nv.UV1 == ov.UV1
1833+
&& nv.Position == ov.Position
1834+
&& nv.Normal == ov.Normal)
18751835
{
1876-
1877-
// Calculate the set of already connected vertices.
1878-
var alreadyMergedVerts = weldedVerts[nIdx];
1879-
HashSet<int> alreadyConnectedOldVerts = new HashSet<int>();
1880-
foreach (var amIdx in alreadyMergedVerts)
1881-
{
1882-
foreach (var cv in connectedVertices[amIdx])
1883-
{
1884-
alreadyConnectedOldVerts.Add(cv);
1885-
}
1886-
}
1887-
1888-
// Get my connected vertices.
1889-
var myConnectedVerts = connectedVertices[oIdx];
1890-
1891-
// If this vertex is a mirror point along a UV seam we can't merge them.
1892-
// Mirror-point check involves looking at the connected vertices of the two
1893-
// points to be welded, and investigating if any point has an identical UV, but differing position.
1894-
1895-
// Note - Under certain circumstances where you have n-poles in the model at the same point where you have
1896-
// a mirror seam and a UV2 or VColor mirror seam, it's possible this could still fail depending on the exact order
1897-
// of the indices/vertices, however, this case should be exceedingly rare, and easily fixable from a modeling standpoint.
1898-
1899-
// Further addendum - Should Tangents be calculated with the entire mesh group welded together, as that is most likely
1900-
// how SE Handles it?
1901-
1902-
bool isMirror = false;
1903-
foreach (var weldedConnection in alreadyConnectedOldVerts)
1904-
{
1905-
var wcVert = p.Vertices[weldedConnection];
1906-
foreach (var newConnection in myConnectedVerts)
1907-
{
1908-
var ncVert = p.Vertices[newConnection];
1909-
1910-
if (ncVert.UV1 == wcVert.UV1 &&
1911-
ncVert.Position != wcVert.Position)
1912-
{
1913-
isMirror = true;
1914-
break;
1915-
}
1916-
}
1917-
if (isMirror)
1918-
{
1919-
break;
1920-
}
1921-
}
1922-
1923-
if (!isMirror)
1924-
{
1925-
idx = nIdx;
1926-
break;
1927-
}
1836+
oldToNewVertex.Add(i, ni);
1837+
vertexTable[ni].Add(ov);
1838+
found = true;
1839+
break;
19281840
}
19291841
}
19301842

1931-
if (idx == -1)
1843+
if (!found)
19321844
{
1933-
tempVertices.Add(oVertex);
1934-
idx = tempVertices.Count - 1;
1935-
weldedVerts.Add(idx, new List<int>());
1845+
var ni = newVertices.Count;
1846+
oldToNewVertex.Add(i, ni);
1847+
vertexTable.Add(ni, new List<TTVertex>());
1848+
vertexTable[ni].Add(ov);
1849+
newVertices.Add(ov);
19361850
}
1937-
1938-
weldedVerts[idx].Add(oIdx);
1939-
vertTranslation.Add(oIdx, idx);
19401851
}
19411852

1942-
// Compute the new triangle indices using the translation table
1943-
List<int> tempIndices = new List<int>();
1944-
for (int i = 0; i < p.TriangleIndices.Count; i++)
1853+
// Create translated index table.
1854+
var finalIndices = new List<int>(indices.Count);
1855+
for (int i = 0; i < indices.Count; i++)
19451856
{
1946-
var oldVert = p.TriangleIndices[i];
1947-
var newVert = vertTranslation[oldVert];
1948-
tempIndices.Add(newVert);
1857+
var ov = indices[i];
1858+
var nv = oldToNewVertex[ov];
1859+
finalIndices.Add(nv);
19491860
}
19501861

1951-
// Interim arrays for calculations
1952-
var tangents = new List<Vector3>(tempVertices.Count);
1953-
tangents.AddRange(Enumerable.Repeat(Vector3.Zero, tempVertices.Count));
1954-
var bitangents = new List<Vector3>(tempVertices.Count);
1955-
bitangents.AddRange(Enumerable.Repeat(Vector3.Zero, tempVertices.Count));
1862+
return (finalIndices, vertexTable);
1863+
}
19561864

1957-
foreach(var v in tempVertices)
1865+
private static void CalculateTangentsForMesh(TTMeshGroup m)
1866+
{
1867+
1868+
// Make sure there's actually data to use...
1869+
if (m.VertexCount == 0 || m.IndexCount == 0)
19581870
{
1871+
return;
1872+
}
19591873

1960-
// We need to use the SE style addressing mode here.
1961-
v.UV1.Y = (-1 * v.UV1.Y) + 1;
19621874

1875+
if (m.Parts.Any(p => p.Vertices.Any(x => x.Binormal != Vector3.Zero)))
1876+
{
1877+
// Faster function.
1878+
foreach (var p in m.Parts)
1879+
{
1880+
CalculateTangentsFromBinormalsForPart(p);
1881+
}
1882+
return;
19631883
}
19641884

1885+
var weldData = GetWeldedMeshData(m);
1886+
1887+
var indices = weldData.Indices;
1888+
var vertices = weldData.VertexTable;
1889+
1890+
// Interim arrays for calculations
1891+
var tangents = new List<Vector3>(vertices.Count);
1892+
tangents.AddRange(Enumerable.Repeat(Vector3.Zero, vertices.Count));
1893+
var bitangents = new List<Vector3>(vertices.Count);
1894+
bitangents.AddRange(Enumerable.Repeat(Vector3.Zero, vertices.Count));
1895+
19651896
// Calculate Tangent, Bitangent/Binormal and Handedness.
19661897

19671898
// This loops for each TRI, building up the sum
19681899
// tangent/bitangent angles at each VERTEX.
1969-
for (var a = 0; a < tempIndices.Count; a += 3)
1900+
for (var a = 0; a < indices.Count; a += 3)
19701901
{
1971-
var vertexId1 = tempIndices[a];
1972-
var vertexId2 = tempIndices[a + 1];
1973-
var vertexId3 = tempIndices[a + 2];
1902+
var vertexId1 = indices[a];
1903+
var vertexId2 = indices[a + 1];
1904+
var vertexId3 = indices[a + 2];
19741905

1975-
var vertex1 = tempVertices[vertexId1];
1976-
var vertex2 = tempVertices[vertexId2];
1977-
var vertex3 = tempVertices[vertexId3];
1906+
var vertex1 = vertices[vertexId1][0];
1907+
var vertex2 = vertices[vertexId2][0];
1908+
var vertex3 = vertices[vertexId3][0];
19781909

19791910
var deltaX1 = vertex2.Position.X - vertex1.Position.X;
19801911
var deltaX2 = vertex3.Position.X - vertex1.Position.X;
@@ -1985,11 +1916,20 @@ private static void CalculateTangentsForPart(TTMeshPart p)
19851916
var deltaZ1 = vertex2.Position.Z - vertex1.Position.Z;
19861917
var deltaZ2 = vertex3.Position.Z - vertex1.Position.Z;
19871918

1988-
var deltaU1 = vertex2.UV1.X - vertex1.UV1.X;
1989-
var deltaU2 = vertex3.UV1.X - vertex1.UV1.X;
1919+
var v1uv = vertex1.UV1;
1920+
var v2uv = vertex2.UV1;
1921+
var v3uv = vertex3.UV1;
1922+
1923+
// Adjust to top-left addressing space.
1924+
v1uv.Y = (v1uv.Y * -1) + 1;
1925+
v2uv.Y = (v2uv.Y * -1) + 1;
1926+
v3uv.Y = (v3uv.Y * -1) + 1;
1927+
1928+
var deltaU1 = v2uv.X - v1uv.X;
1929+
var deltaU2 = v3uv.X - v1uv.X;
19901930

1991-
var deltaV1 = vertex2.UV1.Y - vertex1.UV1.Y;
1992-
var deltaV2 = vertex3.UV1.Y - vertex1.UV1.Y;
1931+
var deltaV1 = v2uv.Y - v1uv.Y;
1932+
var deltaV2 = v3uv.Y - v1uv.Y;
19931933

19941934
var r = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1);
19951935
var sdir = new Vector3((deltaV2 * deltaX1 - deltaV1 * deltaX2) * r, (deltaV2 * deltaY1 - deltaV1 * deltaY2) * r, (deltaV2 * deltaZ1 - deltaV1 * deltaZ2) * r);
@@ -2005,17 +1945,16 @@ private static void CalculateTangentsForPart(TTMeshPart p)
20051945
}
20061946

20071947
// Loop the VERTEXES now to calculate the end tangent/bitangents based on the summed data for each VERTEX
2008-
for (var vertexId = 0; vertexId < tempVertices.Count; ++vertexId)
1948+
for (var vertexId = 0; vertexId < vertices.Count; ++vertexId)
20091949
{
20101950
// Reference: https://marti.works/posts/post-calculating-tangents-for-your-mesh/post/
20111951
// We were already doing these calculations to establish handedness, but we weren't actually
20121952
// using the other results before. Better to kill the previous computations and use these numbers
20131953
// for everything to avoid minor differences causing errors.
20141954

20151955
//var posIdx = vDict[a];
2016-
var vertex = tempVertices[vertexId];
1956+
var vertex = vertices[vertexId][0];
20171957
//List<TTVertex> oVertices = new List<TTVertex>();
2018-
var oVertices = vertTranslation.Where(x => x.Value == vertexId).Select(x => x.Key).ToList();
20191958

20201959
var n = vertex.Normal;
20211960

@@ -2040,18 +1979,19 @@ private static void CalculateTangentsForPart(TTMeshPart p)
20401979

20411980
var boolHandedness = !(bHandedness < 0 ? true : false);
20421981

2043-
foreach (var vIdx in oVertices)
1982+
// Assign results.
1983+
foreach (var v in vertices[vertexId])
20441984
{
2045-
var v = p.Vertices[vIdx];
2046-
v.Tangent = tangent.Normalized();
2047-
v.Binormal = binormal.Normalized();
1985+
v.Tangent = tangent;
1986+
v.Binormal = binormal;
20481987
v.Handedness = boolHandedness;
20491988
}
20501989
}
20511990

2052-
//CalculateTangentsFromBinormalsForPart(p);
2053-
2054-
CopyShapeTangentsForPart(p);
1991+
foreach (var p in m.Parts)
1992+
{
1993+
CopyShapeTangentsForPart(p);
1994+
}
20551995
}
20561996

20571997
private static void CopyShapeTangentsForPart(TTMeshPart p)
@@ -2075,7 +2015,7 @@ private static void CalculateTangentsFromBinormalsForPart(TTMeshPart p)
20752015
{
20762016

20772017
var tangent = Vector3.Cross(v.Normal, v.Binormal);
2078-
//tangent *= (v.Handedness == true ? -1 : 1);
2018+
tangent *= (v.Handedness == true ? -1 : 1);
20792019
v.Tangent = tangent;
20802020
}
20812021
CopyShapeTangentsForPart(p);

0 commit comments

Comments
 (0)