noalak 8 лет назад
Родитель
Сommit
06eba9ae6b

+ 2 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonMesh.cs

@@ -110,6 +110,8 @@ namespace BabylonExport.Entities
         [DataMember]
         public string tags { get; set; }
 
+        public bool isDummy = false;
+
         public BabylonMesh()
         {
             isEnabled = true;

+ 16 - 3
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Camera.cs

@@ -7,12 +7,23 @@ namespace Max2Babylon
 {
     partial class BabylonExporter
     {
-        private void ExportCamera(IIGameScene scene,  IIGameNode cameraNode, BabylonScene babylonScene)
+        private bool IsCameraExportable(IIGameNode cameraNode)
         {
             if (cameraNode.MaxNode.GetBoolProperty("babylonjs_noexport"))
             {
-                return;
+                return false;
             }
+
+            return true;
+        }
+
+        private BabylonCamera ExportCamera(IIGameScene scene,  IIGameNode cameraNode, BabylonScene babylonScene)
+        {
+            if (IsCameraExportable(cameraNode) == false)
+            {
+                return null;
+            }
+
             var gameCamera = cameraNode.IGameObject.AsGameCamera();
             var maxCamera = gameCamera.MaxObject as ICameraObject;
             var initialized = gameCamera.InitializeData;
@@ -23,7 +34,7 @@ namespace Max2Babylon
             babylonCamera.id = cameraNode.MaxNode.GetGuid().ToString();
             if (cameraNode.NodeParent != null)
             {
-                babylonCamera.parentId = GetParentID(cameraNode.NodeParent, babylonScene, scene);
+                babylonCamera.parentId = cameraNode.NodeParent.MaxNode.GetGuid().ToString();
             }
 
             babylonCamera.fov = Tools.ConvertFov(maxCamera.GetFOV(0, Tools.Forever));
@@ -135,6 +146,8 @@ namespace Max2Babylon
             }
 
             babylonScene.CamerasList.Add(babylonCamera);
+
+            return babylonCamera;
         }
     }
 }

+ 15 - 3
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Light.cs

@@ -25,11 +25,21 @@ namespace Max2Babylon
             babylonScene.LightsList.Add(babylonLight);
         }
 
-        private void ExportLight(IIGameScene scene, IIGameNode lightNode, BabylonScene babylonScene)
+        private bool IsLightExportable(IIGameNode lightNode)
         {
             if (lightNode.MaxNode.GetBoolProperty("babylonjs_noexport"))
             {
-                return;
+                return false;
+            }
+
+            return true;
+        }
+
+        private BabylonLight ExportLight(IIGameScene scene, IIGameNode lightNode, BabylonScene babylonScene)
+        {
+            if (IsLightExportable(lightNode) == false)
+            {
+                return null;
             }
 
             var gameLight = lightNode.IGameObject.AsGameLight();
@@ -41,7 +51,7 @@ namespace Max2Babylon
             babylonLight.id = lightNode.MaxNode.GetGuid().ToString();
             if (lightNode.NodeParent != null)
             {
-                babylonLight.parentId = GetParentID(lightNode.NodeParent, babylonScene, scene);
+                babylonLight.parentId = lightNode.NodeParent.MaxNode.GetGuid().ToString();
             }
 
             // Type
@@ -222,6 +232,8 @@ namespace Max2Babylon
             }
 
             babylonScene.LightsList.Add(babylonLight);
+            
+            return babylonLight;
         }
     }
 }

+ 43 - 33
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs

@@ -12,17 +12,46 @@ namespace Max2Babylon
         private int bonesCount;
 
         readonly Dictionary<IIGameSkin, List<int>> skinSortedBones = new Dictionary<IIGameSkin, List<int>>();
