Quellcode durchsuchen

improving accessors API
added polly scene evaluation test

Vicente Penades vor 7 Jahren
Ursprung
Commit
bd507f9884

+ 10 - 3
README.md

@@ -11,14 +11,21 @@ The current status of the library is preview alpha, but, for some cases it is pr
 - [x] Reading and writing *.glb* files.
 - [x] Logical Data Access.
 - [x] Visual Tree Access.
-- [x] Vertex and Index buffer decoding.
-- [ ] gltf.Extra field ; *Help Need*
+- [x] Vertex and Index buffer encoding/decoding.
 - [ ] Scene Evaluation.
+- [ ] GPU Evaluation.
 - [ ] Mesh Building utilities.
 - [ ] Animation utilities.
 - [ ] Material utilities
+- [ ] Skinning utilities
 - [ ] [Mikktspace](https://github.com/tcoppex/ext-mikktspace) Tangent space calculation. *Help Need*
 
+### TODO
+
+- gltf.Extra field support, right now it is ignored.
+- Matrix2x2 and Matrix3x3 support. This is due to missing types in System.Numerics.Vectors
+- Camera API
+
 ### Supported Extensions
 
 - [x] [KHR_materials_pbrSpecularGlossiness](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness)
@@ -30,7 +37,7 @@ The current status of the library is preview alpha, but, for some cases it is pr
 - [ ] [KHR_texture_transform](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform)
 - [ ] [MSFT_texture_dds](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds)
 - [ ] [MSFT_lod](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod)
-  - When this extension is used, the model's visual tree needs to be abstracted, which requires an extensive API rework.
+  - When this extension is used, the model's visual tree needs to be abstracted, which requires an extensive API rework, or a full API layer.
 - [ ] [MSFT_packing_normalRoughnessMetallic](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_packing_normalRoughnessMetallic)
 - [ ] [MSFT_packing_occlusionRoughnessMetallic](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_packing_occlusionRoughnessMetallic)
 - [ ] [AGI_articulations](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations)

+ 2 - 2
src/glTF2Sharp.DOM/Schema2/gltf.AccessorSparse.cs

@@ -46,7 +46,7 @@ namespace glTF2Sharp.Schema2
         {
             var srcBuffer = root.LogicalBufferViews[this._bufferView];
 
-            var accessor = srcBuffer.CreateIndexDecoder(this._byteOffset ?? 0, this._componentType);
+            var accessor = srcBuffer.CreateIndicesAccessor(this._byteOffset ?? 0, this._componentType);
 
             return index => (int)accessor[index];
         }
@@ -72,7 +72,7 @@ namespace glTF2Sharp.Schema2
         {
             var srcBuffer = root.LogicalBufferViews[this._bufferView];
 
-            return srcBuffer.CreateVertexDecoder(this._byteOffset ?? 0, dimensions, encoding, normalized);
+            return srcBuffer.CreateVertexAccessor(this._byteOffset ?? 0, dimensions, encoding, normalized);
         }
 
         public Action<int, IList<Byte>, int, int> CopyTo(ROOT root, ElementType et, ComponentType ct)

+ 61 - 51
src/glTF2Sharp.DOM/Schema2/gltf.Accessors.cs

@@ -10,7 +10,7 @@ namespace glTF2Sharp.Schema2
 
     // https://github.com/KhronosGroup/glTF/issues/827#issuecomment-277537204
 
-    [System.Diagnostics.DebuggerDisplay("Accessor[{LogicalIndex}] BufferView[{Buffer.LogicalIndex}][{ByteOffset}...] => 0 => {Dimensions}x{Encoding}x{Normalized} => [{Count}]")]
+    [System.Diagnostics.DebuggerDisplay("Accessor[{LogicalIndex}] BufferView[{SourceBufferView.LogicalIndex}][{ByteOffset}...] => 0 => {Dimensions}x{Encoding}x{Normalized} => [{Count}]")]
     public partial class Accessor
     {
         #region debug
@@ -88,11 +88,11 @@ namespace glTF2Sharp.Schema2
             _UpdateBounds();
         }
 
-        public Memory.Matrix4x4Accessor CastToMatrix4x4Array()
+        public Memory.Matrix4x4Accessor CastToMatrix4x4Accessor()
         {
             Guard.IsTrue(this.Dimensions == ElementType.MAT4, nameof(Dimensions));
 
-            return SourceBufferView.CreateMatrix4x4Decoder(this.ByteOffset, this._componentType, this.Normalized);
+            return SourceBufferView.CreateMatrix4x4Accessor(this.ByteOffset, this.Encoding, this.Normalized);
         }
 
         #endregion
@@ -119,15 +119,12 @@ namespace glTF2Sharp.Schema2
             _UpdateBounds();
         }
 
