Browse Source

attempting to fix accessors with missing BufferView [WIP]

vpenades 6 months ago
parent
commit
1279d5b993

+ 1 - 1
src/Directory.Build.props

@@ -12,7 +12,7 @@
   <!-- Configuration =================================================================================== -->
   <!-- Configuration =================================================================================== -->
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <LangVersion>8.0</LangVersion>
+    <LangVersion>10.0</LangVersion>
     <IsPackable>true</IsPackable>
     <IsPackable>true</IsPackable>
   </PropertyGroup>
   </PropertyGroup>
 
 

+ 17 - 10
src/SharpGLTF.Core/Diagnostics/DebugViews.cs

@@ -53,7 +53,7 @@ namespace SharpGLTF.Diagnostics
 
 
         public String Identity => $"Accessor[{_Value.LogicalIndex}] {_Value.Name}";
         public String Identity => $"Accessor[{_Value.LogicalIndex}] {_Value.Name}";
 
 
-        public Schema2.BufferView Source => _Value.SourceBufferView;
+        public Schema2.BufferView Source => _Value.TryGetBufferView(out var bv) ? bv : null;
 
 
         public (Schema2.DimensionType Dimensions, Schema2.EncodingType Encoding, bool Normalized) Format => (_Value.Dimensions, _Value.Encoding, _Value.Normalized);
         public (Schema2.DimensionType Dimensions, Schema2.EncodingType Encoding, bool Normalized) Format => (_Value.Dimensions, _Value.Encoding, _Value.Normalized);
 
 
@@ -62,14 +62,15 @@ namespace SharpGLTF.Diagnostics
             get
             get
             {
             {
                 if (_Value == null) return null;
                 if (_Value == null) return null;
-                if (Source == null) return null;
 
 
-                if (Source.IsIndexBuffer)
+                if (!_Value.TryGetBufferView(out var bv)) return null;                
+
+                if (bv.IsIndexBuffer)
                 {
                 {
                     return _Value.AsIndicesArray().Cast<Object>().ToArray();
                     return _Value.AsIndicesArray().Cast<Object>().ToArray();
                 }
                 }
 
 
-                if (Source.IsVertexBuffer)
+                if (bv.IsVertexBuffer)
                 {
                 {
                     if (_Value.Dimensions == Schema2.DimensionType.SCALAR) return _Value.AsScalarArray().Cast<Object>().ToArray();
                     if (_Value.Dimensions == Schema2.DimensionType.SCALAR) return _Value.AsScalarArray().Cast<Object>().ToArray();
                     if (_Value.Dimensions == Schema2.DimensionType.VEC2) return _Value.AsVector2Array().Cast<Object>().ToArray();
                     if (_Value.Dimensions == Schema2.DimensionType.VEC2) return _Value.AsVector2Array().Cast<Object>().ToArray();
@@ -79,15 +80,21 @@ namespace SharpGLTF.Diagnostics
 
 
                 if (_Value.Dimensions == Schema2.DimensionType.MAT4) return _Value.AsMatrix4x4Array().Cast<Object>().ToArray();
                 if (_Value.Dimensions == Schema2.DimensionType.MAT4) return _Value.AsMatrix4x4Array().Cast<Object>().ToArray();
 
 
-                var itemByteSz = _Value.Format.ByteSize;
-                var byteStride = Math.Max(_Value.SourceBufferView.ByteStride, itemByteSz);
-                var items = new ArraySegment<Byte>[_Value.Count];
+                // fallback to plain bytes
 
 
-                var buffer = _Value.SourceBufferView.Content.Slice(_Value.ByteOffset, _Value.Count * byteStride);
+                var items = new ArraySegment<Byte>[_Value.Count];
 
 
-                for (int i = 0; i < items.Length; ++i )
+                if (_Value.TryGetBufferView(out var bufferView))
                 {
                 {
-                    items[i] = buffer.Slice(i * byteStride, itemByteSz);
+                    var itemByteSz = _Value.Format.ByteSize;
+                    var byteStride = Math.Max(bufferView.ByteStride, itemByteSz);
+
+                    var buffer = bufferView.Content.Slice(_Value.ByteOffset, _Value.Count * byteStride);
+
+                    for (int i = 0; i < items.Length; ++i)
+                    {
+                        items[i] = buffer.Slice(i * byteStride, itemByteSz);
+                    }
                 }
                 }
 
 
                 return items.Cast<Object>().ToArray();
                 return items.Cast<Object>().ToArray();

+ 14 - 9
src/SharpGLTF.Core/Diagnostics/DebuggerDisplay.cs

@@ -72,14 +72,15 @@ namespace SharpGLTF.Diagnostics
 
 
         public static string ToReportLong(this Accessor accessor)
         public static string ToReportLong(this Accessor accessor)
         {
         {
-            var path = string.Empty;
-
-            var bv = accessor.SourceBufferView;
+            var path = string.Empty;            
 
 
-            if (bv.IsVertexBuffer) path += "VertexBuffer";
-            else if (bv.IsIndexBuffer) path += "IndexBuffer";
-            else path += "BufferView";
-            path += $"[{bv.LogicalIndex}ᴵᵈˣ] ⇨";
+            if (accessor.TryGetBufferView(out var bv))
+            {
+                if (bv.IsVertexBuffer) path += "VertexBuffer";
+                else if (bv.IsIndexBuffer) path += "IndexBuffer";
+                else path += "BufferView";
+                path += $"[{bv.LogicalIndex}ᴵᵈˣ] ⇨";
+            }
 
 
             path += $" Accessor[{accessor.LogicalIndex}ᴵᵈˣ] Offset:{accessor.ByteOffset}ᴮʸᵗᵉˢ ⇨";
             path += $" Accessor[{accessor.LogicalIndex}ᴵᵈˣ] Offset:{accessor.ByteOffset}ᴮʸᵗᵉˢ ⇨";
 
 
@@ -140,12 +141,16 @@ namespace SharpGLTF.Diagnostics
                 case PrimitiveType.LINES:
                 case PrimitiveType.LINES:
                 case PrimitiveType.LINE_LOOP:
                 case PrimitiveType.LINE_LOOP:
                 case PrimitiveType.LINE_STRIP:
                 case PrimitiveType.LINE_STRIP:
-                    pcount = indices.HasValue ? prim.DrawPrimitiveType.GetLinesIndices(indices.Value).Count() : prim.DrawPrimitiveType.GetLinesIndices(vcount).Count();
+                    pcount = indices != null
+                        ? prim.DrawPrimitiveType.GetLinesIndices(indices).Count()
+                        : prim.DrawPrimitiveType.GetLinesIndices(vcount).Count();
                     break;
                     break;
                 case PrimitiveType.TRIANGLES:
                 case PrimitiveType.TRIANGLES:
                 case PrimitiveType.TRIANGLE_FAN:
                 case PrimitiveType.TRIANGLE_FAN:
                 case PrimitiveType.TRIANGLE_STRIP:
                 case PrimitiveType.TRIANGLE_STRIP:
-                    pcount = indices.HasValue ? prim.DrawPrimitiveType.GetTrianglesIndices(indices.Value).Count() : prim.DrawPrimitiveType.GetTrianglesIndices(vcount).Count();
+                    pcount = indices != null
+                        ? prim.DrawPrimitiveType.GetTrianglesIndices(indices).Count()
+                        : prim.DrawPrimitiveType.GetTrianglesIndices(vcount).Count();
                     break;
                     break;
             }
             }
 
 

+ 1 - 1
src/SharpGLTF.Core/Memory/ColorArray.cs

@@ -14,7 +14,7 @@ namespace SharpGLTF.Memory
     /// 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("Color4[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Color4[{Count}]")]
-    public readonly struct ColorArray : IList<Vector4>, IReadOnlyList<Vector4>
+    public readonly struct ColorArray : IAccessorArray<Vector4>
     {
     {
         #region constructors
         #region constructors
 
 

+ 22 - 22
src/SharpGLTF.Core/Memory/FloatingArrays.cs

@@ -225,10 +225,10 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{single}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{single}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Float[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Float[{Count}]")]
-    public readonly struct ScalarArray : IList<Single>, IReadOnlyList<Single>
+    public readonly struct ScalarArray : IAccessorArray<Single>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -321,10 +321,10 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Vector2}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Vector2}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Vector2[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Vector2[{Count}]")]
-    public readonly struct Vector2Array : IList<Vector2>, IReadOnlyList<Vector2>
+    public readonly struct Vector2Array : IAccessorArray<Vector2>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -425,10 +425,10 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Vector3}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Vector3}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Vector3[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Vector3[{Count}]")]
-    public readonly struct Vector3Array : IList<Vector3>, IReadOnlyList<Vector3>
+    public readonly struct Vector3Array : IAccessorArray<Vector3>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -530,10 +530,10 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Vector4}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Vector4}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Vector4[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Vector4[{Count}]")]
-    public readonly struct Vector4Array : IList<Vector4>, IReadOnlyList<Vector4>
+    public readonly struct Vector4Array : IAccessorArray<Vector4>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -636,10 +636,10 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Quaternion}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Quaternion}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Quaternion[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Quaternion[{Count}]")]
-    public readonly struct QuaternionArray : IList<Quaternion>, IReadOnlyList<Quaternion>
+    public readonly struct QuaternionArray : IAccessorArray<Quaternion>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -720,14 +720,14 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Matrix3x2}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Matrix3x2}"/>.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     /// <see cref="Vector"/> namespace doesn't support a 2x2 matrix, so the array is<br/>
     /// <see cref="Vector"/> namespace doesn't support a 2x2 matrix, so the array is<br/>
     /// decoded as a Matrix2x2 matrix internally, but exposed as a <see cref="Matrix3x2"/>.
     /// decoded as a Matrix2x2 matrix internally, but exposed as a <see cref="Matrix3x2"/>.
     /// </remarks>
     /// </remarks>
     [System.Diagnostics.DebuggerDisplay("Matrix2x2[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Matrix2x2[{Count}]")]
-    public readonly struct Matrix2x2Array : IList<Matrix3x2>, IReadOnlyList<Matrix3x2>
+    public readonly struct Matrix2x2Array : IAccessorArray<Matrix3x2>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -813,10 +813,10 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Matrix3x2}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Matrix3x2}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Matrix3x2[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Matrix3x2[{Count}]")]
-    public readonly struct Matrix3x2Array : IList<Matrix3x2>, IReadOnlyList<Matrix3x2>
+    public readonly struct Matrix3x2Array : IAccessorArray<Matrix3x2>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -904,14 +904,14 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Matrix4x4}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Matrix4x4}"/>.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     /// <see cref="Vector"/> namespace doesn't support a 3x3 matrix, so the array is<br/>
     /// <see cref="Vector"/> namespace doesn't support a 3x3 matrix, so the array is<br/>
     /// decoded as a Matrix3x3 matrix internally, but exposed as a <see cref="Matrix4x4"/>.
     /// decoded as a Matrix3x3 matrix internally, but exposed as a <see cref="Matrix4x4"/>.
     /// </remarks>
     /// </remarks>
     [System.Diagnostics.DebuggerDisplay("Matrix3x3[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Matrix3x3[{Count}]")]
