Browse Source

MeshBuilder porgress...

Vicente Penades 6 years ago
parent
commit
a53ed74d80

+ 24 - 0
src/Shared/_Extensions.cs

@@ -69,6 +69,30 @@ namespace SharpGLTF
             return q.IsNormalized() ? q : Quaternion.Normalize(q);
             return q.IsNormalized() ? q : Quaternion.Normalize(q);
         }
         }
 
 
+        internal static bool IsInRange(this Vector3 value, Vector3 min, Vector3 max)
+        {
+            if (value.X < min.X || value.X > max.X) return false;
+            if (value.Y < min.Y || value.Y > max.Y) return false;
+            if (value.Z < min.Z || value.Z > max.Z) return false;
+            return true;
+        }
+
+        internal static bool IsInRange(this Vector4 value, Vector4 min, Vector4 max)
+        {
+            if (value.X < min.X || value.X > max.X) return false;
+            if (value.Y < min.Y || value.Y > max.Y) return false;
+            if (value.Z < min.Z || value.Z > max.Z) return false;
+            if (value.W < min.W || value.W > max.W) return false;
+            return true;
+        }
+
+        internal static bool IsRound(this Vector4 value)
+        {
+            var r = new Vector4((int)value.X, (int)value.Y, (int)value.Z, (int)value.W);
+
+            return (value - r) == Vector4.Zero;
+        }
+
         #endregion
         #endregion
 
 
         #region linq
         #region linq

+ 113 - 56
src/SharpGLTF.Toolkit/Geometry/MeshBuilder.cs

