Przeglądaj źródła

redesigning higher level API - again -

Vicente Penades 6 lat temu
rodzic
commit
f78c2d955c

+ 111 - 19
src/glTF2Sharp.DOM/Memory/AttributeInfo.cs → src/glTF2Sharp.DOM/Geometry/MemoryAccessor.cs

@@ -4,14 +4,50 @@ using System.Linq;
 using System.Numerics;
 using System.Text;
 
-namespace glTF2Sharp.Memory
+namespace glTF2Sharp.Geometry
 {
+    using Memory;
+
+    using DIMENSIONS = Schema2.ElementType;
+    using ENCODING = Schema2.ComponentType;
+
     /// <summary>
-    /// Represents the vertex attribute access info
+    /// Defines the pattern in which a <see cref="ArraySegment{Byte}"/> is accessed and decoded to meaningful values.
     /// </summary>
-    public struct AttributeInfo
+    public struct MemoryAccessInfo
     {
-        public AttributeInfo(string name, int byteOffset, int itemsCount, int byteStride, Schema2.ElementType dimensions, Schema2.ComponentType encoding, Boolean normalized)
+        #region constructor
+
+        public static MemoryAccessInfo[] Create(params string[] attributes)
+        {
+            return attributes.Select(item => CreateDefaultElement(item)).ToArray();
+        }
+
+        public static MemoryAccessInfo CreateDefaultElement(string attribute)
+        {
+            switch (attribute)
+            {
+                case "INDEX": return new MemoryAccessInfo("INDEX", 0, 0, 0, DIMENSIONS.SCALAR, ENCODING.UNSIGNED_INT, false);
+
+                case "POSITION": return new MemoryAccessInfo("POSITION", 0, 0, 0, DIMENSIONS.VEC3);
+                case "NORMAL": return new MemoryAccessInfo("NORMAL", 0, 0, 0, DIMENSIONS.VEC3);
+                case "TANGENT": return new MemoryAccessInfo("TANGENT", 0, 0, 0, DIMENSIONS.VEC4);
+
+                case "TEXCOORD_0": return new MemoryAccessInfo("TEXCOORD_0", 0, 0, 0, DIMENSIONS.VEC2);
+                case "TEXCOORD_1": return new MemoryAccessInfo("TEXCOORD_1", 0, 0, 0, DIMENSIONS.VEC2);
+                case "TEXCOORD_2": return new MemoryAccessInfo("TEXCOORD_2", 0, 0, 0, DIMENSIONS.VEC2);
+                case "TEXCOORD_3": return new MemoryAccessInfo("TEXCOORD_3", 0, 0, 0, DIMENSIONS.VEC2);
+
+                case "COLOR_0": return new MemoryAccessInfo("COLOR_0", 0, 0, 0, DIMENSIONS.VEC4, ENCODING.UNSIGNED_BYTE, true);
+
+                case "JOINTS_0": return new MemoryAccessInfo("JOINTS_0", 0, 0, 0, DIMENSIONS.VEC4, ENCODING.UNSIGNED_BYTE);
+                case "WEIGHTS_0": return new MemoryAccessInfo("WEIGHTS_0", 0, 0, 0, DIMENSIONS.VEC4, ENCODING.UNSIGNED_BYTE, true);
+            }
+
+            throw new NotImplementedException();
+        }
+
+        public MemoryAccessInfo(string name, int byteOffset, int itemsCount, int byteStride, DIMENSIONS dimensions, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
         {
             this.Name = name;
             this.ByteOffset = byteOffset;
@@ -22,14 +58,22 @@ namespace glTF2Sharp.Memory
             this.Normalized = normalized;
         }
 
+        #endregion
+
+        #region data
+
         public String Name;
         public int ByteOffset;
         public int ItemsCount;
         public int ByteStride;
-        public Schema2.ElementType Dimensions;
-        public Schema2.ComponentType Encoding;
+        public DIMENSIONS Dimensions;
+        public ENCODING Encoding;
         public Boolean Normalized;
 
+        #endregion
+
+        #region API
+
         public int ByteLength => this.Dimensions.DimCount() * this.Encoding.ByteLength();
 
         public Boolean IsValidVertexAttribute
@@ -56,7 +100,7 @@ namespace glTF2Sharp.Memory
                 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.Dimensions != DIMENSIONS.SCALAR) return false;
                 if (this.Normalized) return false;
                 if (this.ByteStride == 0) return true;
                 if (this.ByteStride == 1) return true;
@@ -65,55 +109,101 @@ namespace glTF2Sharp.Memory
                 return false;
             }
         }
+
+        #endregion
     }
 
-    public struct AttributeAccessor
+    /// <summary>
+    /// Wraps a <see cref="ArraySegment{Byte}"/> decoding it and exposing its content as arrays of different types.
+    /// </summary>
+    public struct MemoryAccessor
     {
-        public AttributeAccessor(AttributeInfo info, ArraySegment<Byte> data)
+        #region constructor
+
+        public MemoryAccessor(MemoryAccessInfo info, ArraySegment<Byte> data)
         {
             this.Attribute = info;
             this.Data = data;
         }
 
-        public AttributeInfo Attribute;
+        public MemoryAccessor(MemoryAccessInfo info)
+        {
+            this.Attribute = info;
+            this.Data = default;
+        }
+
+        #endregion
+
+        #region data
+
+        public MemoryAccessInfo Attribute;
         public ArraySegment<Byte> Data;
 
+        #endregion
+
+        #region API
+
+        public void SetIndexDataSource(ArraySegment<Byte> data, int byteOffset, int itemsCount)
+        {
+            Guard.IsTrue(Attribute.IsValidIndexer, nameof(Attribute));
+            Data = data;
+            Attribute.ByteOffset = byteOffset;
+            Attribute.ItemsCount = itemsCount;
+            Attribute.ByteStride = 0;
+        }
+
+        public void SetVertexDataSource(ArraySegment<Byte> data, int byteOffset, int itemsCount, int byteStride)
+        {
+            Guard.IsTrue(Attribute.IsValidVertexAttribute, nameof(Attribute));
+            Data = data;
+            Attribute.ByteOffset = byteOffset;
+            Attribute.ItemsCount = itemsCount;
+            Attribute.ByteStride = byteStride;
+        }
+
+        public IntegerArray AsIntegerArray()
+        {
+            Guard.IsTrue(Attribute.IsValidIndexer, nameof(Attribute));
+            Guard.IsTrue(Attribute.Dimensions == DIMENSIONS.SCALAR, nameof(Attribute));
+            return new IntegerArray(Data, Attribute.ByteOffset, Attribute.ItemsCount, Attribute.Encoding.ToIndex());
+        }
+
         public ScalarArray AsScalarArray()
         {
             Guard.IsTrue(Attribute.IsValidVertexAttribute, nameof(Attribute));
-            Guard.IsTrue(Attribute.Dimensions == Schema2.ElementType.SCALAR, nameof(Attribute));
+            Guard.IsTrue(Attribute.Dimensions == DIMENSIONS.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));
+            Guard.IsTrue(Attribute.Dimensions == DIMENSIONS.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));
+            Guard.IsTrue(Attribute.Dimensions == DIMENSIONS.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));
+            Guard.IsTrue(Attribute.Dimensions == DIMENSIONS.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));
+            Guard.IsTrue(Attribute.Dimensions == DIMENSIONS.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)
+        public static IEncodedArray<Single> CreateScalarSparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
             Guard.IsTrue(bottom.Attribute.Dimensions == topValues.Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(topKeys.Count <= bottom.Attribute.ItemsCount, nameof(topKeys));
@@ -123,7 +213,7 @@ namespace glTF2Sharp.Memory
             return new SparseArray<Single>(bottom.AsScalarArray(), topValues.AsScalarArray(), topKeys);
         }
 
