Pārlūkot izejas kodu

+progress on mesh building

Vicente Penades 6 gadi atpakaļ
vecāks
revīzija
551af4787c

+ 193 - 27
src/glTF2Sharp.DOM/Geometry/VertexBuffer.cs

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

+ 19 - 7
src/glTF2Sharp.DOM/Geometry/VertexElement.cs

@@ -8,6 +8,9 @@ namespace glTF2Sharp.Geometry
 {
     using Schema2;    
 
+    /// <summary>
+    /// Defines a vertex attribute, dimensions and encoding.
+    /// </summary>
     public struct VertexElement
     {
         #region lifecycle
@@ -67,6 +70,12 @@ namespace glTF2Sharp.Geometry
             return elements.Sum(item => item.ByteSize);
         }
 
+        public static int FindDimensions(VertexElement[] elements, string attribute)
+        {
+            var idx = Array.FindIndex(elements, item => item.Attribute == attribute);
+            return idx < 0 ? 0 : elements[idx].Dimensions.DimCount();
+        }
+
         public static int GetBufferByteSize(int count, params VertexElement[] elements)
         {
             var stride = GetVertexByteSize(elements);
@@ -75,33 +84,33 @@ namespace glTF2Sharp.Geometry
 
         public static Memory.IEncodedArray<Single> GetScalarColumn(Byte[] data, string attribute, params VertexElement[] elements)
         {
-            var column = _GetColumn(data, attribute, elements);
+            var column = _GetColumn(data, elements, attribute, 0, int.MaxValue);
             if (column.Item3.Dimensions.DimCount() != 1) throw new ArgumentException(nameof(elements));
             return new Memory.ScalarArray(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
         }
 
         public static Memory.IEncodedArray<Vector2> GetVector2Column(Byte[] data, string attribute, params VertexElement[] elements)
         {
-            var column = _GetColumn(data, attribute, elements);
+            var column = _GetColumn(data, elements, attribute, 0, int.MaxValue);
             if (column.Item3.Dimensions.DimCount() != 2) throw new ArgumentException(nameof(elements));
             return new Memory.Vector2Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
         }
 
         public static Memory.IEncodedArray<Vector3> GetVector3Column(Byte[] data, string attribute, params VertexElement[] elements)
         {
-            var column = _GetColumn(data, attribute, elements);
+            var column = _GetColumn(data, elements, attribute, 0, int.MaxValue);
             if (column.Item3.Dimensions.DimCount() != 3) throw new ArgumentException(nameof(elements));
             return new Memory.Vector3Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
         }
 
         public static Memory.IEncodedArray<Vector4> GetVector4Column(Byte[] data, string attribute, params VertexElement[] elements)
         {
-            var column = _GetColumn(data, attribute, elements);
+            var column = _GetColumn(data, elements, attribute, 0, int.MaxValue);
             if (column.Item3.Dimensions.DimCount() != 4) throw new ArgumentException(nameof(elements));
             return new Memory.Vector4Array(column.Item1, column.Item2, column.Item3.Encoding, column.Item3.Normalized);
         }
 
-        private static (ArraySegment<Byte>, int, VertexElement) _GetColumn(Byte[] data, string attribute, params VertexElement[] elements)
+        internal static (ArraySegment<Byte>, int, VertexElement) _GetColumn(Byte[] data, VertexElement[] elements, string attribute, int rowStart, int rowCount)
         {
             var index = Array.FindIndex(elements, item => item.Attribute == attribute);
             if (index < 0) throw new ArgumentException(nameof(attribute));
@@ -109,9 +118,12 @@ namespace glTF2Sharp.Geometry
             var element = elements[index];
 
             var byteStride = GetVertexByteSize(elements);
-            var byteOffset = elements.Take(index).Sum(item => item.ByteSize);
+            var byteOffset = elements.Take(index).Sum(item => item.ByteSize) + rowStart * byteStride;
+            var byteLength = data.Length - byteOffset;
+
+            if (rowCount < int.MaxValue) byteLength = rowCount * byteStride;
 
-            var source = new ArraySegment<Byte>(data, byteOffset, data.Length - byteOffset);
+            var source = new ArraySegment<Byte>(data, byteOffset, byteLength);
 
             return (source, byteStride, element);
         }

+ 13 - 2
src/glTF2Sharp.DOM/Memory/Arrays.cs

@@ -13,6 +13,8 @@ namespace glTF2Sharp.Memory
 
         void CopyTo(ArraySegment<T> dst);
 
+        // void CopyTo(IEncodedArray<T> dst);
+
         (T, T) GetBounds();
     }
 
@@ -65,12 +67,21 @@ namespace glTF2Sharp.Memory
 
     public static class EncodedArrayUtils
     {
-        public static void CopyTo<T>(T[] src, IEncodedArray<T> dst)
+        public static void CopyTo<T>(IEncodedArray<T> src, IEncodedArray<T> dst, int dstOffset = 0)
+            where T : unmanaged
+        {
+            for (int i = 0; i < src.Count; ++i)
+            {
+                dst[i + dstOffset] = src[i];
+            }
+        }
+
+        public static void CopyTo<T>(T[] src, IEncodedArray<T> dst, int dstOffset = 0)
             where T: unmanaged
         {
             for(int i=0; i < src.Length; ++i)
             {
-                dst[i] = src[i];
+                dst[i+ dstOffset] = src[i];
             }
         }
 

+ 13 - 0
src/glTF2Sharp.DOM/Schema2/gltf.MeshPrimitive.cs

@@ -147,6 +147,19 @@ namespace glTF2Sharp.Schema2
             }
         }        
 
+        public void SetVertexAccessors(BufferView buffer, int byteOffset, int vertexCount, IEnumerable<Geometry.VertexElement> elements)
+        {
+            int count = 0;
+            foreach(var e in elements)
+            {
+                var accessor = this.LogicalParent.LogicalParent.CreateAccessor(e.Attribute);
+                accessor.SetVertexData(buffer, byteOffset + count, e.Dimensions, e.Encoding, e.Normalized, vertexCount);
+                count += e.ByteSize;
+
+                SetVertexAccessor(e.Attribute, accessor);
+            }            
+        }
+
         public IReadOnlyDictionary<String, Accessor> GetMorphTargetAccessors(int idx)
         {
             return new ReadOnlyLinqDictionary<String, int, Accessor>(_targets[idx], alidx => this.LogicalParent.LogicalParent.LogicalAccessors[alidx]);

+ 31 - 9
src/glTF2Sharp.Tests/Geometry/VertexBufferTests.cs

@@ -19,6 +19,8 @@ namespace glTF2Sharp.Geometry
                 new Vector3(0,1,2),
                 new Vector3(4,1,3),
                 new Vector3(2,3,5),
+                new Vector3(2,4,9),
+                new Vector3(1,3,5),
             };
 
             var normals = new[]
@@ -26,18 +28,38 @@ namespace glTF2Sharp.Geometry
                 Vector3.UnitX,
                 Vector3.UnitX,
                 Vector3.UnitX,
+                Vector3.UnitY,
+                Vector3.UnitZ,
             };
 