@@ -6,11 +6,40 @@ using System.Linq;
 namespace SharpGLTF.Geometry
 namespace SharpGLTF.Geometry
 {
 {
     using Collections;
     using Collections;
-
+    using VertexTypes;
+
+    /// <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="TvP">
+    /// The vertex fragment type with Position, Normal and Tangent.
+    /// Valid types are:
+    /// <see cref="VertexPosition"/>,
+    /// <see cref="VertexPositionNormal"/>,
+    /// <see cref="VertexPositionNormalTangent"/>.
+    /// </typeparam>
+    /// <typeparam name="TvM">
+    /// The vertex fragment type with Colors and Texture Coordinates.
+    /// Valid types are:
+    /// <see cref="VertexEmpty"/>,
+    /// <see cref="VertexColor1"/>,
+    /// <see cref="VertexTexture1"/>,
+    /// <see cref="VertexColor1Texture1"/>.
+    /// </typeparam>
+    /// <typeparam name="TvJ">
+    /// The vertex fragment type with Skin Joint Weights.
+    /// Valid types are:
+    /// <see cref="VertexEmpty"/>,
+    /// <see cref="VertexJoints8x4"/>,
+    /// <see cref="VertexJoints8x8"/>,
+    /// <see cref="VertexJoints16x4"/>,
+    /// <see cref="VertexJoints16x8"/>.
+    /// </typeparam>
     public class PrimitiveBuilder<TMaterial, TvP, TvM, TvJ>
     public class PrimitiveBuilder<TMaterial, TvP, TvM, TvJ>
-        where TvP : struct, VertexTypes.IVertexPosition
-        where TvM : struct, VertexTypes.IVertexMaterial
-        where TvJ : struct, VertexTypes.IVertexJoints
+        where TvP : struct, IVertexPosition
+        where TvM : struct, IVertexMaterial
+        where TvJ : struct, IVertexJoints
     {
     {
         #region lifecycle
         #region lifecycle
 
 
@@ -91,6 +120,53 @@ namespace SharpGLTF.Geometry
             _Indices.Add(cc);
             _Indices.Add(cc);
         }
         }
 
 
+        public void AddTriangle((TvP, TvM) a, (TvP, TvM) b, (TvP, TvM) c)
+        {
+            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)
+        {
+            AddTriangle((a.Item1, default, a.Item2), (b.Item1, default, b.Item2), (c.Item1, default, c.Item2));
+        }
+
+        public void AddTriangle(TvP a, TvP b, TvP c)
+        {
+            AddTriangle((a, default, default), (b, default, default), (c, default, default));
+        }
+
+        public void AddPolygon(params (TvP, TvM, TvJ)[] points)
+        {
+            for (int i = 2; i < points.Length; ++i)
+            {
+                AddTriangle(points[0], points[i - 1], points[i]);
+            }
+        }
+
+        public void AddPolygon(params (TvP, TvM)[] points)
+        {
+            for (int i = 2; i < points.Length; ++i)
+            {
+                AddTriangle(points[0], points[i - 1], points[i]);
+            }
+        }
+
+        public void AddPolygon(params (TvP, TvJ)[] points)
+        {
+            for (int i = 2; i < points.Length; ++i)
+            {
+                AddTriangle(points[0], points[i - 1], points[i]);
+            }
+        }
+
+        public void AddPolygon(params TvP[] points)
+        {
+            for (int i = 2; i < points.Length; ++i)
+            {
+                AddTriangle(points[0], points[i - 1], points[i]);
+            }
+        }
+
         public void Validate()
         public void Validate()
         {
         {
             foreach (var v in _Vertices)
             foreach (var v in _Vertices)
@@ -104,10 +180,38 @@ namespace SharpGLTF.Geometry
         #endregion
         #endregion
     }
     }
 
 
+    /// <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="TvP">
+    /// The vertex fragment type with Position, Normal and Tangent.
+    /// Valid types are:
+    /// <see cref="VertexPosition"/>,
+    /// <see cref="VertexPositionNormal"/>,
+    /// <see cref="VertexPositionNormalTangent"/>.
+    /// </typeparam>
+    /// <typeparam name="TvM">
+    /// The vertex fragment type with Colors and Texture Coordinates.
+    /// Valid types are:
+    /// <see cref="VertexEmpty"/>,
+    /// <see cref="VertexColor1"/>,
+    /// <see cref="VertexTexture1"/>,
+    /// <see cref="VertexColor1Texture1"/>.
+    /// </typeparam>
+    /// <typeparam name="TvJ">
+    /// The vertex fragment type with Skin Joint Weights.
+    /// Valid types are:
+    /// <see cref="VertexEmpty"/>,
+    /// <see cref="VertexJoints8x4"/>,
+    /// <see cref="VertexJoints8x8"/>,
+    /// <see cref="VertexJoints16x4"/>,
+    /// <see cref="VertexJoints16x8"/>.
+    /// </typeparam>
     public class MeshBuilder<TMaterial, TvP, TvM, TvJ>
     public class MeshBuilder<TMaterial, TvP, TvM, TvJ>
-        where TvP : struct, VertexTypes.IVertexPosition
-        where TvM : struct, VertexTypes.IVertexMaterial
-        where TvJ : struct, VertexTypes.IVertexJoints
+        where TvP : struct, IVertexPosition
+        where TvM : struct, IVertexMaterial
+        where TvJ : struct, IVertexJoints
     {
     {
         #region lifecycle
         #region lifecycle
 
 
@@ -136,39 +240,7 @@ namespace SharpGLTF.Geometry
 
 
         #region API
         #region API
 
 
-        public void AddPolygon(TMaterial material, params (TvP, TvM, TvJ)[] points)
-        {
-            for (int i = 2; i < points.Length; ++i)
-            {
-                AddTriangle(material, points[0], points[i - 1], points[i]);
-            }
-        }
-
-        public void AddPolygon(TMaterial material, params (TvP, TvM)[] points)
-        {
-            for (int i = 2; i < points.Length; ++i)
-            {
-                AddTriangle(material, points[0], points[i - 1], points[i]);
-            }
-        }
-
-        public void AddPolygon(TMaterial material, params (TvP, TvJ)[] points)
-        {
-            for (int i = 2; i < points.Length; ++i)
-            {
-                AddTriangle(material, points[0], points[i - 1], points[i]);
-            }
-        }
-
-        public void AddPolygon(TMaterial material, params TvP[] points)
-        {
-            for (int i = 2; i < points.Length; ++i)
-            {
-                AddTriangle(material, points[0], points[i - 1], points[i]);
-            }
-        }
-
-        public void AddTriangle(TMaterial material, (TvP, TvM, TvJ) a, (TvP, TvM, TvJ) b, (TvP, TvM, TvJ) c)
+        public PrimitiveBuilder<TMaterial, TvP, TvM, TvJ> UsePrimitive(TMaterial material)
         {
         {
             if (!_Primitives.TryGetValue(material, out PrimitiveBuilder<TMaterial, TvP, TvM, TvJ> primitive))
             if (!_Primitives.TryGetValue(material, out PrimitiveBuilder<TMaterial, TvP, TvM, TvJ> primitive))
             {
             {
@@ -176,22 +248,7 @@ namespace SharpGLTF.Geometry
                 _Primitives[material] = primitive;
                 _Primitives[material] = primitive;
             }
             }
 
 
-            primitive.AddTriangle(a, b, c);
-        }
-
-        public void AddTriangle(TMaterial material, (TvP, TvM) a, (TvP, TvM) b, (TvP, TvM) c)
-        {
-            AddTriangle(material, (a.Item1, a.Item2, default), (b.Item1, b.Item2, default), (c.Item1, c.Item2, default));
-        }
-
-        public void AddTriangle(TMaterial material, (TvP, TvJ) a, (TvP, TvJ) b, (TvP, TvJ) c)
-        {
-            AddTriangle(material, (a.Item1, default, a.Item2), (b.Item1, default, b.Item2), (c.Item1, default, c.Item2));
-        }
-
-        public void AddTriangle(TMaterial material, TvP a, TvP b, TvP c)
-        {
-            AddTriangle(material, (a, default, default), (b, default, default), (c, default, default));
+            return primitive;
         }
         }
 
 
         public IEnumerable<(int, int, int)> GetTriangles(TMaterial material)
         public IEnumerable<(int, int, int)> GetTriangles(TMaterial material)

+ 35 - 5
src/SharpGLTF.Toolkit/Geometry/StaticMeshBuilder.cs

@@ -6,16 +6,46 @@ using System.Text;
 
 
 namespace SharpGLTF.Geometry
 namespace SharpGLTF.Geometry
 {
 {
-    public class MeshBuilder<TMaterial, TVertex, TValues> : MeshBuilder<TMaterial, TVertex, TValues, VertexTypes.VertexEmpty>
-        where TVertex : struct, VertexTypes.IVertexPosition
-        where TValues : struct, VertexTypes.IVertexMaterial
+    /// <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="TvP">
+    /// The vertex fragment type with Position, Normal and Tangent.
+    /// Valid types are:
+    /// <see cref="VertexTypes.VertexPosition"/>,
+    /// <see cref="VertexTypes.VertexPositionNormal"/>,
+    /// <see cref="VertexTypes.VertexPositionNormalTangent"/>.
+    /// </typeparam>
+    /// <typeparam name="TvM">
+    /// The vertex fragment type with Colors and Texture Coordinates.
+    /// Valid types are:
+    /// <see cref="VertexTypes.VertexEmpty"/>,
+    /// <see cref="VertexTypes.VertexColor1"/>,
+    /// <see cref="VertexTypes.VertexTexture1"/>,
+    /// <see cref="VertexTypes.VertexColor1Texture1"/>.
+    /// </typeparam>
+    public class MeshBuilder<TMaterial, TvP, TvM> : MeshBuilder<TMaterial, TvP, TvM, VertexTypes.VertexEmpty>
+        where TvP : struct, VertexTypes.IVertexPosition
+        where TvM : struct, VertexTypes.IVertexMaterial
     {
     {
         public MeshBuilder(string name = null)
         public MeshBuilder(string name = null)
             : base(name) { }
             : base(name) { }
     }
     }
 
 
-    public class MeshBuilder<TMaterial, TVertex> : MeshBuilder<TMaterial, TVertex, VertexTypes.VertexEmpty, VertexTypes.VertexEmpty>
-       where TVertex : struct, VertexTypes.IVertexPosition
+    /// <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="TvP">
+    /// The vertex fragment type with Position, Normal and Tangent.
+    /// Valid types are:
+    /// <see cref="VertexTypes.VertexPosition"/>,
+    /// <see cref="VertexTypes.VertexPositionNormal"/>,
+    /// <see cref="VertexTypes.VertexPositionNormalTangent"/>.
+    /// </typeparam>
+    public class MeshBuilder<TMaterial, TvP> : MeshBuilder<TMaterial, TvP, VertexTypes.VertexEmpty, VertexTypes.VertexEmpty>
+       where TvP : struct, VertexTypes.IVertexPosition
     {
     {
         public MeshBuilder(string name = null)
         public MeshBuilder(string name = null)
             : base(name) { }
             : base(name) { }

+ 96 - 6
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexJoints.cs

@@ -10,15 +10,18 @@ namespace SharpGLTF.Geometry.VertexTypes
         void Validate();
         void Validate();
     }
     }
 
 
-    public struct VertexJoints4 : IVertexJoints
+    /// <summary>
+    /// Defines a Vertex attribute with up to 256 bone joints and 4 weights.
+    /// </summary>
+    public struct VertexJoints8x4 : IVertexJoints
     {
     {
-        public VertexJoints4(int jointIndex)
+        public VertexJoints8x4(int jointIndex)
         {
         {
             Joints = new Vector4(jointIndex);
             Joints = new Vector4(jointIndex);
             Weights = Vector4.UnitX;
             Weights = Vector4.UnitX;
         }
         }
 
 
-        public VertexJoints4(int jointIndex1, int jointIndex2)
+        public VertexJoints8x4(int jointIndex1, int jointIndex2)
         {
         {
             Joints = new Vector4(jointIndex1, jointIndex2, 0, 0);
             Joints = new Vector4(jointIndex1, jointIndex2, 0, 0);
             Weights = new Vector4(0.5f, 0.5f, 0, 0);
             Weights = new Vector4(0.5f, 0.5f, 0, 0);
@@ -33,13 +36,50 @@ namespace SharpGLTF.Geometry.VertexTypes
         public void Validate()
         public void Validate()
         {
         {
             if (!Joints._IsReal()) throw new NotFiniteNumberException(nameof(Joints));
             if (!Joints._IsReal()) throw new NotFiniteNumberException(nameof(Joints));
+            if (!Joints.IsRound() || !Joints.IsInRange(Vector4.Zero, new Vector4(255))) throw new IndexOutOfRangeException(nameof(Joints));
+
+            if (!Weights._IsReal()) throw new NotFiniteNumberException(nameof(Weights));
+        }
+    }
+
+    /// <summary>
+    /// Defines a Vertex attribute with up to 65535 bone joints and 4 weights.
+    /// </summary>
+    public struct VertexJoints16x4 : IVertexJoints
+    {
+        public VertexJoints16x4(int jointIndex)
+        {
+            Joints = new Vector4(jointIndex);
+            Weights = Vector4.UnitX;
+        }
+
+        public VertexJoints16x4(int jointIndex1, int jointIndex2)
+        {
+            Joints = new Vector4(jointIndex1, jointIndex2, 0, 0);
+            Weights = new Vector4(0.5f, 0.5f, 0, 0);
+        }
+
+        [VertexAttribute("JOINTS_0", Schema2.EncodingType.UNSIGNED_SHORT, false)]
+        public Vector4 Joints;
+
+        [VertexAttribute("WEIGHTS_0", Schema2.EncodingType.UNSIGNED_BYTE, true)]
+        public Vector4 Weights;
+
+        public void Validate()
+        {
+            if (!Joints._IsReal()) throw new NotFiniteNumberException(nameof(Joints));
+            if (!Joints.IsRound() || !Joints.IsInRange(Vector4.Zero, new Vector4(65535))) throw new IndexOutOfRangeException(nameof(Joints));
+
             if (!Weights._IsReal()) throw new NotFiniteNumberException(nameof(Weights));
             if (!Weights._IsReal()) throw new NotFiniteNumberException(nameof(Weights));
         }
         }
     }
     }
 
 
-    public struct VertexJoints8 : IVertexJoints
+    /// <summary>
+    /// Defines a Vertex attribute with up to 256 bone joints and 8 weights.
+    /// </summary>
+    public struct VertexJoints8x8 : IVertexJoints
     {
     {
-        public VertexJoints8(int jointIndex)
+        public VertexJoints8x8(int jointIndex)
         {
         {
             Joints0 = new Vector4(jointIndex);
             Joints0 = new Vector4(jointIndex);
             Joints1 = new Vector4(jointIndex);
             Joints1 = new Vector4(jointIndex);
@@ -47,7 +87,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             Weights1 = Vector4.Zero;
             Weights1 = Vector4.Zero;
         }
         }
 
 
-        public VertexJoints8(int jointIndex1, int jointIndex2)
+        public VertexJoints8x8(int jointIndex1, int jointIndex2)
         {
         {
             Joints0 = new Vector4(jointIndex1, jointIndex2, 0, 0);
             Joints0 = new Vector4(jointIndex1, jointIndex2, 0, 0);
             Joints1 = Vector4.Zero;
             Joints1 = Vector4.Zero;
@@ -72,6 +112,56 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (!Joints0._IsReal()) throw new NotFiniteNumberException(nameof(Joints0));
             if (!Joints0._IsReal()) throw new NotFiniteNumberException(nameof(Joints0));
             if (!Joints1._IsReal()) throw new NotFiniteNumberException(nameof(Joints1));
             if (!Joints1._IsReal()) throw new NotFiniteNumberException(nameof(Joints1));
 
 
+            if (!Joints0.IsRound() || !Joints0.IsInRange(Vector4.Zero, new Vector4(255))) throw new IndexOutOfRangeException(nameof(Joints0));
+            if (!Joints1.IsRound() || !Joints1.IsInRange(Vector4.Zero, new Vector4(255))) throw new IndexOutOfRangeException(nameof(Joints1));
+
+            if (!Weights0._IsReal()) throw new NotFiniteNumberException(nameof(Weights0));
+            if (!Weights1._IsReal()) throw new NotFiniteNumberException(nameof(Weights1));
+        }
+
+    }
+
+    /// <summary>
+    /// Defines a Vertex attribute with up to 65535 bone joints and 8 weights.
+    /// </summary>
+    public struct VertexJoints16x8 : IVertexJoints
+    {
+        public VertexJoints16x8(int jointIndex)
+        {
+            Joints0 = new Vector4(jointIndex);
+            Joints1 = new Vector4(jointIndex);
+            Weights0 = Vector4.UnitX;
+            Weights1 = Vector4.Zero;
+        }
+
+        public VertexJoints16x8(int jointIndex1, int jointIndex2)
+        {
+            Joints0 = new Vector4(jointIndex1, jointIndex2, 0, 0);
+            Joints1 = Vector4.Zero;
+            Weights0 = new Vector4(0.5f, 0.5f, 0, 0);
+            Weights1 = Vector4.Zero;
+        }
+
+        [VertexAttribute("JOINTS_0", Schema2.EncodingType.UNSIGNED_SHORT, false)]
+        public Vector4 Joints0;
+
+        [VertexAttribute("JOINTS_1", Schema2.EncodingType.UNSIGNED_SHORT, false)]
+        public Vector4 Joints1;
+
+        [VertexAttribute("WEIGHTS_0", Schema2.EncodingType.UNSIGNED_BYTE, true)]
+        public Vector4 Weights0;
+
+        [VertexAttribute("WEIGHTS_1", Schema2.EncodingType.UNSIGNED_BYTE, true)]
+        public Vector4 Weights1;
+
+        public void Validate()
+        {
+            if (!Joints0._IsReal()) throw new NotFiniteNumberException(nameof(Joints0));
+            if (!Joints1._IsReal()) throw new NotFiniteNumberException(nameof(Joints1));
+
+            if (!Joints0.IsRound() || !Joints0.IsInRange(Vector4.Zero, new Vector4(65535))) throw new IndexOutOfRangeException(nameof(Joints0));
+            if (!Joints1.IsRound() || !Joints1.IsInRange(Vector4.Zero, new Vector4(65535))) throw new IndexOutOfRangeException(nameof(Joints1));
+
             if (!Weights0._IsReal()) throw new NotFiniteNumberException(nameof(Weights0));
             if (!Weights0._IsReal()) throw new NotFiniteNumberException(nameof(Weights0));
             if (!Weights1._IsReal()) throw new NotFiniteNumberException(nameof(Weights1));
             if (!Weights1._IsReal()) throw new NotFiniteNumberException(nameof(Weights1));
         }
         }

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

@@ -10,6 +10,9 @@ namespace SharpGLTF.Geometry.VertexTypes
         void Validate();
         void Validate();
     }
     }
 
 
+    /// <summary>
+    /// Defines a Vertex attribute with a Color material.
+    /// </summary>
     public struct VertexColor1 : IVertexMaterial
     public struct VertexColor1 : IVertexMaterial
     {
     {
         public VertexColor1(Vector4 color)
         public VertexColor1(Vector4 color)
@@ -28,9 +31,13 @@ namespace SharpGLTF.Geometry.VertexTypes
         public void Validate()
         public void Validate()
         {
         {
             if (!Color._IsReal()) throw new NotFiniteNumberException(nameof(Color));
             if (!Color._IsReal()) throw new NotFiniteNumberException(nameof(Color));
+            if (!Color.IsInRange(Vector4.Zero, Vector4.One)) throw new IndexOutOfRangeException(nameof(Color));
         }
         }
     }
     }
 
 
+    /// <summary>
+    /// Defines a Vertex attribute with a Texture Coordinate.
+    /// </summary>
     public struct VertexTexture1 : IVertexMaterial
     public struct VertexTexture1 : IVertexMaterial
     {
     {
         public VertexTexture1(Vector2 uv)
         public VertexTexture1(Vector2 uv)
@@ -52,6 +59,9 @@ namespace SharpGLTF.Geometry.VertexTypes
         }
         }
     }
     }
 
 
+    /// <summary>
+    /// Defines a Vertex attribute with a Color material and a Texture Coordinate.
+    /// </summary>
     public struct VertexColor1Texture1 : IVertexMaterial
     public struct VertexColor1Texture1 : IVertexMaterial
     {
     {
         public VertexColor1Texture1(Vector4 color, Vector2 tex)
         public VertexColor1Texture1(Vector4 color, Vector2 tex)
@@ -69,6 +79,8 @@ namespace SharpGLTF.Geometry.VertexTypes
         public void Validate()
         public void Validate()
         {
         {
             if (!Color._IsReal()) throw new NotFiniteNumberException(nameof(Color));
             if (!Color._IsReal()) throw new NotFiniteNumberException(nameof(Color));
+            if (!Color.IsInRange(Vector4.Zero, Vector4.One)) throw new IndexOutOfRangeException(nameof(Color));
+
             if (!TexCoord._IsReal()) throw new NotFiniteNumberException(nameof(TexCoord));
             if (!TexCoord._IsReal()) throw new NotFiniteNumberException(nameof(TexCoord));
         }
         }
     }
     }

+ 14 - 0
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexPosition.cs

@@ -10,6 +10,9 @@ namespace SharpGLTF.Geometry.VertexTypes
         void Validate();
         void Validate();
     }
     }
 
 
+    /// <summary>
+    /// Defines a Vertex attribute with a Position.
+    /// </summary>
     public struct VertexPosition : IVertexPosition
     public struct VertexPosition : IVertexPosition
     {
     {
         public VertexPosition(Vector3 position)
         public VertexPosition(Vector3 position)
@@ -36,6 +39,9 @@ namespace SharpGLTF.Geometry.VertexTypes
         }
         }
     }
     }
 
 
+    /// <summary>
+    /// Defines a Vertex attribute with a Position and a Normal.
+    /// </summary>
     public struct VertexPositionNormal : IVertexPosition
     public struct VertexPositionNormal : IVertexPosition
     {
     {
         public VertexPositionNormal(Vector3 p, Vector3 n)
         public VertexPositionNormal(Vector3 p, Vector3 n)
@@ -63,6 +69,9 @@ namespace SharpGLTF.Geometry.VertexTypes
         }
         }
     }
     }
 
 