-    public readonly struct Matrix3x3Array : IList<Matrix4x4>, IReadOnlyList<Matrix4x4>
+    public readonly struct Matrix3x3Array : IAccessorArray<Matrix4x4>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -1003,14 +1003,14 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Matrix4x4}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Matrix4x4}"/>.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     /// <see cref="Vector"/> namespace doesn't support a 4x3 matrix, so the array is<br/>
     /// <see cref="Vector"/> namespace doesn't support a 4x3 matrix, so the array is<br/>
     /// decoded as a Matrix4x3 matrix internally, but exposed as a <see cref="Matrix4x4"/>.
     /// decoded as a Matrix4x3 matrix internally, but exposed as a <see cref="Matrix4x4"/>.
     /// </remarks>
     /// </remarks>
     [System.Diagnostics.DebuggerDisplay("Matrix4x3[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Matrix4x3[{Count}]")]
-    public readonly struct Matrix4x3Array : IList<Matrix4x4>, IReadOnlyList<Matrix4x4>
+    public readonly struct Matrix4x3Array : IAccessorArray<Matrix4x4>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -1105,10 +1105,10 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{Matrix4x4}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Matrix4x4}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Matrix4x4[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Matrix4x4[{Count}]")]
-    public readonly struct Matrix4x4Array : IList<Matrix4x4>, IReadOnlyList<Matrix4x4>
+    public readonly struct Matrix4x4Array : IAccessorArray<Matrix4x4>
     {
     {
         #region constructors
         #region constructors
 
 
@@ -1207,10 +1207,10 @@ namespace SharpGLTF.Memory
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an IList{Single[]}/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{Single[]}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Float[][{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Float[][{Count}]")]
-    public readonly struct MultiArray : IList<Single[]>, IReadOnlyList<Single[]>
+    public readonly struct MultiArray : IAccessorArray<Single[]>
     {
     {
         #region constructors
         #region constructors
         public MultiArray(BYTES source, int byteOffset, int itemsCount, int byteStride, int dimensions, ENCODING encoding, Boolean normalized)
         public MultiArray(BYTES source, int byteOffset, int itemsCount, int byteStride, int dimensions, ENCODING encoding, Boolean normalized)

+ 2 - 2
src/SharpGLTF.Core/Memory/IntegerArrays.cs

@@ -10,10 +10,10 @@ using ENCODING = SharpGLTF.Schema2.IndexEncodingType;
 namespace SharpGLTF.Memory
 namespace SharpGLTF.Memory
 {
 {
     /// <summary>
     /// <summary>
-    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IList{UInt32}"/>.
+    /// Wraps an encoded <see cref="BYTES"/> and exposes it as an <see cref="IAccessorArray{UInt32}"/>.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("Integer[{Count}]")]
     [System.Diagnostics.DebuggerDisplay("Integer[{Count}]")]
-    public readonly struct IntegerArray : IList<UInt32>, IReadOnlyList<UInt32>
+    public readonly struct IntegerArray : IAccessorArray<UInt32>
     {
     {
         #region constructors
         #region constructors
 
 

+ 65 - 5
src/SharpGLTF.Core/Memory/MemoryAccessor.cs

@@ -337,7 +337,7 @@ namespace SharpGLTF.Memory
             this._Data = default;
             this._Data = default;
         }
         }
 
 
-        public static IList<Single> CreateScalarSparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
+        public static IAccessorArray<Single> CreateScalarSparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
         {
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(topValues, nameof(topValues));
             Guard.NotNull(topValues, nameof(topValues));
@@ -349,7 +349,28 @@ namespace SharpGLTF.Memory
             return new SparseArray<Single>(bottom.AsScalarArray(), topValues.AsScalarArray(), topKeys);
             return new SparseArray<Single>(bottom.AsScalarArray(), topValues.AsScalarArray(), topKeys);
         }
         }
 
 
-        public static IList<Vector2> CreateVector2SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
+        public static IAccessorArray<Single> CreateScalarSparseArray(int bottomCount, IntegerArray topKeys, MemoryAccessor topValues)
+        {            
+            Guard.NotNull(topValues, nameof(topValues));            
+            Guard.IsTrue(topKeys.Count <= bottomCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues._Slicer.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottomCount), nameof(topKeys));
+
+            return new SparseArray<Single>(new ZeroAccessorArray<float>(bottomCount), topValues.AsScalarArray(), topKeys);
+        }
+
+
+        public static IAccessorArray<Vector2> CreateVector2SparseArray(int bottomCount, IntegerArray topKeys, MemoryAccessor topValues)
+        {            
+            Guard.NotNull(topValues, nameof(topValues));            
+            Guard.IsTrue(topKeys.Count <= bottomCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues._Slicer.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottomCount), nameof(topKeys));
+
+            return new SparseArray<Vector2>(new ZeroAccessorArray<Vector2>(bottomCount), topValues.AsVector2Array(), topKeys);
+        }
+
+        public static IAccessorArray<Vector2> CreateVector2SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
         {
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(topValues, nameof(topValues));
             Guard.NotNull(topValues, nameof(topValues));
@@ -361,7 +382,18 @@ namespace SharpGLTF.Memory
             return new SparseArray<Vector2>(bottom.AsVector2Array(), topValues.AsVector2Array(), topKeys);
             return new SparseArray<Vector2>(bottom.AsVector2Array(), topValues.AsVector2Array(), topKeys);
         }
         }
 
 
-        public static IList<Vector3> CreateVector3SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
+
+        public static IAccessorArray<Vector3> CreateVector3SparseArray(int bottomCount, IntegerArray topKeys, MemoryAccessor topValues)
+        {            
+            Guard.NotNull(topValues, nameof(topValues));            
+            Guard.IsTrue(topKeys.Count <= bottomCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues._Slicer.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottomCount), nameof(topKeys));
+
+            return new SparseArray<Vector3>(new ZeroAccessorArray<Vector3>(bottomCount), topValues.AsVector3Array(), topKeys);
+        }
+
+        public static IAccessorArray<Vector3> CreateVector3SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
         {
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(topValues, nameof(topValues));
             Guard.NotNull(topValues, nameof(topValues));
@@ -373,7 +405,17 @@ namespace SharpGLTF.Memory
             return new SparseArray<Vector3>(bottom.AsVector3Array(), topValues.AsVector3Array(), topKeys);
             return new SparseArray<Vector3>(bottom.AsVector3Array(), topValues.AsVector3Array(), topKeys);
         }
         }
 
 
-        public static IList<Vector4> CreateVector4SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
+        public static IAccessorArray<Vector4> CreateVector4SparseArray(int bottomCount, IntegerArray topKeys, MemoryAccessor topValues)
+        {            
+            Guard.NotNull(topValues, nameof(topValues));            
+            Guard.IsTrue(topKeys.Count <= bottomCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues._Slicer.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottomCount), nameof(topKeys));
+
+            return new SparseArray<Vector4>(new ZeroAccessorArray<Vector4>(bottomCount), topValues.AsVector4Array(), topKeys);
+        }
+
+        public static IAccessorArray<Vector4> CreateVector4SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
         {
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(topValues, nameof(topValues));
             Guard.NotNull(topValues, nameof(topValues));
@@ -385,7 +427,17 @@ namespace SharpGLTF.Memory
             return new SparseArray<Vector4>(bottom.AsVector4Array(), topValues.AsVector4Array(), topKeys);
             return new SparseArray<Vector4>(bottom.AsVector4Array(), topValues.AsVector4Array(), topKeys);
         }
         }
 
 
-        public static IList<Vector4> CreateColorSparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues, Single defaultW = 1)
+        public static IAccessorArray<Vector4> CreateColorSparseArray(int bottomCount, IntegerArray topKeys, MemoryAccessor topValues, Single defaultW = 1)
+        {            
+            Guard.NotNull(topValues, nameof(topValues));            
+            Guard.IsTrue(topKeys.Count <= bottomCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues._Slicer.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottomCount), nameof(topKeys));
+
+            return new SparseArray<Vector4>(new ZeroAccessorArray<Vector4>(bottomCount), topValues.AsColorArray(defaultW), topKeys);
+        }
+
+        public static IAccessorArray<Vector4> CreateColorSparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues, Single defaultW = 1)
         {
         {
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(bottom, nameof(bottom));
             Guard.NotNull(topValues, nameof(topValues));
             Guard.NotNull(topValues, nameof(topValues));
@@ -488,6 +540,14 @@ namespace SharpGLTF.Memory
             return new Matrix3x3Array(_Data, _Slicer.ByteOffset, _Slicer.ItemsCount, _Slicer.ByteStride, _Slicer.Encoding, _Slicer.Normalized);
             return new Matrix3x3Array(_Data, _Slicer.ByteOffset, _Slicer.ItemsCount, _Slicer.ByteStride, _Slicer.Encoding, _Slicer.Normalized);
         }
         }
 
 
