Browse Source

Improving meshbuilder
adding examples

Vicente Penades 6 years ago
parent
commit
214c9a9840

+ 9 - 0
SharpGLTF.sln

@@ -23,6 +23,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{D7D51F42
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpGLTF.Toolkit", "src\SharpGLTF.Toolkit\SharpGLTF.Toolkit.csproj", "{41690879-1F91-4555-A40A-F67B01868D7E}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example1", "examples\Example1\Example1.csproj", "{68662AA0-8523-4B9E-9230-DE79F2B07EAB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{83E7E49D-8A28-45E8-9DBD-1F3AEDEF3E42}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -45,6 +49,10 @@ Global
 		{41690879-1F91-4555-A40A-F67B01868D7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{41690879-1F91-4555-A40A-F67B01868D7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{41690879-1F91-4555-A40A-F67B01868D7E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{68662AA0-8523-4B9E-9230-DE79F2B07EAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{68662AA0-8523-4B9E-9230-DE79F2B07EAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{68662AA0-8523-4B9E-9230-DE79F2B07EAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{68662AA0-8523-4B9E-9230-DE79F2B07EAB}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -54,6 +62,7 @@ Global
 		{B1DA4F42-AB6A-4021-9989-674B1394E8A2} = {072B725F-773F-4751-9616-E9778897C1D2}
 		{4FCBB910-67D4-4628-9B2B-F5F2C8D92257} = {0CBF510D-D836-40BA-95EC-E93FDBB90632}
 		{41690879-1F91-4555-A40A-F67B01868D7E} = {072B725F-773F-4751-9616-E9778897C1D2}
+		{68662AA0-8523-4B9E-9230-DE79F2B07EAB} = {83E7E49D-8A28-45E8-9DBD-1F3AEDEF3E42}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {1D7BBAD9-834C-4981-AC96-0AA5226FC43F}

+ 12 - 0
examples/Example1/Example1.csproj

@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.2</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\SharpGLTF.Toolkit\SharpGLTF.Toolkit.csproj" />
+  </ItemGroup>
+
+</Project>

+ 56 - 0
examples/Example1/Program.cs

@@ -0,0 +1,56 @@
+using System;
+using System.Numerics;
+
+using SharpGLTF.Geometry;
+using SharpGLTF.Materials;
+using SharpGLTF.Schema2;
+
+namespace Example1
+{
+    using VERTEX = SharpGLTF.Geometry.VertexTypes.VertexPosition;
+
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            // create two materials
+
+            var material1 = new MaterialBuilder()
+                .WithDoubleSide(true)
+                .WithMetallicRoughnessShader()
+                .WithChannelParam("BaseColor", new Vector4(1,0,0,1) );
+
+            var material2 = new MaterialBuilder()
+                .WithDoubleSide(true)
+                .WithMetallicRoughnessShader()
+                .WithChannelParam("BaseColor", new Vector4(1, 0, 1, 1));
+
+            // create a mesh with two primitives, one for each material
+
+            var mesh = new MeshBuilder<VERTEX>("mesh");
+
+            var prim = mesh.UsePrimitive(material1);
+            prim.AddTriangle(new VERTEX(-10, 0, 0), new VERTEX(10, 0, 0), new VERTEX(0, 10, 0));
+            prim.AddTriangle(new VERTEX(10, 0, 0), new VERTEX(-10, 0, 0), new VERTEX(0, -10, 0));
+
+            prim = mesh.UsePrimitive(material2);
+            prim.AddPolygon(new VERTEX(-5, 0, 3), new VERTEX(0, -5, 3), new VERTEX(5, 0, 3), new VERTEX(0, 5, 3));
+            
+            // create a new gltf model
+            var model = ModelRoot.CreateModel();
+
+            // add all meshes (just one in this case) to the model
+            model.CreateMeshes(mesh);
+
+            // create a scene, a node, and assign the first mesh
+            model.UseScene("Default")
+                .CreateNode()
+                .WithMesh(model.LogicalMeshes[0]);
+
+            // save the model in different formats
+            model.SaveAsWavefront("mesh.obj");
+            model.SaveGLB("mesh.glb");
+            model.SaveGLTF("mesh.gltf");
+        }
+    }
+}

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

@@ -51,7 +51,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexJoints16x8"/>.
     /// </typeparam>
     public class MeshBuilder<TMaterial, TvP, TvM, TvS> : IMeshBuilder<TMaterial>
-        where TvP : struct, IVertexPosition
+        where TvP : struct, IVertexGeometry
         where TvM : struct, IVertexMaterial
         where TvS : struct, IVertexSkinning
     {
@@ -160,7 +160,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexJoints16x8"/>.
     /// </typeparam>
     public class MeshBuilder<TvP, TvM, TvS> : MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvS>
-        where TvP : struct, IVertexPosition
+        where TvP : struct, IVertexGeometry
         where TvM : struct, IVertexMaterial
         where TvS : struct, IVertexSkinning
     {
@@ -187,7 +187,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexColor1Texture1"/>.
     /// </typeparam>
     public class MeshBuilder<TvP, TvM> : MeshBuilder<Materials.MaterialBuilder, TvP, TvM, VertexEmpty>
-        where TvP : struct, IVertexPosition
+        where TvP : struct, IVertexGeometry
         where TvM : struct, IVertexMaterial
     {
         public MeshBuilder(string name = null)
@@ -205,7 +205,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexPositionNormalTangent"/>.
     /// </typeparam>
     public class MeshBuilder<TvP> : MeshBuilder<Materials.MaterialBuilder, TvP, VertexEmpty, VertexEmpty>
-        where TvP : struct, IVertexPosition
+        where TvP : struct, IVertexGeometry
     {
         public MeshBuilder(string name = null)
             : base(name) { }

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

@@ -27,7 +27,7 @@ namespace SharpGLTF.Geometry
         /// <param name="meshBuilders">A collection of <see cref="MeshBuilder{TMaterial, TvP, TvM, TvS}"/> instances.</param>
         /// <returns>A collection of <see cref="PackedMeshBuilder{TMaterial}"/> instances.</returns>
         internal static IEnumerable<PackedMeshBuilder<TMaterial>> PackMeshes<TvP, TvM, TvS>(IEnumerable<MeshBuilder<TMaterial, TvP, TvM, TvS>> meshBuilders)
-            where TvP : struct, VertexTypes.IVertexPosition
+            where TvP : struct, VertexTypes.IVertexGeometry
             where TvM : struct, VertexTypes.IVertexMaterial
             where TvS : struct, VertexTypes.IVertexSkinning
         {

+ 39 - 82
src/SharpGLTF.Toolkit/Geometry/PrimitiveBuilder.cs

@@ -15,8 +15,8 @@ namespace SharpGLTF.Geometry
 
         int VertexCount { get; }
 
-        TvPP GetVertexPosition<TvPP>(int index)
-            where TvPP : struct, IVertexPosition;
+        TvPP GetVertexGeometry<TvPP>(int index)
+            where TvPP : struct, IVertexGeometry;
 
         TvMM GetVertexMaterial<TvMM>(int index)
             where TvMM : struct, IVertexMaterial;
@@ -33,11 +33,11 @@ namespace SharpGLTF.Geometry
     {
         void AddTriangle<TvPP, TvMM, TvSS>
             (
-            (TvPP, TvMM, TvSS) a,
-            (TvPP, TvMM, TvSS) b,
-            (TvPP, TvMM, TvSS) c
+            Vertex<TvPP, TvMM, TvSS> a,
+            Vertex<TvPP, TvMM, TvSS> b,
+            Vertex<TvPP, TvMM, TvSS> c
             )
-            where TvPP : struct, IVertexPosition
+            where TvPP : struct, IVertexGeometry
             where TvMM : struct, IVertexMaterial
             where TvSS : struct, IVertexSkinning;
     }
@@ -72,7 +72,7 @@ namespace SharpGLTF.Geometry
     /// </typeparam>
     [System.Diagnostics.DebuggerDisplay("Primitive {_Material}")]
     public class PrimitiveBuilder<TMaterial, TvP, TvM, TvS> : IPrimitiveBuilder, IPrimitive<TMaterial>
-        where TvP : struct, IVertexPosition
+        where TvP : struct, IVertexGeometry
         where TvM : struct, IVertexMaterial
         where TvS : struct, IVertexSkinning
     {
@@ -95,7 +95,7 @@ namespace SharpGLTF.Geometry
 
         private readonly TMaterial _Material;
 
-        private readonly VertexList<(TvP, TvM, TvS)> _Vertices = new VertexList<(TvP, TvM, TvS)>();
+        private readonly VertexList<Vertex<TvP, TvM, TvS>> _Vertices = new VertexList<Vertex<TvP, TvM, TvS>>();
         private readonly List<int> _Indices = new List<int>();
 
         #endregion
@@ -108,7 +108,7 @@ namespace SharpGLTF.Geometry
 
         public int VertexCount => _Vertices.Count;
 
-        public IReadOnlyList<(TvP, TvM, TvS)> Vertices => _Vertices;
+        public IReadOnlyList<Vertex<TvP, TvM, TvS>> Vertices => _Vertices;
 
         public IReadOnlyList<int> Indices => _Indices;
 
@@ -128,19 +128,20 @@ namespace SharpGLTF.Geometry
         /// <typeparamref name="TvS"/> fragments.
         /// </param>
         /// <returns>The index of the vertex.</returns>
-        public int UseVertex((TvP, TvM, TvS) vertex)
+        public int UseVertex(Vertex<TvP, TvM, TvS> vertex)
         {
-            if (_Scrict)
-            {
-                vertex.Item1.Validate();
-                vertex.Item2.Validate();
-                vertex.Item3.Validate();
-            }
+            if (_Scrict) vertex.Validate();
 
             return _Vertices.Use(vertex);
         }
 
-        public void AddTriangle((TvP, TvM, TvS) a, (TvP, TvM, TvS) b, (TvP, TvM, TvS) c)
+        /// <summary>
+        /// Adds a triangle.
+        /// </summary>
+        /// <param name="a">First corner of the triangle.</param>
+        /// <param name="b">Second corner of the triangle.</param>
+        /// <param name="c">Third corner of the triangle.</param>
+        public void AddTriangle(Vertex<TvP, TvM, TvS> a, Vertex<TvP, TvM, TvS> b, Vertex<TvP, TvM, TvS> c)
         {
             var aa = UseVertex(a);
             var bb = UseVertex(b);
@@ -160,46 +161,12 @@ namespace SharpGLTF.Geometry
             _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, TvS) a, (TvP, TvS) b, (TvP, TvS) 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, TvS)[] 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, TvS)[] points)
-        {
-            for (int i = 2; i < points.Length; ++i)
-            {
-                AddTriangle(points[0], points[i - 1], points[i]);
-            }
-        }
-
-        public void AddPolygon(params TvP[] points)
+        /// <summary>
+        /// Adds a polygon as a decomposed collection of triangles.
+        /// Currently only convex polygons are supported.
+        /// </summary>
+        /// <param name="points">The corners of the polygon.</param>
+        public void AddPolygon(params Vertex<TvP, TvM, TvS>[] points)
         {
             for (int i = 2; i < points.Length; ++i)
             {
@@ -217,9 +184,9 @@ namespace SharpGLTF.Geometry
                 var b = primitive.Vertices[t.Item2];
                 var c = primitive.Vertices[t.Item3];
 
-                var aa = a.Item1; aa.Transform(transform); a.Item1 = aa;
-                var bb = b.Item1; bb.Transform(transform); b.Item1 = bb;
-                var cc = c.Item1; cc.Transform(transform); c.Item1 = cc;
+                var aa = a.Geometry; aa.Transform(transform); a.Geometry = aa;
+                var bb = b.Geometry; bb.Transform(transform); b.Geometry = bb;
+                var cc = c.Geometry; cc.Transform(transform); c.Geometry = cc;
 
                 AddTriangle(a, b, c);
             }
@@ -229,48 +196,38 @@ namespace SharpGLTF.Geometry
         {
             foreach (var v in _Vertices)
             {
-                v.Item1.Validate();
-                v.Item2.Validate();
-                v.Item3.Validate();
+                v.Validate();
             }
         }
 
-        public void AddTriangle<TvPP, TvMM, TvSS>((TvPP, TvMM, TvSS) a, (TvPP, TvMM, TvSS) b, (TvPP, TvMM, TvSS) c)
-            where TvPP : struct, IVertexPosition
+        public void AddTriangle<TvPP, TvMM, TvSS>(Vertex<TvPP, TvMM, TvSS> a, Vertex<TvPP, TvMM, TvSS> b, Vertex<TvPP, TvMM, TvSS> c)
+            where TvPP : struct, IVertexGeometry
             where TvMM : struct, IVertexMaterial
             where TvSS : struct, IVertexSkinning
         {
-            var p1 = a.Item1.CloneAs<TvP>();
-            var p2 = b.Item1.CloneAs<TvP>();
-            var p3 = c.Item1.CloneAs<TvP>();
-
-            var m1 = a.Item2.CloneAs<TvM>();
-            var m2 = b.Item2.CloneAs<TvM>();
-            var m3 = c.Item2.CloneAs<TvM>();
-
-            var s1 = a.Item3.CloneAs<TvS>();
-            var s2 = b.Item3.CloneAs<TvS>();
-            var s3 = c.Item3.CloneAs<TvS>();
+            var aa = a.CloneAs<TvP, TvM, TvS>();
+            var bb = b.CloneAs<TvP, TvM, TvS>();
+            var cc = c.CloneAs<TvP, TvM, TvS>();
 
-            AddTriangle((p1, m1, s1), (p2, m2, s2), (p3, m3, s3));
+            AddTriangle(aa, bb, cc);
         }
 
-        public TvPP GetVertexPosition<TvPP>(int index)
-            where TvPP : struct, IVertexPosition
+        public TvPP GetVertexGeometry<TvPP>(int index)
+            where TvPP : struct, IVertexGeometry
         {
-            return _Vertices[index].Item1.CloneAs<TvPP>();
+            return _Vertices[index].Geometry.CloneAs<TvPP>();
         }
 
         public TvMM GetVertexMaterial<TvMM>(int index)
             where TvMM : struct, IVertexMaterial
         {
-            return _Vertices[index].Item2.CloneAs<TvMM>();
+            return _Vertices[index].Material.CloneAs<TvMM>();
         }
 
         public TvSS GetVertexSkinning<TvSS>(int index)
             where TvSS : struct, IVertexSkinning
         {
-            return _Vertices[index].Item3.CloneAs<TvSS>();
+            return _Vertices[index].Skinning.CloneAs<TvSS>();
         }
 
         #endregion

+ 136 - 0
src/SharpGLTF.Toolkit/Geometry/Vertex.cs

@@ -0,0 +1,136 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SharpGLTF.Geometry
+{
+    using System.Numerics;
+    using VertexTypes;
+
+    /// <summary>
+    /// Represents an individual vertex object.
+    /// </summary>
+    /// <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="TvS">
+    /// 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>
+    [System.Diagnostics.DebuggerDisplay("Primitive {_Material}")]
+    public struct Vertex<TvP, TvM, TvS>
+        where TvP : struct, IVertexGeometry
+        where TvM : struct, IVertexMaterial
+        where TvS : struct, IVertexSkinning
+    {
+        #region constructors
+
+        public Vertex(TvP g, TvM m, TvS s)
+        {
+            Geometry = g;
+            Material = m;
+            Skinning = s;
+        }
+
+        public Vertex(TvP g, TvM m)
+        {
+            Geometry = g;
+            Material = m;
+            Skinning = default;
+        }
+
+        public Vertex(TvP g, TvS s)
+        {
+            Geometry = g;
+            Material = default;
+            Skinning = s;
+        }
+
+        public Vertex(TvP g)
+        {
+            Geometry = g;
+            Material = default;
+            Skinning = default;
+        }
+
+        public static implicit operator Vertex<TvP, TvM, TvS>((TvP, TvM, TvS) tuple)
+        {
+            return new Vertex<TvP, TvM, TvS>(tuple.Item1, tuple.Item2, tuple.Item3);
+        }
+
+        public static implicit operator Vertex<TvP, TvM, TvS>((TvP, TvM) tuple)
+        {
+            return new Vertex<TvP, TvM, TvS>(tuple.Item1, tuple.Item2);
+        }
+
+        public static implicit operator Vertex<TvP, TvM, TvS>((TvP, TvS) tuple)
+        {
+            return new Vertex<TvP, TvM, TvS>(tuple.Item1, tuple.Item2);
+        }
+
+        public static implicit operator Vertex<TvP, TvM, TvS>(TvP g)
+        {
+            return new Vertex<TvP, TvM, TvS>(g);
+        }
+
+        public Vertex<TvPP, TvMM, TvSS> CloneAs<TvPP, TvMM, TvSS>()
+            where TvPP : struct, IVertexGeometry
+            where TvMM : struct, IVertexMaterial
+            where TvSS : struct, IVertexSkinning
+        {
+            var p = Geometry.CloneAs<TvPP>();
+            var m = Material.CloneAs<TvMM>();
+            var s = Skinning.CloneAs<TvSS>();
+
+            return new Vertex<TvPP, TvMM, TvSS>(p, m, s);
+        }
+
+        #endregion
+
+        #region data
+
+        public TvP Geometry;
+        public TvM Material;
+        public TvS Skinning;
+
+        #endregion
+
+        #region properties
+
+        public Vector3 Position
+        {
+            get => Geometry.GetPosition();
+            set => Geometry.SetPosition(value);
+        }
+
+        #endregion
+
+        #region API
+
+        public void Validate()
+        {
+            Geometry.Validate();
+            Material.Validate();
+            Skinning.Validate();
+        }
+
+        #endregion
+    }
+}

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

@@ -48,7 +48,7 @@ namespace SharpGLTF.Geometry.VertexTypes
         }
 
         public TvP GetPositionFragment<TvP>(int index)
-            where TvP : struct, IVertexPosition
+            where TvP : struct, IVertexGeometry
         {
             var pnt = default(TvP);
 

+ 13 - 13
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexPosition.cs

@@ -5,7 +5,7 @@ using System.Text;
 
 namespace SharpGLTF.Geometry.VertexTypes
 {
-    public interface IVertexPosition
+    public interface IVertexGeometry
     {
         void Validate();
 
@@ -24,7 +24,7 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// Defines a Vertex attribute with a Position.
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("{Position}")]
-    public struct VertexPosition : IVertexPosition
+    public struct VertexPosition : IVertexGeometry
     {
         #region constructors
 
@@ -54,11 +54,11 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #region API
 
-        void IVertexPosition.SetPosition(Vector3 position) { this.Position = position; }
+        void IVertexGeometry.SetPosition(Vector3 position) { this.Position = position; }
 
-        void IVertexPosition.SetNormal(Vector3 normal) { }
+        void IVertexGeometry.SetNormal(Vector3 normal) { }
 
-        void IVertexPosition.SetTangent(Vector4 tangent) { }
+        void IVertexGeometry.SetTangent(Vector4 tangent) { }
 
         public Vector3 GetPosition() { return this.Position; }
 
@@ -83,7 +83,7 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// Defines a Vertex attribute with a Position and a Normal.
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("{Position} {Normal}")]
-    public struct VertexPositionNormal : IVertexPosition
+    public struct VertexPositionNormal : IVertexGeometry
     {
         #region constructors
 
@@ -113,11 +113,11 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #region API
 
-        void IVertexPosition.SetPosition(Vector3 position) { this.Position = position; }
+        void IVertexGeometry.SetPosition(Vector3 position) { this.Position = position; }
 
-        void IVertexPosition.SetNormal(Vector3 normal) { this.Normal = normal; }
+        void IVertexGeometry.SetNormal(Vector3 normal) { this.Normal = normal; }
 
-        void IVertexPosition.SetTangent(Vector4 tangent) { }
+        void IVertexGeometry.SetTangent(Vector4 tangent) { }
 
         public Vector3 GetPosition() { return this.Position; }
 
@@ -144,7 +144,7 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// Defines a Vertex attribute with a Position, a Normal and a Tangent.
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("{Position} {Normal} {Tangent}")]
-    public struct VertexPositionNormalTangent : IVertexPosition
+    public struct VertexPositionNormalTangent : IVertexGeometry
     {
         #region constructors
 
@@ -172,11 +172,11 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #region API
 
-        void IVertexPosition.SetPosition(Vector3 position) { this.Position = position; }
+        void IVertexGeometry.SetPosition(Vector3 position) { this.Position = position; }
 
-        void IVertexPosition.SetNormal(Vector3 normal) { this.Normal = normal; }
+        void IVertexGeometry.SetNormal(Vector3 normal) { this.Normal = normal; }
 
-        void IVertexPosition.SetTangent(Vector4 tangent) { this.Tangent = tangent; }
+        void IVertexGeometry.SetTangent(Vector4 tangent) { this.Tangent = tangent; }
 
         public Vector3 GetPosition() { return this.Position; }
 

+ 31 - 13
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexUtils.cs

@@ -11,8 +11,8 @@ namespace SharpGLTF.Geometry.VertexTypes
 
     static class VertexUtils
     {
-        public static IEnumerable<MemoryAccessor[]> CreateVertexMemoryAccessors<TvP, TvM, TvS>(this IEnumerable<IReadOnlyList<(TvP, TvM, TvS)>> vertexBlocks)
-            where TvP : struct, IVertexPosition
+        public static IEnumerable<MemoryAccessor[]> CreateVertexMemoryAccessors<TvP, TvM, TvS>(this IEnumerable<IReadOnlyList<Vertex<TvP, TvM, TvS>>> vertexBlocks)
+            where TvP : struct, IVertexGeometry
             where TvM : struct, IVertexMaterial
             where TvS : struct, IVertexSkinning
         {
@@ -141,41 +141,59 @@ namespace SharpGLTF.Geometry.VertexTypes
             return new MemoryAccessInfo(attribute.Name, 0, 0, 0, dimensions.Value, attribute.Encoding, attribute.Normalized);
         }
 
-        private static Func<(TvP, TvM, TvS), Object> GetItemValueFunc<TvP, TvM, TvS>(string attributeName)
+        private static Func<Vertex<TvP, TvM, TvS>, Object> GetItemValueFunc<TvP, TvM, TvS>(string attributeName)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             var finfo = GetVertexField(typeof(TvP), attributeName);
-            if (finfo != null) return vertex => finfo.GetValue(vertex.Item1);
+            if (finfo != null) return vertex => finfo.GetValue(vertex.Geometry);
 
             finfo = GetVertexField(typeof(TvM), attributeName);
-            if (finfo != null) return vertex => finfo.GetValue(vertex.Item2);
+            if (finfo != null) return vertex => finfo.GetValue(vertex.Material);
 
             finfo = GetVertexField(typeof(TvS), attributeName);
-            if (finfo != null) return vertex => finfo.GetValue(vertex.Item3);
+            if (finfo != null) return vertex => finfo.GetValue(vertex.Skinning);
 
             throw new NotImplementedException();
         }
 
-        private static Single[] GetScalarColumn<TvP, TvM, TvS>(this IReadOnlyList<(TvP, TvM, TvS)> vertices, Func<(TvP, TvM, TvS), Object> func)
+        private static Single[] GetScalarColumn<TvP, TvM, TvS>(this IReadOnlyList<Vertex<TvP, TvM, TvS>> vertices, Func<Vertex<TvP, TvM, TvS>, Object> func)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return GetColumn<TvP, TvM, TvS, Single>(vertices, func);
         }
 
-        private static Vector2[] GetVector2Column<TvP, TvM, TvS>(this IReadOnlyList<(TvP, TvM, TvS)> vertices, Func<(TvP, TvM, TvS), Object> func)
+        private static Vector2[] GetVector2Column<TvP, TvM, TvS>(this IReadOnlyList<Vertex<TvP, TvM, TvS>> vertices, Func<Vertex<TvP, TvM, TvS>, Object> func)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return GetColumn<TvP, TvM, TvS, Vector2>(vertices, func);
         }
 
-        private static Vector3[] GetVector3Column<TvP, TvM, TvS>(this IReadOnlyList<(TvP, TvM, TvS)> vertices, Func<(TvP, TvM, TvS), Object> func)
+        private static Vector3[] GetVector3Column<TvP, TvM, TvS>(this IReadOnlyList<Vertex<TvP, TvM, TvS>> vertices, Func<Vertex<TvP, TvM, TvS>, Object> func)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return GetColumn<TvP, TvM, TvS, Vector3>(vertices, func);
         }
 
-        private static Vector4[] GetVector4Column<TvP, TvM, TvS>(this IReadOnlyList<(TvP, TvM, TvS)> vertices, Func<(TvP, TvM, TvS), Object> func)
+        private static Vector4[] GetVector4Column<TvP, TvM, TvS>(this IReadOnlyList<Vertex<TvP, TvM, TvS>> vertices, Func<Vertex<TvP, TvM, TvS>, Object> func)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return GetColumn<TvP, TvM, TvS, Vector4>(vertices, func);
         }
 
-        private static TColumn[] GetColumn<TvP, TvM, TvS, TColumn>(this IReadOnlyList<(TvP, TvM, TvS)> vertices, Func<(TvP, TvM, TvS), Object> func)
+        private static TColumn[] GetColumn<TvP, TvM, TvS, TColumn>(this IReadOnlyList<Vertex<TvP, TvM, TvS>> vertices, Func<Vertex<TvP, TvM, TvS>, Object> func)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             var dst = new TColumn[vertices.Count];
 
@@ -189,8 +207,8 @@ namespace SharpGLTF.Geometry.VertexTypes
             return dst;
         }
 
-        public static TvP CloneAs<TvP>(this IVertexPosition src)
-            where TvP : struct, IVertexPosition
+        public static TvP CloneAs<TvP>(this IVertexGeometry src)
+            where TvP : struct, IVertexGeometry
         {
             if (src.GetType() == typeof(TvP)) return (TvP)src;
 

+ 3 - 3
src/SharpGLTF.Toolkit/IO/WavefrontWriter.cs

@@ -134,7 +134,7 @@ namespace SharpGLTF.IO
             {
                 foreach (var v in p.Vertices)
                 {
-                    var pos = v.Item1.Position;
+                    var pos = v.Position;
                     sb.AppendLine(Invariant($"v {pos.X} {pos.Y} {pos.Z}"));
                 }
             }
@@ -145,7 +145,7 @@ namespace SharpGLTF.IO
             {
                 foreach (var v in p.Vertices)
                 {
-                    var nrm = v.Item1.Normal;
+                    var nrm = v.Geometry.Normal;
                     sb.AppendLine(Invariant($"vn {nrm.X} {nrm.Y} {nrm.Z}"));
                 }
             }
@@ -156,7 +156,7 @@ namespace SharpGLTF.IO
             {
                 foreach (var v in p.Vertices)
                 {
-                    var uv = v.Item2.TexCoord;
+                    var uv = v.Material.TexCoord;
                     uv.Y = 1 - uv.Y;
 
                     sb.AppendLine(Invariant($"vt {uv.X} {uv.Y}"));

+ 30 - 20
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -13,7 +13,7 @@ namespace SharpGLTF.Schema2
         #region meshes
 
         public static Mesh CreateMesh<TvP, TvM, TvS>(this ModelRoot root, Geometry.MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvS> meshBuilder)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
@@ -21,7 +21,7 @@ namespace SharpGLTF.Schema2
         }
 
         public static Mesh CreateMesh<TvP, TvM, TvS>(this ModelRoot root, Geometry.MeshBuilder<Material, TvP, TvM, TvS> meshBuilder)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
@@ -29,7 +29,7 @@ namespace SharpGLTF.Schema2
         }
 
         public static Mesh CreateMesh<TMaterial, TvP, TvM, TvS>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, Geometry.MeshBuilder<TMaterial, TvP, TvM, TvS> meshBuilder)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
@@ -37,7 +37,7 @@ namespace SharpGLTF.Schema2
         }
 
         public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, TvS>(this ModelRoot root, params Geometry.MeshBuilder<Material, TvP, TvM, TvS>[] meshBuilders)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
@@ -45,7 +45,7 @@ namespace SharpGLTF.Schema2
         }
 
         public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, TvS>(this ModelRoot root, params Geometry.MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvS>[] meshBuilders)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
@@ -53,10 +53,16 @@ namespace SharpGLTF.Schema2
         }
 
         public static IReadOnlyList<Mesh> CreateMeshes<TMaterial, TvP, TvM, TvS>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, params Geometry.MeshBuilder<TMaterial, TvP, TvM, TvS>[] meshBuilders)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
+            Guard.NotNull(root, nameof(root));
+            Guard.NotNull(materialEvaluator, nameof(materialEvaluator));
+            Guard.NotNull(meshBuilders, nameof(meshBuilders));
+
+            foreach (var m in meshBuilders) m.Validate();
+
             // create a new material for every unique material in the mesh builders.
             var mapMaterials = meshBuilders
                 .SelectMany(item => item.Primitives)
@@ -184,27 +190,31 @@ namespace SharpGLTF.Schema2
             return primitive;
         }
 
-        public static MeshPrimitive WithVertexAccessors<TVertex>(this MeshPrimitive primitive, IReadOnlyList<TVertex> vertices)
-            where TVertex : struct, Geometry.VertexTypes.IVertexPosition
+        public static MeshPrimitive WithVertexAccessors<TvP>(this MeshPrimitive primitive, IReadOnlyList<TvP> vertices)
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
         {
-            var xvertices = vertices.Select(item => (item, default(Geometry.VertexTypes.VertexEmpty), default(Geometry.VertexTypes.VertexEmpty))).ToList();
+            var xvertices = vertices
+                .Select(item => new Geometry.Vertex<TvP, Geometry.VertexTypes.VertexEmpty, Geometry.VertexTypes.VertexEmpty>(item))
+                .ToList();
 
             return primitive.WithVertexAccessors(xvertices);
         }
 
-        public static MeshPrimitive WithVertexAccessors<TVertex, TValues>(this MeshPrimitive primitive, IReadOnlyList<(TVertex, TValues)> vertices)
-            where TVertex : struct, Geometry.VertexTypes.IVertexPosition
-            where TValues : struct, Geometry.VertexTypes.IVertexMaterial
+        public static MeshPrimitive WithVertexAccessors<TvP, TvM>(this MeshPrimitive primitive, IReadOnlyList<(TvP, TvM)> vertices)
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
+            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
         {
-            var xvertices = vertices.Select(item => (item.Item1, item.Item2, default(Geometry.VertexTypes.VertexEmpty))).ToList();
+            var xvertices = vertices
+                .Select(item => new Geometry.Vertex<TvP, TvM, Geometry.VertexTypes.VertexEmpty>(item.Item1, item.Item2))
+                .ToList();
 
             return primitive.WithVertexAccessors(xvertices);
         }
 
-        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.IVertexSkinning
+        public static MeshPrimitive WithVertexAccessors<TvP, TvM, TvS>(this MeshPrimitive primitive, IReadOnlyList<Geometry.Vertex<TvP, TvM, TvS>> vertices)
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
+            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
+            where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
             var memAccessors = Geometry.VertexTypes.VertexUtils.CreateVertexMemoryAccessors(new[] { vertices }).First();
 
@@ -256,7 +266,7 @@ namespace SharpGLTF.Schema2
         #region evaluation
 
         public static IEnumerable<((TvP, TvM, TvS), (TvP, TvM, TvS), (TvP, TvM, TvS), Material)> Triangulate<TvP, TvM, TvS>(this Mesh mesh, Matrix4x4 xform)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
@@ -266,7 +276,7 @@ namespace SharpGLTF.Schema2
         }
 
         public static IEnumerable<((TvP, TvM, TvS), (TvP, TvM, TvS), (TvP, TvM, TvS), Material)> Triangulate<TvP, TvM, TvS>(this MeshPrimitive prim, Matrix4x4 xform, IReadOnlyDictionary<Vector3, Vector3> defaultNormals)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
@@ -370,7 +380,7 @@ namespace SharpGLTF.Schema2
         }
 
         public static void AddMesh<TMaterial, TvP, TvM, TvS>(this Geometry.MeshBuilder<TMaterial, TvP, TvM, TvS> meshBuilder, Mesh srcMesh, Matrix4x4 xform, Func<Material, TMaterial> materialFunc)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {

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

@@ -91,7 +91,7 @@ namespace SharpGLTF.Schema2
         /// <param name="scene">A <see cref="Scene"/> instance.</param>
         /// <returns>A collection of triangles in world space.</returns>
         public static IEnumerable<((TvP, TvM, TvS), (TvP, TvM, TvS), (TvP, TvM, TvS), Material)> Triangulate<TvP, TvM, TvS>(this Scene scene)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {
@@ -109,7 +109,7 @@ namespace SharpGLTF.Schema2
         /// <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, TvS), (TvP, TvM, TvS), (TvP, TvM, TvS), Material)> Triangulate<TvP, TvM, TvS>(this Node node, bool inWorldSpace)
-            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvS : struct, Geometry.VertexTypes.IVertexSkinning
         {