+    /// <summary>
+    /// Defines a Vertex attribute with a Position, a Normal and a Tangent.
+    /// </summary>
     public struct VertexPositionNormalTangent : IVertexPosition
     public struct VertexPositionNormalTangent : IVertexPosition
     {
     {
         public VertexPositionNormalTangent(Vector3 p, Vector3 n, Vector4 t)
         public VertexPositionNormalTangent(Vector3 p, Vector3 n, Vector4 t)
@@ -87,5 +96,10 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (!Normal._IsReal()) throw new NotFiniteNumberException(nameof(Normal));
             if (!Normal._IsReal()) throw new NotFiniteNumberException(nameof(Normal));
             if (!Tangent._IsReal()) throw new NotFiniteNumberException(nameof(Tangent));
             if (!Tangent._IsReal()) throw new NotFiniteNumberException(nameof(Tangent));
         }
         }
+
+        void IVertexPosition.Validate()
+        {
+            throw new NotImplementedException();
+        }
     }
     }
 }
 }

+ 6 - 7
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexUtils.cs

@@ -2,13 +2,12 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Numerics;
 using System.Numerics;
-using System.Text;
 
 
 namespace SharpGLTF.Geometry.VertexTypes
 namespace SharpGLTF.Geometry.VertexTypes
 {
 {
     using Memory;
     using Memory;
 
 
-    public static class VertexUtils
+    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, TvJ>(this IEnumerable<IReadOnlyList<(TvP, TvM, TvJ)>> vertexBlocks)
             where TvP : struct, IVertexPosition
             where TvP : struct, IVertexPosition
@@ -154,27 +153,27 @@ namespace SharpGLTF.Geometry.VertexTypes
             throw new NotImplementedException();
             throw new NotImplementedException();
         }
         }
 
 
