Browse Source

some more API changes
+ documentation

Vicente Penades 6 năm trước cách đây
mục cha
commit
40f6e7e2c7

+ 19 - 5
README.md

@@ -1,12 +1,26 @@
-# SharpGLTF
+<p align="center">
+<img src="build/Icons/glTF2Sharp.png" height=128 />
+</p>
 
-SharpGLTF is a NetStandard 2.0, C# library designed to parse and create [Khronos Group glTF 2.0](https://github.com/KhronosGroup/glTF) files.
+---
+
+### Overview
+
+SharpGLTF is a NetStandard 2.0, .NET library designed to support
+[Khronos Group glTF 2.0](https://github.com/KhronosGroup/glTF) file format.
 
 The current status of the library is preview alpha, but, for some use cases it is already usable.
 
-Prerelease Nuget packages available [here.](https://www.nuget.org/packages/SharpGLTF)
+#### Design
+
+The framework is divided in two packages:
+
+- __SharpGLTF.Core__ provides the core glTF2 schema implementation, read & write operations,
+and low level API access to let direct document manipulation.
+- __SharpGLTF.Toolkit__ provides an abstraction layer over the Core package, adding convenient
+extensions and utilities to help creating meshes, materials and scenes.
 
-### Examples
+#### Examples
 
 - [Load and save glTF and GLB files.](https://github.com/vpenades/SharpGLTF/blob/3dfe005ba7210c8327867127681a2b39aa567412/tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSampleTests.cs#L32)
 - [Create a simple triangle.](https://github.com/vpenades/SharpGLTF/blob/3dfe005ba7210c8327867127681a2b39aa567412/tests/SharpGLTF.Tests/Schema2/Authoring/BasicSceneCreationTests.cs#L95)
@@ -26,7 +40,7 @@ Prerelease Nuget packages available [here.](https://www.nuget.org/packages/Sharp
 - [x] Reading Base64 encoded buffers.
 - [x] Support of merging buffers to write one buffer *.glb* files.
 
-#### ToDo:
+#### To do:
 - [ ] Writing Base64 encoded buffers.
 - [ ] Scene Evaluation.
 - [ ] GPU Evaluation.

+ 1 - 1
src/SharpGLTF.Core/Schema2/gltf.MaterialsFactory.cs

@@ -150,7 +150,7 @@ namespace SharpGLTF.Schema2
             {
                 return new Vector4
                     (
-                    (float) _metallicFactor.AsValue( _metallicFactorDefault),
+                    (float)_metallicFactor.AsValue( _metallicFactorDefault),
                     (float)_roughnessFactor.AsValue(_roughnessFactorDefault),
                     0,
                     0

+ 27 - 27
src/SharpGLTF.Toolkit/Geometry/MeshBuilder.cs

@@ -12,7 +12,7 @@ namespace SharpGLTF.Geometry
     /// <summary>
     /// Represents an utility class to help build mesh primitives by adding triangles
     /// </summary>
-    /// <typeparam name="TMaterial">The material type used by this <see cref="PrimitiveBuilder{TMaterial, TvP, TvM, TvJ}"/> instance.</typeparam>
+    /// <typeparam name="TMaterial">The material type used by this <see cref="PrimitiveBuilder{TMaterial, TvP, TvM, JvS}"/> instance.</typeparam>
     /// <typeparam name="TvP">
     /// The vertex fragment type with Position, Normal and Tangent.
     /// Valid types are:
@@ -28,7 +28,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexTexture1"/>,
     /// <see cref="VertexColor1Texture1"/>.
     /// </typeparam>
-    /// <typeparam name="TvJ">
+    /// <typeparam name="JvS">
     /// The vertex fragment type with Skin Joint Weights.
     /// Valid types are:
     /// <see cref="VertexEmpty"/>,
@@ -37,14 +37,14 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexJoints16x4"/>,
     /// <see cref="VertexJoints16x8"/>.
     /// </typeparam>
-    public class PrimitiveBuilder<TMaterial, TvP, TvM, TvJ>
+    public class PrimitiveBuilder<TMaterial, TvP, TvM, JvS>
         where TvP : struct, IVertexPosition
         where TvM : struct, IVertexMaterial
-        where TvJ : struct, IVertexJoints
+        where JvS : struct, IVertexSkinning
     {
         #region lifecycle
 
-        internal PrimitiveBuilder(MeshBuilder<TMaterial, TvP, TvM, TvJ> mesh, TMaterial material, bool strict)
+        internal PrimitiveBuilder(MeshBuilder<TMaterial, TvP, TvM, JvS> mesh, TMaterial material, bool strict)
         {
             this._Scrict = strict;
             this._Mesh = mesh;
@@ -57,22 +57,22 @@ namespace SharpGLTF.Geometry
 
         private readonly bool _Scrict;
 
-        private readonly MeshBuilder<TMaterial, TvP, TvM, TvJ> _Mesh;
+        private readonly MeshBuilder<TMaterial, TvP, TvM, JvS> _Mesh;
 
         private readonly TMaterial _Material;
 
-        private readonly VertexList<(TvP, TvM, TvJ)> _Vertices = new VertexList<(TvP, TvM, TvJ)>();
+        private readonly VertexList<(TvP, TvM, JvS)> _Vertices = new VertexList<(TvP, TvM, JvS)>();
         private readonly List<int> _Indices = new List<int>();
 
         #endregion
 
         #region properties
 
-        public MeshBuilder<TMaterial, TvP, TvM, TvJ> Mesh => _Mesh;
+        public MeshBuilder<TMaterial, TvP, TvM, JvS> Mesh => _Mesh;
 
         public TMaterial Material => _Material;
 
-        public IReadOnlyList<(TvP, TvM, TvJ)> Vertices => _Vertices;
+        public IReadOnlyList<(TvP, TvM, JvS)> Vertices => _Vertices;
 
         public IReadOnlyList<int> Indices => _Indices;
 
@@ -91,7 +91,7 @@ namespace SharpGLTF.Geometry
 
         #region API
 
-        public int UseVertex((TvP, TvM, TvJ) vertex)
+        public int UseVertex((TvP, TvM, JvS) vertex)
         {
             if (_Scrict)
             {
@@ -103,7 +103,7 @@ namespace SharpGLTF.Geometry
             return _Vertices.Use(vertex);
         }
 
-        public void AddTriangle((TvP, TvM, TvJ) a, (TvP, TvM, TvJ) b, (TvP, TvM, TvJ) c)
+        public void AddTriangle((TvP, TvM, JvS) a, (TvP, TvM, JvS) b, (TvP, TvM, JvS) c)
         {
             var aa = UseVertex(a);
             var bb = UseVertex(b);
@@ -126,7 +126,7 @@ namespace SharpGLTF.Geometry
             AddTriangle((a.Item1, a.Item2, default), (b.Item1, b.Item2, default), (c.Item1, c.Item2, default));
         }
 
-        public void AddTriangle((TvP, TvJ) a, (TvP, TvJ) b, (TvP, TvJ) c)
+        public void AddTriangle((TvP, JvS) a, (TvP, JvS) b, (TvP, JvS) c)
         {
             AddTriangle((a.Item1, default, a.Item2), (b.Item1, default, b.Item2), (c.Item1, default, c.Item2));
         }
@@ -136,7 +136,7 @@ namespace SharpGLTF.Geometry
             AddTriangle((a, default, default), (b, default, default), (c, default, default));
         }
 
-        public void AddPolygon(params (TvP, TvM, TvJ)[] points)
+        public void AddPolygon(params (TvP, TvM, JvS)[] points)
         {
             for (int i = 2; i < points.Length; ++i)
             {
@@ -152,7 +152,7 @@ namespace SharpGLTF.Geometry
             }
         }
 
-        public void AddPolygon(params (TvP, TvJ)[] points)
+        public void AddPolygon(params (TvP, JvS)[] points)
         {
             for (int i = 2; i < points.Length; ++i)
             {
@@ -168,7 +168,7 @@ namespace SharpGLTF.Geometry
             }
         }
 
-        public void AddPrimitive(PrimitiveBuilder<TMaterial, TvP, TvM, TvJ> primitive, Matrix4x4 transform)
+        public void AddPrimitive(PrimitiveBuilder<TMaterial, TvP, TvM, JvS> primitive, Matrix4x4 transform)
         {
             if (primitive == null) throw new ArgumentNullException(nameof(primitive));
 
@@ -202,7 +202,7 @@ namespace SharpGLTF.Geometry
     /// <summary>
     /// Represents an utility class to help build meshes by adding primitives associated with a given material.
     /// </summary>
-    /// <typeparam name="TMaterial">The material type used by this <see cref="PrimitiveBuilder{TMaterial, TvP, TvM, TvJ}"/> instance.</typeparam>
+    /// <typeparam name="TMaterial">The material type used by this <see cref="PrimitiveBuilder{TMaterial, TvP, TvM, JvS}"/> instance.</typeparam>
     /// <typeparam name="TvP">
     /// The vertex fragment type with Position, Normal and Tangent.
     /// Valid types are:
@@ -218,7 +218,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexTexture1"/>,
     /// <see cref="VertexColor1Texture1"/>.
     /// </typeparam>
-    /// <typeparam name="TvJ">
+    /// <typeparam name="JvS">
     /// The vertex fragment type with Skin Joint Weights.
     /// Valid types are:
     /// <see cref="VertexEmpty"/>,
@@ -227,10 +227,10 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexJoints16x4"/>,
     /// <see cref="VertexJoints16x8"/>.
     /// </typeparam>
-    public class MeshBuilder<TMaterial, TvP, TvM, TvJ>
+    public class MeshBuilder<TMaterial, TvP, TvM, JvS>
         where TvP : struct, IVertexPosition
         where TvM : struct, IVertexMaterial
-        where TvJ : struct, IVertexJoints
+        where JvS : struct, IVertexSkinning
     {
         #region lifecycle
 
@@ -243,7 +243,7 @@ namespace SharpGLTF.Geometry
 
         #region data
 
-        private readonly Dictionary<TMaterial, PrimitiveBuilder<TMaterial, TvP, TvM, TvJ>> _Primitives = new Dictionary<TMaterial, PrimitiveBuilder<TMaterial, TvP, TvM, TvJ>>();
+        private readonly Dictionary<TMaterial, PrimitiveBuilder<TMaterial, TvP, TvM, JvS>> _Primitives = new Dictionary<TMaterial, PrimitiveBuilder<TMaterial, TvP, TvM, JvS>>();
 
         #endregion
 
@@ -253,17 +253,17 @@ namespace SharpGLTF.Geometry
 
         public string Name { get; set; }
 
-        public IReadOnlyCollection<PrimitiveBuilder<TMaterial, TvP, TvM, TvJ>> Primitives => _Primitives.Values;
+        public IReadOnlyCollection<PrimitiveBuilder<TMaterial, TvP, TvM, JvS>> Primitives => _Primitives.Values;
 
         #endregion
 
         #region API
 
-        public PrimitiveBuilder<TMaterial, TvP, TvM, TvJ> UsePrimitive(TMaterial material)
+        public PrimitiveBuilder<TMaterial, TvP, TvM, JvS> UsePrimitive(TMaterial material)
         {
-            if (!_Primitives.TryGetValue(material, out PrimitiveBuilder<TMaterial, TvP, TvM, TvJ> primitive))
+            if (!_Primitives.TryGetValue(material, out PrimitiveBuilder<TMaterial, TvP, TvM, JvS> primitive))
             {
-                primitive = new PrimitiveBuilder<TMaterial, TvP, TvM, TvJ>(this, material, StrictMode);
+                primitive = new PrimitiveBuilder<TMaterial, TvP, TvM, JvS>(this, material, StrictMode);
                 _Primitives[material] = primitive;
             }
 
@@ -272,19 +272,19 @@ namespace SharpGLTF.Geometry
 
         public IEnumerable<(int, int, int)> GetTriangles(TMaterial material)
         {
-            if (_Primitives.TryGetValue(material, out PrimitiveBuilder<TMaterial, TvP, TvM, TvJ> primitive)) return primitive.Triangles;
+            if (_Primitives.TryGetValue(material, out PrimitiveBuilder<TMaterial, TvP, TvM, JvS> primitive)) return primitive.Triangles;
 
             return Enumerable.Empty<(int, int, int)>();
         }
 
         public IReadOnlyList<int> GetIndices(TMaterial material)
         {
-            if (_Primitives.TryGetValue(material, out PrimitiveBuilder<TMaterial, TvP, TvM, TvJ> primitive)) return primitive.Indices;
+            if (_Primitives.TryGetValue(material, out PrimitiveBuilder<TMaterial, TvP, TvM, JvS> primitive)) return primitive.Indices;
 
             return new int[0];
         }
 
-        public void AddMesh(MeshBuilder<TMaterial, TvP, TvM, TvJ> mesh, Matrix4x4 transform)
+        public void AddMesh(MeshBuilder<TMaterial, TvP, TvM, JvS> mesh, Matrix4x4 transform)
         {
             foreach (var p in mesh.Primitives)
             {

+ 6 - 6
src/SharpGLTF.Toolkit/Geometry/PackedMeshBuilder.cs

@@ -8,7 +8,7 @@ namespace SharpGLTF.Geometry
     using Schema2;
 
     /// <summary>
-    /// Used internally to convert a <see cref="MeshBuilder{TMaterial, TvP, TvM, TvJ}"/>
+    /// Used internally to convert a <see cref="MeshBuilder{TMaterial, TvP, TvM, JvS}"/>
     /// to <see cref="Schema2.Mesh"/>.
     /// </summary>
     /// <typeparam name="TMaterial">A material key to split primitives by material.</typeparam>
@@ -17,19 +17,19 @@ namespace SharpGLTF.Geometry
         #region lifecycle
 
         /// <summary>
-        /// Converts a collection of <see cref="MeshBuilder{TMaterial, TvP, TvM, TvJ}"/>
+        /// Converts a collection of <see cref="MeshBuilder{TMaterial, TvP, TvM, JvS}"/>
         /// to a collection of <see cref="PackedMeshBuilder{TMaterial}"/>, trying to use
         /// a single vertex buffer and a single index buffer shared by all meshes.
         /// </summary>
         /// <typeparam name="TvP">The vertex fragment type with Position, Normal and Tangent.</typeparam>
         /// <typeparam name="TvM">The vertex fragment type with Colors and Texture Coordinates.</typeparam>
-        /// <typeparam name="TvJ">The vertex fragment type with Skin Joint Weights.</typeparam>
-        /// <param name="meshBuilders">A collection of <see cref="MeshBuilder{TMaterial, TvP, TvM, TvJ}"/> instances.</param>
+        /// <typeparam name="JvS">The vertex fragment type with Skin Joint Weights.</typeparam>
+        /// <param name="meshBuilders">A collection of <see cref="MeshBuilder{TMaterial, TvP, TvM, JvS}"/> instances.</param>
         /// <returns>A collection of <see cref="PackedMeshBuilder{TMaterial}"/> instances.</returns>
-        internal static IEnumerable<PackedMeshBuilder<TMaterial>> PackMeshes<TvP, TvM, TvJ>(IEnumerable<MeshBuilder<TMaterial, TvP, TvM, TvJ>> meshBuilders)
+        internal static IEnumerable<PackedMeshBuilder<TMaterial>> PackMeshes<TvP, TvM, JvS>(IEnumerable<MeshBuilder<TMaterial, TvP, TvM, JvS>> meshBuilders)
             where TvP : struct, VertexTypes.IVertexPosition
             where TvM : struct, VertexTypes.IVertexMaterial
-            where TvJ : struct, VertexTypes.IVertexJoints
+            where JvS : struct, VertexTypes.IVertexSkinning
         {
             try
             {

+ 3 - 3
src/SharpGLTF.Toolkit/Geometry/StaticMeshBuilder.cs

@@ -26,7 +26,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexTexture1"/>,
     /// <see cref="VertexColor1Texture1"/>.
     /// </typeparam>
-    /// <typeparam name="TvJ">
+    /// <typeparam name="JvS">
     /// The vertex fragment type with Skin Joint Weights.
     /// Valid types are:
     /// <see cref="VertexEmpty"/>,
@@ -35,10 +35,10 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexJoints16x4"/>,
     /// <see cref="VertexJoints16x8"/>.
     /// </typeparam>
-    public class MeshBuilder<TvP, TvM, TvJ> : MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvJ>
+    public class MeshBuilder<TvP, TvM, JvS> : MeshBuilder<Materials.MaterialBuilder, TvP, TvM, JvS>
         where TvP : struct, IVertexPosition
         where TvM : struct, IVertexMaterial
-        where TvJ : struct, IVertexJoints
+        where JvS : struct, IVertexSkinning
     {
         public MeshBuilder(string name = null)
             : base(name) { }

+ 3 - 3
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexColumns.cs

@@ -67,10 +67,10 @@ namespace SharpGLTF.Geometry.VertexTypes
             return cctt;
         }
 
-        public TvJ GetJointsFragment<TvJ>(int index)
-            where TvJ : struct, IVertexJoints
+        public JvS GetJointsFragment<JvS>(int index)
+            where JvS : struct, IVertexSkinning
         {
-            var jjjj = default(TvJ);
+            var jjjj = default(JvS);
 
             if (Joints0 != null && Weights0 != null) jjjj.SetJoints(0, Joints0[index], Weights0[index]);
             if (Joints1 != null && Weights1 != null) jjjj.SetJoints(1, Joints1[index], Weights1[index]);

+ 2 - 2
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexEmpty.cs

@@ -5,7 +5,7 @@ using System.Text;
 
 namespace SharpGLTF.Geometry.VertexTypes
 {
-    public struct VertexEmpty : IVertexMaterial, IVertexJoints
+    public struct VertexEmpty : IVertexMaterial, IVertexSkinning
     {
         void IVertexMaterial.SetColor(int setIndex, Vector4 color)
         {
@@ -15,7 +15,7 @@ namespace SharpGLTF.Geometry.VertexTypes
         {
         }
 
-        void IVertexJoints.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
+        void IVertexSkinning.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
         {
         }
 

+ 89 - 0
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexMaterial.cs

@@ -99,4 +99,93 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (!TexCoord._IsReal()) throw new NotFiniteNumberException(nameof(TexCoord));
         }
     }
+
+    /// <summary>
+    /// Defines a Vertex attribute with a Color material and two Texture Coordinates.
+    /// </summary>
+    public struct VertexColor1Texture2 : IVertexMaterial
+    {
+        public VertexColor1Texture2(Vector4 color, Vector2 tex0, Vector2 tex1)
+        {
+            Color = color;
+            TexCoord0 = tex0;
+            TexCoord1 = tex1;
+        }
+
+        [VertexAttribute("COLOR_0", Schema2.EncodingType.UNSIGNED_BYTE, true)]
+        public Vector4 Color;
+
+        [VertexAttribute("TEXCOORD_0")]
+        public Vector2 TexCoord0;
+
+        [VertexAttribute("TEXCOORD_1")]
+        public Vector2 TexCoord1;
+
+        void IVertexMaterial.SetColor(int setIndex, Vector4 color) { if (setIndex == 0) this.Color = color; }
+
+        void IVertexMaterial.SetTexCoord(int setIndex, Vector2 coord)
+        {
+            if (setIndex == 0) this.TexCoord0 = coord;
+            if (setIndex == 1) this.TexCoord1 = coord;
+        }
+
+        public void Validate()
+        {
+            if (!Color._IsReal()) throw new NotFiniteNumberException(nameof(Color));
+            if (!Color.IsInRange(Vector4.Zero, Vector4.One)) throw new IndexOutOfRangeException(nameof(Color));
+
+            if (!TexCoord0._IsReal()) throw new NotFiniteNumberException(nameof(TexCoord0));
+            if (!TexCoord1._IsReal()) throw new NotFiniteNumberException(nameof(TexCoord1));
+        }
+    }
+
+    /// <summary>
+    /// Defines a Vertex attribute with a Color material and two Texture Coordinates.
+    /// </summary>
+    public struct VertexColor2Texture2 : IVertexMaterial
+    {
+        public VertexColor2Texture2(Vector4 color0, Vector4 color1, Vector2 tex0, Vector2 tex1)
+        {
+            Color0 = color0;
+            Color1 = color1;
+            TexCoord0 = tex0;
+            TexCoord1 = tex1;
+        }
+
+        [VertexAttribute("COLOR_0", Schema2.EncodingType.UNSIGNED_BYTE, true)]
+        public Vector4 Color0;
+
+        [VertexAttribute("COLOR_1", Schema2.EncodingType.UNSIGNED_BYTE, true)]
+        public Vector4 Color1;
+
+        [VertexAttribute("TEXCOORD_0")]
+        public Vector2 TexCoord0;
+
+        [VertexAttribute("TEXCOORD_1")]
+        public Vector2 TexCoord1;
+
+        void IVertexMaterial.SetColor(int setIndex, Vector4 color)
+        {
+            if (setIndex == 0) this.Color0 = color;
+            if (setIndex == 1) this.Color1 = color;
+        }
+
+        void IVertexMaterial.SetTexCoord(int setIndex, Vector2 coord)
+        {
+            if (setIndex == 0) this.TexCoord0 = coord;
+            if (setIndex == 1) this.TexCoord1 = coord;
+        }
+
+        public void Validate()
+        {
+            if (!Color0._IsReal()) throw new NotFiniteNumberException(nameof(Color0));
+            if (!Color0.IsInRange(Vector4.Zero, Vector4.One)) throw new IndexOutOfRangeException(nameof(Color0));
+
+            if (!Color1._IsReal()) throw new NotFiniteNumberException(nameof(Color1));
+            if (!Color1.IsInRange(Vector4.Zero, Vector4.One)) throw new IndexOutOfRangeException(nameof(Color1));
+
+            if (!TexCoord0._IsReal()) throw new NotFiniteNumberException(nameof(TexCoord0));
+            if (!TexCoord1._IsReal()) throw new NotFiniteNumberException(nameof(TexCoord1));
+        }
+    }
 }

+ 9 - 9
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexJoints.cs → src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexSkinning.cs

@@ -5,7 +5,7 @@ using System.Text;
 
 namespace SharpGLTF.Geometry.VertexTypes
 {
-    public interface IVertexJoints
+    public interface IVertexSkinning
     {
         void SetJoints(int jointSet, Vector4 joints, Vector4 weights);
 
@@ -15,7 +15,7 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// <summary>
     /// Defines a Vertex attribute with up to 256 bone joints and 4 weights.
     /// </summary>
-    public struct VertexJoints8x4 : IVertexJoints
+    public struct VertexJoints8x4 : IVertexSkinning
     {
         #region constructors
 
@@ -45,7 +45,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #region API
 
-        void IVertexJoints.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
+        void IVertexSkinning.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
         {
             if (jointSet == 0) { this.Joints = joints; this.Weights = weights; }
         }
@@ -64,7 +64,7 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// <summary>
     /// Defines a Vertex attribute with up to 65535 bone joints and 4 weights.
     /// </summary>
-    public struct VertexJoints16x4 : IVertexJoints
+    public struct VertexJoints16x4 : IVertexSkinning
     {
         #region constructors
 
@@ -94,7 +94,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #region API
 
-        void IVertexJoints.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
+        void IVertexSkinning.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
         {
             if (jointSet == 0) { this.Joints = joints; this.Weights = weights; }
         }
@@ -113,7 +113,7 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// <summary>
     /// Defines a Vertex attribute with up to 256 bone joints and 8 weights.
     /// </summary>
-    public struct VertexJoints8x8 : IVertexJoints
+    public struct VertexJoints8x8 : IVertexSkinning
     {
         #region constructors
 
@@ -153,7 +153,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #region API
 
-        void IVertexJoints.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
+        void IVertexSkinning.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
         {
             if (jointSet == 0) { this.Joints0 = joints; this.Weights0 = weights; }
             if (jointSet == 1) { this.Joints1 = joints; this.Weights1 = weights; }
@@ -177,7 +177,7 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// <summary>
     /// Defines a Vertex attribute with up to 65535 bone joints and 8 weights.
     /// </summary>
-    public struct VertexJoints16x8 : IVertexJoints
+    public struct VertexJoints16x8 : IVertexSkinning
     {
         #region constructors
 
@@ -217,7 +217,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #region API
 
-        void IVertexJoints.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
+        void IVertexSkinning.SetJoints(int jointSet, Vector4 joints, Vector4 weights)
         {
             if (jointSet == 0) { this.Joints0 = joints; this.Weights0 = weights; }
             if (jointSet == 1) { this.Joints1 = joints; this.Weights1 = weights; }

+ 15 - 15
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexUtils.cs

@@ -9,16 +9,16 @@ namespace SharpGLTF.Geometry.VertexTypes
 
     static class VertexUtils
     {
-        public static IEnumerable<MemoryAccessor[]> CreateVertexMemoryAccessors<TvP, TvM, TvJ>(this IEnumerable<IReadOnlyList<(TvP, TvM, TvJ)>> vertexBlocks)
+        public static IEnumerable<MemoryAccessor[]> CreateVertexMemoryAccessors<TvP, TvM, JvS>(this IEnumerable<IReadOnlyList<(TvP, TvM, JvS)>> vertexBlocks)
             where TvP : struct, IVertexPosition
             where TvM : struct, IVertexMaterial
-            where TvJ : struct, IVertexJoints
+            where JvS : struct, IVertexSkinning
         {
             // total number of vertices
             var totalCount = vertexBlocks.Sum(item => item.Count);
 
             // vertex attributes
-            var attributes = GetVertexAttributes(typeof(TvP), typeof(TvM), typeof(TvJ), totalCount);
+            var attributes = GetVertexAttributes(typeof(TvP), typeof(TvM), typeof(JvS), totalCount);
 
             // create master vertex buffer
             int byteStride = attributes[0].ByteStride;
@@ -35,7 +35,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
                 foreach (var accessor in accessors)
                 {
-                    var columnFunc = GetItemValueFunc<TvP, TvM, TvJ>(accessor.Attribute.Name);
+                    var columnFunc = GetItemValueFunc<TvP, TvM, JvS>(accessor.Attribute.Name);
 
                     if (accessor.Attribute.Dimensions == Schema2.DimensionType.SCALAR) accessor.Fill(block.GetScalarColumn(columnFunc));
                     if (accessor.Attribute.Dimensions == Schema2.DimensionType.VEC2) accessor.Fill(block.GetVector2Column(columnFunc));
@@ -139,7 +139,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             return new MemoryAccessInfo(attribute.Name, 0, 0, 0, dimensions.Value, attribute.Encoding, attribute.Normalized);
         }
 
-        private static Func<(TvP, TvM, TvJ), Object> GetItemValueFunc<TvP, TvM, TvJ>(string attributeName)
+        private static Func<(TvP, TvM, JvS), Object> GetItemValueFunc<TvP, TvM, JvS>(string attributeName)
         {
             var finfo = GetVertexField(typeof(TvP), attributeName);
             if (finfo != null) return vertex => finfo.GetValue(vertex.Item1);
@@ -147,33 +147,33 @@ namespace SharpGLTF.Geometry.VertexTypes
             finfo = GetVertexField(typeof(TvM), attributeName);
             if (finfo != null) return vertex => finfo.GetValue(vertex.Item2);
 
-            finfo = GetVertexField(typeof(TvJ), attributeName);
+            finfo = GetVertexField(typeof(JvS), attributeName);
             if (finfo != null) return vertex => finfo.GetValue(vertex.Item3);
 
             throw new NotImplementedException();
         }
 
-        private static Single[] GetScalarColumn<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static Single[] GetScalarColumn<TvP, TvM, JvS>(this IReadOnlyList<(TvP, TvM, JvS)> vertices, Func<(TvP, TvM, JvS), Object> func)
         {
-            return GetColumn<TvP, TvM, TvJ, Single>(vertices, func);
+            return GetColumn<TvP, TvM, JvS, Single>(vertices, func);
         }
 
-        private static Vector2[] GetVector2Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static Vector2[] GetVector2Column<TvP, TvM, JvS>(this IReadOnlyList<(TvP, TvM, JvS)> vertices, Func<(TvP, TvM, JvS), Object> func)
         {
-            return GetColumn<TvP, TvM, TvJ, Vector2>(vertices, func);
+            return GetColumn<TvP, TvM, JvS, Vector2>(vertices, func);
         }
 
-        private static Vector3[] GetVector3Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static Vector3[] GetVector3Column<TvP, TvM, JvS>(this IReadOnlyList<(TvP, TvM, JvS)> vertices, Func<(TvP, TvM, JvS), Object> func)
         {
-            return GetColumn<TvP, TvM, TvJ, Vector3>(vertices, func);
+            return GetColumn<TvP, TvM, JvS, Vector3>(vertices, func);
         }
 
-        private static Vector4[] GetVector4Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static Vector4[] GetVector4Column<TvP, TvM, JvS>(this IReadOnlyList<(TvP, TvM, JvS)> vertices, Func<(TvP, TvM, JvS), Object> func)
         {
-            return GetColumn<TvP, TvM, TvJ, Vector4>(vertices, func);
+            return GetColumn<TvP, TvM, JvS, Vector4>(vertices, func);
         }
 
-        private static TColumn[] GetColumn<TvP, TvM, TvJ, TColumn>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static TColumn[] GetColumn<TvP, TvM, JvS, TColumn>(this IReadOnlyList<(TvP, TvM, JvS)> vertices, Func<(TvP, TvM, JvS), Object> func)
         {
             var dst = new TColumn[vertices.Count];
 

+ 56 - 0
src/SharpGLTF.Toolkit/Geometry/VertexTypes/readme.md

@@ -0,0 +1,56 @@
+# Toolkit Vertex API
+
+#### Overview
+
+glTF descrives a vertex buffer as a collection of accessors, where each accessor is described
+with an attribute, a dimension and an encoding.
+
+Also, glTF theoretically allows a huge range of combinations, but in practice, only a very
+narrow number of combinations is valid. this means that glTF runtimes need to carefully
+check the buffers layout.
+
+In order to simplify mesh creation, SharpGLTF Toolkit introduces the concept of
+vertex fragments, not in the sense of OpenGL shaders, but as components that can form
+a full vertex in a vertex buffer.
+
+The table below shows the possible combinations:
+
+|Position|Material|Skinning _(maxBones×weights)_|
+|-|
+|Position|Empty|Empty|
+|Position Normal|Color0|256 ×4
+|Position Normal Tangent|Texture0|256 ×8
+||Color0 Texture0|65536 ×4
+||Color0 Texture0 Texture1|65536 ×8
+||Color0 Color1 Texture0 Texture1|
+
+By using these predefined building blocks, declaring a vertex structure can be
+greatly simplified.
+
+#### Implementation
+
+SharpGLTF Toolkit defines these vertex fragment types
+in `SharpGLTF.Geometry.VertexTypes` namespace:
+  
+- Position
+  - `VertexPosition`
+  - `VertexPositionNormal`
+  - `VertexPositionNormalTangent`
+- Material
+  - `VertexEmpty`
+  - `VertexColor1`
+  - `VertexTexture1`
+  - `VertexColor1Texture1`
+  - `VertexColor1Texture2`
+  - `VertexColor2Texture2`
+- Skinning
+  - `VertexEmpty`
+  - `VertexJoints8x4`
+  - `VertexJoints8x8`
+  - `VertexJoints16x4`
+  - `VertexJoints16x8`
+
+#### Appendix
+
+- [MeshBuilder documentation](../readme.md)
+

+ 95 - 0
src/SharpGLTF.Toolkit/Geometry/readme.md

@@ -0,0 +1,95 @@
+# Toolkit Mesh API
+
+#### Overview
+
+Creating meshes from scratch accessing directly with `SharpGLTF.Schema2`
+namespace API is quite complicated, since it implies creating Buffers,
+Accessors, Primitives and Meshes very carefully.
+
+It's even more complicated if we want to optimize the meshes by arrange
+the vertices in an interleaved layour, or even allowing multiple meshes
+to share a Vertex and Index buffer.
+
+As an alternative to filling meshes directly into the glTF schema,
+SharpGLTF Toolkit provides a MeshBuilder class to simplify mesh creation.
+
+#### Implementation
+
+The most useful class for mesh creation is `class MeshBuilder<TvP, TvM, TvS>`
+where:
+
+- TvP is a Position Vertex Fragment structure.
+- TvM is a Material Vertex Fragment structure.
+- TvS is a Skinning Vertex Fragment structure.
+
+Vertex fragment types are described in [here](VertexTypes/readme.md).
+
+```c#
+// define a Mesh Builder with a Position and a Color.
+var mesh = new MeshBuilder<VertexPosition, VertexColor1, VertexEmpty>("mesh");
+```
+
+`MeshBuilder` groups primitives using a material as a dictionary key.
+The base implementation of `MeshBuilder` allows to pass a templated type as a
+material key:
+
+```c#
+// define a MeshBuilder with "string" material and a vertex with Position and a Color.
+var mesh = new MeshBuilder<String, VertexPosition, VertexColor1, VertexEmpty>("mesh");
+
+mesh.UsePrimitive("Material1").AddTriangle((V1,C1), (V2,C2), (V3,C3));
+mesh.UsePrimitive("Material2").AddTriangle((V1,C1), (V3,C3), (V4,C4));
+```
+
+But the default implementation of `MeshBuilder` uses `MaterialBuilder` as described
+[here](../Materials/readme.md).
+
+```c#
+// define a material:
+var material = new MaterialBuilder("material");
+
+// define a MeshBuilder with a vertex with Position and a Color.
+var mesh = new MeshBuilder<VertexPosition, VertexColor1, VertexEmpty>("mesh");
+
+mesh.UsePrimitive(material).AddTriangle((V1,C1), (V2,C2), (V3,C3));
+mesh.UsePrimitive(material).AddTriangle((V1,C1), (V3,C3), (V4,C4));
+```
+
+Finally, in order to convert a MeshBuilder to a glTF mesh:
+
+```c#
+var mesh1 = new MeshBuilder<VertexPosition, VertexColor1, VertexEmpty>("mesh1");
+// fill mesh1 with geometry
+var mesh2 = new MeshBuilder<VertexPosition, VertexColor1, VertexEmpty>("mesh2");
+// fill mesh2 with geometry
+
+var model = Schema2.ModelRoot.CreateModel();
+
+model.CreateMeshes(mesh1, mesh2);
+```
+
+The interesting thing about `CreateMeshes` is that it is able to batch all the meshes and
+its primitives in a single vertex and index buffer, provided they use equivalent vertex
+structures.
+
+#### Skinning support
+
+Skinning attributes are provided by the third vertex fragment in the `MeshBuilder` definition.
+
+If no skinning is required, a `VertexEmpty` placeholder is used instead.
+
+
+#### Morphing support
+
+Not supported yet.
+
+#### Appendix
+
+- [Vertex Fragments documentation](VertexTypes/readme.md)
+- [Material Builder documentation](../Materials/readme.md)
+
+
+
+
+
+

+ 6 - 2
src/SharpGLTF.Toolkit/Materials/readme.md

@@ -3,7 +3,7 @@
 #### Overview
 
 glTF materials specification has a rather complex architecture,
-with a number of speciallised objects, plus a good number of extensions.
+with a number of specialized objects, plus a good number of extensions.
 
 In order to streamline and simplify access to materials, a Material Build
 API is provided.
@@ -23,4 +23,8 @@ element has a different meaning, depending on the kind of channel:
 |BaseColor|Metallic Roughness & Unlit|Red|Green|Blue|Alpha
 |MetallicRoughness|Metallic Roughness|Metallic Factor|Roughness Factor
 |Diffuse|Specular Glossiness|Diffuse Red|Diffuse Green|Diffuse Blue|Alpha
-|SpecularGlossiness|Specular Glossiness|Specular Red|Specular Green|Specular Blue|Glossiness
+|SpecularGlossiness|Specular Glossiness|Specular Red|Specular Green|Specular Blue|Glossiness
+
+#### Implementation
+
+TODO

+ 24 - 24
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -12,50 +12,50 @@ namespace SharpGLTF.Schema2
     {
         #region meshes
 
-        public static Mesh CreateMesh<TvP, TvM, TvJ>(this ModelRoot root, Geometry.MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvJ> meshBuilder)
+        public static Mesh CreateMesh<TvP, TvM, JvS>(this ModelRoot root, Geometry.MeshBuilder<Materials.MaterialBuilder, TvP, TvM, JvS> meshBuilder)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             return root.CreateMeshes(meshBuilder).First();
         }
 
-        public static Mesh CreateMesh<TvP, TvM, TvJ>(this ModelRoot root, Geometry.MeshBuilder<Material, TvP, TvM, TvJ> meshBuilder)
+        public static Mesh CreateMesh<TvP, TvM, JvS>(this ModelRoot root, Geometry.MeshBuilder<Material, TvP, TvM, JvS> meshBuilder)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             return root.CreateMeshes(meshBuilder).First();
         }
 
-        public static Mesh CreateMesh<TMaterial, TvP, TvM, TvJ>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, Geometry.MeshBuilder<TMaterial, TvP, TvM, TvJ> meshBuilder)
+        public static Mesh CreateMesh<TMaterial, TvP, TvM, JvS>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, Geometry.MeshBuilder<TMaterial, TvP, TvM, JvS> meshBuilder)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             return root.CreateMeshes(materialEvaluator, meshBuilder).First();
         }
 
-        public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, TvJ>(this ModelRoot root, params Geometry.MeshBuilder<Material, TvP, TvM, TvJ>[] meshBuilders)
+        public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, JvS>(this ModelRoot root, params Geometry.MeshBuilder<Material, TvP, TvM, JvS>[] meshBuilders)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             return root.CreateMeshes(m => m, meshBuilders);
         }
 
-        public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, TvJ>(this ModelRoot root, params Geometry.MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvJ>[] meshBuilders)
+        public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, JvS>(this ModelRoot root, params Geometry.MeshBuilder<Materials.MaterialBuilder, TvP, TvM, JvS>[] meshBuilders)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             return root.CreateMeshes(mb => root.CreateMaterial(mb), meshBuilders);
         }
 
-        public static IReadOnlyList<Mesh> CreateMeshes<TMaterial, TvP, TvM, TvJ>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, params Geometry.MeshBuilder<TMaterial, TvP, TvM, TvJ>[] meshBuilders)
+        public static IReadOnlyList<Mesh> CreateMeshes<TMaterial, TvP, TvM, JvS>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, params Geometry.MeshBuilder<TMaterial, TvP, TvM, JvS>[] meshBuilders)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             // create a new material for every unique material in the mesh builders.
             var mapMaterials = meshBuilders
@@ -226,7 +226,7 @@ namespace SharpGLTF.Schema2
         public static MeshPrimitive WithVertexAccessors<TVertex, TValues, TJoints>(this MeshPrimitive primitive, IReadOnlyList<(TVertex, TValues, TJoints)> vertices)
             where TVertex : struct, Geometry.VertexTypes.IVertexPosition
             where TValues : struct, Geometry.VertexTypes.IVertexMaterial
-            where TJoints : struct, Geometry.VertexTypes.IVertexJoints
+            where TJoints : struct, Geometry.VertexTypes.IVertexSkinning
         {
             var memAccessors = Geometry.VertexTypes.VertexUtils.CreateVertexMemoryAccessors(new[] { vertices }).First();
 
@@ -277,20 +277,20 @@ namespace SharpGLTF.Schema2
 
         #region evaluation
 
-        public static IEnumerable<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)> Triangulate<TvP, TvM, TvJ>(this Mesh mesh, Matrix4x4 xform)
+        public static IEnumerable<((TvP, TvM, JvS), (TvP, TvM, JvS), (TvP, TvM, JvS), Material)> Triangulate<TvP, TvM, JvS>(this Mesh mesh, Matrix4x4 xform)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             var normals = mesh.GetComputedNormals();
 
-            return mesh.Primitives.SelectMany(item => item.Triangulate<TvP, TvM, TvJ>(xform, normals));
+            return mesh.Primitives.SelectMany(item => item.Triangulate<TvP, TvM, JvS>(xform, normals));
         }
 
-        public static IEnumerable<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)> Triangulate<TvP, TvM, TvJ>(this MeshPrimitive prim, Matrix4x4 xform, IReadOnlyDictionary<Vector3, Vector3> defaultNormals)
+        public static IEnumerable<((TvP, TvM, JvS), (TvP, TvM, JvS), (TvP, TvM, JvS), Material)> Triangulate<TvP, TvM, JvS>(this MeshPrimitive prim, Matrix4x4 xform, IReadOnlyDictionary<Vector3, Vector3> defaultNormals)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             var vertices = prim.GetVertexColumns();
             if (vertices.Normals == null && defaultNormals != null) vertices.SetNormals(defaultNormals);
@@ -311,9 +311,9 @@ namespace SharpGLTF.Schema2
                 var bm = vertices.GetMaterialFragment<TvM>(t.Item2);
                 var cm = vertices.GetMaterialFragment<TvM>(t.Item3);
 
-                var aj = vertices.GetJointsFragment<TvJ>(t.Item1);
-                var bj = vertices.GetJointsFragment<TvJ>(t.Item2);
-                var cj = vertices.GetJointsFragment<TvJ>(t.Item3);
+                var aj = vertices.GetJointsFragment<JvS>(t.Item1);
+                var bj = vertices.GetJointsFragment<JvS>(t.Item2);
+                var cj = vertices.GetJointsFragment<JvS>(t.Item3);
 
                 yield return ((ap, am, aj), (bp, bm, bj), (cp, cm, cj), prim.Material);
             }
@@ -391,10 +391,10 @@ namespace SharpGLTF.Schema2
             return posnrm;
         }
 
-        public static void AddMesh<TMaterial, TvP, TvM, TvJ>(this Geometry.MeshBuilder<TMaterial, TvP, TvM, TvJ> meshBuilder, Mesh srcMesh, Matrix4x4 xform, Func<Material, TMaterial> materialFunc)
+        public static void AddMesh<TMaterial, TvP, TvM, JvS>(this Geometry.MeshBuilder<TMaterial, TvP, TvM, JvS> meshBuilder, Mesh srcMesh, Matrix4x4 xform, Func<Material, TMaterial> materialFunc)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             var normals = srcMesh.GetComputedNormals();
 
@@ -402,7 +402,7 @@ namespace SharpGLTF.Schema2
             {
                 var dstPrim = meshBuilder.UsePrimitive(materialFunc(srcPrim.Material));
 
-                foreach (var tri in srcPrim.Triangulate<TvP, TvM, TvJ>(xform, normals))
+                foreach (var tri in srcPrim.Triangulate<TvP, TvM, JvS>(xform, normals))
                 {
                     dstPrim.AddTriangle(tri.Item1, tri.Item2, tri.Item3);
                 }

+ 9 - 9
src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs

@@ -87,15 +87,15 @@ namespace SharpGLTF.Schema2
         /// </summary>
         /// <typeparam name="TvP">The vertex fragment type with Position, Normal and Tangent.</typeparam>
         /// <typeparam name="TvM">The vertex fragment type with Colors and Texture Coordinates.</typeparam>
-        /// <typeparam name="TvJ">The vertex fragment type with Skin Joint Weights.</typeparam>
+        /// <typeparam name="JvS">The vertex fragment type with Skin Joint Weights.</typeparam>
         /// <param name="scene">A <see cref="Scene"/> instance.</param>
         /// <returns>A collection of triangles in world space.</returns>
-        public static IEnumerable<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)> Triangulate<TvP, TvM, TvJ>(this Scene scene)
+        public static IEnumerable<((TvP, TvM, JvS), (TvP, TvM, JvS), (TvP, TvM, JvS), Material)> Triangulate<TvP, TvM, JvS>(this Scene scene)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
-            return Node.Flatten(scene).SelectMany(item => item.Triangulate<TvP, TvM, TvJ>(true));
+            return Node.Flatten(scene).SelectMany(item => item.Triangulate<TvP, TvM, JvS>(true));
         }
 
         /// <summary>
@@ -104,21 +104,21 @@ namespace SharpGLTF.Schema2
         /// </summary>
         /// <typeparam name="TvP">The vertex fragment type with Position, Normal and Tangent.</typeparam>
         /// <typeparam name="TvM">The vertex fragment type with Colors and Texture Coordinates.</typeparam>
-        /// <typeparam name="TvJ">The vertex fragment type with Skin Joint Weights.</typeparam>
+        /// <typeparam name="JvS">The vertex fragment type with Skin Joint Weights.</typeparam>
         /// <param name="node">A <see cref="Node"/> instance.</param>
         /// <param name="inWorldSpace">A value indicating whether the returned triangles must be in local (false) or world (true) space.</param>
         /// <returns>A collection of triangles in local or world space.</returns>
-        public static IEnumerable<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)> Triangulate<TvP, TvM, TvJ>(this Node node, bool inWorldSpace)
+        public static IEnumerable<((TvP, TvM, JvS), (TvP, TvM, JvS), (TvP, TvM, JvS), Material)> Triangulate<TvP, TvM, JvS>(this Node node, bool inWorldSpace)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+            where JvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             var mesh = node.Mesh;
-            if (mesh == null) return Enumerable.Empty<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)>();
+            if (mesh == null) return Enumerable.Empty<((TvP, TvM, JvS), (TvP, TvM, JvS), (TvP, TvM, JvS), Material)>();
 
             var xform = inWorldSpace ? node.WorldMatrix : Matrix4x4.Identity;
 
-            return mesh.Triangulate<TvP, TvM, TvJ>(xform);
+            return mesh.Triangulate<TvP, TvM, JvS>(xform);
         }
 
         #endregion

+ 31 - 1
tests/SharpGLTF.Tests/AssemblyAPITests.cs

@@ -13,7 +13,37 @@ namespace SharpGLTF
     {
         public class TestClass
         {
-            public int xyz(int x, out int y, ref int z) { y = 0; return x + 7; }
+            public int PointerFunc(int x, out int y, ref int z) { y = 0; return x + 7; }
+
+            public void ParamsFunc(int x, params string[] values) { }
+
+            public void MultiArgsFunc(int x, int y=1, int z = 2) { }
+
+            public int[][,][,,] MultiArray;
+
+            public const string Alpha = "Alpha";
+
+            public static readonly string Beta = "Beta";
+
+            public delegate void MyDelegate(int x);
+
+            public MyDelegate DelegateImpl;
+
+            public enum Hello
+            {
+                World = 7
+            }
+
+            public event EventHandler EventX;
+
+            public struct Structure
+            {
+                public int X;
+                public int Y { get; }
+                public int Z { get; set; }
+
+                public System.Numerics.Vector3 Vector;
+            }
         }
 
         [Test]

+ 80 - 73
tests/SharpGLTF.Tests/DumpAssemblyAPI.cs

@@ -9,30 +9,16 @@ using static System.FormattableString;
 namespace SharpGLTF
 {
     /// <summary>
-    /// Utility class to hopefully compare breaking changes between APIs
+    /// Utility class to dump the public API of an assembly
     /// </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=""
-
-        // NS foo.bar { STRUCT name        { FIELD name type       } }
-        // NS foo.bar { ABSTRACTCLASS name { PROPERTYGET name type } }
-        // NS foo.bar { ABSTRACTCLASS name { PROTECTEDPROPERTYSET name type } }
-
-        // NamesPace { Class|Struct { Class|Struct|Method|Field } }
-
-
         public static IEnumerable<String> GetAssemblySignature(Assembly assembly)
         {
             return assembly.ExportedTypes
-                .SelectMany(item => GetTypeSignature(item.GetTypeInfo()).Select(l => "NS " + item.Namespace + " { " + l + " } " ) );
+                .SelectMany(item => GetTypeSignature(item.GetTypeInfo()).Select(l => "NS " + item.Namespace + " { " + l + " }" ) );
         }
 
         public static IEnumerable<String> GetTypeSignature(TypeInfo tinfo)
@@ -40,7 +26,7 @@ namespace SharpGLTF
             if (tinfo.IsNestedPrivate) yield break;
             if (tinfo.IsNestedAssembly) yield break;
 
-            string baseName = string.Empty;            
+            string baseName = string.Empty;
 
             if (tinfo.IsInterface) baseName = "INTERFACE";
             else if (tinfo.IsEnum) baseName = "ENUM";
@@ -61,82 +47,90 @@ namespace SharpGLTF
 
             baseName += " " + tinfo.GetQualifiedName();
 
+            if (tinfo.IsEnum)
+            {
+                foreach(var val in Enum.GetValues(tinfo.AsType()))
+                {
+                    yield return baseName + " { " + val + $"={(int)val}" + " }";
+                }
+
+                yield break;
+            }
+
             if (tinfo.IsClass)
             {
                 // base classes
                 var baseType = tinfo.BaseType;
                 while (baseType != null && baseType != typeof(object))
                 {
-                    yield return baseName + " { USING " + baseType.GetQualifiedName() + " } ";
+                    yield return baseName + " { USING " + baseType.GetQualifiedName() + " }";
                     baseType = baseType.BaseType;
                 }
             }
-
-            foreach(var ifaceType in tinfo.GetInterfaces())
+            
+            foreach (var ifaceType in tinfo.GetInterfaces())
             {
                 if (ifaceType.IsNotPublic) continue;
 
-                yield return baseName + " { USING " + ifaceType.GetQualifiedName() + " } ";
+                yield return baseName + " { USING " + ifaceType.GetQualifiedName() + " }";
             }
 
-
-            Object instance = null;
-
-            try { instance = Activator.CreateInstance(tinfo); }
-            catch { }
-
             foreach (var m in tinfo.DeclaredMembers)
             {
-                var signatures = GetMemberSignature(instance, m);
+                var signatures = GetMemberSignature(m);
                 if (signatures == null) continue;
                 foreach (var s in signatures)
                 {
-                    yield return baseName + " { " +  s + " } ";
+                    yield return baseName + " { " +  s + " }";
                 }
             }            
         }
 
-        public static IEnumerable<string> GetMemberSignature(Object instance, MemberInfo minfo)
+        public static IEnumerable<String> GetMemberSignature(MemberInfo minfo)
         {
-            if (minfo is FieldInfo finfo) return GetFieldSignature(instance, finfo);
+            if (minfo is FieldInfo finfo) return GetFieldSignature(finfo);
 
             if (minfo is TypeInfo tinfo) return GetTypeSignature(tinfo);
 
-            if (minfo is PropertyInfo pinfo) return GetPropertySignature(instance, pinfo);
+            if (minfo is PropertyInfo pinfo) return GetPropertySignature(pinfo);
 
-            if (minfo is MethodInfo xinfo) return GetMethodSignature(instance, xinfo);
+            if (minfo is MethodInfo xinfo) return GetMethodSignature(xinfo);
 
-            if (minfo is ConstructorInfo cinfo) return GetMethodSignature(instance, cinfo);
+            if (minfo is ConstructorInfo cinfo) return GetMethodSignature(cinfo);
+
+            if (minfo is EventInfo einfo) return GetEventSignature(einfo);
 
             return null;
         }
 
-        public static IEnumerable<string> GetFieldSignature(Object instance, FieldInfo finfo)
+        public static IEnumerable<String> GetFieldSignature(FieldInfo finfo)
         {
             if (!IsVisible(finfo)) yield break;
 
             var name = "FIELD";
 
             if (finfo.IsLiteral) name += ":CONST";
-            if (finfo.IsStatic) name += ":STATIC";
+            else if (finfo.IsStatic) name += ":STATIC";
+
+            if (finfo.IsInitOnly) name += ":READONLY";
 
             name += $" {finfo.Name} {finfo.FieldType.GetQualifiedName()}";
 
-            if (finfo.IsStatic)
-            {
-                var v = finfo.GetValue(null);
-                if (v != null) name += Invariant($"={v}");
-            }
-            else if (instance != null)
-            {
-                var v = finfo.GetValue(instance);
-                if (v != null) name += Invariant($"={v}");
-            }
+            yield return name;
+        }
+
+        public static IEnumerable<String> GetEventSignature(EventInfo einfo)
+        {
+            // if (!IsVisible(einfo)) yield break;
+
+            var name = "EVENT";
+
+            name += $" {einfo.Name} {einfo.EventHandlerType.GetQualifiedName()}";
 
             yield return name;
         }
 
-        public static IEnumerable<string> GetPropertySignature(Object instance, PropertyInfo pinfo)
+        public static IEnumerable<String> GetPropertySignature(PropertyInfo pinfo)
         {
             var pname = $"{pinfo.Name} {pinfo.PropertyType.GetQualifiedName()}";
 
@@ -147,10 +141,8 @@ namespace SharpGLTF
             if (IsVisible(setter, true)) yield return "METHOD:SET"+ GetMethodModifiers(setter) +" " + pname;
         }
 
-        public static IEnumerable<string> GetMethodSignature(Object instance, MethodBase minfo)
+        public static IEnumerable<String> GetMethodSignature(MethodBase minfo)
         {
-            // TODO: if parameters have default values, dump the same method multiple times with one parameter less each time.
-
             string mname = "METHOD";
 
             if (minfo is MethodInfo mminfo)
@@ -158,7 +150,7 @@ namespace SharpGLTF
                 var isExtension = mminfo.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), true);
 
                 if (isExtension) mname += ":EXTENSION";
-                mname += GetMethodModifiers(minfo);
+                else mname += GetMethodModifiers(minfo);
 
                 mname += " ";
 
@@ -181,7 +173,7 @@ namespace SharpGLTF
             yield return mname + "(" + string.Join(", ", mparams) + ")";
         }
 
-        public static string GetParameterSignature(ParameterInfo pinfo)
+        public static String GetParameterSignature(ParameterInfo pinfo)
         {
             var prefix = string.Empty;
 
@@ -194,21 +186,18 @@ namespace SharpGLTF
                 else prefix += "ref ";
             }
 
-            return prefix + pinfo.ParameterType.GetQualifiedName();
-        }
+            var postfix = string.Empty;
 
-        public static bool IsVisible(FieldInfo finfo)
-        {
-            if (finfo == null) return false;
-
-            if (finfo.IsPrivate) return false;
-            if (finfo.IsAssembly) return false;            
-            if (finfo.IsFamilyOrAssembly) return false;
+            if (pinfo.HasDefaultValue)
+            {
+                if (pinfo.DefaultValue == null) postfix = "=null";
+                else postfix = "=" + pinfo.DefaultValue.ToString();
+            }
 
-            return true;
-        }
+            return prefix + pinfo.ParameterType.GetQualifiedName() + postfix;
+        }        
 
-        public static bool IsVisible(MethodBase minfo, bool withSpecials = false)
+        public static Boolean IsVisible(MethodBase minfo, Boolean withSpecials = false)
         {
             if (minfo == null) return false;
 
@@ -219,9 +208,20 @@ namespace SharpGLTF
             if (minfo.IsFamilyOrAssembly) return false;
 
             return true;
-        }        
+        }
 
-        public static string GetMethodModifiers(MethodBase minfo)
+        public static Boolean IsVisible(FieldInfo finfo)
+        {
+            if (finfo == null) return false;
+
+            if (finfo.IsPrivate) return false;
+            if (finfo.IsAssembly) return false;
+            if (finfo.IsFamilyOrAssembly) return false;
+
+            return true;
+        }
+
+        public static String GetMethodModifiers(MethodBase minfo)
         {
             if (minfo.IsPrivate) return string.Empty;
 
@@ -236,24 +236,31 @@ namespace SharpGLTF
             return mod;
         }
 
-        public static string GetQualifiedName(this Type tinfo)
+        public static String GetQualifiedName(this Type tinfo)
         {
             return tinfo.GetTypeInfo().GetQualifiedName();
         }
 
-        public static string GetQualifiedName(this System.Reflection.TypeInfo tinfo)
+        public static String GetQualifiedName(this TypeInfo tinfo)
         {
-            if (tinfo.IsArray)
+            var postfix = string.Empty;
+
+            // unwrap jagged array
+            while (tinfo.IsArray)
             {
-                var itemName = tinfo.GetElementType().GetQualifiedName();
-                itemName += "[" + string.Join("", Enumerable.Repeat(",", tinfo.GetArrayRank() - 1)) + "]";
-                return itemName;
+                postfix += "[" + string.Join("", Enumerable.Repeat(",", tinfo.GetArrayRank() - 1)) + "]";
+                tinfo = tinfo.GetElementType().GetTypeInfo();                
             }
 
             var name = tinfo.Name;
 
-            name = name.Replace("&", ""); // remove PTR semantics
+            // remove pointer semantics
+            name = name.Replace("&", ""); 
+
+            // add jagged array postfix
+            name += postfix;
 
+            // handle generic types
             if (tinfo.IsGenericType || tinfo.IsGenericTypeDefinition)
             {
                 name = name.Replace("`1", "");