Browse Source

Bug fixes & improving MeshDecoder

Vicente Penades 5 years ago
parent
commit
750c6c82e3

+ 21 - 0
examples/SharpGLTF.Plotly/PlotlyScene.cs

@@ -10,6 +10,7 @@ using Plotly.Types;
 using TRACES = Plotly.Box<Plotly.Types.ITracesProperty>;
 using TRACES = Plotly.Box<Plotly.Types.ITracesProperty>;
 
 
 using VERTEX = SharpGLTF.Geometry.IVertexBuilder;
 using VERTEX = SharpGLTF.Geometry.IVertexBuilder;
+using VERTEXBUILDER = SharpGLTF.Geometry.VertexBuilder<SharpGLTF.Geometry.VertexTypes.VertexPositionNormal, SharpGLTF.Geometry.VertexTypes.VertexEmpty, SharpGLTF.Geometry.VertexTypes.VertexEmpty>;
 
 
 namespace SharpGLTF
 namespace SharpGLTF
 {
 {
@@ -31,6 +32,26 @@ namespace SharpGLTF
             _Traces.Add(trace);
             _Traces.Add(trace);
         }
         }
 
 
+        public void AppendTriangles<TMaterial>(IEnumerable<(Vector3 A, Vector3 B, Vector3 C, TMaterial Material)> tris, Func<TMaterial, int> materialColorFunc)
+        {
+            (VERTEX A, VERTEX B, VERTEX C, TMaterial Material) _Convert((Vector3 A, Vector3 B, Vector3 C, TMaterial Material) tri)
+            {
+                var ab = tri.B - tri.A;
+                var ac = tri.C - tri.A;
+                var n = Vector3.Normalize(Vector3.Cross(ab, ac));
+
+                var aa = new VERTEXBUILDER((tri.A, n));
+                var bb = new VERTEXBUILDER((tri.B, n));
+                var cc = new VERTEXBUILDER((tri.C, n));
+
+                return (aa, bb, cc, tri.Material);
+            }
+
+            var trace = _CreateTrace(tris.Select(_Convert), materialColorFunc);
+
+            _Traces.Add(trace);
+        }
+
         public Box<IPlotProperty> ToPlotProperties() { return Plot.traces(_Traces.ToArray()); }
         public Box<IPlotProperty> ToPlotProperties() { return Plot.traces(_Traces.ToArray()); }
         
         
         public Plot ToPlot()
         public Plot ToPlot()

+ 1 - 1
src/SharpGLTF.Core/Collections/NamedList.cs