-        public UInt32[] TryGetIndices()
+        public Memory.IntegerAccessor CastToIndicesAccessor()
         {
-            Guard.IsTrue(this.Dimensions == ElementType.SCALAR, nameof(Dimensions));            
-
-            return SourceBufferView
-                .CreateIndexDecoder(this.ByteOffset, this.Encoding.ToIndex())
-                .ToArray();
+            Guard.IsTrue(this.Dimensions == ElementType.SCALAR, nameof(Dimensions));
+            return SourceBufferView.CreateIndicesAccessor(this.ByteOffset, this.Encoding.ToIndex());
         }
-
+        
         #endregion
 
         #region Vertex Buffer API
@@ -152,6 +149,30 @@ namespace glTF2Sharp.Schema2
             _UpdateBounds();
         }
 
+        public Memory.ScalarAccessor CastToScalarAccessor()
+        {
+            Guard.IsTrue(this.Dimensions == ElementType.SCALAR, nameof(Dimensions));
+            return SourceBufferView.CreateScalarAccessor(this.ByteOffset, this.Encoding, this.Normalized);
+        }
+
+        public Memory.Vector2Accessor CastToVector2Accessor()
+        {
+            Guard.IsTrue(this.Dimensions == ElementType.VEC2, nameof(Dimensions));
+            return SourceBufferView.CreateVector2Accessor(this.ByteOffset, this.Encoding, this.Normalized);
+        }
+
+        public Memory.Vector3Accessor CastToVector3Accessor()
+        {
+            Guard.IsTrue(this.Dimensions == ElementType.VEC3, nameof(Dimensions));
+            return SourceBufferView.CreateVector3Accessor(this.ByteOffset, this.Encoding, this.Normalized);
+        }
+
+        public Memory.Vector4Accessor CastToVector4Accessor()
+        {
+            Guard.IsTrue(this.Dimensions == ElementType.VEC4, nameof(Dimensions));
+            return SourceBufferView.CreateVector4Accessor(this.ByteOffset, this.Encoding, this.Normalized);
+        }
+
         public ArraySegment<Byte> TryGetVertexBytes(int vertexIdx)
         {
             if (_sparse != null) throw new InvalidOperationException("Can't be used on Acessors with Sparse Data");
@@ -178,9 +199,7 @@ namespace glTF2Sharp.Schema2
 
             if (count == 1)
             {
-                var accessor = SourceBufferView.CreateScalarDecoder(this.ByteOffset, this.Encoding, this.Normalized);
-
-                var minmax = Memory.AccessorsUtils.GetBounds(accessor);
+                var minmax = this.CastToScalarAccessor().GetBounds();
 
                 this._min[0] = minmax.Item1;
                 this._max[0] = minmax.Item2;
@@ -188,53 +207,44 @@ namespace glTF2Sharp.Schema2
 
             if (count == 2)
             {
-                var accessor = SourceBufferView.CreateVector2Decoder(this.ByteOffset, this.Encoding, this.Normalized);
+                var minmax = this.CastToVector2Accessor().GetBounds();
 
-                for (int i = 0; i < Count; ++i)
-                {
-                    var v2 = accessor[i];
+                this._min[0] = minmax.Item1.X;
+                this._max[0] = minmax.Item2.X;
 
-                    if (v2.X < this._min[0]) this._min[0] = v2.X;
-                    if (v2.X > this._max[0]) this._max[0] = v2.X;
-                    if (v2.Y < this._min[1]) this._min[1] = v2.Y;
-                    if (v2.Y > this._max[1]) this._max[1] = v2.Y;
-                }
+                this._min[1] = minmax.Item1.Y;
+                this._max[1] = minmax.Item2.Y;
             }
 
             if (count == 3)
             {
-                var accessor = SourceBufferView.CreateVector3Decoder(this.ByteOffset, this.Encoding, this.Normalized);
-
-                for (int i = 0; i < Count; ++i)
-                {
-                    var v3 = accessor[i];
-
-                    if (v3.X < this._min[0]) this._min[0] = v3.X;
-                    if (v3.X > this._max[0]) this._max[0] = v3.X;
-                    if (v3.Y < this._min[1]) this._min[1] = v3.Y;
-                    if (v3.Y > this._max[1]) this._max[1] = v3.Y;
-                    if (v3.Z < this._min[2]) this._min[2] = v3.Z;
-                    if (v3.Z > this._max[2]) this._max[2] = v3.Z;
-                }
+                var minmax = this.CastToVector3Accessor().GetBounds();
+
+                this._min[0] = minmax.Item1.X;
+                this._max[0] = minmax.Item2.X;
+
+                this._min[1] = minmax.Item1.Y;
+                this._max[1] = minmax.Item2.Y;
+
+                this._min[2] = minmax.Item1.Z;
+                this._max[2] = minmax.Item2.Z;
             }
 
             if (count == 4)
             {
-                var accessor = SourceBufferView.CreateVector4Decoder(this.ByteOffset, this.Encoding, this.Normalized);
-
-                for (int i = 0; i < Count; ++i)
-                {
-                    var v4 = accessor[i];
-
-                    if (v4.X < this._min[0]) this._min[0] = v4.X;
-                    if (v4.X > this._max[0]) this._max[0] = v4.X;
-                    if (v4.Y < this._min[1]) this._min[1] = v4.Y;
-                    if (v4.Y > this._max[1]) this._max[1] = v4.Y;
-                    if (v4.Z < this._min[2]) this._min[2] = v4.Z;
-                    if (v4.Z > this._max[2]) this._max[2] = v4.Z;
-                    if (v4.W < this._min[3]) this._min[3] = v4.W;
-                    if (v4.W > this._max[3]) this._max[3] = v4.W;
-                }
+                var minmax = this.CastToVector4Accessor().GetBounds();
+
+                this._min[0] = minmax.Item1.X;
+                this._max[0] = minmax.Item2.X;
+
+                this._min[1] = minmax.Item1.Y;
+                this._max[1] = minmax.Item2.Y;
+
+                this._min[2] = minmax.Item1.Z;
+                this._max[2] = minmax.Item2.Z;
+
+                this._min[3] = minmax.Item1.W;
+                this._max[3] = minmax.Item2.W;
             }
 
             if (count > 4)
@@ -257,7 +267,7 @@ namespace glTF2Sharp.Schema2
             if (SourceBufferView.DeviceBufferTarget == BufferMode.ARRAY_BUFFER)
             {
                 var len = Encoding.ByteLength() * Dimensions.DimCount();
-                if ((len & 3) != 0) exxx.Add(new ModelException(this, $"Expected length to be multiple of 4, found {len}"));
+                if (len > 0 && (len & 3) != 0) exxx.Add(new ModelException(this, $"Expected length to be multiple of 4, found {len}"));
             }
 
             if (SourceBufferView.DeviceBufferTarget == BufferMode.ELEMENT_ARRAY_BUFFER)

+ 10 - 8
src/glTF2Sharp.DOM/Schema2/gltf.BufferView.cs

@@ -8,6 +8,8 @@ namespace glTF2Sharp.Schema2
 {
     using BYTES = ArraySegment<Byte>;
 
+    using ENCODING = ComponentType;
+
     [System.Diagnostics.DebuggerTypeProxy(typeof(Debug._BufferDebugView))]
     public partial class BufferView
     {
@@ -100,14 +102,14 @@ namespace glTF2Sharp.Schema2
             return String.Join(" ", accessors.Select(item => item._DebuggerDisplay_TryIdentifyContent()));
         }
 
-        public Memory.IntegerAccessor CreateIndexDecoder(int byteOffset, IndexType encoding)
+        public Memory.IntegerAccessor CreateIndicesAccessor(int byteOffset, IndexType encoding)
         {
             Guard.IsTrue(this.ByteStride == 0,null, "bytestride must be zero");
 
             return new Memory.IntegerAccessor(this.Data.Slice(byteOffset), encoding);
         }
 
-        public Memory.IAccessor<Vector4> CreateVertexDecoder(int byteOffset, ElementType dimensions, ComponentType encoding, Boolean normalized)
+        public Memory.IAccessor<Vector4> CreateVertexAccessor(int byteOffset, ElementType dimensions, ENCODING encoding, Boolean normalized)
         {
             var srcData = this.Data.Slice(byteOffset);
 
@@ -121,32 +123,32 @@ namespace glTF2Sharp.Schema2
             }            
         }
 
-        public Memory.ScalarAccessor CreateScalarDecoder(int byteOffset, ComponentType encoding, Boolean normalized)
+        public Memory.ScalarAccessor CreateScalarAccessor(int byteOffset, ENCODING encoding, Boolean normalized)
         {
             return new Memory.ScalarAccessor(this.Data.Slice(byteOffset), this.ByteStride, encoding, normalized);            
         }
 
-        public Memory.Vector2Accessor CreateVector2Decoder(int byteOffset, ComponentType encoding, Boolean normalized)
+        public Memory.Vector2Accessor CreateVector2Accessor(int byteOffset, ENCODING encoding, Boolean normalized)
         {
             return new Memory.Vector2Accessor(this.Data.Slice(byteOffset), this.ByteStride, encoding, normalized);
         }
 
-        public Memory.Vector3Accessor CreateVector3Decoder(int byteOffset, ComponentType encoding, Boolean normalized)
+        public Memory.Vector3Accessor CreateVector3Accessor(int byteOffset, ENCODING encoding, Boolean normalized)
         {
             return new Memory.Vector3Accessor(this.Data.Slice(byteOffset), this.ByteStride, encoding, normalized);
         }
 
-        public Memory.Vector4Accessor CreateVector4Decoder(int byteOffset, ComponentType encoding, Boolean normalized)
+        public Memory.Vector4Accessor CreateVector4Accessor(int byteOffset, ENCODING encoding, Boolean normalized)
         {
             return new Memory.Vector4Accessor(this.Data.Slice(byteOffset), this.ByteStride, encoding, normalized);
         }
 
-        public Memory.QuaternionAccessor CreateQuaternionDecoder(int byteOffset, ComponentType encoding, Boolean normalized)
+        public Memory.QuaternionAccessor CreateQuaternionAccessor(int byteOffset, ENCODING encoding, Boolean normalized)
         {
             return new Memory.QuaternionAccessor(this.Data.Slice(byteOffset), this.ByteStride, encoding, normalized);
         }
 
-        public Memory.Matrix4x4Accessor CreateMatrix4x4Decoder(int byteOffset, ComponentType encoding, Boolean normalized)
+        public Memory.Matrix4x4Accessor CreateMatrix4x4Accessor(int byteOffset, ENCODING encoding, Boolean normalized)
         {
             return new Memory.Matrix4x4Accessor(this.Data.Slice(byteOffset), this.ByteStride, encoding, normalized);
         }

+ 12 - 0
src/glTF2Sharp.DOM/Schema2/gltf.Scene.cs

@@ -11,6 +11,8 @@ namespace glTF2Sharp.Schema2
         IEnumerable<Node> VisualChildren { get; }
 
         Node AddNode(string name);
+
+        Node FindNode(string name);
     }
 
     [System.Diagnostics.DebuggerDisplay("Node[{LogicalIndex}] {Name} SkinJoint:{IsSkinJoint} T:{LocalTransform.Translation.X} {LocalTransform.Translation.Y} {LocalTransform.Translation.Z}")]
@@ -173,6 +175,11 @@ namespace glTF2Sharp.Schema2
             this._children.Add(idx);
         }
 
