Skip to content

Commit ec0176a

Browse files
pandaGaumeDrigax
andauthored
Update Tangent W calculation (#938)
* Update Tangent W calculation W Tangent calculation was impacted by floating point rounding error and generate inverted vector randomly * Aggregate calculation into MathUtilities function * set default parameter value Co-authored-by: Nicholas Barlow <whoisdrigax@gmail.com>
1 parent 8c4cb32 commit ec0176a

File tree

3 files changed

+82
-42
lines changed

3 files changed

+82
-42
lines changed

3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using Newtonsoft.Json.Linq;
88
using System.Globalization;
9+
using Utilities;
910

1011
namespace Max2Babylon
1112
{
@@ -1097,12 +1098,16 @@ int CreateGlobalVertex(IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh,
10971098
// tangent
10981099
if (exportParameters.exportTangents)
10991100
{
1100-
int indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1);
1101-
IPoint3 normal = vertex.Normal.Normalize;
1102-
IPoint3 tangent = mesh.GetTangent(indexTangentBinormal, 1).Normalize;
1103-
IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize;
1104-
int w = GetW(normal, tangent, bitangent);
1105-
vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w };
1101+
int mapChannel = 1; // Texture Coordinates
1102+
if (mesh.GetNumberOfTangents(mapChannel) != 0)
1103+
{
1104+
int indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, mapChannel);
1105+
IPoint3 normal = vertex.Normal.Normalize;
1106+
IPoint3 tangent = mesh.GetTangent(indexTangentBinormal, mapChannel).Normalize;
1107+
IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, mapChannel).Normalize;
1108+
float w = GetW(normal, tangent, bitangent);
1109+
vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w };
1110+
}
11061111
}
11071112

11081113
if (hasUV)
@@ -1336,33 +1341,45 @@ public void GenerateCoordinatesAnimations(IIGameNode meshNode, List<BabylonAnima
13361341
}
13371342

13381343
/// <summary>
1339-
/// get the w of the tangent
1344+
/// get the w of the UV tangent
13401345
/// </summary>
13411346
/// <param name="normal"></param>
13421347
/// <param name="tangent"></param>
13431348
/// <param name="bitangent"></param>
13441349
/// <returns>
1345-
/// -1 when the normal is not flipped
1346-
/// 1 when the normal is flipped
1350+
/// 1 when the bitangent is nearly 0,0,0 or is not flipped
1351+
/// -1 when the bitangent is flipped (oposite direction of the cross product of normal ^ tangent)
13471352
/// </returns>
1348-
private int GetW(IPoint3 normal, IPoint3 tangent, IPoint3 bitangent)
1353+
private float GetW(IPoint3 normal, IPoint3 tangent, IPoint3 bitangent)
13491354
{
1350-
//Cross product bitangent = w * normal ^ tangent
1351-
float x = normal.Y * tangent.Z - normal.Z * tangent.Y;
1352-
float y = normal.Z * tangent.X - normal.X * tangent.Z;
1353-
float z = normal.X * tangent.Y - normal.Y * tangent.X;
1355+
float btx = MathUtilities.RoundToIfAlmostEqualTo(bitangent.X, 0, Tools.Epsilon);
1356+
float bty = MathUtilities.RoundToIfAlmostEqualTo(bitangent.Y, 0, Tools.Epsilon);
1357+
float btz = MathUtilities.RoundToIfAlmostEqualTo(bitangent.Z, 0, Tools.Epsilon);
13541358

1355-
int w = Math.Sign(bitangent.X * x);
1356-
if (w == 0)
1359+
if( btx == 0 && bty == 0 && btz == 0)
13571360
{
1358-
w = Math.Sign(bitangent.Y * y);
1361+
return 1;
13591362
}
1360-
if (w == 0)
1361-
{
1362-
w = Math.Sign(bitangent.Z * z);
1363-
}
1364-
1365-
return w;
1363+
1364+
float nx = MathUtilities.RoundToIfAlmostEqualTo(normal.X, 0, Tools.Epsilon);
1365+
float ny = MathUtilities.RoundToIfAlmostEqualTo(normal.Y, 0, Tools.Epsilon);
1366+
float nz = MathUtilities.RoundToIfAlmostEqualTo(normal.Z, 0, Tools.Epsilon);
1367+
1368+
float tx = MathUtilities.RoundToIfAlmostEqualTo(tangent.X, 0, Tools.Epsilon);
1369+
float ty = MathUtilities.RoundToIfAlmostEqualTo(tangent.Y, 0, Tools.Epsilon);
1370+
float tz = MathUtilities.RoundToIfAlmostEqualTo(tangent.Z, 0, Tools.Epsilon);
1371+
1372+
// Cross product bitangent = w * normal ^ tangent
1373+
1374+
// theorical bittangent
1375+
MathUtilities.CrossProduct(nx, ny, nz, tx, ty, tz, out float x, out float y, out float z);
1376+
1377+
// Speaking in broadest terms, if the dot product of two non-zero vectors is positive,
1378+
// then the two vectors point in the same general direction, meaning less than 90 degrees.
1379+
// If the dot product is negative, then the two vectors point in opposite directions,
1380+
// or above 90 and less than or equal to 180 degrees.
1381+
var dot = MathUtilities.DotProduct(btx, bty,btz, x,y,z);
1382+
return dot < 0 ? -1 : 1;
13661383
}
13671384

