Browse Source

refactored memory array Accessors
refactored validation

Vicente Penades 6 years ago
parent
commit
16f33902ed

+ 11 - 2
src/SharpGLTF.Core/IO/JsonSerializable.cs

@@ -12,9 +12,17 @@ namespace SharpGLTF.IO
     {
     {
         #region validation
         #region validation
 
 
-        public virtual IEnumerable<Exception> Validate()
+        public IEnumerable<Exception> Validate()
+        {
+            var result = new List<Exception>();
+
+            Validate(result);
+
+            return result;
+        }
+
+        internal virtual void Validate(IList<Exception> result)
         {
         {
-            yield break;
         }
         }
 
 
         #endregion
         #endregion
@@ -213,6 +221,7 @@ namespace SharpGLTF.IO
                     writer.WritePropertyName(key.ToString());
                     writer.WritePropertyName(key.ToString());
                     _Serialize(writer, dict[key]);
                     _Serialize(writer, dict[key]);
                 }
                 }
+
                 writer.WriteEndObject();
                 writer.WriteEndObject();
                 return;
                 return;
             }
             }

+ 10 - 227
src/SharpGLTF.Core/Memory/EncodedArrays.cs

@@ -7,22 +7,11 @@ using System.Text;
 
 
 namespace SharpGLTF.Memory
 namespace SharpGLTF.Memory
 {
 {
-    public interface IEncodedArray<T> : IReadOnlyCollection<T>
-    {
-        T this[int index] { get; set; }
-
-        void CopyTo(ArraySegment<T> dst);
-
-        // void CopyTo(IEncodedArray<T> dst);
-
-        (T, T) GetBounds();
-    }
-
     struct EncodedArrayEnumerator<T> : IEnumerator<T>
     struct EncodedArrayEnumerator<T> : IEnumerator<T>
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        public EncodedArrayEnumerator(IEncodedArray<T> accessor)
+        public EncodedArrayEnumerator(IReadOnlyList<T> accessor)
         {
         {
             this._Accessor = accessor;
             this._Accessor = accessor;
             this._Count = accessor.Count;
             this._Count = accessor.Count;
@@ -37,7 +26,7 @@ namespace SharpGLTF.Memory
 
 
         #region data
         #region data
 
 
-        private readonly IEncodedArray<T> _Accessor;
+        private readonly IReadOnlyList<T> _Accessor;
         private readonly int _Count;
         private readonly int _Count;
         private int _Index;
         private int _Index;
 
 
@@ -63,21 +52,9 @@ namespace SharpGLTF.Memory
         #endregion
         #endregion
     }
     }
 
 
-    public static class EncodedArrayUtils
+    static class EncodedArrayUtils
     {
     {
-        public static IntegerArray IndicesRange(int start, int count)
-        {
-            var array = new IntegerArray( new ArraySegment<Byte>(new Byte[count * 4]), Schema2.IndexEncodingType.UNSIGNED_INT);
-
-            for (int i = 0; i < count; ++i)
-            {
-                array[i] = (UInt32)(start + i);
-            }
-
-            return array;
-        }
-
-        public static void FillFrom(this IEncodedArray<UInt32> dst, int dstIndex, IEnumerable<Int32> src)
+        public static void FillFrom(this IList<UInt32> dst, int dstIndex, IEnumerable<Int32> src)
         {
         {
             using (var ator = src.GetEnumerator())
             using (var ator = src.GetEnumerator())
             {
             {
@@ -88,7 +65,7 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public static void FillFrom<T>(this IEncodedArray<T> dst, int dstIndex, IEnumerable<T> src)
+        public static void FillFrom<T>(this IList<T> dst, int dstIndex, IEnumerable<T> src)
         {
         {
             using (var ator = src.GetEnumerator())
             using (var ator = src.GetEnumerator())
             {
             {
@@ -99,16 +76,7 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public static void FillFrom<T>(this IEncodedArray<T> dst, int dstIndex, params T[] src)
-            where T : unmanaged
-        {
-            for (int i = 0; i < src.Length; ++i)
-            {
-                dst[dstIndex + i] = src[i];
-            }
-        }
-
-        public static void CopyTo<T>(IEncodedArray<T> src, IEncodedArray<T> dst, int dstOffset = 0)
+        public static void CopyTo<T>(this IReadOnlyList<T> src, IList<T> dst, int dstOffset = 0)
         {
         {
             for (int i = 0; i < src.Count; ++i)
             for (int i = 0; i < src.Count; ++i)
             {
             {
@@ -116,203 +84,18 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public static void CopyTo<T>(T[] src, IEncodedArray<T> dst, int dstOffset = 0)
-        {
-            for (int i = 0; i < src.Length; ++i)
-            {
-                dst[i + dstOffset] = src[i];
-            }
-        }
-
-        public static void Copy<T>(IEncodedArray<T> src, T[] dst)
+        public static int FirstIndexOf<T>(this IReadOnlyList<T> src, T value)
         {
         {
-            Copy<T>(src, new ArraySegment<T>(dst));
-        }
+            var comparer = EqualityComparer<T>.Default;
 
 
-        public static void Copy<T>(IEncodedArray<T> src, ArraySegment<T> dst)
-        {
             var c = src.Count;
             var c = src.Count;
-            for (int i = 0; i < c; ++i) dst.Array[dst.Offset + i] = src[i];
-        }
 
 
-        public static (Single, Single) GetBounds(ScalarArray accesor)
-        {
-            var min = Single.MaxValue;
-            var max = Single.MinValue;
-
-            int c = accesor.Count;
-            for (int i = 0; i < c; ++i)
-            {
-                var v = accesor[i];
-                min = Math.Min(min, v);
-                max = Math.Max(max, v);
-            }
-
-            return (min, max);
-        }
-
-        public static (Vector2, Vector2) GetBounds(Vector2Array accesor)
-        {
-            var min = new Vector2(Single.MaxValue);
-            var max = new Vector2(Single.MinValue);
-
-            int c = accesor.Count;
             for (int i = 0; i < c; ++i)
             for (int i = 0; i < c; ++i)
             {
             {
-                var v = accesor[i];
-                min = Vector2.Min(min, v);
-                max = Vector2.Max(max, v);
+                if (comparer.Equals(src[i], value)) return i;
             }
             }
 
 
-            return (min, max);
+            return -1;
         }
         }
-
-        public static (Vector3, Vector3) GetBounds(Vector3Array accesor)
-        {
-            var min = new Vector3(Single.MaxValue);
-            var max = new Vector3(Single.MinValue);
-
-            int c = accesor.Count;
-            for (int i = 0; i < c; ++i)
-            {
-                var v = accesor[i];
-                min = Vector3.Min(min, v);
-                max = Vector3.Max(max, v);
-            }
-
-            return (min, max);
-        }
-
-        public static (Vector4, Vector4) GetBounds(IEncodedArray<Vector4> accesor)
-        {
-            var min = new Vector4(Single.MaxValue);
-            var max = new Vector4(Single.MinValue);
-
-            int c = accesor.Count;
-            for (int i = 0; i < c; ++i)
-            {
-                var v = accesor[i];
-                min = Vector4.Min(min, v);
-                max = Vector4.Max(max, v);
-            }
-
-            return (min, max);
-        }
-    }
-
-    /// <summary>
-    /// Wraps a collection of Scalar values and exposes it as a collection of Vector4 values
-    /// </summary>
-    struct _MapScalarToVector4 : IEncodedArray<Vector4>
-    {
-        public _MapScalarToVector4(ScalarArray source)
-        {
-            _Accessor = source;
-        }
-
-        private ScalarArray _Accessor;
-
-        public int Count => _Accessor.Count;
-
-        public Vector4 this[int index]
-        {
-            get => new Vector4(_Accessor[index], 0, 0, 0);
-            set => _Accessor[index] = value.X;
-        }
-
-        public void CopyTo(ArraySegment<Vector4> dst) { EncodedArrayUtils.Copy(this, dst); }
-
-        public IEnumerator<Vector4> GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
-
-        IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
-
-        public (Vector4, Vector4) GetBounds() { return EncodedArrayUtils.GetBounds(this); }
-    }
-
-    /// <summary>
-    /// Wraps a collection of Vector2 values and exposes it as a collection of Vector4 values
-    /// </summary>
-    struct _MapVector2ToVector4 : IEncodedArray<Vector4>
-    {
-        public _MapVector2ToVector4(Vector2Array source)
-        {
-            _Accessor = source;
-        }
-
-        private Vector2Array _Accessor;
-
-        public int Count => _Accessor.Count;
-
-        public Vector4 this[int index]
-        {
-            get { var v = _Accessor[index]; return new Vector4(v.X, v.Y, 0, 0); }
-            set => _Accessor[index] = new Vector2(value.X, value.Y);
-        }
-
-        public void CopyTo(ArraySegment<Vector4> dst) { EncodedArrayUtils.Copy(this, dst); }
-
-        public IEnumerator<Vector4> GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
-
-        IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
-
-        public (Vector4, Vector4) GetBounds() { return EncodedArrayUtils.GetBounds(this); }
-    }
-
-    /// <summary>
-    /// Wraps a collection of Vector3 values and exposes it as a collection of Vector4 values
-    /// </summary>
-    struct _MapVector3ToVector4 : IEncodedArray<Vector4>
-    {
-        public _MapVector3ToVector4(Vector3Array source)
-        {
-            _Accessor = source;
-        }
-
-        private Vector3Array _Accessor;
-
-        public int Count => _Accessor.Count;
-
-        public Vector4 this[int index]
-        {
-            get { var v = _Accessor[index]; return new Vector4(v.X, v.Y, v.Z, 0); }
-            set => _Accessor[index] = new Vector3(value.X, value.Y, value.Z);
-        }
-
-        public void CopyTo(ArraySegment<Vector4> dst) { EncodedArrayUtils.Copy(this, dst); }
-
-        public IEnumerator<Vector4> GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
-
-        IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
-
-        public (Vector4, Vector4) GetBounds() { return EncodedArrayUtils.GetBounds(this); }
-    }
-
-    /// <summary>
-    /// Wraps a collection of Quaternion values and exposes it as a collection of Vector4 values
-    /// </summary>
-    struct _MapQuaternionToVector4 : IEncodedArray<Vector4>
-    {
-        public _MapQuaternionToVector4(QuaternionArray source)
-        {
-            _Accessor = source;
-        }
-
-        private QuaternionArray _Accessor;
-
-        public int Count => _Accessor.Count;
-
-        public Vector4 this[int index]
-        {
-            get { var v = _Accessor[index]; return new Vector4(v.X, v.Y, v.Z, v.W); }
-            set => _Accessor[index] = new Quaternion(value.X, value.Y, value.Z, value.W);
-        }
-
-        public void CopyTo(ArraySegment<Vector4> dst) { EncodedArrayUtils.Copy(this, dst); }
-
-        public IEnumerator<Vector4> GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
-
-        IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
-
-        public (Vector4, Vector4) GetBounds() { return EncodedArrayUtils.GetBounds(this); }
     }
     }
 }
 }

+ 161 - 39
src/SharpGLTF.Core/Memory/FloatingArrays.cs

@@ -12,7 +12,7 @@ namespace SharpGLTF.Memory
     using ENCODING = Schema2.EncodingType;
     using ENCODING = Schema2.EncodingType;
 
 
     /// <summary>
     /// <summary>
-    /// Wraps a <see cref="ArraySegment{Byte}"/> containing encoded <see cref="Single"/> values
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of strided <see cref="Single"/> values.
     /// </summary>
     /// </summary>
     struct FloatingAccessor
     struct FloatingAccessor
     {
     {
@@ -211,7 +211,7 @@ namespace SharpGLTF.Memory
     /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Single"/> values
     /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Single"/> values
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Scalar Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("Scalar Accessor {Count}")]
-    public struct ScalarArray : IEncodedArray<Single>
+    public struct ScalarArray : IList<Single>, IReadOnlyList<Single>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -277,39 +277,85 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Count => _Accessor.Count;
         public int Count => _Accessor.Count;
 
 
+        bool ICollection<Single>.IsReadOnly => false;
+
         public Single this[int index]
         public Single this[int index]
         {
         {
             get => _Accessor[index, 0];
             get => _Accessor[index, 0];
             set => _Accessor[index, 0] = value;
             set => _Accessor[index, 0] = value;
         }
         }
 
 
-        public void CopyTo(ArraySegment<Single> dst) { EncodedArrayUtils.Copy<Single>(this, dst); }
-
         public IEnumerator<Single> GetEnumerator() { return new EncodedArrayEnumerator<Single>(this); }
         public IEnumerator<Single> GetEnumerator() { return new EncodedArrayEnumerator<Single>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Single>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Single>(this); }
 
 
-        public (Single, Single) GetBounds() { return EncodedArrayUtils.GetBounds(this); }
+        public bool Contains(Single item) { return IndexOf(item) >= 0; }
+
+        public int IndexOf(Single item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(Single[] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<Single>.Insert(int index, Single item) { throw new NotSupportedException(); }
+
+        void IList<Single>.RemoveAt(int index) { throw new NotSupportedException(); }
 
 
-        public IEncodedArray<Vector4> AsVector4() { return new _MapScalarToVector4(this); }
+        void ICollection<Single>.Add(Single item) { throw new NotSupportedException(); }
+
+        void ICollection<Single>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<Single>.Remove(Single item) { throw new NotSupportedException(); }
 
 
         #endregion
         #endregion
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Vector2"/> values
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Vector2"/> values.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Vector2 Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("Vector2 Accessor {Count}")]
-    public struct Vector2Array : IEncodedArray<Vector2>
+    public struct Vector2Array : IList<Vector2>, IReadOnlyList<Vector2>
     {
     {
         #region constructors
         #region constructors
 
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Vector2Array"/> struct.
+        /// </summary>
+        /// <param name="source">The array range to wrap.</param>
+        /// <param name="byteOffset">The zero-based index of the first <see cref="Byte"/> in <paramref name="source"/>.</param>
+        /// <param name="itemsCount">>The number of <see cref="Vector2"/> items in <paramref name="source"/>.</param>
+        /// <param name="byteStride">
+        /// The byte stride between elements.
+        /// If the value is zero, the size of the item is used instead.
+        /// </param>
+        /// <param name="encoding">A value of <see cref="ENCODING"/>.</param>
+        /// <param name="normalized">True if values are normalized.</param>
         public Vector2Array(Byte[] source, int byteOffset, int itemsCount, int byteStride, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
         public Vector2Array(Byte[] source, int byteOffset, int itemsCount, int byteStride, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(new BYTES(source), byteOffset, itemsCount, byteStride, encoding, normalized) { }
             : this(new BYTES(source), byteOffset, itemsCount, byteStride, encoding, normalized) { }
 
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Vector2Array"/> struct.
+        /// </summary>
+        /// <param name="source">The array range to wrap.</param>
+        /// <param name="byteStride">
+        /// The byte stride between elements.
+        /// If the value is zero, the size of the item is used instead.
+        /// </param>
+        /// <param name="encoding">A value of <see cref="ENCODING"/>.</param>
+        /// <param name="normalized">True if values are normalized.</param>
         public Vector2Array(BYTES source, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
         public Vector2Array(BYTES source, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(source, 0, int.MaxValue, byteStride, encoding, normalized) { }
             : this(source, 0, int.MaxValue, byteStride, encoding, normalized) { }
 
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Vector2Array"/> struct.
+        /// </summary>
+        /// <param name="source">The array range to wrap.</param>
+        /// <param name="byteOffset">The zero-based index of the first <see cref="Byte"/> in <paramref name="source"/>.</param>
+        /// <param name="itemsCount">>The number of <see cref="Vector2"/> items in <paramref name="source"/>.</param>
+        /// <param name="byteStride">
+        /// The byte stride between elements.
+        /// If the value is zero, the size of the item is used instead.
+        /// </param>
+        /// <param name="encoding">A value of <see cref="ENCODING"/>.</param>
+        /// <param name="normalized">True if values are normalized.</param>
         public Vector2Array(BYTES source, int byteOffset, int itemsCount, int byteStride, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
         public Vector2Array(BYTES source, int byteOffset, int itemsCount, int byteStride, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
         {
         {
             _Accessor = new FloatingAccessor(source, byteOffset, itemsCount, byteStride, 2, encoding, normalized);
             _Accessor = new FloatingAccessor(source, byteOffset, itemsCount, byteStride, 2, encoding, normalized);
@@ -332,6 +378,8 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Count => _Accessor.Count;
         public int Count => _Accessor.Count;
 
 
+        bool ICollection<Vector2>.IsReadOnly => false;
+
         public Vector2 this[int index]
         public Vector2 this[int index]
         {
         {
             get
             get
@@ -346,24 +394,34 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public void CopyTo(ArraySegment<Vector2> dst) { EncodedArrayUtils.Copy<Vector2>(this, dst); }
-
         public IEnumerator<Vector2> GetEnumerator() { return new EncodedArrayEnumerator<Vector2>(this); }
         public IEnumerator<Vector2> GetEnumerator() { return new EncodedArrayEnumerator<Vector2>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector2>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector2>(this); }
 
 
-        public (Vector2, Vector2) GetBounds() { return EncodedArrayUtils.GetBounds(this); }
+        public bool Contains(Vector2 item) { return IndexOf(item) >= 0; }
+
+        public int IndexOf(Vector2 item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(Vector2[] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<Vector2>.Insert(int index, Vector2 item) { throw new NotSupportedException(); }
+
+        void IList<Vector2>.RemoveAt(int index) { throw new NotSupportedException(); }
+
+        void ICollection<Vector2>.Add(Vector2 item) { throw new NotSupportedException(); }
 
 
-        public IEncodedArray<Vector4> AsVector4() { return new _MapVector2ToVector4(this); }
+        void ICollection<Vector2>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<Vector2>.Remove(Vector2 item) { throw new NotSupportedException(); }
 
 
         #endregion
         #endregion
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Vector3"/> values
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Vector3"/> values.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Vector3 Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("Vector3 Accessor {Count}")]
-    public struct Vector3Array : IEncodedArray<Vector3>
+    public struct Vector3Array : IList<Vector3>, IReadOnlyList<Vector3>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -429,6 +487,8 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Count => _Accessor.Count;
         public int Count => _Accessor.Count;
 
 
+        bool ICollection<Vector3>.IsReadOnly => false;
+
         public Vector3 this[int index]
         public Vector3 this[int index]
         {
         {
             get
             get
@@ -444,24 +504,34 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public void CopyTo(ArraySegment<Vector3> dst) { EncodedArrayUtils.Copy<Vector3>(this, dst); }
-
         public IEnumerator<Vector3> GetEnumerator() { return new EncodedArrayEnumerator<Vector3>(this); }
         public IEnumerator<Vector3> GetEnumerator() { return new EncodedArrayEnumerator<Vector3>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector3>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector3>(this); }
 
 
-        public (Vector3, Vector3) GetBounds() { return EncodedArrayUtils.GetBounds(this); }
+        public bool Contains(Vector3 item) { return IndexOf(item) >= 0; }
+
+        public int IndexOf(Vector3 item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(Vector3[] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<Vector3>.Insert(int index, Vector3 item) { throw new NotSupportedException(); }
 
 
-        public IEncodedArray<Vector4> AsVector4() { return new _MapVector3ToVector4(this); }
+        void IList<Vector3>.RemoveAt(int index) { throw new NotSupportedException(); }
+
+        void ICollection<Vector3>.Add(Vector3 item) { throw new NotSupportedException(); }
+
+        void ICollection<Vector3>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<Vector3>.Remove(Vector3 item) { throw new NotSupportedException(); }
 
 
         #endregion
         #endregion
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Vector4"/> values
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Vector4"/> values.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Vector4 Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("Vector4 Accessor {Count}")]
-    public struct Vector4Array : IEncodedArray<Vector4>
+    public struct Vector4Array : IList<Vector4>, IReadOnlyList<Vector4>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -493,6 +563,8 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Count => _Accessor.Count;
         public int Count => _Accessor.Count;
 
 
+        bool ICollection<Vector4>.IsReadOnly => false;
+
         public Vector4 this[int index]
         public Vector4 this[int index]
         {
         {
             get
             get
@@ -509,24 +581,34 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public void CopyTo(ArraySegment<Vector4> dst) { EncodedArrayUtils.Copy<Vector4>(this, dst); }
-
         public IEnumerator<Vector4> GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
         public IEnumerator<Vector4> GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Vector4>(this); }
 
 
-        public (Vector4, Vector4) GetBounds() { return EncodedArrayUtils.GetBounds(this); }
+        public bool Contains(Vector4 item) { return IndexOf(item) >= 0; }
 
 
-        public IEncodedArray<Vector4> AsVector4() { return this; }
+        public int IndexOf(Vector4 item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(Vector4[] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<Vector4>.Insert(int index, Vector4 item) { throw new NotSupportedException(); }
+
+        void IList<Vector4>.RemoveAt(int index) { throw new NotSupportedException(); }
+
+        void ICollection<Vector4>.Add(Vector4 item) { throw new NotSupportedException(); }
+
+        void ICollection<Vector4>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<Vector4>.Remove(Vector4 item) { throw new NotSupportedException(); }
 
 
         #endregion
         #endregion
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Quaternion"/> values
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Quaternion"/> values.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Quaternion Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("Quaternion Accessor {Count}")]
-    public struct QuaternionArray : IEncodedArray<Quaternion>
+    public struct QuaternionArray : IList<Quaternion>, IReadOnlyList<Quaternion>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -558,6 +640,8 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Count => _Accessor.Count;
         public int Count => _Accessor.Count;
 
 
+        bool ICollection<Quaternion>.IsReadOnly => false;
+
         public Quaternion this[int index]
         public Quaternion this[int index]
         {
         {
             get
             get
@@ -574,24 +658,34 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public void CopyTo(ArraySegment<Quaternion> dst) { EncodedArrayUtils.Copy<Quaternion>(this, dst); }
-
         public IEnumerator<Quaternion> GetEnumerator() { return new EncodedArrayEnumerator<Quaternion>(this); }
         public IEnumerator<Quaternion> GetEnumerator() { return new EncodedArrayEnumerator<Quaternion>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Quaternion>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Quaternion>(this); }
 
 
-        public (Quaternion, Quaternion) GetBounds() { throw new NotImplementedException(); }
+        public bool Contains(Quaternion item) { return IndexOf(item) >= 0; }
+
+        public int IndexOf(Quaternion item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(Quaternion[] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<Quaternion>.Insert(int index, Quaternion item) { throw new NotSupportedException(); }
 
 
-        public IEncodedArray<Vector4> AsVector4() { return new _MapQuaternionToVector4(this); }
+        void IList<Quaternion>.RemoveAt(int index) { throw new NotSupportedException(); }
+
+        void ICollection<Quaternion>.Add(Quaternion item) { throw new NotSupportedException(); }
+
+        void ICollection<Quaternion>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<Quaternion>.Remove(Quaternion item) { throw new NotSupportedException(); }
 
 
         #endregion
         #endregion
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Matrix4x4"/> values
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="Matrix4x4"/> values.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("MAtrix4x4 Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("MAtrix4x4 Accessor {Count}")]
-    public struct Matrix4x4Array : IEncodedArray<Matrix4x4>
+    public struct Matrix4x4Array : IList<Matrix4x4>, IReadOnlyList<Matrix4x4>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -623,6 +717,8 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Count => _Accessor.Count;
         public int Count => _Accessor.Count;
 
 
+        bool ICollection<Matrix4x4>.IsReadOnly => false;
+
         public Matrix4x4 this[int index]
         public Matrix4x4 this[int index]
         {
         {
             get
             get
@@ -657,13 +753,25 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public void CopyTo(ArraySegment<Matrix4x4> dst) { EncodedArrayUtils.Copy<Matrix4x4>(this, dst); }
-
         public IEnumerator<Matrix4x4> GetEnumerator() { return new EncodedArrayEnumerator<Matrix4x4>(this); }
         public IEnumerator<Matrix4x4> GetEnumerator() { return new EncodedArrayEnumerator<Matrix4x4>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Matrix4x4>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Matrix4x4>(this); }
 
 
-        public (Matrix4x4, Matrix4x4) GetBounds() { throw new NotImplementedException(); }
+        public bool Contains(Matrix4x4 item) { return IndexOf(item) >= 0; }
+
+        public int IndexOf(Matrix4x4 item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(Matrix4x4[] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<Matrix4x4>.Insert(int index, Matrix4x4 item) { throw new NotSupportedException(); }
+
+        void IList<Matrix4x4>.RemoveAt(int index) { throw new NotSupportedException(); }
+
+        void ICollection<Matrix4x4>.Add(Matrix4x4 item) { throw new NotSupportedException(); }
+
+        void ICollection<Matrix4x4>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<Matrix4x4>.Remove(Matrix4x4 item) { throw new NotSupportedException(); }
 
 
         #endregion
         #endregion
     }
     }
@@ -672,7 +780,7 @@ namespace SharpGLTF.Memory
     /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="float"/> array values.
     /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="float"/> array values.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("MultiArray Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("MultiArray Accessor {Count}")]
-    public struct MultiArray : IEncodedArray<Single[]>
+    public struct MultiArray : IList<Single[]>, IReadOnlyList<Single[]>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -708,6 +816,8 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Dimensions => _Dimensions;
         public int Dimensions => _Dimensions;
 
 
+        bool ICollection<Single[]>.IsReadOnly => false;
+
         public Single[] this[int index]
         public Single[] this[int index]
         {
         {
             get
             get
@@ -736,13 +846,25 @@ namespace SharpGLTF.Memory
             for (int i = 0; i < count; ++i) dstItem[i] = _Accessor[index, i];
             for (int i = 0; i < count; ++i) dstItem[i] = _Accessor[index, i];
         }
         }
 
 
-        public void CopyTo(ArraySegment<Single[]> dst) { EncodedArrayUtils.Copy<Single[]>(this, dst); }
-
         public IEnumerator<Single[]> GetEnumerator() { return new EncodedArrayEnumerator<Single[]>(this); }
         public IEnumerator<Single[]> GetEnumerator() { return new EncodedArrayEnumerator<Single[]>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Single[]>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<Single[]>(this); }
 
 
-        public (Single[], Single[]) GetBounds() { throw new NotImplementedException(); }
+        public bool Contains(Single[] item) { return IndexOf(item) >= 0; }
+
+        public int IndexOf(Single[] item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(Single[][] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<Single[]>.Insert(int index, Single[] item) { throw new NotSupportedException(); }
+
+        void IList<Single[]>.RemoveAt(int index) { throw new NotSupportedException(); }
+
+        void ICollection<Single[]>.Add(Single[] item) { throw new NotSupportedException(); }
+
+        void ICollection<Single[]>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<Single[]>.Remove(Single[] item) { throw new NotSupportedException(); }
 
 
         #endregion
         #endregion
     }
     }

+ 18 - 4
src/SharpGLTF.Core/Memory/IntegerArrays.cs

@@ -14,7 +14,7 @@ namespace SharpGLTF.Memory
     /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="UInt32"/> values
     /// Wraps an encoded <see cref="BYTES"/> and exposes it as an array of <see cref="UInt32"/> values
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Integer Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("Integer Accessor {Count}")]
-    public struct IntegerArray : IEncodedArray<UInt32>
+    public struct IntegerArray : IList<UInt32>, IReadOnlyList<UInt32>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -130,19 +130,33 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Count => _Data.Count / _ByteStride;
         public int Count => _Data.Count / _ByteStride;
 
 
+        bool ICollection<UInt32>.IsReadOnly => false;
+
         public UInt32 this[int index]
         public UInt32 this[int index]
         {
         {
             get => _Getter(index);
             get => _Getter(index);
             set => _Setter(index, value);
             set => _Setter(index, value);
         }
         }
 
 
-        public void CopyTo(ArraySegment<UInt32> dst) { EncodedArrayUtils.Copy<UInt32>(this, dst); }
-
         public IEnumerator<UInt32> GetEnumerator() { return new EncodedArrayEnumerator<UInt32>(this); }
         public IEnumerator<UInt32> GetEnumerator() { return new EncodedArrayEnumerator<UInt32>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<UInt32>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<UInt32>(this); }
 
 
-        public (UInt32, UInt32) GetBounds() { throw new NotImplementedException(); }
+        public bool Contains(UInt32 item) { return IndexOf(item) >= 0; }
+
+        public int IndexOf(UInt32 item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(UInt32[] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<UInt32>.Insert(int index, UInt32 item) { throw new NotSupportedException(); }
+
+        void IList<UInt32>.RemoveAt(int index) { throw new NotSupportedException(); }
+
+        void ICollection<UInt32>.Add(UInt32 item) { throw new NotSupportedException(); }
+
+        void ICollection<UInt32>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<UInt32>.Remove(uint item) { throw new NotSupportedException(); }
 
 
         #endregion
         #endregion
     }
     }

+ 12 - 12
src/SharpGLTF.Core/Memory/MemoryAccessor.cs

@@ -231,9 +231,9 @@ namespace SharpGLTF.Memory
             _Attribute.ByteStride = byteStride;
             _Attribute.ByteStride = byteStride;
         }
         }
 
 
-        public void Fill(IEnumerable<Int32> values) { AsIntegerArray().FillFrom(0, values); }
+        public void Fill(IReadOnlyList<Int32> values) { AsIntegerArray().FillFrom(0, values); }
 
 
-        public void Fill(IEnumerable<UInt32> values) { AsIntegerArray().FillFrom(0, values); }
+        public void Fill(IReadOnlyList<UInt32> values) { values.CopyTo(AsIntegerArray()); }
 
 
         public IntegerArray AsIntegerArray()
         public IntegerArray AsIntegerArray()
         {
         {
@@ -242,7 +242,7 @@ namespace SharpGLTF.Memory
             return new IntegerArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.Encoding.ToIndex());
             return new IntegerArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.Encoding.ToIndex());
         }
         }
 
 
-        public void Fill(IEnumerable<Single> values) { AsScalarArray().FillFrom(0, values); }
+        public void Fill(IReadOnlyList<Single> values) { values.CopyTo(AsScalarArray()); }
 
 
         public ScalarArray AsScalarArray()
         public ScalarArray AsScalarArray()
         {
         {
@@ -251,7 +251,7 @@ namespace SharpGLTF.Memory
             return new ScalarArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
             return new ScalarArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
         }
 
 
-        public void Fill(IEnumerable<Vector2> values) { AsVector2Array().FillFrom(0, values); }
+        public void Fill(IReadOnlyList<Vector2> values) { values.CopyTo(AsVector2Array()); }
 
 
         public Vector2Array AsVector2Array()
         public Vector2Array AsVector2Array()
         {
         {
@@ -260,7 +260,7 @@ namespace SharpGLTF.Memory
             return new Vector2Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
             return new Vector2Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
         }
 
 
-        public void Fill(IEnumerable<Vector3> values) { AsVector3Array().FillFrom(0, values); }
+        public void Fill(IReadOnlyList<Vector3> values) { values.CopyTo(AsVector3Array()); }
 
 
         public Vector3Array AsVector3Array()
         public Vector3Array AsVector3Array()
         {
         {
@@ -269,7 +269,7 @@ namespace SharpGLTF.Memory
             return new Vector3Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
             return new Vector3Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
         }
 
 
-        public void Fill(IEnumerable<Vector4> values) { AsVector4Array().FillFrom(0, values); }
+        public void Fill(IReadOnlyList<Vector4> values) { values.CopyTo(AsVector4Array()); }
 
 
         public Vector4Array AsVector4Array()
         public Vector4Array AsVector4Array()
         {
         {
@@ -278,7 +278,7 @@ namespace SharpGLTF.Memory
             return new Vector4Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
             return new Vector4Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
         }
 
 
-        public void Fill(IEnumerable<Quaternion> values) { AsQuaternionArray().FillFrom(0, values); }
+        public void Fill(IReadOnlyList<Quaternion> values) { values.CopyTo(AsQuaternionArray()); }
 
 
         public QuaternionArray AsQuaternionArray()
         public QuaternionArray AsQuaternionArray()
         {
         {
@@ -287,7 +287,7 @@ namespace SharpGLTF.Memory
             return new QuaternionArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
             return new QuaternionArray(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
         }
 
 
-        public void Fill(IEnumerable<Matrix4x4> values) { AsMatrix4x4Array().FillFrom(0, values); }
+        public void Fill(IReadOnlyList<Matrix4x4> values) { values.CopyTo(AsMatrix4x4Array()); }
 
 
         public Matrix4x4Array AsMatrix4x4Array()
         public Matrix4x4Array AsMatrix4x4Array()
         {
         {
@@ -296,7 +296,7 @@ namespace SharpGLTF.Memory
             return new Matrix4x4Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
             return new Matrix4x4Array(_Data, _Attribute.ByteOffset, _Attribute.ItemsCount, _Attribute.ByteStride, _Attribute.Encoding, _Attribute.Normalized);
         }
         }
 
 
-        public static IEncodedArray<Single> CreateScalarSparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
+        public static IList<Single> CreateScalarSparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
         {
             Guard.IsTrue(bottom._Attribute.Dimensions == topValues._Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(bottom._Attribute.Dimensions == topValues._Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(topKeys.Count <= bottom._Attribute.ItemsCount, nameof(topKeys));
             Guard.IsTrue(topKeys.Count <= bottom._Attribute.ItemsCount, nameof(topKeys));
@@ -306,7 +306,7 @@ namespace SharpGLTF.Memory
             return new SparseArray<Single>(bottom.AsScalarArray(), topValues.AsScalarArray(), topKeys);
             return new SparseArray<Single>(bottom.AsScalarArray(), topValues.AsScalarArray(), topKeys);
         }
         }
 
 
-        public static IEncodedArray<Vector2> CreateVector2SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
+        public static IList<Vector2> CreateVector2SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
         {
             Guard.IsTrue(bottom._Attribute.Dimensions == topValues._Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(bottom._Attribute.Dimensions == topValues._Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(topKeys.Count <= bottom._Attribute.ItemsCount, nameof(topKeys));
             Guard.IsTrue(topKeys.Count <= bottom._Attribute.ItemsCount, nameof(topKeys));
@@ -316,7 +316,7 @@ namespace SharpGLTF.Memory
             return new SparseArray<Vector2>(bottom.AsVector2Array(), topValues.AsVector2Array(), topKeys);
             return new SparseArray<Vector2>(bottom.AsVector2Array(), topValues.AsVector2Array(), topKeys);
         }
         }
 
 
-        public static IEncodedArray<Vector3> CreateVector3SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
+        public static IList<Vector3> CreateVector3SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
         {
             Guard.IsTrue(bottom._Attribute.Dimensions == topValues._Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(bottom._Attribute.Dimensions == topValues._Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(topKeys.Count <= bottom._Attribute.ItemsCount, nameof(topKeys));
             Guard.IsTrue(topKeys.Count <= bottom._Attribute.ItemsCount, nameof(topKeys));
@@ -326,7 +326,7 @@ namespace SharpGLTF.Memory
             return new SparseArray<Vector3>(bottom.AsVector3Array(), topValues.AsVector3Array(), topKeys);
             return new SparseArray<Vector3>(bottom.AsVector3Array(), topValues.AsVector3Array(), topKeys);
         }
         }
 
 
-        public static IEncodedArray<Vector4> CreateVector4SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
+        public static IList<Vector4> CreateVector4SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
         {
             Guard.IsTrue(bottom._Attribute.Dimensions == topValues._Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(bottom._Attribute.Dimensions == topValues._Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(topKeys.Count <= bottom._Attribute.ItemsCount, nameof(topKeys));
             Guard.IsTrue(topKeys.Count <= bottom._Attribute.ItemsCount, nameof(topKeys));

+ 22 - 11
src/SharpGLTF.Core/Memory/SparseArrays.cs

@@ -11,12 +11,12 @@ namespace SharpGLTF.Memory
     /// </summary>
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <typeparam name="T"></typeparam>
     [System.Diagnostics.DebuggerDisplay("Sparse {typeof(T).Name} Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("Sparse {typeof(T).Name} Accessor {Count}")]
-    public struct SparseArray<T> : IEncodedArray<T>
+    public struct SparseArray<T> : IList<T>, IReadOnlyList<T>
         where T : unmanaged
         where T : unmanaged
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        public SparseArray(IEncodedArray<T> bottom, IEncodedArray<T> top, IntegerArray topMapping)
+        public SparseArray(IList<T> bottom, IList<T> top, IntegerArray topMapping)
         {
         {
             _BottomItems = bottom;
             _BottomItems = bottom;
             _TopItems = top;
             _TopItems = top;
@@ -34,10 +34,10 @@ namespace SharpGLTF.Memory
         #region data
         #region data
 
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly IEncodedArray<T> _BottomItems;
+        private readonly IList<T> _BottomItems;
 
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly IEncodedArray<T> _TopItems;
+        private readonly IList<T> _TopItems;
 
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private readonly Dictionary<int, int> _Mapping;
         private readonly Dictionary<int, int> _Mapping;
@@ -52,6 +52,8 @@ namespace SharpGLTF.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public int Count => _BottomItems.Count;
         public int Count => _BottomItems.Count;
 
 
+        public bool IsReadOnly => false;
+
         public T this[int index]
         public T this[int index]
         {
         {
             get => _Mapping.TryGetValue(index, out int topIndex) ? _TopItems[topIndex] : _BottomItems[index];
             get => _Mapping.TryGetValue(index, out int topIndex) ? _TopItems[topIndex] : _BottomItems[index];
@@ -61,17 +63,26 @@ namespace SharpGLTF.Memory
             }
             }
         }
         }
 
 
-        public void CopyTo(ArraySegment<T> dst) { EncodedArrayUtils.Copy(this, dst); }
-
-        public (T, T) GetBounds()
-        {
-            throw new NotImplementedException();
-        }
-
         public IEnumerator<T> GetEnumerator() { return new EncodedArrayEnumerator<T>(this); }
         public IEnumerator<T> GetEnumerator() { return new EncodedArrayEnumerator<T>(this); }
 
 
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<T>(this); }
         IEnumerator IEnumerable.GetEnumerator() { return new EncodedArrayEnumerator<T>(this); }
 
 
+        public bool Contains(T item) { return IndexOf(item) >= 0; }
+
+        public int IndexOf(T item) { return EncodedArrayUtils.FirstIndexOf(this, item); }
+
+        public void CopyTo(T[] array, int arrayIndex) { EncodedArrayUtils.CopyTo(this, array, arrayIndex); }
+
+        void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); }
+
+        void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); }
+
+        void ICollection<T>.Add(T item) { throw new NotSupportedException(); }
+
+        void ICollection<T>.Clear() { throw new NotSupportedException(); }
+
+        bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); }
+
         #endregion
         #endregion
     }
     }
 }
 }

+ 0 - 12
src/SharpGLTF.Core/Schema2/gltf.AccessorSparse.cs

@@ -2,18 +2,6 @@
 
 
 namespace SharpGLTF.Schema2
 namespace SharpGLTF.Schema2
 {
 {
-    // TODO:
-    // AccessorSparse is a single child of an Accessor
-    // when an Accessor defines an AccessorSparse, it becomes a "two layers" collection.
-    // the layer at the bottom is the data of the base accessor
-    // the layer on top, replaces just a few elements in the base accessor.
-    // Unlike many other objects in the API, AccessorSparse does not implement IChildOf<Accessor>
-    // which would allow AccessorSparse to access the interal data of its parent.
-    // So we have two choices here:
-    // 1- Implement IChildOf<Accessor> and finish the SingleChild<T> collection object
-    // 2- Make these classes non public, and expose them with a public view object.
-    // 3- Make the whole Accessor+AccessorSparse block non public, and expose them with a public view.
-
     using ROOT = ModelRoot;
     using ROOT = ModelRoot;
 
 
     public sealed partial class AccessorSparse
     public sealed partial class AccessorSparse

+ 15 - 17
src/SharpGLTF.Core/Schema2/gltf.Accessors.cs

@@ -220,7 +220,7 @@ namespace SharpGLTF.Schema2
             SetData(buffer, bufferByteOffset, itemCount, dimensions, encoding, normalized);
             SetData(buffer, bufferByteOffset, itemCount, dimensions, encoding, normalized);
         }
         }
 
 
-        public IEncodedArray<Single> AsScalarArray()
+        public IList<Single> AsScalarArray()
         {
         {
             var memory = _GetMemoryAccessor();
             var memory = _GetMemoryAccessor();
 
 
@@ -230,7 +230,7 @@ namespace SharpGLTF.Schema2
             return MemoryAccessor.CreateScalarSparseArray(memory, sparseKV.Key, sparseKV.Value);
             return MemoryAccessor.CreateScalarSparseArray(memory, sparseKV.Key, sparseKV.Value);
         }
         }
 
 
-        public IEncodedArray<Vector2> AsVector2Array()
+        public IList<Vector2> AsVector2Array()
         {
         {
             var memory = _GetMemoryAccessor();
             var memory = _GetMemoryAccessor();
 
 
@@ -240,7 +240,7 @@ namespace SharpGLTF.Schema2
             return MemoryAccessor.CreateVector2SparseArray(memory, sparseKV.Key, sparseKV.Value);
             return MemoryAccessor.CreateVector2SparseArray(memory, sparseKV.Key, sparseKV.Value);
         }
         }
 
 
-        public IEncodedArray<Vector3> AsVector3Array()
+        public IList<Vector3> AsVector3Array()
         {
         {
             var memory = _GetMemoryAccessor();
             var memory = _GetMemoryAccessor();
 
 
@@ -250,7 +250,7 @@ namespace SharpGLTF.Schema2
             return MemoryAccessor.CreateVector3SparseArray(memory, sparseKV.Key, sparseKV.Value);
             return MemoryAccessor.CreateVector3SparseArray(memory, sparseKV.Key, sparseKV.Value);
         }
         }
 
 
-        public IEncodedArray<Vector4> AsVector4Array()
+        public IList<Vector4> AsVector4Array()
         {
         {
             var memory = _GetMemoryAccessor();
             var memory = _GetMemoryAccessor();
 
 
@@ -260,7 +260,7 @@ namespace SharpGLTF.Schema2
             return MemoryAccessor.CreateVector4SparseArray(memory, sparseKV.Key, sparseKV.Value);
             return MemoryAccessor.CreateVector4SparseArray(memory, sparseKV.Key, sparseKV.Value);
         }
         }
 
 
-        public IEncodedArray<Quaternion> AsQuaternionArray()
+        public IList<Quaternion> AsQuaternionArray()
         {
         {
             var memory = _GetMemoryAccessor();
             var memory = _GetMemoryAccessor();
 
 
@@ -318,38 +318,36 @@ namespace SharpGLTF.Schema2
             }
             }
         }
         }
 
 
-        public override IEnumerable<Exception> Validate()
+        internal override void Validate(IList<Exception> result)
         {
         {
-            var exxx = base.Validate().ToList();
+            base.Validate(result);
 
 
-            if (!_bufferView.HasValue) { exxx.Add(new EXCEPTION(this, $"BufferView index missing")); return exxx; }
-            if (_bufferView < 0 || _bufferView >= LogicalParent.LogicalBufferViews.Count) exxx.Add(new EXCEPTION(this, $"BufferView index out of range"));
+            if (!_bufferView.HasValue) { result.Add(new EXCEPTION(this, $"BufferView index missing")); return; }
+            if (_bufferView < 0 || _bufferView >= LogicalParent.LogicalBufferViews.Count) result.Add(new EXCEPTION(this, $"BufferView index out of range"));
 
 
-            if (_count < 0) exxx.Add(new EXCEPTION(this, $"Count is out of range"));
-            if (_byteOffset < 0) exxx.Add(new EXCEPTION(this, $"ByteOffset is out of range"));
+            if (_count < 0) result.Add(new EXCEPTION(this, $"Count is out of range"));
+            if (_byteOffset < 0) result.Add(new EXCEPTION(this, $"ByteOffset is out of range"));
 
 
             if (SourceBufferView.DeviceBufferTarget == BufferMode.ARRAY_BUFFER)
             if (SourceBufferView.DeviceBufferTarget == BufferMode.ARRAY_BUFFER)
             {
             {
                 var len = Encoding.ByteLength() * Dimensions.DimCount();
                 var len = Encoding.ByteLength() * Dimensions.DimCount();
-                if (len > 0 && (len & 3) != 0) exxx.Add(new EXCEPTION(this, $"Expected length to be multiple of 4, found {len}"));
+                if (len > 0 && (len & 3) != 0) result.Add(new EXCEPTION(this, $"Expected length to be multiple of 4, found {len}"));
             }
             }
 
 
             if (SourceBufferView.DeviceBufferTarget == BufferMode.ELEMENT_ARRAY_BUFFER)
             if (SourceBufferView.DeviceBufferTarget == BufferMode.ELEMENT_ARRAY_BUFFER)
             {
             {
                 var len = Encoding.ByteLength() * Dimensions.DimCount();
                 var len = Encoding.ByteLength() * Dimensions.DimCount();
-                if (len != 1 && len != 2 && len != 4) exxx.Add(new EXCEPTION(this, $"Expected length to be 1, 2 or 4, found {len}"));
+                if (len != 1 && len != 2 && len != 4) result.Add(new EXCEPTION(this, $"Expected length to be 1, 2 or 4, found {len}"));
             }
             }
 
 
             // validate bounds
             // validate bounds
 
 
-            if (_min.Count != _max.Count) { exxx.Add(new EXCEPTION(this, "min and max length mismatch")); return exxx; }
+            if (_min.Count != _max.Count) { result.Add(new EXCEPTION(this, "min and max length mismatch")); return; }
 
 
             for (int i = 0; i < _min.Count; ++i)
             for (int i = 0; i < _min.Count; ++i)
             {
             {
-                if (_min[i] > _max[i]) exxx.Add(new EXCEPTION(this, $"min[{i}] is larger than max[{i}]"));
+                if (_min[i] > _max[i]) result.Add(new EXCEPTION(this, $"min[{i}] is larger than max[{i}]"));
             }
             }
-
-            return exxx;
         }
         }
 
 
         #endregion
         #endregion

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

@@ -299,7 +299,7 @@ namespace SharpGLTF.Schema2
 
 
             accessor.SetData(buffer, 0, input.Count, DimensionType.SCALAR, EncodingType.FLOAT, false);
             accessor.SetData(buffer, 0, input.Count, DimensionType.SCALAR, EncodingType.FLOAT, false);
 
 
-            Memory.EncodedArrayUtils.FillFrom(accessor.AsScalarArray(), 0, input);
+            Memory.EncodedArrayUtils.CopyTo(input, accessor.AsScalarArray());
 
 
             accessor.UpdateBounds();
             accessor.UpdateBounds();
 
 
@@ -318,7 +318,7 @@ namespace SharpGLTF.Schema2
 
 
             accessor.SetData(buffer, 0, output.Count, DimensionType.VEC3, EncodingType.FLOAT, false);
             accessor.SetData(buffer, 0, output.Count, DimensionType.VEC3, EncodingType.FLOAT, false);
 
 
-            Memory.EncodedArrayUtils.FillFrom(accessor.AsVector3Array(), 0, output);
+            Memory.EncodedArrayUtils.CopyTo(output, accessor.AsVector3Array());
 
 
             accessor.UpdateBounds();
             accessor.UpdateBounds();
 
 
@@ -334,7 +334,7 @@ namespace SharpGLTF.Schema2
 
 
             accessor.SetData(buffer, 0, output.Count, DimensionType.VEC4, EncodingType.FLOAT, false);
             accessor.SetData(buffer, 0, output.Count, DimensionType.VEC4, EncodingType.FLOAT, false);
 
 
-            Memory.EncodedArrayUtils.FillFrom(accessor.AsQuaternionArray(), 0, output);
+            Memory.EncodedArrayUtils.CopyTo(output, accessor.AsQuaternionArray());
 
 
             accessor.UpdateBounds();
             accessor.UpdateBounds();
 
 

+ 7 - 5
src/SharpGLTF.Core/Schema2/gltf.Asset.cs

@@ -11,6 +11,8 @@ namespace SharpGLTF.Schema2
     {
     {
         #region lifecycle
         #region lifecycle
 
 
+        internal Asset() { }
+
         internal static Asset CreateDefault(string copyright)
         internal static Asset CreateDefault(string copyright)
         {
         {
             var av = typeof(Asset).Assembly.GetCustomAttributes(true)
             var av = typeof(Asset).Assembly.GetCustomAttributes(true)
@@ -56,17 +58,17 @@ namespace SharpGLTF.Schema2
 
 
         #region API
         #region API
 
 
-        public override IEnumerable<Exception> Validate()
+        internal override void Validate(IList<Exception> result)
         {
         {
-            foreach (var ex in base.Validate()) yield return ex;
+            base.Validate(result);
 
 
-            if (string.IsNullOrWhiteSpace(_version)) yield return new EXCEPTION(this, "version number is missing");
+            if (string.IsNullOrWhiteSpace(_version)) result.Add(new EXCEPTION(this, "version number is missing"));
 
 
             var curVer = this.Version;
             var curVer = this.Version;
             var minVer = this.MinVersion;
             var minVer = this.MinVersion;
 
 
-            if (curVer < MINVERSION) yield return new EXCEPTION(this, $"invalid version number {this.Version} expected {MINVERSION}");
-            if (curVer > MAXVERSION) yield return new EXCEPTION(this, $"invalid version number {this.Version} expected {MAXVERSION}");
+            if (curVer < MINVERSION) result.Add(new EXCEPTION(this, $"invalid version number {this.Version} expected {MINVERSION}"));
+            if (curVer > MAXVERSION) result.Add(new EXCEPTION(this, $"invalid version number {this.Version} expected {MAXVERSION}"));
         }
         }
 
 
         private string _GetExtraInfo(string key)
         private string _GetExtraInfo(string key)

+ 1 - 1
src/SharpGLTF.Core/Schema2/gltf.Camera.cs

@@ -42,7 +42,7 @@ namespace SharpGLTF.Schema2
     public partial class ModelRoot
     public partial class ModelRoot
     {
     {
         /// <summary>
         /// <summary>
-        /// Creates a new <see cref="Camera"/> instance
+        /// Creates a new <see cref="Camera"/> instance.
         /// and adds it to <see cref="ModelRoot.LogicalCameras"/>.
         /// and adds it to <see cref="ModelRoot.LogicalCameras"/>.
         /// </summary>
         /// </summary>
         /// <param name="name">The name of the instance.</param>
         /// <param name="name">The name of the instance.</param>

+ 29 - 1
src/SharpGLTF.Core/Schema2/gltf.ExtraProperties.cs

@@ -26,7 +26,7 @@ namespace SharpGLTF.Schema2
         public IReadOnlyCollection<JsonSerializable> Extensions => _extensions;
         public IReadOnlyCollection<JsonSerializable> Extensions => _extensions;
 
 
         /// <summary>
         /// <summary>
-        /// Gets the extras object, where the object can be either an <see cref="Object"/>, a <see cref="JsonList"/> or a <see cref="JsonDictionary"/>
+        /// Gets the extras value, where the value can be either an intrinsic type <see cref="TypeCode"/> , a <see cref="JsonList"/> or a <see cref="JsonDictionary"/>
         /// </summary>
         /// </summary>
         public Object Extras => _extras;
         public Object Extras => _extras;
 
 
@@ -99,6 +99,34 @@ namespace SharpGLTF.Schema2
             return this._extras as JsonDictionary;
             return this._extras as JsonDictionary;
         }
         }
 
 
+        /// <summary>
+        /// Gets the Extras property as a <see cref="JsonList"/>
+        /// </summary>
+        /// <param name="overwrite">true if the current value is to be replaced by a <see cref="JsonList"/> instance.</param>
+        /// <returns>A <see cref="JsonDictionary"/> instance or null.</returns>
+        public JsonList TryUseExtrasAsList(bool overwrite)
+        {
+            if (this._extras is JsonList list) return list;
+
+            if (overwrite) this._extras = new JsonList();
+
+            return this._extras as JsonList;
+        }
+
+        #endregion
+
+        #region validation
+
+        internal override void Validate(IList<Exception> result)
+        {
+            base.Validate(result);
+
+            if (this._extras != null)
+            {
+                if (!IO.JsonUtils.IsSerializable(this._extras)) result.Add(new ModelException(this, $"Invalid {Extras} content."));
+            }
+        }
+
         #endregion
         #endregion
 
 
         #region serialization API
         #region serialization API

+ 3 - 5
src/SharpGLTF.Core/Schema2/gltf.LogicalChildOfRoot.cs

@@ -39,13 +39,11 @@ namespace SharpGLTF.Schema2
             return items.All(item => Object.ReferenceEquals(this.LogicalParent, item.LogicalParent));
             return items.All(item => Object.ReferenceEquals(this.LogicalParent, item.LogicalParent));
         }
         }
 
 
-        public override IEnumerable<Exception> Validate()
+        internal override void Validate(IList<Exception> result)
         {
         {
-            foreach (var ex in base.Validate()) yield return ex;
+            base.Validate(result);
 
 
-            if (_name == null) yield break;
-
-            // todo, verify the name does not have invalid characters
+            // TODO: verify the name does not have invalid characters
         }
         }
 
 
         #endregion
         #endregion

+ 3 - 5
src/SharpGLTF.Core/Schema2/gltf.Mesh.cs

@@ -60,16 +60,14 @@ namespace SharpGLTF.Schema2
             return mp;
             return mp;
         }
         }
 
 
-        public override IEnumerable<Exception> Validate()
+        internal override void Validate(IList<Exception> result)
         {
         {
-            var exx = base.Validate().ToList();
+            base.Validate(result);
 
 
             foreach (var p in this.Primitives)
             foreach (var p in this.Primitives)
             {
             {
-                exx.AddRange(p.Validate());
+                p.Validate(result);
             }
             }
-
-            return exx;
         }
         }
 
 
         #endregion
         #endregion

+ 3 - 5
src/SharpGLTF.Core/Schema2/gltf.MeshPrimitive.cs

@@ -199,21 +199,19 @@ namespace SharpGLTF.Schema2
 
 
         #region validation
         #region validation
 
 
-        public override IEnumerable<Exception> Validate()
+        internal override void Validate(IList<Exception> result)
         {
         {
-            var exx = base.Validate().ToList();
+            base.Validate(result);
 
 
             if (IndexAccessor != null)
             if (IndexAccessor != null)
             {
             {
                 switch (DrawPrimitiveType)
                 switch (DrawPrimitiveType)
                 {
                 {
                     case PrimitiveType.TRIANGLES:
                     case PrimitiveType.TRIANGLES:
-                        if ((IndexAccessor.Count % 3) != 0) exx.Add(new EXCEPTION(this, $"Indices count {IndexAccessor.Count} incompatible with Primitive.{DrawPrimitiveType}"));
+                        if ((IndexAccessor.Count % 3) != 0) result.Add(new EXCEPTION(this, $"Indices count {IndexAccessor.Count} incompatible with Primitive.{DrawPrimitiveType}"));
                         break;
                         break;
                 }
                 }
             }
             }
-
-            return exx;
         }
         }
 
 
         #endregion
         #endregion

+ 12 - 16
src/SharpGLTF.Core/Schema2/gltf.Root.cs

@@ -144,38 +144,34 @@ namespace SharpGLTF.Schema2
 
 
         #region validation
         #region validation
 
 
-        public override IEnumerable<Exception> Validate()
+        internal override void Validate(IList<Exception> result)
         {
         {
-            var exx = new List<Exception>();
-
             // 1st check version number
             // 1st check version number
 
 
-            if (Asset == null) exx.Add(new IO.ModelException(this, "missing Asset object, can't check glTF version")); // fix: create a default Asset
-            else exx.AddRange(Asset.Validate());
+            if (Asset == null) result.Add(new IO.ModelException(this, "missing Asset object, can't check glTF version")); // fix: create a default Asset
+            else Asset.Validate(result);
 
 
-            if (exx.Count > 0) return exx;
+            if (result.Count > 0) return;
 
 
             // 2nd check incompatible extensions
             // 2nd check incompatible extensions
 
 
             foreach (var iex in this.IncompatibleExtensions)
             foreach (var iex in this.IncompatibleExtensions)
             {
             {
-                exx.Add(new IO.UnsupportedExtensionException(this, iex)); // fix: attempt to remove given extension
+                result.Add(new IO.UnsupportedExtensionException(this, iex)); // fix: attempt to remove given extension
             }
             }
 
 
-            if (exx.Count > 0) return exx;
+            if (result.Count > 0) return;
 
 
             // 3rd check base class
             // 3rd check base class
 
 
-            exx.AddRange(base.Validate());
+            base.Validate(result);
 
 
             // 4th check contents
             // 4th check contents
-            foreach (var s in _scenes) exx.AddRange(s.Validate());
-            foreach (var n in _nodes) exx.AddRange(n.Validate());
-            foreach (var a in _accessors) exx.AddRange(a.Validate());
-            foreach (var m in _meshes) exx.AddRange(m.Validate());
-            foreach (var s in _skins) exx.AddRange(s.Validate());
-
-            return exx;
+            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 m in _meshes) m.Validate(result);
+            foreach (var s in _skins) s.Validate(result);
         }
         }
 
 
         #endregion
         #endregion

+ 63 - 68
src/SharpGLTF.Core/Schema2/gltf.Scene.cs

@@ -13,8 +13,6 @@ namespace SharpGLTF.Schema2
         IEnumerable<Node> VisualChildren { get; }
         IEnumerable<Node> VisualChildren { get; }
 
 
         Node CreateNode(string name);
         Node CreateNode(string name);
-
-        Node FindNode(string name);
     }
     }
 
 
     [System.Diagnostics.DebuggerDisplay("Node[{LogicalIndex}] {Name} SkinJoint:{IsSkinJoint} T:{LocalTransform.Translation.X} {LocalTransform.Translation.Y} {LocalTransform.Translation.Z}")]
     [System.Diagnostics.DebuggerDisplay("Node[{LogicalIndex}] {Name} SkinJoint:{IsSkinJoint} T:{LocalTransform.Translation.X} {LocalTransform.Translation.Y} {LocalTransform.Translation.Z}")]
@@ -37,8 +35,14 @@ namespace SharpGLTF.Schema2
         /// </summary>
         /// </summary>
         public int LogicalIndex => this.LogicalParent.LogicalNodes.IndexOfReference(this);
         public int LogicalIndex => this.LogicalParent.LogicalNodes.IndexOfReference(this);
 
 
+        /// <summary>
+        /// Gets the visual parent <see cref="Node"/> instance that contains this <see cref="Node"/>.
+        /// </summary>
         public Node VisualParent => this.LogicalParent._FindVisualParentNode(this);
         public Node VisualParent => this.LogicalParent._FindVisualParentNode(this);
 
 
+        /// <summary>
+        /// Gets the visual root <see cref="Scene"/> instance that contains this <see cref="Node"/>.
+        /// </summary>
         public Scene VisualScene
         public Scene VisualScene
         {
         {
             get
             get
@@ -49,8 +53,14 @@ namespace SharpGLTF.Schema2
             }
             }
         }
         }
 
 
-        public IEnumerable<Node> VisualChildren => GetVisualChildren();
+        /// <summary>
+        /// Gets the visual children <see cref="Node"/> instances contained in this <see cref="Node"/>.
+        /// </summary>
+        public IEnumerable<Node> VisualChildren => _GetVisualChildren();
 
 
+        /// <summary>
+        /// Gets a value indicating whether this node is used as a Bone joint in a <see cref="Skin"/>.
+        /// </summary>
         public Boolean IsSkinJoint => Skin.FindSkinsUsingJoint(this).Any();
         public Boolean IsSkinJoint => Skin.FindSkinsUsingJoint(this).Any();
 
 
         #endregion
         #endregion
@@ -157,34 +167,10 @@ namespace SharpGLTF.Schema2
 
 
         #region API
         #region API
 
 
-        internal bool _ContainsVisualNode(Node node, bool recursive)
-        {
-            Guard.NotNull(node, nameof(node));
-            Guard.MustShareLogicalParent(this, node, nameof(node));
-
-            if (!recursive) return VisualChildren.Any(item => item == node);
-
-            return VisualChildren.Any(item => item == node || item._ContainsVisualNode(node, recursive));
-        }
-
-        internal bool _HasVisualChild(int nodeIndex) { return _children.Contains(nodeIndex); }
-
-        public IEnumerable<Node> GetVisualChildren()
-        {
-            // TODO: handle MSFT_Lod here ?
-            // maybe we can have a VisualHierarchyManager abstract class with a default implementation
-            // a class declared in the extension... but then, it makes edition horribly complicated.
-            // maybe it's better to have a non serializable _LodLevel that is applied when serializing.
-
-            var allChildren = _children.Select(idx => LogicalParent.LogicalNodes[idx]);
-
-            return allChildren;
-        }
-
         /// <summary>
         /// <summary>
         /// Creates a new <see cref="Node"/> instance,
         /// Creates a new <see cref="Node"/> instance,
         /// adds it to <see cref="ModelRoot.LogicalNodes"/>
         /// adds it to <see cref="ModelRoot.LogicalNodes"/>
-        /// and references it in the current <see cref="Node"/>.
+        /// and references it as a child in the current graph.
         /// </summary>
         /// </summary>
         /// <param name="name">The name of the instance.</param>
         /// <param name="name">The name of the instance.</param>
         /// <returns>A <see cref="Node"/> instance.</returns>
         /// <returns>A <see cref="Node"/> instance.</returns>
@@ -196,19 +182,9 @@ namespace SharpGLTF.Schema2
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Finds a node with the given <paramref name="name"/>
-        /// </summary>
-        /// <param name="name">A name.</param>
-        /// <returns>A <see cref="Node"/> instance.</returns>
-        public Node FindNode(string name)
-        {
-            return this.VisualChildren.FirstOrDefault(item => item.Name == name);
-        }
-
-        /// <summary>
-        /// Returns all the nodes of a visual hierarchy as a flattened list.
+        /// Returns all the <see cref="Node"/> instances of a visual hierarchy as a flattened list.
         /// </summary>
         /// </summary>
-        /// <param name="container">A <see cref="Scene"/> instance or a <see cref="Node"/> instance.</param>
+        /// <param name="container">A <see cref="IVisualNodeContainer"/> instance.</param>
         /// <returns>A collection of <see cref="Node"/> instances.</returns>
         /// <returns>A collection of <see cref="Node"/> instances.</returns>
         public static IEnumerable<Node> Flatten(IVisualNodeContainer container)
         public static IEnumerable<Node> Flatten(IVisualNodeContainer container)
         {
         {
@@ -254,21 +230,44 @@ namespace SharpGLTF.Schema2
                 .Where(item => item._skin.AsValue(int.MinValue) == meshIdx);
                 .Where(item => item._skin.AsValue(int.MinValue) == meshIdx);
         }
         }
 
 
-        public override IEnumerable<Exception> Validate()
+        internal bool _ContainsVisualNode(Node node, bool recursive)
         {
         {
-            foreach (var ex in base.Validate()) yield return ex;
+            Guard.NotNull(node, nameof(node));
+            Guard.MustShareLogicalParent(this, node, nameof(node));
+
+            if (!recursive) return VisualChildren.Any(item => item == node);
+
+            return VisualChildren.Any(item => item == node || item._ContainsVisualNode(node, recursive));
+        }
+
+        internal bool _HasVisualChild(int nodeIndex) { return _children.Contains(nodeIndex); }
+
+        internal IEnumerable<Node> _GetVisualChildren()
+        {
+            var allChildren = _children.Select(idx => LogicalParent.LogicalNodes[idx]);
+
+            return allChildren;
+        }
+
+        #endregion
+
+        #region validation
+
+        internal override void Validate(IList<Exception> result)
+        {
+            base.Validate(result);
 
 
             // check out of range indices
             // check out of range indices
             foreach (var idx in this._children)
             foreach (var idx in this._children)
             {
             {
-                if (idx < 0 || idx >= this.LogicalParent.LogicalNodes.Count) yield return new EXCEPTION(this, $"references invalid Node[{idx}]");
+                if (idx < 0 || idx >= this.LogicalParent.LogicalNodes.Count) result.Add(new EXCEPTION(this, $"references invalid Node[{idx}]"));
             }
             }
 
 
             // check duplicated indices
             // check duplicated indices
-            if (this._children.Distinct().Count() != this._children.Count) yield return new EXCEPTION(this, "has duplicated node references");
+            if (this._children.Distinct().Count() != this._children.Count) result.Add(new EXCEPTION(this, "has duplicated node references"));
 
 
             // check self references
             // check self references
-            if (this._children.Contains(this.LogicalIndex)) yield return new EXCEPTION(this, "has self references");
+            if (this._children.Contains(this.LogicalIndex)) result.Add(new EXCEPTION(this, "has self references"));
 
 
             // check circular references
             // check circular references
             var p = this;
             var p = this;
@@ -278,7 +277,7 @@ namespace SharpGLTF.Schema2
                 if (p == null) break;
                 if (p == null) break;
                 if (p.LogicalIndex == this.LogicalIndex)
                 if (p.LogicalIndex == this.LogicalIndex)
                 {
                 {
-                    yield return new EXCEPTION(this, "has a circular reference");
+                    result.Add(new EXCEPTION(this, "has a circular reference"));
                     break;
                     break;
                 }
                 }
             }
             }
@@ -319,6 +318,18 @@ namespace SharpGLTF.Schema2
         #endregion
         #endregion
 
 
         #region API
         #region API
+        
+        /// <summary>
+        /// Creates a new <see cref="Node"/> instance,
+        /// adds it to <see cref="ModelRoot.LogicalNodes"/>
+        /// and references it as a child in the current graph.
+        /// </summary>
+        /// <param name="name">The name of the instance.</param>
+        /// <returns>A <see cref="Node"/> instance.</returns>
+        public Node CreateNode(String name = null)
+        {
+            return this.LogicalParent._CreateLogicalNode(this._nodes);
+        }
 
 
         internal bool _ContainsVisualNode(Node node, bool recursive)
         internal bool _ContainsVisualNode(Node node, bool recursive)
         {
         {
@@ -332,40 +343,24 @@ namespace SharpGLTF.Schema2
             return VisualChildren.Any(item => item._ContainsVisualNode(node, true));
             return VisualChildren.Any(item => item._ContainsVisualNode(node, true));
         }
         }
 
 
-        /// <summary>
-        /// Creates a new <see cref="Node"/> instance,
-        /// adds it to <see cref="ModelRoot.LogicalNodes"/>
-        /// and references it in the current <see cref="Scene"/>.
-        /// </summary>
-        /// <param name="name">The name of the instance.</param>
-        /// <returns>A <see cref="Node"/> instance.</returns>
-        public Node CreateNode(String name = null)
-        {
-            return this.LogicalParent._CreateLogicalNode(this._nodes);
-        }
+        #endregion
 
 
-        public Node FindNode(String name)
-        {
-            return this.VisualChildren.FirstOrDefault(item => item.Name == name);
-        }
+        #region validation
 
 
-        public override IEnumerable<Exception> Validate()
+        internal override void Validate(IList<Exception> result)
         {
         {
-            foreach (var ex in base.Validate()) yield return ex;
+            base.Validate(result);
 
 
             // check out of range indices
             // check out of range indices
             foreach (var idx in this._nodes)
             foreach (var idx in this._nodes)
             {
             {
-                if (idx < 0 || idx >= this.LogicalParent.LogicalNodes.Count) yield return new EXCEPTION(this, $"references invalid Node[{idx}]");
+                if (idx < 0 || idx >= this.LogicalParent.LogicalNodes.Count) result.Add(new EXCEPTION(this, $"references invalid Node[{idx}]"));
             }
             }
 
 
             // check duplicated indices
             // check duplicated indices
-            if (this._nodes.Distinct().Count() != this._nodes.Count) yield return new EXCEPTION(this, "has duplicated node references");
+            if (this._nodes.Distinct().Count() != this._nodes.Count) result.Add(new EXCEPTION(this, "has duplicated node references"));
         }
         }
 
 
-        // TODO: AddVisualChild must return a "NodeBuilder"
-        // public Node AddVisualChild() { return LogicalParent._AddLogicalNode(_nodes); }
-
         #endregion
         #endregion
     }
     }
 
 

+ 3 - 4
src/SharpGLTF.Core/Schema2/gltf.Skin.cs

@@ -96,9 +96,9 @@ namespace SharpGLTF.Schema2
             return new KeyValuePair<Node, Matrix4x4>(node, matrix);
             return new KeyValuePair<Node, Matrix4x4>(node, matrix);
         }
         }
 
 
-        public override IEnumerable<Exception> Validate()
+        internal override void Validate(IList<Exception> result)
         {
         {
-            var exx = base.Validate().ToList();
+            base.Validate(result);
 
 
             // note: this check will fail if the buffers are not set
             // note: this check will fail if the buffers are not set
 
 
@@ -111,8 +111,6 @@ namespace SharpGLTF.Schema2
 
 
                 if (invXform.M44 != 1) exx.Add(new ModelException(this, $"Joint {i} has invalid inverse matrix"));
                 if (invXform.M44 != 1) exx.Add(new ModelException(this, $"Joint {i} has invalid inverse matrix"));
             }*/
             }*/
-
-            return exx;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -164,6 +162,7 @@ namespace SharpGLTF.Schema2
             var data = new Byte[joints.Length * 16 * 4];
             var data = new Byte[joints.Length * 16 * 4];
 
 
             var matrices = new Memory.Matrix4x4Array(data.Slice(0), 0, EncodingType.FLOAT, false);
             var matrices = new Memory.Matrix4x4Array(data.Slice(0), 0, EncodingType.FLOAT, false);
+
             Memory.EncodedArrayUtils.FillFrom(matrices, 0, joints.Select(item => item.Value));
             Memory.EncodedArrayUtils.FillFrom(matrices, 0, joints.Select(item => item.Value));
 
 
             var accessor = LogicalParent.CreateAccessor("Bind Matrices");
             var accessor = LogicalParent.CreateAccessor("Bind Matrices");

+ 1 - 1
src/SharpGLTF.Core/Schema2/gltf.TextureInfo.cs

@@ -58,7 +58,7 @@ namespace SharpGLTF.Schema2
     }
     }
 
 
     [System.Diagnostics.DebuggerDisplay("TextureTransform {Offset} {Scale} {Rotation} {TextureCoordinate}")]
     [System.Diagnostics.DebuggerDisplay("TextureTransform {Offset} {Scale} {Rotation} {TextureCoordinate}")]
-    public partial class TextureTransform
+    public sealed partial class TextureTransform
     {
     {
         #region lifecycle
         #region lifecycle
 
 

+ 1 - 1
src/SharpGLTF.Core/Schema2/khr.lights.cs

@@ -43,7 +43,7 @@ namespace SharpGLTF.Schema2
     /// This is part of <see href="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual"/> extension.
     /// This is part of <see href="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual"/> extension.
     /// </remarks>
     /// </remarks>
     [System.Diagnostics.DebuggerDisplay("{LightType} {Color} {Intensity} {Range}")]
     [System.Diagnostics.DebuggerDisplay("{LightType} {Color} {Intensity} {Range}")]
-    public partial class PunctualLight
+    public sealed partial class PunctualLight
     {
     {
         #region lifecycle
         #region lifecycle
 
 

+ 11 - 11
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexColumns.cs

@@ -9,21 +9,21 @@ namespace SharpGLTF.Geometry.VertexTypes
     {
     {
         #region columns
         #region columns
 
 
-        public Memory.IEncodedArray<Vector3> Positions { get; set; }
-        public Memory.IEncodedArray<Vector3> Normals { get; set; }
-        public Memory.IEncodedArray<Vector4> Tangents { get; set; }
+        public IList<Vector3> Positions { get; set; }
+        public IList<Vector3> Normals { get; set; }
+        public IList<Vector4> Tangents { get; set; }
 
 
-        public Memory.IEncodedArray<Vector4> Colors0 { get; set; }
-        public Memory.IEncodedArray<Vector4> Colors1 { get; set; }
+        public IList<Vector4> Colors0 { get; set; }
+        public IList<Vector4> Colors1 { get; set; }
 
 
-        public Memory.IEncodedArray<Vector2> Textures0 { get; set; }
-        public Memory.IEncodedArray<Vector2> Textures1 { get; set; }
+        public IList<Vector2> Textures0 { get; set; }
+        public IList<Vector2> Textures1 { get; set; }
 
 
-        public Memory.IEncodedArray<Vector4> Joints0 { get; set; }
-        public Memory.IEncodedArray<Vector4> Joints1 { get; set; }
+        public IList<Vector4> Joints0 { get; set; }
+        public IList<Vector4> Joints1 { get; set; }
 
 
-        public Memory.IEncodedArray<Vector4> Weights0 { get; set; }
-        public Memory.IEncodedArray<Vector4> Weights1 { get; set; }
+        public IList<Vector4> Weights0 { get; set; }
+        public IList<Vector4> Weights1 { get; set; }
 
 
         #endregion
         #endregion
 
 

+ 22 - 0
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -85,6 +85,28 @@ namespace SharpGLTF.Schema2
 
 
         #region accessors
         #region accessors
 
 
+        private static void FillFrom(this IList<UInt32> dst, int dstIndex, IEnumerable<Int32> src)
+        {
+            using (var ator = src.GetEnumerator())
+            {
+                while (dstIndex < dst.Count && ator.MoveNext())
+                {
+                    dst[dstIndex++] = (UInt32)ator.Current;
+                }
+            }
+        }
+
+        private static void FillFrom<T>(this IList<T> dst, int dstIndex, IEnumerable<T> src)
+        {
+            using (var ator = src.GetEnumerator())
+            {
+                while (dstIndex < dst.Count && ator.MoveNext())
+                {
+                    dst[dstIndex++] = ator.Current;
+                }
+            }
+        }
+
         public static MeshPrimitive WithVertexAccessor(this MeshPrimitive primitive, string attribute, IReadOnlyList<Single> values)
         public static MeshPrimitive WithVertexAccessor(this MeshPrimitive primitive, string attribute, IReadOnlyList<Single> values)
         {
         {
             var root = primitive.LogicalParent.LogicalParent;
             var root = primitive.LogicalParent.LogicalParent;

+ 34 - 0
src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs

@@ -47,6 +47,40 @@ namespace SharpGLTF.Schema2
 
 
         #region evaluation
         #region evaluation
 
 
+        /// <summary>
+        /// Finds a <see cref="Node"/> by name in the current graph.
+        /// </summary>
+        /// <param name="scene">This <see cref="Scene"/> instance.</param>
+        /// <param name="predicate">A function to test each <see cref="Node"/> for a condition.</param>
+        /// <returns>A <see cref="Node"/> instance, or Null.</returns>
+        public static Node FindNode(this Scene scene, Predicate<Node> predicate)
+        {
+            Guard.NotNull(predicate, nameof(predicate));
+
+            return scene.VisualChildren.FirstOrDefault(n => predicate(n));
+        }
+
+        /// <summary>
+        /// Finds a <see cref="Node"/> by name in the current graph.
+        /// </summary>
+        /// <param name="node">This <see cref="Node"/> instance.</param>
+        /// <param name="predicate">A function to test each <see cref="Node"/> for a condition.</param>
+        /// <returns>A <see cref="Node"/> instance, or Null.</returns>
+        public static Node FindNode(this Node node, Predicate<Node> predicate)
+        {
+            Guard.NotNull(predicate, nameof(predicate));
+
+            if (predicate(node)) return node;
+
+            foreach (var child in node.VisualChildren)
+            {
+                var r = child.FindNode(predicate);
+                if (r != null) return r;
+            }
+
+            return null;
+        }
+
         /// <summary>
         /// <summary>
         /// Yield a collection of triangles representing the geometry
         /// Yield a collection of triangles representing the geometry
         /// of the input <see cref="Scene"/> in world space.
         /// of the input <see cref="Scene"/> in world space.

+ 1 - 1
tests/SharpGLTF.Tests/AssemblyAPITests.cs

@@ -15,7 +15,7 @@ namespace SharpGLTF
         {
         {
             var assembly = typeof(Schema2.ModelRoot).Assembly;
             var assembly = typeof(Schema2.ModelRoot).Assembly;
 
 
-            var API = DumpAssemblyAPI.DumpAPI(assembly).ToList();
+            var API = DumpAssemblyAPI.GetAssemblySignature(assembly).OrderBy(item => item).ToList();
 
 
             foreach(var l in API)
             foreach(var l in API)
             {
             {

+ 144 - 52
tests/SharpGLTF.Tests/DumpAssemblyAPI.cs

@@ -22,90 +22,178 @@ namespace SharpGLTF
         // Namespace="" Class="" Property=""
         // Namespace="" Class="" Property=""
         // Namespace="" Class="" Event=""
         // Namespace="" Class="" Event=""
 
 
+        // NS foo.bar { STRUCT name        { FIELD name type       } }
+        // NS foo.bar { ABSTRACTCLASS name { PROPERTYGET name type } }
+        // NS foo.bar { ABSTRACTCLASS name { PROTECTEDPROPERTYSET name type } }
 
 
-        public static IEnumerable<String> DumpAPI(Assembly assembly)
+        // NamesPace { Class|Struct { Class|Struct|Method|Field } }
+
+
+        public static IEnumerable<String> GetAssemblySignature(Assembly assembly)
         {
         {
-            return assembly.ExportedTypes.SelectMany(item => DumpAPI(item.Namespace, item.GetTypeInfo()));
+            return assembly.ExportedTypes
+                .SelectMany(item => GetTypeSignature(item.GetTypeInfo()).Select(l => "NS " + item.Namespace + " { " + l + " } " ) );
         }
         }
 
 
-        public static IEnumerable<String> DumpAPI(string baseName, TypeInfo type)
+        public static IEnumerable<String> GetTypeSignature(TypeInfo tinfo)
         {
         {
-            if (type.IsNestedPrivate) yield break;
+            if (tinfo.IsNestedPrivate) yield break;
+            if (tinfo.IsNestedAssembly) yield break;
+
+            string baseName = string.Empty;
+
+            if (tinfo.IsNestedFamily) baseName += "PROTECTED";                        
 
 
-            if (type.IsInterface) baseName += ".INTERFACE";
-            if (type.IsEnum) baseName += ".ENUM";
-            if (type.IsClass)
+            if (tinfo.IsInterface) baseName += "INTERFACE";
+            else if (tinfo.IsEnum) baseName += "ENUM";
+            else if (tinfo.IsValueType) baseName += "STRUCT";
+            else if (tinfo.IsClass)
             {
             {
-                baseName += ".";
-                if (type.IsSealed) baseName += "SEALED";
-                if (type.IsAbstract) baseName += "ABSTRACT";
+                if (tinfo.IsSealed && tinfo.IsAbstract) baseName += "STATIC";
+                else
+                {
+                    if (tinfo.IsSealed) baseName += "SEALED";
+                    if (tinfo.IsAbstract) baseName += "ABSTRACT";
+                }
+
                 baseName += "CLASS";
                 baseName += "CLASS";
-            }            
+            }
 
 
-            baseName += "." + type.GetFriendlyName();
+            baseName += " " + tinfo.GetFriendlyName();
 
 
             Object instance = null;
             Object instance = null;
 
 
-            try { instance = System.Activator.CreateInstance(type); }
+            try { instance = Activator.CreateInstance(tinfo); }
             catch { }
             catch { }
 
 
-            foreach (var f in type.DeclaredFields)
+            foreach (var m in tinfo.DeclaredMembers)
             {
             {
-                if (f.IsPrivate) continue;
+                var signatures = GetMemberSignature(instance, m);
+                if (signatures == null) continue;
+                foreach (var s in signatures)
+                {
+                    yield return baseName + " { " +  s + " } ";
+                }
+            }            
+        }
 
 
-                var name = baseName;
+        public static IEnumerable<string> GetMemberSignature(Object instance, MemberInfo minfo)
+        {
+            if (minfo is FieldInfo finfo) return GetFieldSignature(instance, finfo);
 
 
-                if (f.IsLiteral) name += ".CONST";
-                else name += ".FIELD";
+            if (minfo is TypeInfo tinfo) return GetTypeSignature(tinfo);
 
 
-                name += $".{f.FieldType.GetFriendlyName()}.{f.Name}";
+            if (minfo is PropertyInfo pinfo) return GetPropertySignature(instance, pinfo);
 
 
-                if (f.IsStatic)
-                {
-                    var v = f.GetValue(null);
-                    if (v != null) name += Invariant($"= {v}");
-                }
-                else if (instance != null)
-                {
-                    var v = f.GetValue(instance);
-                    if (v != null) name += Invariant($"= {v}");
-                }
+            if (minfo is MethodInfo xinfo) return GetMethodSignature(instance, xinfo);
 
 
-                yield return name;
-            }
+            if (minfo is ConstructorInfo cinfo) return GetMethodSignature(instance, cinfo);
 
 
-            /* property getters and setters are already dumped by methods
-            foreach (var p in type.DeclaredProperties)
-            {
-                var pname = $"{baseName}.PROPERTY.{p.PropertyType.GetFriendlyName()}.{p.Name}";
+            return null;
+        }
+
+        public static IEnumerable<string> GetFieldSignature(Object instance, FieldInfo finfo)
+        {
+            if (!IsVisible(finfo)) yield break;
 
 
-                var getter = p.GetGetMethod();
-                if (getter != null && !getter.IsPrivate) yield return pname + ".Get()";
+            var name = string.Empty;
 
 
-                var setter = p.GetSetMethod();
-                if (setter != null && !setter.IsPrivate) yield return pname + ".Set()";                
-            }
-            */
+            if (finfo.IsLiteral) name += "CONST";
+            else name += "FIELD";
 
 
-            foreach(var m in type.DeclaredMethods)
+            name += $" {finfo.Name} {finfo.FieldType.GetFriendlyName()}";
+
+            if (finfo.IsStatic)
             {
             {
-                // TODO: if parameters have default values, dump the same method multiple times with one parameter less each time.
+                name = "STATIC" + name;
 
 
-                if (m.IsPrivate) continue;
+                var v = finfo.GetValue(null);
+                if (v != null) name += Invariant($"= {v}");
+            }
+            else if (instance != null)
+            {
+                var v = finfo.GetValue(instance);
+                if (v != null) name += Invariant($"= {v}");
+            }
 
 
-                var mname = $"{baseName}.METHOD.{m.ReturnType.GetFriendlyName()}.{m.Name}";
+            yield return name;
+        }
 
 
-                var mparams = m.GetParameters()
-                    .Select(item => item.ParameterType.GetFriendlyName())
-                    .ToList();
+        public static IEnumerable<string> GetPropertySignature(Object instance, PropertyInfo pinfo)
+        {
+            var pname = $"{pinfo.Name} {pinfo.PropertyType.GetFriendlyName()}";
 
 
-                yield return mname + "(" + string.Join(", ", mparams) + ")";
-            }
+            var getter = pinfo.GetGetMethod();
+            if (IsVisible(getter,true)) yield return GetMethodModifiers(getter)+ "PROPERTYGET " + pname;
 
 
-            foreach(var n in type.DeclaredNestedTypes)
+            var setter = pinfo.GetSetMethod();
+            if (IsVisible(setter, true)) yield return GetMethodModifiers(getter) + "PROPERTYSET " + pname;
+        }
+
+        public static IEnumerable<string> GetMethodSignature(Object instance, MethodBase minfo)
+        {
+            // TODO: if parameters have default values, dump the same method multiple times with one parameter less each time.
+
+            
+
+            var mname = GetMethodModifiers(minfo);
+
+            if (minfo is MethodInfo mminfo)
             {
             {
-                foreach (var nn in DumpAPI(baseName, n)) yield return nn;
+                if (!IsVisible(minfo)) yield break;
+                mname += "METHOD " + mminfo.Name + $" {mminfo.ReturnType.GetFriendlyName()} ";
             }
             }
+
+            if (minfo is ConstructorInfo cinfo)
+            {
+                if (!IsVisible(minfo,true)) yield break;
+                mname += "CONSTRUCTOR ";
+            }             
+
+            var mparams = minfo.GetParameters()
+                .Select(item => item.ParameterType.GetFriendlyName())
+                .ToList();
+
+            yield return mname + "(" + string.Join(", ", mparams) + ")";
+        }
+
+        public static bool IsVisible(FieldInfo finfo)
+        {
+            if (finfo == null) return false;
+
+            if (finfo.IsPrivate) return false;
+            if (finfo.IsAssembly) return false;            
+            if (finfo.IsFamilyOrAssembly) return false;
+
+            return true;
+        }
+
+        public static bool IsVisible(MethodBase minfo, bool withSpecials = false)
+        {
+            if (minfo == null) return false;
+
+            if (minfo.IsSpecialName && !withSpecials) return false;
+
+            if (minfo.IsPrivate) return false;            
+            if (minfo.IsAssembly) return false;
+            if (minfo.IsFamilyOrAssembly) return false;
+
+            return true;
+        }
+
+        public static string GetMethodModifiers(MethodBase minfo)
+        {
+            if (minfo.IsPrivate) return string.Empty;
+
+            var mod = string.Empty;
+
+            if (minfo.IsFamily) mod += "PROTECTED";
+            if (minfo.IsStatic) mod += "STATIC";            
+
+            if (minfo.IsAbstract) mod += "ABSTRACT";
+            else if (minfo.IsVirtual) mod += "VIRTUAL";
+
+            return mod;
         }
         }
 
 
         public static string GetFriendlyName(this Type tinfo)
         public static string GetFriendlyName(this Type tinfo)
@@ -129,6 +217,10 @@ namespace SharpGLTF
             name = name.Replace("`2", "");
             name = name.Replace("`2", "");
             name = name.Replace("`3", "");
             name = name.Replace("`3", "");
             name = name.Replace("`4", "");
             name = name.Replace("`4", "");
+            name = name.Replace("`5", "");
+            name = name.Replace("`6", "");
+            name = name.Replace("`7", "");
+            name = name.Replace("`8", "");
 
 
             var gpm = tinfo
             var gpm = tinfo
                 .GenericTypeArguments
                 .GenericTypeArguments

+ 1 - 1
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadPollyTest.cs

@@ -40,7 +40,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
 
             var scene = model.DefaultScene;
             var scene = model.DefaultScene;
 
 
-            var pollyNode = scene.FindNode("Polly_Display");
+            var pollyNode = scene.FindNode(n => n.Name == "Polly_Display");
 
 
             var pollyPrimitive = pollyNode.Mesh.Primitives[0];
             var pollyPrimitive = pollyNode.Mesh.Primitives[0];