-        public static IEncodedArray<Vector2> CreateVector2SparseArray(AttributeAccessor bottom, IntegerArray topKeys, AttributeAccessor topValues)
+        public static IEncodedArray<Vector2> CreateVector2SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
             Guard.IsTrue(bottom.Attribute.Dimensions == topValues.Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(topKeys.Count <= bottom.Attribute.ItemsCount, nameof(topKeys));
@@ -133,7 +223,7 @@ namespace glTF2Sharp.Memory
             return new SparseArray<Vector2>(bottom.AsVector2Array(), topValues.AsVector2Array(), topKeys);
         }
 
-        public static IEncodedArray<Vector3> CreateVector3SparseArray(AttributeAccessor bottom, IntegerArray topKeys, AttributeAccessor topValues)
+        public static IEncodedArray<Vector3> CreateVector3SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
             Guard.IsTrue(bottom.Attribute.Dimensions == topValues.Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(topKeys.Count <= bottom.Attribute.ItemsCount, nameof(topKeys));
@@ -143,7 +233,7 @@ namespace glTF2Sharp.Memory
             return new SparseArray<Vector3>(bottom.AsVector3Array(), topValues.AsVector3Array(), topKeys);
         }
 
-        public static IEncodedArray<Vector4> CreateVector4SparseArray(AttributeAccessor bottom, IntegerArray topKeys, AttributeAccessor topValues)
+        public static IEncodedArray<Vector4> CreateVector4SparseArray(MemoryAccessor bottom, IntegerArray topKeys, MemoryAccessor topValues)
         {
             Guard.IsTrue(bottom.Attribute.Dimensions == topValues.Attribute.Dimensions, nameof(topValues));
             Guard.IsTrue(topKeys.Count <= bottom.Attribute.ItemsCount, nameof(topKeys));
@@ -152,5 +242,7 @@ namespace glTF2Sharp.Memory
 
             return new SparseArray<Vector4>(bottom.AsVector4Array(), topValues.AsVector4Array(), topKeys);
         }
+
+        #endregion
     }
 }

+ 304 - 0
src/glTF2Sharp.DOM/Geometry/MeshPrimitive.cs

@@ -0,0 +1,304 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+
+namespace glTF2Sharp.Geometry
+{
+    abstract class NamedObject
+    {
+        public NamedObject() { }
+
+        public NamedObject(Schema2.LogicalChildOfRoot source)
+        {
+            Name = source.Name;
+        }
+
+        public string Name { get; set; }
+        public Object Extra { get; set; }
+    }
+
+    [System.Diagnostics.DebuggerDisplay("{Name}.{_MemoryAccessor.Attribute.Name} {_MemoryAccessor.Attribute.ItemsCount}")]
+    class VertexAccessor : NamedObject
+    {
+        #region lifecycle
+
+        public VertexAccessor(string attributeName, Schema2.Accessor accessor)
+            : base(accessor)
+        {
+            _MemoryAccessor = accessor._GetMemoryAccessor();
+            _MemoryAccessor.Attribute.Name = attributeName;
+            _Sparse = accessor._GetSparseMemoryAccessor();
+        }
+
+        public VertexAccessor(MemoryAccessInfo info)
+        {
+            _MemoryAccessor = new MemoryAccessor(info);
+        }
+
+        public static VertexAccessor[] CreateAccessors(int itemsCount, params string[] attributes)
+        {
+            var accessors = MemoryAccessInfo
+                .Create(attributes)
+                .Select(item => new VertexAccessor(item))
+                .ToArray();
+
+            int byteOffset = 0;
+            var byteStride = accessors.Sum(item => item._MemoryAccessor.Attribute.ByteLength);
+
+            var data = new ArraySegment<Byte>(new Byte[byteStride * itemsCount]);
+
+            for (int i = 0; i < accessors.Length; ++i)
+            {
+                var a = accessors[i];
+
+                a._MemoryAccessor.SetVertexDataSource(data, byteOffset, itemsCount, byteStride);
+
+                byteOffset += a._MemoryAccessor.Attribute.ByteLength;
+            }
+
+            return accessors;
+        }
+
+        #endregion
+
+        #region data
+
+        private Geometry.MemoryAccessor _MemoryAccessor;
+        private KeyValuePair<Memory.IntegerArray, Geometry.MemoryAccessor>? _Sparse;
+
+        #endregion
+
+        #region API
+
+        public void SetValues(int startIndex, params Single[] values)
+        {
+            Memory.EncodedArrayUtils.CopyFrom(AsScalarArray(), startIndex, values);
+        }
+
+        public void SetValues(int startIndex, params Vector2[] values)
+        {
+            Memory.EncodedArrayUtils.CopyFrom(AsVector2Array(), startIndex, values);
+        }
+
+        public void SetValues(int startIndex, params Vector3[] values)
+        {
+            Memory.EncodedArrayUtils.CopyFrom(AsVector3Array(), startIndex, values);
+        }
+
+        public void SetValues(int startIndex, params Vector4[] values)
+        {
+            Memory.EncodedArrayUtils.CopyFrom(AsVector4Array(), startIndex, values);
+        }
+
+        public Memory.IEncodedArray<Single> AsScalarArray()
+        {
+            if (!_Sparse.HasValue) return _MemoryAccessor.AsScalarArray();
+
+            return MemoryAccessor.CreateScalarSparseArray(_MemoryAccessor, _Sparse.Value.Key, _Sparse.Value.Value);
+        }
+
+        public Memory.IEncodedArray<Vector2> AsVector2Array()
+        {
+            if (!_Sparse.HasValue) return _MemoryAccessor.AsVector2Array();
+
+            return MemoryAccessor.CreateVector2SparseArray(_MemoryAccessor, _Sparse.Value.Key, _Sparse.Value.Value);
+        }
+
+        public Memory.IEncodedArray<Vector3> AsVector3Array()
+        {
+            if (!_Sparse.HasValue) return _MemoryAccessor.AsVector3Array();
+
+            return MemoryAccessor.CreateVector3SparseArray(_MemoryAccessor, _Sparse.Value.Key, _Sparse.Value.Value);
+        }
+
+        public Memory.IEncodedArray<Vector4> AsVector4Array()
+        {
+            if (!_Sparse.HasValue) return _MemoryAccessor.AsVector4Array();
+
+            return MemoryAccessor.CreateVector4SparseArray(_MemoryAccessor, _Sparse.Value.Key, _Sparse.Value.Value);
+        }
+
+        public void AssignTo(Schema2.MeshPrimitive dstPrim)
+        {
+            var dstAccessor = dstPrim.LogicalParent.LogicalParent.CreateAccessor(this.Name);
+            dstAccessor.SetVertexData(_MemoryAccessor);
+            dstPrim.SetVertexAccessor(this._MemoryAccessor.Attribute.Name, dstAccessor);
+        }
+
+        #endregion
+    }
+
+    [System.Diagnostics.DebuggerDisplay("{Name}.{_MemoryAccessor.Attribute.Name} {_MemoryAccessor.Attribute.ItemsCount}")]
+    class IndicesAccessor : NamedObject
+    {
+        #region lifecycle
+
+        public IndicesAccessor(Schema2.Accessor accessor)
+            : base(accessor)
+        {
+            _MemoryAccessor = accessor._GetMemoryAccessor();
+            _MemoryAccessor.Attribute.Name = "INDEX";
+        }
+
+        public IndicesAccessor(MemoryAccessInfo info)
+        {
+            _MemoryAccessor = new MemoryAccessor(info);
+        }
+
+        public static IndicesAccessor CreateAccessors(int itemsCount)
+        {
+            var info = MemoryAccessInfo.CreateDefaultElement("INDEX");
+            info.ItemsCount = itemsCount;
+
+            var data = new ArraySegment<Byte>(new Byte[info.ByteLength * itemsCount]);
+
+            var accessor =  new IndicesAccessor(info);
+
+            accessor._MemoryAccessor.SetIndexDataSource(data, 0, itemsCount);
+
+            return accessor;
+        }
+
+        #endregion
+
+        #region data
+
+        private MemoryAccessor _MemoryAccessor;
+
+        #endregion
+
+        #region API
+
+        public void SetValues(int startIndex, params UInt32[] values)
+        {
+            Memory.EncodedArrayUtils.CopyFrom(AsIntegerArray(), startIndex, values);
+        }
+
+        public Memory.IEncodedArray<UInt32> AsIntegerArray()
+        {
+            return _MemoryAccessor.AsIntegerArray();
+        }
+
+        public void AssignTo(Schema2.MeshPrimitive dstPrim)
+        {
+            var dstAccessor = dstPrim.LogicalParent.LogicalParent.CreateAccessor(this.Name);
+            dstAccessor.SetIndexData(_MemoryAccessor);
+            dstPrim.IndexAccessor = dstAccessor;
+        }
+
+        #endregion
+    }
+
+    class MeshPrimitive
+    {
+        #region lifecycle
+
+        public MeshPrimitive() { }
+
+        public MeshPrimitive(Schema2.MeshPrimitive primitive)
+        {
+            _Vertices = primitive.VertexAccessors
+                .Select(kvp => new VertexAccessor(kvp.Key, kvp.Value))
+                .ToArray();
+
+            for (int i = 0; i < primitive.MorpthTargetsCount; ++i)
+            {
+                var accessors = primitive.GetMorphTargetAccessors(i)
+                    .Select(kvp => new VertexAccessor(kvp.Key, kvp.Value))
+                    .ToArray();
+
+                _MorphAccessors.Add(accessors);
+            }
+
+            _Indices = primitive.IndexAccessor == null ? null : new IndicesAccessor(primitive.IndexAccessor);
+            _Primitive = primitive.DrawPrimitiveType;
+            _MaterialIndex = primitive.Material?.LogicalIndex;
+        }
+
+        #endregion
+
+        #region data
+
+        private VertexAccessor[] _Vertices;
+        private readonly List<VertexAccessor[]> _MorphAccessors = new List<VertexAccessor[]>();
+
+        private IndicesAccessor _Indices;
+        private Schema2.PrimitiveType _Primitive;
+
+        private int? _MaterialIndex;
+
+        #endregion
+
+        #region properties
+
+        public IReadOnlyList<VertexAccessor> Vertices => _Vertices;
+
+        public IndicesAccessor Indices => _Indices;
+
+        #endregion
+
+        #region API
+
+        public void SetVertices(int itemsCount, params string[] attributes)
+        {
+            _Vertices = VertexAccessor.CreateAccessors(itemsCount, attributes);
+        }
+
+        public void SetIndices(int itemsCount)
+        {
+            _Indices = IndicesAccessor.CreateAccessors(itemsCount);
+        }
+
+        public void AssignTo(Schema2.MeshPrimitive dstPrim)
+        {
+            // TODO: clear primitive
+
+            foreach (var va in this._Vertices)
+            {
+                va.AssignTo(dstPrim);
+            }
+
+            if (this._Indices != null) this._Indices.AssignTo(dstPrim);
+
+            dstPrim.DrawPrimitiveType = this._Primitive;
+        }
+
+        #endregion
+    }
+
+    [System.Diagnostics.DebuggerDisplay("Mesh {Name}")]
+    class Mesh : NamedObject
+    {
+        #region lifecycle
+
+        public static Mesh[] Create(IReadOnlyList<Schema2.Mesh> src)
+        {
+            var dst = new Mesh[src.Count];
+
+            for (int i = 0; i < dst.Length; ++i)
+            {
+                dst[i] = new Mesh(src[i]);
+            }
+
+            return dst;
+        }
+
+        public Mesh(Schema2.Mesh mesh)
+            : base(mesh)
+        {
+            _Primitives.AddRange(mesh.Primitives, item => new MeshPrimitive(item));
+            _MorpthWeights.AddRange(mesh.MorphWeights);
+        }
+
+        #endregion
+
+        #region data
+
+        private readonly List<MeshPrimitive> _Primitives = new List<MeshPrimitive>();
+        private readonly List<Single> _MorpthWeights = new List<float>();
+
+        #endregion
+    }
+}

