Browse Source

Added texture uv baking when exporting to wavefront

Vicente Penades 3 years ago
parent
commit
f34511e1d6

+ 1 - 0
src/SharpGLTF.Core/Schema2/gltf.Material.cs

@@ -114,6 +114,7 @@ namespace SharpGLTF.Schema2
         ///   - "SpecularGlossiness"
         ///   - "SpecularGlossiness"
         /// </param>
         /// </param>
         /// <returns>A <see cref="MaterialChannel"/> structure. or null if it does not exist</returns>
         /// <returns>A <see cref="MaterialChannel"/> structure. or null if it does not exist</returns>
+        [System.Diagnostics.DebuggerStepThrough]
         public MaterialChannel? FindChannel(string channelKey)
         public MaterialChannel? FindChannel(string channelKey)
         {
         {
             foreach (var ch in Channels)
             foreach (var ch in Channels)

+ 15 - 2
src/SharpGLTF.Toolkit/IO/WavefrontWriter.cs

@@ -231,7 +231,13 @@ namespace SharpGLTF.IO
 
 
         public void AddModel(ModelRoot model)
         public void AddModel(ModelRoot model)
         {
         {
-            foreach (var triangle in Toolkit.EvaluateTriangles<VGEOMETRY, VMATERIAL>(model.DefaultScene))
+            // retrieve a "snapshot" of all the triangles of the scene
+            var triangles = Toolkit.EvaluateTriangles<VGEOMETRY, VMATERIAL>(model.DefaultScene);
+
+            // bake the material transforms into the UV coordinates
+            triangles = EvaluatedTriangle<VGEOMETRY, VMATERIAL, VEMPTY>.TransformTextureCoordsByMaterial(triangles);
+
+            foreach (var triangle in triangles)
             {
             {
                 var dstMaterial = GetMaterialFromTriangle(triangle.Material);
                 var dstMaterial = GetMaterialFromTriangle(triangle.Material);
                 this.AddTriangle(dstMaterial, triangle.A, triangle.B, triangle.C);
                 this.AddTriangle(dstMaterial, triangle.A, triangle.B, triangle.C);
@@ -242,9 +248,16 @@ namespace SharpGLTF.IO
         {
         {
             var options = new Runtime.RuntimeOptions();
             var options = new Runtime.RuntimeOptions();
             options.IsolateMemory = false;
             options.IsolateMemory = false;
+            // options.BakeTextureTransforms = true;
             options.GpuMeshInstancing = Runtime.MeshInstancing.SingleMesh;
             options.GpuMeshInstancing = Runtime.MeshInstancing.SingleMesh;
 
 
-            foreach (var triangle in Toolkit.EvaluateTriangles<VGEOMETRY, VMATERIAL>(model.DefaultScene, options, animation, time))
+            // retrieve a "snapshot" of all the triangles of the scene
+            var triangles = Toolkit.EvaluateTriangles<VGEOMETRY, VMATERIAL>(model.DefaultScene, options, animation, time);
+
+            // bake the material transforms into the UV coordinates
+            triangles = EvaluatedTriangle<VGEOMETRY, VMATERIAL, VEMPTY>.TransformTextureCoordsByMaterial(triangles);
+
+            foreach (var triangle in triangles)
             {
             {
                 var dstMaterial = GetMaterialFromTriangle(triangle.Material);
                 var dstMaterial = GetMaterialFromTriangle(triangle.Material);
                 this.AddTriangle(dstMaterial, triangle.A, triangle.B, triangle.C);
                 this.AddTriangle(dstMaterial, triangle.A, triangle.B, triangle.C);

+ 163 - 0
src/SharpGLTF.Toolkit/Schema2/EvaluatedTriangle.cs

@@ -0,0 +1,163 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+
+using SharpGLTF.Geometry;
+using SharpGLTF.Geometry.VertexTypes;
+
+using MESHXFORM = SharpGLTF.Transforms.IGeometryTransform;
+
+
+namespace SharpGLTF.Schema2
+{
+    public readonly struct EvaluatedTriangle<TvG, TvM, TvS>
+        where TvG : struct, IVertexGeometry
+        where TvM : struct, IVertexMaterial
+        where TvS : struct, IVertexSkinning
+    {
+        #region factory
+
+        public static IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> GetTrianglesFromMesh(Mesh mesh, MESHXFORM xform = null)
+        {
+            if (xform != null && !xform.Visible) mesh = null;
+            if (mesh == null) return Enumerable.Empty<EvaluatedTriangle<TvG, TvM, TvS>>();
+
+            var primitives = _GatherMeshGeometry(mesh);
+
+            return Transforms.InstancingTransform
+                .Evaluate(xform)
+                .SelectMany
+                (
+                    xinst => primitives.SelectMany
+                    (
+                        prim =>
+                        {
+                            var xvertices = xinst != null
+                                ? prim.Vertices.WithTransform(xinst)
+                                : prim.Vertices;
+
+                            return _EvaluateTriangles(prim.Material, xvertices, prim.Triangles);
+                        }
+
+                    )
+                );
+        }
+
+        private static IReadOnlyList<(Material Material, VertexBufferColumns Vertices, IEnumerable<(int, int, int)> Triangles)> _GatherMeshGeometry(Mesh mesh)
+        {
+            var primitives = mesh.Primitives
+                            .Where(prim => prim.GetTriangleIndices().Any())
+                            .Select(prim => (prim.Material, prim.GetVertexColumns(), (IEnumerable<(int, int, int)>)prim.GetTriangleIndices().ToList()))
+                            .ToList();
+
+            bool needsNormals = default(TvG).TryGetNormal(out Vector3 nrm);
+            bool needsTangents = default(TvG).TryGetTangent(out Vector4 tgt);
+
+            if (needsNormals)
+            {
+                var prims = primitives
+                    .Where(p => p.Item2.Normals == null)
+                    .Select(p => (p.Item2, p.Item3))
+                    .ToList();
+
+                if (prims.Any()) VertexBufferColumns.CalculateSmoothNormals(prims);
+            }
+
+            if (needsTangents)
+            {
+                var prims = primitives
+                    .Where(p => p.Item2.Tangents == null && p.Item2.TexCoords0 != null)
+                    .Select(p => (p.Item2, p.Item3))
+                    .ToList();
+
+                if (prims.Any()) VertexBufferColumns.CalculateTangents(prims);
+            }
+
+            return primitives;
+        }
+
+        private static IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> _EvaluateTriangles(Material material, VertexBufferColumns vertices, IEnumerable<(int A, int B, int C)> indices)            
+        {
+            foreach (var (ta, tb, tc) in indices)
+            {
+                var va = vertices.GetVertex<TvG, TvM, TvS>(ta);
+                var vb = vertices.GetVertex<TvG, TvM, TvS>(tb);
+                var vc = vertices.GetVertex<TvG, TvM, TvS>(tc);
+
+                yield return (va, vb, vc, material);
+            }
+        }
+
+        #endregion
+
+        #region constructor
+
+        public static implicit operator EvaluatedTriangle<TvG, TvM, TvS>((VertexBuilder<TvG, TvM, TvS> A, VertexBuilder<TvG, TvM, TvS> B, VertexBuilder<TvG, TvM, TvS> C, Material Material) tri)
+        {
+            return new Schema2.EvaluatedTriangle<TvG, TvM, TvS>(tri.A, tri.B, tri.C, tri.Material);
+        }
+
+        public EvaluatedTriangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c, Material m)
+        {
+            A = a;
+            B = b;
+            C = c;
+            Material = m;
+        }
+
+        #endregion
+
+        #region data
+
+        public readonly VertexBuilder<TvG, TvM, TvS> A;
+        public readonly VertexBuilder<TvG, TvM, TvS> B;
+        public readonly VertexBuilder<TvG, TvM, TvS> C;
+
+        public readonly Material Material;
+
+        #endregion
+
+        #region API
+
+        public static IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> TransformTextureCoordsByMaterial(IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> triangles)
+        {
+            // cache transform for speed up
+            var xformDict = new Dictionary<Material, Matrix3x2>();
+
+            EvaluatedTriangle<TvG, TvM, TvS> _getTransformedTriangle(EvaluatedTriangle<TvG, TvM, TvS> triangle)
+            {
+                if (!xformDict.TryGetValue(triangle.Material, out var xform))
+                {
+                    xform = triangle.Material.GetDiffuseTextureTransform()
+                        ?.Matrix
+                        ?? Matrix3x2.Identity;
+
+                    xformDict[triangle.Material] = xform;
+                }
+
+                if (xform.IsIdentity) return triangle;
+
+                return triangle._TransformTextureBy(xform);
+            }
+
+            return triangles.Select(tri => _getTransformedTriangle(tri));
+        }
+
+        private EvaluatedTriangle<TvG, TvM, TvS> _TransformTextureBy(in Matrix3x2 xform)
+        {
+            var a = this.A;
+            var b = this.B;
+            var c = this.C;
+
+            a.Material.SetTexCoord(0, Vector2.Transform(a.Material.GetTexCoord(0), xform));
+            b.Material.SetTexCoord(0, Vector2.Transform(b.Material.GetTexCoord(0), xform));
+            c.Material.SetTexCoord(0, Vector2.Transform(c.Material.GetTexCoord(0), xform));
+
+            return (a, b, c, Material);
+        }
+
+        #endregion
+    }
+}

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

@@ -547,6 +547,19 @@ namespace SharpGLTF.Schema2
             return null;
             return null;
         }
         }
 
 
+        public static TextureTransform GetDiffuseTextureTransform(this Material material)
+        {
+            if (material == null) return null;
+
+            var channel = material.FindChannel("Diffuse");
+            if (channel.HasValue) return channel.Value.TextureTransform;
+
+            channel = material.FindChannel("BaseColor");
+            if (channel.HasValue) return channel.Value.TextureTransform;
+
+            return null;
+        }
+
         #endregion
         #endregion
     }
     }
 }
 }

+ 13 - 69
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -535,78 +535,12 @@ namespace SharpGLTF.Schema2
             }
             }
         }
         }
 
 