@@ -23,7 +23,7 @@ namespace SharpGLTF.Collections
             {
             {
                 if (_ByName == null) _ByName = new Dictionary<string, int>();
                 if (_ByName == null) _ByName = new Dictionary<string, int>();
                 if (_ByIndex == null) _ByIndex = new List<string>();
                 if (_ByIndex == null) _ByIndex = new List<string>();
-                if (_ByIndex.Count <= index) _ByIndex.Add(null);
+                while (_ByIndex.Count <= index) _ByIndex.Add(null);
 
 
                 _ByName[name] = index;
                 _ByName[name] = index;
                 _ByIndex[index] = name;
                 _ByIndex[index] = name;

+ 4 - 11
src/SharpGLTF.Core/Runtime/AnimatableProperty.cs

@@ -47,18 +47,11 @@ namespace SharpGLTF.Runtime
         #region API
         #region API
 
 
         /// <summary>
         /// <summary>
-        /// Evaluates the value of this <see cref="AnimatableProperty{T}"/> at a given <paramref name="offset"/> for a given <paramref name="trackName"/>.
+        /// Evaluates the value of this <see cref="AnimatableProperty{T}"/> at a given <paramref name="offset"/> for a given <paramref name="trackLogicalIndex"/>.
         /// </summary>
         /// </summary>
-        /// <param name="trackName">An animation track name, or null.</param>
-        /// <param name="offset">A time offset within the given animation track.</param>
-        /// <returns>The evaluated value taken from the animation <paramref name="trackName"/>, or <see cref="Value"/> if a track was not found.</returns>
-        public T GetValueAt(string trackName, float offset)
-        {
-            var idx = _Animations?.IndexOf(trackName) ?? -1;
-
-            return GetValueAt(idx, offset);
-        }
-
+        /// <param name="trackLogicalIndex">The index of the animation track</param>
+        /// <param name="offset">The time offset within the curve</param>
+        /// <returns>The evaluated value taken from the animation <paramref name="trackLogicalIndex"/>, or <see cref="Value"/> if a track was not found.</returns>
         public T GetValueAt(int trackLogicalIndex, float offset)
         public T GetValueAt(int trackLogicalIndex, float offset)
         {
         {
             if (_Animations == null) return this.Value;
             if (_Animations == null) return this.Value;

+ 12 - 14
src/SharpGLTF.Core/Runtime/MeshDecoder.Schema2.cs

@@ -209,14 +209,24 @@ namespace SharpGLTF.Runtime
 
 
         public int MorphTargetsCount => _MorphTargets.Count;
         public int MorphTargetsCount => _MorphTargets.Count;
 
 
+        public bool IsPointIndices => this._PrimitiveType.GetPrimitiveVertexSize() == 1;
+
+        public IEnumerable<(int A, int B)> LineIndices
+        {
+            get
+            {
+                if (this._PrimitiveType.GetPrimitiveVertexSize() != 2) return Enumerable.Empty<(int, int)>();
+                if (this._PrimitiveIndices == null) return this._PrimitiveType.GetLinesIndices(VertexCount);
+                return this._PrimitiveType.GetLinesIndices(this._PrimitiveIndices);
+            }
+        }
+
         public IEnumerable<(int A, int B, int C)> TriangleIndices
         public IEnumerable<(int A, int B, int C)> TriangleIndices
         {
         {
             get
             get
             {
             {
                 if (this._PrimitiveType.GetPrimitiveVertexSize() != 3) return Enumerable.Empty<(int, int, int)>();
                 if (this._PrimitiveType.GetPrimitiveVertexSize() != 3) return Enumerable.Empty<(int, int, int)>();
-
                 if (this._PrimitiveIndices == null) return this._PrimitiveType.GetTrianglesIndices(VertexCount);
                 if (this._PrimitiveIndices == null) return this._PrimitiveType.GetTrianglesIndices(VertexCount);
-
                 return this._PrimitiveType.GetTrianglesIndices(this._PrimitiveIndices);
                 return this._PrimitiveType.GetTrianglesIndices(this._PrimitiveIndices);
             }
             }
         }
         }
@@ -267,18 +277,6 @@ namespace SharpGLTF.Runtime
             return XYZW.One;
             return XYZW.One;
         }
         }
 
 
-        public XYZW GetJoints(int vertexIndex)
-        {
-            if (_Joints0 != null) return _Joints0[vertexIndex];
-            return XYZW.Zero;
-        }
-
-        public XYZW GetWeights(int vertexIndex)
-        {
-            if (_Weights0 != null) return _Weights0[vertexIndex];
-            return XYZW.UnitX;
-        }
-
         public Transforms.SparseWeight8 GetSkinWeights(int vertexIndex)
         public Transforms.SparseWeight8 GetSkinWeights(int vertexIndex)
         {
         {
             if (_Weights0 == null) return default;
             if (_Weights0 == null) return default;

+ 73 - 31
src/SharpGLTF.Core/Runtime/MeshDecoder.cs

@@ -21,16 +21,42 @@ namespace SharpGLTF.Runtime
     {
     {
         #region properties
         #region properties
 
 
+        /// <summary>
+        /// Gets a value indicating the total number of vertices for this primitive.
+        /// </summary>
         int VertexCount { get; }
         int VertexCount { get; }
 
 
+        /// <summary>
+        /// Gets a value indicating the total number of morph targets for this primitive.
+        /// </summary>
         int MorphTargetsCount { get; }
         int MorphTargetsCount { get; }
 
 
+        /// <summary>
+        /// Gets a value indicating the number of color vertex attributes.
+        /// In the range of 0 to 2.
+        /// </summary>
         int ColorsCount { get; }
         int ColorsCount { get; }
 
 
+        /// <summary>
+        /// Gets a value indicating the number of texture coordinate vertex attributes.
+        /// In the range of 0 to 2.
+        /// </summary>
         int TexCoordsCount { get; }
         int TexCoordsCount { get; }
 
 
+        /// <summary>
+        /// Gets a value indicating the number of skinning joint-weight attributes.
+        /// The values can be 0, 4 or 8.
+        /// </summary>
         int JointsWeightsCount { get; }
         int JointsWeightsCount { get; }
 
 
+        /// <summary>
+        /// Gets a sequence of tuples where each item represents the vertex indices of a line.
+        /// </summary>
+        IEnumerable<(int A, int B)> LineIndices { get; }
+
+        /// <summary>
+        /// Gets a sequence of tuples where each item represents the vertex indices of a triangle.
+        /// </summary>
         IEnumerable<(int A, int B, int C)> TriangleIndices { get; }
         IEnumerable<(int A, int B, int C)> TriangleIndices { get; }
 
 
         #endregion
         #endregion
@@ -53,10 +79,6 @@ namespace SharpGLTF.Runtime
 
 
         XYZW GetColor(int vertexIndex, int colorSetIndex);
         XYZW GetColor(int vertexIndex, int colorSetIndex);
 
 
-        XYZW GetJoints(int vertexIndex);
-
-        XYZW GetWeights(int vertexIndex);
-
         Transforms.SparseWeight8 GetSkinWeights(int vertexIndex);
         Transforms.SparseWeight8 GetSkinWeights(int vertexIndex);
 
 
         #endregion
         #endregion
@@ -189,26 +211,7 @@ namespace SharpGLTF.Runtime
                     sceneInstance.SetAnimationFrame(trackIdx, time);
                     sceneInstance.SetAnimationFrame(trackIdx, time);
                     var (fc, fr) = sceneInstance.EvaluateBoundingSphere(decodedMeshes);
                     var (fc, fr) = sceneInstance.EvaluateBoundingSphere(decodedMeshes);
 
 
-                    if (radius < 0) { center = fc; radius = fr; continue; }
-
-                    // combine spheres
-
-                    var direction = fc - center;
-                    var distance = direction.Length();
-
-                    // check if current frame is already contained in master sphere.
-                    if (radius >= (fr + distance)) continue;
-
-                    // check if master sphere is already contained in current frame.
-                    if (fr >= (radius + distance)) { center = fc; radius = fr; continue; }
-
-                    // combine
-                    direction = XYZ.Normalize(direction);
-                    var p0 = center - (direction * radius);
-                    var p1 = fc + (direction * fr);
-
-                    center = (p0 + p1) / 2;
-                    radius = (p0 - p1).Length() / 2;
+                    _MergeSphere(ref center, ref radius, fc, fr);
                 }
                 }
             }
             }
 
 
@@ -244,18 +247,57 @@ namespace SharpGLTF.Runtime
 
 
             foreach (var p1 in instance.GetWorldVertices(meshes))
             foreach (var p1 in instance.GetWorldVertices(meshes))
             {
             {
-                if (radius < 0) { center = p1; radius = 0; continue; }
-
-                var dir = XYZ.Normalize(p1 - center);
-                var p2 = center - (dir * radius);
-
-                center = (p1 + p2) / 2;
-                radius = (p1 - p2).Length() / 2;
+                _AddPointToSphere(ref center, ref radius, p1);
             }
             }
 
 
             return (center, radius);
             return (center, radius);
         }
         }
 
 