+ 0 - 278
src/glTF2Sharp.DOM/Geometry/VertexBuffer.cs

@@ -1,278 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Linq;
-using System.Numerics;
-
-namespace glTF2Sharp.Geometry
-{
-    using SCALARARRAY = Memory.IEncodedArray<Single>;
-    using VECTOR2ARRAY = Memory.IEncodedArray<Vector2>;
-    using VECTOR3ARRAY = Memory.IEncodedArray<Vector3>;
-    using VECTOR4ARRAY = Memory.IEncodedArray<Vector4>;
-
-    /// <summary>
-    /// represents an abstraction of a vertex buffer.
-    /// </summary>
-    public abstract class VertexBuffer
-    {
-        #region properties
-
-        public abstract IReadOnlyList<VertexElement> Attributes { get; }
-
-        public abstract int Count { get; }
-
-        #endregion
-
-        #region API
-
-        public abstract int GetDimensions(string attribute);
-
-        public abstract SCALARARRAY GetScalarColumn(String attribute, int rowStart = 0, int rowCount = int.MaxValue);
-
-        public abstract VECTOR2ARRAY GetVector2Column(String attribute, int rowStart = 0, int rowCount = int.MaxValue);
-
-        public abstract VECTOR3ARRAY GetVector3Column(String attribute, int rowStart = 0, int rowCount = int.MaxValue);
-
-        public abstract VECTOR4ARRAY GetVector4Column(String attribute, int rowStart = 0, int rowCount = int.MaxValue);
-
-        public void SetScalarColumn(String attribute, int rowStart, Single[] values)
-        {
-            var dstColumn = GetScalarColumn(attribute, rowStart);
-            Memory.EncodedArrayUtils.CopyTo(values, dstColumn);
-        }
-
-        public void SetVector2Column(String attribute, int rowStart, Vector2[] values)
-        {
-            var dstColumn = GetVector2Column(attribute, rowStart);
-            Memory.EncodedArrayUtils.CopyTo(values, dstColumn);
-        }
-
-        public void SetVector3Column(String attribute, int rowStart, Vector3[] values)
-        {
-            var dstColumn = GetVector3Column(attribute, rowStart);
-            Memory.EncodedArrayUtils.CopyTo(values, dstColumn);
-        }
-
-        public void SetVector4Column(String attribute, int rowStart, Vector4[] values)
-        {
-            var dstColumn = GetVector4Column(attribute, rowStart);
-            Memory.EncodedArrayUtils.CopyTo(values, dstColumn);
-        }
-
-        public void SetBuffer(int rowIndex, VertexBuffer srcBuffer)
-        {
-            foreach (var ve in this.Attributes)
-            {
-                var l = ve.Dimensions.DimCount();
-
-                if (l == 1 && srcBuffer.GetDimensions(ve.Attribute) == 1)
-                {
-                    var values = srcBuffer.GetScalarColumn(ve.Attribute).ToArray();
-                    SetScalarColumn(ve.Attribute, rowIndex, values);
-                }
-
-                if (l == 2 && srcBuffer.GetDimensions(ve.Attribute) == 2)
-                {
-                    var values = srcBuffer.GetVector2Column(ve.Attribute).ToArray();
-                    SetVector2Column(ve.Attribute, rowIndex, values);
-                }
-
-                if (l == 3 && srcBuffer.GetDimensions(ve.Attribute) == 3)
-                {
-                    var values = srcBuffer.GetVector3Column(ve.Attribute).ToArray();
-                    SetVector3Column(ve.Attribute, rowIndex, values);
-                }
-
-                if (l == 4 && srcBuffer.GetDimensions(ve.Attribute) == 4)
-                {
-                    var values = srcBuffer.GetVector4Column(ve.Attribute).ToArray();
-                    SetVector4Column(ve.Attribute, rowIndex, values);
-                }
-            }
-        }
-
-        public void CopyTo(IReadOnlyDictionary<string, Schema2.Accessor> vertexAccessors)
-        {
-            foreach (var key in vertexAccessors.Keys)
-            {
-                var dim = GetDimensions(key);
-                if (dim == 1) Memory.EncodedArrayUtils.CopyTo(this.GetScalarColumn(key), vertexAccessors[key].AsScalarArray());
-                if (dim == 2) Memory.EncodedArrayUtils.CopyTo(this.GetVector2Column(key), vertexAccessors[key].AsVector2Array());
-                if (dim == 3) Memory.EncodedArrayUtils.CopyTo(this.GetVector3Column(key), vertexAccessors[key].AsVector3Array());
-                if (dim == 4) Memory.EncodedArrayUtils.CopyTo(this.GetVector4Column(key), vertexAccessors[key].AsVector4Array());
-            }
-        }
-
-        #endregion
-
-        #region static API
-
-        public static VertexArray MergeBuffers(VertexBuffer a, VertexBuffer b, params VertexElement[] elements)
-        {
-            var dstBuffer = new VertexArray(a.Count + b.Count, elements);
-
-            dstBuffer.SetBuffer(0, a);
-            dstBuffer.SetBuffer(a.Count, b);
-
-            return dstBuffer;
-        }
-
-        #endregion
-    }
-
-    /// <summary>
-    /// Represents a fixed collection of vertices with a specific vertex attributes definition.
-    /// </summary>
-    public class VertexArray : VertexBuffer
-    {
-        #region lifecycle
-
-        public VertexArray(int count, params string[] attributes)
-        {
-            _Elements = VertexElement.Create(attributes);
-
-            _ByteStride = VertexElement.GetVertexByteSize(_Elements);
-
-            _Buffer = new Byte[_ByteStride * count];
-        }
-
-        public VertexArray(int count, params VertexElement[] elements)
-        {
-            _Elements = elements;
-
-            _ByteStride = VertexElement.GetVertexByteSize(_Elements);
-
-            _Buffer = new Byte[_ByteStride * count];
-        }
-
-        #endregion
-
-        #region data
-
-        private VertexElement[] _Elements;
-        private int _ByteStride;
-        private Byte[] _Buffer;
-
-        #endregion
-
-        #region properties
-
-        public override IReadOnlyList<VertexElement> Attributes => _Elements;
-
-        public override int Count => _Buffer.Length / _ByteStride;
-
-        public int ByteStride => _ByteStride;
-
-        public Byte[] Data => _Buffer;
-
-        #endregion
-
-        #region API
-
-        public override int GetDimensions(string attribute) { return VertexElement.FindDimensions(_Elements, attribute); }
-
-        public override SCALARARRAY GetScalarColumn(String attribute, int rowStart = 0, int rowCount = int.MaxValue)
-        {
-            Guard.MustBeBetweenOrEqualTo(rowStart, 0, Count - rowCount, nameof(rowStart));
-
-            var column = VertexElement._GetColumn(_Buffer, _Elements, attribute, rowStart, rowCount);
-            if (column.Item3.Dimensions.DimCount() != 1) throw new ArgumentException(nameof(attribute));
-            return new Memory.ScalarArray(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
-        }
-
-        public override VECTOR2ARRAY GetVector2Column(String attribute, int rowStart = 0, int rowCount = int.MaxValue)
-        {
-            Guard.MustBeBetweenOrEqualTo(rowStart, 0, Count - rowCount, nameof(rowStart));
-
-            var column = VertexElement._GetColumn(_Buffer, _Elements, attribute, rowStart, rowCount);
-            if (column.Item3.Dimensions.DimCount() != 2) throw new ArgumentException(nameof(attribute));
-            return new Memory.Vector2Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
-        }
-
-        public override VECTOR3ARRAY GetVector3Column(String attribute, int rowStart = 0, int rowCount = int.MaxValue)
-        {
-            Guard.MustBeBetweenOrEqualTo(rowStart, 0, Count - rowCount, nameof(rowStart));
-
-            var column = VertexElement._GetColumn(_Buffer, _Elements, attribute, rowStart, rowCount);
-            if (column.Item3.Dimensions.DimCount() != 3) throw new ArgumentException(nameof(attribute));
-            return new Memory.Vector3Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
-        }
-
-        public override VECTOR4ARRAY GetVector4Column(String attribute, int rowStart = 0, int rowCount = int.MaxValue)
-        {
-            Guard.MustBeBetweenOrEqualTo(rowStart, 0, Count - rowCount, nameof(rowStart));
-
-            var column = VertexElement._GetColumn(_Buffer, _Elements, attribute, rowStart, rowCount);
-            if (column.Item3.Dimensions.DimCount() != 4) throw new ArgumentException(nameof(attribute));
-            return new Memory.Vector4Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
-        }
-
-        #endregion
-    }
-
-    /// <summary>
-    /// Represents a segment within a VertexArray
-    /// </summary>
-    public class VertexArraySegment : VertexBuffer
-    {
-        #region lifecycle
-
-        public VertexArraySegment(VertexArray array, int offset, int count)
-        {
-            _Buffer = array;
-            _Offset = offset;
-            _Count = count;
-        }
-
-        #endregion
-
-        #region data
-
-        private VertexArray _Buffer;
-        private int _Offset;
-        private int _Count;
-
-        #endregion
-
-        #region properties
-
-        public override IReadOnlyList<VertexElement> Attributes => _Buffer.Attributes;
-
-        public int Offset => _Offset;
-
-        public int ByteOffset => _Buffer.ByteStride * _Offset;
-
-        public override int Count => _Count;
-
-        public VertexArray Array => _Buffer;
-
-        #endregion
-
-        #region API
-
-        public override int GetDimensions(string attribute) { return _Buffer.GetDimensions(attribute); }
-
-        public override SCALARARRAY GetScalarColumn(String attribute, int rowStart = 0, int rowCount = int.MaxValue)
-        {
-            return _Buffer.GetScalarColumn(attribute, _Offset + rowStart, Math.Min(_Count, rowCount));
-        }
-
-        public override VECTOR2ARRAY GetVector2Column(string attribute, int rowStart = 0, int rowCount = int.MaxValue)
-        {
-            return _Buffer.GetVector2Column(attribute, _Offset + rowStart, Math.Min(_Count, rowCount));
-        }
-
-        public override VECTOR3ARRAY GetVector3Column(string attribute, int rowStart = 0, int rowCount = int.MaxValue)
-        {
-            return _Buffer.GetVector3Column(attribute, _Offset + rowStart, Math.Min(_Count, rowCount));
-        }
-
-        public override VECTOR4ARRAY GetVector4Column(string attribute, int rowStart = 0, int rowCount = int.MaxValue)
-        {
-            return _Buffer.GetVector4Column(attribute, _Offset + rowStart, Math.Min(_Count, rowCount));
-        }
-
-        #endregion
-    }
-}

+ 0 - 133
src/glTF2Sharp.DOM/Geometry/VertexElement.cs

@@ -1,133 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Linq;
-using System.Numerics;
-
-namespace glTF2Sharp.Geometry
-{
-    using Schema2;
-
-    /// <summary>
-    /// Defines a vertex attribute, dimensions and encoding.
-    /// </summary>
-    public struct VertexElement
-    {
-        #region lifecycle
-
-        public static VertexElement[] Create(params string[] attributes)
-        {
-            return attributes.Select(item => CreateDefaultElement(item)).ToArray();
-        }
-
-        public static VertexElement CreateDefaultElement(string attribute)
-        {
-            switch (attribute)
-            {
-                case "POSITION": return new VertexElement("POSITION", ElementType.VEC3);
-                case "NORMAL": return new VertexElement("NORMAL", ElementType.VEC3);
-                case "TANGENT": return new VertexElement("TANGENT", ElementType.VEC4);
-
-                case "TEXCOORD_0": return new VertexElement("TEXCOORD_0", ElementType.VEC2);
-                case "TEXCOORD_1": return new VertexElement("TEXCOORD_1", ElementType.VEC2);
-                case "TEXCOORD_2": return new VertexElement("TEXCOORD_2", ElementType.VEC2);
-                case "TEXCOORD_3": return new VertexElement("TEXCOORD_3", ElementType.VEC2);
-
-                case "COLOR_0": return new VertexElement("COLOR_0", ElementType.VEC4, ComponentType.UNSIGNED_BYTE, true);
-
-                case "JOINTS_0": return new VertexElement("JOINTS_0", ElementType.VEC4, ComponentType.UNSIGNED_BYTE);
-                case "WEIGHTS_0": return new VertexElement("WEIGHTS_0", ElementType.VEC4, ComponentType.UNSIGNED_BYTE, true);
-            }
-
-            throw new NotImplementedException();
-        }
-
-        public VertexElement(String attr, ElementType dim, ComponentType enc = ComponentType.FLOAT, Boolean nrm = false)
-        {
-            Attribute = attr;
-            Dimensions = dim;
-            Encoding = enc;
-            Normalized = nrm;
-        }
-
-        #endregion
-
-        #region data
-
-        public String Attribute;
-        public ElementType Dimensions;
-        public ComponentType Encoding;
-        public Boolean Normalized;
-
-        #endregion
-
-        #region API
-
-        public int ByteSize => Dimensions.DimCount() * Encoding.ByteLength();
-
-        public static int GetVertexByteSize(params VertexElement[] elements)
-        {
-            return elements.Sum(item => item.ByteSize);
-        }
-
-        public static int FindDimensions(VertexElement[] elements, string attribute)
-        {
-            var idx = Array.FindIndex(elements, item => item.Attribute == attribute);
-            return idx < 0 ? 0 : elements[idx].Dimensions.DimCount();
-        }
-
-        public static int GetBufferByteSize(int count, params VertexElement[] elements)
-        {
-            var stride = GetVertexByteSize(elements);
-            return stride * count;
-        }
-
-        public static Memory.IEncodedArray<Single> GetScalarColumn(Byte[] data, string attribute, params VertexElement[] elements)
-        {
-            var column = _GetColumn(data, elements, attribute, 0, int.MaxValue);
-            if (column.Item3.Dimensions.DimCount() != 1) throw new ArgumentException(nameof(elements));
-            return new Memory.ScalarArray(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
-        }
-
-        public static Memory.IEncodedArray<Vector2> GetVector2Column(Byte[] data, string attribute, params VertexElement[] elements)
-        {
-            var column = _GetColumn(data, elements, attribute, 0, int.MaxValue);
-            if (column.Item3.Dimensions.DimCount() != 2) throw new ArgumentException(nameof(elements));
-            return new Memory.Vector2Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
-        }
-
-        public static Memory.IEncodedArray<Vector3> GetVector3Column(Byte[] data, string attribute, params VertexElement[] elements)
-        {
-            var column = _GetColumn(data, elements, attribute, 0, int.MaxValue);
-            if (column.Item3.Dimensions.DimCount() != 3) throw new ArgumentException(nameof(elements));
-            return new Memory.Vector3Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
-        }
-
-        public static Memory.IEncodedArray<Vector4> GetVector4Column(Byte[] data, string attribute, params VertexElement[] elements)
-        {
-            var column = _GetColumn(data, elements, attribute, 0, int.MaxValue);
-            if (column.Item3.Dimensions.DimCount() != 4) throw new ArgumentException(nameof(elements));
-            return new Memory.Vector4Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
-        }
-
-        internal static(ArraySegment<Byte>, int, VertexElement) _GetColumn(Byte[] data, VertexElement[] elements, string attribute, int rowStart, int rowCount)
-        {
-            var index = Array.FindIndex(elements, item => item.Attribute == attribute);
-            if (index < 0) throw new ArgumentException(nameof(attribute));
-
-            var element = elements[index];
-
-            var byteStride = GetVertexByteSize(elements);
-            var byteOffset = elements.Take(index).Sum(item => item.ByteSize) + (rowStart * byteStride);
-            var byteLength = data.Length - byteOffset;
-
-            if (rowCount < int.MaxValue) byteLength = rowCount * byteStride;
-
-            var source = new ArraySegment<Byte>(data, byteOffset, byteLength);
-
-            return (source, byteStride, element);
-        }
-
-        #endregion
-    }
-}

+ 9 - 0
src/glTF2Sharp.DOM/Memory/Arrays.cs

@@ -66,6 +66,15 @@ namespace glTF2Sharp.Memory
 
     public static class EncodedArrayUtils
     {
+        public static void CopyFrom<T>(this IEncodedArray<T> dst, int index, params T[] src)
+            where T : unmanaged
+        {
+            for (int i = 0; i < src.Length; ++i)
+            {
+                dst[index + i] = src[i];
+            }
+        }
+
         public static void CopyTo<T>(IEncodedArray<T> src, IEncodedArray<T> dst, int dstOffset = 0)
             where T : unmanaged
         {

+ 3 - 3
src/glTF2Sharp.DOM/Memory/IntegerArrays.cs

@@ -24,14 +24,14 @@ namespace glTF2Sharp.Memory
         public IntegerArray(BYTES data, ENCODING encoding)
             : this(data, 0, int.MaxValue, encoding) { }
 
-        public IntegerArray(BYTES data, int offset, int count, ENCODING encoding)
+        public IntegerArray(BYTES data, int byteOffset, int itemsCount, ENCODING encoding)
         {
-            _Data = data;
+            _Data = data.Slice(byteOffset);
             _ByteStride = encoding.ByteLength();
             this._Setter = null;
             this._Getter = null;
 
-            if (count < this.Count) _Data = _Data.Slice(0, count * _ByteStride);
+            if (itemsCount < this.Count) _Data = _Data.Slice(0, itemsCount * _ByteStride);
 
             switch (encoding)
             {

+ 5 - 5
src/glTF2Sharp.DOM/Schema2/gltf.AccessorSparse.cs

@@ -36,12 +36,12 @@ namespace glTF2Sharp.Schema2
 
         public int Count => _count;
 
-        internal (Memory.IntegerArray, Memory.AttributeAccessor) _CreateMemoryAccessors(Accessor baseAccessor)
+        internal KeyValuePair<Memory.IntegerArray, Geometry.MemoryAccessor> _CreateMemoryAccessors(Accessor baseAccessor)
         {
             var key = this._indices._GetIndicesArray(baseAccessor.LogicalParent, _count);
             var val = this._values._GetMemoryAccessor(baseAccessor.LogicalParent, _count, baseAccessor);
 
-            return (key, val);
+            return new KeyValuePair<Memory.IntegerArray, Geometry.MemoryAccessor>(key, val);
         }
     }
 
@@ -79,11 +79,11 @@ namespace glTF2Sharp.Schema2
             this._byteOffset = byteOffset.AsNullable(_byteOffsetDefault);
         }
 
-        internal Memory.AttributeAccessor _GetMemoryAccessor(ROOT root, int count, Accessor baseAccessor)
+        internal Geometry.MemoryAccessor _GetMemoryAccessor(ROOT root, int count, Accessor baseAccessor)
         {
             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);
+            var info = new Geometry.MemoryAccessInfo(null, this._byteOffset ?? 0, count, view.ByteStride, baseAccessor.Dimensions, baseAccessor.Encoding, baseAccessor.Normalized);
+            return new Geometry.MemoryAccessor(info, view.Data);
         }
     }
 }

+ 28 - 7
src/glTF2Sharp.DOM/Schema2/gltf.Accessors.cs

@@ -69,11 +69,20 @@ namespace glTF2Sharp.Schema2
 
         #region API
 
-        internal Memory.AttributeAccessor _GetMemoryAccessor()
+        internal Geometry.MemoryAccessor _GetMemoryAccessor()
         {
             var view = SourceBufferView;
-            var info = new Memory.AttributeInfo(null, ByteOffset, Count, view.ByteStride, Dimensions, Encoding, Normalized);
-            return new Memory.AttributeAccessor(info, view.Data);
+            var info = new Geometry.MemoryAccessInfo(null, ByteOffset, Count, view.ByteStride, Dimensions, Encoding, Normalized);
+            return new Geometry.MemoryAccessor(info, view.Data);
+        }
+
+        internal KeyValuePair<Memory.IntegerArray, Geometry.MemoryAccessor>? _GetSparseMemoryAccessor()
+        {
+            return this._sparse == null
+                ?
+                (KeyValuePair<Memory.IntegerArray, Geometry.MemoryAccessor>?)null
+                :
+                this._sparse._CreateMemoryAccessors(this);
         }
 
         #endregion
@@ -108,6 +117,12 @@ namespace glTF2Sharp.Schema2
 
         #region Index Buffer API
 
+        public void SetIndexData(Geometry.MemoryAccessor src)
+        {
+            var bv = this.LogicalParent.UseBufferView(src.Data, src.Attribute.ByteStride, BufferMode.ELEMENT_ARRAY_BUFFER);
+            this.SetIndexData(bv, src.Attribute.ByteOffset, src.Attribute.Encoding.ToIndex(), src.Attribute.ItemsCount);
+        }
+
         public void SetIndexData(BufferView buffer, int byteOffset, IndexType encoding, int count)
         {
             Guard.NotNull(buffer, nameof(buffer));
@@ -139,6 +154,12 @@ namespace glTF2Sharp.Schema2
 
         #region Vertex Buffer API
 
+        public void SetVertexData(Geometry.MemoryAccessor src)
+        {
+            var bv = this.LogicalParent.UseBufferView(src.Data, src.Attribute.ByteStride, BufferMode.ARRAY_BUFFER);
+            this.SetVertexData(bv, src.Attribute.ByteOffset, src.Attribute.Dimensions, src.Attribute.Encoding, src.Attribute.Normalized, src.Attribute.ItemsCount);
+        }
+
         public void SetVertexData(BufferView buffer, int byteOffset, ElementType dimensions, ComponentType encoding, Boolean normalized, int count)
         {
             Guard.NotNull(buffer, nameof(buffer));
@@ -166,7 +187,7 @@ namespace glTF2Sharp.Schema2
             if (this._sparse == null) return memory.AsScalarArray();
 
             var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return Memory.AttributeAccessor.CreateScalarSparseArray(memory, sparseKV.Item1, sparseKV.Item2);
+            return Geometry.MemoryAccessor.CreateScalarSparseArray(memory, sparseKV.Key, sparseKV.Value);
         }
 
         public Memory.IEncodedArray<Vector2> AsVector2Array()
@@ -176,7 +197,7 @@ namespace glTF2Sharp.Schema2
             if (this._sparse == null) return memory.AsVector2Array();
 
             var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return Memory.AttributeAccessor.CreateVector2SparseArray(memory, sparseKV.Item1, sparseKV.Item2);
+            return Geometry.MemoryAccessor.CreateVector2SparseArray(memory, sparseKV.Key, sparseKV.Value);
         }
 
         public Memory.IEncodedArray<Vector3> AsVector3Array()
@@ -186,7 +207,7 @@ namespace glTF2Sharp.Schema2
             if (this._sparse == null) return memory.AsVector3Array();
 
             var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return Memory.AttributeAccessor.CreateVector3SparseArray(memory, sparseKV.Item1, sparseKV.Item2);
+            return Geometry.MemoryAccessor.CreateVector3SparseArray(memory, sparseKV.Key, sparseKV.Value);
         }
 
         public Memory.IEncodedArray<Vector4> AsVector4Array()