+        public Matrix4x3Array AsMatrix4x3Array()
+        {
+            Guard.IsTrue(_Slicer.IsValidVertexAttribute, nameof(_Slicer));
+            // Guard.IsTrue(_Slicer.Dimensions == DIMENSIONS.MAT3, nameof(_Slicer));
+
+            return new Matrix4x3Array(_Data, _Slicer.ByteOffset, _Slicer.ItemsCount, _Slicer.ByteStride, _Slicer.Encoding, _Slicer.Normalized);
+        }
+
         public Matrix4x4Array AsMatrix4x4Array()
         public Matrix4x4Array AsMatrix4x4Array()
         {
         {
             Guard.IsTrue(_Slicer.IsValidVertexAttribute, nameof(_Slicer));
             Guard.IsTrue(_Slicer.IsValidVertexAttribute, nameof(_Slicer));

+ 6 - 5
src/SharpGLTF.Core/Memory/SparseArrays.cs

@@ -10,17 +10,18 @@ namespace SharpGLTF.Memory
     /// </summary>
     /// </summary>
     /// <typeparam name="T">An unmanage structure type.</typeparam>
     /// <typeparam name="T">An unmanage structure type.</typeparam>
     [System.Diagnostics.DebuggerDisplay("Sparse {typeof(T).Name} Accessor {Count}")]
     [System.Diagnostics.DebuggerDisplay("Sparse {typeof(T).Name} Accessor {Count}")]
-    public readonly struct SparseArray<T> : IList<T>, IReadOnlyList<T>
+    public readonly struct SparseArray<T> : IAccessorArray<T>
         where T : unmanaged
         where T : unmanaged
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        public SparseArray(IList<T> bottom, IList<T> top, IntegerArray topMapping)
+        public SparseArray(IReadOnlyList<T> bottom, IReadOnlyList<T> top, IReadOnlyList<uint> topMapping)
         {
         {
             _BottomItems = bottom;
             _BottomItems = bottom;
             _TopItems = top;
             _TopItems = top;
-            _Mapping = new Dictionary<int, int>();
 
 
+            // expand indices for fast access
+            _Mapping = new Dictionary<int, int>();
             for (int val = 0; val < topMapping.Count; ++val)
             for (int val = 0; val < topMapping.Count; ++val)
             {
             {
                 var key = (int)topMapping[val];
                 var key = (int)topMapping[val];
@@ -33,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 IList<T> _BottomItems;
+        private readonly IReadOnlyList<T> _BottomItems;
 
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly IList<T> _TopItems;
+        private readonly IReadOnlyList<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;

+ 221 - 91
src/SharpGLTF.Core/Schema2/gltf.Accessors.cs

@@ -47,6 +47,11 @@ namespace SharpGLTF.Schema2
 
 
         internal int _SourceBufferViewIndex => this._bufferView.AsValue(-1);
         internal int _SourceBufferViewIndex => this._bufferView.AsValue(-1);
 
 
+        /// <summary>
+        /// Gets the number of bytes, starting at <see cref="ByteOffset"/> use by this <see cref="Accessor"/>
+        /// </summary>
+        public int ByteLength => BufferView.GetAccessorByteLength(Format, Count, SourceBufferView);
+
         /// <summary>
         /// <summary>
         /// Gets the <see cref="BufferView"/> buffer that contains the items as an encoded byte array.
         /// Gets the <see cref="BufferView"/> buffer that contains the items as an encoded byte array.
         /// </summary>
         /// </summary>
@@ -58,14 +63,9 @@ namespace SharpGLTF.Schema2
         public int Count => this._count;
         public int Count => this._count;
 
 
         /// <summary>
         /// <summary>
-        /// Gets the starting byte offset within <see cref="SourceBufferView"/>.
-        /// </summary>
-        public int ByteOffset => this._byteOffset.AsValue(0);
-
-        /// <summary>
-        /// Gets the number of bytes, starting at <see cref="ByteOffset"/> use by this <see cref="Accessor"/>
+        /// Gets the starting byte offset within the byte data.
         /// </summary>
         /// </summary>
-        public int ByteLength => SourceBufferView.GetAccessorByteLength(Format, Count);
+        public int ByteOffset => this._byteOffset.AsValue(0);        
 
 
         /// <summary>
         /// <summary>
         /// Gets the <see cref="DimensionType"/> of an item.
         /// Gets the <see cref="DimensionType"/> of an item.
@@ -128,20 +128,34 @@ namespace SharpGLTF.Schema2
             _CachedType = Enum.TryParse<DimensionType>(this._type, out var r) ? r : DimensionType.CUSTOM;
             _CachedType = Enum.TryParse<DimensionType>(this._type, out var r) ? r : DimensionType.CUSTOM;
 
 
             return _CachedType.Value;
             return _CachedType.Value;
+        }        
+
+        internal bool _TryGetMemoryAccessor(out MemoryAccessor mem)
+        {
+            if (!TryGetBufferView(out var view)) { mem = default; return false; }
+            var info = new MemoryAccessInfo(null, ByteOffset, Count, view.ByteStride, Format);
+            mem = new MemoryAccessor(view.Content, info);
+            return true;
         }
         }
 
 
-        internal MemoryAccessor _GetMemoryAccessor(string name = null)
+        internal bool _TryGetMemoryAccessor(string name, out MemoryAccessor mem)
         {
         {
-            var view = SourceBufferView;
+            if (!TryGetBufferView(out var view)) { mem = default; return false; }
             var info = new MemoryAccessInfo(name, ByteOffset, Count, view.ByteStride, Format);
             var info = new MemoryAccessInfo(name, ByteOffset, Count, view.ByteStride, Format);
-            return new MemoryAccessor(view.Content, info);
+            mem = new MemoryAccessor(view.Content, info);
+            return true;
         }
         }
 
 
-        internal KeyValuePair<IntegerArray, MemoryAccessor>? _GetSparseMemoryAccessor()
+        public bool TryGetBufferView(out BufferView bv)
         {
         {
-            return this._sparse == null
-                ? (KeyValuePair<IntegerArray, MemoryAccessor>?)null
-                : this._sparse._CreateMemoryAccessors(this);
+            if (_bufferView.HasValue)
+            {
+                bv = this.LogicalParent.LogicalBufferViews[this._bufferView.Value];
+                return true;
+            }
+
+            bv = null;
+            return false;
         }
         }
 
 
         public void UpdateBounds()
         public void UpdateBounds()
@@ -164,20 +178,28 @@ namespace SharpGLTF.Schema2
                 this._max.Add(double.MinValue);
                 this._max.Add(double.MinValue);
             }
             }
 
 
-            var array = new MultiArray(this.SourceBufferView.Content, this.ByteOffset, this.Count, this.SourceBufferView.ByteStride, dimensions, this.Encoding, false);
-
-            var current = new float[dimensions];
-
-            for (int i = 0; i < array.Count; ++i)
+            if (this.TryGetBufferView(out var view))
             {
             {
-                array.CopyItemTo(i, current);
+                var array = new MultiArray(view.Content, this.ByteOffset, this.Count, view.ByteStride, dimensions, this.Encoding, false);
+
+                var current = new float[dimensions];
 
 
-                for (int j = 0; j < current.Length; ++j)
+                for (int i = 0; i < array.Count; ++i)
                 {
                 {
-                    this._min[j] = Math.Min(this._min[j], current[j]);
-                    this._max[j] = Math.Max(this._max[j], current[j]);
+                    array.CopyItemTo(i, current);
+
+                    for (int j = 0; j < current.Length; ++j)
+                    {
+                        this._min[j] = Math.Min(this._min[j], current[j]);
+                        this._max[j] = Math.Max(this._max[j], current[j]);
+                    }
                 }
                 }
             }
             }
+            else
+            {
+                for (int i = 0; i < this._min.Count; ++i) this._min[i] = 0;
+                for (int i = 0; i < this._max.Count; ++i) this._max[i] = 0;
+            }
         }
         }
 
 
         #endregion
         #endregion