-        public static IEnumerable<(VertexBuilder<TvG, TvM, TvS> A, VertexBuilder<TvG, TvM, TvS> B, VertexBuilder<TvG, TvM, TvS> C, Material Material)> EvaluateTriangles<TvG, TvM, TvS>(this Mesh mesh, MESHXFORM xform = null)
+        public static IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> EvaluateTriangles<TvG, TvM, TvS>(this Mesh mesh, MESHXFORM xform = null)
             where TvG : struct, IVertexGeometry
             where TvG : struct, IVertexGeometry
             where TvM : struct, IVertexMaterial
             where TvM : struct, IVertexMaterial
             where TvS : struct, IVertexSkinning
             where TvS : struct, IVertexSkinning
         {
         {
-            if (xform != null && !xform.Visible) mesh = null;
-            if (mesh == null) return Enumerable.Empty<(VertexBuilder<TvG, TvM, TvS>, VertexBuilder<TvG, TvM, TvS>, VertexBuilder<TvG, TvM, TvS>, Material)>();
-
-            var primitives = _GatherMeshGeometry<TvG>(mesh);
-
-            return Transforms.InstancingTransform.Evaluate(xform)
-                .SelectMany
-                (
-                    xinst => primitives.SelectMany
-                    (
-                        prim =>
-                        {
-                            var xvertices = xinst != null ? prim.Vertices.WithTransform(xinst) : prim.Vertices;
-                            return _EvaluateTriangles<TvG, TvM, TvS>(prim.Material, xvertices, prim.Triangles);
-                        }
-
-                    )
-                );
-        }
-
-        private static IReadOnlyList<(Material Material, VertexBufferColumns Vertices, IEnumerable<(int, int, int)> Triangles)> _GatherMeshGeometry<TvG>(Mesh mesh)
-            where TvG : struct, IVertexGeometry
-        {
-            var primitives = mesh.Primitives
-                            .Where(prim => prim.GetTriangleIndices().Any())
-                            .Select(prim => (prim.Material, prim.GetVertexColumns(), (IEnumerable<(int, int, int)>)prim.GetTriangleIndices().ToList()))
-                            .ToList();
-
-            bool needsNormals = default(TvG).TryGetNormal(out Vector3 nrm);
-            bool needsTangents = default(TvG).TryGetTangent(out Vector4 tgt);
-
-            if (needsNormals)
-            {
-                var prims = primitives
-                    .Where(p => p.Item2.Normals == null)
-                    .Select(p => (p.Item2, p.Item3))
-                    .ToList();
-
-                if (prims.Any()) VertexBufferColumns.CalculateSmoothNormals(prims);
-            }
-
-            if (needsTangents)
-            {
-                var prims = primitives
-                    .Where(p => p.Item2.Tangents == null && p.Item2.TexCoords0 != null)
-                    .Select(p => (p.Item2, p.Item3))
-                    .ToList();
-
-                if (prims.Any()) VertexBufferColumns.CalculateTangents(prims);
-            }
-
-            return primitives;
-        }
-
-        private static IEnumerable<(VertexBuilder<TvG, TvM, TvS> A, VertexBuilder<TvG, TvM, TvS> B, VertexBuilder<TvG, TvM, TvS> C, Material Material)> _EvaluateTriangles<TvG, TvM, TvS>(Material material, VertexBufferColumns vertices, IEnumerable<(int A, int B, int C)> indices)
-            where TvG : struct, IVertexGeometry
-            where TvM : struct, IVertexMaterial
-            where TvS : struct, IVertexSkinning
-        {
-            foreach (var (ta, tb, tc) in indices)
-            {
-                var va = vertices.GetVertex<TvG, TvM, TvS>(ta);
-                var vb = vertices.GetVertex<TvG, TvM, TvS>(tb);
-                var vc = vertices.GetVertex<TvG, TvM, TvS>(tc);
-
-                yield return (va, vb, vc, material);
-            }
+            return EvaluatedTriangle<TvG, TvM, TvS>.GetTrianglesFromMesh(mesh, xform);
         }
         }
 
 
         #endregion
         #endregion
