Ver Fonte

WIP: allow generic less mesh generation.

Vicente Penades há 6 anos atrás
pai
commit
b5a8583ef5

+ 49 - 0
src/SharpGLTF.Toolkit/Geometry/PackedMeshBuilder.cs

@@ -71,6 +71,55 @@ namespace SharpGLTF.Geometry
             }
         }
 
+        /// <summary>
+        /// Converts a collection of <see cref="MeshBuilder{TMaterial, TvP, TvM, TvS}"/>
+        /// 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>
+        /// <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(IEnumerable<IMeshBuilder<TMaterial>> meshBuilders)
+        {
+            try
+            {
+                foreach (var m in meshBuilders) m.Validate();
+            }
+            catch (Exception ex)
+            {
+                throw new ArgumentException(ex.Message, nameof(meshBuilders), ex);
+            }
+
+            var vertexBlocks = VertexTypes.VertexUtils.CreateVertexMemoryAccessors
+                (
+                meshBuilders
+                .SelectMany(item => item.Primitives)
+                .Select(item => item.Vertices)
+                ).ToList();
+
+            var indexBlocks = VertexTypes.VertexUtils.CreateIndexMemoryAccessors
+                (
+                meshBuilders
+                .SelectMany(item => item.Primitives)
+                .Select(item => item.Indices)
+                ).ToList();
+
+            int idx = 0;
+
+            foreach (var meshBuilder in meshBuilders)
+            {
+                var dstMesh = new PackedMeshBuilder<TMaterial>(meshBuilder.Name);
+
+                foreach (var primitiveBuilder in meshBuilder.Primitives)
+                {
+                    dstMesh.AddPrimitive(primitiveBuilder.Material, primitiveBuilder.VerticesPerPrimitive, vertexBlocks[idx], indexBlocks[idx]);
+
+                    ++idx;
+                }
+
+                yield return dstMesh;
+            }
+        }
+
         private PackedMeshBuilder(string name) { _MeshName = name; }
 
         #endregion

+ 17 - 1
src/SharpGLTF.Toolkit/Geometry/PrimitiveBuilder.cs

@@ -13,6 +13,8 @@ namespace SharpGLTF.Geometry
     {
         TMaterial Material { get; }
 
+        int VerticesPerPrimitive { get; }
+
         int VertexCount { get; }
 
         VertexBuilder<TvGG, TvMM, TvSS> GetVertex<TvGG, TvMM, TvSS>(int index)
@@ -22,6 +24,8 @@ namespace SharpGLTF.Geometry
 
         IReadOnlyList<int> Indices { get; }
 
+        IReadOnlyList<IVertexBuilder> Vertices { get; }
+
         IEnumerable<int> Points { get; }
 
         IEnumerable<(int, int)> Lines { get; }
@@ -97,7 +101,17 @@ namespace SharpGLTF.Geometry
 
         private readonly int _PrimitiveVertexCount;
 
-        private readonly VertexList<VertexBuilder<TvG, TvM, TvS>> _Vertices = new VertexList<VertexBuilder<TvG, TvM, TvS>>();
+        class PrimitiveVertexList : VertexList<VertexBuilder<TvG, TvM, TvS>>, IReadOnlyList<IVertexBuilder>
+        {
+            IVertexBuilder IReadOnlyList<IVertexBuilder>.this[int index] => base[index];
+
+            IEnumerator<IVertexBuilder> IEnumerable<IVertexBuilder>.GetEnumerator()
+            {
+                throw new NotImplementedException();
+            }
+        }
+
+        private readonly PrimitiveVertexList _Vertices = new PrimitiveVertexList();
         private readonly List<int> _Indices = new List<int>();
 
         #endregion
@@ -120,6 +134,8 @@ namespace SharpGLTF.Geometry
 
         public IReadOnlyList<VertexBuilder<TvG, TvM, TvS>> Vertices => _Vertices;
 
+        IReadOnlyList<IVertexBuilder> IPrimitive<TMaterial>.Vertices => _Vertices;
+
         public IReadOnlyList<int> Indices => _Indices;
 
         public IEnumerable<int> Points => _GetPointIndices();

+ 18 - 1
src/SharpGLTF.Toolkit/Geometry/VertexBuilder.cs

@@ -7,6 +7,17 @@ using SharpGLTF.Geometry.VertexTypes;
 
 namespace SharpGLTF.Geometry
 {
+    public interface IVertexBuilder
+    {
+        IVertexGeometry GetGeometry();
+        IVertexMaterial GetMaterial();
+        IVertexSkinning GetSkinning();
+
+        // void SetGeometry(IVertexGeometry);
+        // void SetMaterial(IVertexMaterial);
+        // void SetSkinning(IVertexSkinning);
+    }
+
     /// <summary>
     /// Represents an individual vertex object.
     /// </summary>
@@ -37,7 +48,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexJoints16x8"/>.
     /// </typeparam>
     [System.Diagnostics.DebuggerDisplay("Vertex 𝐏:{Position} {_GetDebugWarnings()}")]
-    public partial struct VertexBuilder<TvG, TvM, TvS>
+    public partial struct VertexBuilder<TvG, TvM, TvS> : IVertexBuilder
         where TvG : struct, IVertexGeometry
         where TvM : struct, IVertexMaterial
         where TvS : struct, IVertexSkinning
@@ -296,6 +307,12 @@ namespace SharpGLTF.Geometry
             return sb.ToString();
         }
 
+        IVertexGeometry IVertexBuilder.GetGeometry() { return this.Geometry; }
+
+        IVertexMaterial IVertexBuilder.GetMaterial() { return this.Material; }
+
+        IVertexSkinning IVertexBuilder.GetSkinning() { return this.Skinning; }
+
         #endregion
     }
 }