+        private static void _AddPointToSphere(ref XYZ c1, ref float r1, XYZ c2)
+        {
+            if (r1 < 0) { c1 = c2; r1 = 0; return; }
+
+            var dir = c2 - c1;
+            var len = dir.Length();
+            if (len <= r1) return; // if inside, exit.
+
+            dir /= len;
+            var p1 = c1 - (dir * r1);
+
+            c1 = (p1 + c2) / 2;
+            r1 = (p1 - c2).Length() / 2;
+
+            #if DEBUG
+            var dist = (c2 - c1).Length();
+            System.Diagnostics.Debug.Assert(dist <= (r1 + 0.001f));
+            dist = (p1 - c1).Length();
+            System.Diagnostics.Debug.Assert(dist <= (r1 + 0.001f));
+            #endif
+        }
+
+        private static void _MergeSphere(ref XYZ c1, ref float r1, XYZ c2, float r2)
+        {
+            if (r1 < 0) { c1 = c2; r1 = r2; return; }
+
+            var dir = c2 - c1;
+            var len = dir.Length();
+            if (r1 >= (r2 + len)) return; // new inside current, exit.
+            if (r2 >= (r1 + len)) { c1 = c2; r1 = r2; return; } // current inside new, update & exit.
+
+            // combine
+            dir /= len;
+            var p1 = c1 - (dir * r1);
+            var p2 = c2 + (dir * r2);
+
+            c1 = (p1 + p2) / 2;
+            r1 = (p1 - p2).Length() / 2;
+
+            #if DEBUG
+            var dist = (c2 - c1).Length() + r2;
+            System.Diagnostics.Debug.Assert(dist <= (r1 + 0.001f));
+            #endif
+        }
+
         public static IEnumerable<XYZ> GetWorldVertices<TMaterial>(this SceneInstance instance, IReadOnlyList<IMeshDecoder<TMaterial>> meshes)
         public static IEnumerable<XYZ> GetWorldVertices<TMaterial>(this SceneInstance instance, IReadOnlyList<IMeshDecoder<TMaterial>> meshes)
             where TMaterial : class
             where TMaterial : class
         {
         {

+ 13 - 7
src/SharpGLTF.Core/Runtime/NodeTemplate.cs

@@ -50,9 +50,9 @@ namespace SharpGLTF.Runtime
                 if (mrpAnim != null) _Morphing.AddCurve(index, name, mrpAnim);
                 if (mrpAnim != null) _Morphing.AddCurve(index, name, mrpAnim);
             }
             }
 
 