-
-        private void ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene)
+        
+        private bool IsMeshExportable(IIGameNode meshNode)
         {
             if (meshNode.MaxNode.GetBoolProperty("babylonjs_noexport"))
             {
-                return;
+                return false;
             }
 
             if (!ExportHiddenObjects && meshNode.MaxNode.IsHidden(NodeHideFlags.None, false))
             {
-                return;
+                return false;
+            }
+
+            return true;
+        }
+
+        private BabylonNode ExportDummy(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene)
+        {
+            RaiseMessage(meshNode.Name, 1);
+
+            var gameMesh = meshNode.IGameObject.AsGameMesh();
+            bool initialized = gameMesh.InitializeData; // needed, the property is in fact a method initializing the exporter that has wrongly been auto 
+                                                        // translated into a property because it has no parameters
+
+            var babylonMesh = new BabylonMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() };
+            babylonMesh.isDummy = true;
+
+            // Position / rotation / scaling / hierarchy
+            exportNode(babylonMesh, meshNode, scene, babylonScene);
+
+            babylonScene.MeshesList.Add(babylonMesh);
+
+            return babylonMesh;
+        }
+
+        private BabylonNode ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene)
+        {
+            if (IsMeshExportable(meshNode) == false)
+            {
+                return null;
             }
 
             RaiseMessage(meshNode.Name, 1);
@@ -47,7 +76,12 @@ namespace Max2Babylon
 #endif
                     var tab = tabs[indexer];
 
-                    babylonMasterMesh = babylonScene.MeshesList.Find(_babylonMesh => _babylonMesh.id == tab.GetGuid().ToString());
+                    babylonMasterMesh = babylonScene.MeshesList.Find(_babylonMesh => {
+                               // Same id
+                        return _babylonMesh.id == tab.GetGuid().ToString() &&
+                               // Mesh is not a dummy
+                               _babylonMesh.isDummy == false;
+                    });
                 }
 
                 if (babylonMasterMesh != null)
@@ -70,7 +104,7 @@ namespace Max2Babylon
                     // Animations
                     exportAnimation(babylonInstanceMesh, meshNode);
 
-                    return;
+                    return babylonInstanceMesh;
                 }
             }
             
@@ -376,6 +410,8 @@ namespace Max2Babylon
             }
 
             babylonScene.MeshesList.Add(babylonMesh);
+
+            return babylonMesh;
         }
 
         private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, List<GlobalVertex> vertices, List<int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List<int> boneIds)
@@ -473,29 +509,6 @@ namespace Max2Babylon
 
         }
 
-        private string GetParentID(IIGameNode parentNode, BabylonScene babylonScene, IIGameScene scene)
-        {
-            var parentType = parentNode.IGameObject.IGameType;
-            var parentId = parentNode.MaxNode.GetGuid().ToString();
-            switch (parentType)
-            {
-                case Autodesk.Max.IGameObject.ObjectTypes.Light:
-                case Autodesk.Max.IGameObject.ObjectTypes.Mesh:
-                case Autodesk.Max.IGameObject.ObjectTypes.Camera:
-                    break;
-
-
-                default:
-                    // Create a dummy (empty mesh) when the type of parent node is not exportable (spline, xref...)
-                    if (babylonScene.MeshesList.All(m => m.id != parentId))
-                    {
-                        ExportMesh(scene, parentNode, babylonScene);
-                    }
-                    break;
-            }
-            return parentId;
-        }
-
         private float[] QuaternionToEulerAngles(IQuat rotation)
         {
             float rotx = 0, roty = 0, rotz = 0;
@@ -684,7 +697,7 @@ namespace Max2Babylon
             // Hierarchy
             if (maxGameNode.NodeParent != null)
             {
-                babylonAbstractMesh.parentId = GetParentID(maxGameNode.NodeParent, babylonScene, maxGameScene);
+                babylonAbstractMesh.parentId = maxGameNode.NodeParent.MaxNode.GetGuid().ToString();
             }
         }
 
@@ -761,8 +774,6 @@ namespace Max2Babylon
                     var parentWorld = meshNode.NodeParent.GetObjectTM(key);
                     worldMatrix.MultiplyBy(parentWorld.Inverse);
                 }
-
-
                 var rot = worldMatrix.Rotation;
                 return new[] { rot.X, rot.Y, rot.Z, -rot.W };
             });
@@ -776,7 +787,6 @@ namespace Max2Babylon
                     worldMatrix.MultiplyBy(parentWorld.Inverse);
                 }
                 var scale = worldMatrix.Scaling;
-
                 return new[] { scale.X, scale.Y, scale.Z };
             });
         }

+ 211 - 95
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs

@@ -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;
+            }
+        }
     }
 }