Vicente Penades 6 лет назад
Родитель
Сommit
8292e04e98

+ 156 - 0
src/glTF2Sharp.DOM/Memory/AttributeInfo.cs

@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+
+namespace glTF2Sharp.Memory
+{
+    /// <summary>
+    /// Represents the vertex attribute access info
+    /// </summary>
+    public struct AttributeInfo
+    {
+        public AttributeInfo(string name, int byteOffset, int itemsCount, int byteStride, Schema2.ElementType dimensions, Schema2.ComponentType encoding, Boolean normalized)
+        {
+            this.Name = name;
+            this.ByteOffset = byteOffset;
+            this.ItemsCount = itemsCount;
+            this.ByteStride = byteStride;
+            this.Dimensions = dimensions;
+            this.Encoding = encoding;
+            this.Normalized = normalized;
+        }
+
+        public String Name;
+        public int ByteOffset;
+        public int ItemsCount;
+        public int ByteStride;
+        public Schema2.ElementType Dimensions;
+        public Schema2.ComponentType Encoding;
+        public Boolean Normalized;
+
+        public int ByteLength => this.Dimensions.DimCount() * this.Encoding.ByteLength();
+
+        public Boolean IsValidVertexAttribute
+        {
+            get
+            {
+                if (this.ByteOffset < 0) return false;
+                if (this.ItemsCount < 0) return false;
+                if (this.ByteStride < 0) return false;
+                var len = this.Dimensions.DimCount() * this.Encoding.ByteLength();
+                if (len == 0 || (len & 3) != 0) return false;
+
+                if (this.ByteStride > 0 && this.ByteStride < len) return false;
+                if ((this.ByteStride & 3) != 0) return false;
+
+                return true;
+            }
+        }
+
+        public Boolean IsValidIndexer
+        {
+            get
+            {
+                if (this.ByteOffset < 0) return false;
+                if (this.ItemsCount < 0) return false;
+                if (this.ByteStride < 0) return false;
+                if (this.Dimensions != Schema2.ElementType.SCALAR) return false;
+                if (this.Normalized) return false;
+                if (this.ByteStride == 0) return true;
+                if (this.ByteStride == 1) return true;
+                if (this.ByteStride == 2) return true;
+                if (this.ByteStride == 4) return true;
+                return false;
+            }
+        }
+    }
+
+    public struct AttributeAccessor
+    {
+        public AttributeAccessor(AttributeInfo info, ArraySegment<Byte> data)
+        {
+            this.Attribute = info;
+            this.Data = data;
+        }
+
+        public AttributeInfo Attribute;
+        public ArraySegment<Byte> Data;
+
+        public ScalarArray AsScalarArray()
+        {
+            Guard.IsTrue(Attribute.IsValidVertexAttribute, nameof(Attribute));
+            Guard.IsTrue(Attribute.Dimensions == Schema2.ElementType.SCALAR, nameof(Attribute));
+            return new ScalarArray(Data, Attribute.ByteOffset, Attribute.ItemsCount, Attribute.ByteStride, Attribute.Encoding, Attribute.Normalized);
+        }
+
+        public Vector2Array AsVector2Array()
+        {
+            Guard.IsTrue(Attribute.IsValidVertexAttribute, nameof(Attribute));
+            Guard.IsTrue(Attribute.Dimensions == Schema2.ElementType.VEC2, nameof(Attribute));
+            return new Vector2Array(Data, Attribute.ByteOffset, Attribute.ItemsCount, Attribute.ByteStride, Attribute.Encoding, Attribute.Normalized);
+        }
+
+        public Vector3Array AsVector3Array()
+        {
+            Guard.IsTrue(Attribute.IsValidVertexAttribute, nameof(Attribute));
+            Guard.IsTrue(Attribute.Dimensions == Schema2.ElementType.VEC3, nameof(Attribute));
+            return new Vector3Array(Data, Attribute.ByteOffset, Attribute.ItemsCount, Attribute.ByteStride, Attribute.Encoding, Attribute.Normalized);
+        }
+
+        public Vector4Array AsVector4Array()
+        {
+            Guard.IsTrue(Attribute.IsValidVertexAttribute, nameof(Attribute));
+            Guard.IsTrue(Attribute.Dimensions == Schema2.ElementType.VEC4, nameof(Attribute));
+            return new Vector4Array(Data, Attribute.ByteOffset, Attribute.ItemsCount, Attribute.ByteStride, Attribute.Encoding, Attribute.Normalized);
+        }
+
+        public Matrix4x4Array AsMatrix4x4Array()
+        {
+            Guard.IsTrue(Attribute.IsValidVertexAttribute, nameof(Attribute));
+            Guard.IsTrue(Attribute.Dimensions == Schema2.ElementType.MAT4, nameof(Attribute));
+            return new Matrix4x4Array(Data, Attribute.ByteOffset, Attribute.ItemsCount, Attribute.ByteStride, Attribute.Encoding, Attribute.Normalized);
+        }
+
+        public static IEncodedArray<Single> CreateScalarSparseArray(AttributeAccessor bottom, IntegerArray topKeys, AttributeAccessor topValues)
+        {
+            Guard.IsTrue(bottom.Attribute.Dimensions == topValues.Attribute.Dimensions, nameof(topValues));
+            Guard.IsTrue(topKeys.Count <= bottom.Attribute.ItemsCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues.Attribute.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottom.Attribute.ItemsCount), nameof(topKeys));
+
+            return new SparseArray<Single>(bottom.AsScalarArray(), topValues.AsScalarArray(), topKeys);
+        }
+
+        public static IEncodedArray<Vector2> CreateVector2SparseArray(AttributeAccessor bottom, IntegerArray topKeys, AttributeAccessor topValues)
+        {
+            Guard.IsTrue(bottom.Attribute.Dimensions == topValues.Attribute.Dimensions, nameof(topValues));
+            Guard.IsTrue(topKeys.Count <= bottom.Attribute.ItemsCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues.Attribute.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottom.Attribute.ItemsCount), nameof(topKeys));
+
+            return new SparseArray<Vector2>(bottom.AsVector2Array(), topValues.AsVector2Array(), topKeys);
+        }
+
+        public static IEncodedArray<Vector3> CreateVector3SparseArray(AttributeAccessor bottom, IntegerArray topKeys, AttributeAccessor topValues)
+        {
+            Guard.IsTrue(bottom.Attribute.Dimensions == topValues.Attribute.Dimensions, nameof(topValues));
+            Guard.IsTrue(topKeys.Count <= bottom.Attribute.ItemsCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues.Attribute.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottom.Attribute.ItemsCount), nameof(topKeys));
+
+            return new SparseArray<Vector3>(bottom.AsVector3Array(), topValues.AsVector3Array(), topKeys);
+        }
+
+        public static IEncodedArray<Vector4> CreateVector4SparseArray(AttributeAccessor bottom, IntegerArray topKeys, AttributeAccessor topValues)
+        {
+            Guard.IsTrue(bottom.Attribute.Dimensions == topValues.Attribute.Dimensions, nameof(topValues));
+            Guard.IsTrue(topKeys.Count <= bottom.Attribute.ItemsCount, nameof(topKeys));
+            Guard.IsTrue(topKeys.Count == topValues.Attribute.ItemsCount, nameof(topValues));
+            Guard.IsTrue(topKeys.All(item => item < (uint)bottom.Attribute.ItemsCount), nameof(topKeys));
+
+            return new SparseArray<Vector4>(bottom.AsVector4Array(), topValues.AsVector4Array(), topKeys);
+        }
+    }
+}