@@ -196,7 +217,7 @@ namespace glTF2Sharp.Schema2
             if (this._sparse == null) return memory.AsVector4Array();
 
             var sparseKV = this._sparse._CreateMemoryAccessors(this);
-            return Memory.AttributeAccessor.CreateVector4SparseArray(memory, sparseKV.Item1, sparseKV.Item2);
+            return Geometry.MemoryAccessor.CreateVector4SparseArray(memory, sparseKV.Key, sparseKV.Value);
         }
 
         public ArraySegment<Byte> TryGetVertexBytes(int vertexIdx)

+ 35 - 23
src/glTF2Sharp.DOM/Schema2/gltf.Buffer.cs

@@ -14,27 +14,6 @@ namespace glTF2Sharp.Schema2
 
         internal Buffer() { }
 
-        internal Buffer(int byteCount)
-        {
-            Guard.MustBeGreaterThan(byteCount, 0, nameof(byteCount));
-
-            _Data = new byte[byteCount];
-        }
-
-        internal Buffer(IReadOnlyList<Byte> data)
-        {
-            Guard.NotNullOrEmpty(data, nameof(data));
-
-            _Data = data.ToArray();
-        }
-
-        internal Buffer(ReadOnlySpan<Byte> data)
-        {
-            Guard.IsFalse(data.IsEmpty, nameof(data));
-
-            _Data = data.ToArray();
-        }
-
         #endregion
 
         #region non serializable data