@@ -213,18 +235,23 @@ namespace SharpGLTF.Schema2
             UpdateBounds();
             UpdateBounds();
         }
         }
 
 
-        public IList<Matrix3x2> AsMatrix2x2Array()
+        public IAccessorArray<Matrix3x2> AsMatrix2x2Array()
         {
         {
-            return _GetMemoryAccessor().AsMatrix2x2Array();
+            return _TryGetMemoryAccessor(out var mem)
+                ? mem.AsMatrix2x2Array()
+                : new ZeroAccessorArray<Matrix3x2>(this._count);
         }
         }
 
 
-        public IList<Matrix4x4> AsMatrix3x3Array()
+        public IAccessorArray<Matrix4x4> AsMatrix3x3Array()
         {
         {
-            return _GetMemoryAccessor().AsMatrix3x3Array();
+            return _TryGetMemoryAccessor(out var mem)
+                ? mem.AsMatrix3x3Array()
+                : new ZeroAccessorArray<Matrix4x4>(this._count);
         }
         }
 
 
-        public IList<Matrix4x4> AsMatrix4x3Array()
+        public IAccessorArray<Matrix4x4> AsMatrix4x3Array()
         {
         {
+            /*
             const int dimsize = 4 * 3;
             const int dimsize = 4 * 3;
 
 
             var view = SourceBufferView;
             var view = SourceBufferView;
@@ -232,16 +259,59 @@ namespace SharpGLTF.Schema2
             var content = view.Content.Slice(this.ByteOffset, Count * stride);
             var content = view.Content.Slice(this.ByteOffset, Count * stride);
 
 
             return new Matrix4x3Array(content, stride, this.Encoding, this.Normalized);
             return new Matrix4x3Array(content, stride, this.Encoding, this.Normalized);
+            */
+
+            return _TryGetMemoryAccessor(out var mem)
+                ? mem.AsMatrix4x3Array()
+                : new ZeroAccessorArray<Matrix4x4>(this._count);
         }
         }
 
 
-        public IList<Matrix4x4> AsMatrix4x4Array()
+        public IAccessorArray<Matrix4x4> AsMatrix4x4Array()
         {
         {
-            return _GetMemoryAccessor().AsMatrix4x4Array();
+            return _TryGetMemoryAccessor(out var mem)
+                ? mem.AsMatrix4x4Array()
+                : new ZeroAccessorArray<Matrix4x4>(this._count);
         }
         }
 
 