@@ -708,7 +642,7 @@ namespace SharpGLTF.Schema2
 
 
             foreach (var tri in srcScene.EvaluateTriangles<VertexPositionNormal, VertexColor1Texture1>(options, animation, time))
             foreach (var tri in srcScene.EvaluateTriangles<VertexPositionNormal, VertexColor1Texture1>(options, animation, time))
             {
             {
-                var material = materialFunc(tri.Item4);
+                var material = materialFunc(tri.Material);
 
 
                 mesh.UsePrimitive(material).AddTriangle(tri.A, tri.B, tri.C);
                 mesh.UsePrimitive(material).AddTriangle(tri.A, tri.B, tri.C);
             }
             }
@@ -795,6 +729,16 @@ namespace SharpGLTF.Schema2
             return dstMesh;
             return dstMesh;
         }
         }
 
 
+        public static MeshBuilder<Materials.MaterialBuilder, TvG, TvM, TvS> ToMeshBuilder<TvG, TvM, TvS>(this IEnumerable<EvaluatedTriangle<TvG, TvM, TvS>> triangles)
+            where TvG : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
+        {
+            return triangles
+                .Select(item => (item.A, item.B, item.C, item.Material))
+                .ToMeshBuilder(m => m.ToMaterialBuilder());
+        }
+
         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, Converter<TMaterial, Materials.MaterialBuilder> materialFunc)
         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, Converter<TMaterial, Materials.MaterialBuilder> materialFunc)
             where TvG : struct, IVertexGeometry
             where TvG : struct, IVertexGeometry
             where TvM : struct, IVertexMaterial
             where TvM : struct, IVertexMaterial

