2
0
Эх сурвалжийг харах

more API refactoring; trying to streamline the process of creating meshes.

Vicente Penades 6 жил өмнө
parent
commit
c672e5d3d7

+ 2 - 2
src/SharpGLTF.DOM/Geometry/MeshPrimitive.cs

@@ -123,7 +123,7 @@ namespace SharpGLTF.Geometry
         public void AssignTo(Schema2.MeshPrimitive dstPrim)
         {
             var dstAccessor = dstPrim.LogicalParent.LogicalParent.CreateAccessor(this.Name);
-            dstAccessor.SetVertexData(_MemoryAccessor);
+            dstAccessor.WithVertexData(_MemoryAccessor);
             dstPrim.SetVertexAccessor(this._MemoryAccessor.Attribute.Name, dstAccessor);
         }
 
@@ -184,7 +184,7 @@ namespace SharpGLTF.Geometry
         public void AssignToSchema(Schema2.MeshPrimitive dstPrim)
         {
             var dstAccessor = dstPrim.LogicalParent.LogicalParent.CreateAccessor(this.Name);
-            dstAccessor.SetIndexData(_MemoryAccessor);
+            dstAccessor.WithIndexData(_MemoryAccessor);
             dstPrim.IndexAccessor = dstAccessor;
         }
 

+ 12 - 3
src/SharpGLTF.DOM/Memory/Arrays.cs