+        [Obsolete("Use AsMatrix4x4Array instead", true)]
         internal IReadOnlyList<Matrix4x4> AsMatrix4x4ReadOnlyList()
         internal IReadOnlyList<Matrix4x4> AsMatrix4x4ReadOnlyList()
         {
         {
-            return _GetMemoryAccessor().AsMatrix4x4Array();
+            return _TryGetMemoryAccessor(out var mem)
+                ? mem.AsMatrix4x4Array()
+                : new ZeroAccessorArray<Matrix4x4>(this._count);
+        }
+
+        public IAccessorArray<Quaternion> AsQuaternionArray()
+        {
+            if (_TryGetMemoryAccessor(out var memory))
+            {
+                if (this._sparse == null) return memory.AsQuaternionArray();
+
+                throw new NotImplementedException();
+            }
+            else
+            {
+                if (this._sparse == null) return new ZeroAccessorArray<Quaternion>(this._count);
+
+                throw new NotImplementedException();
+            }
+        }
+
+        public IAccessorArray<Single[]> AsMultiArray(int dimensions)
+        {
+            if (_TryGetMemoryAccessor(out var memory))
+            {
+
+                if (this._sparse == null) return memory.AsMultiArray(dimensions);
+
+                throw new NotImplementedException();
+            }
+            else
+            {
+                if (this._sparse == null) return new ZeroAccessorArray<Single[]>(this._count);
+
+                throw new NotImplementedException();
+            }
         }
         }
 
 
         #endregion
         #endregion
@@ -272,12 +342,14 @@ namespace SharpGLTF.Schema2
             SetData(buffer, bufferByteOffset, itemCount, DimensionType.SCALAR, encoding.ToComponent(), false);
             SetData(buffer, bufferByteOffset, itemCount, DimensionType.SCALAR, encoding.ToComponent(), false);
         }
         }
 
 
-        public IntegerArray AsIndicesArray()
+        public IAccessorArray<UInt32> AsIndicesArray()
         {
         {
             Guard.IsFalse(this.IsSparse, nameof(IsSparse));
             Guard.IsFalse(this.IsSparse, nameof(IsSparse));
             Guard.IsTrue(this.Dimensions == DimensionType.SCALAR, nameof(Dimensions));
             Guard.IsTrue(this.Dimensions == DimensionType.SCALAR, nameof(Dimensions));
 
 
-            return new IntegerArray(SourceBufferView.Content, this.ByteOffset, this._count, this.Encoding.ToIndex());
+            return _TryGetMemoryAccessor(out var mem)
+                ? new IntegerArray(SourceBufferView.Content, this.ByteOffset, this._count, this.Encoding.ToIndex())
+                : new ZeroAccessorArray<UInt32>(this._count);
         }
         }
 
 
         #endregion
         #endregion
@@ -312,83 +384,119 @@ namespace SharpGLTF.Schema2
             SetData(buffer, bufferByteOffset, itemCount, dimensions, encoding, normalized);
             SetData(buffer, bufferByteOffset, itemCount, dimensions, encoding, normalized);
         }
         }
 
 
-        public IList<Single> AsScalarArray()
+        internal IAccessorArray<T> AsVertexArray<T>()
         {
         {
-            var memory = _GetMemoryAccessor();
+            if (typeof(T) == typeof(float)) return (IAccessorArray<T>)AsScalarArray();
+            if (typeof(T) == typeof(Vector2)) return (IAccessorArray<T>)AsVector2Array();
+            if (typeof(T) == typeof(Vector3)) return (IAccessorArray<T>)AsVector3Array();
+            if (typeof(T) == typeof(Vector4)) return (IAccessorArray<T>)AsVector4Array();
 
 
-            if (this._sparse == null) return memory.AsScalarArray();
-
-            var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return MemoryAccessor.CreateScalarSparseArray(memory, sparseKV.Key, sparseKV.Value);
+            throw new NotImplementedException();
         }
         }
 
 
-        public IList<Vector2> AsVector2Array()
+        public IAccessorArray<Single> AsScalarArray()
         {
         {
-            var memory = _GetMemoryAccessor();
+            if (_TryGetMemoryAccessor(out var memory))
+            {
+                if (this._sparse == null) return memory.AsScalarArray();
 
 
-            if (this._sparse == null) return memory.AsVector2Array();
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateScalarSparseArray(memory, sparseKV.Key, sparseKV.Value);
+            }
+            else
+            {
+                if (this._sparse == null) return new ZeroAccessorArray<Single>(this._count);
 
 
-            var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return MemoryAccessor.CreateVector2SparseArray(memory, sparseKV.Key, sparseKV.Value);
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateScalarSparseArray(this._count, sparseKV.Key, sparseKV.Value);
+            }            
         }
         }
 
 
-        public IList<Vector3> AsVector3Array()
+        public IAccessorArray<Vector2> AsVector2Array()
         {
         {
-            var memory = _GetMemoryAccessor();
+            if (_TryGetMemoryAccessor(out var memory))
+            {
+                if (this._sparse == null) return memory.AsVector2Array();
 
 
-            if (this._sparse == null) return memory.AsVector3Array();
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateVector2SparseArray(memory, sparseKV.Key, sparseKV.Value);
+            }
+            else
+            {
+                if (this._sparse == null) return new ZeroAccessorArray<Vector2>(this._count);
 
 
-            var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return MemoryAccessor.CreateVector3SparseArray(memory, sparseKV.Key, sparseKV.Value);
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateVector2SparseArray(this._count, sparseKV.Key, sparseKV.Value);
+            }
         }
         }
 
 
-        public IList<Vector4> AsVector4Array()
+        public IAccessorArray<Vector3> AsVector3Array()
         {
         {
-            var memory = _GetMemoryAccessor();
-
-            if (this._sparse == null) return memory.AsVector4Array();
-
-            var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return MemoryAccessor.CreateVector4SparseArray(memory, sparseKV.Key, sparseKV.Value);
-        }
+            if (_TryGetMemoryAccessor(out var memory))
+            {
 
 
-        public IList<Vector4> AsColorArray(Single defaultW = 1)
-        {
-            var memory = _GetMemoryAccessor();
+                if (this._sparse == null) return memory.AsVector3Array();
 
 
-            if (this._sparse == null) return memory.AsColorArray(defaultW);
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateVector3SparseArray(memory, sparseKV.Key, sparseKV.Value);
+            }
+            else
+            {
+                if (this._sparse == null) return new ZeroAccessorArray<Vector3>(this._count);
 
 
-            var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return MemoryAccessor.CreateColorSparseArray(memory, sparseKV.Key, sparseKV.Value, defaultW);
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateVector3SparseArray(this._count, sparseKV.Key, sparseKV.Value);
+            }
         }
         }
 
 
-        public IList<Quaternion> AsQuaternionArray()
+        public IAccessorArray<Vector4> AsVector4Array()
         {
         {
-            var memory = _GetMemoryAccessor();
+            if (_TryGetMemoryAccessor(out var memory))
+            {
+                if (this._sparse == null) return memory.AsVector4Array();
 
 
-            if (this._sparse == null) return memory.AsQuaternionArray();
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateVector4SparseArray(memory, sparseKV.Key, sparseKV.Value);
+            }
+            else
+            {
+                if (this._sparse == null) return new ZeroAccessorArray<Vector4>(this._count);
 
 
-            throw new NotImplementedException();
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateVector4SparseArray(this._count, sparseKV.Key, sparseKV.Value);
+            }
         }
         }
 
 