+ 65 - 160
src/glTF2Sharp.DOM/Memory/FloatingArrays.cs

@@ -12,21 +12,29 @@ namespace glTF2Sharp.Memory
     using ENCODING = Schema2.ComponentType;
 
     /// <summary>
-    /// Helper structure to access any Byte array as an array of floating Singles/>
+    /// Wraps a <see cref="ArraySegment{Byte}"/> containing encoded floating point values
     /// </summary>
     struct FloatingAccessor
     {
         #region constructors
 
-        public FloatingAccessor(Byte[] data, ENCODING encoding, Boolean normalized)
-            : this(new BYTES(data), encoding, normalized) { }
-
-        public FloatingAccessor(BYTES data, ENCODING encoding, Boolean normalized)
+        public FloatingAccessor(BYTES data, int byteOffset, int itemsCount, int byteStride, int dimensions, ENCODING encoding, Boolean normalized)
         {
-            this._Data = data;
+            this._Data = data.Slice(byteOffset);
+            this._ItemCount = 0;
             this._Getter = null;
             this._Setter = null;
 
+            var len = encoding.ByteLength();
+
+            this._ByteStride = Math.Max(byteStride, len * dimensions);
+            this._ValueStride = len;
+
+            _ItemCount = _Data.Count / _ByteStride;
+            if ((_Data.Count % _ByteStride) >= len * dimensions) ++_ItemCount;
+
+            _ItemCount = Math.Min(itemsCount, _ItemCount);
+
             if (encoding == ENCODING.FLOAT)
             {
                 this._Setter = this._SetValue<Single>;
@@ -121,54 +129,60 @@ namespace glTF2Sharp.Memory
 
         #region encoding / decoding
 
-        private Single _GetValueU8(int byteOffset, int index) { return _GetValue<Byte>(byteOffset, index); }
-        private void _SetValueU8(int byteOffset, int index, Single value) { _SetValue<Byte>(byteOffset, index, (Byte)value); }
+        private Single _GetValueU8(int byteOffset) { return _GetValue<Byte>(byteOffset); }
+        private void _SetValueU8(int byteOffset, Single value) { _SetValue<Byte>(byteOffset, (Byte)value); }
 
-        private Single _GetValueS8(int byteOffset, int index) { return _GetValue<SByte>(byteOffset, index); }
-        private void _SetValueS8(int byteOffset, int index, Single value) { _SetValue<SByte>(byteOffset, index, (SByte)value); }
+        private Single _GetValueS8(int byteOffset) { return _GetValue<SByte>(byteOffset); }
+        private void _SetValueS8(int byteOffset, Single value) { _SetValue<SByte>(byteOffset, (SByte)value); }
 
-        private Single _GetValueU16(int byteOffset, int index) { return _GetValue<UInt16>(byteOffset, index); }
-        private void _SetValueU16(int byteOffset, int index, Single value) { _SetValue<UInt16>(byteOffset, index, (UInt16)value); }
+        private Single _GetValueU16(int byteOffset) { return _GetValue<UInt16>(byteOffset); }
+        private void _SetValueU16(int byteOffset, Single value) { _SetValue<UInt16>(byteOffset, (UInt16)value); }
 
-        private Single _GetValueS16(int byteOffset, int index) { return _GetValue<Int16>(byteOffset, index); }
-        private void _SetValueS16(int byteOffset, int index, Single value) { _SetValue<Int16>(byteOffset, index, (Int16)value); }
+        private Single _GetValueS16(int byteOffset) { return _GetValue<Int16>(byteOffset); }
+        private void _SetValueS16(int byteOffset, Single value) { _SetValue<Int16>(byteOffset, (Int16)value); }
 
-        private Single _GetValueU32(int byteOffset, int index) { return _GetValue<UInt32>(byteOffset, index); }
-        private void _SetValueU32(int byteOffset, int index, Single value) { _SetValue<UInt32>(byteOffset, index, (UInt32)value); }
+        private Single _GetValueU32(int byteOffset) { return _GetValue<UInt32>(byteOffset); }
+        private void _SetValueU32(int byteOffset, Single value) { _SetValue<UInt32>(byteOffset, (UInt32)value); }
 
-        private Single _GetNormalizedU8(int byteOffset, int index) { return _GetValueU8(byteOffset, index) / 255.0f; }
-        private void _SetNormalizedU8(int byteOffset, int index, Single value) { _SetValueU8(byteOffset, index, value * 255.0f); }
+        private Single _GetNormalizedU8(int byteOffset) { return _GetValueU8(byteOffset) / 255.0f; }
+        private void _SetNormalizedU8(int byteOffset, Single value) { _SetValueU8(byteOffset, value * 255.0f); }
 
-        private Single _GetNormalizedS8(int byteOffset, int index) { return Math.Max(_GetValueS8(byteOffset, index) / 127.0f, -1); }
-        private void _SetNormalizedS8(int byteOffset, int index, Single value) { _SetValueS8(byteOffset, index, (Single)Math.Round(value * 127.0f)); }
+        private Single _GetNormalizedS8(int byteOffset) { return Math.Max(_GetValueS8(byteOffset) / 127.0f, -1); }
+        private void _SetNormalizedS8(int byteOffset, Single value) { _SetValueS8(byteOffset, (Single)Math.Round(value * 127.0f)); }
 
-        private Single _GetNormalizedU16(int byteOffset, int index) { return _GetValueU16(byteOffset, index) / 65535.0f; }
-        private void _SetNormalizedU16(int byteOffset, int index, Single value) { _SetValueU16(byteOffset, index, value * 65535.0f); }
+        private Single _GetNormalizedU16(int byteOffset) { return _GetValueU16(byteOffset) / 65535.0f; }
+        private void _SetNormalizedU16(int byteOffset, Single value) { _SetValueU16(byteOffset, value * 65535.0f); }
 
-        private Single _GetNormalizedS16(int byteOffset, int index) { return Math.Max(_GetValueS16(byteOffset, index) / 32767.0f, -1); }
-        private void _SetNormalizedS16(int byteOffset, int index, Single value) { _SetValueS16(byteOffset, index, (Single)Math.Round(value * 32767.0f)); }
+        private Single _GetNormalizedS16(int byteOffset) { return Math.Max(_GetValueS16(byteOffset) / 32767.0f, -1); }
+        private void _SetNormalizedS16(int byteOffset, Single value) { _SetValueS16(byteOffset, (Single)Math.Round(value * 32767.0f)); }
 
-        private T _GetValue<T>(int byteOffset, int index)
+        private T _GetValue<T>(int byteOffset)
             where T : unmanaged
         {
-            return System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, T>(_Data.AsSpan(byteOffset))[index];
+            return System.Runtime.InteropServices.MemoryMarshal.Read<T>(_Data.AsSpan(byteOffset));
         }
 
-        private void _SetValue<T>(int byteOffset, int index, T value)
+        private void _SetValue<T>(int byteOffset, T value)
             where T : unmanaged
         {
-            System.Runtime.InteropServices.MemoryMarshal.Cast<Byte, T>(_Data.AsSpan(byteOffset))[index] = value;
+            System.Runtime.InteropServices.MemoryMarshal.Write<T>(_Data.AsSpan(byteOffset), ref value);
         }
 
         #endregion
 
         #region data
 
-        delegate Single _GetterCallback(int byteOffset, int index);
+        delegate Single _GetterCallback(int byteOffset);
 
-        delegate void _SetterCallback(int byteOffset, int index, Single value);
+        delegate void _SetterCallback(int byteOffset, Single value);
 
         private readonly BYTES _Data;
+
+        private readonly int _ByteStride;
+        private readonly int _ValueStride;
+
+        private readonly int _ItemCount;
+
         private readonly _GetterCallback _Getter;
         private readonly _SetterCallback _Setter;
 
@@ -178,16 +192,18 @@ namespace glTF2Sharp.Memory
 
         public int ByteLength => _Data.Count;
 
+        public int Count => _ItemCount;
+
         public Single this[int index]
         {
-            get => _Getter(0, index);
-            set => _Setter(0, index, value);
+            get => _Getter(index * _ByteStride);
+            set => _Setter(index * _ByteStride, value);
         }
 
-        public Single this[int byteOffset, int index]
+        public Single this[int rowIndex, int subIndex]
         {
-            get => _Getter(byteOffset, index);
-            set => _Setter(byteOffset, index, value);
+            get => _Getter((rowIndex * _ByteStride) + (subIndex * _ValueStride));
+            set => _Setter((rowIndex * _ByteStride) + (subIndex * _ValueStride), value);
         }
 
         #endregion
@@ -209,18 +225,7 @@ namespace glTF2Sharp.Memory
 
         public ScalarArray(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            data = data.Slice(byteOffset);
-
-            var len = encoding.ByteLength() * 1;
-
-            _ByteStride = Math.Max(len, byteStride);
-
-            _Accesor = new FloatingAccessor(data, encoding, normalized);
-
-            _ItemCount = _Accesor.ByteLength / _ByteStride;
-            if ((_Accesor.ByteLength % _ByteStride) >= len) ++_ItemCount;
-
-            _ItemCount = Math.Min(_ItemCount, count);
+            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 1, encoding, normalized);
         }
 
         #endregion
@@ -230,12 +235,6 @@ namespace glTF2Sharp.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private FloatingAccessor _Accesor;
 
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ByteStride;
-
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ItemCount;
-
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private Single[] _DebugItems => this.ToArray();
 
@@ -244,12 +243,12 @@ namespace glTF2Sharp.Memory
         #region API
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        public int Count => _ItemCount;
+        public int Count => _Accesor.Count;
 
         public Single this[int index]
         {
-            get => _Accesor[index * _ByteStride, 0];
-            set => _Accesor[index * _ByteStride, 0] = value;
+            get => _Accesor[index, 0];
+            set => _Accesor[index, 0] = value;
         }
 
         public void CopyTo(ArraySegment<Single> dst) { EncodedArrayUtils.Copy<Single>(this, dst); }
@@ -281,18 +280,7 @@ namespace glTF2Sharp.Memory
 
         public Vector2Array(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            data = data.Slice(byteOffset);
-
-            var len = encoding.ByteLength() * 2;
-
-            _ByteStride = Math.Max(len, byteStride);
-
-            _Accesor = new FloatingAccessor(data, encoding, normalized);
-
-            _ItemCount = _Accesor.ByteLength / _ByteStride;
-            if ((_Accesor.ByteLength % _ByteStride) >= len) ++_ItemCount;
-
-            _ItemCount = Math.Min(_ItemCount, count);
+            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 2, encoding, normalized);
         }
 
         #endregion
@@ -302,12 +290,6 @@ namespace glTF2Sharp.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private FloatingAccessor _Accesor;
 
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ByteStride;
-
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ItemCount;
-
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private Vector2[] _DebugItems => this.ToArray();
 
@@ -316,19 +298,17 @@ namespace glTF2Sharp.Memory
         #region API
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        public int Count => _ItemCount;
+        public int Count => _Accesor.Count;
 
         public Vector2 this[int index]
         {
             get
             {
-                index *= _ByteStride;
                 return new Vector2(_Accesor[index, 0], _Accesor[index, 1]);
             }
 
             set
             {
-                index *= _ByteStride;
                 _Accesor[index, 0] = value.X;
                 _Accesor[index, 1] = value.Y;
             }
@@ -363,18 +343,7 @@ namespace glTF2Sharp.Memory
 
         public Vector3Array(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            data = data.Slice(byteOffset);
-
-            var len = encoding.ByteLength() * 3;
-
-            _ByteStride = Math.Max(len, byteStride);
-
-            _Accesor = new FloatingAccessor(data, encoding, normalized);
-
-            _ItemCount = _Accesor.ByteLength / _ByteStride;
-            if ((_Accesor.ByteLength % _ByteStride) >= len) ++_ItemCount;
-
-            _ItemCount = Math.Min(_ItemCount, count);
+            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 3, encoding, normalized);
         }
 
         #endregion
@@ -384,12 +353,6 @@ namespace glTF2Sharp.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private FloatingAccessor _Accesor;
 
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ByteStride;
-
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ItemCount;
-
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private Vector3[] _DebugItems => this.ToArray();
 
@@ -398,19 +361,17 @@ namespace glTF2Sharp.Memory
         #region API
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        public int Count => _ItemCount;
+        public int Count => _Accesor.Count;
 
         public Vector3 this[int index]
         {
             get
             {
-                index *= _ByteStride;
                 return new Vector3(_Accesor[index, 0], _Accesor[index, 1], _Accesor[index, 2]);
             }
 
             set
             {
-                index *= _ByteStride;
                 _Accesor[index, 0] = value.X;
                 _Accesor[index, 1] = value.Y;
                 _Accesor[index, 2] = value.Z;
@@ -446,18 +407,7 @@ namespace glTF2Sharp.Memory
 
         public Vector4Array(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            data = data.Slice(byteOffset);
-
-            var len = encoding.ByteLength() * 4;
-
-            _ByteStride = Math.Max(len, byteStride);
-
-            _Accesor = new FloatingAccessor(data, encoding, normalized);
-
-            _ItemCount = _Accesor.ByteLength / _ByteStride;
-            if ((_Accesor.ByteLength % _ByteStride) >= len) ++_ItemCount;
-
-            _ItemCount = Math.Min(_ItemCount, count);
+            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 4, encoding, normalized);
         }
 
         #endregion
@@ -467,12 +417,6 @@ namespace glTF2Sharp.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private FloatingAccessor _Accesor;
 
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ByteStride;
-
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ItemCount;
-
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private Vector4[] _DebugItems => this.ToArray();
 
@@ -481,19 +425,17 @@ namespace glTF2Sharp.Memory
         #region API
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        public int Count => _ItemCount;
+        public int Count => _Accesor.Count;
 
         public Vector4 this[int index]
         {
             get
             {
-                index *= _ByteStride;
                 return new Vector4(_Accesor[index, 0], _Accesor[index, 1], _Accesor[index, 2], _Accesor[index, 3]);
             }
 
             set
             {
-                index *= _ByteStride;
                 _Accesor[index, 0] = value.X;
                 _Accesor[index, 1] = value.Y;
                 _Accesor[index, 2] = value.Z;
@@ -530,17 +472,7 @@ namespace glTF2Sharp.Memory
 
         public QuaternionArray(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            data = data.Slice(byteOffset);
-
-            var len = encoding.ByteLength() * 4;
-
-            _Accesor = new FloatingAccessor(data, encoding, normalized);
-            _ByteStride = Math.Max(len, byteStride);
-
-            _ItemCount = _Accesor.ByteLength / _ByteStride;
-            if ((_Accesor.ByteLength % _ByteStride) >= len) ++_ItemCount;
-
-            _ItemCount = Math.Min(_ItemCount, count);
+            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 4, encoding, normalized);
         }
 
         #endregion
@@ -550,12 +482,6 @@ namespace glTF2Sharp.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private FloatingAccessor _Accesor;
 
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ByteStride;
-
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ItemCount;
-
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private Quaternion[] _DebugItems => this.ToArray();
 
@@ -564,19 +490,17 @@ namespace glTF2Sharp.Memory
         #region API
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        public int Count => _ItemCount;
+        public int Count => _Accesor.Count;
 
         public Quaternion this[int index]
         {
             get
             {
-                index *= _ByteStride;
                 return new Quaternion(_Accesor[index, 0], _Accesor[index, 1], _Accesor[index, 2], _Accesor[index, 3]);
             }
 
             set
             {
-                index *= _ByteStride;
                 _Accesor[index, 0] = value.X;
                 _Accesor[index, 1] = value.Y;
                 _Accesor[index, 2] = value.Z;
@@ -613,18 +537,7 @@ namespace glTF2Sharp.Memory
 
         public Matrix4x4Array(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            data = data.Slice(byteOffset);
-
-            var len = encoding.ByteLength() * 16;
-
-            _ByteStride = Math.Max(len, byteStride);
-
-            _Accesor = new FloatingAccessor(data, encoding, normalized);
-
-            _ItemCount = _Accesor.ByteLength / _ByteStride;
-            if ((_Accesor.ByteLength % _ByteStride) >= len) ++_ItemCount;
-
-            _ItemCount = Math.Min(_ItemCount, count);
+            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 16, encoding, normalized);
         }
 
         #endregion
@@ -634,12 +547,6 @@ namespace glTF2Sharp.Memory
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private FloatingAccessor _Accesor;
 
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ByteStride;
-
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        private readonly int _ItemCount;
-
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private Matrix4x4[] _DebugItems => this.ToArray();
 
@@ -648,13 +555,12 @@ namespace glTF2Sharp.Memory
         #region API
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        public int Count => _ItemCount;
+        public int Count => _Accesor.Count;
 
         public Matrix4x4 this[int index]
         {
             get
             {
-                index *= _ByteStride;
                 return new Matrix4x4
                     (
                     _Accesor[index, 0], _Accesor[index, 1], _Accesor[index, 2], _Accesor[index, 3],
@@ -666,7 +572,6 @@ namespace glTF2Sharp.Memory
 
             set
             {
-                index *= _ByteStride;
                 _Accesor[index, 0] = value.M11;
                 _Accesor[index, 1] = value.M12;
                 _Accesor[index, 2] = value.M13;

+ 10 - 55
src/glTF2Sharp.DOM/Schema2/gltf.AccessorSparse.cs

@@ -34,42 +34,14 @@ namespace glTF2Sharp.Schema2
             this._values = new AccessorSparseValues(values, valuesOffset);
         }
 
-        public int Count => _count; // what is this!?? TODO: check with specs
+        public int Count => _count;
 
-        public Memory.SparseArray<Single> GetScalarArray(Accessor baseAccessor)
+        internal (Memory.IntegerArray, Memory.AttributeAccessor) _CreateMemoryAccessors(Accessor baseAccessor)
         {
-            var bot = baseAccessor.AsScalarArray(false);
-            var top = this._values.GetScalarArray(baseAccessor.LogicalParent, _count, baseAccessor.Encoding, baseAccessor.Normalized);
-            var idx = this._indices.GetIndicesArray(baseAccessor.LogicalParent, _count);
+            var key = this._indices._GetIndicesArray(baseAccessor.LogicalParent, _count);
+            var val = this._values._GetMemoryAccessor(baseAccessor.LogicalParent, _count, baseAccessor);
 
-            return new Memory.SparseArray<Single>(bot, top, idx);
-        }
-
-        public Memory.SparseArray<Vector2> GetVector2Array(Accessor baseAccessor)
-        {
-            var bot = baseAccessor.AsVector2Array(false);
-            var top = this._values.GetVector2Array(baseAccessor.LogicalParent, _count, baseAccessor.Encoding, baseAccessor.Normalized);
-            var idx = this._indices.GetIndicesArray(baseAccessor.LogicalParent, _count);
-
-            return new Memory.SparseArray<Vector2>(bot, top, idx);
-        }
-
-        public Memory.SparseArray<Vector3> GetVector3Array(Accessor baseAccessor)
-        {
-            var bot = baseAccessor.AsVector3Array(false);
-            var top = this._values.GetVector3Array(baseAccessor.LogicalParent, _count, baseAccessor.Encoding, baseAccessor.Normalized);
-            var idx = this._indices.GetIndicesArray(baseAccessor.LogicalParent, _count);
-
-            return new Memory.SparseArray<Vector3>(bot, top, idx);
-        }
-
-        public Memory.SparseArray<Vector4> GetVector4Array(Accessor baseAccessor)
-        {
-            var bot = baseAccessor.AsVector4Array(false);
-            var top = this._values.GetVector4Array(baseAccessor.LogicalParent, _count, baseAccessor.Encoding, baseAccessor.Normalized);
-            var idx = this._indices.GetIndicesArray(baseAccessor.LogicalParent, _count);
-
-            return new Memory.SparseArray<Vector4>(bot, top, idx);
+            return (key, val);
         }
     }
 
@@ -87,7 +59,7 @@ namespace glTF2Sharp.Schema2
             this._componentType = encoding;
         }
 
-        public Memory.IntegerArray GetIndicesArray(ROOT root, int count)
+        internal Memory.IntegerArray _GetIndicesArray(ROOT root, int count)
         {
             var srcBuffer = root.LogicalBufferViews[this._bufferView];
             return srcBuffer.CreateIndicesArray(this._byteOffset ?? 0, count, this._componentType);
@@ -107,28 +79,11 @@ namespace glTF2Sharp.Schema2
             this._byteOffset = byteOffset.AsNullable(_byteOffsetDefault);
         }
 
-        public Memory.ScalarArray GetScalarArray(ROOT root, int count, ComponentType encoding, Boolean normalized)
+        internal Memory.AttributeAccessor _GetMemoryAccessor(ROOT root, int count, Accessor baseAccessor)
         {
-            var srcBuffer = root.LogicalBufferViews[this._bufferView];
-            return srcBuffer.CreateScalarArray(this._byteOffset ?? 0, count, encoding, normalized);
-        }
-
-        public Memory.Vector2Array GetVector2Array(ROOT root, int count, ComponentType encoding, Boolean normalized)
-        {
-            var srcBuffer = root.LogicalBufferViews[this._bufferView];
-            return srcBuffer.CreateVector2Array(this._byteOffset ?? 0, count, encoding, normalized);
-        }
-
-        public Memory.Vector3Array GetVector3Array(ROOT root, int count, ComponentType encoding, Boolean normalized)
-        {
-            var srcBuffer = root.LogicalBufferViews[this._bufferView];
-            return srcBuffer.CreateVector3Array(this._byteOffset ?? 0, count, encoding, normalized);
-        }
-
-        public Memory.Vector4Array GetVector4Array(ROOT root, int count, ComponentType encoding, Boolean normalized)
-        {
-            var srcBuffer = root.LogicalBufferViews[this._bufferView];
-            return srcBuffer.CreateVector4Array(this._byteOffset ?? 0, count, encoding, normalized);
+            var view = root.LogicalBufferViews[this._bufferView];
+            var info = new Memory.AttributeInfo(null, this._byteOffset ?? 0, count, view.ByteStride, baseAccessor.Dimensions, baseAccessor.Encoding, baseAccessor.Normalized);
+            return new Memory.AttributeAccessor(info, view.Data);
         }
     }
 }

+ 32 - 20
src/glTF2Sharp.DOM/Schema2/gltf.Accessors.cs

@@ -67,6 +67,17 @@ namespace glTF2Sharp.Schema2
 
         #endregion
 
+        #region API
+
+        internal Memory.AttributeAccessor _GetMemoryAccessor()
+        {
+            var view = SourceBufferView;
+            var info = new Memory.AttributeInfo(null, ByteOffset, Count, view.ByteStride, Dimensions, Encoding, Normalized);
+            return new Memory.AttributeAccessor(info, view.Data);
+        }
+
+        #endregion
+
         #region Data Buffer API
 
         internal void SetData(BufferView buffer, int byteOffset, ElementType dimensions, ComponentType encoding, Boolean normalized, int count)
@@ -90,10 +101,7 @@ namespace glTF2Sharp.Schema2
 
         public Memory.Matrix4x4Array CastToMatrix4x4Accessor()
         {
-            Guard.IsFalse(this.IsSparse, nameof(IsSparse));
-            Guard.IsTrue(this.Dimensions == ElementType.MAT4, nameof(Dimensions));
-
-            return SourceBufferView.CreateMatrix4x4Array(this.ByteOffset, this.Encoding, this.Normalized);
+            return _GetMemoryAccessor().AsMatrix4x4Array();
         }
 
         #endregion
@@ -151,40 +159,44 @@ namespace glTF2Sharp.Schema2
             _UpdateBounds();
         }
 
-        public Memory.IEncodedArray<Single> AsScalarArray(bool useSparse = true)
+        public Memory.IEncodedArray<Single> AsScalarArray()
         {
-            Guard.IsTrue(this.Dimensions == ElementType.SCALAR, nameof(Dimensions));
+            var memory = _GetMemoryAccessor();
 
-            if (this._sparse != null && useSparse) return this._sparse.GetScalarArray(this);
+            if (this._sparse == null) return memory.AsScalarArray();
 
-            return SourceBufferView.CreateScalarArray(this.ByteOffset, this.Count, this.Encoding, this.Normalized);
+            var sparseKV = this._sparse._CreateMemoryAccessors(this);
+            return Memory.AttributeAccessor.CreateScalarSparseArray(memory, sparseKV.Item1, sparseKV.Item2);
         }
 
-        public Memory.IEncodedArray<Vector2> AsVector2Array(bool useSparse = true)
+        public Memory.IEncodedArray<Vector2> AsVector2Array()
         {
-            Guard.IsTrue(this.Dimensions == ElementType.VEC2, nameof(Dimensions));
+            var memory = _GetMemoryAccessor();
 
-            if (this._sparse != null && useSparse) return this._sparse.GetVector2Array(this);
+            if (this._sparse == null) return memory.AsVector2Array();
 
-            return SourceBufferView.CreateVector2Array(this.ByteOffset, this.Count, this.Encoding, this.Normalized);
+            var sparseKV = this._sparse._CreateMemoryAccessors(this);
+            return Memory.AttributeAccessor.CreateVector2SparseArray(memory, sparseKV.Item1, sparseKV.Item2);
         }
 
-        public Memory.IEncodedArray<Vector3> AsVector3Array(bool useSparse = true)
+        public Memory.IEncodedArray<Vector3> AsVector3Array()
         {
-            Guard.IsTrue(this.Dimensions == ElementType.VEC3, nameof(Dimensions));
+            var memory = _GetMemoryAccessor();
 
-            if (this._sparse != null && useSparse) return this._sparse.GetVector3Array(this);
+            if (this._sparse == null) return memory.AsVector3Array();
 
-            return SourceBufferView.CreateVector3Array(this.ByteOffset, this.Count, this.Encoding, this.Normalized);
+            var sparseKV = this._sparse._CreateMemoryAccessors(this);
+            return Memory.AttributeAccessor.CreateVector3SparseArray(memory, sparseKV.Item1, sparseKV.Item2);
         }
 
-        public Memory.IEncodedArray<Vector4> AsVector4Array(bool useSparse = true)
+        public Memory.IEncodedArray<Vector4> AsVector4Array()
         {
-            Guard.IsTrue(this.Dimensions == ElementType.VEC4, nameof(Dimensions));
+            var memory = _GetMemoryAccessor();
 
-            if (this._sparse != null && useSparse) return this._sparse.GetVector4Array(this);
+            if (this._sparse == null) return memory.AsVector4Array();
 
-            return SourceBufferView.CreateVector4Array(this.ByteOffset, this.Count, this.Encoding, this.Normalized);
+            var sparseKV = this._sparse._CreateMemoryAccessors(this);
+            return Memory.AttributeAccessor.CreateVector4SparseArray(memory, sparseKV.Item1, sparseKV.Item2);
         }
 
         public ArraySegment<Byte> TryGetVertexBytes(int vertexIdx)

+ 0 - 30
src/glTF2Sharp.DOM/Schema2/gltf.BufferView.cs

@@ -109,36 +109,6 @@ namespace glTF2Sharp.Schema2
             return new Memory.IntegerArray(this.Data, byteOffset, count, encoding);
         }
 
-        public Memory.ScalarArray CreateScalarArray(int byteOffset, int count, ENCODING encoding, Boolean normalized)
-        {
-            return new Memory.ScalarArray(this.Data, byteOffset, count, this.ByteStride, encoding, normalized);
-        }
-
-        public Memory.Vector2Array CreateVector2Array(int byteOffset, int count, ENCODING encoding, Boolean normalized)
-        {
-            return new Memory.Vector2Array(this.Data, byteOffset, count, this.ByteStride, encoding, normalized);
-        }
-
-        public Memory.Vector3Array CreateVector3Array(int byteOffset, int count, ENCODING encoding, Boolean normalized)
-        {
-            return new Memory.Vector3Array(this.Data, byteOffset, count, this.ByteStride, encoding, normalized);
-        }
-
-        public Memory.Vector4Array CreateVector4Array(int byteOffset, int count, ENCODING encoding, Boolean normalized)
-        {
-            return new Memory.Vector4Array(this.Data, byteOffset, count, this.ByteStride, encoding, normalized);
-        }
-
-        public Memory.QuaternionArray CreateQuaternionArray(int byteOffset, ENCODING encoding, Boolean normalized)
-        {
-            return new Memory.QuaternionArray(this.Data.Slice(byteOffset), this.ByteStride, encoding, normalized);
-        }
-
-        public Memory.Matrix4x4Array CreateMatrix4x4Array(int byteOffset, ENCODING encoding, Boolean normalized)
-        {
-            return new Memory.Matrix4x4Array(this.Data.Slice(byteOffset), this.ByteStride, encoding, normalized);
-        }
-
         /// <summary>
         /// Checks if all the accessors use this buffer in interleaved arrangement
         /// </summary>

+ 17 - 12
src/glTF2Sharp.Tests/Memory/MemoryArrayTests.cs

@@ -13,22 +13,27 @@ namespace glTF2Sharp.Memory
         [Test]
         public void TestFloatingArray()
         {
-            Assert.AreEqual(17, new FloatingAccessor(new Byte[] { 17 }, Schema2.ComponentType.UNSIGNED_BYTE, false)[0]);
-            Assert.AreEqual(17, new FloatingAccessor(new Byte[] { 17, 0 }, Schema2.ComponentType.UNSIGNED_SHORT, false)[0]);            
+            Assert.AreEqual(17, _CreateFloatingAccessor(new Byte[] { 17 }, Schema2.ComponentType.UNSIGNED_BYTE, false)[0]);
+            Assert.AreEqual(17, _CreateFloatingAccessor(new Byte[] { 17, 0 }, Schema2.ComponentType.UNSIGNED_SHORT, false)[0]);            
 
-            Assert.AreEqual(17, new FloatingAccessor(new Byte[] { 17 }, Schema2.ComponentType.BYTE, false)[0]);
-            Assert.AreEqual(17, new FloatingAccessor(new Byte[] { 17, 0 }, Schema2.ComponentType.SHORT, false)[0]);
+            Assert.AreEqual(17, _CreateFloatingAccessor(new Byte[] { 17 }, Schema2.ComponentType.BYTE, false)[0]);
+            Assert.AreEqual(17, _CreateFloatingAccessor(new Byte[] { 17, 0 }, Schema2.ComponentType.SHORT, false)[0]);
 
-            Assert.AreEqual(1, new FloatingAccessor(new Byte[] { 255 }, Schema2.ComponentType.UNSIGNED_BYTE, true)[0]);
-            Assert.AreEqual(1, new FloatingAccessor(new Byte[] { 127 }, Schema2.ComponentType.BYTE, true)[0]);
-            Assert.AreEqual(-1, new FloatingAccessor(new Byte[] { 128 }, Schema2.ComponentType.BYTE, true)[0]);
+            Assert.AreEqual(1, _CreateFloatingAccessor(new Byte[] { 255 }, Schema2.ComponentType.UNSIGNED_BYTE, true)[0]);
+            Assert.AreEqual(1, _CreateFloatingAccessor(new Byte[] { 127 }, Schema2.ComponentType.BYTE, true)[0]);
+            Assert.AreEqual(-1, _CreateFloatingAccessor(new Byte[] { 128 }, Schema2.ComponentType.BYTE, true)[0]);
 
-            Assert.AreEqual(1, new FloatingAccessor(new Byte[] { 255, 255 }, Schema2.ComponentType.UNSIGNED_SHORT, true)[0]);
-            Assert.AreEqual(1, new FloatingAccessor(new Byte[] { 255, 127 }, Schema2.ComponentType.SHORT, true)[0]);
-            Assert.AreEqual(-1, new FloatingAccessor(new Byte[] { 0,  128 }, Schema2.ComponentType.SHORT, true)[0]);
+            Assert.AreEqual(1, _CreateFloatingAccessor(new Byte[] { 255, 255 }, Schema2.ComponentType.UNSIGNED_SHORT, true)[0]);
+            Assert.AreEqual(1, _CreateFloatingAccessor(new Byte[] { 255, 127 }, Schema2.ComponentType.SHORT, true)[0]);
+            Assert.AreEqual(-1, _CreateFloatingAccessor(new Byte[] { 0,  128 }, Schema2.ComponentType.SHORT, true)[0]);
 
-            Assert.AreEqual(17, new FloatingAccessor(new Byte[] { 17, 0, 0, 0 }, Schema2.ComponentType.UNSIGNED_INT, false)[0]);
-            Assert.AreEqual(1, new FloatingAccessor(new Byte[] { 0,0, 0x80, 0x3f }, Schema2.ComponentType.FLOAT, false)[0]);
+            Assert.AreEqual(17, _CreateFloatingAccessor(new Byte[] { 17, 0, 0, 0 }, Schema2.ComponentType.UNSIGNED_INT, false)[0]);
+            Assert.AreEqual(1, _CreateFloatingAccessor(new Byte[] { 0,0, 0x80, 0x3f }, Schema2.ComponentType.FLOAT, false)[0]);
+        }
+
+        private static FloatingAccessor _CreateFloatingAccessor(byte[] data, Schema2.ComponentType encoding, bool normalized)
+        {
+            return new FloatingAccessor(new ArraySegment<byte>(data), 0, int.MaxValue, 0, 1, encoding, normalized);
         }
 
         [Test]

+ 3 - 2
src/glTF2Sharp.Tests/Schema2/AccessorSparseTests.cs

@@ -32,8 +32,9 @@ namespace glTF2Sharp.Schema2
 
             var accessor = primitive.GetVertexAccessor("POSITION");
 
-            var basePositions = accessor.AsVector3Array(false);
-            var goodPositions = accessor.AsVector3Array(true);
+            var basePositions = accessor._GetMemoryAccessor().AsVector3Array();
+
+            var positions = accessor.AsVector3Array();            
         }
     }
 }