Browse Source

+ Numeric validation

Vicente Penades 6 years ago
parent
commit
3a9449ca3f

+ 10 - 2
src/SharpGLTF.Core/Memory/FloatingArrays.cs

@@ -195,13 +195,21 @@ namespace SharpGLTF.Memory
         public Single this[int index]
         {
             get => _Getter(index * _ByteStride);
-            set => _Setter(index * _ByteStride, value);
+            set
+            {
+                if (!value._IsReal()) throw new NotFiniteNumberException(nameof(value), value);
+                _Setter(index * _ByteStride, value);
+            }
         }
 
         public Single this[int rowIndex, int subIndex]
         {
             get => _Getter((rowIndex * _ByteStride) + (subIndex * _EncodedLen));
-            set => _Setter((rowIndex * _ByteStride) + (subIndex * _EncodedLen), value);
+            set
+            {
+                if (!value._IsReal()) throw new NotFiniteNumberException(nameof(value), value);
+                _Setter((rowIndex * _ByteStride) + (subIndex * _EncodedLen), value);
+            }
         }
 
         #endregion

+ 18 - 16
src/SharpGLTF.Core/Memory/MemoryAccessor.cs

@@ -99,13 +99,15 @@ namespace SharpGLTF.Memory
         {
             get
             {
-                if (this.ByteOffset < 0) return false;
                 if (this.ItemsCount < 0) return false;
+
+                if (this.ByteOffset < 0) return false;
+                if (!this.ByteOffset.IsMultipleOf(4)) return false;
+
                 if (this.ByteStride < 0) return false;
-                var blen = this.PaddedByteLength;
+                if (!this.ByteStride.IsMultipleOf(4)) return false;
 
-                if (this.ByteStride > 0 && this.ByteStride < blen) return false;
-                if ((this.ByteStride & 3) != 0) return false;
+                if (this.ByteStride > 0 && this.ByteStride < this.PaddedByteLength) return false;
 
                 return true;
             }
@@ -237,6 +239,18 @@ namespace SharpGLTF.Memory
 
         public void Fill(IReadOnlyList<UInt32> values) { values.CopyTo(AsIntegerArray()); }
 
+        public void Fill(IReadOnlyList<Single> values) { values.CopyTo(AsScalarArray()); }
+
+        public void Fill(IReadOnlyList<Vector2> values) { values.CopyTo(AsVector2Array()); }
+
+        public void Fill(IReadOnlyList<Vector3> values) { values.CopyTo(AsVector3Array()); }
+
+        public void Fill(IReadOnlyList<Vector4> values) { values.CopyTo(AsVector4Array()); }
+
+        public void Fill(IReadOnlyList<Quaternion> values) { values.CopyTo(AsQuaternionArray()); }
+
+        public void Fill(IReadOnlyList<Matrix4x4> values) { values.CopyTo(AsMatrix4x4Array()); }
+
         public IntegerArray AsIntegerArray()
         {
             Guard.IsTrue(_Attribute.IsValidIndexer, nameof(_Attribute));
@@ -244,8 +258,6 @@ namespace SharpGLTF.Memory
             return new IntegerArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.Encoding.ToIndex());
         }
 