-        public IList<Single[]> AsMultiArray(int dimensions)
+        public IAccessorArray<Vector4> AsColorArray(Single defaultW = 1)
         {
         {
-            var memory = _GetMemoryAccessor();
+            if (_TryGetMemoryAccessor(out var memory))
+            {
 
 
-            if (this._sparse == null) return memory.AsMultiArray(dimensions);
+                if (this._sparse == null) return memory.AsColorArray(defaultW);
 
 
-            throw new NotImplementedException();
-        }
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateColorSparseArray(memory, sparseKV.Key, sparseKV.Value, defaultW);
+            }
+            else
+            {
+                if (this._sparse == null) return new ZeroAccessorArray<Vector4>(this._count);
 
 
+                var sparseKV = this._sparse._CreateMemoryAccessors(this);
+                return MemoryAccessor.CreateColorSparseArray(this._count, sparseKV.Key, sparseKV.Value);
+            }
+        }
+        
         public ArraySegment<Byte> TryGetVertexBytes(int vertexIdx)
         public ArraySegment<Byte> TryGetVertexBytes(int vertexIdx)
         {
         {
             if (_sparse != null) throw new InvalidOperationException("Can't be used on Acessors with Sparse Data");
             if (_sparse != null) throw new InvalidOperationException("Can't be used on Acessors with Sparse Data");
 
 
+            if (!this.TryGetBufferView(out var bufferView)) return default;
+
             var itemByteSz = Encoding.ByteLength() * Dimensions.DimCount();
             var itemByteSz = Encoding.ByteLength() * Dimensions.DimCount();
-            var byteStride = Math.Max(itemByteSz, SourceBufferView.ByteStride);
+            var byteStride = Math.Max(itemByteSz, bufferView.ByteStride);
             var byteOffset = vertexIdx * byteStride;
             var byteOffset = vertexIdx * byteStride;
 
 
-            return SourceBufferView.Content.Slice(this.ByteOffset + (vertexIdx * byteStride), itemByteSz);
+            return bufferView.Content.Slice(this.ByteOffset + (vertexIdx * byteStride), itemByteSz);
         }
         }
 
 
         #endregion
         #endregion
@@ -400,7 +508,7 @@ namespace SharpGLTF.Schema2
             base.OnValidateReferences(validate);
             base.OnValidateReferences(validate);
 
 
             validate
             validate
-                .IsDefined(nameof(_bufferView), _bufferView)
+                // .IsDefined(nameof(_bufferView), _bufferView) as per specification, this is not required to be defined.
                 .NonNegative(nameof(_byteOffset), _byteOffset)
                 .NonNegative(nameof(_byteOffset), _byteOffset)
                 .IsGreaterOrEqual(nameof(_count), _count, _countMinimum)
                 .IsGreaterOrEqual(nameof(_count), _count, _countMinimum)
                 .IsNullOrIndex(nameof(_bufferView), _bufferView, this.LogicalParent.LogicalBufferViews);
                 .IsNullOrIndex(nameof(_bufferView), _bufferView, this.LogicalParent.LogicalBufferViews);
@@ -414,9 +522,15 @@ namespace SharpGLTF.Schema2
             // we cannot check the rest of the accessor.
             // we cannot check the rest of the accessor.
             if (this.Dimensions == DimensionType.CUSTOM) return;
             if (this.Dimensions == DimensionType.CUSTOM) return;
 
 
-            BufferView.VerifyAccess(validate, this.SourceBufferView, this.ByteOffset, this.Format, this.Count);
+            if (TryGetBufferView(out var bv))
+            {
+                BufferView.VerifyAccess(validate, bv, this.ByteOffset, this.Format, this.Count);
+            }            
 
 
-            validate.That(() => MemoryAccessor.VerifyAccessorBounds(_GetMemoryAccessor(), _min, _max));
+            if (_TryGetMemoryAccessor(out var mem))
+            {
+                validate.That(() => MemoryAccessor.VerifyAccessorBounds(mem, _min, _max));
+            }            
 
 
             // at this point we don't know which kind of data we're accessing, so it's up to the components
             // at this point we don't know which kind of data we're accessing, so it's up to the components
             // using this accessor to validate the data.
             // using this accessor to validate the data.
@@ -426,12 +540,19 @@ namespace SharpGLTF.Schema2
         {
         {
             validate = validate.GetContext(this);
             validate = validate.GetContext(this);
 
 
-            SourceBufferView.ValidateBufferUsageGPU(validate, BufferMode.ELEMENT_ARRAY_BUFFER);
             validate.IsAnyOf("Format", Format, (DimensionType.SCALAR, EncodingType.UNSIGNED_BYTE), (DimensionType.SCALAR, EncodingType.UNSIGNED_SHORT), (DimensionType.SCALAR, EncodingType.UNSIGNED_INT));
             validate.IsAnyOf("Format", Format, (DimensionType.SCALAR, EncodingType.UNSIGNED_BYTE), (DimensionType.SCALAR, EncodingType.UNSIGNED_SHORT), (DimensionType.SCALAR, EncodingType.UNSIGNED_INT));
 
 
-            validate.AreEqual(nameof(SourceBufferView.ByteStride), SourceBufferView.ByteStride, 0); // "bufferView.byteStride must not be defined for indices accessor.";
+            if (TryGetBufferView(out var bufferView))
+            {
+                bufferView.ValidateBufferUsageGPU(validate, BufferMode.ELEMENT_ARRAY_BUFFER);
+
+                validate.AreEqual(nameof(bufferView.ByteStride), bufferView.ByteStride, 0); // "bufferView.byteStride must not be defined for indices accessor.";
+            }
 
 
-            validate.That(() => MemoryAccessor.VerifyVertexIndices(_GetMemoryAccessor(), vertexCount));
+            if (_TryGetMemoryAccessor(out var mem))
+            {
+                validate.That(() => MemoryAccessor.VerifyVertexIndices(mem, vertexCount));
+            }            
         }
         }
 
 
         internal static void ValidateVertexAttributes(VALIDATIONCTX validate, IReadOnlyDictionary<string, Accessor> attributes, int skinsMaxJointCount)
         internal static void ValidateVertexAttributes(VALIDATIONCTX validate, IReadOnlyDictionary<string, Accessor> attributes, int skinsMaxJointCount)
