PackedMeshBuilder.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Linq;
  5. using SharpGLTF.Schema2;
  6. namespace SharpGLTF.Geometry
  7. {
  8. /// <summary>
  9. /// Used internally to convert a <see cref="MeshBuilder{TMaterial, TvP, TvM, TvS}"/>
  10. /// to <see cref="Schema2.Mesh"/>.
  11. /// </summary>
  12. /// <typeparam name="TMaterial">A material key to split primitives by material.</typeparam>
  13. class PackedMeshBuilder<TMaterial>
  14. {
  15. #region lifecycle
  16. internal static IEnumerable<PackedMeshBuilder<TMaterial>> PackMeshesColumnVertices(IEnumerable<IMeshBuilder<TMaterial>> meshBuilders)
  17. {
  18. try
  19. {
  20. foreach (var m in meshBuilders) m.Validate();
  21. }
  22. catch (Exception ex)
  23. {
  24. throw new ArgumentException(ex.Message, nameof(meshBuilders), ex);
  25. }
  26. var vertexBuffers = new Dictionary<string, PackedBuffer>();
  27. var indexBuffer = new PackedBuffer();
  28. var indexEncoding = meshBuilders.GetOptimalIndexEncoding();
  29. var dstMeshes = new List<PackedMeshBuilder<TMaterial>>();
  30. foreach (var srcMesh in meshBuilders)
  31. {
  32. var dstMesh = new PackedMeshBuilder<TMaterial>(srcMesh.Name);
  33. foreach (var srcPrim in srcMesh.Primitives)
  34. {
  35. if (srcPrim.Vertices.Count == 0) continue;
  36. var attributeNames = VertexTypes.VertexUtils
  37. .GetVertexAttributes(srcPrim.Vertices[0], srcPrim.Vertices.Count)
  38. .Select(item => item.Name)
  39. .ToList();
  40. var vAccessors = new List<Memory.MemoryAccessor>();
  41. foreach (var an in attributeNames)
  42. {
  43. var vAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(srcPrim.Vertices, an);
  44. if (vAccessor == null) continue;
  45. vAccessors.Add(vAccessor);
  46. if (!vertexBuffers.TryGetValue(an, out PackedBuffer packed))
  47. {
  48. vertexBuffers[an] = packed = new PackedBuffer();
  49. }
  50. packed.AddAccessors(vAccessor);
  51. }
  52. var iAccessor = VertexTypes.VertexUtils.CreateIndexMemoryAccessor(srcPrim.GetIndices(), indexEncoding);
  53. if (iAccessor != null) indexBuffer.AddAccessors(iAccessor);
  54. dstMesh.AddPrimitive(srcPrim.Material, srcPrim.VerticesPerPrimitive, vAccessors.ToArray(), iAccessor);
  55. }
  56. dstMeshes.Add(dstMesh);
  57. }
  58. foreach (var vb in vertexBuffers.Values) vb.MergeBuffers();
  59. indexBuffer.MergeBuffers();
  60. return dstMeshes;
  61. }
  62. internal static IEnumerable<PackedMeshBuilder<TMaterial>> PackMeshesRowVertices(IEnumerable<IMeshBuilder<TMaterial>> meshBuilders)
  63. {
  64. try
  65. {
  66. foreach (var m in meshBuilders) m.Validate();
  67. }
  68. catch (Exception ex)
  69. {
  70. throw new ArgumentException(ex.Message, nameof(meshBuilders), ex);
  71. }
  72. var vertexBuffer = new PackedBuffer();
  73. var indexBuffer = new PackedBuffer();
  74. var indexEncoding = meshBuilders.GetOptimalIndexEncoding();
  75. var dstMeshes = new List<PackedMeshBuilder<TMaterial>>();
  76. foreach (var srcMesh in meshBuilders)
  77. {
  78. var dstMesh = new PackedMeshBuilder<TMaterial>(srcMesh.Name);
  79. foreach (var srcPrim in srcMesh.Primitives)
  80. {
  81. var vAccessors = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(srcPrim.Vertices);
  82. if (vAccessors == null) continue;
  83. vertexBuffer.AddAccessors(vAccessors);
  84. var iAccessor = VertexTypes.VertexUtils.CreateIndexMemoryAccessor(srcPrim.GetIndices(), indexEncoding);
  85. if (iAccessor != null) indexBuffer.AddAccessors(iAccessor);
  86. dstMesh.AddPrimitive(srcPrim.Material, srcPrim.VerticesPerPrimitive, vAccessors, iAccessor);
  87. }
  88. dstMeshes.Add(dstMesh);
  89. }
  90. vertexBuffer.MergeBuffers();
  91. indexBuffer.MergeBuffers();
  92. return dstMeshes;
  93. }
  94. private PackedMeshBuilder(string name) { _MeshName = name; }
  95. #endregion
  96. #region data
  97. private readonly string _MeshName;
  98. private readonly List<PackedPrimitiveBuilder<TMaterial>> _Primitives = new List<PackedPrimitiveBuilder<TMaterial>>();
  99. #endregion
  100. #region API
  101. public void AddPrimitive(TMaterial material, int primitiveVertexCount, Memory.MemoryAccessor[] vrtAccessors, Memory.MemoryAccessor idxAccessor)
  102. {
  103. var p = new PackedPrimitiveBuilder<TMaterial>(material, primitiveVertexCount, vrtAccessors, idxAccessor);
  104. _Primitives.Add(p);
  105. }
  106. public Mesh CreateSchema2Mesh(ModelRoot root, Func<TMaterial, Material> materialEvaluator)
  107. {
  108. if (_Primitives.Count == 0) return null;
  109. var dstMesh = root.CreateMesh(_MeshName);
  110. foreach (var p in _Primitives)
  111. {
  112. p.CopyToMesh(dstMesh, materialEvaluator);
  113. }
  114. return dstMesh;
  115. }
  116. #endregion
  117. }
  118. class PackedPrimitiveBuilder<TMaterial>
  119. {
  120. #region lifecycle
  121. internal PackedPrimitiveBuilder(TMaterial material, int primitiveVertexCount, Memory.MemoryAccessor[] vrtAccessors, Memory.MemoryAccessor idxAccessor)
  122. {
  123. Guard.MustBeBetweenOrEqualTo(primitiveVertexCount, 1, 3, nameof(primitiveVertexCount));
  124. Guard.NotNull(vrtAccessors, nameof(vrtAccessors));
  125. if (primitiveVertexCount == 1) Guard.MustBeNull(idxAccessor, nameof(idxAccessor));
  126. else Guard.NotNull(idxAccessor, nameof(idxAccessor));
  127. _Material = material;
  128. _VerticesPerPrimitive = primitiveVertexCount;
  129. _VertexAccessors = vrtAccessors;
  130. _IndexAccessors = idxAccessor; // indices can be null for points
  131. }
  132. #endregion
  133. #region data
  134. private readonly TMaterial _Material;
  135. private readonly int _VerticesPerPrimitive;
  136. private readonly Memory.MemoryAccessor[] _VertexAccessors;
  137. private readonly Memory.MemoryAccessor _IndexAccessors;
  138. #endregion
  139. #region API
  140. internal void CopyToMesh(Mesh dstMesh, Func<TMaterial, Material> materialEvaluator)
  141. {
  142. if (_VerticesPerPrimitive < 1 || _VerticesPerPrimitive > 3) return;
  143. if (_VerticesPerPrimitive == 1)
  144. {
  145. dstMesh.CreatePrimitive()
  146. .WithMaterial(materialEvaluator(_Material))
  147. .WithVertexAccessors(_VertexAccessors)
  148. .WithIndicesAutomatic(PrimitiveType.POINTS);
  149. return;
  150. }
  151. var pt = PrimitiveType.LINES;
  152. if (_VerticesPerPrimitive == 3) pt = PrimitiveType.TRIANGLES;
  153. dstMesh.CreatePrimitive()
  154. .WithMaterial(materialEvaluator(_Material))
  155. .WithVertexAccessors(_VertexAccessors)
  156. .WithIndicesAccessor(pt, _IndexAccessors);
  157. }
  158. #endregion
  159. }
  160. }