+ 8 - 0
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexEmpty.cs

@@ -30,5 +30,13 @@ namespace SharpGLTF.Geometry.VertexTypes
         JointBinding IVertexSkinning.GetJointBinding(int index) { throw new NotSupportedException(); }
 
         public IEnumerable<JointBinding> JointBindings => Enumerable.Empty<JointBinding>();
+
+        public Vector4 JointsLow => Vector4.Zero;
+
+        public Vector4 JointsHigh => Vector4.Zero;
+
+        public Vector4 WeightsLow => Vector4.Zero;
+
+        public Vector4 Weightshigh => Vector4.Zero;
     }
 }

+ 46 - 0
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexSkinning.cs

@@ -126,6 +126,12 @@ namespace SharpGLTF.Geometry.VertexTypes
         void SetJointBinding(int index, int joint, float weight);
 
         IEnumerable<JointBinding> JointBindings { get; }
+
+        Vector4 JointsLow { get; }
+        Vector4 JointsHigh { get; }
+
+        Vector4 WeightsLow { get; }
+        Vector4 Weightshigh { get; }
     }
 
     /// <summary>
@@ -190,6 +196,16 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #endregion
 
+        #region properties
+
+        public Vector4 JointsLow => this.Joints;
+        public Vector4 JointsHigh => Vector4.Zero;
+
+        public Vector4 WeightsLow => this.Weights;
+        public Vector4 Weightshigh => Vector4.Zero;
+
+        #endregion
+
         #region API
 
         public void Validate() { FragmentPreprocessors.ValidateVertexSkinning(this); }
@@ -289,6 +305,16 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #endregion
 
+        #region properties
+
+        public Vector4 JointsLow => this.Joints;
+        public Vector4 JointsHigh => Vector4.Zero;
+
+        public Vector4 WeightsLow => this.Weights;
+        public Vector4 Weightshigh => Vector4.Zero;
+
+        #endregion
+
         #region API
 
         public void Validate() { FragmentPreprocessors.ValidateVertexSkinning(this); }
@@ -380,6 +406,16 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #endregion
 
+        #region properties
+
+        public Vector4 JointsLow => this.Joints0;
+        public Vector4 JointsHigh => this.Joints1;
+
+        public Vector4 WeightsLow => this.Weights0;
+        public Vector4 Weightshigh => this.Joints1;
+
+        #endregion
+
         #region API
 
         public void Validate() { FragmentPreprocessors.ValidateVertexSkinning(this); }
@@ -464,6 +500,16 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #endregion
 
+        #region properties
+
+        public Vector4 JointsLow => this.Joints0;
+        public Vector4 JointsHigh => this.Joints1;
+
+        public Vector4 WeightsLow => this.Weights0;
+        public Vector4 Weightshigh => this.Joints1;
+
+        #endregion
+
         #region API
 
         public void Validate() { FragmentPreprocessors.ValidateVertexSkinning(this); }

+ 85 - 3
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexUtils.cs

@@ -49,6 +49,49 @@ namespace SharpGLTF.Geometry.VertexTypes
             return true;
         }
 