@@ -100,9 +79,38 @@ namespace glTF2Sharp.Schema2
 
     public partial class ModelRoot
     {
+        /// <summary>
+        /// Creates a buffer with a given size
+        /// </summary>
+        /// <param name="byteCount">the size of the buffer</param>
+        /// <returns>the buffer</returns>
         public Buffer CreateBuffer(int byteCount)
         {
-            var buffer = new Buffer(byteCount);
+            var buffer = new Buffer();
+            buffer._Data = new byte[byteCount];
+
+            _buffers.Add(buffer);
+
+            return buffer;
+        }
+
+        /// <summary>
+        /// Finds and existing buffer that is already using <paramref name="data"/> , or creates a new one if none is found.
+        /// </summary>
+        /// <param name="data">the byte array to be wrapped as a buffer</param>
+        /// <returns>the buffer</returns>
+        public Buffer UseBuffer(Byte[] data)
+        {
+            Guard.IsFalse(data == null, nameof(data));
+
+            foreach (var b in this.LogicalBuffers)
+            {
+                if (b._Data == data) return b;
+            }
+
+            var buffer = new Buffer();
+            buffer._Data = data;
+
             _buffers.Add(buffer);
 
             return buffer;
@@ -112,12 +120,15 @@ namespace glTF2Sharp.Schema2
         {
             Guard.IsFalse(data.IsEmpty, nameof(data));
 
-            var buffer = new Buffer(data);
+            var buffer = new Buffer();
+            buffer._Data = data.ToArray();
+
             _buffers.Add(buffer);
 
             return buffer;
         }
 
+        [Obsolete("to be removed")]
         public Buffer CreateIndexBuffer(params int[] indices)
         {
             var buffer = CreateBuffer(indices.Length * 4);
@@ -132,6 +143,7 @@ namespace glTF2Sharp.Schema2
             return buffer;
         }
 
+        [Obsolete("to be removed")]
         public Buffer CreateVector3Buffer(params Vector3[] vectors)
         {
             var buffer = CreateBuffer(vectors.Length * 12);

+ 12 - 8
src/glTF2Sharp.DOM/Schema2/gltf.BufferView.cs

@@ -141,18 +141,22 @@ namespace glTF2Sharp.Schema2
             return bv;
         }
 
-        public BufferView CreateIndexBufferView(ReadOnlySpan<Byte> data)
+        public BufferView UseBufferView(ArraySegment<Byte> data, int byteStride = 0, BufferMode? mode = null)
         {
-            var buffer = CreateBuffer(data);
+            var buffer = UseBuffer(data.Array);
 
-            return CreateBufferView(buffer, data.Length, null, null, BufferMode.ELEMENT_ARRAY_BUFFER);
-        }
+            foreach (var bv in this.LogicalBufferViews)
+            {
+                if (bv.Data.Array != data.Array) continue;
+                if (bv.Data.Offset != data.Offset) continue;
+                if (bv.Data.Count != data.Count) continue;
+                if (bv.ByteStride != byteStride) continue;
+                if (bv.DeviceBufferTarget != mode) continue;
 
-        public BufferView CreateVertexBufferView(ReadOnlySpan<Byte> data, int byteStride)
-        {
-            var buffer = CreateBuffer(data);
+                return bv;
+            }
 
-            return CreateBufferView(buffer, data.Length, null, byteStride, BufferMode.ARRAY_BUFFER);
+            return CreateBufferView(buffer, data.Count, data.Offset, byteStride, mode);
         }
     }
 

+ 4 - 27
src/glTF2Sharp.DOM/Schema2/gltf.MeshPrimitive.cs

@@ -57,7 +57,7 @@ namespace glTF2Sharp.Schema2
             set => this._mode = value.AsNullable(_modeDefault);
         }
 
