using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework.Graphics; using SharpGLTF.Runtime.Template; namespace SharpGLTF.Runtime.Pipeline { /// /// Writes the vertex and index buffer data to MonoGame vertex and index buffers. /// sealed class MeshPrimitiveWriter { #region data // shared buffers private readonly Dictionary _Buffers = new Dictionary(); // primitives private readonly List<_MeshPrimitive> _MeshPrimitives = new List<_MeshPrimitive>(); #endregion #region API public void WriteMeshPrimitive(int logicalMeshIndex, Effect effect, MeshPrimitiveReader primitive) where TVertex : unmanaged, IVertexType { if (!_Buffers.TryGetValue(typeof(TVertex), out IPrimitivesBuffers pb)) { _Buffers[typeof(TVertex)] = pb = new _PrimitivesBuffers(); } var part = (pb as _PrimitivesBuffers).Append(logicalMeshIndex, effect, primitive); _MeshPrimitives.Add(part); } internal IReadOnlyDictionary GetRuntimeMeshes(GraphicsDevice device, GraphicsResourceTracker disposables) { // create shared vertex/index buffers var vbuffers = _Buffers.Values.ToDictionary(key => key, val => val.CreateVertexBuffer(device)); var ibuffers = _Buffers.Values.ToDictionary(key => key, val => val.CreateIndexBuffer(device)); foreach (var vb in vbuffers.Values) disposables.AddDisposable(vb); foreach (var ib in ibuffers.Values) disposables.AddDisposable(ib); // create RuntimeModelMesh RuntimeModelMesh _convert(IEnumerable<_MeshPrimitive> srcParts) { var dstMesh = new RuntimeModelMesh(device); foreach (var srcPart in srcParts) { var vb = vbuffers[srcPart.PrimitiveBuffers]; var ib = ibuffers[srcPart.PrimitiveBuffers]; var dstPart = dstMesh.CreateMeshPart(); dstPart.Effect = srcPart.PrimitiveEffect; dstPart.SetVertexBuffer(vb, srcPart.VertexOffset, srcPart.VertexCount); dstPart.SetIndexBuffer(ib, srcPart.TriangleOffset * 3, srcPart.TriangleCount); } return dstMesh; } return _MeshPrimitives .GroupBy(item => item.LogicalMeshIndex) .ToDictionary(k => k.Key, v => _convert(v)); } #endregion #region nested types interface IPrimitivesBuffers { VertexBuffer CreateVertexBuffer(GraphicsDevice device); IndexBuffer CreateIndexBuffer(GraphicsDevice device); } /// /// Contains the shared vertex/index buffers of all the mesh primitive that share the same vertex type. /// /// sealed class _PrimitivesBuffers : IPrimitivesBuffers where TVertex : unmanaged, IVertexType { #region data private readonly List _Vertices = new List(); private readonly List<(int, int, int)> _Triangles = new List<(int, int, int)>(); #endregion #region API public _MeshPrimitive Append(int meshKey, Effect effect, MeshPrimitiveReader primitive) { var partVertices = primitive.ToXnaVertices(); var partTriangles = primitive.TriangleIndices; var part = new _MeshPrimitive { LogicalMeshIndex = meshKey, PrimitiveEffect = effect, PrimitiveBuffers = this, VertexOffset = _Vertices.Count, VertexCount = partVertices.Length, TriangleOffset = _Triangles.Count, TriangleCount = partTriangles.Length }; _Vertices.AddRange(partVertices); _Triangles.AddRange(partTriangles); return part; } public VertexBuffer CreateVertexBuffer(GraphicsDevice device) { var data = new VertexBuffer(device, typeof(TVertex), _Vertices.Count, BufferUsage.None); data.SetData(_Vertices.ToArray()); return data; } public IndexBuffer CreateIndexBuffer(GraphicsDevice device) { return CreateIndexBuffer(device, _Triangles); } private static IndexBuffer CreateIndexBuffer(GraphicsDevice device, IEnumerable<(int A, int B, int C)> triangles) { var sequence32 = triangles .SelectMany(item => new[] { (UInt32)item.C, (UInt32)item.B, (UInt32)item.A }) .ToArray(); var max = sequence32.Max(); if (max > 65535) { var indices = new IndexBuffer(device, typeof(UInt32), sequence32.Length, BufferUsage.None); indices.SetData(sequence32); return indices; } else { var sequence16 = sequence32.Select(item => (UInt16)item).ToArray(); var indices = new IndexBuffer(device, typeof(UInt16), sequence16.Length, BufferUsage.None); indices.SetData(sequence16); return indices; } } #endregion } /// /// Represents a mesh primitive /// struct _MeshPrimitive { public int LogicalMeshIndex; public Effect PrimitiveEffect; public IPrimitivesBuffers PrimitiveBuffers; public int VertexOffset; public int VertexCount; public int TriangleOffset; public int TriangleCount; } #endregion } }