+        public static IEnumerable<MemoryAccessor[]> CreateVertexMemoryAccessors(this IEnumerable<IReadOnlyList<IVertexBuilder>> vertexBlocks)
+        {
+            // total number of vertices
+            var totalCount = vertexBlocks.Sum(item => item.Count);
+
+            var firstVertex = vertexBlocks.First().First();
+
+            var tvg = firstVertex.GetGeometry().GetType();
+            var tvm = firstVertex.GetMaterial().GetType();
+            var tvs = firstVertex.GetSkinning().GetType();
+
+            // vertex attributes
+            var attributes = GetVertexAttributes(tvg, tvm, tvs, totalCount);
+
+            // create master vertex buffer
+            int byteStride = attributes[0].ByteStride;
+            var vbuffer = new ArraySegment<byte>(new Byte[byteStride * totalCount]);
+
+            var baseVertexIndex = 0;
+
+            foreach (var block in vertexBlocks)
+            {
+                var accessors = MemoryAccessInfo
+                    .Slice(attributes, baseVertexIndex, block.Count)
+                    .Select(item => new MemoryAccessor(vbuffer, item))
+                    .ToArray();
+
+                foreach (var accessor in accessors)
+                {
+                    var columnFunc = GetVertexBuilderAttributeFunc(accessor.Attribute.Name);
+
+                    if (accessor.Attribute.Dimensions == Schema2.DimensionType.SCALAR) accessor.AsScalarArray().Fill(block.GetColumn<float>(columnFunc));
+                    if (accessor.Attribute.Dimensions == Schema2.DimensionType.VEC2) accessor.AsVector2Array().Fill(block.GetColumn<Vector2>(columnFunc));
+                    if (accessor.Attribute.Dimensions == Schema2.DimensionType.VEC3) accessor.AsVector3Array().Fill(block.GetColumn<Vector3>(columnFunc));
+                    if (accessor.Attribute.Dimensions == Schema2.DimensionType.VEC4) accessor.AsVector4Array().Fill(block.GetColumn<Vector4>(columnFunc));
+                }
+
+                yield return accessors;
+
+                baseVertexIndex += block.Count;
+            }
+        }
+
         public static IEnumerable<MemoryAccessor[]> CreateVertexMemoryAccessors<TvG, TvM, TvS>(this IEnumerable<IReadOnlyList<VertexBuilder<TvG, TvM, TvS>>> vertexBlocks)
             where TvG : struct, IVertexGeometry
             where TvM : struct, IVertexMaterial
@@ -75,7 +118,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
                 foreach (var accessor in accessors)
                 {
-                    var columnFunc = GetItemValueFunc<TvG, TvM, TvS>(accessor.Attribute.Name);
+                    var columnFunc = GetVertexBuilderAttributeFunc<TvG, TvM, TvS>(accessor.Attribute.Name);
 
                     if (accessor.Attribute.Dimensions == Schema2.DimensionType.SCALAR) accessor.AsScalarArray().Fill(block.GetScalarColumn(columnFunc));
                     if (accessor.Attribute.Dimensions == Schema2.DimensionType.VEC2) accessor.AsVector2Array().Fill(block.GetVector2Column(columnFunc));
@@ -179,7 +222,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             return new MemoryAccessInfo(attribute.Name, 0, 0, 0, dimensions.Value, attribute.Encoding, attribute.Normalized);
         }
 
-        private static Func<VertexBuilder<TvG, TvM, TvS>, Object> GetItemValueFunc<TvG, TvM, TvS>(string attributeName)
+        private static Func<VertexBuilder<TvG, TvM, TvS>, Object> GetVertexBuilderAttributeFunc<TvG, TvM, TvS>(string attributeName)
             where TvG : struct, IVertexGeometry
             where TvM : struct, IVertexMaterial
             where TvS : struct, IVertexSkinning
@@ -196,6 +239,31 @@ namespace SharpGLTF.Geometry.VertexTypes
             throw new NotImplementedException();
         }
 
