BabylonExporter.GLTFExporter.Mesh.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. using Autodesk.Max;
  2. using BabylonExport.Entities;
  3. using GLTFExport.Entities;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. namespace Max2Babylon
  8. {
  9. partial class BabylonExporter
  10. {
  11. private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, BabylonScene babylonScene)
  12. {
  13. RaiseMessage("GLTFExporter.Mesh | Export mesh named: " + babylonMesh.name, 1);
  14. // --------------------------
  15. // --- Mesh from babylon ----
  16. // --------------------------
  17. if (babylonMesh.positions == null)
  18. {
  19. RaiseMessage("GLTFExporter.Mesh | Mesh is a dummy", 2);
  20. return null;
  21. }
  22. RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 2);
  23. // Retreive general data from babylon mesh
  24. int nbVertices = babylonMesh.positions.Length / 3;
  25. bool hasUV = babylonMesh.uvs != null && babylonMesh.uvs.Length > 0;
  26. bool hasUV2 = babylonMesh.uvs2 != null && babylonMesh.uvs2.Length > 0;
  27. bool hasColor = babylonMesh.colors != null && babylonMesh.colors.Length > 0;
  28. RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 3);
  29. RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 3);
  30. RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 3);
  31. RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 3);
  32. // Retreive vertices data from babylon mesh
  33. List<GLTFGlobalVertex> globalVertices = new List<GLTFGlobalVertex>();
  34. for (int indexVertex = 0; indexVertex < nbVertices; indexVertex++)
  35. {
  36. GLTFGlobalVertex globalVertex = new GLTFGlobalVertex();
  37. globalVertex.Position = createIPoint3(babylonMesh.positions, indexVertex);
  38. // Switch from left to right handed coordinate system
  39. //globalVertex.Position.X *= -1;
  40. globalVertex.Normal = createIPoint3(babylonMesh.normals, indexVertex);
  41. if (hasUV)
  42. {
  43. globalVertex.UV = createIPoint2(babylonMesh.uvs, indexVertex);
  44. // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
  45. // While for Babylon, it corresponds to the lower left corner of a texture image
  46. globalVertex.UV.Y = 1 - globalVertex.UV.Y;
  47. }
  48. if (hasUV2)
  49. {
  50. globalVertex.UV2 = createIPoint2(babylonMesh.uvs2, indexVertex);
  51. // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
  52. // While for Babylon, it corresponds to the lower left corner of a texture image
  53. globalVertex.UV2.Y = 1 - globalVertex.UV2.Y;
  54. }
  55. if (hasColor)
  56. {
  57. globalVertex.Color = createIPoint4(babylonMesh.colors, indexVertex).ToArray();
  58. }
  59. globalVertices.Add(globalVertex);
  60. }
  61. // Retreive indices from babylon mesh
  62. List<ushort> babylonIndices = new List<ushort>();
  63. babylonIndices = babylonMesh.indices.ToList().ConvertAll(new Converter<int, ushort>(n => (ushort)n));
  64. // --------------------------
  65. // ------- Init glTF --------
  66. // --------------------------
  67. RaiseMessage("GLTFExporter.Mesh | Init glTF", 2);
  68. // Mesh
  69. var gltfMesh = new GLTFMesh { name = babylonMesh.name };
  70. gltfMesh.index = gltf.MeshesList.Count;
  71. gltf.MeshesList.Add(gltfMesh);
  72. gltfMesh.idGroupInstance = babylonMesh.idGroupInstance;
  73. var weights = new List<float>();
  74. // --------------------------
  75. // ---- glTF primitives -----
  76. // --------------------------
  77. RaiseMessage("GLTFExporter.Mesh | glTF primitives", 2);
  78. var meshPrimitives = new List<GLTFMeshPrimitive>();
  79. foreach (BabylonSubMesh babylonSubMesh in babylonMesh.subMeshes)
  80. {
  81. // --------------------------
  82. // ------ SubMesh data ------
  83. // --------------------------
  84. List<GLTFGlobalVertex> globalVerticesSubMesh = globalVertices.GetRange(babylonSubMesh.verticesStart, babylonSubMesh.verticesCount);
  85. List<ushort> gltfIndices = babylonIndices.GetRange(babylonSubMesh.indexStart, babylonSubMesh.indexCount);
  86. // In gltf, indices of each mesh primitive are 0-based (ie: min value is 0)
  87. // Thus, the gltf indices list is a concatenation of sub lists all 0-based
  88. // Example for 2 triangles, each being a submesh:
  89. // babylonIndices = {0,1,2, 3,4,5} gives as result gltfIndicies = {0,1,2, 0,1,2}
  90. var minIndiceValue = gltfIndices.Min(); // Should be equal to babylonSubMesh.indexStart
  91. for (int indexIndice = 0; indexIndice < gltfIndices.Count; indexIndice++)
  92. {
  93. gltfIndices[indexIndice] -= minIndiceValue;
  94. }
  95. // --------------------------
  96. // ----- Mesh primitive -----
  97. // --------------------------
  98. // MeshPrimitive
  99. var meshPrimitive = new GLTFMeshPrimitive
  100. {
  101. attributes = new Dictionary<string, int>()
  102. };
  103. meshPrimitives.Add(meshPrimitive);
  104. // Material
  105. if (babylonMesh.materialId != null)
  106. {
  107. // Retreive the babylon material
  108. var babylonMaterialId = babylonMesh.materialId;
  109. var babylonMaterials = new List<BabylonMaterial>(babylonScene.materials);
  110. var babylonMaterial = babylonMaterials.Find(_babylonMaterial => _babylonMaterial.id == babylonMaterialId);
  111. if (babylonMaterial == null)
  112. {
  113. // It's a multi material
  114. var babylonMultiMaterials = new List<BabylonMultiMaterial>(babylonScene.multiMaterials);
  115. var babylonMultiMaterial = babylonMultiMaterials.Find(_babylonMultiMaterial => _babylonMultiMaterial.id == babylonMesh.materialId);
  116. babylonMaterialId = babylonMultiMaterial.materials[babylonSubMesh.materialIndex];
  117. babylonMaterial = babylonMaterials.Find(_babylonMaterial => _babylonMaterial.id == babylonMaterialId);
  118. }
  119. // Update primitive material index
  120. var indexMaterial = babylonMaterialsToExport.FindIndex(_babylonMaterial => _babylonMaterial == babylonMaterial);
  121. if (indexMaterial == -1)
  122. {
  123. // Store material for exportation
  124. indexMaterial = babylonMaterialsToExport.Count;
  125. babylonMaterialsToExport.Add(babylonMaterial);
  126. }
  127. meshPrimitive.material = indexMaterial;
  128. // TODO - Add and retreive info from babylon material
  129. meshPrimitive.mode = GLTFMeshPrimitive.FillMode.TRIANGLES;
  130. }
  131. // --------------------------
  132. // ------- Accessors --------
  133. // --------------------------
  134. // Buffer
  135. var buffer = GLTFBufferService.Instance.GetBuffer(gltf);
  136. // --- Indices ---
  137. var accessorIndices = GLTFBufferService.Instance.CreateAccessor(
  138. gltf,
  139. GLTFBufferService.Instance.GetBufferViewScalar(gltf, buffer),
  140. "accessorIndices",
  141. GLTFAccessor.ComponentType.UNSIGNED_SHORT,
  142. GLTFAccessor.TypeEnum.SCALAR
  143. );
  144. meshPrimitive.indices = accessorIndices.index;
  145. // Populate accessor
  146. gltfIndices.ForEach(n => accessorIndices.bytesList.AddRange(BitConverter.GetBytes(n)));
  147. accessorIndices.count = gltfIndices.Count;
  148. // --- Positions ---
  149. var accessorPositions = GLTFBufferService.Instance.CreateAccessor(
  150. gltf,
  151. GLTFBufferService.Instance.GetBufferViewFloatVec3(gltf, buffer),
  152. "accessorPositions",
  153. GLTFAccessor.ComponentType.FLOAT,
  154. GLTFAccessor.TypeEnum.VEC3
  155. );
  156. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index);
  157. // Populate accessor
  158. accessorPositions.min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue };
  159. accessorPositions.max = new float[] { float.MinValue, float.MinValue, float.MinValue };
  160. globalVerticesSubMesh.ForEach((globalVertex) =>
  161. {
  162. var positions = new float[] { globalVertex.Position.X, globalVertex.Position.Y, globalVertex.Position.Z };
  163. // Store values as bytes
  164. foreach (var position in positions)
  165. {
  166. accessorPositions.bytesList.AddRange(BitConverter.GetBytes(position));
  167. }
  168. // Update min and max values
  169. GLTFBufferService.UpdateMinMaxAccessor(accessorPositions, positions);
  170. });
  171. accessorPositions.count = globalVerticesSubMesh.Count;
  172. // --- Normals ---
  173. var accessorNormals = GLTFBufferService.Instance.CreateAccessor(
  174. gltf,
  175. GLTFBufferService.Instance.GetBufferViewFloatVec3(gltf, buffer),
  176. "accessorNormals",
  177. GLTFAccessor.ComponentType.FLOAT,
  178. GLTFAccessor.TypeEnum.VEC3
  179. );
  180. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index);
  181. // Populate accessor
  182. List<float> normals = globalVerticesSubMesh.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToList();
  183. normals.ForEach(n => accessorNormals.bytesList.AddRange(BitConverter.GetBytes(n)));
  184. accessorNormals.count = globalVerticesSubMesh.Count;
  185. // --- Colors ---
  186. if (hasColor)
  187. {
  188. var accessorColors = GLTFBufferService.Instance.CreateAccessor(
  189. gltf,
  190. GLTFBufferService.Instance.GetBufferViewFloatVec4(gltf, buffer),
  191. "accessorColors",
  192. GLTFAccessor.ComponentType.FLOAT,
  193. GLTFAccessor.TypeEnum.VEC4
  194. );
  195. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index);
  196. // Populate accessor
  197. List<float> colors = globalVerticesSubMesh.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList();
  198. colors.ForEach(n => accessorColors.bytesList.AddRange(BitConverter.GetBytes(n)));
  199. accessorColors.count = globalVerticesSubMesh.Count;
  200. }
  201. // --- UV ---
  202. if (hasUV)
  203. {
  204. var accessorUVs = GLTFBufferService.Instance.CreateAccessor(
  205. gltf,
  206. GLTFBufferService.Instance.GetBufferViewFloatVec2(gltf, buffer),
  207. "accessorUVs",
  208. GLTFAccessor.ComponentType.FLOAT,
  209. GLTFAccessor.TypeEnum.VEC2
  210. );
  211. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index);
  212. // Populate accessor
  213. List<float> uvs = globalVerticesSubMesh.SelectMany(v => new[] { v.UV.X, v.UV.Y }).ToList();
  214. uvs.ForEach(n => accessorUVs.bytesList.AddRange(BitConverter.GetBytes(n)));
  215. accessorUVs.count = globalVerticesSubMesh.Count;
  216. }
  217. // --- UV2 ---
  218. if (hasUV2)
  219. {
  220. var accessorUV2s = GLTFBufferService.Instance.CreateAccessor(
  221. gltf,
  222. GLTFBufferService.Instance.GetBufferViewFloatVec2(gltf, buffer),
  223. "accessorUV2s",
  224. GLTFAccessor.ComponentType.FLOAT,
  225. GLTFAccessor.TypeEnum.VEC2
  226. );
  227. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index);
  228. // Populate accessor
  229. List<float> uvs2 = globalVerticesSubMesh.SelectMany(v => new[] { v.UV2.X, v.UV2.Y }).ToList();
  230. uvs2.ForEach(n => accessorUV2s.bytesList.AddRange(BitConverter.GetBytes(n)));
  231. accessorUV2s.count = globalVerticesSubMesh.Count;
  232. }
  233. }
  234. gltfMesh.primitives = meshPrimitives.ToArray();
  235. return gltfMesh;
  236. }
  237. private IPoint2 createIPoint2(float[] array, int index)
  238. {
  239. var startIndex = index * 2;
  240. return Loader.Global.Point2.Create(array[startIndex], array[startIndex + 1]);
  241. }
  242. private IPoint3 createIPoint3(float[] array, int index)
  243. {
  244. var startIndex = index * 3;
  245. return Loader.Global.Point3.Create(array[startIndex], array[startIndex + 1], array[startIndex + 2]);
  246. }
  247. private IPoint4 createIPoint4(float[] array, int index)
  248. {
  249. var startIndex = index * 4;
  250. return Loader.Global.Point4.Create(array[startIndex], array[startIndex + 1], array[startIndex + 2], array[startIndex + 3]);
  251. }
  252. }
  253. }