+        public Node FindNode(string name)
+        {
+            return this.VisualChildren.FirstOrDefault(item => item.Name == name);
+        }
+
         // TODO: AddVisualChild must return a "NodeBuilder"
         // public Node AddVisualChild() { return LogicalParent._AddLogicalNode(_children); }
 
@@ -297,6 +304,11 @@ namespace glTF2Sharp.Schema2
             this._nodes.Add(idx);
         }
 
+        public Node FindNode(string name)
+        {
+            return this.VisualChildren.FirstOrDefault(item => item.Name == name);
+        }
+
         public override IEnumerable<Exception> Validate()
         {
             foreach (var ex in base.Validate()) yield return ex;

+ 1 - 1
src/glTF2Sharp.DOM/Schema2/gltf.Skin.cs

@@ -75,7 +75,7 @@ namespace glTF2Sharp.Schema2
 
             var node = this.LogicalParent.LogicalNodes[nodeIdx];
 
-            var matrix = (Matrix4x4)GetInverseBindMatricesAccessor().CastToMatrix4x4Array()[idx];
+            var matrix = (Matrix4x4)GetInverseBindMatricesAccessor().CastToMatrix4x4Accessor()[idx];
 
             return new KeyValuePair<Node, Matrix4x4>(node, matrix);
         }

