using System; using System.Collections.Generic; using System.Text; using System.Linq; using SharpGLTF.Schema2; namespace SharpGLTF.Geometry { /// /// Used internally to convert a /// to . /// /// A material key to split primitives by material. class PackedMeshBuilder { #region lifecycle internal static IEnumerable> PackMeshesColumnVertices(IEnumerable> meshBuilders) { try { foreach (var m in meshBuilders) m.Validate(); } catch (Exception ex) { throw new ArgumentException(ex.Message, nameof(meshBuilders), ex); } var vertexBuffers = new Dictionary(); var indexBuffer = new PackedBuffer(); var indexEncoding = meshBuilders.GetOptimalIndexEncoding(); var dstMeshes = new List>(); foreach (var srcMesh in meshBuilders) { var dstMesh = new PackedMeshBuilder(srcMesh.Name); foreach (var srcPrim in srcMesh.Primitives) { if (srcPrim.Vertices.Count == 0) continue; var attributeNames = VertexTypes.VertexUtils .GetVertexAttributes(srcPrim.Vertices[0], srcPrim.Vertices.Count) .Select(item => item.Name) .ToList(); var vAccessors = new List(); foreach (var an in attributeNames) { var vAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(srcPrim.Vertices, an); if (vAccessor == null) continue; vAccessors.Add(vAccessor); if (!vertexBuffers.TryGetValue(an, out PackedBuffer packed)) { vertexBuffers[an] = packed = new PackedBuffer(); } packed.AddAccessors(vAccessor); } var iAccessor = VertexTypes.VertexUtils.CreateIndexMemoryAccessor(srcPrim.GetIndices(), indexEncoding); if (iAccessor != null) indexBuffer.AddAccessors(iAccessor); dstMesh.AddPrimitive(srcPrim.Material, srcPrim.VerticesPerPrimitive, vAccessors.ToArray(), iAccessor); } dstMeshes.Add(dstMesh); } foreach (var vb in vertexBuffers.Values) vb.MergeBuffers(); indexBuffer.MergeBuffers(); return dstMeshes; } internal static IEnumerable> PackMeshesRowVertices(IEnumerable> meshBuilders) { try { foreach (var m in meshBuilders) m.Validate(); } catch (Exception ex) { throw new ArgumentException(ex.Message, nameof(meshBuilders), ex); } var vertexBuffer = new PackedBuffer(); var indexBuffer = new PackedBuffer(); var indexEncoding = meshBuilders.GetOptimalIndexEncoding(); var dstMeshes = new List>(); foreach (var srcMesh in meshBuilders) { var dstMesh = new PackedMeshBuilder(srcMesh.Name); foreach (var srcPrim in srcMesh.Primitives) { var vAccessors = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(srcPrim.Vertices); if (vAccessors == null) continue; vertexBuffer.AddAccessors(vAccessors); var iAccessor = VertexTypes.VertexUtils.CreateIndexMemoryAccessor(srcPrim.GetIndices(), indexEncoding); if (iAccessor != null) indexBuffer.AddAccessors(iAccessor); dstMesh.AddPrimitive(srcPrim.Material, srcPrim.VerticesPerPrimitive, vAccessors, iAccessor); } dstMeshes.Add(dstMesh); } vertexBuffer.MergeBuffers(); indexBuffer.MergeBuffers(); return dstMeshes; } private PackedMeshBuilder(string name) { _MeshName = name; } #endregion #region data private readonly string _MeshName; private readonly List> _Primitives = new List>(); #endregion #region API public void AddPrimitive(TMaterial material, int primitiveVertexCount, Memory.MemoryAccessor[] vrtAccessors, Memory.MemoryAccessor idxAccessor) { var p = new PackedPrimitiveBuilder(material, primitiveVertexCount, vrtAccessors, idxAccessor); _Primitives.Add(p); } public Mesh CreateSchema2Mesh(ModelRoot root, Func materialEvaluator) { if (_Primitives.Count == 0) return null; var dstMesh = root.CreateMesh(_MeshName); foreach (var p in _Primitives) { p.CopyToMesh(dstMesh, materialEvaluator); } return dstMesh; } #endregion } class PackedPrimitiveBuilder { #region lifecycle internal PackedPrimitiveBuilder(TMaterial material, int primitiveVertexCount, Memory.MemoryAccessor[] vrtAccessors, Memory.MemoryAccessor idxAccessor) { Guard.MustBeBetweenOrEqualTo(primitiveVertexCount, 1, 3, nameof(primitiveVertexCount)); Guard.NotNull(vrtAccessors, nameof(vrtAccessors)); if (primitiveVertexCount == 1) Guard.MustBeNull(idxAccessor, nameof(idxAccessor)); else Guard.NotNull(idxAccessor, nameof(idxAccessor)); _Material = material; _VerticesPerPrimitive = primitiveVertexCount; _VertexAccessors = vrtAccessors; _IndexAccessors = idxAccessor; // indices can be null for points } #endregion #region data private readonly TMaterial _Material; private readonly int _VerticesPerPrimitive; private readonly Memory.MemoryAccessor[] _VertexAccessors; private readonly Memory.MemoryAccessor _IndexAccessors; #endregion #region API internal void CopyToMesh(Mesh dstMesh, Func materialEvaluator) { if (_VerticesPerPrimitive < 1 || _VerticesPerPrimitive > 3) return; if (_VerticesPerPrimitive == 1) { dstMesh.CreatePrimitive() .WithMaterial(materialEvaluator(_Material)) .WithVertexAccessors(_VertexAccessors) .WithIndicesAutomatic(PrimitiveType.POINTS); return; } var pt = PrimitiveType.LINES; if (_VerticesPerPrimitive == 3) pt = PrimitiveType.TRIANGLES; dstMesh.CreatePrimitive() .WithMaterial(materialEvaluator(_Material)) .WithVertexAccessors(_VertexAccessors) .WithIndicesAccessor(pt, _IndexAccessors); } #endregion } }