Преглед изворни кода

progress improving how meshes are baked.

Vicente Penades пре 6 година
родитељ
комит
66d5d2feca

+ 4 - 0
src/SharpGLTF.Core/Runtime/README.md

@@ -31,6 +31,10 @@ Finally, in our render call, we render the meshes like this:
 ```c#
 void RenderFrame(Matrix4x4 modelMatrix)
 {
+    modelInstance.SetAnimationFrame("Walking", 2.17f); // example of animating the instance
+
+    modelInstance.SetWorldMatrix("Head", Matrix.LookAt(...) ); // example of manually setting a single node matrix
+
     foreach(var drawable in modelInstance.DrawableReferences)
     {
         var gpuMesh = gpuMeshes[drawable.Item1];

+ 14 - 0
src/SharpGLTF.Core/Runtime/SceneInstance.cs

@@ -201,6 +201,20 @@ namespace SharpGLTF.Runtime
 
         #region API
 
+        public void SetLocalMatrix(string name, System.Numerics.Matrix4x4 localMatrix)
+        {
+            var n = LogicalNodes.FirstOrDefault(item => item.Name == name);
+            if (n == null) return;
+            n.LocalMatrix = localMatrix;
+        }
+
+        public void SetWorldMatrix(string name, System.Numerics.Matrix4x4 worldMatrix)
+        {
+            var n = LogicalNodes.FirstOrDefault(item => item.Name == name);
+            if (n == null) return;
+            n.WorldMatrix = worldMatrix;
+        }
+
         public void SetPoseTransforms()
         {
             foreach (var n in _NodeInstances) n.SetPoseTransform();

+ 2 - 2
src/SharpGLTF.Toolkit/Animations/CurveFactory.cs

@@ -16,7 +16,7 @@ namespace SharpGLTF.Animations
             if (typeof(T) == typeof(Quaternion)) return new QuaternionCurveBuilder() as CurveBuilder<T>;
             if (typeof(T) == typeof(SPARSE)) return new SparseCurveBuilder() as CurveBuilder<T>;
 
-            throw new ArgumentException($"{nameof(T)} not supported.", nameof(T));
+            throw new InvalidOperationException($"{nameof(T)} not supported.");
         }
 
         public static CurveBuilder<T> CreateCurveBuilder<T>(ICurveSampler<T> curve)
@@ -26,7 +26,7 @@ namespace SharpGLTF.Animations
             if (curve is QuaternionCurveBuilder q4cb) return q4cb.Clone() as CurveBuilder<T>;
             if (curve is SparseCurveBuilder sscb) return sscb.Clone() as CurveBuilder<T>;
 
-            throw new ArgumentException($"{nameof(T)} not supported.", nameof(T));
+            throw new InvalidOperationException($"{nameof(T)} not supported.");
         }
     }
 

+ 1 - 1
src/SharpGLTF.Toolkit/Geometry/MorphTargetBuilder.cs

@@ -180,7 +180,7 @@ namespace SharpGLTF.Geometry
                 }
             }
         }
-        
+
         #endregion
     }
 }

+ 11 - 9
src/SharpGLTF.Toolkit/Geometry/PackedMeshBuilder.cs

@@ -27,10 +27,9 @@ namespace SharpGLTF.Geometry
                 throw new ArgumentException(ex.Message, nameof(meshBuilders), ex);
             }
 
-            var encoding = meshBuilders.GetOptimalIndexEncoding();
-
             var vertexBuffers = new Dictionary<string, PackedBuffer>();
             var indexBuffer = new PackedBuffer();
+            var indexEncoding = meshBuilders.GetOptimalIndexEncoding();
 
             var dstMeshes = new List<PackedMeshBuilder<TMaterial>>();
 
@@ -42,9 +41,14 @@ namespace SharpGLTF.Geometry
                 {
                     if (srcPrim.Vertices.Count == 0) continue;
 
+                    var attributeNames = VertexTypes.VertexUtils
+                        .GetVertexAttributes(srcPrim.Vertices[0], srcPrim.Vertices.Count)
+                        .Select(item => item.Name)
+                        .ToList();
+
                     var vAccessors = new List<Memory.MemoryAccessor>();
 
-                    foreach (var an in new[] { "POSITION", "NORMAL" })
+                    foreach (var an in attributeNames)
                     {
                         var vAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(srcPrim.Vertices, an);
                         if (vAccessor == null) continue;
@@ -59,7 +63,7 @@ namespace SharpGLTF.Geometry
                         packed.AddAccessors(vAccessor);
                     }
 
-                    var iAccessor = VertexTypes.VertexUtils.CreateIndexMemoryAccessor(srcPrim.GetIndices(), encoding);
+                    var iAccessor = VertexTypes.VertexUtils.CreateIndexMemoryAccessor(srcPrim.GetIndices(), indexEncoding);
                     if (iAccessor != null) indexBuffer.AddAccessors(iAccessor);
 
                     dstMesh.AddPrimitive(srcPrim.Material, srcPrim.VerticesPerPrimitive, vAccessors.ToArray(), iAccessor);
@@ -68,11 +72,10 @@ namespace SharpGLTF.Geometry
                 dstMeshes.Add(dstMesh);
             }
 
-            // vertexBuffer.MergeBuffers();
+            foreach (var vb in vertexBuffers.Values) vb.MergeBuffers();
             indexBuffer.MergeBuffers();
 
             return dstMeshes;
-
         }
 
         internal static IEnumerable<PackedMeshBuilder<TMaterial>> PackMeshesRowVertices(IEnumerable<IMeshBuilder<TMaterial>> meshBuilders)
@@ -86,10 +89,9 @@ namespace SharpGLTF.Geometry
                 throw new ArgumentException(ex.Message, nameof(meshBuilders), ex);
             }
 
-            var encoding = meshBuilders.GetOptimalIndexEncoding();
-
             var vertexBuffer = new PackedBuffer();
             var indexBuffer = new PackedBuffer();
+            var indexEncoding = meshBuilders.GetOptimalIndexEncoding();
 
             var dstMeshes = new List<PackedMeshBuilder<TMaterial>>();
 
@@ -103,7 +105,7 @@ namespace SharpGLTF.Geometry
                     if (vAccessors == null) continue;
                     vertexBuffer.AddAccessors(vAccessors);
 
-                    var iAccessor = VertexTypes.VertexUtils.CreateIndexMemoryAccessor(srcPrim.GetIndices(), encoding);
+                    var iAccessor = VertexTypes.VertexUtils.CreateIndexMemoryAccessor(srcPrim.GetIndices(), indexEncoding);
                     if (iAccessor != null) indexBuffer.AddAccessors(iAccessor);
 
                     dstMesh.AddPrimitive(srcPrim.Material, srcPrim.VerticesPerPrimitive, vAccessors, iAccessor);

+ 12 - 17
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexUtils.cs

@@ -213,12 +213,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (vertices == null || vertices.Count == 0) return null;
 
             // determine the vertex attributes from the first vertex.
-            var firstVertex = vertices[0];
-
-            var tvg = firstVertex.GetGeometry().GetType();
-            var tvm = firstVertex.GetMaterial().GetType();
-            var tvs = firstVertex.GetSkinning().GetType();
-            var attributes = _GetVertexAttributes(tvg, tvm, tvs, vertices.Count);
+            var attributes = GetVertexAttributes(vertices[0], vertices.Count);
 
             var attribute = attributes.FirstOrDefault(item => item.Name == attributeName);
             if (attribute.Name == null) return null;
@@ -243,12 +238,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (vertices == null || vertices.Count == 0) return null;
 
             // determine the vertex attributes from the first vertex.
-            var firstVertex = vertices[0];
-
-            var tvg = firstVertex.GetGeometry().GetType();
-            var tvm = firstVertex.GetMaterial().GetType();
-            var tvs = firstVertex.GetSkinning().GetType();
-            var attributes = _GetVertexAttributes(tvg, tvm, tvs, vertices.Count);
+            var attributes = GetVertexAttributes(vertices[0], vertices.Count);
 
             // create a buffer
             int byteStride = attributes[0].ByteStride;
@@ -301,23 +291,28 @@ namespace SharpGLTF.Geometry.VertexTypes
             return accessor;
         }
 
-        private static MemoryAccessInfo[] _GetVertexAttributes(Type vertexType, Type valuesType, Type jointsType, int itemsCount)
+
+        public static MemoryAccessInfo[] GetVertexAttributes(this IVertexBuilder firstVertex, int vertexCount)
         {
+            var tvg = firstVertex.GetGeometry().GetType();
+            var tvm = firstVertex.GetMaterial().GetType();
+            var tvs = firstVertex.GetSkinning().GetType();
+
             var attributes = new List<MemoryAccessInfo>();
 
-            foreach (var finfo in vertexType.GetFields())
+            foreach (var finfo in tvg.GetFields())
             {
                 var attribute = _GetMemoryAccessInfo(finfo);
                 if (attribute.HasValue) attributes.Add(attribute.Value);
             }
 
-            foreach (var finfo in valuesType.GetFields())
+            foreach (var finfo in tvm.GetFields())
             {
                 var attribute = _GetMemoryAccessInfo(finfo);
                 if (attribute.HasValue) attributes.Add(attribute.Value);
             }
 
-            foreach (var finfo in jointsType.GetFields())
+            foreach (var finfo in tvs.GetFields())
             {
                 var attribute = _GetMemoryAccessInfo(finfo);
                 if (attribute.HasValue) attributes.Add(attribute.Value);
@@ -325,7 +320,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
             var array = attributes.ToArray();
 
-            MemoryAccessInfo.SetInterleavedInfo(array, 0, itemsCount);
+            MemoryAccessInfo.SetInterleavedInfo(array, 0, vertexCount);
 
             return array;
         }

+ 17 - 27
src/SharpGLTF.Toolkit/Scenes/SceneBuilder.Schema2.cs

@@ -33,54 +33,44 @@ namespace SharpGLTF.Scenes
 
         public void AddScene(Scene dstScene, SceneBuilder srcScene)
         {
-            // gather all MaterialBuilder unique instances
+            // gather all unique MeshBuilders
 
-            var materialGroups = srcScene.Instances
+            var srcMeshes = srcScene.Instances
                 .Select(item => item.Content?.GetGeometryAsset())
                 .Where(item => item != null)
+                .Distinct()
+                .ToArray();
+
+            // gather all unique MaterialBuilders
+
+            var materialGroups = srcMeshes
                 .SelectMany(item => item.Primitives)
                 .Where(item => !Geometry.MeshBuilderToolkit.IsEmpty(item))
                 .Select(item => item.Material)
-                .Where(item => item != null)
                 .Distinct()
                 .ToList()
                 // group by equal content, to reduce material splitting whenever possible.
                 .GroupBy(item => item, Materials.MaterialBuilder.ContentComparer);
 
+            // create a Schema2.Material for every MaterialBuilder.
+
             foreach (var mg in materialGroups)
             {
                 var val = dstScene.LogicalParent.CreateMaterial(mg.Key);
-
-                foreach (var key in mg)
-                {
-                    _Materials[key] = val;
-                }
+                foreach (var key in mg) _Materials[key] = val;
             }
 
-            // gather all MeshBuilder unique instances
-            // and group them by their vertex attribute layout.
-
-            var meshGroups = srcScene.Instances
-                .Select(item => item.Content?.GetGeometryAsset())
-                .Where(item => item != null)
-                .Distinct()
-                .ToList()
-                .GroupBy(item => item.GetType());
+            // create a Schema2.Mesh for every MeshBuilder.
 
-            // create Schema2.Mesh collections for every gathered group.
+            var dstMeshes = dstScene.LogicalParent.CreateMeshes(mat => _Materials[mat], true,  srcMeshes);
 
-            foreach (var meshGroup in meshGroups)
+            for (int i = 0; i < srcMeshes.Length; ++i)
             {
-                var meshArray = meshGroup.ToArray();
-
-                var meshDst = dstScene.LogicalParent.CreateMeshes(mat => _Materials[mat], meshArray);
-
-                for (int i = 0; i < meshArray.Length; ++i)
-                {
-                    _Meshes[meshArray[i]] = meshDst[i];
-                }
+                _Meshes[srcMeshes[i]] = dstMeshes[i];
             }
 
+            // TODO: here we could check that every dstMesh has been correctly created.
+
             // gather all NodeBuilder unique armatures
 
             var armatures = srcScene.Instances

+ 29 - 5
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -57,6 +57,16 @@ namespace SharpGLTF.Schema2
             Guard.NotNull(materialEvaluator, nameof(materialEvaluator));
             Guard.NotNull(meshBuilders, nameof(meshBuilders));
 
+            return root.CreateMeshes(materialEvaluator, true, meshBuilders);
+        }
+
+        public static IReadOnlyList<Mesh> CreateMeshes<TMaterial>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, bool strided, params IMeshBuilder<TMaterial>[] meshBuilders)
+        {
+            Guard.NotNull(root, nameof(root));
+            Guard.NotNull(materialEvaluator, nameof(materialEvaluator));
+            Guard.NotNull(meshBuilders, nameof(meshBuilders));
+            Guard.IsTrue(meshBuilders.Length == meshBuilders.Distinct().Count(), nameof(meshBuilders), "The collection has repeated meshes.");
+
             foreach (var m in meshBuilders) m.Validate();
 
             // create a new material for every unique (byRef) material in the mesh builders.
@@ -67,10 +77,25 @@ namespace SharpGLTF.Schema2
                 .Distinct()
                 .ToDictionary(m => m, m => materialEvaluator(m));
 
-            // creates meshes and primitives using MemoryAccessors using a single, shared vertex and index buffer
-            var srcMeshes = PackedMeshBuilder<TMaterial>
-                .PackMeshesRowVertices(meshBuilders)
-                .ToList();
+            // create Schema2.Mesh collections for every gathered group.
+
+            List<PackedMeshBuilder<TMaterial>> srcMeshes = null;
+
+            if (strided)
+            {
+                var meshGroups = meshBuilders.GroupBy(item => item.GetType());
+
+                srcMeshes = meshGroups
+                    .SelectMany(grp => PackedMeshBuilder<TMaterial>.PackMeshesRowVertices(grp.ToArray()))
+                    .ToList();
+            }
+            else
+            {
+                srcMeshes = PackedMeshBuilder<TMaterial>.PackMeshesColumnVertices(meshBuilders)
+                    .ToList();
+            }
+
+            // create schema2 meshes
 
             var dstMeshes = new List<Mesh>();
 
@@ -718,7 +743,6 @@ namespace SharpGLTF.Schema2
 
                 vmap[srcLine.Item1] = indices.Item1;
                 vmap[srcLine.Item2] = indices.Item2;
-
             }
 
             foreach (var srcTri in srcPrim.GetTriangleIndices())

+ 106 - 0
tests/SharpGLTF.Tests/Reports.cs

@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using XYZ = System.Numerics.Vector3;
+
+using SharpGLTF.Schema2;
+using System.Linq;
+
+namespace SharpGLTF.Reporting
+{
+    public class VisualReport
+    {
+        private VisualReport(Schema2.MeshPrimitive prim)
+        {
+            SetFrom(prim);
+        }
+
+        internal VisualReport() { }
+
+        public int NumTriangles { get; internal set; }
+        public int NumLines { get; internal set; }
+        public int NumPoints { get; internal set; }
+
+        public (XYZ, XYZ) Bounds { get; internal set; }
+
+        public IEnumerable<string> VertexAttributes { get; internal set; }
+
+        public void SetFrom(Schema2.Mesh mesh)
+        {
+            SetFrom(mesh.Primitives.Select(prim => new VisualReport(prim)));
+        }
+
+        public void SetFrom(Schema2.MeshPrimitive prim)
+        {
+            NumPoints = prim.GetPointIndices().Count();
+            NumLines = prim.GetLineIndices().Count();
+            NumTriangles = prim.GetTriangleIndices().Count();
+            Bounds = prim.GetVertexAccessor("POSITION").AsVector3Array().GetBounds();
+        }
+
+        public void SetFrom(Schema2.Scene scene)
+        {
+            var tris = scene.EvaluateTriangles().ToList();
+
+            this.NumTriangles = tris.Count;
+
+            Bounds = tris
+                .SelectMany(item => new[] { item.Item1, item.Item2, item.Item3 })
+                .Select(item => item.GetGeometry().GetPosition())
+                .GetBounds();
+        }
+
+        internal void SetFrom(IEnumerable<VisualReport> many)
+        {
+            NumTriangles = many.Sum(item => item.NumTriangles);
+            NumLines = many.Sum(item => item.NumLines);
+            NumPoints = many.Sum(item => item.NumPoints);
+
+            var min = many.Select(item => item.Bounds.Item1).GetMin();
+            var max = many.Select(item => item.Bounds.Item2).GetMax();
+            Bounds = (min, max);
+        }
+    }
+
+    public class MeshReport : VisualReport
+    {
+        internal MeshReport(string name)
+        {
+            Name = name;
+        }
+
+        public String Name { get; private set; }
+    }
+    public class ModelReport : VisualReport
+    {
+        private readonly List<MeshReport> _Meshes = new List<MeshReport>();
+        private readonly List<VisualReport> _Scenes = new List<VisualReport>();
+
+        public static ModelReport CreateReportFrom(Schema2.ModelRoot model)
+        {
+            var rrrr = new ModelReport();
+
+            foreach(var mesh in model.LogicalMeshes)
+            {
+                var r = new MeshReport(mesh.Name);
+                r.SetFrom(mesh);
+                rrrr._Meshes.Add(r);
+                
+            }
+
+            foreach (var scene in model.LogicalScenes)
+            {
+                var r = new VisualReport();
+                r.SetFrom(scene);
+                rrrr._Scenes.Add(r);
+            }
+
+            rrrr.SetFrom(rrrr._Scenes);
+
+            return rrrr;
+        }
+    }
+
+    
+}

+ 21 - 4
tests/SharpGLTF.Tests/Scenes/SceneBuilderTests.cs

@@ -8,6 +8,7 @@ using SharpGLTF.Geometry;
 using SharpGLTF.Geometry.VertexTypes;
 using SharpGLTF.Geometry.Parametric;
 using SharpGLTF.Materials;
+using SharpGLTF.Schema2;
 
 namespace SharpGLTF.Scenes
 {
@@ -452,12 +453,28 @@ namespace SharpGLTF.Scenes
 
             // perform roundtrip
 
-            var scene = Schema2.Schema2Toolkit.ToSceneBuilder(modelSrc.DefaultScene);
+            var sceneSrc = Schema2.Schema2Toolkit.ToSceneBuilder(modelSrc.DefaultScene);            
 
-            var cube = new Cube<MaterialBuilder>(MaterialBuilder.CreateDefault(), 1, 0.01f, 1);
-            scene.AddMesh(cube.ToMesh(Matrix4x4.Identity), Matrix4x4.Identity);
+            // var cube = new Cube<MaterialBuilder>(MaterialBuilder.CreateDefault(), 1, 0.01f, 1);
+            // scene.AddMesh(cube.ToMesh(Matrix4x4.Identity), Matrix4x4.Identity);
 
-            var modelBis = scene.ToSchema2();
+            var modelBis = sceneSrc.ToSchema2();
+
+            var sceneBis = Schema2.Schema2Toolkit.ToSceneBuilder(modelBis.DefaultScene);
+
+            // compare files
+
+            var srcTris = modelSrc.DefaultScene.EvaluateTriangles().ToList();
+            var bisTris = modelBis.DefaultScene.EvaluateTriangles().ToList();
+
+            Assert.AreEqual(srcTris.Count, bisTris.Count);
+
+            var srcRep = Reporting.ModelReport.CreateReportFrom(modelSrc);
+            var bisRep = Reporting.ModelReport.CreateReportFrom(modelBis);
+
+            Assert.AreEqual(srcRep.NumTriangles, bisRep.NumTriangles);
+            NumericsAssert.AreEqual(srcRep.Bounds.Item1, bisRep.Bounds.Item1, 0.0001f);
+            NumericsAssert.AreEqual(srcRep.Bounds.Item2, bisRep.Bounds.Item2, 0.0001f);
 
             // save file
 

+ 1 - 1
tests/SharpGLTF.Tests/SharpGLTF.Tests.csproj

@@ -13,7 +13,7 @@
   <ItemGroup>
     <PackageReference Include="LibGit2Sharp" Version="0.26.1" />
     <PackageReference Include="nunit" Version="3.12.0" />
-    <PackageReference Include="NUnit3TestAdapter" Version="3.14.0" />
+    <PackageReference Include="NUnit3TestAdapter" Version="3.15.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
     <PackageReference Include="PLplot" Version="5.13.7" />    
   </ItemGroup>

+ 38 - 0
tests/SharpGLTF.Tests/Utils.cs

@@ -291,6 +291,44 @@ namespace SharpGLTF
 
             return (float)Math.Acos(c);
         }
+
+        public static (Vector3, Vector3) GetBounds(this IEnumerable<Vector3> collection)
+        {
+            var min = new Vector3(float.MaxValue);
+            var max = new Vector3(float.MinValue);
+
+            foreach (var v in collection)
+            {
+                min = Vector3.Min(v, min);
+                max = Vector3.Max(v, max);
+            }
+
+            return (min, max);
+        }
+
+        public static Vector3 GetMin(this IEnumerable<Vector3> collection)
+        {
+            var min = new Vector3(float.MaxValue);            
+
+            foreach (var v in collection)
+            {
+                min = Vector3.Min(v, min);                
+            }
+
+            return min;
+        }
+
+        public static Vector3 GetMax(this IEnumerable<Vector3> collection)
+        {            
+            var max = new Vector3(float.MinValue);
+
+            foreach (var v in collection)
+            {
+                max = Vector3.Max(v, max);
+            }
+
+            return max;
+        }
     }