-        internal static Single[] GetScalarColumn<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static Single[] GetScalarColumn<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
         {
         {
             return GetColumn<TvP, TvM, TvJ, Single>(vertices, func);
             return GetColumn<TvP, TvM, TvJ, Single>(vertices, func);
         }
         }
 
 
-        internal static Vector2[] GetVector2Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static Vector2[] GetVector2Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
         {
         {
             return GetColumn<TvP, TvM, TvJ, Vector2>(vertices, func);
             return GetColumn<TvP, TvM, TvJ, Vector2>(vertices, func);
         }
         }
 
 
-        internal static Vector3[] GetVector3Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static Vector3[] GetVector3Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
         {
         {
             return GetColumn<TvP, TvM, TvJ, Vector3>(vertices, func);
             return GetColumn<TvP, TvM, TvJ, Vector3>(vertices, func);
         }
         }
 
 
-        internal static Vector4[] GetVector4Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
+        private static Vector4[] GetVector4Column<TvP, TvM, TvJ>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
         {
         {
             return GetColumn<TvP, TvM, TvJ, Vector4>(vertices, func);
             return GetColumn<TvP, TvM, TvJ, Vector4>(vertices, func);
         }
         }
 
 
-        internal 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, TvJ, TColumn>(this IReadOnlyList<(TvP, TvM, TvJ)> vertices, Func<(TvP, TvM, TvJ), Object> func)
         {
         {
             var dst = new TColumn[vertices.Count];
             var dst = new TColumn[vertices.Count];
 
 

+ 47 - 39
tests/SharpGLTF.Tests/Schema2/Authoring/CreateModelTests.cs

@@ -11,9 +11,9 @@ namespace SharpGLTF.Schema2.Authoring
     using Geometry;
     using Geometry;
 
 
     using VEMPTY = Geometry.VertexTypes.VertexEmpty;
     using VEMPTY = Geometry.VertexTypes.VertexEmpty;
-    using STATICVERTEX = Geometry.VertexTypes.VertexPositionNormal;
+    using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;
     using VPOS = Geometry.VertexTypes.VertexPosition;
     using VPOS = Geometry.VertexTypes.VertexPosition;
-    using SKIN4 = Geometry.VertexTypes.VertexJoints4;
+    using VSKIN4 = Geometry.VertexTypes.VertexJoints8x4;
 
 
     [TestFixture]
     [TestFixture]
     public class CreateModelTests
     public class CreateModelTests
@@ -217,10 +217,10 @@ namespace SharpGLTF.Schema2.Authoring
 
 
             var vertices = new[]
             var vertices = new[]
             {
             {
-                new STATICVERTEX(-10,  10, 0, -10,  10, 15),
-                new STATICVERTEX( 10,  10, 0,  10,  10, 15),
-                new STATICVERTEX( 10, -10, 0,  10, -10, 15),
-                new STATICVERTEX(-10, -10, 0, -10, -10, 15)
+                new VPOSNRM(-10,  10, 0, -10,  10, 15),
+                new VPOSNRM( 10,  10, 0,  10,  10, 15),
+                new VPOSNRM( 10, -10, 0,  10, -10, 15),
+                new VPOSNRM(-10, -10, 0, -10, -10, 15)
             };
             };
 
 
             var model = ModelRoot.CreateModel();
             var model = ModelRoot.CreateModel();
@@ -245,13 +245,13 @@ namespace SharpGLTF.Schema2.Authoring
             TestContext.CurrentContext.AttachShowDirLink();
             TestContext.CurrentContext.AttachShowDirLink();
             TestContext.CurrentContext.AttachGltfValidatorLink();
             TestContext.CurrentContext.AttachGltfValidatorLink();
 
 
-            var meshBuilder = new MeshBuilder<Vector4, STATICVERTEX>("mesh1");
+            var meshBuilder = new MeshBuilder<Vector4, VPOSNRM>("mesh1");
 
 
-            var v1 = new STATICVERTEX(-10, 10, 0, -10, 10, 15);
-            var v2 = new STATICVERTEX( 10, 10, 0, 10, 10, 15);
-            var v3 = new STATICVERTEX( 10,-10, 0, 10, -10, 15);
-            var v4 = new STATICVERTEX(-10,-10, 0, -10, -10, 15);            
-            meshBuilder.AddPolygon(Vector4.One, v1, v2, v3, v4);
+            var v1 = new VPOSNRM(-10, 10, 0, -10, 10, 15);
+            var v2 = new VPOSNRM( 10, 10, 0, 10, 10, 15);
+            var v3 = new VPOSNRM( 10,-10, 0, 10, -10, 15);
+            var v4 = new VPOSNRM(-10,-10, 0, -10, -10, 15);            
+            meshBuilder.UsePrimitive(Vector4.One).AddPolygon(v1, v2, v3, v4);
 
 
             var model = ModelRoot.CreateModel();
             var model = ModelRoot.CreateModel();
 
 
@@ -273,16 +273,21 @@ namespace SharpGLTF.Schema2.Authoring
             TestContext.CurrentContext.AttachGltfValidatorLink();
             TestContext.CurrentContext.AttachGltfValidatorLink();
 
 
             // create several meshes
             // create several meshes
-            var meshBuilder1 = new MeshBuilder<Vector4, STATICVERTEX>("mesh1");
-            var meshBuilder2 = new MeshBuilder<Vector4, STATICVERTEX>("mesh2");
-            var meshBuilder3 = new MeshBuilder<Vector4, STATICVERTEX>("mesh3");
-            var meshBuilder4 = new MeshBuilder<Vector4, STATICVERTEX>("mesh4");
+            var meshBuilder1 = new MeshBuilder<Vector4, VPOSNRM>("mesh1");
+            var meshBuilder2 = new MeshBuilder<Vector4, VPOSNRM>("mesh2");
+            var meshBuilder3 = new MeshBuilder<Vector4, VPOSNRM>("mesh3");
+            var meshBuilder4 = new MeshBuilder<Vector4, VPOSNRM>("mesh4");
 
 
             meshBuilder1.AddCube(new Vector4(1, 1, 0, 1), Matrix4x4.Identity);
             meshBuilder1.AddCube(new Vector4(1, 1, 0, 1), Matrix4x4.Identity);
             meshBuilder2.AddCube(new Vector4(1, 0, 1, 1), Matrix4x4.Identity);
             meshBuilder2.AddCube(new Vector4(1, 0, 1, 1), Matrix4x4.Identity);
             meshBuilder3.AddSphere(new Vector4(0, 1, 1, 1), 0.5f, Matrix4x4.Identity);
             meshBuilder3.AddSphere(new Vector4(0, 1, 1, 1), 0.5f, Matrix4x4.Identity);
             meshBuilder4.AddSphere(new Vector4(1, 1, 0, 1), 0.5f, Matrix4x4.Identity);
             meshBuilder4.AddSphere(new Vector4(1, 1, 0, 1), 0.5f, Matrix4x4.Identity);
 
 
+            meshBuilder1.Validate();
+            meshBuilder2.Validate();
+            meshBuilder3.Validate();
+            meshBuilder4.Validate();
+
             // create the gltf model
             // create the gltf model
             var model = ModelRoot.CreateModel();
             var model = ModelRoot.CreateModel();
 
 
@@ -324,8 +329,9 @@ namespace SharpGLTF.Schema2.Authoring
             };
             };
 
 
             // create a mesh
             // create a mesh