+ 2 - 2
src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs

@@ -240,11 +240,11 @@ namespace SharpGLTF.Schema2
         /// <param name="animation">An <see cref="Animation"/> instance, or null.</param>
         /// <param name="animation">An <see cref="Animation"/> instance, or null.</param>
         /// <param name="time">The animation time.</param>
         /// <param name="time">The animation time.</param>
         /// <returns>A collection of triangles in world space.</returns>
         /// <returns>A collection of triangles in world space.</returns>
-        public static IEnumerable<(VertexBuilder<TvG, TvM, VertexEmpty> A, VertexBuilder<TvG, TvM, VertexEmpty> B, VertexBuilder<TvG, TvM, VertexEmpty> C, Material Material)> EvaluateTriangles<TvG, TvM>(this Scene scene, Runtime.RuntimeOptions options = null, Animation animation = null, float time = 0)
+        public static IEnumerable<EvaluatedTriangle<TvG, TvM, VertexEmpty>> EvaluateTriangles<TvG, TvM>(this Scene scene, Runtime.RuntimeOptions options = null, Animation animation = null, float time = 0)
             where TvG : struct, IVertexGeometry
             where TvG : struct, IVertexGeometry
             where TvM : struct, IVertexMaterial
             where TvM : struct, IVertexMaterial
         {
         {
-            if (scene == null) return Enumerable.Empty<(VertexBuilder<TvG, TvM, VertexEmpty>, VertexBuilder<TvG, TvM, VertexEmpty>, VertexBuilder<TvG, TvM, VertexEmpty>, Material)>();
+            if (scene == null) return Enumerable.Empty<EvaluatedTriangle<TvG, TvM, VertexEmpty>>();
 
 
             var instance = Runtime.SceneTemplate
             var instance = Runtime.SceneTemplate
                 .Create(scene, options)
                 .Create(scene, options)

+ 1 - 1
tests/SharpGLTF.Core.Tests/Schema2/LoadAndSave/LoadSpecialModelsTest.cs

@@ -197,7 +197,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
 
             var mesh = model.DefaultScene
             var mesh = model.DefaultScene
                 .EvaluateTriangles<Geometry.VertexTypes.VertexPositionNormalTangent, Geometry.VertexTypes.VertexTexture1>()
                 .EvaluateTriangles<Geometry.VertexTypes.VertexPositionNormalTangent, Geometry.VertexTypes.VertexTexture1>()
-                .ToMeshBuilder( m => m.ToMaterialBuilder() );            
+                .ToMeshBuilder();            
 
 
             var editableScene = new Scenes.SceneBuilder();
             var editableScene = new Scenes.SceneBuilder();
             editableScene.AddRigidMesh(mesh, Matrix4x4.Identity);
             editableScene.AddRigidMesh(mesh, Matrix4x4.Identity);