BabylonExporter.GLTFExporter.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. GLTFNode gltfRootNode;
  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. ReportProgressChanged(0);
  21. var gltf = new GLTF(Path.GetDirectoryName(outputFile));
  22. // Asset
  23. gltf.asset = new GLTFAsset
  24. {
  25. version = "2.0",
  26. generator = "Babylon2Gltf2017",
  27. copyright = "2017 (c) BabylonJS"
  28. // no minVersion
  29. };
  30. // Scene
  31. gltf.scene = 0;
  32. // Scenes
  33. GLTFScene scene = new GLTFScene();
  34. GLTFScene[] scenes = { scene };
  35. gltf.scenes = scenes;
  36. // Nodes
  37. List<BabylonNode> babylonNodes = getNodes(babylonScene);
  38. // Root nodes
  39. RaiseMessage("GLTFExporter | Exporting nodes");
  40. List<BabylonNode> babylonRootNodes = babylonNodes.FindAll(node => node.parentId == null);
  41. var progressionStep = 80.0f / babylonRootNodes.Count;
  42. var progression = 10.0f;
  43. ReportProgressChanged((int)progression);
  44. babylonMaterialsToExport = new List<BabylonMaterial>();
  45. babylonRootNodes.ForEach(babylonNode =>
  46. {
  47. exportNodeRec(babylonNode, gltf, babylonScene);
  48. progression += progressionStep;
  49. ReportProgressChanged((int)progression);
  50. CheckCancelled();
  51. });
  52. // TODO - Choose between this method and the reverse of X axis
  53. // Switch from left to right handed coordinate system
  54. var tmpNodesList = new List<int>(scene.NodesList);
  55. var rootNode = new BabylonMesh
  56. {
  57. name = "root",
  58. rotation = new float[] { 0, (float)Math.PI, 0 },
  59. scaling = new float[] { 1, 1, -1 }
  60. };
  61. scene.NodesList.Clear();
  62. gltfRootNode = ExportMesh(rootNode as BabylonMesh, gltf, null, babylonScene);
  63. gltfRootNode.ChildrenList.AddRange(tmpNodesList);
  64. // Materials
  65. RaiseMessage("GLTFExporter | Exporting materials");
  66. foreach (var babylonMaterial in babylonMaterialsToExport)
  67. {
  68. ExportMaterial(babylonMaterial, gltf);
  69. CheckCancelled();
  70. };
  71. RaiseMessage(string.Format("GLTFExporter | Nb materials exported: {0}", gltf.MaterialsList.Count), Color.Gray, 1);
  72. // Output
  73. RaiseMessage("GLTFExporter | Saving to output file");
  74. // Cast lists to arrays
  75. gltf.Prepare();
  76. var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings());
  77. var sb = new StringBuilder();
  78. var sw = new StringWriter(sb, CultureInfo.InvariantCulture);
  79. // Do not use the optimized writer because it's not necessary to truncate values
  80. // Use the bounded writer in case some values are infinity ()
  81. using (var jsonWriter = new JsonTextWriterBounded(sw))
  82. {
  83. jsonWriter.Formatting = Formatting.None;
  84. jsonSerializer.Serialize(jsonWriter, gltf);
  85. }
  86. string outputGltfFile = Path.ChangeExtension(outputFile, "gltf");
  87. File.WriteAllText(outputGltfFile, sb.ToString());
  88. // Binary
  89. if (generateBinary)
  90. {
  91. // TODO - Export glTF data to binary format .glb
  92. RaiseError("GLTFExporter | TODO - Generating binary files");
  93. }
  94. ReportProgressChanged(100);
  95. }
  96. private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null)
  97. {
  98. GLTFNode gltfNode = null;
  99. if (babylonNode.GetType() == typeof(BabylonMesh))
  100. {
  101. gltfNode = ExportMesh(babylonNode as BabylonMesh, gltf, gltfParentNode, babylonScene);
  102. }
  103. else if (babylonNode.GetType() == typeof(BabylonCamera))
  104. {
  105. GLTFCamera gltfCamera = ExportCamera(babylonNode as BabylonCamera, gltf, gltfParentNode);
  106. gltfNode = gltfCamera.gltfNode;
  107. }
  108. else if (babylonNode.GetType() == typeof(BabylonLight))
  109. {
  110. if (isNodeRelevantToExport(babylonNode, babylonScene))
  111. {
  112. // Export light nodes as empty nodes (no lights in glTF 2.0 core)
  113. 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);
  114. gltfNode = ExportLight(babylonNode as BabylonLight, gltf, gltfParentNode);
  115. }
  116. else
  117. {
  118. RaiseMessage($"GLTFExporter | Light named {babylonNode.name} is not relevant to export", 1);
  119. }
  120. }
  121. else
  122. {
  123. RaiseError($"Node named {babylonNode.name} as no exporter", 1);
  124. }
  125. CheckCancelled();
  126. // If parent is exported successfully...
  127. if (gltfNode != null)
  128. {
  129. // ...export its children
  130. List<BabylonNode> babylonDescendants = getDescendants(babylonNode, babylonScene);
  131. babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode));
  132. }
  133. }
  134. private List<BabylonNode> getNodes(BabylonScene babylonScene)
  135. {
  136. List<BabylonNode> babylonNodes = new List<BabylonNode>();
  137. if (babylonScene.meshes != null)
  138. {
  139. babylonNodes.AddRange(babylonScene.meshes);
  140. }
  141. if (babylonScene.lights != null)
  142. {
  143. babylonNodes.AddRange(babylonScene.lights);
  144. }
  145. if (babylonScene.cameras != null)
  146. {
  147. babylonNodes.AddRange(babylonScene.cameras);
  148. }
  149. return babylonNodes;
  150. }
  151. private List<BabylonNode> getDescendants(BabylonNode babylonNode, BabylonScene babylonScene)
  152. {
  153. List<BabylonNode> babylonNodes = getNodes(babylonScene);
  154. return babylonNodes.FindAll(node => node.parentId == babylonNode.id);
  155. }
  156. /// <summary>
  157. /// Return true if node descendant hierarchy has any Mesh or Camera to export
  158. /// </summary>
  159. private bool isNodeRelevantToExport(BabylonNode babylonNode, BabylonScene babylonScene)
  160. {
  161. var type = babylonNode.GetType();
  162. if (type == typeof(BabylonMesh) ||
  163. type == typeof(BabylonCamera))
  164. {
  165. return true;
  166. }
  167. // Descandant recursivity
  168. List<BabylonNode> babylonDescendants = getDescendants(babylonNode, babylonScene);
  169. int indexDescendant = 0;
  170. while (indexDescendant < babylonDescendants.Count) // while instead of for to stop as soon as a relevant node has been found
  171. {
  172. if (isNodeRelevantToExport(babylonDescendants[indexDescendant], babylonScene))
  173. {
  174. return true;
  175. }
  176. indexDescendant++;
  177. }
  178. // No relevant node found in hierarchy
  179. return false;
  180. }
  181. }
  182. }