Sfoglia il codice sorgente

WIP: more improvements on tangents generation; plus aditional tests.

Vicente Penades 6 anni fa
parent
commit
09e132e881

+ 21 - 13
src/SharpGLTF.Toolkit/Geometry/VertexBufferColumns.cs

@@ -346,7 +346,7 @@ namespace SharpGLTF.Geometry
 
         #region utilites
 
-        public static void CalculateSmoothNormals(IEnumerable<(VertexBufferColumns Vertices, IEnumerable<(int A, int B, int C)> Indices)> primitives)
+        public static void CalculateSmoothNormals(IReadOnlyList<(VertexBufferColumns Vertices, IEnumerable<(int A, int B, int C)> Indices)> primitives)
         {
             Guard.NotNull(primitives, nameof(primitives));
 
@@ -406,7 +406,7 @@ namespace SharpGLTF.Geometry
             }
         }
 
-        public static void CalculateTangents(IEnumerable<(VertexBufferColumns Vertices, IEnumerable<(int A, int B, int C)> Indices)> primitives)
+        public static void CalculateTangents(IReadOnlyList<(VertexBufferColumns Vertices, IEnumerable<(int A, int B, int C)> Indices)> primitives)
         {
             // https://gamedev.stackexchange.com/questions/128023/how-does-mikktspace-work-for-calculating-the-tangent-space-during-normal-mapping
             // https://stackoverflow.com/questions/25349350/calculating-per-vertex-tangents-for-glsl
@@ -438,29 +438,37 @@ namespace SharpGLTF.Geometry
                     var p2 = vertices.Positions[i2];
                     var p3 = vertices.Positions[i3];
 
-                    var n1 = vertices.Normals[i1];
-                    var n2 = vertices.Normals[i2];
-                    var n3 = vertices.Normals[i3];
+                    // check for degenerated triangle
+                    if (p1 == p2 || p1 == p3 || p2 == p3) continue;
 
                     var uv1 = vertices.TexCoords0[i1];
                     var uv2 = vertices.TexCoords0[i2];
                     var uv3 = vertices.TexCoords0[i3];
 
+                    // check for degenerated triangle
+                    if (uv1 == uv2 || uv1 == uv3 || uv2 == uv3) continue;
+
+                    var n1 = vertices.Normals[i1];
+                    var n2 = vertices.Normals[i2];
+                    var n3 = vertices.Normals[i3];
+
                     var svec = p2 - p1;
                     var tvec = p3 - p1;
 
                     var stex = uv2 - uv1;
                     var ttex = uv3 - uv1;
 
-                    float s1 = stex.X;
-                    float s2 = ttex.X;
-                    float t1 = stex.Y;
-                    float t2 = ttex.Y;
+                    float sx = stex.X;
+                    float tx = ttex.X;
+                    float sy = stex.Y;
+                    float ty = ttex.Y;
+
+                    var r = 1.0F / ((sx * ty) - (tx * sy));
 
-                    var r = 1.0F / ((s1 * t2) - (s2 * t1));
+                    if (!r._IsFinite()) continue;
 
-                    var sdir = new Vector3((t2 * svec.X) - (t1 * tvec.X), (t2 * svec.Y) - (t1 * tvec.Y), (t2 * svec.Z) - (t1 * tvec.Z) ) * r;
-                    var tdir = new Vector3((s1 * tvec.X) - (s2 * svec.X), (s1 * tvec.Y) - (s2 * svec.Y), (s1 * tvec.Z) - (s2 * svec.Z) ) * r;
+                    var sdir = new Vector3((ty * svec.X) - (sy * tvec.X), (ty * svec.Y) - (sy * tvec.Y), (ty * svec.Z) - (sy * tvec.Z) ) * r;
+                    var tdir = new Vector3((sx * tvec.X) - (tx * svec.X), (sx * tvec.Y) - (tx * svec.Y), (sx * tvec.Z) - (tx * svec.Z) ) * r;
 
                     addTangent(tangentsMap, (p1, n1, uv1), (sdir, tdir));
                     addTangent(tangentsMap, (p2, n2, uv2), (sdir, tdir));
@@ -495,7 +503,7 @@ namespace SharpGLTF.Geometry
 
                     if (tangentsMap.TryGetValue((p, n, t), out (Vector3 u, Vector3 v) tangents))
                     {
-                        var handedness = Vector3.Dot(Vector3.Cross(n, tangents.u), tangents.v) < 0 ? -1.0f : 1.0f;
+                        var handedness = Vector3.Dot(Vector3.Cross(tangents.u, n), tangents.v) < 0 ? -1.0f : 1.0f;
 
                         vertices.Tangents[i] = new Vector4(tangents.u, handedness);
                     }

+ 10 - 0
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -179,6 +179,16 @@ namespace SharpGLTF.Schema2
             return m;
         }
 