-            var vb = new VertexBuffer(3, "POSITION", "NORMAL");
+            var vb = new VertexArray(5, "POSITION", "NORMAL");
 
-            vb.SetVector3Column("POSITION", positions);
-            vb.SetVector3Column("NORMAL", normals);
+            vb.SetVector3Column("POSITION", 0, positions);
+            vb.SetVector3Column("NORMAL", 0, normals);
 
             var p2 = vb.GetVector3Column("POSITION");
             var n2 = vb.GetVector3Column("NORMAL");
-
             CollectionAssert.AreEqual(positions, p2);
             CollectionAssert.AreEqual(normals, n2);
+
+            var vbs = new VertexArraySegment(vb, 1, 2);
+
+            p2 = vbs.GetVector3Column("POSITION");
+            n2 = vbs.GetVector3Column("NORMAL");
+            CollectionAssert.AreEqual(positions.Skip(1).Take(2), p2);
+            CollectionAssert.AreEqual(normals.Skip(1).Take(2), n2);            
+
+            var mdl = new Schema2.ModelRoot();
+            var mdlbv = mdl.CreateVertexBufferView(vb.Data, vb.ByteStride);
+
+            var mdlmesh = mdl.CreateMesh();
+            var mdlprim = mdlmesh.CreatePrimitive();
+            mdlprim.SetVertexAccessors(mdlbv, vbs.ByteOffset, vbs.Count, vbs.Attributes);
+            Memory.EncodedArrayUtils.CopyTo(vbs.GetVector3Column("POSITION"), mdlprim.VertexAccessors["POSITION"].AsVector3Array());
+            Memory.EncodedArrayUtils.CopyTo(vbs.GetVector3Column("NORMAL"), mdlprim.VertexAccessors["NORMAL"].AsVector3Array());
+
+            mdl.AttachToCurrentTest("result.glb");
+            mdl.AttachToCurrentTest("result.gltf");
         }
 
         [Test]
@@ -72,7 +94,7 @@ namespace glTF2Sharp.Geometry
         {
             var attributes = srcBuffer.Declaration.Attributes.ToArray();
 
-            var dstBuffer = new glTF2Sharp.Geometry.VertexBuffer(srcBuffer.Count, attributes);
+            var dstBuffer = new VertexArray(srcBuffer.Count, attributes);
 
             foreach (var attr in attributes)
             {
@@ -81,25 +103,25 @@ namespace glTF2Sharp.Geometry
                 if (srcDim == 1)
                 {
                     var srcColumn = srcBuffer.GetScalarColumn(attr);
-                    dstBuffer.SetScalarColumn(attr, srcColumn);
+                    dstBuffer.SetScalarColumn(attr, 0, srcColumn);
                 }
 
                 if (srcDim == 2)
                 {
                     var srcColumn = srcBuffer.GetVector2Column(attr);
-                    dstBuffer.SetVector2Column(attr, srcColumn);
+                    dstBuffer.SetVector2Column(attr, 0, srcColumn);
                 }
 
                 if (srcDim == 3)
                 {
                     var srcColumn = srcBuffer.GetVector3Column(attr);
-                    dstBuffer.SetVector3Column(attr, srcColumn);
+                    dstBuffer.SetVector3Column(attr, 0, srcColumn);
                 }
 
                 if (srcDim == 4)
                 {
                     var srcColumn = srcBuffer.GetVector4Column(attr);
-                    dstBuffer.SetVector4Column(attr, srcColumn);
+                    dstBuffer.SetVector4Column(attr, 0, srcColumn);
                 }
             }