-            var meshBuilder = new MeshBuilder<Vector4, STATICVERTEX>("mesh1");
-            meshBuilder.AddCube(Vector4.One, Matrix4x4.Identity);            
+            var meshBuilder = new MeshBuilder<Vector4, VPOSNRM>("mesh1");
+            meshBuilder.AddCube(Vector4.One, Matrix4x4.Identity);
+            meshBuilder.Validate();
 
 
             // create the gltf model
             // create the gltf model
             var model = ModelRoot.CreateModel();
             var model = ModelRoot.CreateModel();
@@ -355,35 +361,37 @@ namespace SharpGLTF.Schema2.Authoring
             };
             };
             
             
             // create the mesh
             // create the mesh
-            var meshBuilder = new MeshBuilder<Vector4, VPOS, VEMPTY, SKIN4>("mesh1");
+            var meshBuilder = new MeshBuilder<Vector4, VPOS, VEMPTY, VSKIN4>("mesh1");
 
 
-            var v1 = (new VPOS(-10, 0, +10), new SKIN4(0));
-            var v2 = (new VPOS(+10, 0, +10), new SKIN4(0));
-            var v3 = (new VPOS(+10, 0, -10), new SKIN4(0));
-            var v4 = (new VPOS(-10, 0, -10), new SKIN4(0));
+            var v1 = (new VPOS(-10, 0, +10), new VSKIN4(0));
+            var v2 = (new VPOS(+10, 0, +10), new VSKIN4(0));
+            var v3 = (new VPOS(+10, 0, -10), new VSKIN4(0));
+            var v4 = (new VPOS(-10, 0, -10), new VSKIN4(0));
 
 
-            var v5 = (new VPOS(-10, 40, +10), new SKIN4(0, 1));
-            var v6 = (new VPOS(+10, 40, +10), new SKIN4(0, 1));
-            var v7 = (new VPOS(+10, 40, -10), new SKIN4(0, 1));
-            var v8 = (new VPOS(-10, 40, -10), new SKIN4(0, 1));
+            var v5 = (new VPOS(-10, 40, +10), new VSKIN4(0, 1));
+            var v6 = (new VPOS(+10, 40, +10), new VSKIN4(0, 1));
+            var v7 = (new VPOS(+10, 40, -10), new VSKIN4(0, 1));
+            var v8 = (new VPOS(-10, 40, -10), new VSKIN4(0, 1));
 
 
-            var v9  = (new VPOS(-5, 80, +5), new SKIN4(2));
-            var v10 = (new VPOS(+5, 80, +5), new SKIN4(2));
-            var v11 = (new VPOS(+5, 80, -5), new SKIN4(2));
-            var v12 = (new VPOS(-5, 80, -5), new SKIN4(2));
+            var v9  = (new VPOS(-5, 80, +5), new VSKIN4(2));
+            var v10 = (new VPOS(+5, 80, +5), new VSKIN4(2));
+            var v11 = (new VPOS(+5, 80, -5), new VSKIN4(2));
+            var v12 = (new VPOS(-5, 80, -5), new VSKIN4(2));
 
 
             var pink = new Vector4(1, 0, 1, 1);
             var pink = new Vector4(1, 0, 1, 1);
             var yellow = new Vector4(1, 1, 0, 1);
             var yellow = new Vector4(1, 1, 0, 1);
 
 