@@ -69,9 +69,7 @@ namespace SharpGLTF.Memory
     {
         public static IntegerArray IndicesRange(int start, int count)
         {
-            var data = new Byte[count * 4];
-
-            var array = new IntegerArray(data, Schema2.IndexType.UNSIGNED_INT);
+            var array = new IntegerArray( new ArraySegment<Byte>(new Byte[count * 4]), Schema2.IndexType.UNSIGNED_INT);
 
             for (int i = 0; i < count; ++i)
             {
@@ -81,6 +79,17 @@ namespace SharpGLTF.Memory
             return array;
         }
 
+        public static void FillFrom(this IEncodedArray<UInt32> dst, int dstIndex, IEnumerable<Int32> src)
+        {
+            using (var ator = src.GetEnumerator())
+            {
+                while (dstIndex < dst.Count && ator.MoveNext())
+                {
+                    dst[dstIndex++] = (UInt32)ator.Current;
+                }
+            }
+        }
+
         public static void FillFrom<T>(this IEncodedArray<T> dst, int dstIndex, IEnumerable<T> src)
             where T : unmanaged
         {

+ 96 - 116
src/SharpGLTF.DOM/Memory/FloatingArrays.cs

@@ -20,18 +20,17 @@ namespace SharpGLTF.Memory
 
         public FloatingAccessor(BYTES data, int byteOffset, int itemsCount, int byteStride, int dimensions, ENCODING encoding, Boolean normalized)
         {
+            var enclen = encoding.ByteLength();
+
             this._Data = data.Slice(byteOffset);
-            this._ItemCount = 0;
             this._Getter = null;
             this._Setter = null;
+            this._ByteStride = Math.Max(byteStride, enclen * dimensions);
+            this._EncodedLen = enclen;
+            this._ItemCount = this._Data.Count / this._ByteStride;
 
-            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;
+            // strided buffers usually have room for an extra item
+            if ((_Data.Count % _ByteStride) >= enclen * dimensions) ++_ItemCount;
 
             _ItemCount = Math.Min(itemsCount, _ItemCount);
 
@@ -39,88 +38,87 @@ namespace SharpGLTF.Memory
             {
                 this._Setter = this._SetValue<Single>;
                 this._Getter = this._GetValue<Single>;
+                return;
             }
-            else
+
+            if (normalized)
             {
-                if (normalized)
+                switch (encoding)
                 {
-                    switch (encoding)
-                    {
-                        case ENCODING.BYTE:
-                            {
-                                this._Setter = this._SetNormalizedS8;
-                                this._Getter = this._GetNormalizedS8;
-                                break;
-                            }
-
-                        case ENCODING.UNSIGNED_BYTE:
-                            {
-                                this._Setter = this._SetNormalizedU8;
-                                this._Getter = this._GetNormalizedU8;
-                                break;
-                            }
-
-                        case ENCODING.SHORT:
-                            {
-                                this._Setter = this._SetNormalizedS16;
-                                this._Getter = this._GetNormalizedS16;
-                                break;
-                            }
-
-                        case ENCODING.UNSIGNED_SHORT:
-                            {
-                                this._Setter = this._SetNormalizedU16;
-                                this._Getter = this._GetNormalizedU16;
-                                break;
-                            }
-
-                        default: throw new ArgumentException(nameof(encoding));
-                    }
+                    case ENCODING.BYTE:
+                        {
+                            this._Setter = this._SetNormalizedS8;
+                            this._Getter = this._GetNormalizedS8;
+                            break;
+                        }
+
+                    case ENCODING.UNSIGNED_BYTE:
+                        {
+                            this._Setter = this._SetNormalizedU8;
+                            this._Getter = this._GetNormalizedU8;
+                            break;
+                        }
+
+                    case ENCODING.SHORT:
+                        {
+                            this._Setter = this._SetNormalizedS16;
+                            this._Getter = this._GetNormalizedS16;
+                            break;
+                        }
+
+                    case ENCODING.UNSIGNED_SHORT:
+                        {
+                            this._Setter = this._SetNormalizedU16;
+                            this._Getter = this._GetNormalizedU16;
+                            break;
+                        }
+
+                    default: throw new ArgumentException(nameof(encoding));
                 }
-                else
+            }
+            else
+            {
+                switch (encoding)
                 {
-                    switch (encoding)
-                    {
-                        case ENCODING.BYTE:
-                            {
-                                this._Setter = this._SetValueS8;
-                                this._Getter = this._GetValueS8;
-                                break;
-                            }
-
-                        case ENCODING.UNSIGNED_BYTE:
-                            {
-                                this._Setter = this._SetValueU8;
-                                this._Getter = this._GetValueU8;
-                                break;
-                            }
-
-                        case ENCODING.SHORT:
-                            {
-                                this._Setter = this._SetValueS16;
-                                this._Getter = this._GetValueS16;
-                                break;
-                            }
-
-                        case ENCODING.UNSIGNED_SHORT:
-                            {
-                                this._Setter = this._SetValueU16;
-                                this._Getter = this._GetValueU16;
-                                break;
-                            }
-
-                        case ENCODING.UNSIGNED_INT:
-                            {
-                                this._Setter = this._SetValueU32;
-                                this._Getter = this._GetValueU32;
-                                break;
-                            }
-
-                        case ENCODING.FLOAT:
+                    case ENCODING.BYTE:
+                        {
+                            this._Setter = this._SetValueS8;
+                            this._Getter = this._GetValueS8;
                             break;
+                        }
 
-                        default: throw new ArgumentException(nameof(encoding));
-                    }
+                    case ENCODING.UNSIGNED_BYTE:
+                        {
+                            this._Setter = this._SetValueU8;
+                            this._Getter = this._GetValueU8;
+                            break;
+                        }
+
+                    case ENCODING.SHORT:
+                        {
+                            this._Setter = this._SetValueS16;
+                            this._Getter = this._GetValueS16;
+                            break;
+                        }
+
+                    case ENCODING.UNSIGNED_SHORT:
+                        {
+                            this._Setter = this._SetValueU16;
+                            this._Getter = this._GetValueU16;
+                            break;
+                        }
+
+                    case ENCODING.UNSIGNED_INT:
+                        {
+                            this._Setter = this._SetValueU32;
+                            this._Getter = this._GetValueU32;
+                            break;
+                        }
+
+                    case ENCODING.FLOAT:
+                        break;
+
+                    default: throw new ArgumentException(nameof(encoding));
                 }
             }
         }
@@ -179,7 +177,7 @@ namespace SharpGLTF.Memory
         private readonly BYTES _Data;
 
         private readonly int _ByteStride;
-        private readonly int _ValueStride;
+        private readonly int _EncodedLen;
 
         private readonly int _ItemCount;
 
@@ -202,8 +200,8 @@ namespace SharpGLTF.Memory
 
         public Single this[int rowIndex, int subIndex]
         {
-            get => _Getter((rowIndex * _ByteStride) + (subIndex * _ValueStride));
-            set => _Setter((rowIndex * _ByteStride) + (subIndex * _ValueStride), value);
+            get => _Getter((rowIndex * _ByteStride) + (subIndex * _EncodedLen));
+            set => _Setter((rowIndex * _ByteStride) + (subIndex * _EncodedLen), value);
         }
 
         #endregion
@@ -217,15 +215,12 @@ namespace SharpGLTF.Memory
     {
         #region constructors
 
-        public ScalarArray(Byte[] data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
-            : this(new BYTES(data), 0, int.MaxValue, byteStride, encoding, normalized) { }
-
         public ScalarArray(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
 
-        public ScalarArray(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
+        public ScalarArray(BYTES data, int byteOffset, int itemsCount, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 1, encoding, normalized);
+            _Accesor = new FloatingAccessor(data, byteOffset, itemsCount, byteStride, 1, encoding, normalized);
         }
 
         #endregion
@@ -272,15 +267,12 @@ namespace SharpGLTF.Memory
     {
         #region constructors
 
-        public Vector2Array(Byte[] data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
-            : this(new BYTES(data), 0, int.MaxValue, byteStride, encoding, normalized) { }
-
         public Vector2Array(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
 
-        public Vector2Array(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
+        public Vector2Array(BYTES data, int byteOffset, int itemsCount, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 2, encoding, normalized);
+            _Accesor = new FloatingAccessor(data, byteOffset, itemsCount, byteStride, 2, encoding, normalized);
         }
 
         #endregion
@@ -335,15 +327,12 @@ namespace SharpGLTF.Memory
     {
         #region constructors
 
-        public Vector3Array(Byte[] data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
-            : this(new BYTES(data), 0, int.MaxValue, byteStride, encoding, normalized) { }
-
         public Vector3Array(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
 
-        public Vector3Array(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
+        public Vector3Array(BYTES data, int byteOffset, int itemsCount, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 3, encoding, normalized);
+            _Accesor = new FloatingAccessor(data, byteOffset, itemsCount, byteStride, 3, encoding, normalized);
         }
 
         #endregion
@@ -399,15 +388,12 @@ namespace SharpGLTF.Memory
     {
         #region constructors
 
-        public Vector4Array(Byte[] data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
-            : this(new BYTES(data), 0, int.MaxValue, byteStride, encoding, normalized) { }
-
         public Vector4Array(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
 
-        public Vector4Array(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
+        public Vector4Array(BYTES data, int byteOffset, int itemsCount, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 4, encoding, normalized);
+            _Accesor = new FloatingAccessor(data, byteOffset, itemsCount, byteStride, 4, encoding, normalized);
         }
 
         #endregion
@@ -464,15 +450,12 @@ namespace SharpGLTF.Memory
     {
         #region constructors
 
-        public QuaternionArray(Byte[] data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
-            : this(new BYTES(data), 0, int.MaxValue, byteStride, encoding, normalized) { }
-
         public QuaternionArray(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
 
-        public QuaternionArray(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
+        public QuaternionArray(BYTES data, int byteOffset, int itemsCount, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 4, encoding, normalized);
+            _Accesor = new FloatingAccessor(data, byteOffset, itemsCount, byteStride, 4, encoding, normalized);
         }
 
         #endregion
@@ -529,15 +512,12 @@ namespace SharpGLTF.Memory
     {
         #region constructors
 
-        public Matrix4x4Array(Byte[] data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
-            : this(new BYTES(data), 0, int.MaxValue, byteStride, encoding, normalized) { }
-
         public Matrix4x4Array(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
 
-        public Matrix4x4Array(BYTES data, int byteOffset, int count, int byteStride, ENCODING encoding, Boolean normalized)
+        public Matrix4x4Array(BYTES data, int byteOffset, int itemsCount, int byteStride, ENCODING encoding, Boolean normalized)
         {
-            _Accesor = new FloatingAccessor(data, byteOffset, count, byteStride, 16, encoding, normalized);
+            _Accesor = new FloatingAccessor(data, byteOffset, itemsCount, byteStride, 16, encoding, normalized);
         }
 
         #endregion

+ 0 - 3
src/SharpGLTF.DOM/Memory/IntegerArrays.cs

@@ -18,9 +18,6 @@ namespace SharpGLTF.Memory
     {
         #region constructors
 
-        public IntegerArray(Byte[] data, ENCODING encoding = ENCODING.UNSIGNED_INT)
-            : this(new BYTES(data), encoding) { }
-
         public IntegerArray(BYTES data, ENCODING encoding = ENCODING.UNSIGNED_INT)
             : this(data, 0, int.MaxValue, encoding) { }
 

+ 39 - 10
src/SharpGLTF.DOM/Schema2/gltf.Accessors.cs

@@ -119,30 +119,48 @@ namespace SharpGLTF.Schema2
 
         #region Index Buffer API
 
-        public void SetIndexData(Geometry.MemoryAccessor src)
+        public Accessor WithIndexData(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);
+            this.WithIndexData(bv, src.Attribute.ByteOffset, src.Attribute.ItemsCount, src.Attribute.Encoding.ToIndex());
+
+            return this;
         }
 
-        public void SetIndexData(BufferView buffer, int byteOffset, IndexType encoding, int count)
+        public Accessor WithIndexData(BufferView buffer, int byteOffset, IReadOnlyList<Int32> items, IndexType encoding = IndexType.UNSIGNED_INT)
+        {
+            var array = new Memory.IntegerArray(buffer.Content, byteOffset, items.Count, encoding);
+            Memory.EncodedArrayUtils.FillFrom(array, 0, items);
+            return WithIndexData(buffer, byteOffset, items.Count, encoding);
+        }
+
+        public Accessor WithIndexData(BufferView buffer, int byteOffset, IReadOnlyList<UInt32> items, IndexType encoding = IndexType.UNSIGNED_INT)
+        {
+            var array = new Memory.IntegerArray(buffer.Content, byteOffset, items.Count, encoding);
+            Memory.EncodedArrayUtils.FillFrom(array, 0, items);
+            return WithIndexData(buffer, byteOffset, items.Count, encoding);
+        }
+
+        public Accessor WithIndexData(BufferView buffer, int byteOffset, int itemCount, IndexType encoding)
         {
             Guard.NotNull(buffer, nameof(buffer));
             Guard.MustShareLogicalParent(this, buffer, nameof(buffer));
             if (buffer.DeviceBufferTarget.HasValue) Guard.IsTrue(buffer.DeviceBufferTarget.Value == BufferMode.ELEMENT_ARRAY_BUFFER, nameof(buffer));
 
             Guard.MustBeGreaterThanOrEqualTo(byteOffset, 0, nameof(byteOffset));
-            Guard.MustBeGreaterThan(count, 0, nameof(count));
+            Guard.MustBeGreaterThan(itemCount, 0, nameof(itemCount));
 
             this._bufferView = buffer.LogicalIndex;
             this._byteOffset = byteOffset;
-            this._count = count;
+            this._count = itemCount;
 
             this._type = ElementType.SCALAR;
             this._componentType = encoding.ToComponent();
             this._normalized = null;
 
             UpdateBounds();
+
+            return this;
         }
 
         public Memory.IntegerArray AsIndicesArray()
@@ -157,30 +175,41 @@ namespace SharpGLTF.Schema2
 
         #region Vertex Buffer API
 
-        public void SetVertexData(Geometry.MemoryAccessor src)
+        public Accessor WithVertexData(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);
+            this.WithVertexData(bv, src.Attribute.ByteOffset, src.Attribute.ItemsCount, src.Attribute.Dimensions, src.Attribute.Encoding, src.Attribute.Normalized);
+
+            return this;
+        }
+
+        public Accessor WithVertexData(BufferView buffer, int byteOffset, IReadOnlyList<Vector3> items, ComponentType encoding = ComponentType.FLOAT, Boolean normalized = false)
+        {
+            var array = new Memory.Vector3Array(buffer.Content.Slice(byteOffset), buffer.ByteStride);
+            Memory.EncodedArrayUtils.FillFrom(array, 0, items);
+            return WithVertexData(buffer, byteOffset, items.Count, ElementType.VEC3, encoding, normalized);
         }
 
-        public void SetVertexData(BufferView buffer, int byteOffset, ElementType dimensions, ComponentType encoding, Boolean normalized, int count)
+        public Accessor WithVertexData(BufferView buffer, int byteOffset, int itemCount, ElementType dimensions = ElementType.VEC3, ComponentType encoding = ComponentType.FLOAT, Boolean normalized = false)
         {
             Guard.NotNull(buffer, nameof(buffer));
             Guard.MustShareLogicalParent(this, buffer, nameof(buffer));
             if (buffer.DeviceBufferTarget.HasValue) Guard.IsTrue(buffer.DeviceBufferTarget.Value == BufferMode.ARRAY_BUFFER, nameof(buffer));
 
             Guard.MustBeGreaterThanOrEqualTo(byteOffset, 0, nameof(byteOffset));
-            Guard.MustBeGreaterThan(count, 0, nameof(count));
+            Guard.MustBeGreaterThan(itemCount, 0, nameof(itemCount));
 
             this._bufferView = buffer.LogicalIndex;
             this._byteOffset = byteOffset;
-            this._count = count;
+            this._count = itemCount;
 
             this._type = dimensions;
             this._componentType = encoding;
             this._normalized = normalized.AsNullable(false);
 
             UpdateBounds();
+
+            return this;
         }
 
         public Memory.IEncodedArray<Single> AsScalarArray()

+ 7 - 19
src/SharpGLTF.DOM/Schema2/gltf.Buffer.cs

@@ -82,7 +82,7 @@ namespace SharpGLTF.Schema2
     public partial class ModelRoot
     {
         /// <summary>
-        /// Creates a buffer with a given size
+        /// Creates a buffer with <paramref name="byteCount"/> size.
         /// </summary>
         /// <param name="byteCount">the size of the buffer</param>
         /// <returns>the buffer</returns>
@@ -97,33 +97,21 @@ namespace SharpGLTF.Schema2
         }
 
         /// <summary>
-        /// Finds and existing buffer that is already using <paramref name="data"/> , or creates a new one if none is found.
+        /// Finds and existing buffer that is already using <paramref name="content"/> , or creates a new one if none is found.
         /// </summary>
-        /// <param name="data">the byte array to be wrapped as a buffer</param>
+        /// <param name="content">the byte array to be wrapped as a buffer</param>
         /// <returns>the buffer</returns>
-        public Buffer UseBuffer(Byte[] data)
+        public Buffer UseBuffer(Byte[] content)
         {
-            Guard.IsFalse(data == null, nameof(data));
+            Guard.IsFalse(content == null, nameof(content));
 
             foreach (var b in this.LogicalBuffers)
             {
-                if (b._Content == data) return b;
+                if (b._Content == content) return b;
             }
 
             var buffer = new Buffer();
-            buffer._Content = data;
-
-            _buffers.Add(buffer);
-
-            return buffer;
-        }
-
-        public Buffer CreateBuffer(ReadOnlySpan<Byte> data)
-        {
-            Guard.IsFalse(data.IsEmpty, nameof(data));
-
-            var buffer = new Buffer();
-            buffer._Content = data.ToArray();
+            buffer._Content = content;
 
             _buffers.Add(buffer);
 

+ 32 - 25
src/SharpGLTF.DOM/Schema2/gltf.BufferView.cs

@@ -17,35 +17,35 @@ namespace SharpGLTF.Schema2
 
         internal BufferView() { }
 
-        internal BufferView(Buffer buffer, int? byteLength, int? byteOffset, int? byteStride, BufferMode? target)
+        internal BufferView(Buffer buffer, int byteOffset, int? byteLength, int byteStride, BufferMode? target)
         {
             Guard.NotNull(buffer, nameof(buffer));
             Guard.NotNull(buffer._Content, nameof(buffer));
             Guard.NotNull(buffer.LogicalParent, nameof(buffer));
 
-            byteLength = byteLength.AsValue(buffer._Content.Length - byteOffset.AsValue(0));
+            byteLength = byteLength.AsValue(buffer._Content.Length - byteOffset);
 
             Guard.MustBeGreaterThanOrEqualTo(byteLength.AsValue(0), _byteLengthMinimum, nameof(byteLength));
-            Guard.MustBeGreaterThanOrEqualTo(byteOffset.AsValue(0), _byteOffsetMinimum, nameof(byteOffset));
+            Guard.MustBeGreaterThanOrEqualTo(byteOffset, _byteOffsetMinimum, nameof(byteOffset));
 
             if (target == BufferMode.ELEMENT_ARRAY_BUFFER)
             {
-                Guard.IsTrue(byteStride.AsValue(0) == 0, nameof(byteStride));
+                Guard.IsTrue(byteStride == 0, nameof(byteStride));
             }
-            else if (byteStride.AsValue(0) > 0)
+            else if (byteStride > 0)
             {
                 // TODO: clarify under which conditions bytestride needs to be defined or forbidden.
 
-                Guard.IsTrue(byteStride.AsValue(0).IsMultipleOf(4), nameof(byteStride));
-                Guard.MustBeBetweenOrEqualTo(byteStride.AsValue(0), _byteStrideMinimum, _byteStrideMaximum, nameof(byteStride));
+                Guard.IsTrue(byteStride.IsMultipleOf(4), nameof(byteStride));
+                Guard.MustBeBetweenOrEqualTo(byteStride, _byteStrideMinimum, _byteStrideMaximum, nameof(byteStride));
             }
 
             this._buffer = buffer.LogicalIndex;
 
             this._byteLength = byteLength.AsValue(buffer._Content.Length);
 
-            this._byteOffset = byteOffset.AsValue(0).AsNullable(0);
-            this._byteStride = byteStride.AsValue(0).AsNullable(0);
+            this._byteOffset = byteOffset.AsNullable(_byteOffsetDefault, _byteOffsetMinimum, int.MaxValue);
+            this._byteStride = byteStride.AsNullable(0, _byteStrideMinimum, _byteStrideMaximum);
 
             this._target = target;
         }
@@ -65,7 +65,7 @@ namespace SharpGLTF.Schema2
             get
             {
                 var buffer = this.LogicalParent.LogicalBuffers[this._buffer];
-                return new BYTES(buffer._Content, this._byteOffset ?? 0, this._byteLength);
+                return new BYTES(buffer._Content, this._byteOffset.AsValue(0), this._byteLength);
             }
         }
 
@@ -129,34 +129,41 @@ namespace SharpGLTF.Schema2
 
     public partial class ModelRoot
     {
-        public BufferView CreateBufferView(Buffer buffer, int? byteLength = null, int? byteOffset = null, int? byteStride = null, BufferMode? target = null)
+        public BufferView UseBufferView(ArraySegment<Byte> data, int byteStride = 0, BufferMode? target = null)
         {
-            Guard.NotNull(buffer, nameof(buffer));
-            Guard.MustShareLogicalParent(this, buffer, nameof(buffer));
-
-            var bv = new BufferView(buffer, byteLength, byteOffset, byteStride, target);
-
-            this._bufferViews.Add(bv);
+            Guard.NotNull(data.Array, nameof(data));
+            return UseBufferView(data.Array, data.Offset, data.Count, byteStride, target);
+        }
 
-            return bv;
+        public BufferView UseBufferView(Byte[] buffer, int byteOffset = 0, int? byteLength = null, int byteStride = 0, BufferMode? target = null)
+        {
+            Guard.NotNull(buffer, nameof(buffer));
+            return UseBufferView(UseBuffer(buffer), byteOffset, byteLength, byteStride, target);
         }
 
-        public BufferView UseBufferView(ArraySegment<Byte> data, int byteStride = 0, BufferMode? mode = null)
+        public BufferView UseBufferView(Buffer buffer, int byteOffset = 0, int? byteLength = null, int byteStride = 0, BufferMode? target = null)
         {
-            var buffer = UseBuffer(data.Array);
+            Guard.NotNull(buffer, nameof(buffer));
+            Guard.MustShareLogicalParent(this, buffer, nameof(buffer));
+
+            byteLength = byteLength.AsValue(buffer.Content.Length - byteOffset);
 
             foreach (var bv in this.LogicalBufferViews)
             {
-                if (bv.Content.Array != data.Array) continue;
-                if (bv.Content.Offset != data.Offset) continue;
-                if (bv.Content.Count != data.Count) continue;
+                if (bv.Content.Array != buffer.Content) continue;
+                if (bv.Content.Offset != byteOffset) continue;
+                if (bv.Content.Count != byteLength.Value) continue;
                 if (bv.ByteStride != byteStride) continue;
-                if (bv.DeviceBufferTarget != mode) continue;
+                if (bv.DeviceBufferTarget != target) continue;
 
                 return bv;
             }
 
-            return CreateBufferView(buffer, data.Count, data.Offset, byteStride, mode);
+            var newbv = new BufferView(buffer, byteOffset, byteLength, byteStride, target);
+
+            this._bufferViews.Add(newbv);
+
+            return newbv;
         }
     }
 

+ 1 - 1
src/SharpGLTF.DOM/Schema2/gltf.Mesh.cs

@@ -25,7 +25,7 @@ namespace SharpGLTF.Schema2
 
         public int LogicalIndex => this.LogicalParent.LogicalMeshes.IndexOfReference(this);
 
-        public IEnumerable<Node> VisualParents => Node.GetNodesUsingMesh(this);
+        public IEnumerable<Node> VisualParents => Node.FindNodesUsingMesh(this);
 
         public IReadOnlyList<MeshPrimitive> Primitives => _primitives;
 

+ 5 - 0
src/SharpGLTF.DOM/Schema2/gltf.MeshPrimitive.cs

@@ -151,6 +151,11 @@ namespace SharpGLTF.Schema2
             }
         }
 
+        public void SetIndexAccessor(Accessor accessor)
+        {
+            _indices = accessor == null ? (int?)null : accessor.LogicalIndex;
+        }
+
         public IReadOnlyDictionary<String, Accessor> GetMorphTargetAccessors(int idx)
         {
             return new ReadOnlyLinqDictionary<String, int, Accessor>(_targets[idx], alidx => this.LogicalParent.LogicalParent.LogicalAccessors[alidx]);

+ 16 - 1
src/SharpGLTF.DOM/Schema2/gltf.Root.cs

@@ -78,7 +78,22 @@ namespace SharpGLTF.Schema2
 
         #region Visual Tree
 
-        public Scene DefaultScene => _scenes.Count == 0 ? null : _scenes[_scene ?? 0];
+        public Scene DefaultScene
+        {
+            get => _scenes.Count == 0 ? null : _scenes[_scene.AsValue(0)];
+            set
+            {
+                if (value == null)
+                {
+                    _scene = null;
+                    return;
+                }
+
+                Guard.MustShareLogicalParent(this, value, nameof(value));
+
+                _scene = value.LogicalIndex;
+            }
+        }
 
         #endregion
 

+ 17 - 36
src/SharpGLTF.DOM/Schema2/gltf.Scene.cs

@@ -12,9 +12,9 @@ namespace SharpGLTF.Schema2
     {
         IEnumerable<Node> VisualChildren { get; }
 
-        Node AddVisualNode(string name);
+        Node CreateNode(string name);
 
-        Node FindVisualNode(string name);
+        Node FindNode(string name);
     }
 
     [System.Diagnostics.DebuggerDisplay("Node[{LogicalIndex}] {Name} SkinJoint:{IsSkinJoint} T:{LocalTransform.Translation.X} {LocalTransform.Translation.Y} {LocalTransform.Translation.Z}")]
@@ -34,7 +34,7 @@ namespace SharpGLTF.Schema2
 
         public int LogicalIndex => this.LogicalParent.LogicalNodes.IndexOfReference(this);
 
-        public Node VisualParent => this.LogicalParent._GetVisualParentNode(this);
+        public Node VisualParent => this.LogicalParent._FindVisualParentNode(this);
 
         public IEnumerable<Node> VisualChildren => GetVisualChildren();
 
@@ -160,24 +160,14 @@ namespace SharpGLTF.Schema2
             return allChildren;
         }
 
-        public Node AddVisualNode(string name)
+        public Node CreateNode(string name)
         {
-            var node = this.LogicalParent._AddLogicalNode(this._children);
+            var node = this.LogicalParent._CreateLogicalNode(this._children);
             node.Name = name;
             return node;
         }
 
-        public void AddNode(Node node)
-        {
-            Guard.NotNull(node, nameof(node));
-            Guard.MustShareLogicalParent(this, node, nameof(node));
-
-            var idx = this.LogicalParent._UseLogicaNode(node);
-
-            this._children.Add(idx);
-        }
-
-        public Node FindVisualNode(string name)
+        public Node FindNode(string name)
         {
             return this.VisualChildren.FirstOrDefault(item => item.Name == name);
         }
@@ -197,7 +187,7 @@ namespace SharpGLTF.Schema2
             }
         }
 
-        public static IEnumerable<Node> GetNodesUsingMesh(Mesh mesh)
+        public static IEnumerable<Node> FindNodesUsingMesh(Mesh mesh)
         {
             if (mesh == null) return Enumerable.Empty<Node>();
 
@@ -205,10 +195,10 @@ namespace SharpGLTF.Schema2
 
             return mesh.LogicalParent
                 .LogicalNodes
-                .Where(item => item._mesh.HasValue && item._mesh.Value == meshIdx);
+                .Where(item => item._mesh.AsValue(int.MinValue) == meshIdx);
         }
 
-        public static IEnumerable<Node> GetNodesUsingSkin(Skin skin)
+        public static IEnumerable<Node> FindNodesUsingSkin(Skin skin)
         {
             if (skin == null) return Enumerable.Empty<Node>();
 
@@ -216,7 +206,7 @@ namespace SharpGLTF.Schema2
 
             return skin.LogicalParent
                 .LogicalNodes
-                .Where(item => item._skin.HasValue && item._skin.Value == meshIdx);
+                .Where(item => item._skin.AsValue(int.MinValue) == meshIdx);
         }
 
         public override IEnumerable<Exception> Validate()
@@ -294,19 +284,12 @@ namespace SharpGLTF.Schema2
             return VisualChildren.Any(item => item._ContainsVisualNode(node, true));
         }
 
-        public Node AddVisualNode(String name)
-        {
-            return this.LogicalParent._AddLogicalNode(this._nodes);
-        }
-
-        public void AddVisualNode(Node node)
+        public Node CreateNode(String name)
         {
-            var idx = this.LogicalParent._UseLogicaNode(node);
-
-            this._nodes.Add(idx);
+            return this.LogicalParent._CreateLogicalNode(this._nodes);
         }
 
-        public Node FindVisualNode(String name)
+        public Node FindNode(String name)
         {
             return this.VisualChildren.FirstOrDefault(item => item.Name == name);
         }
@@ -360,7 +343,7 @@ namespace SharpGLTF.Schema2
             return scene;
         }
 
-        internal Node _GetVisualParentNode(Node childNode)
+        internal Node _FindVisualParentNode(Node childNode)
         {
             var childIdx = _nodes.IndexOf(childNode);
             if (childIdx < 0) return null;
@@ -369,22 +352,20 @@ namespace SharpGLTF.Schema2
             return _nodes.FirstOrDefault(item => item._HasVisualChild(childIdx));
         }
 
-        internal Node _AddLogicalNode()
+        internal Node _CreateLogicalNode()
         {
             var n = new Node();
             _nodes.Add(n);
             return n;
         }
 
-        internal Node _AddLogicalNode(IList<int> children)
+        internal Node _CreateLogicalNode(IList<int> children)
         {
-            var n = _AddLogicalNode();
+            var n = _CreateLogicalNode();
             children.Add(n.LogicalIndex);
             return n;
         }
 
-        internal int _UseLogicaNode(Node node) { return _nodes.Use(node); }
-
         internal Boolean _CheckNodeIsJoint(Node n)
         {
             var idx = n.LogicalIndex;

+ 1 - 1
src/SharpGLTF.DOM/Schema2/gltf.Skin.cs

@@ -34,7 +34,7 @@ namespace SharpGLTF.Schema2
 
         public int LogicalIndex => this.LogicalParent.LogicalSkins.IndexOfReference(this);
 
-        public IEnumerable<Node> VisualParents => Node.GetNodesUsingSkin(this);
+        public IEnumerable<Node> VisualParents => Node.FindNodesUsingSkin(this);
 
         public int JointsCount => _joints.Count;
 

+ 142 - 0
tests/SharpGLTF.Tests/DumpAssemblyAPI.cs

@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace SharpGLTF
+{
+    /// <summary>
+    /// Utility class to hopefully compare breaking changes between APIs
+    /// </summary>
+    static class DumpAssemblyAPI
+    {
+        // https://www.hanselman.com/blog/ManagingChangeWithNETAssemblyDiffTools.aspx
+
+        // proposed reporting lines:
+
+        // Namespace="" Class="" Method=""
+        // Namespace="" Enum="" Constant=""
+        // Namespace="" Class="" Property=""
+        // Namespace="" Class="" Event=""
+
+
+        public static IEnumerable<String> DumpAPI(Assembly assembly)
+        {
+            return assembly.ExportedTypes.SelectMany(item => DumpAPI(item.Namespace, item.GetTypeInfo()));
+        }
+
+        public static IEnumerable<String> DumpAPI(string baseName, TypeInfo type)
+        {
+            if (type.IsNestedPrivate) yield break;
+
+            if (type.IsInterface) baseName += ".INTERFACE";
+            if (type.IsEnum) baseName += ".ENUM";
+            if (type.IsClass)
+            {
+                baseName += ".";
+                if (type.IsSealed) baseName += "SEALED";
+                if (type.IsAbstract) baseName += "ABSTRACT";
+                baseName += "CLASS";
+            }            
+
+            baseName += "." + type.GetFriendlyName();
+
+            Object instance = null;
+
+            try { instance = System.Activator.CreateInstance(type); }
+            catch { }
+
+            foreach (var f in type.DeclaredFields)
+            {
+                if (f.IsPrivate) continue;
+
+                var name = baseName;
+
+                if (f.IsLiteral) name += ".CONST";
+                else name += ".FIELD";
+
+                name += $".{f.FieldType.GetFriendlyName()}.{f.Name}";
+
+                if (f.IsStatic)
+                {
+                    var v = f.GetValue(null);
+                    if (v != null) name += "=" + v.ToString();
+                }
+                else if (instance != null)
+                {
+                    var v = f.GetValue(instance);
+                    if (v != null) name += "=" + v.ToString();
+                }
+
+                yield return name;
+            }
+
+            /* property getters and setters are already dumped by methods
+            foreach (var p in type.DeclaredProperties)
+            {
+                var pname = $"{baseName}.PROPERTY.{p.PropertyType.GetFriendlyName()}.{p.Name}";
+
+                var getter = p.GetGetMethod();
+                if (getter != null && !getter.IsPrivate) yield return pname + ".Get()";
+
+                var setter = p.GetSetMethod();
+                if (setter != null && !setter.IsPrivate) yield return pname + ".Set()";                
+            }
+            */
+
+            foreach(var m in type.DeclaredMethods)
+            {
+                // TODO: if parameters have default values, dump the same method multiple times with one parameter less each time.
+
+                if (m.IsPrivate) continue;
+
+                var mname = $"{baseName}.METHOD.{m.ReturnType.GetFriendlyName()}.{m.Name}";
+
+                var mparams = m.GetParameters()
+                    .Select(item => item.ParameterType.GetFriendlyName())
+                    .ToList();
+
+                yield return mname + "(" + string.Join(", ", mparams) + ")";
+            }
+
+            foreach(var n in type.DeclaredNestedTypes)
+            {
+                foreach (var nn in DumpAPI(baseName, n)) yield return nn;
+            }
+        }
+
+        public static string GetFriendlyName(this Type tinfo)
+        {
+            return tinfo.GetTypeInfo().GetFriendlyName();
+        }
+
+        public static string GetFriendlyName(this TypeInfo tinfo)
+        {
+            if (!tinfo.IsGenericType) return tinfo.Name;
+
+            var name = tinfo.Name;
+
+            /*
+            if (tinfo.Name == "System.Nullable`1")
+            {
+                return tinfo.GenericTypeParameters.First().Name + "?";
+            }*/           
+
+            name = name.Replace("`1","");
+            name = name.Replace("`2", "");
+            name = name.Replace("`3", "");
+            name = name.Replace("`4", "");
+
+            var gpm = tinfo
+                .GenericTypeArguments
+                .Select(item => GetFriendlyName(item.GetTypeInfo()))
+                .ToList();
+
+            if (gpm.Count > 0) name += "<" + string.Join(", ", gpm) + ">";
+
+            return name;
+            
+        }
+    }
+}

+ 1 - 1
tests/SharpGLTF.Tests/Geometry/CreateMeshTests.cs

@@ -58,7 +58,7 @@ namespace SharpGLTF.Geometry
 
             var root = Schema2.ModelRoot.CreateModel();                        
             var scene = root.UseScene("default");
-            var node = scene.AddVisualNode("main scene");
+            var node = scene.CreateNode("main scene");
 
             var material = root.CreateMaterial("DefaultMaterial")
                 .InitializeDefault(new Vector4(1, 0, 0, 1));

+ 29 - 8
tests/SharpGLTF.Tests/Schema2/CreateModelTests.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Numerics;
 using System.Text;
 
 using NUnit.Framework;
@@ -30,26 +31,28 @@ namespace SharpGLTF.Schema2
             var root = ModelRoot.CreateModel();
             
             // create a vertex buffer with positions and fill it
-            var positionsView = root.CreateBufferView(root.CreateBuffer(12 * 3), null, null, null, BufferMode.ARRAY_BUFFER);
+            var positionsView = root.UseBufferView(new Byte[12 * 3], 0, null, 0, BufferMode.ARRAY_BUFFER);
             var positionsArray = new Memory.Vector3Array(positionsView.Content);
             positionsArray[0] = new System.Numerics.Vector3(0, 10, 0);
             positionsArray[1] = new System.Numerics.Vector3(-10, -10, 0);
             positionsArray[2] = new System.Numerics.Vector3(10, -10, 0);
 
             // create an index buffer and fill it
-            var indicesView = root.CreateBufferView(root.CreateBuffer(4 * 3), null, null, null, BufferMode.ELEMENT_ARRAY_BUFFER);
+            var indicesView = root.UseBufferView(new Byte[4 * 3], 0, null, 0, BufferMode.ELEMENT_ARRAY_BUFFER);
             var indicesArray = new Memory.IntegerArray(indicesView.Content);
             indicesArray[0] = 0;
             indicesArray[1] = 1;
             indicesArray[2] = 2;
 
             // create a positions accessor
-            var positionsAccessor = root.CreateAccessor();
-            positionsAccessor.SetVertexData(positionsView, 0, ElementType.VEC3, ComponentType.FLOAT, false, 3);
+            var positionsAccessor = root
+                .CreateAccessor()
+                .WithVertexData(positionsView, 0, 3, ElementType.VEC3, ComponentType.FLOAT, false);
 
             // create an indices accessor
-            var indicesAccessor = root.CreateAccessor();
-            indicesAccessor.SetIndexData(indicesView, 0, IndexType.UNSIGNED_INT, 3);
+            var indicesAccessor = root
+                .CreateAccessor()
+                .WithIndexData(indicesView, 0, 3, IndexType.UNSIGNED_INT);
 
             // create a mesh and a mesh primitive
             var mesh = root.CreateMesh();
@@ -59,10 +62,10 @@ namespace SharpGLTF.Schema2
             primitive.IndexAccessor = indicesAccessor;
 
             // create a scene
-            var scene = root.UseScene("Empty Scene");
+            var scene = root.DefaultScene = root.UseScene("Empty Scene");
 
             // create a node
-            var node = scene.AddVisualNode("Triangle");
+            var node = scene.CreateNode("Triangle");
 
             // assign the mesh we previously created
             node.Mesh = mesh;
@@ -71,5 +74,23 @@ namespace SharpGLTF.Schema2
             root.AttachToCurrentTest("result.glb");
             root.AttachToCurrentTest("result.gltf");            
         }
+
+        [Test(Description = "Creates a simple scene using a helper class")]
+        public void CreateManyTrianglesScene()
+        {
+            TestContext.CurrentContext.AttachShowDirLink();
+            TestContext.CurrentContext.AttachGltfValidatorLink();
+
+            var builder = new SimpleSceneBuilder();
+
+            builder.AddPolygon(new Vector4(1, 1, 1, 1), (-10, 10,  0), (10, 10,  0), (10, -10,  0), (-10, -10,  0));
+            builder.AddPolygon(new Vector4(1, 1, 0, 1), (-10, 10, 10), (10, 10, 10), (10, -10, 10), (-10, -10, 10));
+            builder.AddPolygon(new Vector4(1, 0, 0, 1), (-10, 10, 20), (10, 10, 20), (10, -10, 20), (-10, -10, 20));
+
+            var model = builder.ToModel();
+
+            model.AttachToCurrentTest("result.glb");
+            model.AttachToCurrentTest("result.gltf");
+        }
     }
 }

+ 1 - 1
tests/SharpGLTF.Tests/Schema2/LoadModelTests.cs

@@ -107,7 +107,7 @@ namespace SharpGLTF.Schema2
 
             var scene = polly.DefaultScene;
 
-            var pollyNode = scene.FindVisualNode("Polly_Display");            
+            var pollyNode = scene.FindNode("Polly_Display");            
 
             var pollyPrimitive = pollyNode.Mesh.Primitives[0];
 

+ 39 - 31
tests/SharpGLTF.Tests/BufferBuilder.cs → tests/SharpGLTF.Tests/SimpleSceneBuilder.cs

@@ -2,14 +2,13 @@
 using System.Collections.Generic;
 using System.Numerics;
 using System.Text;
+using System.Linq;
 
 namespace SharpGLTF
 {
-    using Memory;
-    using System.Linq;
-    using COLOR = UInt32;
+    using COLOR = Vector4;
 
-    class BufferBuilder
+    class SimpleSceneBuilder
     {
         #region data
 
@@ -18,7 +17,14 @@ namespace SharpGLTF
 
         #endregion
 
-        #region API        
+        #region API
+
+        public void AddPolygon(COLOR color, params (float,float,float)[] points)
+        {
+            var vertices = points.Select(item => new Vector3(item.Item1, item.Item2, item.Item3)).ToArray();
+
+            AddPolygon(color, vertices);
+        }
 
         public void AddTriangle(COLOR color, Vector3 a, Vector3 b, Vector3 c)
         {
@@ -54,43 +60,45 @@ namespace SharpGLTF
         {
             var root = Schema2.ModelRoot.CreateModel();
 
-            var node = root.UseScene(0).AddVisualNode("Default");
+            var node = root.UseScene(0).CreateNode("Default");            
 
-            node.Mesh = root.CreateMesh();
+            // create vertex buffer
+            const int byteStride = 12 * 2;            
+
+            var vbuffer = root.UseBufferView(new Byte[byteStride * _Positions.Count], byteStride, Schema2.BufferMode.ARRAY_BUFFER);
 
-            const int byteStride = 12 * 2;
+            var positions = root
+                .CreateAccessor("Positions")
+                .WithVertexData(vbuffer, 0, _Positions);
 
-            var vbuffer = root.CreateBuffer(_Positions.Count * byteStride);
-            var vview = root.CreateBufferView(vbuffer, null, null, byteStride, Schema2.BufferMode.ARRAY_BUFFER);
+            var ppp = positions.AsVector3Array();
 
-            var vpositions = root.CreateAccessor("Positions");
-            vpositions.SetVertexData(vview, 0, Schema2.ElementType.VEC3, Schema2.ComponentType.FLOAT, false, _Positions.Count);            
-            vpositions.AsVector3Array().FillFrom(0, _Positions.ToArray());
-            vpositions.UpdateBounds();
+            var normals = root
+                .CreateAccessor("Normals")
+                .WithVertexData(vbuffer, 12, _CalculateNormals());
 
-            var vnormals = root.CreateAccessor("Normals");
-            vnormals.SetVertexData(vview, 12, Schema2.ElementType.VEC3, Schema2.ComponentType.FLOAT, false, _Positions.Count);            
-            vnormals.AsVector3Array().FillFrom(0, _CalculateNormals());
-            vnormals.UpdateBounds();
+            var nnn = normals.AsVector3Array();
+
+            // create mesh
+            node.Mesh = root.CreateMesh();
 
             foreach (var kvp in _Indices)
             {
-                var color = new Vector4((kvp.Key >> 24) & 255, (kvp.Key >> 16) & 255, (kvp.Key >> 8) & 255, (kvp.Key) & 255) / 255.0f;                
+                // create index buffer
+                var ibuffer = root.UseBufferView(new Byte[4 * kvp.Value.Count], 0, Schema2.BufferMode.ELEMENT_ARRAY_BUFFER);
+
+                var indices = root
+                    .CreateAccessor("Indices")
+                    .WithIndexData(ibuffer, 0, kvp.Value);
 
+                // create mesh primitive
                 var prim = node.Mesh.CreatePrimitive();
-                prim.Material = root.CreateMaterial().InitializeDefault(color);
+                prim.SetVertexAccessor("POSITION", positions);
+                prim.SetVertexAccessor("NORMAL", normals);
+                prim.SetIndexAccessor(indices);
                 prim.DrawPrimitiveType = Schema2.PrimitiveType.TRIANGLES;
-                
-                prim.SetVertexAccessor("POSITION", vpositions);
-                prim.SetVertexAccessor("NORMAL", vnormals);                
-
-                var ibuffer = root.CreateBuffer(kvp.Value.Count * 4);
-                var iview = root.CreateBufferView(ibuffer, null, null, null, Schema2.BufferMode.ELEMENT_ARRAY_BUFFER);
-                var indices = root.CreateAccessor("Indices");
-                indices.AsIndicesArray().FillFrom(0, kvp.Value.Select(item => (uint)item));
-
-                indices.SetIndexData(iview, 0, Schema2.IndexType.UNSIGNED_INT, kvp.Value.Count);
-                prim.IndexAccessor = indices;
+                prim.Material = root.CreateMaterial().InitializeDefault(kvp.Key);
+                prim.Material.DoubleSided = true;
             }
 
             root.MergeBuffers();

+ 27 - 0
tests/SharpGLTF.Tests/TestAssemblyAPI.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using NUnit.Framework;
+
+namespace SharpGLTF
+{
+    [TestFixture]
+    public class TestAssemblyAPI
+    {
+        [Test]
+        public void DumpCurrentAPI()
+        {
+            var assembly = typeof(Schema2.ModelRoot).Assembly;
+
+            var API = DumpAssemblyAPI.DumpAPI(assembly).ToList();
+
+            foreach(var l in API)
+            {
+                TestContext.WriteLine(l);
+            }
+
+        }
+    }
+}

+ 21 - 2
tests/SharpGLTF.Tests/TestUtils.cs

@@ -86,7 +86,12 @@ namespace SharpGLTF
         {
             context.AttachFileLink("📂 Show Directory", context.GetAttachmentPath(string.Empty));
         }
-        
+
+        public static void AttachGltfValidatorLink(this NUnit.Framework.TestContext context)
+        {
+            context.AttachUrlLink("🌍 glTF Validator", "http://github.khronos.org/glTF-Validator/");
+        }
+
         public static void AttachFileLink(this NUnit.Framework.TestContext context, string linkPath, string targetPath)
         {
             var sb = new StringBuilder();
@@ -103,6 +108,20 @@ namespace SharpGLTF
 
             NUnit.Framework.TestContext.AddTestAttachment(linkPath);
         }
-        
+
+        public static void AttachUrlLink(this NUnit.Framework.TestContext context, string linkPath, string url)
+        {
+            var sb = new StringBuilder();
+            sb.AppendLine("[InternetShortcut]");
+            sb.AppendLine("URL=" + url);            
+
+            linkPath = System.IO.Path.ChangeExtension(linkPath, ".url");
+            linkPath = context.GetAttachmentPath(linkPath, true);
+
+            System.IO.File.WriteAllText(linkPath, sb.ToString());
+
+            NUnit.Framework.TestContext.AddTestAttachment(linkPath);
+        }
+
     }
 }