BabylonExporter.GLTFExporter.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. using BabylonExport.Entities;
  2. using GLTFExport.Entities;
  3. using Newtonsoft.Json;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Globalization;
  7. using System.IO;
  8. using System.Text;
  9. using Color = System.Drawing.Color;
  10. namespace Max2Babylon
  11. {
  12. internal partial class BabylonExporter
  13. {
  14. List<BabylonMaterial> babylonMaterialsToExport;
  15. private List<BabylonNode> babylonNodes;
  16. public void ExportGltf(BabylonScene babylonScene, string outputFile, bool generateBinary)
  17. {
  18. RaiseMessage("GLTFExporter | Export outputFile=" + outputFile + " generateBinary=" + generateBinary);
  19. RaiseMessage("GLTFExporter | Exportation started", Color.Blue);
  20. float progressionStep;
  21. var progression = 0.0f;
  22. ReportProgressChanged((int)progression);
  23. // Initialization
  24. initBabylonNodes(babylonScene);
  25. babylonMaterialsToExport = new List<BabylonMaterial>();
  26. var gltf = new GLTF(Path.GetDirectoryName(outputFile));
  27. // Asset
  28. gltf.asset = new GLTFAsset
  29. {
  30. version = "2.0",
  31. generator = "Babylon2Gltf2017",
  32. copyright = "2017 (c) BabylonJS"
  33. // no minVersion
  34. };
  35. // Scene
  36. gltf.scene = 0;
  37. // Scenes
  38. GLTFScene scene = new GLTFScene();
  39. GLTFScene[] scenes = { scene };
  40. gltf.scenes = scenes;
  41. // Meshes
  42. RaiseMessage("GLTFExporter | Exporting meshes");
  43. progression = 10.0f;
  44. ReportProgressChanged((int)progression);
  45. progressionStep = 40.0f / babylonScene.meshes.Length;
  46. foreach (var babylonMesh in babylonScene.meshes)
  47. {
  48. ExportMesh(babylonMesh, gltf, babylonScene);
  49. progression += progressionStep;
  50. ReportProgressChanged((int)progression);
  51. CheckCancelled();
  52. }
  53. // Root nodes
  54. RaiseMessage("GLTFExporter | Exporting nodes");
  55. List<BabylonNode> babylonRootNodes = babylonNodes.FindAll(node => node.parentId == null);
  56. progressionStep = 40.0f / babylonRootNodes.Count;
  57. babylonRootNodes.ForEach(babylonNode =>
  58. {
  59. exportNodeRec(babylonNode, gltf, babylonScene);
  60. progression += progressionStep;
  61. ReportProgressChanged((int)progression);
  62. CheckCancelled();
  63. });
  64. // TODO - Choose between this method and the reverse of X axis
  65. // Switch from left to right handed coordinate system
  66. RaiseMessage("GLTFExporter | Exporting root node");
  67. var tmpNodesList = new List<int>(scene.NodesList);
  68. var rootNode = new BabylonMesh
  69. {
  70. name = "root",
  71. rotation = new float[] { 0, (float)Math.PI, 0 },
  72. scaling = new float[] { 1, 1, -1 },
  73. idGroupInstance = -1
  74. };
  75. scene.NodesList.Clear();
  76. GLTFNode gltfRootNode = ExportAbstractMesh(rootNode, gltf, null);
  77. gltfRootNode.ChildrenList.AddRange(tmpNodesList);
  78. // Materials
  79. RaiseMessage("GLTFExporter | Exporting materials");
  80. foreach (var babylonMaterial in babylonMaterialsToExport)
  81. {
  82. ExportMaterial(babylonMaterial, gltf);
  83. CheckCancelled();
  84. };
  85. RaiseMessage(string.Format("GLTFExporter | Nb materials exported: {0}", gltf.MaterialsList.Count), Color.Gray, 1);
  86. // Output
  87. RaiseMessage("GLTFExporter | Saving to output file");
  88. // Cast lists to arrays
  89. gltf.Prepare();
  90. var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings());
  91. var sb = new StringBuilder();
  92. var sw = new StringWriter(sb, CultureInfo.InvariantCulture);
  93. // Do not use the optimized writer because it's not necessary to truncate values
  94. // Use the bounded writer in case some values are infinity ()
  95. using (var jsonWriter = new JsonTextWriterBounded(sw))
  96. {
  97. jsonWriter.Formatting = Formatting.None;
  98. jsonSerializer.Serialize(jsonWriter, gltf);
  99. }
  100. string outputGltfFile = Path.ChangeExtension(outputFile, "gltf");
  101. File.WriteAllText(outputGltfFile, sb.ToString());
  102. // Binary
  103. if (generateBinary)
  104. {
  105. // TODO - Export glTF data to binary format .glb
  106. RaiseError("GLTFExporter | TODO - Generating binary files");
  107. }
  108. ReportProgressChanged(100);
  109. }
  110. private List<BabylonNode> initBabylonNodes(BabylonScene babylonScene)
  111. {
  112. babylonNodes = new List<BabylonNode>();
  113. if (babylonScene.meshes != null)
  114. {
  115. int idGroupInstance = 0;
  116. foreach (var babylonMesh in babylonScene.meshes)
  117. {
  118. var babylonAbstractMeshes = new List<BabylonAbstractMesh>();
  119. babylonAbstractMeshes.Add(babylonMesh);
  120. if (babylonMesh.instances != null)
  121. {
  122. babylonAbstractMeshes.AddRange(babylonMesh.instances);
  123. }
  124. // Add mesh and instances to node list
  125. babylonNodes.AddRange(babylonAbstractMeshes);
  126. // Tag mesh and instances with an identifier
  127. babylonAbstractMeshes.ForEach(babylonAbstractMesh => babylonAbstractMesh.idGroupInstance = idGroupInstance);
  128. idGroupInstance++;
  129. }
  130. }
  131. if (babylonScene.lights != null)
  132. {
  133. babylonNodes.AddRange(babylonScene.lights);
  134. }
  135. if (babylonScene.cameras != null)
  136. {
  137. babylonNodes.AddRange(babylonScene.cameras);
  138. }
  139. return babylonNodes;
  140. }
  141. private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null)
  142. {
  143. GLTFNode gltfNode = null;
  144. var type = babylonNode.GetType();
  145. if (type == typeof(BabylonAbstractMesh) ||
  146. type.IsSubclassOf(typeof(BabylonAbstractMesh)))
  147. {
  148. gltfNode = ExportAbstractMesh(babylonNode as BabylonAbstractMesh, gltf, gltfParentNode);
  149. }
  150. else if (type == typeof(BabylonCamera))
  151. {
  152. GLTFCamera gltfCamera = ExportCamera(babylonNode as BabylonCamera, gltf, gltfParentNode);
  153. gltfNode = gltfCamera.gltfNode;
  154. }
  155. else if (type == typeof(BabylonLight))
  156. {
  157. if (isNodeRelevantToExport(babylonNode, babylonScene))
  158. {
  159. // Export light nodes as empty nodes (no lights in glTF 2.0 core)
  160. RaiseWarning($"GLTFExporter | Light named {babylonNode.name} has children but lights are not exported with glTF 2.0 core version. An empty node is used instead.", 1);
  161. gltfNode = ExportLight(babylonNode as BabylonLight, gltf, gltfParentNode);
  162. }
  163. else
  164. {
  165. RaiseMessage($"GLTFExporter | Light named {babylonNode.name} is not relevant to export", 1);
  166. }
  167. }
  168. else
  169. {
  170. RaiseError($"Node named {babylonNode.name} as no exporter", 1);
  171. }
  172. CheckCancelled();
  173. // If node is exported successfully...
  174. if (gltfNode != null)
  175. {
  176. // ...export its children
  177. List<BabylonNode> babylonDescendants = getDescendants(babylonNode, babylonScene);
  178. babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode));
  179. }
  180. }
  181. private List<BabylonNode> getDescendants(BabylonNode babylonNode, BabylonScene babylonScene)
  182. {
  183. return babylonNodes.FindAll(node => node.parentId == babylonNode.id);
  184. }
  185. /// <summary>
  186. /// Return true if node descendant hierarchy has any Mesh or Camera to export
  187. /// </summary>
  188. private bool isNodeRelevantToExport(BabylonNode babylonNode, BabylonScene babylonScene)
  189. {
  190. var type = babylonNode.GetType();
  191. if (type == typeof(BabylonAbstractMesh) ||
  192. type.IsSubclassOf(typeof(BabylonAbstractMesh)) ||
  193. type == typeof(BabylonCamera))
  194. {
  195. return true;
  196. }
  197. // Descandant recursivity
  198. List<BabylonNode> babylonDescendants = getDescendants(babylonNode, babylonScene);
  199. int indexDescendant = 0;
  200. while (indexDescendant < babylonDescendants.Count) // while instead of for to stop as soon as a relevant node has been found
  201. {
  202. if (isNodeRelevantToExport(babylonDescendants[indexDescendant], babylonScene))
  203. {
  204. return true;
  205. }
  206. indexDescendant++;
  207. }
  208. // No relevant node found in hierarchy
  209. return false;
  210. }
  211. }
  212. }