+ 39 - 4
src/glTF2Sharp.Tests/Schema2/LoadModelTests.cs

@@ -74,8 +74,6 @@ namespace glTF2Sharp.Schema2
         {
             foreach (var f in TestFiles.GetSampleFilePaths())
             {
-                TestContext.Progress.WriteLine($"Loading {f}...");
-
                 var root = _LoadModel(f);
                 Assert.NotNull(root);
 
@@ -90,8 +88,6 @@ namespace glTF2Sharp.Schema2
         {
             foreach (var f in TestFiles.GetFilePathsWithSpecularGlossinessPBR())
             {
-                TestContext.Progress.WriteLine($"Loading {f}...");
-
                 var root = _LoadModel(f);
                 Assert.NotNull(root);
             }
@@ -99,12 +95,51 @@ namespace glTF2Sharp.Schema2
 
         #endregion
 
+        #region test polly model
+
+        [Test(Description ="Example of traversing the visual tree all the way to individual vertices and indices")]
+        public void TestLoadPolly()
+        {
+            var model = _LoadModel(TestFiles.GetPollyFilePath());            
+
+            var scene = model.DefaultScene;
+
+            var pollyNode = scene.FindNode("Polly_Display");            
+
+            var pollyPrimitive = pollyNode.Mesh.Primitives[0];
+
+            var pollyIndices = pollyPrimitive.IndexAccessor.CastToIndicesAccessor();
+            var pollyPositions = pollyPrimitive.VertexAccessors["POSITION"].CastToVector3Accessor();
+            var pollyNormals = pollyPrimitive.VertexAccessors["NORMAL"].CastToVector3Accessor();
+
+            for (int i=0; i < pollyIndices.Count; i+=3)
+            {
+                var a = (int)pollyIndices[i + 0];
+                var b = (int)pollyIndices[i + 1];
+                var c = (int)pollyIndices[i + 2];
+
+                var ap = pollyPositions[a];
+                var bp = pollyPositions[b];
+                var cp = pollyPositions[c];
+
+                var an = pollyNormals[a];
+                var bn = pollyNormals[b];
+                var cn = pollyNormals[c];
+
+                TestContext.WriteLine($"Triangle {ap} {an} {bp} {bn} {cp} {cn}");
+            }
+        }
+
+        #endregion
+
         #region model loading
 
         static ModelRoot _LoadModel(string filePath)
         {
             try
             {
+                TestContext.Progress.WriteLine($"Loading {filePath.ToShortDisplayPath()}");
+
                 return ModelRoot.Load(filePath);
             }
             catch(ExtensionException eex)            

+ 15 - 0
src/glTF2Sharp.Tests/TestUtils.cs

@@ -6,6 +6,21 @@ namespace glTF2Sharp
 {
     static class TestUtils
     {
+        public static string ToShortDisplayPath(this string path)
+        {
+            var dir = System.IO.Path.GetDirectoryName(path);
+            var fxt = System.IO.Path.GetFileName(path);
+
+            const int maxdir = 12;
+
+            if (dir.Length > maxdir)
+            {
+                dir = "..." + dir.Substring(dir.Length - maxdir);
+            }
+
+            return System.IO.Path.Combine(dir, fxt);
+        }
+
         public static string GetAttachmentPath(this NUnit.Framework.TestContext context, string fileName)
         {
             if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentNullException(nameof(fileName));