-        public int MorpthTargets => _targets.Count;
+        public int MorpthTargetsCount => _targets.Count;
 
         public BoundingBox3? LocalBounds3 => VertexAccessors["POSITION"]?.LocalBounds3;
 
@@ -108,7 +108,7 @@ namespace glTF2Sharp.Schema2
 
             if (includeMorphs)
             {
-                for (int i = 0; i < MorpthTargets; ++i)
+                for (int i = 0; i < MorpthTargetsCount; ++i)
                 {
                     foreach (var key in attributes)
                     {
@@ -150,19 +150,6 @@ namespace glTF2Sharp.Schema2
             }
         }
 
-        public void SetVertexAccessors(BufferView buffer, int byteOffset, int vertexCount, IEnumerable<Geometry.VertexElement> elements)
-        {
-            int count = 0;
-            foreach (var e in elements)
-            {
-                var accessor = this.LogicalParent.LogicalParent.CreateAccessor(e.Attribute);
-                accessor.SetVertexData(buffer, byteOffset + count, e.Dimensions, e.Encoding, e.Normalized, vertexCount);
-                count += e.ByteSize;
-
-                SetVertexAccessor(e.Attribute, accessor);
-            }
-        }
-
         public IReadOnlyDictionary<String, Accessor> GetMorphTargetAccessors(int idx)
         {
             return new ReadOnlyLinqDictionary<String, int, Accessor>(_targets[idx], alidx => this.LogicalParent.LogicalParent.LogicalAccessors[alidx]);
@@ -199,19 +186,9 @@ namespace glTF2Sharp.Schema2
                 .ToArray();
         }
 
-        public Memory.IEncodedArray<UInt32> GetIndices() => IndexAccessor.CastToIndicesAccessor();
-
-        public Memory.IEncodedArray<Single> GetScalarArray(string attributeKey) => GetVertexAccessor(attributeKey).AsScalarArray();
-
-        public Memory.IEncodedArray<Vector2> GetVector2Array(string attributeKey) => GetVertexAccessor(attributeKey).AsVector2Array();
-
-        public Memory.IEncodedArray<Vector3> GetVector3Array(string attributeKey) => GetVertexAccessor(attributeKey).AsVector3Array();
-
-        public Memory.IEncodedArray<Vector4> GetVector4Array(string attributeKey) => GetVertexAccessor(attributeKey).AsVector4Array();
-
-        public Memory.IEncodedArray<Vector3> GetVertexPositions() => GetVector3Array("POSITION");
+        public Memory.IntegerArray GetIndices() => IndexAccessor.CastToIndicesAccessor();
 
-        public Memory.IEncodedArray<Vector3> GetVertexNormals() => GetVector3Array("NORMAL");
+        public Geometry.MemoryAccessor GetVertices(string attributeKey) => GetVertexAccessor(attributeKey)._GetMemoryAccessor();
 
         #endregion
 

+ 3 - 2
src/glTF2Sharp.DOM/Schema2/gltf.Textures.cs

@@ -189,8 +189,9 @@ namespace glTF2Sharp.Schema2
         {
             if (this._ExternalImageContent == null) return;
 
-            var b = this.LogicalParent.CreateBuffer(this._ExternalImageContent);
-            var bv = this.LogicalParent.CreateBufferView(b, this._ExternalImageContent.Length);
+            var data = new ArraySegment<Byte>(this._ExternalImageContent);
+
+            var bv = this.LogicalParent.UseBufferView(data);
 
             this._uri = null;
             this._bufferView = bv.LogicalIndex;

+ 8 - 0
src/glTF2Sharp.DOM/_Extensions.cs

@@ -219,6 +219,14 @@ namespace glTF2Sharp
             return string.IsNullOrWhiteSpace(name) ? null : name;
         }
 
+        internal static void AddRange<Tin,Tout>(this IList<Tout> dst, IEnumerable<Tin> src, Func<Tin,Tout> cvt)
+        {
+            foreach(var item in src)
+            {
+                dst.Add(cvt(item));
+            }
+        }
+
         #endregion
 
         #region vertex & index accessors

+ 52 - 0
src/glTF2Sharp.Tests/Geometry/CreateMeshTests.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+using NUnit.Framework;
+
+namespace glTF2Sharp.Geometry
+{
+    using Memory;
+
+    [TestFixture]
+    public class CreateMeshTests
+    {
+        [Test]
+        public void CreateMesh1Test()
+        {
+            var positions = new[]
+            {
+                new Vector3(0,1,2),
+                new Vector3(4,1,3),
+                new Vector3(2,3,5),
+                new Vector3(2,4,9),
+                new Vector3(1,3,5),
+            };
+
+            var normals = new[]
+            {
+                Vector3.UnitX,
+                Vector3.UnitX,
+                Vector3.UnitX,
+                Vector3.UnitY,
+                Vector3.UnitZ,
+            };
+
+            var indices = new UInt32[] { 0, 1, 2, 0, 2, 3 };
+
+            var primitive = new MeshPrimitive();
+            primitive.SetVertices(5, "POSITION", "NORMAL");
+            primitive.SetIndices(6);
+
+            primitive.Vertices[0].SetValues(0,positions);
+            primitive.Vertices[1].SetValues(0, normals);
+            primitive.Indices.SetValues(0, indices);            
+
+            CollectionAssert.AreEqual(positions, primitive.Vertices[0].AsVector3Array());
+            CollectionAssert.AreEqual(normals, primitive.Vertices[1].AsVector3Array());
+            CollectionAssert.AreEqual(indices, primitive.Indices.AsIntegerArray());
+
+        }
+    }
+}

+ 34 - 0
src/glTF2Sharp.Tests/Geometry/LoadMeshTests.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using NUnit.Framework;
+
+namespace glTF2Sharp.Geometry
+{
+    [TestFixture]
+    public class LoadMeshTests
+    {
+        #region setup
+
+        [OneTimeSetUp]
+        public void Setup()
+        {
+            TestFiles.CheckoutDataDirectories();
+        }
+
+        #endregion
+
+        [Test]
+        public void LoadModels()
+        {
+            foreach (var f in TestFiles.GetSampleFilePaths())
+            {
+                var root = GltfUtils.LoadModel(f);
+                Assert.NotNull(root);
+
+                var meshes = Mesh.Create(root.LogicalMeshes);                
+            }
+        }
+    }
+}

+ 0 - 136
src/glTF2Sharp.Tests/Geometry/VertexBufferTests.cs

@@ -1,136 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Numerics;
-using System.Text;
-
-using NUnit.Framework;
-
-namespace glTF2Sharp.Geometry
-{
-    [TestFixture]
-    public class VertexBufferTests
-    {
-        [Test]
-        public void CreateVertexBuffer()
-        {
-            var positions = new[]
-            {
-                new Vector3(0,1,2),
-                new Vector3(4,1,3),
-                new Vector3(2,3,5),
-                new Vector3(2,4,9),
-                new Vector3(1,3,5),
-            };
-
-            var normals = new[]
-            {
-                Vector3.UnitX,
-                Vector3.UnitX,
-                Vector3.UnitX,
-                Vector3.UnitY,
-                Vector3.UnitZ,
-            };
-
-            var vb = new VertexArray(5, "POSITION", "NORMAL");
-
-            vb.SetVector3Column("POSITION", 0, positions);
-            vb.SetVector3Column("NORMAL", 0, normals);
-
-            var p2 = vb.GetVector3Column("POSITION");
-            var n2 = vb.GetVector3Column("NORMAL");
-            CollectionAssert.AreEqual(positions, p2);
-            CollectionAssert.AreEqual(normals, n2);
-
-            var vbs = new VertexArraySegment(vb, 1, 2);
-
-            p2 = vbs.GetVector3Column("POSITION");
-            n2 = vbs.GetVector3Column("NORMAL");
-            CollectionAssert.AreEqual(positions.Skip(1).Take(2), p2);
-            CollectionAssert.AreEqual(normals.Skip(1).Take(2), n2);            
-
-            var mdl = new Schema2.ModelRoot();
-            var mdlbv = mdl.CreateVertexBufferView(vb.Data, vb.ByteStride);
-
-            var mdlmesh = mdl.CreateMesh();
-            var mdlprim = mdlmesh.CreatePrimitive();
-            mdlprim.SetVertexAccessors(mdlbv, vbs.ByteOffset, vbs.Count, vbs.Attributes);
-            vbs.CopyTo(mdlprim.VertexAccessors);
-
-            var mdlprim_pos = mdlprim.VertexAccessors["POSITION"].AsVector3Array();
-            var mdlprim_nrm = mdlprim.VertexAccessors["NORMAL"].AsVector3Array();
-
-            CollectionAssert.AreEqual(positions.Skip(1).Take(2), mdlprim_pos);
-            CollectionAssert.AreEqual(normals.Skip(1).Take(2), mdlprim_nrm);
-
-            mdl.AttachToCurrentTest("result.glb");
-            mdl.AttachToCurrentTest("result.gltf");
-        }
-
-        [Test]
-        public void CreateVertexBufferTest()
-        {
-            var vbdecl = new MeshBuffers.VertexDeclaration()
-                .WithVector3("POSITION")
-                .WithVector3("NORMAL");
-
-            Assert.AreEqual(6, vbdecl.Stride);
-
-            var vertex1 = vbdecl.CreateVertex();
-            var vertex2 = vbdecl.CreateVertex();
-            var vertex3 = vbdecl.CreateVertex();
-
-            var vbuffer = new MeshBuffers.TriangleBufferBuilder(vbdecl);
-
-            vertex1.Position = new Vector3(1, 2, 3);
-            vertex1.Normal = Vector3.UnitX;
-            vertex2.Position = new Vector3(4, 2, 3);
-            vertex2.Normal = Vector3.UnitY;
-            vertex3.Position = new Vector3(1, 5, 3);
-            vertex3.Normal = Vector3.UnitZ;
-
-            vbuffer.AddTriangle(vertex1, vertex2, vertex3);
-            vbuffer.AddTriangle(vertex1, vertex2, vertex3);
-
-            var data = EncodeVertexBuffer(vbuffer.Vertices);
-        }
-
-        static VertexBuffer EncodeVertexBuffer(MeshBuffers.VertexBuffer srcBuffer)
-        {
-            var attributes = srcBuffer.Declaration.Attributes.ToArray();
-
-            var dstBuffer = new VertexArray(srcBuffer.Count, attributes);
-
-            foreach (var attr in attributes)
-            {
-                var srcDim = srcBuffer.Declaration.GetDimensions(attr);
-
-                if (srcDim == 1)
-                {
-                    var srcColumn = srcBuffer.GetScalarColumn(attr);
-                    dstBuffer.SetScalarColumn(attr, 0, srcColumn);
-                }
-
-                if (srcDim == 2)
-                {
-                    var srcColumn = srcBuffer.GetVector2Column(attr);
-                    dstBuffer.SetVector2Column(attr, 0, srcColumn);
-                }
-
-                if (srcDim == 3)
-                {
-                    var srcColumn = srcBuffer.GetVector3Column(attr);
-                    dstBuffer.SetVector3Column(attr, 0, srcColumn);
-                }
-
-                if (srcDim == 4)
-                {
-                    var srcColumn = srcBuffer.GetVector4Column(attr);
-                    dstBuffer.SetVector4Column(attr, 0, srcColumn);
-                }
-            }
-
-            return dstBuffer;
-        }
-    }
-}

+ 33 - 0
src/glTF2Sharp.Tests/GltfUtils.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using NUnit.Framework;
+
+namespace glTF2Sharp
+{    
+    using Schema2;
+
+    static class GltfUtils
+    {
+        #region model loading
+
+        public static ModelRoot LoadModel(string filePath)
+        {
+            try
+            {
+                TestContext.Progress.WriteLine($"Loading {filePath.ToShortDisplayPath()}");
+
+                return ModelRoot.Load(filePath);
+            }
+            catch (UnsupportedExtensionException eex)
+            {
+                TestContext.WriteLine($"{filePath} ERROR: {eex.Message}");
+
+                return null;
+            }
+        }
+
+        #endregion
+    }
+}

+ 8 - 28
src/glTF2Sharp.Tests/Schema2/LoadModelTests.cs

@@ -26,7 +26,7 @@ namespace glTF2Sharp.Schema2
         {
             foreach (var f in TestFiles.GetGeneratedFilePaths())
             {
-                var model = _LoadModel(f);
+                var model = GltfUtils.LoadModel(f);
 
                 Assert.NotNull(model);
             }
@@ -39,7 +39,7 @@ namespace glTF2Sharp.Schema2
         {
             var filePath = TestFiles.GetCompatibilityFilePath(idx);
 
-            var model = _LoadModel(filePath);
+            var model = GltfUtils.LoadModel(filePath);
 
             Assert.NotNull(model);
         }
@@ -74,7 +74,7 @@ namespace glTF2Sharp.Schema2
         {
             foreach (var f in TestFiles.GetSampleFilePaths())
             {
-                var root = _LoadModel(f);
+                var root = GltfUtils.LoadModel(f);
                 Assert.NotNull(root);
 
                 // var fileName = System.IO.Path.GetFileNameWithoutExtension(f);
@@ -88,7 +88,7 @@ namespace glTF2Sharp.Schema2
         {
             foreach (var f in TestFiles.GetFilePathsWithSpecularGlossinessPBR())
             {
-                var root = _LoadModel(f);
+                var root = GltfUtils.LoadModel(f);
                 Assert.NotNull(root);
             }
         }
@@ -100,7 +100,7 @@ namespace glTF2Sharp.Schema2
         [Test(Description ="Example of traversing the visual tree all the way to individual vertices and indices")]
         public void TestLoadPolly()
         {
-            var model = _LoadModel(TestFiles.GetPollyFilePath());            
+            var model = GltfUtils.LoadModel(TestFiles.GetPollyFilePath());            
 
             var scene = model.DefaultScene;
 
@@ -109,8 +109,8 @@ namespace glTF2Sharp.Schema2
             var pollyPrimitive = pollyNode.Mesh.Primitives[0];
 
             var pollyIndices = pollyPrimitive.GetIndices();
-            var pollyPositions = pollyPrimitive.GetVertexPositions();
-            var pollyNormals = pollyPrimitive.GetVertexNormals();
+            var pollyPositions = pollyPrimitive.GetVertices("POSITION").AsVector3Array();
+            var pollyNormals = pollyPrimitive.GetVertices("NORMAL").AsVector3Array();
 
             for (int i=0; i < pollyIndices.Count; i+=3)
             {
@@ -130,26 +130,6 @@ namespace glTF2Sharp.Schema2
             }
         }
 
-        #endregion
-
-        #region model loading
-
-        public static ModelRoot _LoadModel(string filePath)
-        {
-            try
-            {
-                TestContext.Progress.WriteLine($"Loading {filePath.ToShortDisplayPath()}");
-
-                return ModelRoot.Load(filePath);
-            }
-            catch(UnsupportedExtensionException eex)            
-            {
-                TestContext.WriteLine($"{filePath} ERROR: {eex.Message}");
-
-                return null;
-            }
-        }
-
-        #endregion
+        #endregion        
     }
 }