+        private static Func<IVertexBuilder, Object> GetVertexBuilderAttributeFunc(string attributeName)
+        {
+            if (attributeName == "POSITION") return v => v.GetGeometry().GetPosition();
+            if (attributeName == "NORMAL") return v => { return v.GetGeometry().TryGetNormal(out Vector3 n) ? n : Vector3.Zero; };
+            if (attributeName == "TANGENT") return v => { return v.GetGeometry().TryGetTangent(out Vector4 n) ? n : Vector4.Zero; };
+
+            if (attributeName == "COLOR_0") return v => { var m = v.GetMaterial(); return m.MaxColors <= 0 ? Vector4.One : m.GetColor(0); };
+            if (attributeName == "COLOR_1") return v => { var m = v.GetMaterial(); return m.MaxColors <= 1 ? Vector4.One : m.GetColor(1); };
+            if (attributeName == "COLOR_2") return v => { var m = v.GetMaterial(); return m.MaxColors <= 2 ? Vector4.One : m.GetColor(2); };
+            if (attributeName == "COLOR_3") return v => { var m = v.GetMaterial(); return m.MaxColors <= 3 ? Vector4.One : m.GetColor(3); };
+
+            if (attributeName == "TEXCOORD_0") return v => { var m = v.GetMaterial(); return m.MaxTextCoords <= 0 ? Vector2.Zero : m.GetTexCoord(0); };
+            if (attributeName == "TEXCOORD_1") return v => { var m = v.GetMaterial(); return m.MaxTextCoords <= 1 ? Vector2.Zero : m.GetTexCoord(1); };
+            if (attributeName == "TEXCOORD_2") return v => { var m = v.GetMaterial(); return m.MaxTextCoords <= 2 ? Vector2.Zero : m.GetTexCoord(2); };
+            if (attributeName == "TEXCOORD_3") return v => { var m = v.GetMaterial(); return m.MaxTextCoords <= 3 ? Vector2.Zero : m.GetTexCoord(3); };
+
+            if (attributeName == "JOINTS_0") return v => v.GetSkinning().JointsLow;
+            if (attributeName == "JOINTS_1") return v => v.GetSkinning().JointsHigh;
+
+            if (attributeName == "WEIGHTS_0") return v => v.GetSkinning().WeightsLow;
+            if (attributeName == "WEIGHTS_1") return v => v.GetSkinning().Weightshigh;
+
+            throw new NotImplementedException();
+        }
+
         private static Single[] GetScalarColumn<TvG, TvM, TvS>(this IReadOnlyList<VertexBuilder<TvG, TvM, TvS>> vertices, Func<VertexBuilder<TvG, TvM, TvS>, Object> func)
             where TvG : struct, IVertexGeometry
             where TvM : struct, IVertexMaterial
@@ -245,6 +313,20 @@ namespace SharpGLTF.Geometry.VertexTypes
             return dst;
         }
 
+        private static TColumn[] GetColumn<TColumn>(this IReadOnlyList<IVertexBuilder> vertices, Func<IVertexBuilder, Object> func)
+        {
+            var dst = new TColumn[vertices.Count];
+
+            for (int i = 0; i < dst.Length; ++i)
+            {
+                var v = vertices[i];
+
+                dst[i] = (TColumn)func(v);
+            }
+
+            return dst;
+        }
+
         public static TvP ConvertTo<TvP>(this IVertexGeometry src)
             where TvP : struct, IVertexGeometry
         {
@@ -337,6 +419,6 @@ namespace SharpGLTF.Geometry.VertexTypes
             }
 
             return dst;
-        }
+        }        
     }
 }

+ 9 - 0
src/SharpGLTF.Toolkit/Scenes/InstanceBuilder.cs

@@ -22,5 +22,14 @@ namespace SharpGLTF.Scenes
         private IContentRoot _Content;
 
         #endregion
+
+        #region API
+
+        internal Geometry.IMeshBuilder<Materials.MaterialBuilder> GetMesh()
+        {
+            throw new NotImplementedException();
+        }
+
+        #endregion
     }
 }

+ 45 - 0
src/SharpGLTF.Toolkit/Scenes/SceneBuilder.Schema2.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SharpGLTF.Scenes
+{
+    using Schema2;
+
+    public partial class SceneBuilder
+    {
+
+        public ModelRoot ToSchema2()
+        {
+            var dstModel = ModelRoot.CreateModel();
+
+            // gather all meshes and group them by their attribute layout.
+
+            var meshGroups = _Instances
+                .Select(item => item.GetMesh())
+                .Where(item => item != null)
+                .Distinct()
+                .ToList()
+                .GroupBy(item => item.GetType());
+
+            // create schema2.mesh collections for every gathered group.
+
+            var meshMap = new Dictionary<Geometry.IMeshBuilder<Materials.MaterialBuilder>, Mesh>();
+
+            foreach (var meshGroup in meshGroups)
+            {
+                var meshArray = meshGroup.ToArray();
+
+                var meshDst = dstModel.CreateMeshes(meshArray);
+
+                for (int i = 0; i < meshArray.Length; ++i)
+                {
+                    meshMap[meshArray[i]] = meshDst[i];
+                }
+            }
+
+            return dstModel;
+        }
+    }
+}