-            meshBuilder.AddPolygon(pink, v1, v2, v6, v5);
-            meshBuilder.AddPolygon(pink, v2, v3, v7, v6);
-            meshBuilder.AddPolygon(pink, v3, v4, v8, v7);
-            meshBuilder.AddPolygon(pink, v4, v1, v5, v8);
+            meshBuilder.UsePrimitive(pink).AddPolygon(v1, v2, v6, v5);
+            meshBuilder.UsePrimitive(pink).AddPolygon(v2, v3, v7, v6);
+            meshBuilder.UsePrimitive(pink).AddPolygon(v3, v4, v8, v7);
+            meshBuilder.UsePrimitive(pink).AddPolygon(v4, v1, v5, v8);
+
+            meshBuilder.UsePrimitive(yellow).AddPolygon(v5, v6, v10, v9);
+            meshBuilder.UsePrimitive(yellow).AddPolygon(v6, v7, v11, v10);
+            meshBuilder.UsePrimitive(yellow).AddPolygon(v7, v8, v12, v11);
+            meshBuilder.UsePrimitive(yellow).AddPolygon(v8, v5, v9, v12);
 
 
-            meshBuilder.AddPolygon(yellow, v5, v6, v10, v9);
-            meshBuilder.AddPolygon(yellow, v6, v7, v11, v10);
-            meshBuilder.AddPolygon(yellow, v7, v8, v12, v11);
-            meshBuilder.AddPolygon(yellow, v8, v5, v9, v12);
+            meshBuilder.Validate();
 
 
             // create base model
             // create base model
             var model = ModelRoot.CreateModel();
             var model = ModelRoot.CreateModel();