@@ -463,7 +584,7 @@ namespace SharpGLTF.Schema2
         {
         {
             validate = validate.GetContext(this);
             validate = validate.GetContext(this);
 
 
-            SourceBufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
+            if (TryGetBufferView(out var bufferView)) bufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
 
 
             if (!this.LogicalParent.MeshQuantizationAllowed)
             if (!this.LogicalParent.MeshQuantizationAllowed)
             {
             {
@@ -481,7 +602,7 @@ namespace SharpGLTF.Schema2
         {
         {
             validate = validate.GetContext(this);
             validate = validate.GetContext(this);
 
 
-            SourceBufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
+            if (TryGetBufferView(out var bufferView)) bufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
 
 
             if (!this.LogicalParent.MeshQuantizationAllowed)
             if (!this.LogicalParent.MeshQuantizationAllowed)
             {
             {
@@ -501,7 +622,7 @@ namespace SharpGLTF.Schema2
         {
         {
             validate = validate.GetContext(this);
             validate = validate.GetContext(this);
 
 
-            SourceBufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
+            if (TryGetBufferView(out var bufferView)) bufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
 
 
             if (!this.LogicalParent.MeshQuantizationAllowed)
             if (!this.LogicalParent.MeshQuantizationAllowed)
             {
             {
@@ -526,7 +647,7 @@ namespace SharpGLTF.Schema2
         {
         {
             validate = validate.GetContext(this);
             validate = validate.GetContext(this);
 
 
-            SourceBufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
+            if (TryGetBufferView(out var bufferView)) bufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
 
 
             validate
             validate
                 .IsAnyOf(nameof(Format), Format, (DimensionType.VEC4, EncodingType.UNSIGNED_BYTE), (DimensionType.VEC4, EncodingType.UNSIGNED_SHORT), DimensionType.VEC4)
                 .IsAnyOf(nameof(Format), Format, (DimensionType.VEC4, EncodingType.UNSIGNED_BYTE), (DimensionType.VEC4, EncodingType.UNSIGNED_SHORT), DimensionType.VEC4)
@@ -538,8 +659,8 @@ namespace SharpGLTF.Schema2
             weights0?._ValidateWeights(validate);
             weights0?._ValidateWeights(validate);
             weights1?._ValidateWeights(validate);
             weights1?._ValidateWeights(validate);
 
 
-            var memory0 = weights0?._GetMemoryAccessor("WEIGHTS_0");
-            var memory1 = weights1?._GetMemoryAccessor("WEIGHTS_1");
+            var memory0 = (weights0?._TryGetMemoryAccessor("WEIGHTS_0", out var mem0) ?? false) ? mem0 : null;
+            var memory1 = (weights1?._TryGetMemoryAccessor("WEIGHTS_1", out var mem1) ?? false) ? mem1 : null;
 
 
             validate.That(() => MemoryAccessor.VerifyWeightsSum(memory0, memory1));
             validate.That(() => MemoryAccessor.VerifyWeightsSum(memory0, memory1));
         }
         }
@@ -548,7 +669,7 @@ namespace SharpGLTF.Schema2
         {
         {
             validate = validate.GetContext(this);
             validate = validate.GetContext(this);
 
 
-            SourceBufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
+            if (TryGetBufferView(out var bufferView)) bufferView.ValidateBufferUsageGPU(validate, BufferMode.ARRAY_BUFFER);
 
 
             validate.IsAnyOf(nameof(Format), Format, (DimensionType.VEC4, EncodingType.UNSIGNED_BYTE, true), (DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true), DimensionType.VEC4);
             validate.IsAnyOf(nameof(Format), Format, (DimensionType.VEC4, EncodingType.UNSIGNED_BYTE, true), (DimensionType.VEC4, EncodingType.UNSIGNED_SHORT, true), DimensionType.VEC4);
         }
         }
@@ -557,11 +678,14 @@ namespace SharpGLTF.Schema2
         {
         {
             validate = validate.GetContext(this);
             validate = validate.GetContext(this);
 
 
-            SourceBufferView.ValidateBufferUsagePlainData(validate);
+            if (TryGetBufferView(out var bv))
+            {
+                bv.ValidateBufferUsagePlainData(validate);
+            }            
 
 
             validate.IsAnyOf(nameof(Format), Format, (DimensionType.MAT4, EncodingType.BYTE, true), (DimensionType.MAT4, EncodingType.SHORT, true), DimensionType.MAT4);
             validate.IsAnyOf(nameof(Format), Format, (DimensionType.MAT4, EncodingType.BYTE, true), (DimensionType.MAT4, EncodingType.SHORT, true), DimensionType.MAT4);
 
 
-            var matrices = this.AsMatrix4x4Array();
+            IReadOnlyList<Matrix4x4> matrices = this.AsMatrix4x4Array();
 
 
             for (int i = 0; i < matrices.Count; ++i)
             for (int i = 0; i < matrices.Count; ++i)
             {
             {
@@ -571,14 +695,20 @@ namespace SharpGLTF.Schema2
 
 
         internal void ValidateAnimationInput(VALIDATIONCTX validate)
         internal void ValidateAnimationInput(VALIDATIONCTX validate)
         {
         {
-            SourceBufferView.ValidateBufferUsagePlainData(validate, false); // as per glTF specification, animation accessors must not have ByteStride
+            if (TryGetBufferView(out var bv))
+            {
+                bv.ValidateBufferUsagePlainData(validate, false); // as per glTF specification, animation accessors must not have ByteStride
+            }
 
 
             validate.IsAnyOf(nameof(Dimensions), Dimensions, DimensionType.SCALAR);
             validate.IsAnyOf(nameof(Dimensions), Dimensions, DimensionType.SCALAR);
         }
         }
 
 
         internal void ValidateAnimationOutput(VALIDATIONCTX validate)
         internal void ValidateAnimationOutput(VALIDATIONCTX validate)
         {
         {
-            SourceBufferView.ValidateBufferUsagePlainData(validate, false); // as per glTF specification, animation accessors must not have ByteStride
+            if (TryGetBufferView(out var bv))
+            {
+                bv.ValidateBufferUsagePlainData(validate, false); // as per glTF specification, animation accessors must not have ByteStride
+            }
 
 
             validate.IsAnyOf(nameof(Dimensions), Dimensions, DimensionType.SCALAR, DimensionType.VEC2, DimensionType.VEC3, DimensionType.VEC4);
             validate.IsAnyOf(nameof(Dimensions), Dimensions, DimensionType.SCALAR, DimensionType.VEC2, DimensionType.VEC3, DimensionType.VEC4);
         }
         }

+ 2 - 2
src/SharpGLTF.Core/Schema2/gltf.AnimationSampler.cs

@@ -119,7 +119,7 @@ namespace SharpGLTF.Schema2
         {
         {
             get
             get
             {
             {
-                var keys = Input.AsScalarArray();
+                IReadOnlyList<float> keys = Input.AsScalarArray();
                 return keys.Count == 0 ? 0 : keys[keys.Count - 1];
                 return keys.Count == 0 ? 0 : keys[keys.Count - 1];
             }
             }
         }
         }
@@ -286,7 +286,7 @@ namespace SharpGLTF.Schema2
 
 
             accessor.SetData(buffer, 0, itemCount * itemsStride, DimensionType.SCALAR, EncodingType.FLOAT, false);
             accessor.SetData(buffer, 0, itemCount * itemsStride, DimensionType.SCALAR, EncodingType.FLOAT, false);
 
 
-            var dst = accessor.AsScalarArray();
+            IList<float> dst = accessor.AsScalarArray();
 
 
             for (int y = 0; y < itemCount; ++y)
             for (int y = 0; y < itemCount; ++y)
             {
             {

+ 8 - 4
src/SharpGLTF.Core/Schema2/gltf.BufferView.cs

@@ -172,11 +172,15 @@ namespace SharpGLTF.Schema2
         /// taking into account if the source <see cref="BufferView"/> is strided.
         /// taking into account if the source <see cref="BufferView"/> is strided.
         /// </summary>
         /// </summary>
         /// <returns>The number of bytes to access.</returns>
         /// <returns>The number of bytes to access.</returns>
-        internal int GetAccessorByteLength(in Memory.AttributeFormat fmt, int count)
+        internal static int GetAccessorByteLength(in Memory.AttributeFormat fmt, int count, BufferView bv)
         {
         {
             var elementByteSize = fmt.ByteSize;
             var elementByteSize = fmt.ByteSize;
-            if (this.ByteStride == 0) return elementByteSize * count;
-            return (this.ByteStride * (count - 1)) + elementByteSize;
+
+            if (bv == null || bv.ByteStride == 0) return elementByteSize * count;
+
+            System.Diagnostics.Debug.Assert(bv.ByteStride >= elementByteSize, "unexpected byte stride size.");
+
+            return (bv.ByteStride * (count - 1)) + elementByteSize;
         }
         }
 
 
         #endregion
         #endregion
@@ -203,7 +207,7 @@ namespace SharpGLTF.Schema2
 
 
             if (bv.ByteStride > 0) validate.IsGreaterOrEqual("ElementByteSize", bv.ByteStride, format.ByteSize);
             if (bv.ByteStride > 0) validate.IsGreaterOrEqual("ElementByteSize", bv.ByteStride, format.ByteSize);
 
 
-            var accessorByteLength = bv.GetAccessorByteLength(format, count);
+            var accessorByteLength = GetAccessorByteLength(format, count, bv);
 
 
             // "Accessor(offset: {0}, length: {1}) does not fit referenced bufferView[% 3] length %4.";
             // "Accessor(offset: {0}, length: {1}) does not fit referenced bufferView[% 3] length %4.";
             validate.IsNullOrInRange(("BufferView", bv.LogicalIndex), accessorByteOffset, accessorByteLength, bv.Content);
             validate.IsNullOrInRange(("BufferView", bv.LogicalIndex), accessorByteOffset, accessorByteLength, bv.Content);

+ 10 - 4
src/SharpGLTF.Core/Schema2/gltf.MeshPrimitive.cs

@@ -153,9 +153,11 @@ namespace SharpGLTF.Schema2
             }
             }
         }
         }
 
 
-        public Memory.MemoryAccessor GetVertices(string attributeKey)
+        internal IReadOnlyList<T> GetVertices<T>(string attributeKey)
         {
         {
-            return GetVertexAccessor(attributeKey)._GetMemoryAccessor(attributeKey);
+            var accessor = GetVertexAccessor(attributeKey);
+
+            return accessor.AsVertexArray<T>();
         }
         }
 
 
         #endregion
         #endregion
@@ -403,7 +405,10 @@ namespace SharpGLTF.Schema2
                 }
                 }
                 else
                 else
                 {
                 {
-                    var memories = group.Select(item => item._GetMemoryAccessor());
+                    var memories = group
+                        .Select(item => item._TryGetMemoryAccessor(out var mem) ? mem : null)
+                        .Where(item => item != null);
+
                     var overlap = Memory.MemoryAccessor.HaveOverlappingBuffers(memories);
                     var overlap = Memory.MemoryAccessor.HaveOverlappingBuffers(memories);
                     if (overlap)
                     if (overlap)
                     {
                     {
@@ -417,7 +422,8 @@ namespace SharpGLTF.Schema2
             if (validate.TryFix)
             if (validate.TryFix)
             {
             {
                 var vattributes = this.VertexAccessors
                 var vattributes = this.VertexAccessors
-                    .Select(item => item.Value._GetMemoryAccessor(item.Key))
+                    .Select(item => item.Value._TryGetMemoryAccessor(item.Key, out var mem) ? mem : null)
+                    .Where(item => item != null)
                     .ToArray();
                     .ToArray();
 
 
                 Memory.MemoryAccessor.SanitizeVertexAttributes(vattributes);
                 Memory.MemoryAccessor.SanitizeVertexAttributes(vattributes);

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

@@ -62,7 +62,7 @@ namespace SharpGLTF.Schema2
 
 
                 System.Diagnostics.Debug.Assert(matrices.Count == _joints.Count, "IBM and Joints count mismatch");
                 System.Diagnostics.Debug.Assert(matrices.Count == _joints.Count, "IBM and Joints count mismatch");
 
 
-                return matrices.AsMatrix4x4ReadOnlyList();
+                return matrices.AsMatrix4x4Array();
             }
             }
         }
         }
 
 
@@ -125,11 +125,11 @@ namespace SharpGLTF.Schema2
 
 
             var node = this.LogicalParent.LogicalNodes[nodeIdx];
             var node = this.LogicalParent.LogicalNodes[nodeIdx];
 
 
-            var matrices = GetInverseBindMatricesAccessor();
+            IReadOnlyList<Matrix4x4> matrices = GetInverseBindMatricesAccessor()?.AsMatrix4x4Array();
 
 
             var matrix = matrices == null
             var matrix = matrices == null
                 ? Matrix4x4.Identity
                 ? Matrix4x4.Identity
-                : matrices.AsMatrix4x4Array()[idx];
+                : matrices[idx];
 
 
             return (node, matrix);
             return (node, matrix);
         }
         }

+ 5 - 5
src/SharpGLTF.Core/Validation/ValidationContext.Guards.cs

@@ -293,7 +293,7 @@ namespace SharpGLTF.Validation
             return this;
             return this;
         }
         }
 
 
-        public OUTTYPE ArePositions(PARAMNAME pname, IList<System.Numerics.Vector3> positions)
+        public OUTTYPE ArePositions(PARAMNAME pname, IReadOnlyList<System.Numerics.Vector3> positions)
         {
         {
             Guard.NotNull(positions, nameof(positions));
             Guard.NotNull(positions, nameof(positions));
 
 
@@ -305,7 +305,7 @@ namespace SharpGLTF.Validation
             return this;
             return this;
         }
         }
 
 
-        public OUTTYPE AreNormals(PARAMNAME pname, IList<System.Numerics.Vector3> normals)
+        public OUTTYPE AreNormals(PARAMNAME pname, IReadOnlyList<System.Numerics.Vector3> normals)
         {
         {
             Guard.NotNull(normals, nameof(normals));
             Guard.NotNull(normals, nameof(normals));
 
 
@@ -317,7 +317,7 @@ namespace SharpGLTF.Validation
             return this;
             return this;
         }
         }
 
 
-        public OUTTYPE AreTangents(PARAMNAME pname, IList<System.Numerics.Vector4> tangents)
+        public OUTTYPE AreTangents(PARAMNAME pname, IReadOnlyList<System.Numerics.Vector4> tangents)
         {
         {
             Guard.NotNull(tangents, nameof(tangents));
             Guard.NotNull(tangents, nameof(tangents));
 
 
@@ -329,7 +329,7 @@ namespace SharpGLTF.Validation
             return this;
             return this;
         }
         }
 
 
-        public OUTTYPE AreRotations(PARAMNAME pname, IList<System.Numerics.Quaternion> rotations)
+        public OUTTYPE AreRotations(PARAMNAME pname, IReadOnlyList<System.Numerics.Quaternion> rotations)
         {
         {
             Guard.NotNull(rotations, nameof(rotations));
             Guard.NotNull(rotations, nameof(rotations));
 
 
@@ -341,7 +341,7 @@ namespace SharpGLTF.Validation
             return this;
             return this;
         }
         }
 
 
-        public OUTTYPE AreJoints(PARAMNAME pname, IList<System.Numerics.Vector4> joints, int skinsMaxJointCount)
+        public OUTTYPE AreJoints(PARAMNAME pname, IReadOnlyList<System.Numerics.Vector4> joints, int skinsMaxJointCount)
         {
         {
             Guard.NotNull(joints, nameof(joints));
             Guard.NotNull(joints, nameof(joints));
 
 

+ 3 - 1
tests/SharpGLTF.Core.Tests/Schema2/LoadAndSave/LoadSampleTests.cs

@@ -251,7 +251,9 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
 
             var accessor = primitive.GetVertexAccessor("POSITION");
             var accessor = primitive.GetVertexAccessor("POSITION");
 
 
-            var basePositions = accessor._GetMemoryAccessor().AsVector3Array();
+            if (!accessor._TryGetMemoryAccessor(out var baseMem)) Assert.Fail("can't get underlaying data");
+
+            var basePositions = baseMem.AsVector3Array();
 
 
             var positions = accessor.AsVector3Array();
             var positions = accessor.AsVector3Array();
         }
         }

+ 2 - 2
tests/SharpGLTF.Core.Tests/Schema2/LoadAndSave/LoadSpecialModelsTest.cs

@@ -66,8 +66,8 @@ namespace SharpGLTF.Schema2.LoadAndSave
             var pollyPrimitive = pollyNode.Mesh.Primitives[0];
             var pollyPrimitive = pollyNode.Mesh.Primitives[0];
 
 
             var pollyIndices = pollyPrimitive.GetIndices();
             var pollyIndices = pollyPrimitive.GetIndices();
-            var pollyPositions = pollyPrimitive.GetVertices("POSITION").AsVector3Array();
-            var pollyNormals = pollyPrimitive.GetVertices("NORMAL").AsVector3Array();
+            var pollyPositions = pollyPrimitive.GetVertices<Vector3>("POSITION");
+            var pollyNormals = pollyPrimitive.GetVertices<Vector3>("NORMAL");
 
 
             for (int i = 0; i < pollyIndices.Count; i += 3)
             for (int i = 0; i < pollyIndices.Count; i += 3)
             {
             {