+ 1 - 1
src/SharpGLTF.Toolkit/Scenes/SceneBuilder.cs

@@ -4,7 +4,7 @@ using System.Text;
 
 namespace SharpGLTF.Scenes
 {
-    public class SceneBuilder
+    public partial class SceneBuilder
     {
         #region data
 

+ 62 - 25
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -14,50 +14,87 @@ 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.IVertexGeometry
-            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvS : struct, Geometry.VertexTypes.IVertexSkinning
+        public static Mesh CreateMesh<TvP, TvM, TvS>(this ModelRoot root, MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvS> meshBuilder)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return root.CreateMeshes(meshBuilder).First();
         }
 
-        public static Mesh CreateMesh<TvP, TvM, TvS>(this ModelRoot root, Geometry.MeshBuilder<Material, TvP, TvM, TvS> meshBuilder)
-            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
-            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvS : struct, Geometry.VertexTypes.IVertexSkinning
+        public static Mesh CreateMesh<TvP, TvM, TvS>(this ModelRoot root, MeshBuilder<Material, TvP, TvM, TvS> meshBuilder)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return root.CreateMeshes(meshBuilder).First();
         }
 
-        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.IVertexGeometry
-            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvS : struct, Geometry.VertexTypes.IVertexSkinning
+        public static Mesh CreateMesh<TMaterial, TvP, TvM, TvS>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, MeshBuilder<TMaterial, TvP, TvM, TvS> meshBuilder)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return root.CreateMeshes(materialEvaluator, meshBuilder).First();
         }
 
-        public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, TvS>(this ModelRoot root, params Geometry.MeshBuilder<Material, TvP, TvM, TvS>[] meshBuilders)
-            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
-            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvS : struct, Geometry.VertexTypes.IVertexSkinning
+        public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, TvS>(this ModelRoot root, params MeshBuilder<Material, TvP, TvM, TvS>[] meshBuilders)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return root.CreateMeshes(m => m, meshBuilders);
         }
 
-        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.IVertexGeometry
-            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvS : struct, Geometry.VertexTypes.IVertexSkinning
+        public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, TvS>(this ModelRoot root, params MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvS>[] meshBuilders)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, IVertexSkinning
         {
             return root.CreateMeshes(mb => root.CreateMaterial(mb), meshBuilders);
         }
 
-        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.IVertexGeometry
-            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
-            where TvS : struct, Geometry.VertexTypes.IVertexSkinning
+        public static IReadOnlyList<Mesh> CreateMeshes<TMaterial, TvP, TvM, TvS>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, params MeshBuilder<TMaterial, TvP, TvM, TvS>[] meshBuilders)
+            where TvP : struct, IVertexGeometry
+            where TvM : struct, IVertexMaterial
+            where TvS : struct, 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)
+                .Select(item => item.Material)
+                .Distinct()
+                .ToDictionary(m => m, m => materialEvaluator(m));
+
+            // creates meshes and primitives using MemoryAccessors using a single, shared vertex and index buffer
+            var srcMeshes = PackedMeshBuilder<TMaterial>
+                .PackMeshes(meshBuilders)
+                .ToList();
+
+            var dstMeshes = new List<Mesh>();
+
+            foreach (var srcMesh in srcMeshes)
+            {
+                var dstMesh = srcMesh.CreateSchema2Mesh(root, m => mapMaterials[m]);
+
+                dstMeshes.Add(dstMesh);
+            }
+
+            return dstMeshes;
+        }
+
+        public static IReadOnlyList<Mesh> CreateMeshes(this ModelRoot root, params IMeshBuilder<Materials.MaterialBuilder>[] meshBuilders)
+        {
+            return root.CreateMeshes(mb => root.CreateMaterial(mb), meshBuilders);
+        }
+
+        public static IReadOnlyList<Mesh> CreateMeshes<TMaterial>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, params IMeshBuilder<TMaterial>[] meshBuilders)
         {
             Guard.NotNull(root, nameof(root));
             Guard.NotNull(materialEvaluator, nameof(materialEvaluator));
@@ -73,7 +110,7 @@ namespace SharpGLTF.Schema2
                 .ToDictionary(m => m, m => materialEvaluator(m));
 
             // creates meshes and primitives using MemoryAccessors using a single, shared vertex and index buffer
-            var srcMeshes = Geometry.PackedMeshBuilder<TMaterial>
+            var srcMeshes = PackedMeshBuilder<TMaterial>
                 .PackMeshes(meshBuilders)
                 .ToList();