Răsfoiți Sursa

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

Vicente Penades 6 ani în urmă
părinte
comite
c672e5d3d7

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

@@ -123,7 +123,7 @@ namespace SharpGLTF.Geometry
         public void AssignTo(Schema2.MeshPrimitive dstPrim)
         public void AssignTo(Schema2.MeshPrimitive dstPrim)
         {
         {
             var dstAccessor = dstPrim.LogicalParent.LogicalParent.CreateAccessor(this.Name);
             var dstAccessor = dstPrim.LogicalParent.LogicalParent.CreateAccessor(this.Name);
-            dstAccessor.SetVertexData(_MemoryAccessor);
+            dstAccessor.WithVertexData(_MemoryAccessor);
             dstPrim.SetVertexAccessor(this._MemoryAccessor.Attribute.Name, dstAccessor);
             dstPrim.SetVertexAccessor(this._MemoryAccessor.Attribute.Name, dstAccessor);
         }
         }
 
 
@@ -184,7 +184,7 @@ namespace SharpGLTF.Geometry
         public void AssignToSchema(Schema2.MeshPrimitive dstPrim)
         public void AssignToSchema(Schema2.MeshPrimitive dstPrim)
         {
         {
             var dstAccessor = dstPrim.LogicalParent.LogicalParent.CreateAccessor(this.Name);
             var dstAccessor = dstPrim.LogicalParent.LogicalParent.CreateAccessor(this.Name);
-            dstAccessor.SetIndexData(_MemoryAccessor);
+            dstAccessor.WithIndexData(_MemoryAccessor);
             dstPrim.IndexAccessor = dstAccessor;
             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)
         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)
             for (int i = 0; i < count; ++i)
             {
             {
@@ -81,6 +79,17 @@ namespace SharpGLTF.Memory
             return array;
             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)
         public static void FillFrom<T>(this IEncodedArray<T> dst, int dstIndex, IEnumerable<T> src)
             where T : unmanaged
             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)
         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._Data = data.Slice(byteOffset);
-            this._ItemCount = 0;
             this._Getter = null;
             this._Getter = null;
             this._Setter = 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);
             _ItemCount = Math.Min(itemsCount, _ItemCount);
 
 
