|
@@ -2,6 +2,7 @@
|
|
|
using BabylonExport.Entities;
|
|
|
using Newtonsoft.Json;
|
|
|
using System;
|
|
|
+using System.Collections.Generic;
|
|
|
using System.Diagnostics;
|
|
|
using System.Globalization;
|
|
|
using System.IO;
|
|
@@ -158,44 +159,69 @@ namespace Max2Babylon
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Cameras
|
|
|
- BabylonCamera mainCamera = null;
|
|
|
- ICameraObject mainCameraNode = null;
|
|
|
-
|
|
|
- RaiseMessage("Exporting cameras");
|
|
|
- var camerasTab = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Camera);
|
|
|
- for (int ix = 0; ix < camerasTab.Count; ++ix)
|
|
|
+ // Root nodes
|
|
|
+ RaiseMessage("Exporting nodes");
|
|
|
+ HashSet<IIGameNode> maxRootNodes = getRootNodes(gameScene);
|
|
|
+ var progressionStep = 80.0f / maxRootNodes.Count;
|
|
|
+ var progression = 10.0f;
|
|
|
+ ReportProgressChanged((int)progression);
|
|
|
+ referencedMaterials.Clear();
|
|
|
+ foreach (var maxRootNode in maxRootNodes)
|
|
|
{
|
|
|
-#if MAX2017
|
|
|
- var indexer = ix;
|
|
|
-#else
|
|
|
- var indexer = new IntPtr(ix);
|
|
|
-#endif
|
|
|
- var cameraNode = camerasTab[indexer];
|
|
|
-
|
|
|
-#if !MAX2017
|
|
|
- Marshal.FreeHGlobal(indexer);
|
|
|
-#endif
|
|
|
-
|
|
|
- ExportCamera(gameScene, cameraNode, babylonScene);
|
|
|
+ exportNodeRec(maxRootNode, babylonScene, gameScene);
|
|
|
+ progression += progressionStep;
|
|
|
+ ReportProgressChanged((int)progression);
|
|
|
+ CheckCancelled();
|
|
|
+ };
|
|
|
+ RaiseMessage(string.Format("Total meshes: {0}", babylonScene.MeshesList.Count), Color.Gray, 1);
|
|
|
|
|
|
- if (mainCamera == null && babylonScene.CamerasList.Count > 0)
|
|
|
- {
|
|
|
- mainCameraNode = (cameraNode.MaxNode.ObjectRef as ICameraObject);
|
|
|
- mainCamera = babylonScene.CamerasList[0];
|
|
|
- babylonScene.activeCameraID = mainCamera.id;
|
|
|
- RaiseMessage("Active camera set to " + mainCamera.name, Color.Green, 1, true);
|
|
|
- }
|
|
|
+ // Main camera
|
|
|
+ BabylonCamera babylonMainCamera = null;
|
|
|
+ ICameraObject maxMainCameraObject = null;
|
|
|
+ if (babylonMainCamera == null && babylonScene.CamerasList.Count > 0)
|
|
|
+ {
|
|
|
+ // Set first camera as main one
|
|
|
+ babylonMainCamera = babylonScene.CamerasList[0];
|
|
|
+ babylonScene.activeCameraID = babylonMainCamera.id;
|
|
|
+ RaiseMessage("Active camera set to " + babylonMainCamera.name, Color.Green, 1, true);
|
|
|
+
|
|
|
+ // Retreive camera node with same GUID
|
|
|
+ var maxCameraNodesAsTab = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Camera);
|
|
|
+ var maxCameraNodes = TabToList(maxCameraNodesAsTab);
|
|
|
+ var maxMainCameraNode = maxCameraNodes.Find(_camera => _camera.MaxNode.GetGuid().ToString() == babylonMainCamera.id);
|
|
|
+ maxMainCameraObject = (maxMainCameraNode.MaxNode.ObjectRef as ICameraObject);
|
|
|
}
|
|
|
|
|
|
- if (mainCamera == null)
|
|
|
+ if (babylonMainCamera == null)
|
|
|
{
|
|
|
RaiseWarning("No camera defined", 1);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- RaiseMessage(string.Format("Total: {0}", babylonScene.CamerasList.Count), Color.Gray, 1);
|
|
|
+ RaiseMessage(string.Format("Total cameras: {0}", babylonScene.CamerasList.Count), Color.Gray, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Default light
|
|
|
+ if (babylonScene.LightsList.Count == 0)
|
|
|
+ {
|
|
|
+ RaiseWarning("No light defined", 1);
|
|
|
+ RaiseWarning("A default hemispheric light was added for your convenience", 1);
|
|
|
+ ExportDefaultLight(babylonScene);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ RaiseMessage(string.Format("Total lights: {0}", babylonScene.LightsList.Count), Color.Gray, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Materials
|
|
|
+ RaiseMessage("Exporting materials");
|
|
|
+ var matsToExport = referencedMaterials.ToArray(); // Snapshot because multimaterials can export new materials
|
|
|
+ foreach (var mat in matsToExport)
|
|
|
+ {
|
|
|
+ ExportMaterial(mat, babylonScene);
|
|
|
+ CheckCancelled();
|
|
|
}
|
|
|
+ RaiseMessage(string.Format("Total: {0}", babylonScene.MaterialsList.Count + babylonScene.MultiMaterialsList.Count), Color.Gray, 1);
|
|
|
|
|
|
// Fog
|
|
|
for (var index = 0; index < Loader.Core.NumAtmospheric; index++)
|
|
@@ -222,78 +248,14 @@ namespace Max2Babylon
|
|
|
babylonScene.fogMode = 3;
|
|
|
}
|
|
|
#endif
|
|
|
- if (mainCamera != null)
|
|
|
+ if (babylonMainCamera != null)
|
|
|
{
|
|
|
- babylonScene.fogStart = mainCameraNode.GetEnvRange(0, 0, Tools.Forever);
|
|
|
- babylonScene.fogEnd = mainCameraNode.GetEnvRange(0, 1, Tools.Forever);
|
|
|
+ babylonScene.fogStart = maxMainCameraObject.GetEnvRange(0, 0, Tools.Forever);
|
|
|
+ babylonScene.fogEnd = maxMainCameraObject.GetEnvRange(0, 1, Tools.Forever);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Meshes
|
|
|
- ReportProgressChanged(10);
|
|
|
- RaiseMessage("Exporting meshes");
|
|
|
- var meshes = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Mesh);
|
|
|
- var progressionStep = 80.0f / meshes.Count;
|
|
|
- var progression = 10.0f;
|
|
|
- for (int ix = 0; ix < meshes.Count; ++ix)
|
|
|
- {
|
|
|
-#if MAX2017
|
|
|
- var indexer = ix;
|
|
|
-#else
|
|
|
- var indexer = new IntPtr(ix);
|
|
|
-#endif
|
|
|
- var meshNode = meshes[indexer];
|
|
|
-
|
|
|
-#if !MAX2017
|
|
|
- Marshal.FreeHGlobal(indexer);
|
|
|
-#endif
|
|
|
- ExportMesh(gameScene, meshNode, babylonScene);
|
|
|
-
|
|
|
-
|
|
|
- ReportProgressChanged((int)progression);
|
|
|
-
|
|
|
- progression += progressionStep;
|
|
|
-
|
|
|
- CheckCancelled();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // Materials
|
|
|
- RaiseMessage("Exporting materials");
|
|
|
- var matsToExport = referencedMaterials.ToArray(); // Snapshot because multimaterials can export new materials
|
|
|
- foreach (var mat in matsToExport)
|
|
|
- {
|
|
|
- ExportMaterial(mat, babylonScene);
|
|
|
- CheckCancelled();
|
|
|
- }
|
|
|
- RaiseMessage(string.Format("Total: {0}", babylonScene.MaterialsList.Count + babylonScene.MultiMaterialsList.Count), Color.Gray, 1);
|
|
|
-
|
|
|
- // Lights
|
|
|
- RaiseMessage("Exporting lights");
|
|
|
- var lightNodes = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Light);
|
|
|
- for (var i = 0; i < lightNodes.Count; ++i)
|
|
|
- {
|
|
|
-#if MAX2017
|
|
|
- ExportLight(gameScene, lightNodes[i], babylonScene);
|
|
|
-#else
|
|
|
- ExportLight(gameScene, lightNodes[new IntPtr(i)], babylonScene);
|
|
|
-#endif
|
|
|
- CheckCancelled();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- if (babylonScene.LightsList.Count == 0)
|
|
|
- {
|
|
|
- RaiseWarning("No light defined", 1);
|
|
|
- RaiseWarning("A default hemispheric light was added for your convenience", 1);
|
|
|
- ExportDefaultLight(babylonScene);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- RaiseMessage(string.Format("Total: {0}", babylonScene.LightsList.Count), Color.Gray, 1);
|
|
|
- }
|
|
|
-
|
|
|
// Skeletons
|
|
|
if (skins.Count > 0)
|
|
|
{
|
|
@@ -350,5 +312,159 @@ namespace Max2Babylon
|
|
|
watch.Stop();
|
|
|
RaiseMessage(string.Format("Exportation done in {0:0.00}s", watch.ElapsedMilliseconds / 1000.0), Color.Blue);
|
|
|
}
|
|
|
+
|
|
|
+ private void exportNodeRec(IIGameNode maxGameNode, BabylonScene babylonScene, IIGameScene maxGameScene)
|
|
|
+ {
|
|
|
+ BabylonNode babylonNode = null;
|
|
|
+ bool hasExporter = true;
|
|
|
+ switch (maxGameNode.IGameObject.IGameType)
|
|
|
+ {
|
|
|
+ case Autodesk.Max.IGameObject.ObjectTypes.Mesh:
|
|
|
+ babylonNode = ExportMesh(maxGameScene, maxGameNode, babylonScene);
|
|
|
+ break;
|
|
|
+ case Autodesk.Max.IGameObject.ObjectTypes.Camera:
|
|
|
+ babylonNode = ExportCamera(maxGameScene, maxGameNode, babylonScene);
|
|
|
+ break;
|
|
|
+ case Autodesk.Max.IGameObject.ObjectTypes.Light:
|
|
|
+ babylonNode = ExportLight(maxGameScene, maxGameNode, babylonScene);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ // The type of node is not exportable (helper, spline, xref...)
|
|
|
+ hasExporter = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ CheckCancelled();
|
|
|
+
|
|
|
+ // If node is not exported successfully but is significant
|
|
|
+ if (babylonNode == null &&
|
|
|
+ isNodeRelevantToExport(maxGameNode))
|
|
|
+ {
|
|
|
+ if (!hasExporter)
|
|
|
+ {
|
|
|
+ RaiseWarning($"Type '{maxGameNode.IGameObject.IGameType}' of node '{maxGameNode.Name}' has no exporter, an empty node is exported instead", 1);
|
|
|
+ }
|
|
|
+ // Create a dummy (empty mesh)
|
|
|
+ babylonNode = ExportDummy(maxGameScene, maxGameNode, babylonScene);
|
|
|
+ };
|
|
|
+
|
|
|
+ if (babylonNode != null)
|
|
|
+ {
|
|
|
+ // Export its children
|
|
|
+ for (int i = 0; i < maxGameNode.ChildCount; i++)
|
|
|
+ {
|
|
|
+ var descendant = maxGameNode.GetNodeChild(i);
|
|
|
+ exportNodeRec(descendant, babylonScene, maxGameScene);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Return true if node descendant hierarchy has any exportable Mesh, Camera or Light
|
|
|
+ /// </summary>
|
|
|
+ private bool isNodeRelevantToExport(IIGameNode maxGameNode)
|
|
|
+ {
|
|
|
+ bool isRelevantToExport;
|
|
|
+ switch (maxGameNode.IGameObject.IGameType)
|
|
|
+ {
|
|
|
+ case Autodesk.Max.IGameObject.ObjectTypes.Mesh:
|
|
|
+ isRelevantToExport = IsMeshExportable(maxGameNode);
|
|
|
+ break;
|
|
|
+ case Autodesk.Max.IGameObject.ObjectTypes.Camera:
|
|
|
+ isRelevantToExport = IsCameraExportable(maxGameNode);
|
|
|
+ break;
|
|
|
+ case Autodesk.Max.IGameObject.ObjectTypes.Light:
|
|
|
+ isRelevantToExport = IsLightExportable(maxGameNode);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ isRelevantToExport = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isRelevantToExport)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Descandant recursivity
|
|
|
+ List<IIGameNode> maxDescendants = getDescendants(maxGameNode);
|
|
|
+ int indexDescendant = 0;
|
|
|
+ while (indexDescendant < maxDescendants.Count) // while instead of for to stop as soon as a relevant node has been found
|
|
|
+ {
|
|
|
+ if (isNodeRelevantToExport(maxDescendants[indexDescendant]))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ indexDescendant++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // No relevant node found in hierarchy
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<IIGameNode> getDescendants(IIGameNode maxGameNode)
|
|
|
+ {
|
|
|
+ var maxDescendants = new List<IIGameNode>();
|
|
|
+ for (int i = 0; i < maxGameNode.ChildCount; i++)
|
|
|
+ {
|
|
|
+ maxDescendants.Add(maxGameNode.GetNodeChild(i));
|
|
|
+ }
|
|
|
+ return maxDescendants;
|
|
|
+ }
|
|
|
+
|
|
|
+ private HashSet<IIGameNode> getRootNodes(IIGameScene maxGameScene)
|
|
|
+ {
|
|
|
+ HashSet<IIGameNode> maxGameNodes = new HashSet<IIGameNode>();
|
|
|
+
|
|
|
+ Func<IIGameNode, IIGameNode> getMaxRootNode = delegate (IIGameNode maxGameNode)
|
|
|
+ {
|
|
|
+ while (maxGameNode.NodeParent != null)
|
|
|
+ {
|
|
|
+ maxGameNode = maxGameNode.NodeParent;
|
|
|
+ }
|
|
|
+ return maxGameNode;
|
|
|
+ };
|
|
|
+
|
|
|
+ Action<Autodesk.Max.IGameObject.ObjectTypes> addMaxRootNodes = delegate (Autodesk.Max.IGameObject.ObjectTypes type)
|
|
|
+ {
|
|
|
+ ITab<IIGameNode> maxGameNodesOfType = maxGameScene.GetIGameNodeByType(type);
|
|
|
+ if (maxGameNodesOfType != null)
|
|
|
+ {
|
|
|
+ TabToList(maxGameNodesOfType).ForEach(maxGameNode =>
|
|
|
+ {
|
|
|
+ var maxRootNode = getMaxRootNode(maxGameNode);
|
|
|
+ maxGameNodes.Add(maxRootNode);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ addMaxRootNodes(Autodesk.Max.IGameObject.ObjectTypes.Mesh);
|
|
|
+ addMaxRootNodes(Autodesk.Max.IGameObject.ObjectTypes.Light);
|
|
|
+ addMaxRootNodes(Autodesk.Max.IGameObject.ObjectTypes.Camera);
|
|
|
+
|
|
|
+ return maxGameNodes;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<T> TabToList<T>(ITab<T> tab)
|
|
|
+ {
|
|
|
+ if (tab == null)
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ List<T> list = new List<T>();
|
|
|
+ for (int i = 0; i < tab.Count; i++)
|
|
|
+ {
|
|
|
+#if MAX2017
|
|
|
+ var indexer = i;
|
|
|
+#else
|
|
|
+ var indexer = new IntPtr(i);
|
|
|
+ Marshal.FreeHGlobal(indexer);
|
|
|
+#endif
|
|
|
+ list.Add(tab[indexer]);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|