-        public void Fill(IReadOnlyList<Single> values) { values.CopyTo(AsScalarArray()); }
-
         public ScalarArray AsScalarArray()
         {
             Guard.IsTrue(_Attribute.IsValidVertexAttribute, nameof(_Attribute));
@@ -253,8 +265,6 @@ namespace SharpGLTF.Memory
             return new ScalarArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
 
-        public void Fill(IReadOnlyList<Vector2> values) { values.CopyTo(AsVector2Array()); }
-
         public Vector2Array AsVector2Array()
         {
             Guard.IsTrue(_Attribute.IsValidVertexAttribute, nameof(_Attribute));
@@ -262,8 +272,6 @@ namespace SharpGLTF.Memory
             return new Vector2Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
 
-        public void Fill(IReadOnlyList<Vector3> values) { values.CopyTo(AsVector3Array()); }
-
         public Vector3Array AsVector3Array()
         {
             Guard.IsTrue(_Attribute.IsValidVertexAttribute, nameof(_Attribute));
@@ -271,8 +279,6 @@ namespace SharpGLTF.Memory
             return new Vector3Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
 
-        public void Fill(IReadOnlyList<Vector4> values) { values.CopyTo(AsVector4Array()); }
-
         public Vector4Array AsVector4Array()
         {
             Guard.IsTrue(_Attribute.IsValidVertexAttribute, nameof(_Attribute));
@@ -287,8 +293,6 @@ namespace SharpGLTF.Memory
             return new ColorArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Dimensions.DimCount(), _Attribute.Encoding, _Attribute.Normalized);
         }
 
-        public void Fill(IReadOnlyList<Quaternion> values) { values.CopyTo(AsQuaternionArray()); }
-
         public QuaternionArray AsQuaternionArray()
         {
             Guard.IsTrue(_Attribute.IsValidVertexAttribute, nameof(_Attribute));
@@ -296,8 +300,6 @@ namespace SharpGLTF.Memory
             return new QuaternionArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
 
-        public void Fill(IReadOnlyList<Matrix4x4> values) { values.CopyTo(AsMatrix4x4Array()); }
-
         public Matrix4x4Array AsMatrix4x4Array()
         {
             Guard.IsTrue(_Attribute.IsValidVertexAttribute, nameof(_Attribute));

+ 34 - 3
src/SharpGLTF.Core/Schema2/gltf.Accessors.cs

@@ -300,7 +300,7 @@ namespace SharpGLTF.Schema2
 
             // https://github.com/KhronosGroup/glTF-Validator/issues/79
 
-            var dimensions = this._type.DimCount();
+            var dimensions = this.Dimensions.DimCount();
 
             for (int i = 0; i < dimensions; ++i)
             {
@@ -345,15 +345,46 @@ namespace SharpGLTF.Schema2
                 var len = Encoding.ByteLength() * Dimensions.DimCount();
                 if (len != 1 && len != 2 && len != 4) result.Add(new EXCEPTION(this, $"Expected length to be 1, 2 or 4, found {len}"));
             }
+        }
+
+        internal void ValidateBounds(IList<Exception> result)
+        {
+            if (_min.Count == 0 && _max.Count == 0) return;
 
-            // validate bounds
+            var dimensions = this.Dimensions.DimCount();
 
-            if (_min.Count != _max.Count) { result.Add(new EXCEPTION(this, "min and max length mismatch")); return; }
+            if (_min.Count != dimensions) { result.Add(new EXCEPTION(this, $"min bounds length mismatch; expected {dimensions} but found {_min.Count}")); return; }
+            if (_max.Count != dimensions) { result.Add(new EXCEPTION(this, $"max bounds length mismatch; expected {dimensions} but found {_max.Count}")); return; }
 
             for (int i = 0; i < _min.Count; ++i)
             {
                 if (_min[i] > _max[i]) result.Add(new EXCEPTION(this, $"min[{i}] is larger than max[{i}]"));
             }
+
+            if (this.Encoding != EncodingType.FLOAT) return;
+
+            var current = new float[dimensions];
+            var minimum = this._min.ConvertAll(item => (float)item);
+            var maximum = this._max.ConvertAll(item => (float)item);
+
+            var array = new MultiArray(this.SourceBufferView.Content, this.ByteOffset, this.Count, this.SourceBufferView.ByteStride, dimensions, this.Encoding, false);
+
+            for (int i = 0; i < array.Count; ++i)
+            {
+                array.CopyItemTo(i, current);
+
+                for (int j = 0; j < current.Length; ++j)
+                {
+                    var v = current[j];
+
+                    if (!v._IsReal()) result.Add(new EXCEPTION(this, $"Item[{j}][{i}] is not a finite number: {v}"));
+
+                    var min = minimum[j];
+                    var max = maximum[j];
+
+                    if (v < min || v > max) result.Add(new EXCEPTION(this, $"Item[{j}][{i}] is out of bounds. {min} <= {v} <= {max}"));
+                }
+            }
         }
 
         #endregion

+ 7 - 1
src/SharpGLTF.Core/Schema2/gltf.Root.cs

@@ -169,7 +169,13 @@ namespace SharpGLTF.Schema2
             // 4th check contents
             foreach (var s in _scenes) s.Validate(result);
             foreach (var n in _nodes) n.Validate(result);
-            foreach (var a in _accessors) a.Validate(result);
+
+            foreach (var a in _accessors)
+            {
+                a.Validate(result);
+                a.ValidateBounds(result);
+            }
+
             foreach (var m in _meshes) m.Validate(result);
             foreach (var s in _skins) s.Validate(result);
         }

+ 3 - 3
src/SharpGLTF.Core/Schema2/gltf.Serialization.cs

@@ -395,9 +395,6 @@ namespace SharpGLTF.Schema2
                 reader.Read();
                 root.Deserialize(reader);
 
-                var ex = root.Validate().FirstOrDefault();
-                if (ex != null) throw ex;
-
                 foreach (var buffer in root._buffers)
                 {
                     buffer._ResolveUri(settings.FileReader);
@@ -408,6 +405,9 @@ namespace SharpGLTF.Schema2
                     image._ResolveUri(settings.FileReader);
                 }
 
+                var ex = root.Validate().FirstOrDefault();
+                if (ex != null) throw ex;
+
                 return root;
             }
         }

+ 41 - 0
tests/SharpGLTF.Tests/Memory/MemoryAccessorTests.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using NUnit.Framework;
+
+namespace SharpGLTF.Memory
+{
+    [TestFixture]
+    public class MemoryAccessorTests
+    {
+        [Test]
+        public void TestCreateInterleaved1()
+        {
+            var pos = MemoryAccessInfo.CreateDefaultElement("POSITION");
+            var nrm = MemoryAccessInfo.CreateDefaultElement("NORMAL");
+
+            var attributes = new[] { pos, nrm };
+
+            const int baseOffset = 8;
+
+            var byteStride = MemoryAccessInfo.SetInterleavedInfo(attributes, baseOffset, 5);
+
+            pos = attributes[0];
+            nrm = attributes[1];
+
+            Assert.AreEqual(24, byteStride);
+
+            Assert.AreEqual(baseOffset + 0, pos.ByteOffset);
+            Assert.AreEqual(24, pos.ByteStride);
+            Assert.AreEqual(5, pos.ItemsCount);
+
+            Assert.AreEqual(baseOffset + 12, nrm.ByteOffset);
+            Assert.AreEqual(24, nrm.ByteStride);
+            Assert.AreEqual(5, nrm.ItemsCount);
+
+            Assert.IsTrue(pos.IsValidVertexAttribute);
+            Assert.IsTrue(nrm.IsValidVertexAttribute);
+        }
+    }
+}