@@ -39,88 +38,87 @@ namespace SharpGLTF.Memory
             {
             {
                 this._Setter = this._SetValue<Single>;
                 this._Setter = this._SetValue<Single>;
                 this._Getter = this._GetValue<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;
                             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 BYTES _Data;
 
 
         private readonly int _ByteStride;
         private readonly int _ByteStride;
-        private readonly int _ValueStride;
+        private readonly int _EncodedLen;
 
 
         private readonly int _ItemCount;
         private readonly int _ItemCount;
 
 
@@ -202,8 +200,8 @@ namespace SharpGLTF.Memory
 
 
         public Single this[int rowIndex, int subIndex]
         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
         #endregion
@@ -217,15 +215,12 @@ namespace SharpGLTF.Memory
     {
     {
         #region constructors
         #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)
         public ScalarArray(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
             : 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
         #endregion
@@ -272,15 +267,12 @@ namespace SharpGLTF.Memory
     {
     {
         #region constructors
         #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)
         public Vector2Array(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
             : 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
         #endregion
@@ -335,15 +327,12 @@ namespace SharpGLTF.Memory
     {
     {
         #region constructors
         #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)
         public Vector3Array(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
             : 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
         #endregion
@@ -399,15 +388,12 @@ namespace SharpGLTF.Memory
     {
     {
         #region constructors
         #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)
         public Vector4Array(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
             : 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
         #endregion
@@ -464,15 +450,12 @@ namespace SharpGLTF.Memory
     {
     {
         #region constructors
         #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)
         public QuaternionArray(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
             : 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
         #endregion
@@ -529,15 +512,12 @@ namespace SharpGLTF.Memory
     {
     {
         #region constructors
         #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)
         public Matrix4x4Array(BYTES data, int byteStride = 0, ENCODING encoding = ENCODING.FLOAT, Boolean normalized = false)
             : this(data, 0, int.MaxValue, byteStride, encoding, normalized) { }
             : 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
         #endregion

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

@@ -18,9 +18,6 @@ namespace SharpGLTF.Memory
     {
     {
         #region constructors
         #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)
         public IntegerArray(BYTES data, ENCODING encoding = ENCODING.UNSIGNED_INT)
             : this(data, 0, int.MaxValue, encoding) { }
             : 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
         #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);
             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.NotNull(buffer, nameof(buffer));
             Guard.MustShareLogicalParent(this, buffer, nameof(buffer));
             Guard.MustShareLogicalParent(this, buffer, nameof(buffer));
             if (buffer.DeviceBufferTarget.HasValue) Guard.IsTrue(buffer.DeviceBufferTarget.Value == BufferMode.ELEMENT_ARRAY_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.MustBeGreaterThanOrEqualTo(byteOffset, 0, nameof(byteOffset));
-            Guard.MustBeGreaterThan(count, 0, nameof(count));
+            Guard.MustBeGreaterThan(itemCount, 0, nameof(itemCount));
 
 
             this._bufferView = buffer.LogicalIndex;
             this._bufferView = buffer.LogicalIndex;
             this._byteOffset = byteOffset;
             this._byteOffset = byteOffset;
-            this._count = count;
+            this._count = itemCount;
 
 
             this._type = ElementType.SCALAR;
             this._type = ElementType.SCALAR;
             this._componentType = encoding.ToComponent();
             this._componentType = encoding.ToComponent();
             this._normalized = null;
             this._normalized = null;
 
 
             UpdateBounds();
             UpdateBounds();
+
+            return this;
         }
         }
 
 
         public Memory.IntegerArray AsIndicesArray()
         public Memory.IntegerArray AsIndicesArray()
@@ -157,30 +175,41 @@ namespace SharpGLTF.Schema2
 
 
         #region Vertex Buffer API
         #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);
             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.NotNull(buffer, nameof(buffer));
             Guard.MustShareLogicalParent(this, buffer, nameof(buffer));
             Guard.MustShareLogicalParent(this, buffer, nameof(buffer));
             if (buffer.DeviceBufferTarget.HasValue) Guard.IsTrue(buffer.DeviceBufferTarget.Value == BufferMode.ARRAY_BUFFER, nameof(buffer));
             if (buffer.DeviceBufferTarget.HasValue) Guard.IsTrue(buffer.DeviceBufferTarget.Value == BufferMode.ARRAY_BUFFER, nameof(buffer));
 
 
             Guard.MustBeGreaterThanOrEqualTo(byteOffset, 0, nameof(byteOffset));
             Guard.MustBeGreaterThanOrEqualTo(byteOffset, 0, nameof(byteOffset));
-            Guard.MustBeGreaterThan(count, 0, nameof(count));
+            Guard.MustBeGreaterThan(itemCount, 0, nameof(itemCount));
 
 
             this._bufferView = buffer.LogicalIndex;
             this._bufferView = buffer.LogicalIndex;
             this._byteOffset = byteOffset;
             this._byteOffset = byteOffset;
-            this._count = count;
+            this._count = itemCount;
 
 
             this._type = dimensions;
             this._type = dimensions;
             this._componentType = encoding;
             this._componentType = encoding;
             this._normalized = normalized.AsNullable(false);
             this._normalized = normalized.AsNullable(false);
 
 
             UpdateBounds();
             UpdateBounds();
+
+            return this;
         }
         }
 
 
         public Memory.IEncodedArray<Single> AsScalarArray()
         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
     public partial class ModelRoot
     {
     {
         /// <summary>
         /// <summary>
-        /// Creates a buffer with a given size
+        /// Creates a buffer with <paramref name="byteCount"/> size.
         /// </summary>
         /// </summary>
         /// <param name="byteCount">the size of the buffer</param>
         /// <param name="byteCount">the size of the buffer</param>
         /// <returns>the buffer</returns>
         /// <returns>the buffer</returns>
@@ -97,33 +97,21 @@ namespace SharpGLTF.Schema2
         }
         }
 
 
         /// <summary>
         /// <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>
         /// </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>
         /// <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)
             foreach (var b in this.LogicalBuffers)
             {
             {
-                if (b._Content == data) return b;
+                if (b._Content == content) return b;
             }
             }
 
 
             var buffer = new Buffer();
             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);
             _buffers.Add(buffer);
 
 

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

@@ -17,35 +17,35 @@ namespace SharpGLTF.Schema2
 
 
         internal BufferView() { }
         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, nameof(buffer));
             Guard.NotNull(buffer._Content, nameof(buffer));
             Guard.NotNull(buffer._Content, nameof(buffer));
             Guard.NotNull(buffer.LogicalParent, 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(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)
             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.
                 // 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._buffer = buffer.LogicalIndex;
 
 
             this._byteLength = byteLength.AsValue(buffer._Content.Length);
             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;
             this._target = target;
         }
         }
@@ -65,7 +65,7 @@ namespace SharpGLTF.Schema2
             get
             get
             {
             {
                 var buffer = this.LogicalParent.LogicalBuffers[this._buffer];
                 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 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)
             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.ByteStride != byteStride) continue;
-                if (bv.DeviceBufferTarget != mode) continue;
+                if (bv.DeviceBufferTarget != target) continue;
 
 
                 return bv;
                 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 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;
         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)
         public IReadOnlyDictionary<String, Accessor> GetMorphTargetAccessors(int idx)
         {
         {
             return new ReadOnlyLinqDictionary<String, int, Accessor>(_targets[idx], alidx => this.LogicalParent.LogicalParent.LogicalAccessors[alidx]);
             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
         #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
         #endregion
 
 

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

@@ -12,9 +12,9 @@ namespace SharpGLTF.Schema2
     {
     {
         IEnumerable<Node> VisualChildren { get; }
         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}")]
     [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 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();
         public IEnumerable<Node> VisualChildren => GetVisualChildren();
 
 
@@ -160,24 +160,14 @@ namespace SharpGLTF.Schema2
             return allChildren;
             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;
             node.Name = name;
             return node;
             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);
             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>();
             if (mesh == null) return Enumerable.Empty<Node>();
 
 
@@ -205,10 +195,10 @@ namespace SharpGLTF.Schema2
 
 
             return mesh.LogicalParent
             return mesh.LogicalParent
                 .LogicalNodes
                 .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>();
             if (skin == null) return Enumerable.Empty<Node>();
 
 
@@ -216,7 +206,7 @@ namespace SharpGLTF.Schema2
 
 
             return skin.LogicalParent
             return skin.LogicalParent
                 .LogicalNodes
                 .LogicalNodes
-                .Where(item => item._skin.HasValue && item._skin.Value == meshIdx);
+                .Where(item => item._skin.AsValue(int.MinValue) == meshIdx);
         }
         }
 
 
         public override IEnumerable<Exception> Validate()
         public override IEnumerable<Exception> Validate()
@@ -294,19 +284,12 @@ namespace SharpGLTF.Schema2
             return VisualChildren.Any(item => item._ContainsVisualNode(node, true));
             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);
             return this.VisualChildren.FirstOrDefault(item => item.Name == name);
         }
         }
@@ -360,7 +343,7 @@ namespace SharpGLTF.Schema2
             return scene;
             return scene;
         }
         }
 
 
-        internal Node _GetVisualParentNode(Node childNode)
+        internal Node _FindVisualParentNode(Node childNode)
         {
         {
             var childIdx = _nodes.IndexOf(childNode);
             var childIdx = _nodes.IndexOf(childNode);
             if (childIdx < 0) return null;
             if (childIdx < 0) return null;
@@ -369,22 +352,20 @@ namespace SharpGLTF.Schema2
             return _nodes.FirstOrDefault(item => item._HasVisualChild(childIdx));
             return _nodes.FirstOrDefault(item => item._HasVisualChild(childIdx));
         }
         }
 
 
-        internal Node _AddLogicalNode()
+        internal Node _CreateLogicalNode()
         {
         {
             var n = new Node();
             var n = new Node();
             _nodes.Add(n);
             _nodes.Add(n);
             return 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);
             children.Add(n.LogicalIndex);
             return n;
             return n;
         }
         }
 
 
-        internal int _UseLogicaNode(Node node) { return _nodes.Use(node); }
-
         internal Boolean _CheckNodeIsJoint(Node n)
         internal Boolean _CheckNodeIsJoint(Node n)
         {
         {
             var idx = n.LogicalIndex;
             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 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;
         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 root = Schema2.ModelRoot.CreateModel();                        
             var scene = root.UseScene("default");
             var scene = root.UseScene("default");
-            var node = scene.AddVisualNode("main scene");
+            var node = scene.CreateNode("main scene");
 
 
             var material = root.CreateMaterial("DefaultMaterial")
             var material = root.CreateMaterial("DefaultMaterial")
                 .InitializeDefault(new Vector4(1, 0, 0, 1));
                 .InitializeDefault(new Vector4(1, 0, 0, 1));

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

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Numerics;
 using System.Text;
 using System.Text;
 
 
 using NUnit.Framework;
 using NUnit.Framework;
@@ -30,26 +31,28 @@ namespace SharpGLTF.Schema2
             var root = ModelRoot.CreateModel();
             var root = ModelRoot.CreateModel();
             
             
             // create a vertex buffer with positions and fill it
             // 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);
             var positionsArray = new Memory.Vector3Array(positionsView.Content);
             positionsArray[0] = new System.Numerics.Vector3(0, 10, 0);
             positionsArray[0] = new System.Numerics.Vector3(0, 10, 0);
             positionsArray[1] = new System.Numerics.Vector3(-10, -10, 0);
             positionsArray[1] = new System.Numerics.Vector3(-10, -10, 0);
             positionsArray[2] = new System.Numerics.Vector3(10, -10, 0);
             positionsArray[2] = new System.Numerics.Vector3(10, -10, 0);
 
 
             // create an index buffer and fill it
             // 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);
             var indicesArray = new Memory.IntegerArray(indicesView.Content);
             indicesArray[0] = 0;
             indicesArray[0] = 0;
             indicesArray[1] = 1;
             indicesArray[1] = 1;
             indicesArray[2] = 2;
             indicesArray[2] = 2;
 
 
             // create a positions accessor
             // 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
             // 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
             // create a mesh and a mesh primitive
             var mesh = root.CreateMesh();
             var mesh = root.CreateMesh();
@@ -59,10 +62,10 @@ namespace SharpGLTF.Schema2
             primitive.IndexAccessor = indicesAccessor;
             primitive.IndexAccessor = indicesAccessor;
 
 
             // create a scene
             // create a scene
-            var scene = root.UseScene("Empty Scene");
+            var scene = root.DefaultScene = root.UseScene("Empty Scene");
 
 
             // create a node
             // create a node
-            var node = scene.AddVisualNode("Triangle");
+            var node = scene.CreateNode("Triangle");
 
 
             // assign the mesh we previously created
             // assign the mesh we previously created
             node.Mesh = mesh;
             node.Mesh = mesh;
@@ -71,5 +74,23 @@ namespace SharpGLTF.Schema2
             root.AttachToCurrentTest("result.glb");
             root.AttachToCurrentTest("result.glb");
             root.AttachToCurrentTest("result.gltf");            
             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 scene = polly.DefaultScene;
 
 
-            var pollyNode = scene.FindVisualNode("Polly_Display");            
+            var pollyNode = scene.FindNode("Polly_Display");            
 
 
             var pollyPrimitive = pollyNode.Mesh.Primitives[0];
             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.Collections.Generic;
 using System.Numerics;
 using System.Numerics;
 using System.Text;
 using System.Text;
+using System.Linq;
 
 
 namespace SharpGLTF
 namespace SharpGLTF
 {
 {
-    using Memory;
-    using System.Linq;
-    using COLOR = UInt32;
+    using COLOR = Vector4;
 
 
-    class BufferBuilder
+    class SimpleSceneBuilder
     {
     {
         #region data
         #region data
 
 
@@ -18,7 +17,14 @@ namespace SharpGLTF
 
 
         #endregion
         #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)
         public void AddTriangle(COLOR color, Vector3 a, Vector3 b, Vector3 c)
         {
         {
@@ -54,43 +60,45 @@ namespace SharpGLTF
         {
         {
             var root = Schema2.ModelRoot.CreateModel();
             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)
             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();
                 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.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();
             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));
             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)
         public static void AttachFileLink(this NUnit.Framework.TestContext context, string linkPath, string targetPath)
         {
         {
             var sb = new StringBuilder();
             var sb = new StringBuilder();
@@ -103,6 +108,20 @@ namespace SharpGLTF
 
 
             NUnit.Framework.TestContext.AddTestAttachment(linkPath);
             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);
+        }
+
     }
     }
 }
 }