Bläddra i källkod

attempting to fix accessors with missing BufferView [WIP]

vpenades 6 månader sedan
förälder
incheckning
1279d5b993

+ 1 - 1
src/Directory.Build.props

@@ -12,7 +12,7 @@
   <!-- Configuration =================================================================================== -->
 
   <PropertyGroup>
-    <LangVersion>8.0</LangVersion>
+    <LangVersion>10.0</LangVersion>
     <IsPackable>true</IsPackable>
   </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 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);
 
@@ -62,14 +62,15 @@ namespace SharpGLTF.Diagnostics
             get
             {
                 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();
                 }
 
-                if (Source.IsVertexBuffer)
+                if (bv.IsVertexBuffer)
                 {
                     if (_Value.Dimensions == Schema2.DimensionType.SCALAR) return _Value.AsScalarArray().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();
 
-                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();

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

@@ -72,14 +72,15 @@ namespace SharpGLTF.Diagnostics
 
         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}ᴮʸᵗᵉˢ ⇨";
 
@@ -140,12 +141,16 @@ namespace SharpGLTF.Diagnostics
                 case PrimitiveType.LINES:
                 case PrimitiveType.LINE_LOOP:
                 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;
                 case PrimitiveType.TRIANGLES:
                 case PrimitiveType.TRIANGLE_FAN:
                 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;
             }
 

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

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

