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
}
}