+        public static Materials.MaterialBuilder ToMaterialBuilder(this Material srcMaterial)
+        {
+            if (srcMaterial == null) return Materials.MaterialBuilder.CreateDefault();
+            var dstMaterial = new Materials.MaterialBuilder(srcMaterial.Name);
+
+            srcMaterial.CopyTo(dstMaterial);
+
+            return dstMaterial;
+        }
+
         #endregion
 
         #region transfer API

+ 28 - 0
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -704,6 +704,34 @@ namespace SharpGLTF.Schema2
             return dstMesh;
         }
 
+        public static MeshBuilder<Materials.MaterialBuilder, TvG, TvM, TvS> ToMeshBuilder<TMaterial, TvG, TvM, TvS>(this IEnumerable<(VertexBuilder<TvG, TvM, TvS> A, VertexBuilder<TvG, TvM, TvS> B, VertexBuilder<TvG, TvM, TvS> C, TMaterial Material)> triangles, Func<TMaterial, Materials.MaterialBuilder> materialFunc)
+            where TvG : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
+        {
+            var mats = new Dictionary<TMaterial, Materials.MaterialBuilder>();
+
+            Materials.MaterialBuilder useMaterial(TMaterial srcMat)
+            {
+                if (mats.TryGetValue(srcMat, out Materials.MaterialBuilder dstMat)) return dstMat;
+
+                mats[srcMat] = dstMat = materialFunc(srcMat);
+
+                return dstMat;
+            }
+
+            var m = new MeshBuilder<Materials.MaterialBuilder, TvG, TvM, TvS>();
+
+            foreach (var tri in triangles)
+            {
+                var prim = m.UsePrimitive(useMaterial(tri.Material));
+
+                prim.AddTriangle(tri.A, tri.B, tri.C);
+            }
+
+            return m;
+        }
+
         private static void AddPrimitiveGeometry(this IPrimitiveBuilder dstPrim, MeshPrimitive srcPrim)
         {
             Guard.NotNull(dstPrim, nameof(dstPrim));

+ 15 - 0
tests/SharpGLTF.Tests/Geometry/MeshBuilderTests.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Numerics;
 
@@ -262,5 +263,19 @@ namespace SharpGLTF.Geometry
 
             CollectionAssert.AreEqual(new float[] { 1, 2, 3 }, batchId);
         }
+
+        [Test]
+        public void GenerateTangents()
+        {
+            var vertices = new VertexBufferColumns();
+            vertices.Positions = new[] { Vector3.Zero, Vector3.UnitX, Vector3.UnitY };
+            vertices.Normals = new[] { Vector3.UnitZ, Vector3.UnitZ, Vector3.UnitZ };
+            vertices.TexCoords0 = new[] { Vector2.Zero, Vector2.UnitX, Vector2.UnitY };
+
+            var indices = new[] { (0,1,2) } as IEnumerable<(int,int,int)>;
+
+            VertexBufferColumns.CalculateTangents(new[] { (vertices, indices) });
+
+        }
     }
 }

+ 14 - 5
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSpecialModelsTest.cs

@@ -96,18 +96,27 @@ namespace SharpGLTF.Schema2.LoadAndSave
             Assert.NotNull(model);
         }
 
-        [Test]
-        public void LoadGeneratedTangetsTest()
+        // these models show normal mapping but lack tangents, which are expected to be
+        // generated at runtime; These tests generate the tangents and check them against the baseline.
+        [TestCase("NormalTangentTest.glb")]
+        [TestCase("NormalTangentMirrorTest.glb")]
+        public void LoadGeneratedTangetsTest(string fileName)
         {
             TestContext.CurrentContext.AttachShowDirLink();
 
-            var path = TestFiles.GetSampleModelsPaths().FirstOrDefault(item => item.EndsWith("NormalTangentTest.glb"));
+            var path = TestFiles.GetSampleModelsPaths().FirstOrDefault(item => item.EndsWith(fileName));
 
             var model = ModelRoot.Load(path);
 
-            var tris = model.DefaultScene
+            var mesh = model.DefaultScene
                 .EvaluateTriangles<Geometry.VertexTypes.VertexPositionNormalTangent, Geometry.VertexTypes.VertexTexture1>()
-                .ToArray();
+                .ToMeshBuilder( m => m.ToMaterialBuilder() );            
+
+            var editableScene = new Scenes.SceneBuilder();
+            editableScene.AddMesh(mesh, System.Numerics.Matrix4x4.Identity);
+
+            model.AttachToCurrentTest("original.glb");
+            editableScene.ToSchema2().AttachToCurrentTest("WithTangents.glb");
         }
     }
 }