BabylonExporter.GLTFExporter.Mesh.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. using Autodesk.Max;
  2. using BabylonExport.Entities;
  3. using GLTFExport.Entities;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using System.Linq;
  8. namespace Max2Babylon
  9. {
  10. partial class BabylonExporter
  11. {
  12. private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, GLTFNode gltfParentNode)
  13. {
  14. RaiseMessage("GLTFExporter.Mesh | ExportMesh babylonMesh.name=" + babylonMesh.name, 1);
  15. // --------------------------
  16. // ---------- Node ----------
  17. // --------------------------
  18. RaiseMessage("GLTFExporter.Mesh | Node", 1);
  19. // Node
  20. var gltfNode = new GLTFNode();
  21. gltfNode.name = babylonMesh.name;
  22. gltfNode.index = gltf.NodesList.Count;
  23. gltf.NodesList.Add(gltfNode);
  24. // Hierarchy
  25. if (gltfParentNode != null)
  26. {
  27. RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as child to " + gltfParentNode.name, 2);
  28. gltfParentNode.ChildrenList.Add(gltfNode.index);
  29. }
  30. else
  31. {
  32. // It's a root node
  33. // Only root nodes are listed in a gltf scene
  34. RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as root node to scene", 2);
  35. gltf.scenes[0].NodesList.Add(gltfNode.index);
  36. }
  37. // Transform
  38. gltfNode.translation = babylonMesh.position;
  39. if (babylonMesh.rotationQuaternion != null)
  40. {
  41. gltfNode.rotation = babylonMesh.rotationQuaternion;
  42. }
  43. else
  44. {
  45. // Convert rotation vector to quaternion
  46. // TODO - Fix it
  47. BabylonVector3 rotationVector3 = new BabylonVector3
  48. {
  49. X = babylonMesh.rotation[0],
  50. Y = babylonMesh.rotation[1],
  51. Z = babylonMesh.rotation[2]
  52. };
  53. gltfNode.rotation = rotationVector3.toQuaternion().ToArray();
  54. RaiseMessage("GLTFExporter.Mesh | rotationVector3=[" + rotationVector3.X + "; " + rotationVector3.Y + "; " + rotationVector3.Z + "]", 2);
  55. RaiseMessage("GLTFExporter.Mesh | gltfNode.rotation=[" + gltfNode.rotation[0] + "; " + gltfNode.rotation[1] + "; " + gltfNode.rotation[2] + "; " + gltfNode.rotation[3] + "]", 2);
  56. }
  57. gltfNode.scale = babylonMesh.scaling;
  58. // --------------------------
  59. // --- Mesh from babylon ----
  60. // --------------------------
  61. RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 1);
  62. // Retreive general data from babylon mesh
  63. int nbVertices = babylonMesh.positions.Length / 3;
  64. bool hasUV = babylonMesh.uvs != null && babylonMesh.uvs.Length > 0;
  65. bool hasUV2 = babylonMesh.uvs2 != null && babylonMesh.uvs2.Length > 0;
  66. bool hasColor = babylonMesh.colors != null && babylonMesh.colors.Length > 0;
  67. RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 2);
  68. RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 2);
  69. RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 2);
  70. RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 2);
  71. // Retreive vertices data from babylon mesh
  72. List<GLTFGlobalVertex> globalVertices = new List<GLTFGlobalVertex>();
  73. for (int i = 0; i < nbVertices; i++)
  74. {
  75. GLTFGlobalVertex globalVertex = new GLTFGlobalVertex();
  76. globalVertex.Position = createIPoint3(babylonMesh.positions, i);
  77. globalVertex.Normal = createIPoint3(babylonMesh.normals, i);
  78. if (hasUV)
  79. {
  80. globalVertex.UV = createIPoint2(babylonMesh.uvs, i);
  81. // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
  82. // While for Babylon, it corresponds to the lower left corner of a texture image
  83. globalVertex.UV.Y = 1 - globalVertex.UV.Y;
  84. }
  85. if (hasUV2)
  86. {
  87. globalVertex.UV2 = createIPoint2(babylonMesh.uvs2, i);
  88. // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
  89. // While for Babylon, it corresponds to the lower left corner of a texture image
  90. globalVertex.UV2.Y = 1 - globalVertex.UV2.Y;
  91. }
  92. if (hasColor)
  93. {
  94. globalVertex.Color = createIPoint4(babylonMesh.colors, i).ToArray();
  95. }
  96. globalVertices.Add(globalVertex);
  97. }
  98. // Retreive indices from babylon mesh
  99. List<ushort> indices = new List<ushort>();
  100. indices = babylonMesh.indices.ToList().ConvertAll(new Converter<int, ushort>(n => (ushort)n));
  101. // Swap face side
  102. for (int i = 0; i < indices.Count; i += 3)
  103. {
  104. var tmp = indices[i];
  105. indices[i] = indices[i + 2];
  106. indices[i+2] = tmp;
  107. }
  108. // --------------------------
  109. // ------- Init glTF --------
  110. // --------------------------
  111. RaiseMessage("GLTFExporter.Mesh | Init glTF", 1);
  112. // Mesh
  113. var gltfMesh = new GLTFMesh { name = babylonMesh.name };
  114. gltfMesh.index = gltf.MeshesList.Count;
  115. gltf.MeshesList.Add(gltfMesh);
  116. gltfNode.mesh = gltfMesh.index;
  117. gltfMesh.gltfNode = gltfNode;
  118. // MeshPrimitive
  119. var meshPrimitives = new List<GLTFMeshPrimitive>();
  120. var meshPrimitive = new GLTFMeshPrimitive
  121. {
  122. attributes = new Dictionary<string, int>(),
  123. mode = GLTFMeshPrimitive.FillMode.TRIANGLES // TODO reteive info from babylon material
  124. };
  125. meshPrimitives.Add(meshPrimitive);
  126. // Buffer
  127. var buffer = new GLTFBuffer
  128. {
  129. uri = gltfMesh.name + ".bin"
  130. };
  131. buffer.index = gltf.BuffersList.Count;
  132. gltf.BuffersList.Add(buffer);
  133. // BufferView - Scalar
  134. var bufferViewScalar = new GLTFBufferView
  135. {
  136. name = "bufferViewScalar",
  137. buffer = buffer.index,
  138. Buffer = buffer
  139. };
  140. bufferViewScalar.index = gltf.BufferViewsList.Count;
  141. gltf.BufferViewsList.Add(bufferViewScalar);
  142. // BufferView - Vector3
  143. var bufferViewFloatVec3 = new GLTFBufferView
  144. {
  145. name = "bufferViewFloatVec3",
  146. buffer = buffer.index,
  147. Buffer = buffer,
  148. byteOffset = 0,
  149. byteStride = 12 // Field only defined for buffer views that contain vertex attributes. A vertex needs 3 * 4 bytes
  150. };
  151. bufferViewFloatVec3.index = gltf.BufferViewsList.Count;
  152. gltf.BufferViewsList.Add(bufferViewFloatVec3);
  153. // Accessor - Indices
  154. var accessorIndices = new GLTFAccessor
  155. {
  156. name = "accessorIndices",
  157. bufferView = bufferViewScalar.index,
  158. BufferView = bufferViewScalar,
  159. componentType = GLTFAccessor.ComponentType.UNSIGNED_SHORT,
  160. type = GLTFAccessor.TypeEnum.SCALAR.ToString()
  161. };
  162. accessorIndices.index = gltf.AccessorsList.Count;
  163. gltf.AccessorsList.Add(accessorIndices);
  164. meshPrimitive.indices = accessorIndices.index;
  165. // Accessor - Positions
  166. var accessorPositions = new GLTFAccessor
  167. {
  168. name = "accessorPositions",
  169. bufferView = bufferViewFloatVec3.index,
  170. BufferView = bufferViewFloatVec3,
  171. componentType = GLTFAccessor.ComponentType.FLOAT,
  172. type = GLTFAccessor.TypeEnum.VEC3.ToString(),
  173. min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue },
  174. max = new float[] { float.MinValue, float.MinValue, float.MinValue }
  175. };
  176. accessorPositions.index = gltf.AccessorsList.Count;
  177. gltf.AccessorsList.Add(accessorPositions);
  178. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index);
  179. // Accessor - Normals
  180. var accessorNormals = new GLTFAccessor
  181. {
  182. name = "accessorNormals",
  183. bufferView = bufferViewFloatVec3.index,
  184. BufferView = bufferViewFloatVec3,
  185. componentType = GLTFAccessor.ComponentType.FLOAT,
  186. type = GLTFAccessor.TypeEnum.VEC3.ToString()
  187. };
  188. accessorNormals.index = gltf.AccessorsList.Count;
  189. gltf.AccessorsList.Add(accessorNormals);
  190. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index);
  191. // BufferView - Vector4
  192. GLTFBufferView bufferViewFloatVec4 = null;
  193. // Accessor - Colors
  194. GLTFAccessor accessorColors = null;
  195. if (hasColor)
  196. {
  197. bufferViewFloatVec4 = new GLTFBufferView
  198. {
  199. name = "bufferViewFloatVec4",
  200. buffer = buffer.index,
  201. Buffer = buffer,
  202. byteOffset = 0,
  203. byteStride = 16 // Field only defined for buffer views that contain vertex attributes. A vertex needs 4 * 4 bytes
  204. };
  205. bufferViewFloatVec4.index = gltf.BufferViewsList.Count;
  206. gltf.BufferViewsList.Add(bufferViewFloatVec4);
  207. accessorColors = new GLTFAccessor
  208. {
  209. name = "accessorColors",
  210. bufferView = bufferViewFloatVec4.index,
  211. BufferView = bufferViewFloatVec4,
  212. componentType = GLTFAccessor.ComponentType.FLOAT,
  213. type = GLTFAccessor.TypeEnum.VEC4.ToString()
  214. };
  215. accessorColors.index = gltf.AccessorsList.Count;
  216. gltf.AccessorsList.Add(accessorColors);
  217. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index);
  218. }
  219. // BufferView - Vector2
  220. GLTFBufferView bufferViewFloatVec2 = null;
  221. if (hasUV ||hasUV2)
  222. {
  223. bufferViewFloatVec2 = new GLTFBufferView
  224. {
  225. name = "bufferViewFloatVec2",
  226. buffer = buffer.index,
  227. Buffer = buffer,
  228. byteStride = 8 // Field only defined for buffer views that contain vertex attributes. A vertex needs 2 * 4 bytes
  229. };
  230. bufferViewFloatVec2.index = gltf.BufferViewsList.Count;
  231. gltf.BufferViewsList.Add(bufferViewFloatVec2);
  232. }
  233. // Accessor - UV
  234. GLTFAccessor accessorUVs = null;
  235. if (hasUV)
  236. {
  237. accessorUVs = new GLTFAccessor
  238. {
  239. name = "accessorUVs",
  240. bufferView = bufferViewFloatVec2.index,
  241. BufferView = bufferViewFloatVec2,
  242. componentType = GLTFAccessor.ComponentType.FLOAT,
  243. type = GLTFAccessor.TypeEnum.VEC2.ToString()
  244. };
  245. accessorUVs.index = gltf.AccessorsList.Count;
  246. gltf.AccessorsList.Add(accessorUVs);
  247. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index);
  248. }
  249. // Accessor - UV2
  250. GLTFAccessor accessorUV2s = null;
  251. if (hasUV2)
  252. {
  253. accessorUV2s = new GLTFAccessor
  254. {
  255. name = "accessorUV2s",
  256. bufferView = bufferViewFloatVec2.index,
  257. BufferView = bufferViewFloatVec2,
  258. componentType = GLTFAccessor.ComponentType.FLOAT,
  259. type = GLTFAccessor.TypeEnum.VEC2.ToString()
  260. };
  261. accessorUV2s.index = gltf.AccessorsList.Count;
  262. gltf.AccessorsList.Add(accessorUV2s);
  263. meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index);
  264. }
  265. // --------------------------
  266. // ------ Mesh as glTF ------
  267. // --------------------------
  268. RaiseMessage("GLTFExporter.Mesh | Mesh as glTF", 1);
  269. // Material
  270. //TODO - Handle multimaterials
  271. GLTFMaterial gltfMaterial = gltf.MaterialsList.Find(material => material.id == babylonMesh.materialId);
  272. if (gltfMaterial != null)
  273. {
  274. meshPrimitive.material = gltfMaterial.index;
  275. }
  276. // Update min and max vertex position for each component (X, Y, Z)
  277. globalVertices.ForEach((globalVertex) =>
  278. {
  279. var positionArray = new float[] { globalVertex.Position.X, globalVertex.Position.Y, globalVertex.Position.Z };
  280. for (int indexComponent = 0; indexComponent < positionArray.Length; indexComponent++)
  281. {
  282. if (positionArray[indexComponent] < accessorPositions.min[indexComponent])
  283. {
  284. accessorPositions.min[indexComponent] = positionArray[indexComponent];
  285. }
  286. if (positionArray[indexComponent] > accessorPositions.max[indexComponent])
  287. {
  288. accessorPositions.max[indexComponent] = positionArray[indexComponent];
  289. }
  290. }
  291. });
  292. // Update byte length and count of accessors, bufferViews and buffers
  293. // Scalar
  294. AddElementsToAccessor(accessorIndices, indices.Count);
  295. // Vector3
  296. bufferViewFloatVec3.byteOffset = buffer.byteLength;
  297. AddElementsToAccessor(accessorPositions, globalVertices.Count);
  298. AddElementsToAccessor(accessorNormals, globalVertices.Count);
  299. // Vector4
  300. if (hasColor)
  301. {
  302. bufferViewFloatVec4.byteOffset = buffer.byteLength;
  303. AddElementsToAccessor(accessorColors, globalVertices.Count);
  304. }
  305. // Vector2
  306. if (hasUV || hasUV2)
  307. {
  308. bufferViewFloatVec2.byteOffset = buffer.byteLength;
  309. if (hasUV)
  310. {
  311. AddElementsToAccessor(accessorUVs, globalVertices.Count);
  312. }
  313. if (hasUV2)
  314. {
  315. AddElementsToAccessor(accessorUV2s, globalVertices.Count);
  316. }
  317. }
  318. // --------------------------
  319. // --------- Saving ---------
  320. // --------------------------
  321. string outputBinaryFile = Path.Combine(gltf.OutputPath, gltfMesh.name + ".bin");
  322. RaiseMessage("GLTFExporter.Mesh | Saving " + outputBinaryFile, 1);
  323. using (BinaryWriter writer = new BinaryWriter(File.Open(outputBinaryFile, FileMode.Create)))
  324. {
  325. // Binary arrays
  326. List<float> vertices = globalVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToList();
  327. List<float> normals = globalVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToList();
  328. List<float> colors = new List<float>();
  329. if (hasColor)
  330. {
  331. colors = globalVertices.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList();
  332. }
  333. List<float> uvs = new List<float>();
  334. if (hasUV)
  335. {
  336. uvs = globalVertices.SelectMany(v => new[] { v.UV.X, v.UV.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion
  337. }
  338. List<float> uvs2 = new List<float>();
  339. if (hasUV2)
  340. {
  341. uvs2 = globalVertices.SelectMany(v => new[] { v.UV2.X, v.UV2.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion
  342. }
  343. // Write data to binary file
  344. indices.ForEach(n => writer.Write(n));
  345. vertices.ForEach(n => writer.Write(n));
  346. normals.ForEach(n => writer.Write(n));
  347. colors.ForEach(n => writer.Write(n));
  348. uvs.ForEach(n => writer.Write(n));
  349. }
  350. gltfMesh.primitives = meshPrimitives.ToArray();
  351. return gltfMesh;
  352. }
  353. private IPoint2 createIPoint2(float[] array, int index)
  354. {
  355. var startIndex = index * 2;
  356. return Loader.Global.Point2.Create(array[startIndex], array[startIndex + 1]);
  357. }
  358. private IPoint3 createIPoint3(float[] array, int index)
  359. {
  360. var startIndex = index * 3;
  361. return Loader.Global.Point3.Create(array[startIndex], array[startIndex + 1], array[startIndex + 2]);
  362. }
  363. private IPoint4 createIPoint4(float[] array, int index)
  364. {
  365. var startIndex = index * 4;
  366. return Loader.Global.Point4.Create(array[startIndex], array[startIndex + 1], array[startIndex + 2], array[startIndex + 3]);
  367. }
  368. private void AddElementsToAccessor(GLTFAccessor accessor, int count)
  369. {
  370. GLTFBufferView bufferView = accessor.BufferView;
  371. GLTFBuffer buffer = bufferView.Buffer;
  372. accessor.byteOffset = bufferView.byteLength;
  373. accessor.count += count;
  374. bufferView.byteLength += accessor.getByteLength();
  375. buffer.byteLength += accessor.getByteLength();
  376. }
  377. }
  378. }