+ 4 - 3
tests/SharpGLTF.Tests/Schema2/Authoring/SolidMeshUtils.cs

@@ -31,8 +31,9 @@ namespace SharpGLTF.Schema2.Authoring
             var p4 = Vector3.Transform(origin - axisX + axisY, xform);
             var p4 = Vector3.Transform(origin - axisX + axisY, xform);
             var n = Vector3.Normalize(Vector3.TransformNormal(origin, xform));
             var n = Vector3.Normalize(Vector3.TransformNormal(origin, xform));
 
 
-            meshBuilder.AddPolygon
-                (material,
+            meshBuilder.UsePrimitive(material)
+                .AddPolygon
+                (
                 new VPOSNRM(p1, n),
                 new VPOSNRM(p1, n),
                 new VPOSNRM(p2, n),
                 new VPOSNRM(p2, n),
                 new VPOSNRM(p3, n),
                 new VPOSNRM(p3, n),
@@ -98,7 +99,7 @@ namespace SharpGLTF.Schema2.Authoring
                 var bb = new VPOSNRM(Vector3.Transform(b, xform), Vector3.Normalize(Vector3.TransformNormal(b, xform)));
                 var bb = new VPOSNRM(Vector3.Transform(b, xform), Vector3.Normalize(Vector3.TransformNormal(b, xform)));
                 var cc = new VPOSNRM(Vector3.Transform(c, xform), Vector3.Normalize(Vector3.TransformNormal(c, xform)));
                 var cc = new VPOSNRM(Vector3.Transform(c, xform), Vector3.Normalize(Vector3.TransformNormal(c, xform)));
 
 
-                meshBuilder.AddTriangle(material, aa, bb, cc);
+                meshBuilder.UsePrimitive(material).AddTriangle(aa, bb, cc);
                 return;
                 return;
             }
             }
 
 

+ 1 - 1
tests/SharpGLTF.Tests/WavefrontWriter.cs

@@ -31,7 +31,7 @@ namespace SharpGLTF
             var bb = new POSITION { Position = b };
             var bb = new POSITION { Position = b };
             var cc = new POSITION { Position = c };
             var cc = new POSITION { Position = c };
 
 
-            _Mesh.AddTriangle(Vector4.One, aa, bb, cc);
+            _Mesh.UsePrimitive(Vector4.One).AddTriangle(aa, bb, cc);
         }
         }
 
 
         public override string ToString()
         public override string ToString()