13681385
}

3ds Max/Max2Babylon/Exporter/GlobalVertex.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,21 @@ public GlobalVertex(GlobalVertex other)
3737

3838
public override int GetHashCode()
3939
{
40-
/*
41-
return string.Format("{0}-{1}-{2}-{3}-{4}-{5}-{6}-{7}-{8}-{9}",
42-
BaseIndex,
43-
CurrentIndex,
44-
Position != null ? Position.ToArray() : null,
45-
Normal != null ? Normal.ToArray() : null,
46-
Tangent,
47-
UV != null ? UV.ToArray() : null,
48-
UV2 != null ? UV.ToArray() : null,
49-
BonesIndices,
50-
Weights != null ? Weights.ToArray() : null,
51-
BonesIndicesExtra,
52-
WeightsExtra != null ? WeightsExtra.ToArray() : null,
53-
Color).GetHashCode();
54-
*/
40+
41+
//return string.Format("{0}-{1}-{2}-{3}-{4}-{5}-{6}-{7}-{8}-{9}",
42+
// BaseIndex,
43+
// CurrentIndex,
44+
// Position != null ? Position.ToArray() : null,
45+
// Normal != null ? Normal.ToArray() : null,
46+
// Tangent,
47+
// UV != null ? UV.ToArray() : null,
48+
// UV2 != null ? UV.ToArray() : null,
49+
// BonesIndices,
50+
// Weights != null ? Weights.ToArray() : null,
51+
// BonesIndicesExtra,
52+
// WeightsExtra != null ? WeightsExtra.ToArray() : null,
53+
// Color).GetHashCode();
54+
5555
return base.GetHashCode();
5656
}
5757

SharedProjects/Utilities/MathUtilities.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
42
using BabylonExport.Entities;
53

64
namespace Utilities
75
{
86
static class MathUtilities
97
{
8+
public const float Epsilon = 1E-7f;
109
public static float GetLerpFactor(float from, float to, float value)
1110
{
1211
return (value - from) / (to - from);
@@ -53,11 +52,35 @@ public static int RoundToInt(float f)
5352
return Convert.ToInt32(Math.Round(f, MidpointRounding.AwayFromZero));
5453
}
5554

56-
public static bool IsAlmostEqualTo(float first, float second, float epsilon)
55+
public static bool IsAlmostEqualTo(float first, float second, float epsilon = Epsilon)
5756
{
58-
return Math.Abs(first - second) < epsilon;
57+
return Math.Abs(first - second) <= epsilon;
5958
}
6059

60+
/// <summary>
61+
/// This is used to round floating
62+
/// </summary>
63+
/// <param name="a"></param>
64+
/// <param name="b"></param>
65+
/// <param name="epsilon"></param>
66+
/// <returns></returns>
67+
public static float RoundToIfAlmostEqualTo(float a, float b, float epsilon = Epsilon)
68+
{
69+
return Math.Abs(a - b) <= epsilon ? b : a ;
70+
}
71+
72+
public static float DotProduct(float x1, float y1, float z1, float x2, float y2, float z2)
73+
{
74+
return x1 * x2 + y1 * y2 + z1 * z2;
75+
}
76+
public static void CrossProduct(float x1, float y1, float z1, float x2, float y2, float z2, out float x3, out float y3, out float z3)
77+
{
78+
x3 = y1 * z2 - z1 * y2;
79+
y3 = z1 * x2 - x1 * z2;
80+
z3 = x1 * y2 - y1 * x2;
81+
}
82+
83+
6184
/**
6285
* Computes a texture transform matrix with a pre-transformation
6386
*/

0 commit comments

Comments
 (0)