@@ -225,10 +225,10 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Float[{Count}]")]
-    public readonly struct ScalarArray : IList<Single>, IReadOnlyList<Single>
+    public readonly struct ScalarArray : IAccessorArray<Single>
     {
         #region constructors
 
@@ -321,10 +321,10 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Vector2[{Count}]")]
-    public readonly struct Vector2Array : IList<Vector2>, IReadOnlyList<Vector2>
+    public readonly struct Vector2Array : IAccessorArray<Vector2>
     {
         #region constructors
 
@@ -425,10 +425,10 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Vector3[{Count}]")]
-    public readonly struct Vector3Array : IList<Vector3>, IReadOnlyList<Vector3>
+    public readonly struct Vector3Array : IAccessorArray<Vector3>
     {
         #region constructors
 
@@ -530,10 +530,10 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Vector4[{Count}]")]
-    public readonly struct Vector4Array : IList<Vector4>, IReadOnlyList<Vector4>
+    public readonly struct Vector4Array : IAccessorArray<Vector4>
     {
         #region constructors
 
@@ -636,10 +636,10 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Quaternion[{Count}]")]
-    public readonly struct QuaternionArray : IList<Quaternion>, IReadOnlyList<Quaternion>
+    public readonly struct QuaternionArray : IAccessorArray<Quaternion>
     {
         #region constructors
 
@@ -720,14 +720,14 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     /// <remarks>
     /// <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"/>.
     /// </remarks>
     [System.Diagnostics.DebuggerDisplay("Matrix2x2[{Count}]")]
-    public readonly struct Matrix2x2Array : IList<Matrix3x2>, IReadOnlyList<Matrix3x2>
+    public readonly struct Matrix2x2Array : IAccessorArray<Matrix3x2>
     {
         #region constructors
 
@@ -813,10 +813,10 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Matrix3x2[{Count}]")]
-    public readonly struct Matrix3x2Array : IList<Matrix3x2>, IReadOnlyList<Matrix3x2>
+    public readonly struct Matrix3x2Array : IAccessorArray<Matrix3x2>
     {
         #region constructors
 
@@ -904,14 +904,14 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     /// <remarks>
     /// <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"/>.
     /// </remarks>
     [System.Diagnostics.DebuggerDisplay("Matrix3x3[{Count}]")]
-    public readonly struct Matrix3x3Array : IList<Matrix4x4>, IReadOnlyList<Matrix4x4>
+    public readonly struct Matrix3x3Array : IAccessorArray<Matrix4x4>
     {
         #region constructors
 
@@ -1003,14 +1003,14 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     /// <remarks>
     /// <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"/>.
     /// </remarks>
     [System.Diagnostics.DebuggerDisplay("Matrix4x3[{Count}]")]
-    public readonly struct Matrix4x3Array : IList<Matrix4x4>, IReadOnlyList<Matrix4x4>
+    public readonly struct Matrix4x3Array : IAccessorArray<Matrix4x4>
     {
         #region constructors
 
@@ -1105,10 +1105,10 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Matrix4x4[{Count}]")]
-    public readonly struct Matrix4x4Array : IList<Matrix4x4>, IReadOnlyList<Matrix4x4>
+    public readonly struct Matrix4x4Array : IAccessorArray<Matrix4x4>
     {
         #region constructors
 
@@ -1207,10 +1207,10 @@ namespace SharpGLTF.Memory
     }
 
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Float[][{Count}]")]
-    public readonly struct MultiArray : IList<Single[]>, IReadOnlyList<Single[]>
+    public readonly struct MultiArray : IAccessorArray<Single[]>
     {
         #region constructors
         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
 {
     /// <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>
     [System.Diagnostics.DebuggerDisplay("Integer[{Count}]")]
-    public readonly struct IntegerArray : IList<UInt32>, IReadOnlyList<UInt32>
+    public readonly struct IntegerArray : IAccessorArray<UInt32>
     {
         #region constructors
 

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

@@ -337,7 +337,7 @@ namespace SharpGLTF.Memory
             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(topValues, nameof(topValues));
@@ -349,7 +349,28 @@ namespace SharpGLTF.Memory
             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(topValues, nameof(topValues));
@@ -361,7 +382,18 @@ namespace SharpGLTF.Memory
             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(topValues, nameof(topValues));
@@ -373,7 +405,17 @@ namespace SharpGLTF.Memory
             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(topValues, nameof(topValues));
@@ -385,7 +427,17 @@ namespace SharpGLTF.Memory
             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(topValues, nameof(topValues));
@@ -488,6 +540,14 @@ namespace SharpGLTF.Memory
             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()
         {
             Guard.IsTrue(_Slicer.IsValidVertexAttribute, nameof(_Slicer));

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

@@ -10,17 +10,18 @@ namespace SharpGLTF.Memory
     /// </summary>
     /// <typeparam name="T">An unmanage structure type.</typeparam>
     [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
     {
         #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;
             _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)
             {
                 var key = (int)topMapping[val];
@@ -33,10 +34,10 @@ namespace SharpGLTF.Memory
         #region data
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly IList<T> _BottomItems;
+        private readonly IReadOnlyList<T> _BottomItems;
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly IList<T> _TopItems;
+        private readonly IReadOnlyList<T> _TopItems;
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         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);
 
+        /// <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>
         /// Gets the <see cref="BufferView"/> buffer that contains the items as an encoded byte array.
         /// </summary>
@@ -58,14 +63,9 @@ namespace SharpGLTF.Schema2
         public int Count => this._count;
 
         /// <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>
-        public int ByteLength => SourceBufferView.GetAccessorByteLength(Format, Count);
+        public int ByteOffset => this._byteOffset.AsValue(0);        
 
         /// <summary>
         /// 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;
 
             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);
-            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()
@@ -164,20 +178,28 @@ namespace SharpGLTF.Schema2
                 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
@@ -213,18 +235,23 @@ namespace SharpGLTF.Schema2
             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;
 
             var view = SourceBufferView;
@@ -232,16 +259,59 @@ namespace SharpGLTF.Schema2
             var content = view.Content.Slice(this.ByteOffset, Count * stride);
 
             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()
         {
-            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
@@ -272,12 +342,14 @@ namespace SharpGLTF.Schema2
             SetData(buffer, bufferByteOffset, itemCount, DimensionType.SCALAR, encoding.ToComponent(), false);
         }
 
-        public IntegerArray AsIndicesArray()
+        public IAccessorArray<UInt32> AsIndicesArray()
         {
             Guard.IsFalse(this.IsSparse, nameof(IsSparse));
             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
@@ -312,83 +384,119 @@ namespace SharpGLTF.Schema2
             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)
         {
             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 byteStride = Math.Max(itemByteSz, SourceBufferView.ByteStride);
+            var byteStride = Math.Max(itemByteSz, bufferView.ByteStride);
             var byteOffset = vertexIdx * byteStride;
 
-            return SourceBufferView.Content.Slice(this.ByteOffset + (vertexIdx * byteStride), itemByteSz);
+            return bufferView.Content.Slice(this.ByteOffset + (vertexIdx * byteStride), itemByteSz);
         }
 
         #endregion
@@ -400,7 +508,7 @@ namespace SharpGLTF.Schema2
             base.OnValidateReferences(validate);
 
             validate
-                .IsDefined(nameof(_bufferView), _bufferView)
+                // .IsDefined(nameof(_bufferView), _bufferView) as per specification, this is not required to be defined.
                 .NonNegative(nameof(_byteOffset), _byteOffset)
                 .IsGreaterOrEqual(nameof(_count), _count, _countMinimum)
                 .IsNullOrIndex(nameof(_bufferView), _bufferView, this.LogicalParent.LogicalBufferViews);
@@ -414,9 +522,15 @@ namespace SharpGLTF.Schema2
             // we cannot check the rest of the accessor.
             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
             // using this accessor to validate the data.
@@ -426,12 +540,19 @@ namespace SharpGLTF.Schema2
         {
             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.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)
@@ -463,7 +584,7 @@ namespace SharpGLTF.Schema2
         {
             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)
             {
@@ -481,7 +602,7 @@ namespace SharpGLTF.Schema2
         {
             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)
             {
@@ -501,7 +622,7 @@ namespace SharpGLTF.Schema2
         {
             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)
             {
@@ -526,7 +647,7 @@ namespace SharpGLTF.Schema2
         {
             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), (DimensionType.VEC4, EncodingType.UNSIGNED_SHORT), DimensionType.VEC4)
@@ -538,8 +659,8 @@ namespace SharpGLTF.Schema2
             weights0?._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));
         }
@@ -548,7 +669,7 @@ namespace SharpGLTF.Schema2
         {
             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);
         }
@@ -557,11 +678,14 @@ namespace SharpGLTF.Schema2
         {
             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);
 
-            var matrices = this.AsMatrix4x4Array();
+            IReadOnlyList<Matrix4x4> matrices = this.AsMatrix4x4Array();
 
             for (int i = 0; i < matrices.Count; ++i)
             {
@@ -571,14 +695,20 @@ namespace SharpGLTF.Schema2
 
         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);
         }
 
         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);
         }

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

@@ -119,7 +119,7 @@ namespace SharpGLTF.Schema2
         {
             get
             {
-                var keys = Input.AsScalarArray();
+                IReadOnlyList<float> keys = Input.AsScalarArray();
                 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);
 
-            var dst = accessor.AsScalarArray();
+            IList<float> dst = accessor.AsScalarArray();
 
             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.
         /// </summary>
         /// <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;
-            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
@@ -203,7 +207,7 @@ namespace SharpGLTF.Schema2
 
             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.";
             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
@@ -403,7 +405,10 @@ namespace SharpGLTF.Schema2
                 }
                 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);
                     if (overlap)
                     {
@@ -417,7 +422,8 @@ namespace SharpGLTF.Schema2
             if (validate.TryFix)
             {
                 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();
 
                 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");
 
-                return matrices.AsMatrix4x4ReadOnlyList();
+                return matrices.AsMatrix4x4Array();
             }
         }
 
@@ -125,11 +125,11 @@ namespace SharpGLTF.Schema2
 
             var node = this.LogicalParent.LogicalNodes[nodeIdx];
 
-            var matrices = GetInverseBindMatricesAccessor();
+            IReadOnlyList<Matrix4x4> matrices = GetInverseBindMatricesAccessor()?.AsMatrix4x4Array();
 
             var matrix = matrices == null
                 ? Matrix4x4.Identity
-                : matrices.AsMatrix4x4Array()[idx];
+                : matrices[idx];
 
             return (node, matrix);
         }

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

@@ -293,7 +293,7 @@ namespace SharpGLTF.Validation
             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));
 
@@ -305,7 +305,7 @@ namespace SharpGLTF.Validation
             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));
 
@@ -317,7 +317,7 @@ namespace SharpGLTF.Validation
             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));
 
@@ -329,7 +329,7 @@ namespace SharpGLTF.Validation
             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));
 
@@ -341,7 +341,7 @@ namespace SharpGLTF.Validation
             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));
 

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

@@ -251,7 +251,9 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
             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();
         }

+ 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 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)
             {