-            _UsePrecomputed = !(_Scale.IsAnimated | _Rotation.IsAnimated | _Translation.IsAnimated);
+            _UseAnimatedTransforms = _Scale.IsAnimated | _Rotation.IsAnimated | _Translation.IsAnimated;
 
 
-            if (_UsePrecomputed)
+            if (!_UseAnimatedTransforms)
             {
             {
                 _Scale = null;
                 _Scale = null;
                 _Rotation = null;
                 _Rotation = null;
@@ -64,15 +64,21 @@ namespace SharpGLTF.Runtime
 
 
         #region data
         #region data
 
 
+        /// <summary>
+        /// the index of this node within <see cref="SceneTemplate._NodeTemplates"/>
+        /// </summary>
         private readonly int _LogicalSourceIndex;
         private readonly int _LogicalSourceIndex;
 
 
+        /// <summary>
+        /// the index of the parent node within <see cref="SceneTemplate._NodeTemplates"/>
+        /// </summary>
         private readonly int _ParentIndex;
         private readonly int _ParentIndex;
         private readonly int[] _ChildIndices;
         private readonly int[] _ChildIndices;
 
 
-        private readonly bool _UsePrecomputed;
         private readonly Matrix4x4 _LocalMatrix;
         private readonly Matrix4x4 _LocalMatrix;
         private readonly Transforms.AffineTransform _LocalTransform;
         private readonly Transforms.AffineTransform _LocalTransform;
 
 
+        private readonly bool _UseAnimatedTransforms;
         private readonly AnimatableProperty<Vector3> _Scale;
         private readonly AnimatableProperty<Vector3> _Scale;
         private readonly AnimatableProperty<Quaternion> _Rotation;
         private readonly AnimatableProperty<Quaternion> _Rotation;
         private readonly AnimatableProperty<Vector3> _Translation;
         private readonly AnimatableProperty<Vector3> _Translation;
@@ -128,7 +134,7 @@ namespace SharpGLTF.Runtime
 
 
         public Transforms.AffineTransform GetLocalTransform(int trackLogicalIndex, float time)
         public Transforms.AffineTransform GetLocalTransform(int trackLogicalIndex, float time)
         {
         {
-            if (_UsePrecomputed || trackLogicalIndex < 0) return _LocalTransform;
+            if (!_UseAnimatedTransforms || trackLogicalIndex < 0) return _LocalTransform;
 
 
             var s = _Scale?.GetValueAt(trackLogicalIndex, time);
             var s = _Scale?.GetValueAt(trackLogicalIndex, time);
             var r = _Rotation?.GetValueAt(trackLogicalIndex, time);
             var r = _Rotation?.GetValueAt(trackLogicalIndex, time);
@@ -139,7 +145,7 @@ namespace SharpGLTF.Runtime
 
 
         public Transforms.AffineTransform GetLocalTransform(ReadOnlySpan<int> track, ReadOnlySpan<float> time, ReadOnlySpan<float> weight)
         public Transforms.AffineTransform GetLocalTransform(ReadOnlySpan<int> track, ReadOnlySpan<float> time, ReadOnlySpan<float> weight)
         {
         {
-            if (_UsePrecomputed) return _LocalTransform;
+            if (!_UseAnimatedTransforms) return _LocalTransform;
 
 
             Span<Transforms.AffineTransform> xforms = stackalloc Transforms.AffineTransform[track.Length];
             Span<Transforms.AffineTransform> xforms = stackalloc Transforms.AffineTransform[track.Length];
 
 
@@ -153,14 +159,14 @@ namespace SharpGLTF.Runtime
 
 
         public Matrix4x4 GetLocalMatrix(int trackLogicalIndex, float time)
         public Matrix4x4 GetLocalMatrix(int trackLogicalIndex, float time)
         {
         {
-            if (_UsePrecomputed || trackLogicalIndex < 0) return _LocalMatrix;
+            if (!_UseAnimatedTransforms || trackLogicalIndex < 0) return _LocalMatrix;
 
 
             return GetLocalTransform(trackLogicalIndex, time).Matrix;
             return GetLocalTransform(trackLogicalIndex, time).Matrix;
         }
         }
 
 
         public Matrix4x4 GetLocalMatrix(ReadOnlySpan<int> track, ReadOnlySpan<float> time, ReadOnlySpan<float> weight)
         public Matrix4x4 GetLocalMatrix(ReadOnlySpan<int> track, ReadOnlySpan<float> time, ReadOnlySpan<float> weight)
         {
         {
-            if (_UsePrecomputed) return _LocalMatrix;
+            if (!_UseAnimatedTransforms) return _LocalMatrix;
 
 
             return GetLocalTransform(track, time, weight).Matrix;
             return GetLocalTransform(track, time, weight).Matrix;
         }
         }

+ 1 - 1
src/SharpGLTF.Core/Runtime/SceneTemplate.cs

@@ -122,7 +122,7 @@ namespace SharpGLTF.Runtime
         /// Gets the unique indices of <see cref="Schema2.Mesh"/> instances in <see cref="Schema2.ModelRoot.LogicalMeshes"/>
         /// Gets the unique indices of <see cref="Schema2.Mesh"/> instances in <see cref="Schema2.ModelRoot.LogicalMeshes"/>
         /// </summary>
         /// </summary>
         public IEnumerable<int> LogicalMeshIds => _DrawableReferences.Select(item => item.LogicalMeshIndex).Distinct();
         public IEnumerable<int> LogicalMeshIds => _DrawableReferences.Select(item => item.LogicalMeshIndex).Distinct();
-        
+
         #endregion
         #endregion
 
 
         #region API
         #region API

+ 0 - 6
src/SharpGLTF.Core/Runtime/vertexIndex.cs

@@ -1,6 +0,0 @@
-namespace SharpGLTF.Runtime
-{
-    public class vertexIndex
-    {
-    }
-}

+ 4 - 2
tests/SharpGLTF.NUnit/NUnitUtils.cs

@@ -76,11 +76,13 @@ namespace SharpGLTF
             return fileName;
             return fileName;
         }
         }
 
 
-        public static T AttachToCurrentTest<T>(this T target, string fileName, Action<T, string> onSave)
+        public static T AttachToCurrentTest<T>(this T target, string fileName, Action<T, System.IO.FileInfo> onSave)
         {
         {
             var filePath = TestContext.CurrentContext.GetAttachmentPath(fileName, true);
             var filePath = TestContext.CurrentContext.GetAttachmentPath(fileName, true);
 
 
-            onSave(target, filePath);
+            var finfo = new System.IO.FileInfo(filePath);
+
+            onSave(target, finfo);
 
 
             if (System.IO.File.Exists(filePath)) TestContext.AddTestAttachment(filePath);
             if (System.IO.File.Exists(filePath)) TestContext.AddTestAttachment(filePath);
 
 

+ 62 - 7
tests/SharpGLTF.Tests/Runtime/SceneTemplateTests.cs

@@ -1,10 +1,13 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Numerics;
 using System.Text;
 using System.Text;
 
 
 using NUnit.Framework;
 using NUnit.Framework;
 
 
+using Plotly;
+
 namespace SharpGLTF.Runtime
 namespace SharpGLTF.Runtime
 {
 {
     [Category("Core.Runtime")]
     [Category("Core.Runtime")]
@@ -56,14 +59,66 @@ namespace SharpGLTF.Runtime
 
 
             var model = Schema2.ModelRoot.Load(modelPath);
             var model = Schema2.ModelRoot.Load(modelPath);
 
 
-            var (center, radius) = model.DefaultScene.EvaluateBoundingSphere(1.0f);
+            model.AttachToCurrentTest("reference.plotly");
+
+
+            var scene = model.DefaultScene;
+            
+            var decodedMeshes = scene.LogicalParent.LogicalMeshes.Decode();
+            var sceneTemplate = SceneTemplate.Create(scene, false);
+            var sceneInstance = sceneTemplate.CreateInstance();
+
+            var duration = sceneInstance.GetAnimationDuration(0);
+            sceneInstance.SetAnimationFrame(0, duration/2);
+
+            IEnumerable<(Vector3,Vector3,Vector3, int)> evaluateTriangles(DrawableInstance inst)
+            {
+                var mesh = decodedMeshes[inst.Template.LogicalMeshIndex];
+
+                foreach(var prim in mesh.Primitives)
+                {
+                    foreach(var (idxA, idxB, idxC) in prim.TriangleIndices)
+                    {
+                        var posA = prim.GetPosition(idxA, inst.Transform);
+                        var posB = prim.GetPosition(idxB, inst.Transform);
+                        var posC = prim.GetPosition(idxC, inst.Transform);
+
+                        yield return (posA, posB, posC, 0xb0b0b0);
+                    }
+                }
+            }
+
+            var worldTriangles = sceneInstance.DrawableInstances.SelectMany(item => evaluateTriangles(item));            
+
+            var scenePlot = new PlotlyScene();
+            scenePlot.AppendTriangles(worldTriangles, c=>c);
+
+            scenePlot
+                .ToHtml()
+                .AttachToCurrentTest("result.html", (content, finfo) => System.IO.File.WriteAllText(finfo.FullName, content));
+        }
+
+        [Test]
+        public static void TestMeshDecodingBounds()
+        {
+            var modelPath = TestFiles.GetSampleModelsPaths()
+                                .FirstOrDefault(item => item.Contains("BrainStem.glb"));
+
+            var model = Schema2.ModelRoot.Load(modelPath);
+
+            var (center, radius) = model.DefaultScene.EvaluateBoundingSphere(0.25f);           
             
             
-            // precission needs to be fairly low because calculation results
-            // in NetCore and NetFramework are amazingly different.
-            Assert.AreEqual(-0.07429607f, center.X, 0.0001f);
-            Assert.AreEqual( 0.8432209f, center.Y, 0.0001f);
-            Assert.AreEqual(-0.04639983f, center.Z, 0.0001f);
-            Assert.AreEqual( 2.528468f, radius, 0.0001f);
+            var sceneTemplate = SceneTemplate.Create(model.DefaultScene, false);
+            var sceneInstance = sceneTemplate.CreateInstance();
+            sceneInstance.SetAnimationFrame(0, 0.1f);
+
+            var vertices = sceneInstance.GetWorldVertices(model.LogicalMeshes.Decode()).ToList();
+
+            foreach(var p in vertices)
+            {
+                var d = (p - center).Length();
+                Assert.LessOrEqual(d, radius + 0.0001f);
+            }
         }
         }
 
 
     }
     }