Esempio n. 1
0
        private void ExportHierarchy(BabylonNode babylonNode, MFnTransform mFnTransform)
        {
            if (mFnTransform.parentCount != 0)
            {
                RaiseVerbose("BabylonExporter.Node | Hierarchy", 2);

                var mDagPath = new MDagPath(mFnTransform.dagPath);
                mDagPath.pop();

                MObject parentMObject = mDagPath.node;
                // Children of World node don't have parent in Babylon
                if (parentMObject.apiType != MFn.Type.kWorld)
                {
                    MFnDagNode mFnTransformParent = new MFnDagNode(parentMObject);
                    babylonNode.parentId = mFnTransformParent.uuid().asString();
                }
            }
        }
Esempio n. 2
0
        private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null)
        {
            GLTFNode gltfNode = null;
            var      type     = babylonNode.GetType();

            if (type == typeof(BabylonAbstractMesh) ||
                type.IsSubclassOf(typeof(BabylonAbstractMesh)))
            {
                gltfNode = ExportAbstractMesh(babylonNode as BabylonAbstractMesh, gltf, gltfParentNode, babylonScene);
            }
            else if (type == typeof(BabylonCamera))
            {
                GLTFCamera gltfCamera = ExportCamera(babylonNode as BabylonCamera, gltf, gltfParentNode);
                gltfNode = gltfCamera.gltfNode;
            }
            else if (type == typeof(BabylonLight))
            {
                if (isNodeRelevantToExport(babylonNode))
                {
                    // Export light nodes as empty nodes (no lights in glTF 2.0 core)
                    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);
                    gltfNode = ExportLight(babylonNode as BabylonLight, gltf, gltfParentNode);
                }
                else
                {
                    RaiseMessage($"GLTFExporter | Light named {babylonNode.name} is not relevant to export", 1);
                }
            }
            else
            {
                RaiseError($"Node named {babylonNode.name} as no exporter", 1);
            }

            CheckCancelled();

            // If node is exported successfully...
            if (gltfNode != null)
            {
                // ...export its children
                List <BabylonNode> babylonDescendants = getDescendants(babylonNode);
                babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode));
            }
        }
Esempio n. 3
0
        private void exportNodeTypeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null)
        {
            var type = babylonNode.GetType();

            logger.RaiseMessage($"GLTFExporter | ExportNode {babylonNode.name} of Type {type.ToString()}", 1);

            var      nodeNodePair = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id));
            GLTFNode gltfNode     = nodeNodePair.Value;

            if (gltfNode != null)
            {
                if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh)))
                {
                    gltfNode = ExportAbstractMesh(ref gltfNode, babylonNode as BabylonAbstractMesh, gltf, gltfParentNode, babylonScene);
                }
                else if (type == typeof(BabylonCamera))
                {
                    GLTFCamera gltfCamera = ExportCamera(ref gltfNode, babylonNode as BabylonCamera, gltf, gltfParentNode);
                }
                else if (type == typeof(BabylonLight) || type.IsSubclassOf(typeof(BabylonLight)))
                {
                    if (((BabylonLight)babylonNode).type != 3)
                    {
                        ExportLight(ref gltfNode, babylonNode as BabylonLight, gltf, gltfParentNode, babylonScene);
                    }
                }
                else if (type == typeof(BabylonNode))
                {
                    logger.RaiseVerbose($"Node named {babylonNode.name} already exported as gltfNode", 1);
                }
                else
                {
                    logger.RaiseError($"Node named {babylonNode.name} has no exporter", 1);
                }

                logger.CheckCancelled();

                // ...export its children
                List <BabylonNode> babylonDescendants = getDescendants(babylonNode);
                babylonDescendants.ForEach(descendant => exportNodeTypeRec(descendant, gltf, babylonScene, gltfNode));
            }
        }
Esempio n. 4
0
        private void SetNodePosition(ref BabylonNode node, ref BabylonScene babylonScene, float[] newPosition)
        {
            float[] offset = new float[] { newPosition[0] - node.position[0], newPosition[1] - node.position[1], newPosition[2] - node.position[2] };
            node.position = newPosition;

            List <BabylonAnimation> animations        = new List <BabylonAnimation>(node.animations);
            BabylonAnimation        animationPosition = animations.Find(animation => animation.property.Equals("position"));

            if (animationPosition != null)
            {
                foreach (BabylonAnimationKey key in animationPosition.keys)
                {
                    key.values = new float[] {
                        key.values[0] + offset[0],
                        key.values[1] + offset[1],
                        key.values[2] + offset[2]
                    };
                }
            }
        }
Esempio n. 5
0
        private void exportAnimation(BabylonNode babylonNode, IIGameNode maxGameNode)
        {
            var animations = new List <BabylonAnimation>();

            GenerateCoordinatesAnimations(maxGameNode, animations);

            if (!ExportFloatController(maxGameNode.MaxNode.VisController, "visibility", animations))
            {
                ExportFloatAnimation("visibility", animations, key => new[] { maxGameNode.MaxNode.GetVisibility(key, Tools.Forever) });
            }

            babylonNode.animations = animations.ToArray();

            if (maxGameNode.MaxNode.GetBoolProperty("babylonjs_autoanimate", 1))
            {
                babylonNode.autoAnimate     = true;
                babylonNode.autoAnimateFrom = (int)maxGameNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from");
                babylonNode.autoAnimateTo   = (int)maxGameNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to", 100);
                babylonNode.autoAnimateLoop = maxGameNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop", 1);
            }
        }
Esempio n. 6
0
        private IList <BabylonAnimation> GetSubAnimations(BabylonNode babylonNode, float from, float to)
        {
            IList <BabylonAnimation> subAnimations = new List <BabylonAnimation>();

            foreach (BabylonAnimation nodeAnimation in babylonNode.animations)
            {
                // clone the animation
                BabylonAnimation animation = (BabylonAnimation)nodeAnimation.Clone();

                // Select usefull keys
                var keys = animation.keysFull = animation.keysFull.FindAll(k => from <= k.frame && k.frame <= to);

                // Optimize these keys
                OptimizeAnimations(keys, true);

                //
                animation.keys = keys.ToArray();
                subAnimations.Add(animation);
            }

            return(subAnimations);
        }
Esempio n. 7
0
        private void ExportTransform(BabylonNode babylonNode, MFnTransform mFnTransform)
        {
            // Position / rotation / scaling
            RaiseVerbose("BabylonExporter.Node | ExportTransform", 2);
            float[] position           = null;
            float[] rotationQuaternion = null;
            float[] rotation           = null;
            float[] scaling            = null;
            BabylonVector3.EulerRotationOrder rotationOrder = BabylonVector3.EulerRotationOrder.XYZ;
            GetTransform(mFnTransform, ref position, ref rotationQuaternion, ref rotation, ref rotationOrder, ref scaling);

            babylonNode.position = position;
            if (_exportQuaternionsInsteadOfEulers)
            {
                babylonNode.rotationQuaternion = rotationQuaternion;
            }
            else
            {
                babylonNode.rotation = rotation;
            }
            babylonNode.scaling = scaling;
        }
Esempio n. 8
0
        private IList <BabylonAnimation> GetSubAnimations(BabylonNode babylonNode, float from, float to)
        {
            IList <BabylonAnimation> subAnimations = new List <BabylonAnimation>();

            foreach (BabylonAnimation nodeAnimation in babylonNode.animations)
            {
                // clone the animation
                BabylonAnimation animation = (BabylonAnimation)nodeAnimation.Clone();

                // Select usefull keys
                var  keys = animation.keysFull.FindAll(k => from <= k.frame && k.frame <= to);
                bool keysInRangeAreRelevant = true;

                // Optimize these keys
                if (exportParameters.optimizeAnimations)
                {
                    OptimizeAnimations(keys, true);
                    keysInRangeAreRelevant = IsAnimationKeysRelevant(keys, animation.property);

                    // if we are baking the animation frames, then do a less efficient check against all frames in the scene for this animation channel if the first check fails.
                    if (!keysInRangeAreRelevant && exportParameters.bakeAnimationFrames)
                    {
                        List <BabylonAnimationKey> optimizedKeysFull = new List <BabylonAnimationKey>(nodeAnimation.keysFull);
                        OptimizeAnimations(optimizedKeysFull, true);
                        keysInRangeAreRelevant = IsAnimationKeysRelevant(optimizedKeysFull, nodeAnimation.property);
                    }
                }

                // If animation keys should be included in export, add to animation list.
                if (keysInRangeAreRelevant)
                {
                    animation.keys = keys.ToArray();
                    subAnimations.Add(animation);
                }
            }

            return(subAnimations);
        }
        private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null)
        {
            GLTFNode gltfNode = null;

            if (babylonNode.GetType() == typeof(BabylonMesh))
            {
                GLTFMesh gltfMesh = ExportMesh(babylonNode as BabylonMesh, gltf, gltfParentNode);
                if (gltfMesh != null)
                {
                    gltfNode = gltfMesh.gltfNode;
                }
            }
            else if (babylonNode.GetType() == typeof(BabylonCamera))
            {
                // TODO - Export camera nodes
                RaiseError($"TODO - Export camera node named {babylonNode.name}", 1);
            }
            else if (babylonNode.GetType() == typeof(BabylonLight))
            {
                // TODO - Export light nodes as empty nodes (no lights in glTF 2.0 core)
                RaiseError($"TODO - Export light node named {babylonNode.name}", 1);
                RaiseWarning($"GLTFExporter.Node | Light named {babylonNode.name} has children but lights are not exported with glTF 2.0 core version. An empty node is used instead.", 1);
            }
            else
            {
                RaiseError($"Node named {babylonNode.name} as no exporter", 1);
            }

            CheckCancelled();

            // If parent is exported successfully...
            if (gltfNode != null)
            {
                // ...export its children
                List <BabylonNode> babylonDescendants = getDescendants(babylonNode, babylonScene);
                babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode));
            }
        }
Esempio n. 10
0
        private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null)
        {
            GLTFNode gltfNode = ExportNode(babylonNode, gltf, babylonScene, gltfParentNode);

            if (gltfNode != null)
            {
                logger.CheckCancelled();

                // export its tag
                if (!string.IsNullOrEmpty(babylonNode.tags))
                {
                    if (gltfNode.extras == null)
                    {
                        gltfNode.extras = new Dictionary <string, object>();
                    }
                    gltfNode.extras["tags"] = babylonNode.tags;
                }

                // ...export its children
                List <BabylonNode> babylonDescendants = getDescendants(babylonNode);
                babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode));
            }
        }
        private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene)
        {
            AnimationGroupList animationList = new AnimationGroupList();

            animationList.LoadFromData();

            gltf.AnimationsList.Clear();
            gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationList.Count);

            foreach (AnimationGroup animGroup in animationList)
            {
                GLTFAnimation gltfAnimation = new GLTFAnimation();
                gltfAnimation.name = animGroup.Name;
                foreach (uint nodeHandle in animGroup.NodeHandles)
                {
                    // todo: make something a little more efficient..
                    IINode      maxNode     = Loader.Core.RootNode.FindChildNode(nodeHandle);
                    string      id          = maxNode.GetGuid().ToString();
                    BabylonNode babylonNode = babylonNodes.Find(node => node.id.Equals(id));

                    if (babylonNode != null && nodeToGltfNodeMap.TryGetValue(babylonNode, out GLTFNode gltfNode))
                    {
                        ExportNodeAnimation(gltfAnimation, animGroup.FrameStart, animGroup.FrameEnd, gltf, babylonNode, gltfNode, babylonScene);
                    }

                    // export all bones that match this id
                    foreach (KeyValuePair <BabylonBone, GLTFNode> pair in boneToGltfNodeMap)
                    {
                        if (pair.Key.id.Equals(id))
                        {
                            ExportBoneAnimation(gltfAnimation, animGroup.FrameStart, animGroup.FrameEnd, gltf, pair.Key, pair.Value);
                        }
                    }
                }
                gltf.AnimationsList.Add(gltfAnimation);
            }
        }
        private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null)
        {
            var type = babylonNode.GetType();

            GLTFNode gltfNode = ExportNode(babylonNode, gltf, babylonScene, gltfParentNode);

            if (gltfNode != null)
            {
                if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh)))
                {
                    gltfNode = ExportAbstractMesh(ref gltfNode, babylonNode as BabylonAbstractMesh, gltf, gltfParentNode, babylonScene);
                }
                else if (type == typeof(BabylonCamera))
                {
                    GLTFCamera gltfCamera = ExportCamera(ref gltfNode, babylonNode as BabylonCamera, gltf, gltfParentNode);
                }
                else if (type == typeof(BabylonLight) || type.IsSubclassOf(typeof(BabylonLight)))
                {
                    if (_exportKHRLightsPunctual)
                    {
                        RaiseMessage("exporting light", 1);
                        ExportLight(ref gltfNode, babylonNode as BabylonLight, gltf, gltfParentNode, babylonScene);
                    }
                }
                else
                {
                    RaiseError($"Node named {babylonNode.name} as no exporter", 1);
                }

                CheckCancelled();

                // ...export its children
                List <BabylonNode> babylonDescendants = getDescendants(babylonNode);
                babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode));
            }
        }
Esempio n. 13
0
        public void Export(ExportParameters exportParameters)
        {
            // Check input text is valid
            var    scaleFactorFloat = 1.0f;
            string scaleFactor      = exportParameters.scaleFactor;

            try
            {
                scaleFactor      = scaleFactor.Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator);
                scaleFactor      = scaleFactor.Replace(",", System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator);
                scaleFactorFloat = float.Parse(scaleFactor);
            }
            catch
            {
                RaiseError("Scale factor is not a valid number.");
                return;
            }

            long   quality    = 0L;
            string txtQuality = exportParameters.txtQuality;

            try
            {
                quality = long.Parse(txtQuality);

                if (quality < 0 || quality > 100)
                {
                    throw new Exception();
                }
            }
            catch
            {
                RaiseError("Quality is not a valid number. It should be an integer between 0 and 100.");
                RaiseError("This parameter set the quality of jpg compression.");
                return;
            }

            this.exportParameters = exportParameters;

            var gameConversionManger = Loader.Global.ConversionManager;

            gameConversionManger.CoordSystem = Autodesk.Max.IGameConversionManager.CoordSystem.D3d;

            var gameScene = Loader.Global.IGameInterface;

            if (exportParameters.exportNode == null)
            {
                gameScene.InitialiseIGame(false);
            }
            else
            {
                gameScene.InitialiseIGame(exportParameters.exportNode, true);
            }
            gameScene.SetStaticFrame(0);

            MaxSceneFileName = gameScene.SceneFileName;

            IsCancelled = false;

            string fileExportString = exportParameters.exportNode != null
                ? $"{exportParameters.exportNode.NodeName} | {exportParameters.outputPath}"
                : exportParameters.outputPath;

            RaiseMessage($"Exportation started: {fileExportString}", Color.Blue);
            ReportProgressChanged(0);

            string tempOutputDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
            string outputDirectory     = Path.GetDirectoryName(exportParameters.outputPath);
            string outputFileName      = Path.GetFileName(exportParameters.outputPath);

            // Check directory exists
            if (!Directory.Exists(outputDirectory))
            {
                RaiseError("Exportation stopped: Output folder does not exist");
                ReportProgressChanged(100);
                return;
            }
            Directory.CreateDirectory(tempOutputDirectory);

            var outputBabylonDirectory = tempOutputDirectory;

            // Force output file extension to be babylon
            outputFileName = Path.ChangeExtension(outputFileName, "babylon");

            var babylonScene = new BabylonScene(outputBabylonDirectory);

            var rawScene = Loader.Core.RootNode;

            var watch = new Stopwatch();

            watch.Start();

            string outputFormat = exportParameters.outputFormat;

            isBabylonExported = outputFormat == "babylon" || outputFormat == "binary babylon";
            isGltfExported    = outputFormat == "gltf" || outputFormat == "glb";

            // Get scene parameters
            optimizeAnimations = !Loader.Core.RootNode.GetBoolProperty("babylonjs_donotoptimizeanimations");
            exportNonAnimated  = Loader.Core.RootNode.GetBoolProperty("babylonjs_animgroup_exportnonanimated");

            // Save scene
            if (exportParameters.autoSave3dsMaxFile)
            {
                RaiseMessage("Saving 3ds max file");
                var forceSave = Loader.Core.FileSave;

                callerForm?.BringToFront();
            }

            // Producer
            babylonScene.producer = new BabylonProducer
            {
                name = "3dsmax",
#if MAX2019
                version = "2019",
#elif MAX2018
                version = "2018",
#elif MAX2017
                version = "2017",
#else
                version = Loader.Core.ProductVersion.ToString(),
#endif
                exporter_version = exporterVersion,
                file             = outputFileName
            };

            // Global
            babylonScene.autoClear    = true;
            babylonScene.clearColor   = Loader.Core.GetBackGround(0, Tools.Forever).ToArray();
            babylonScene.ambientColor = Loader.Core.GetAmbient(0, Tools.Forever).ToArray();

            babylonScene.gravity             = rawScene.GetVector3Property("babylonjs_gravity");
            ExportQuaternionsInsteadOfEulers = rawScene.GetBoolProperty("babylonjs_exportquaternions", 1);
            if (Loader.Core.UseEnvironmentMap && Loader.Core.EnvironmentMap != null)
            {
                // Environment texture
                var environmentMap = Loader.Core.EnvironmentMap;
                // Copy image file to output if necessary
                var babylonTexture = ExportEnvironmnentTexture(environmentMap, babylonScene);
                if (babylonTexture != null)
                {
                    babylonScene.environmentTexture = babylonTexture.name;

                    // Skybox
                    babylonScene.createDefaultSkybox = rawScene.GetBoolProperty("babylonjs_createDefaultSkybox");
                    babylonScene.skyboxBlurLevel     = rawScene.GetFloatProperty("babylonjs_skyboxBlurLevel");
                }
            }

            // Sounds
            var soundName = rawScene.GetStringProperty("babylonjs_sound_filename", "");

            if (!string.IsNullOrEmpty(soundName))
            {
                var filename = Path.GetFileName(soundName);

                var globalSound = new BabylonSound
                {
                    autoplay = rawScene.GetBoolProperty("babylonjs_sound_autoplay", 1),
                    loop     = rawScene.GetBoolProperty("babylonjs_sound_loop", 1),
                    name     = filename
                };

                babylonScene.SoundsList.Add(globalSound);

                if (isBabylonExported)
                {
                    try
                    {
                        File.Copy(soundName, Path.Combine(babylonScene.OutputPath, filename), true);
                    }
                    catch
                    {
                    }
                }
            }

            // 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();
            Tools.guids.Clear();
            // Reseting is optionnal. It makes each morph target manager export starts from id = 0.
            BabylonMorphTargetManager.Reset();
            foreach (var maxRootNode in maxRootNodes)
            {
                BabylonNode node = exportNodeRec(maxRootNode, babylonScene, gameScene);

                // if we're exporting from a specific node, reset the pivot to {0,0,0}
                if (node != null && exportParameters.exportNode != null)
                {
                    SetNodePosition(ref node, ref babylonScene, new float[] { 0, 0, 0 });
                }

                progression += progressionStep;
                ReportProgressChanged((int)progression);
                CheckCancelled();
            }
            ;
            RaiseMessage(string.Format("Total meshes: {0}", babylonScene.MeshesList.Count), Color.Gray, 1);


            // In 3DS Max the default camera look down (in the -z direction for the 3DS Max reference (+y for babylon))
            // In Babylon the default camera look to the horizon (in the +z direction for the babylon reference)
            // In glTF the default camera look to the horizon (in the +Z direction for glTF reference)
            RaiseMessage("Update camera rotation and position", 1);
            for (int index = 0; index < babylonScene.CamerasList.Count; index++)
            {
                BabylonCamera camera = babylonScene.CamerasList[index];
                FixCamera(ref camera, ref babylonScene);
            }

            // Light for glTF
            if (isGltfExported)
            {
                RaiseMessage("Update light rotation for glTF export", 1);
                for (int index = 0; index < babylonScene.LightsList.Count; index++)
                {
                    BabylonNode light = babylonScene.LightsList[index];
                    FixNodeRotation(ref light, ref babylonScene, -Math.PI / 2);
                }
            }

            // 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 (babylonMainCamera == null)
            {
                RaiseWarning("No camera defined", 1);
            }
            else
            {
                RaiseMessage(string.Format("Total cameras: {0}", babylonScene.CamerasList.Count), Color.Gray, 1);
            }

            // Default light
            bool addDefaultLight = rawScene.GetBoolProperty("babylonjs_addDefaultLight", 1);
            if (addDefaultLight && 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);
            }

            if (scaleFactorFloat != 1.0f)
            {
                RaiseMessage("A root node is added for scaling", 1);

                // Create root node for scaling
                BabylonMesh rootNode = new BabylonMesh {
                    name = "root", id = Guid.NewGuid().ToString()
                };
                rootNode.isDummy = true;
                float rootNodeScale = scaleFactorFloat;
                rootNode.scaling = new float[3] {
                    rootNodeScale, rootNodeScale, rootNodeScale
                };

                if (ExportQuaternionsInsteadOfEulers)
                {
                    rootNode.rotationQuaternion = new float[] { 0, 0, 0, 1 };
                }
                else
                {
                    rootNode.rotation = new float[] { 0, 0, 0 };
                }

                // Update all top nodes
                var babylonNodes = new List <BabylonNode>();
                babylonNodes.AddRange(babylonScene.MeshesList);
                babylonNodes.AddRange(babylonScene.CamerasList);
                babylonNodes.AddRange(babylonScene.LightsList);
                foreach (BabylonNode babylonNode in babylonNodes)
                {
                    if (babylonNode.parentId == null)
                    {
                        babylonNode.parentId = rootNode.id;
                    }
                }

                // Store root node
                babylonScene.MeshesList.Add(rootNode);
            }

            // Materials
            if (exportParameters.exportMaterials)
            {
                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);
            }
            else
            {
                RaiseMessage("Skipping material export.");
            }

            // Fog
            for (var index = 0; index < Loader.Core.NumAtmospheric; index++)
            {
                var atmospheric = Loader.Core.GetAtmospheric(index);

                if (atmospheric.Active(0) && atmospheric.ClassName == "Fog")
                {
                    var fog = atmospheric as IStdFog;

                    RaiseMessage("Exporting fog");

                    if (fog != null)
                    {
                        babylonScene.fogColor = fog.GetColor(0).ToArray();
                        babylonScene.fogMode  = 3;
                    }
                    if (babylonMainCamera != null)
                    {
                        babylonScene.fogStart = maxMainCameraObject.GetEnvRange(0, 0, Tools.Forever);
                        babylonScene.fogEnd   = maxMainCameraObject.GetEnvRange(0, 1, Tools.Forever);
                    }
                }
            }

            // Skeletons
            if (skins.Count > 0)
            {
                RaiseMessage("Exporting skeletons");
                foreach (var skin in skins)
                {
                    ExportSkin(skin, babylonScene);
                }
            }

            // Animation group
            if (isBabylonExported)
            {
                RaiseMessage("Export animation groups");
                // add animation groups to the scene
                babylonScene.animationGroups = ExportAnimationGroups(babylonScene);

                // if there is animationGroup, then remove animations from nodes
                if (babylonScene.animationGroups.Count > 0)
                {
                    foreach (BabylonNode node in babylonScene.MeshesList)
                    {
                        node.animations = null;
                    }
                    foreach (BabylonNode node in babylonScene.LightsList)
                    {
                        node.animations = null;
                    }
                    foreach (BabylonNode node in babylonScene.CamerasList)
                    {
                        node.animations = null;
                    }
                    foreach (BabylonSkeleton skel in babylonScene.SkeletonsList)
                    {
                        foreach (BabylonBone bone in skel.bones)
                        {
                            bone.animation = null;
                        }
                    }
                }
            }


            // Output
            babylonScene.Prepare(false, false);
            if (isBabylonExported)
            {
                RaiseMessage("Saving to output file");

                var outputFile = Path.Combine(outputBabylonDirectory, outputFileName);

                var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings());
                var sb             = new StringBuilder();
                var sw             = new StringWriter(sb, CultureInfo.InvariantCulture);
                using (var jsonWriter = new JsonTextWriterOptimized(sw))
                {
                    jsonWriter.Formatting = Formatting.None;
                    jsonSerializer.Serialize(jsonWriter, babylonScene);
                }
                File.WriteAllText(outputFile, sb.ToString());

                if (exportParameters.generateManifest)
                {
                    File.WriteAllText(outputFile + ".manifest",
                                      "{\r\n\"version\" : 1,\r\n\"enableSceneOffline\" : true,\r\n\"enableTexturesOffline\" : true\r\n}");
                }

                // Binary
                if (outputFormat == "binary babylon")
                {
                    RaiseMessage("Generating binary files");
                    BabylonFileConverter.BinaryConverter.Convert(outputFile, outputBabylonDirectory + "\\Binary",
                                                                 message => RaiseMessage(message, 1),
                                                                 error => RaiseError(error, 1));
                }
            }

            ReportProgressChanged(100);

            // Export glTF
            if (isGltfExported)
            {
                bool generateBinary = outputFormat == "glb";
                ExportGltf(babylonScene, tempOutputDirectory, outputFileName, generateBinary);
            }
            // Move files to output directory
            var filePaths = Directory.GetFiles(tempOutputDirectory);
            if (outputFormat == "binary babylon")
            {
                var tempBinaryOutputDirectory = Path.Combine(tempOutputDirectory, "Binary");
                var binaryFilePaths           = Directory.GetFiles(tempBinaryOutputDirectory);
                foreach (var filePath in binaryFilePaths)
                {
                    if (filePath.EndsWith(".binary.babylon"))
                    {
                        var file         = Path.GetFileName(filePath);
                        var tempFilePath = Path.Combine(tempBinaryOutputDirectory, file);
                        var outputFile   = Path.Combine(outputDirectory, file);
                        moveFileToOutputDirectory(tempFilePath, outputFile, exportParameters);
                    }
                    else if (filePath.EndsWith(".babylonbinarymeshdata"))
                    {
                        var file         = Path.GetFileName(filePath);
                        var tempFilePath = Path.Combine(tempBinaryOutputDirectory, file);
                        var outputFile   = Path.Combine(outputDirectory, file);
                        moveFileToOutputDirectory(tempFilePath, outputFile, exportParameters);
                    }
                }
            }
            if (outputFormat == "glb")
            {
                foreach (var file_path in filePaths)
                {
                    if (Path.GetExtension(file_path) == ".glb")
                    {
                        var file         = Path.GetFileName(file_path);
                        var tempFilePath = Path.Combine(tempOutputDirectory, file);
                        var outputFile   = Path.Combine(outputDirectory, file);
                        moveFileToOutputDirectory(tempFilePath, outputFile, exportParameters);
                        break;
                    }
                }
            }
            else
            {
                foreach (var filePath in filePaths)
                {
                    var file         = Path.GetFileName(filePath);
                    var outputPath   = Path.Combine(outputDirectory, file);
                    var tempFilePath = Path.Combine(tempOutputDirectory, file);
                    moveFileToOutputDirectory(tempFilePath, outputPath, exportParameters);
                }
            }
            Directory.Delete(tempOutputDirectory, true);
            watch.Stop();

            RaiseMessage(string.Format("Exportation done in {0:0.00}s: {1}", watch.ElapsedMilliseconds / 1000.0, fileExportString), Color.Blue);
        }
Esempio n. 14
0
        /// <summary>
        /// In 3DS Max default element can look in different direction than the same default element in Babylon or in glTF.
        /// This function correct the node rotation.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="babylonScene"></param>
        /// <param name="angle"></param>
        private void FixNodeRotation(ref BabylonNode node, ref BabylonScene babylonScene, double angle)
        {
            string id = node.id;
            IList <BabylonMesh> meshes = babylonScene.MeshesList.FindAll(mesh => mesh.parentId == null ? false : mesh.parentId.Equals(id));

            RaiseMessage($"{node.name}", 2);

            // fix the vue
            // Rotation around the axis X of PI / 2 in the indirect direction for camera
            // double angle = Math.PI / 2; // for camera
            // double angle = -Math.PI / 2; // for light

            if (node.rotation != null)
            {
                node.rotation[0] += (float)angle;
            }
            if (node.rotationQuaternion != null)
            {
                BabylonQuaternion rotationQuaternion = FixCameraQuaternion(node, angle);

                node.rotationQuaternion = rotationQuaternion.ToArray();
                node.rotation           = rotationQuaternion.toEulerAngles().ToArray();
            }

            // animation
            List <BabylonAnimation> animations = new List <BabylonAnimation>(node.animations);
            BabylonAnimation        animationRotationQuaternion = animations.Find(animation => animation.property.Equals("rotationQuaternion"));

            if (animationRotationQuaternion != null)
            {
                foreach (BabylonAnimationKey key in animationRotationQuaternion.keys)
                {
                    key.values = FixCameraQuaternion(key.values, angle);
                }
            }
            else   // if the camera has a lockedTargetId, it is the extraAnimations that stores the rotation animation
            {
                if (node.extraAnimations != null)
                {
                    List <BabylonAnimation> extraAnimations = new List <BabylonAnimation>(node.extraAnimations);
                    animationRotationQuaternion = extraAnimations.Find(animation => animation.property.Equals("rotationQuaternion"));
                    if (animationRotationQuaternion != null)
                    {
                        foreach (BabylonAnimationKey key in animationRotationQuaternion.keys)
                        {
                            key.values = FixCameraQuaternion(key.values, angle);
                        }
                    }
                }
            }

            // fix direct children
            // Rotation around the axis X of -PI / 2 in the direct direction for camera children
            angle = -angle;
            foreach (var mesh in meshes)
            {
                RaiseVerbose($"{mesh.name}", 3);
                mesh.position = new float[] { mesh.position[0], mesh.position[2], -mesh.position[1] };

                // Add a rotation of PI/2 axis X in direct direction
                if (mesh.rotationQuaternion != null)
                {
                    // Rotation around the axis X of -PI / 2 in the direct direction
                    BabylonQuaternion quaternion = FixChildQuaternion(mesh, angle);

                    mesh.rotationQuaternion = quaternion.ToArray();
                }
                if (mesh.rotation != null)
                {
                    mesh.rotation[0] += (float)angle;
                }


                // Animations
                animations = new List <BabylonAnimation>(mesh.animations);
                // Position
                BabylonAnimation animationPosition = animations.Find(animation => animation.property.Equals("position"));
                if (animationPosition != null)
                {
                    foreach (BabylonAnimationKey key in animationPosition.keys)
                    {
                        key.values = new float[] { key.values[0], key.values[2], -key.values[1] };
                    }
                }

                // Rotation
                animationRotationQuaternion = animations.Find(animation => animation.property.Equals("rotationQuaternion"));
                if (animationRotationQuaternion != null)
                {
                    foreach (BabylonAnimationKey key in animationRotationQuaternion.keys)
                    {
                        key.values = FixChildQuaternion(key.values, angle);
                    }
                }
            }
        }
Esempio n. 15
0
        private void ExportNodeAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonNode babylonNode, GLTFNode gltfNode, BabylonScene babylonScene)
        {
            var channelList = gltfAnimation.ChannelList;
            var samplerList = gltfAnimation.SamplerList;

            bool exportNonAnimated = Loader.Core.RootNode.GetBoolProperty("babylonjs_animgroup_exportnonanimated");

            // Combine babylon animations from .babylon file and cached ones
            var babylonAnimations = new List <BabylonAnimation>();

            if (babylonNode.animations != null)
            {
                babylonAnimations.AddRange(babylonNode.animations);
            }
            if (babylonNode.extraAnimations != null)
            {
                babylonAnimations.AddRange(babylonNode.extraAnimations);
            }

            // Filter animations to only keep TRS ones
            babylonAnimations = babylonAnimations.FindAll(babylonAnimation => _getTargetPath(babylonAnimation.property) != null);

            // Optimize animations to only keep ones animated between start and end frames
            var optimizeAnimations = !Loader.Core.RootNode.GetBoolProperty("babylonjs_donotoptimizeanimations"); // reverse negation for clarity

            if (optimizeAnimations)
            {
                List <BabylonAnimation> babylonAnimationsOptimized = new List <BabylonAnimation>();
                foreach (BabylonAnimation babylonAnimation in babylonAnimations)
                {
                    // Filter animation keys to only keep frames between start and end
                    List <BabylonAnimationKey> keysInRangeFull = babylonAnimation.keysFull.FindAll(babylonAnimationKey => babylonAnimationKey.frame >= startFrame && babylonAnimationKey.frame <= endFrame);

                    // Optimization process always keeps first and last frames
                    OptimizeAnimations(keysInRangeFull, true);

                    if (IsAnimationKeysRelevant(keysInRangeFull))
                    {
                        // Override animation keys
                        babylonAnimation.keys = keysInRangeFull.ToArray();

                        babylonAnimationsOptimized.Add(babylonAnimation);
                    }
                }

                // From now, use optimized animations instead
                babylonAnimations = babylonAnimationsOptimized;
            }

            if (babylonAnimations.Count > 0 || exportNonAnimated)
            {
                if (babylonAnimations.Count > 0)
                {
                    RaiseMessage("GLTFExporter.Animation | Export animations of node named: " + babylonNode.name, 2);
                }
                else if (exportNonAnimated)
                {
                    RaiseMessage("GLTFExporter.Animation | Export dummy animation for node named: " + babylonNode.name, 2);
                    // Export a dummy animation
                    babylonAnimations.Add(GetDummyAnimation(gltfNode, startFrame, endFrame));
                }

                foreach (BabylonAnimation babylonAnimation in babylonAnimations)
                {
                    // Target
                    var gltfTarget = new GLTFChannelTarget
                    {
                        node = gltfNode.index
                    };
                    gltfTarget.path = _getTargetPath(babylonAnimation.property);

                    // --- Input ---
                    var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame);
                    if (accessorInput == null)
                    {
                        continue;
                    }

                    // --- Output ---
                    GLTFAccessor accessorOutput = _createAccessorOfPath(gltfTarget.path, gltf);

                    // Populate accessor
                    int            numKeys  = 0;
                    QuatCorrection quatCorr = new QuatCorrection(); // restart correction for each curve
                    foreach (var babylonAnimationKey in babylonAnimation.keys)
                    {
                        if (babylonAnimationKey.frame < startFrame)
                        {
                            continue;
                        }

                        if (babylonAnimationKey.frame > endFrame)
                        {
                            continue;
                        }

                        numKeys++;

                        // copy data before changing it in case animation groups overlap
                        float[] outputValues = new float[babylonAnimationKey.values.Length];
                        babylonAnimationKey.values.CopyTo(outputValues, 0);

                        // Switch coordinate system at object level
                        if (babylonAnimation.property == "position")
                        {
                            outputValues[2] *= -1;
                        }
                        else if (babylonAnimation.property == "rotationQuaternion")
                        {
                            quatCorr.Quat = new BabylonQuaternion(outputValues[0], outputValues[1], outputValues[2], outputValues[3]);
                            if (exportParameters.kuesaContQuats)
                            {
                                quatCorr.Quat   = new BabylonQuaternion(outputValues[0], outputValues[1], outputValues[2], outputValues[3]);
                                outputValues[0] = quatCorr.Quat.X;
                                outputValues[1] = quatCorr.Quat.Y;
                                outputValues[2] = quatCorr.Quat.Z;
                                outputValues[3] = quatCorr.Quat.W;
                            }
                            outputValues[0] *= -1;
                            outputValues[1] *= -1;
                        }

                        // Store values as bytes
                        foreach (var outputValue in outputValues)
                        {
                            accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue));
                        }
                    }
                    ;
                    accessorOutput.count = numKeys;

                    // bail out if no keyframes to export (?)
                    // todo [KeyInterpolation]: bail out only when there are no keyframes at all (?) and otherwise add the appropriate (interpolated) keyframes
                    if (numKeys == 0)
                    {
                        continue;
                    }

                    // Animation sampler
                    var gltfAnimationSampler = new GLTFAnimationSampler
                    {
                        input  = accessorInput.index,
                        output = accessorOutput.index
                    };
                    gltfAnimationSampler.index = samplerList.Count;
                    samplerList.Add(gltfAnimationSampler);

                    // Channel
                    var gltfChannel = new GLTFChannel
                    {
                        sampler = gltfAnimationSampler.index,
                        target  = gltfTarget
                    };
                    channelList.Add(gltfChannel);
                }
            }

            if (babylonNode.GetType() == typeof(BabylonMesh))
            {
                var babylonMesh = babylonNode as BabylonMesh;

                // Morph targets
                var babylonMorphTargetManager = GetBabylonMorphTargetManager(babylonScene, babylonMesh);
                if (babylonMorphTargetManager != null)
                {
                    ExportMorphTargetWeightAnimation(babylonMorphTargetManager, gltf, gltfNode, channelList, samplerList, startFrame, endFrame);
                }
            }
        }
Esempio n. 16
0
        private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene)
        {
            // Retreive and parse animation group data
            AnimationGroupList animationList = InitAnimationGroups();

            gltf.AnimationsList.Clear();
            gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationList.Count);

            if (animationList.Count <= 0)
            {
                RaiseMessage("GLTFExporter.Animation | No AnimationGroups: exporting all animations together.", 1);
                GLTFAnimation gltfAnimation = new GLTFAnimation();
                gltfAnimation.name = "All Animations";

                int minFrame = Loader.Core.AnimRange.Start / Loader.Global.TicksPerFrame;
                int maxFrame = Loader.Core.AnimRange.End / Loader.Global.TicksPerFrame;

                foreach (var pair in nodeToGltfNodeMap)
                {
                    ExportNodeAnimation(gltfAnimation, minFrame, maxFrame, gltf, pair.Key, pair.Value, babylonScene);
                }
                foreach (var pair in boneToGltfNodeMap)
                {
                    ExportBoneAnimation(gltfAnimation, minFrame, maxFrame, gltf, pair.Key, pair.Value);
                }

                if (gltfAnimation.ChannelList.Count > 0)
                {
                    gltf.AnimationsList.Add(gltfAnimation);
                }
                else
                {
                    RaiseMessage("GLTFExporter.Animation | No animation data for this animation, it is ignored.", 2);
                }
            }
            else
            {
                foreach (AnimationGroup animGroup in animationList)
                {
                    RaiseMessage("GLTFExporter.Animation | " + animGroup.Name, 1);

                    GLTFAnimation gltfAnimation = new GLTFAnimation();
                    gltfAnimation.name = animGroup.Name;

                    int startFrame = animGroup.FrameStart;
                    int endFrame   = animGroup.FrameEnd;

                    foreach (uint nodeHandle in animGroup.NodeHandles)
                    {
                        // todo: make something a little more efficient..
                        IINode maxNode = Loader.Core.RootNode.FindChildNode(nodeHandle);

                        // node could have been deleted, silently ignore it
                        if (maxNode == null)
                        {
                            continue;
                        }

                        string      id          = maxNode.GetGuid().ToString();
                        BabylonNode babylonNode = babylonNodes.Find(node => node.id.Equals(id));

                        if (babylonNode != null && nodeToGltfNodeMap.TryGetValue(babylonNode, out GLTFNode gltfNode))
                        {
                            ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, babylonScene);
                        }

                        // export all bones that match this id
                        foreach (KeyValuePair <BabylonBone, GLTFNode> pair in boneToGltfNodeMap)
                        {
                            if (pair.Key.id.Equals(id))
                            {
                                ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, pair.Key, pair.Value);
                            }
                        }
                    }

                    if (gltfAnimation.ChannelList.Count > 0)
                    {
                        gltf.AnimationsList.Add(gltfAnimation);
                    }
                    else
                    {
                        RaiseMessage("No data exported for this animation, it is ignored.", 2);
                    }
                }
            }
        }
Esempio n. 17
0
        /// <summary>
        /// Export to file
        /// </summary>
        /// <param name="outputDirectory">The directory to store the generated files</param>
        /// <param name="outputFileName">The filename to use for the generated files</param>
        /// <param name="outputFormat">The format to use for the generated files</param>
        /// <param name="generateManifest">Specifies if a manifest file should be generated</param>
        /// <param name="onlySelected">Specifies if only the selected objects should be exported</param>
        /// <param name="autoSaveMayaFile">Specifies if the Maya scene should be auto-saved</param>
        /// <param name="exportHiddenObjects">Specifies if hidden objects should be exported</param>
        /// <param name="copyTexturesToOutput">Specifies if textures should be copied to the output directory</param>
        /// <param name="optimizeVertices">Specifies if vertices should be optimized on export</param>
        /// <param name="exportTangents">Specifies if tangents should be exported</param>
        /// <param name="scaleFactor">Scales the scene by this factor</param>
        /// <param name="exportSkin">Specifies if skins should be exported</param>
        /// <param name="quality">The texture quality</param>
        /// <param name="dracoCompression">Specifies if draco compression should be used</param>
        /// <param name="exportMorphNormal">Specifies if normals should be exported for morph targets</param>
        /// <param name="exportMorphTangent">Specifies if tangents should be exported for morph targets</param>
        /// <param name="exportKHRLightsPunctual">Specifies if the KHR_lights_punctual extension should be enabled</param>
        /// <param name="exportKHRTextureTransform">Specifies if the KHR_texture_transform extension should be enabled</param>
        /// <param name="bakeAnimationFrames">Specifies if animations should be exporting keyframes directly or should manually bake out animations frame by frame</param>
        public void Export(ExportParameters exportParameters)
        {
            this.exportParameters = exportParameters;

            // Check if the animation is running
            MGlobal.executeCommand("play -q - state", out int isPlayed);
            if (isPlayed == 1)
            {
                RaiseError("Stop the animation before exporting.");
                return;
            }

            RaiseMessage("Export started", Color.Blue);
            var progression = 0.0f;

            ReportProgressChanged(progression);

            // Store export options
            this.isBabylonExported = exportParameters.outputFormat == "babylon" || exportParameters.outputFormat == "binary babylon";

            var outputBabylonDirectory = Path.GetDirectoryName(exportParameters.outputPath);

            // Check directory exists
            if (!Directory.Exists(outputBabylonDirectory))
            {
                RaiseError("Export stopped: Output folder does not exist");
                ReportProgressChanged(100);
                return;
            }

            var watch = new Stopwatch();

            watch.Start();


            var babylonScene = new BabylonScene(outputBabylonDirectory);

            // Save scene
            if (exportParameters.autoSaveSceneFile)
            {
                RaiseMessage("Saving Maya file");

                // Query expand file name
                string fileName = MGlobal.executeCommandStringResult($@"file -q -exn;");

                // If scene has already been saved previously
                if (fileName.EndsWith(".ma") || fileName.EndsWith(".mb"))
                {
                    // Name is already specified and this line will not fail
                    MFileIO.save();
                }
                else
                {
                    // Open SaveAs dialog window
                    MGlobal.executeCommand($@"fileDialog2;");
                }
            }

            // Force output file extension to be babylon
            var outputFileName = Path.ChangeExtension(Path.GetFileName(exportParameters.outputPath), "babylon");

            // Store selected nodes
            MSelectionList selectedNodes = new MSelectionList();

            MGlobal.getActiveSelectionList(selectedNodes);
            selectedNodeFullPaths = new List <string>();
            MItSelectionList mItSelectionList = new MItSelectionList(selectedNodes);

            while (!mItSelectionList.isDone)
            {
                MDagPath mDagPath = new MDagPath();
                try
                {
                    mItSelectionList.getDagPath(mDagPath);
                    selectedNodeFullPaths.Add(mDagPath.fullPathName);
                } catch
                {
                    // selected object is not a DAG object
                    // fail silently
                }

                mItSelectionList.next();
            }
            if (selectedNodeFullPaths.Count > 0)
            {
                RaiseMessage("Selected nodes full path");
                foreach (string selectedNodeFullPath in selectedNodeFullPaths)
                {
                    RaiseMessage(selectedNodeFullPath, 1);
                }
            }

            // Producer
            babylonScene.producer = new BabylonProducer
            {
                name             = "Maya",
                version          = "2018",
                exporter_version = exporterVersion,
                file             = outputFileName
            };

            // Global
            babylonScene.autoClear = true;
            // TODO - Retreive colors from Maya
            //babylonScene.clearColor = Loader.Core.GetBackGround(0, Tools.Forever).ToArray();
            //babylonScene.ambientColor = Loader.Core.GetAmbient(0, Tools.Forever).ToArray();

            babylonScene.TimelineStartFrame      = Loader.GetMinTime();
            babylonScene.TimelineEndFrame        = Loader.GetMaxTime();
            babylonScene.TimelineFramesPerSecond = Loader.GetFPS();

            // TODO - Add custom properties
            _exportQuaternionsInsteadOfEulers = true;

            PrintDAG(true);
            PrintDAG(false);

            // Store the current frame. It can be change to find a proper one for the node/bone export
            double currentTime = Loader.GetCurrentTime();

            // --------------------
            // ------ Nodes -------
            // --------------------
            RaiseMessage("Exporting nodes");

            // It makes each morph target manager export starts from id = 0.
            BabylonMorphTargetManager.Reset();

            // Clear materials
            referencedMaterials.Clear();
            multiMaterials.Clear();

            // Get all nodes
            var             dagIterator = new MItDag(MItDag.TraversalType.kDepthFirst, MFn.Type.kTransform);
            List <MDagPath> nodes       = new List <MDagPath>();

            while (!dagIterator.isDone)
            {
                MDagPath mDagPath = new MDagPath();
                dagIterator.getPath(mDagPath);

                // Check if one of its descendant (direct or not) is a mesh/camera/light/locator
                if (isNodeRelevantToExportRec(mDagPath)
                    // Ensure it's not one of the default cameras used as viewports in Maya
                    && defaultCameraNames.Contains(mDagPath.partialPathName) == false)
                {
                    nodes.Add(mDagPath);
                }
                else
                {
                    // Skip descendants
                    dagIterator.prune();
                }

                dagIterator.next();
            }
            // Export all nodes
            var progressionStep = 100.0f / nodes.Count;

            foreach (MDagPath mDagPath in nodes)
            {
                BabylonNode babylonNode = null;

                try
                {
                    switch (getApiTypeOfDirectDescendants(mDagPath))
                    {
                    case MFn.Type.kMesh:
                        babylonNode = ExportMesh(mDagPath, babylonScene);
                        break;

                    case MFn.Type.kCamera:
                        babylonNode = ExportCamera(mDagPath, babylonScene);
                        break;

                    case MFn.Type.kLight:     // Lights api type are actually kPointLight, kSpotLight...
                        babylonNode = ExportLight(mDagPath, babylonScene);
                        break;

                    case MFn.Type.kLocator:     // Camera target
                        babylonNode = ExportDummy(mDagPath, babylonScene);
                        break;
                    }
                }
                catch (Exception e)
                {
                    this.RaiseWarning(String.Format("Exception raised during export. Node will be exported as dummy node. \r\nMessage: \r\n{0} \r\n{1}", e.Message, e.InnerException), 2);
                }

                // If node is not exported successfully
                if (babylonNode == null)
                {
                    // Create a dummy (empty mesh)
                    babylonNode = ExportDummy(mDagPath, babylonScene);
                }
                ;

                // Update progress bar
                progression += progressionStep;
                ReportProgressChanged(progression);

                CheckCancelled();
            }
            RaiseMessage(string.Format("Total meshes: {0}", babylonScene.MeshesList.Count), Color.Gray, 1);


            // if nothing is enlightened, exclude all meshes
            foreach (BabylonLight light in babylonScene.LightsList)
            {
                if (light.includedOnlyMeshesIds.Length == 0)
                {
                    light.excludedMeshesIds = babylonScene.MeshesList.Select(m => m.id).ToArray();
                }
            }

            /*
             * Switch coordinate system at global level
             *
             * Add a root node with negative scaling
             * Pros - It's safer to use a root node
             * Cons - It's cleaner to switch at object level (as it is done now)
             * Use root node method when you want to be 100% sure of the output
             * Don't forget to also inverse winding order of mesh indices
             */
            //// Switch from right to left handed coordinate system
            //MUuid mUuid = new MUuid();
            //mUuid.generate();
            //var rootNode = new BabylonMesh
            //{
            //    name = "root",
            //    id = mUuid.asString(),
            //    scaling = new float[] { 1, 1, -1 }
            //};
            //foreach(var babylonMesh in babylonScene.MeshesList)
            //{
            //    // Add root meshes as child to root node
            //    if (babylonMesh.parentId == null)
            //    {
            //        babylonMesh.parentId = rootNode.id;
            //    }
            //}
            //babylonScene.MeshesList.Add(rootNode);

            // Main camera
            BabylonCamera babylonMainCamera = null;

            if (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);
            }

            if (babylonMainCamera == null)
            {
                RaiseWarning("No camera defined", 1);
            }
            else
            {
                RaiseMessage(string.Format("Total cameras: {0}", babylonScene.CamerasList.Count), Color.Gray, 1);
            }

            // Default light
            if (!exportParameters.pbrNoLight && babylonScene.LightsList.Count == 0)
            {
                RaiseWarning("No light defined", 1);
                RaiseWarning("A default ambient light was added for your convenience", 1);
                ExportDefaultLight(babylonScene);
            }
            else
            {
                RaiseMessage(string.Format("Total lights: {0}", babylonScene.LightsList.Count), Color.Gray, 1);
            }

            var sceneScaleFactor = exportParameters.scaleFactor;

            if (exportParameters.scaleFactor != 1.0f)
            {
                RaiseMessage(String.Format("A root node is added to globally scale the scene by {0}", sceneScaleFactor), 1);

                // Create root node for scaling
                BabylonMesh rootNode = new BabylonMesh {
                    name = "root", id = Tools.GenerateUUID()
                };
                rootNode.isDummy = true;
                float rootNodeScale = sceneScaleFactor;
                rootNode.scaling = new float[3] {
                    rootNodeScale, rootNodeScale, rootNodeScale
                };

                if (ExportQuaternionsInsteadOfEulers)
                {
                    rootNode.rotationQuaternion = new float[] { 0, 0, 0, 1 };
                }
                else
                {
                    rootNode.rotation = new float[] { 0, 0, 0 };
                }

                // Update all top nodes
                var babylonNodes = new List <BabylonNode>();
                babylonNodes.AddRange(babylonScene.MeshesList);
                babylonNodes.AddRange(babylonScene.CamerasList);
                babylonNodes.AddRange(babylonScene.LightsList);
                foreach (BabylonNode babylonNode in babylonNodes)
                {
                    if (babylonNode.parentId == null)
                    {
                        babylonNode.parentId = rootNode.id;
                    }
                }

                // Store root node
                babylonScene.MeshesList.Add(rootNode);
            }

            // --------------------
            // ----- Materials ----
            // --------------------
            RaiseMessage("Exporting materials");
            GenerateMaterialDuplicationDatas(babylonScene);
            foreach (var mat in referencedMaterials)
            {
                ExportMaterial(mat, babylonScene, exportParameters.pbrFull);
                CheckCancelled();
            }
            foreach (var mat in multiMaterials)
            {
                ExportMultiMaterial(mat.Key, mat.Value, babylonScene, exportParameters.pbrFull);
                CheckCancelled();
            }
            UpdateMeshesMaterialId(babylonScene);
            RaiseMessage(string.Format("Total: {0}", babylonScene.MaterialsList.Count + babylonScene.MultiMaterialsList.Count), Color.Gray, 1);


            // Export skeletons
            if (exportParameters.exportSkins && skins.Count > 0)
            {
                progressSkin     = 0;
                progressSkinStep = 100 / skins.Count;
                ReportProgressChanged(progressSkin);
                RaiseMessage("Exporting skeletons");
                foreach (var skin in skins)
                {
                    ExportSkin(skin, babylonScene);
                }
            }

            // set back the frame
            Loader.SetCurrentTime(currentTime);

            // ----------------------------
            // ----- Animation groups -----
            // ----------------------------
            RaiseMessage("Export animation groups");
            // add animation groups to the scene
            babylonScene.animationGroups = ExportAnimationGroups(babylonScene);


            if (isBabylonExported)
            {
                // if we are exporting to .Babylon then remove then remove animations from nodes if there are animation groups.
                if (babylonScene.animationGroups.Count > 0)
                {
                    // add animations of each nodes in the animGroup
                    List <BabylonNode> babylonNodes = new List <BabylonNode>();
                    babylonNodes.AddRange(babylonScene.MeshesList);
                    babylonNodes.AddRange(babylonScene.CamerasList);
                    babylonNodes.AddRange(babylonScene.LightsList);

                    foreach (BabylonNode node in babylonNodes)
                    {
                        node.animations = null;
                    }
                    foreach (BabylonSkeleton skel in babylonScene.SkeletonsList)
                    {
                        foreach (BabylonBone bone in skel.bones)
                        {
                            bone.animation = null;
                        }
                    }
                }

                // setup a default skybox for the scene for .Babylon export.
                var sourcePath = exportParameters.pbrEnvironment;
                if (!string.IsNullOrEmpty(sourcePath))
                {
                    babylonScene.createDefaultSkybox = exportParameters.createDefaultSkybox;
                    var fileName = Path.GetFileName(sourcePath);

                    // Allow only dds file format
                    if (!fileName.EndsWith(".dds"))
                    {
                        RaiseWarning("Failed to export defauenvironment texture: only .dds format is supported.");
                    }
                    else
                    {
                        RaiseMessage($"texture id = Max_Babylon_Default_Environment");
                        babylonScene.environmentTexture = fileName;

                        if (exportParameters.writeTextures)
                        {
                            try
                            {
                                var destPath = Path.Combine(babylonScene.OutputPath, fileName);
                                if (File.Exists(sourcePath) && sourcePath != destPath)
                                {
                                    File.Copy(sourcePath, destPath, true);
                                }
                            }
                            catch
                            {
                                // silently fails
                                RaiseMessage($"Fail to export the default env texture", 3);
                            }
                        }
                    }
                }
            }

            // Output
            babylonScene.Prepare(false, false);

            if (isBabylonExported)
            {
                Write(babylonScene, outputBabylonDirectory, outputFileName, exportParameters.outputFormat, exportParameters.generateManifest);
            }

            ReportProgressChanged(100);

            // Export glTF
            if (exportParameters.outputFormat == "gltf" || exportParameters.outputFormat == "glb")
            {
                bool generateBinary = exportParameters.outputFormat == "glb";

                GLTFExporter gltfExporter = new GLTFExporter();
                gltfExporter.ExportGltf(this.exportParameters, babylonScene, outputBabylonDirectory, outputFileName, generateBinary, this);
            }

            watch.Stop();
            RaiseMessage(string.Format("Export done in {0:0.00}s", watch.ElapsedMilliseconds / 1000.0), Color.Blue);
        }
Esempio n. 18
0
        private static float FPS_FACTOR = 30.0f; // TODO - Which FPS factor ?

        private GLTFAnimation ExportNodeAnimation(BabylonNode babylonNode, GLTF gltf, GLTFNode gltfNode, BabylonScene babylonScene = null)
        {
            GLTFAnimation gltfAnimation = null;

            if (gltf.AnimationsList.Count > 0)
            {
                gltfAnimation = gltf.AnimationsList[0];
            }
            else
            {
                gltfAnimation = new GLTFAnimation();
                gltf.AnimationsList.Add(gltfAnimation);
            }

            var channelList = gltfAnimation.ChannelList;
            var samplerList = gltfAnimation.SamplerList;

            if ((babylonNode.animations != null && babylonNode.animations.Length > 0) ||
                (babylonNode.extraAnimations != null && babylonNode.extraAnimations.Count > 0))
            {
                RaiseMessage("GLTFExporter.Animation | Export animation of node named: " + babylonNode.name, 2);

                // Combine babylon animations from .babylon file and cached ones
                var babylonAnimations = new List <BabylonAnimation>();
                if (babylonNode.animations != null)
                {
                    babylonAnimations.AddRange(babylonNode.animations);
                }
                if (babylonNode.extraAnimations != null)
                {
                    babylonAnimations.AddRange(babylonNode.extraAnimations);
                }
                foreach (BabylonAnimation babylonAnimation in babylonAnimations)
                {
                    // Target
                    var gltfTarget = new GLTFChannelTarget
                    {
                        node = gltfNode.index
                    };
                    gltfTarget.path = _getTargetPath(babylonAnimation.property);
                    if (gltfTarget.path == null)
                    {
                        // Unkown babylon animation property
                        //RaiseWarning("GLTFExporter.Animation | Unkown animation property '" + babylonAnimation.property + "'", 3);
                        // Ignore this babylon animation
                        continue;
                    }

                    // --- Input ---
                    var accessorInput = _createAndPopulateInput(gltf, babylonAnimation);

                    // --- Output ---
                    GLTFAccessor accessorOutput = _createAccessorOfPath(gltfTarget.path, gltf);
                    // Populate accessor
                    foreach (var babylonAnimationKey in babylonAnimation.keys)
                    {
                        var outputValues = babylonAnimationKey.values;

                        // Switch coordinate system at object level
                        if (babylonAnimation.property == "position")
                        {
                            outputValues[2] *= -1;
                        }
                        else if (babylonAnimation.property == "rotationQuaternion")
                        {
                            outputValues[0] *= -1;
                            outputValues[1] *= -1;
                        }

                        // Store values as bytes
                        foreach (var outputValue in outputValues)
                        {
                            accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue));
                        }
                    }
                    ;
                    accessorOutput.count = babylonAnimation.keys.Length;

                    // Animation sampler
                    var gltfAnimationSampler = new GLTFAnimationSampler
                    {
                        input  = accessorInput.index,
                        output = accessorOutput.index
                    };
                    gltfAnimationSampler.index = samplerList.Count;
                    samplerList.Add(gltfAnimationSampler);

                    // Channel
                    var gltfChannel = new GLTFChannel
                    {
                        sampler = gltfAnimationSampler.index,
                        target  = gltfTarget
                    };
                    channelList.Add(gltfChannel);
                }
            }

            if (babylonNode.GetType() == typeof(BabylonMesh))
            {
                var babylonMesh = babylonNode as BabylonMesh;

                // Morph targets
                var babylonMorphTargetManager = GetBabylonMorphTargetManager(babylonScene, babylonMesh);
                if (babylonMorphTargetManager != null)
                {
                    ExportMorphTargetWeightAnimation(babylonMorphTargetManager, gltf, gltfNode, channelList, samplerList);
                }
            }

            return(gltfAnimation);
        }
Esempio n. 19
0
        private static float FPS_FACTOR = 60.0f; // TODO - Which FPS factor ?

        private GLTFAnimation ExportNodeAnimation(BabylonNode babylonNode, GLTF gltf, GLTFNode gltfNode, BabylonScene babylonScene = null)
        {
            var channelList = new List <GLTFChannel>();
            var samplerList = new List <GLTFAnimationSampler>();

            if (babylonNode.animations != null && babylonNode.animations.Length > 0)
            {
                RaiseMessage("GLTFExporter.Animation | Export animation of node named: " + babylonNode.name, 2);

                foreach (BabylonAnimation babylonAnimation in babylonNode.animations)
                {
                    // Target
                    var gltfTarget = new GLTFChannelTarget
                    {
                        node = gltfNode.index
                    };
                    gltfTarget.path = _getTargetPath(babylonAnimation.property);
                    if (gltfTarget.path == null)
                    {
                        // Unkown babylon animation property
                        RaiseWarning("GLTFExporter.Animation | Unkown animation property '" + babylonAnimation.property + "'", 3);
                        // Ignore this babylon animation
                        continue;
                    }

                    // Buffer
                    var buffer = GLTFBufferService.Instance.GetBuffer(gltf);

                    // --- Input ---
                    var accessorInput = GLTFBufferService.Instance.CreateAccessor(
                        gltf,
                        GLTFBufferService.Instance.GetBufferViewAnimationFloatScalar(gltf, buffer),
                        "accessorAnimationInput",
                        GLTFAccessor.ComponentType.FLOAT,
                        GLTFAccessor.TypeEnum.SCALAR
                        );
                    // Populate accessor
                    accessorInput.min = new float[] { float.MaxValue };
                    accessorInput.max = new float[] { float.MinValue };
                    foreach (var babylonAnimationKey in babylonAnimation.keys)
                    {
                        var inputValue = babylonAnimationKey.frame / FPS_FACTOR;
                        // Store values as bytes
                        accessorInput.bytesList.AddRange(BitConverter.GetBytes(inputValue));
                        // Update min and max values
                        GLTFBufferService.UpdateMinMaxAccessor(accessorInput, inputValue);
                    }
                    ;
                    accessorInput.count = babylonAnimation.keys.Length;

                    // --- Output ---
                    GLTFAccessor accessorOutput = null;
                    switch (gltfTarget.path)
                    {
                    case "translation":
                        accessorOutput = GLTFBufferService.Instance.CreateAccessor(
                            gltf,
                            GLTFBufferService.Instance.GetBufferViewAnimationFloatVec3(gltf, buffer),
                            "accessorAnimationPositions",
                            GLTFAccessor.ComponentType.FLOAT,
                            GLTFAccessor.TypeEnum.VEC3
                            );
                        break;

                    case "rotation":
                        accessorOutput = GLTFBufferService.Instance.CreateAccessor(
                            gltf,
                            GLTFBufferService.Instance.GetBufferViewAnimationFloatVec4(gltf, buffer),
                            "accessorAnimationRotations",
                            GLTFAccessor.ComponentType.FLOAT,
                            GLTFAccessor.TypeEnum.VEC4
                            );
                        break;

                    case "scale":
                        accessorOutput = GLTFBufferService.Instance.CreateAccessor(
                            gltf,
                            GLTFBufferService.Instance.GetBufferViewAnimationFloatVec3(gltf, buffer),
                            "accessorAnimationScales",
                            GLTFAccessor.ComponentType.FLOAT,
                            GLTFAccessor.TypeEnum.VEC3
                            );
                        break;
                    }
                    // Populate accessor
                    foreach (var babylonAnimationKey in babylonAnimation.keys)
                    {
                        var outputValues = babylonAnimationKey.values;
                        // Store values as bytes
                        foreach (var outputValue in outputValues)
                        {
                            accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue));
                        }
                    }
                    ;
                    accessorOutput.count = babylonAnimation.keys.Length;

                    // Animation sampler
                    var gltfAnimationSampler = new GLTFAnimationSampler
                    {
                        input  = accessorInput.index,
                        output = accessorOutput.index
                    };
                    gltfAnimationSampler.index = samplerList.Count;
                    samplerList.Add(gltfAnimationSampler);

                    // Channel
                    var gltfChannel = new GLTFChannel
                    {
                        sampler = gltfAnimationSampler.index,
                        target  = gltfTarget
                    };
                    channelList.Add(gltfChannel);
                }
            }

            if (babylonNode.GetType() == typeof(BabylonMesh))
            {
                var babylonMesh = babylonNode as BabylonMesh;

                // Morph targets
                var babylonMorphTargetManager = GetBabylonMorphTargetManager(babylonScene, babylonMesh);
                if (babylonMorphTargetManager != null)
                {
                    ExportMorphTargetWeightAnimation(babylonMorphTargetManager, gltf, gltfNode, channelList, samplerList);
                }
            }

            // Do not export empty arrays
            if (channelList.Count > 0)
            {
                // Animation
                var gltfAnimation = new GLTFAnimation
                {
                    channels = channelList.ToArray(),
                    samplers = samplerList.ToArray()
                };
                gltf.AnimationsList.Add(gltfAnimation);
                return(gltfAnimation);
            }
            else
            {
                return(null);
            }
        }
Esempio n. 20
0
        private void ExportBoneAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonNode babylonNode, GLTFNode gltfNode, BabylonAnimationGroup animationGroup = null)
        {
            var channelList = gltfAnimation.ChannelList;
            var samplerList = gltfAnimation.SamplerList;

            if (babylonNode.animations != null && babylonNode.animations[0].property == "_matrix")
            {
                logger.RaiseMessage("GLTFExporter.Animation | Export animation of bone named: " + babylonNode.name, 2);

                BabylonAnimation babylonAnimation = null;
                if (animationGroup != null)
                {
                    var targetedAnimation = animationGroup.targetedAnimations.FirstOrDefault(animation => animation.targetId == babylonNode.id);
                    if (targetedAnimation != null)
                    {
                        babylonAnimation = targetedAnimation.animation;
                    }
                }

                // otherwise fall back to the full animation track on the node.
                if (babylonAnimation == null)
                {
                    babylonAnimation = babylonNode.animations[0];
                }

                var babylonAnimationKeysInRange = babylonAnimation.keys.Where(key => key.frame >= startFrame && key.frame <= endFrame);
                if (babylonAnimationKeysInRange.Count() <= 0)
                {
                    return;
                }

                // --- Input ---
                var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame);
                if (accessorInput == null)
                {
                    return;
                }

                // --- Output ---
                var paths = new string[] { "translation", "rotation", "scale" };
                var accessorOutputByPath = new Dictionary <string, GLTFAccessor>();

                foreach (string path in paths)
                {
                    GLTFAccessor accessorOutput = _createAccessorOfPath(path, gltf);
                    accessorOutputByPath.Add(path, accessorOutput);
                }

                // Populate accessors
                foreach (var babylonAnimationKey in babylonAnimationKeysInRange)
                {
                    var matrix = new BabylonMatrix();
                    matrix.m = babylonAnimationKey.values;

                    var translationBabylon  = new BabylonVector3();
                    var rotationQuatBabylon = new BabylonQuaternion();
                    var scaleBabylon        = new BabylonVector3();
                    matrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon);

                    // Switch coordinate system at object level
                    translationBabylon.Z  *= -1;
                    rotationQuatBabylon.X *= -1;
                    rotationQuatBabylon.Y *= -1;

                    var outputValuesByPath = new Dictionary <string, float[]>();
                    outputValuesByPath.Add("translation", translationBabylon.ToArray());
                    outputValuesByPath.Add("rotation", rotationQuatBabylon.ToArray());
                    outputValuesByPath.Add("scale", scaleBabylon.ToArray());

                    // Store values as bytes
                    foreach (string path in paths)
                    {
                        var accessorOutput = accessorOutputByPath[path];
                        var outputValues   = outputValuesByPath[path];

                        foreach (var outputValue in outputValues)
                        {
                            accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue));
                        }
                        accessorOutput.count++;
                    }
                }
                ;

                foreach (string path in paths)
                {
                    var accessorOutput = accessorOutputByPath[path];

                    // Animation sampler
                    var gltfAnimationSampler = new GLTFAnimationSampler
                    {
                        input  = accessorInput.index,
                        output = accessorOutput.index
                    };
                    gltfAnimationSampler.index = samplerList.Count;
                    samplerList.Add(gltfAnimationSampler);

                    // Target
                    var gltfTarget = new GLTFChannelTarget
                    {
                        node = gltfNode.index
                    };
                    gltfTarget.path = path;

                    // Channel
                    var gltfChannel = new GLTFChannel
                    {
                        sampler = gltfAnimationSampler.index,
                        target  = gltfTarget
                    };
                    channelList.Add(gltfChannel);
                }
            }

            ExportGLTFExtension(babylonNode, ref gltfAnimation, gltf);
        }
Esempio n. 21
0
        private IList <BabylonAnimationGroup> ExportAnimationGroups(BabylonScene babylonScene)
        {
            IList <BabylonAnimationGroup> animationGroups = new List <BabylonAnimationGroup>();

            // Retrieve and parse animation group data
            AnimationGroupList animationList = InitAnimationGroups();

            foreach (AnimationGroup animGroup in animationList)
            {
                RaiseMessage("Exporter.animationGroups | " + animGroup.Name, 1);

                BabylonAnimationGroup animationGroup = new BabylonAnimationGroup
                {
                    name = animGroup.Name,
                    from = animGroup.FrameStart,
                    to   = animGroup.FrameEnd,
                    targetedAnimations = new List <BabylonTargetedAnimation>()
                };

                // add animations of each nodes contained in the animGroup
                foreach (uint nodeHandle in animGroup.NodeHandles)
                {
                    IINode maxNode = Loader.Core.RootNode.FindChildNode(nodeHandle);

                    // node could have been deleted, silently ignore it
                    if (maxNode == null)
                    {
                        continue;
                    }


                    // Helpers can be exported as dummies and as bones
                    string nodeId = maxNode.GetGuid().ToString();
                    string boneId = maxNode.GetGuid().ToString() + "-bone";   // the suffix "-bone" is added in babylon export format to assure the uniqueness of IDs


                    // Node
                    BabylonNode node = babylonScene.MeshesList.FirstOrDefault(m => m.id == nodeId);
                    if (node == null)
                    {
                        node = babylonScene.CamerasList.FirstOrDefault(c => c.id == nodeId);
                    }
                    if (node == null)
                    {
                        node = babylonScene.LightsList.FirstOrDefault(l => l.id == nodeId);
                    }

                    if (node != null)
                    {
                        if (node.animations != null && node.animations.Length != 0)
                        {
                            IList <BabylonAnimation> animations = GetSubAnimations(node, animationGroup.from, animationGroup.to);
                            foreach (BabylonAnimation animation in animations)
                            {
                                BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation
                                {
                                    animation = animation,
                                    targetId  = nodeId
                                };

                                animationGroup.targetedAnimations.Add(targetedAnimation);
                            }
                        }
                        else if (exportNonAnimated)
                        {
                            BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation
                            {
                                animation = CreatePositionAnimation(animationGroup.from, animationGroup.to, node.position),
                                targetId  = node.id
                            };

                            animationGroup.targetedAnimations.Add(targetedAnimation);
                        }
                    }

                    // bone
                    BabylonBone bone  = null;
                    int         index = 0;
                    while (index < babylonScene.SkeletonsList.Count && bone == null)
                    {
                        BabylonSkeleton skel = babylonScene.SkeletonsList[index];
                        bone = skel.bones.FirstOrDefault(b => b.id == boneId);
                        index++;
                    }

                    if (bone != null)
                    {
                        if (bone.animation != null)
                        {
                            IList <BabylonAnimation> animations = GetSubAnimations(bone, animationGroup.from, animationGroup.to);
                            foreach (BabylonAnimation animation in animations)
                            {
                                BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation
                                {
                                    animation = animation,
                                    targetId  = boneId
                                };

                                animationGroup.targetedAnimations.Add(targetedAnimation);
                            }
                        }
                        else if (exportNonAnimated)
                        {
                            BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation
                            {
                                animation = CreateMatrixAnimation(animationGroup.from, animationGroup.to, bone.matrix),
                                targetId  = bone.id
                            };

                            animationGroup.targetedAnimations.Add(targetedAnimation);
                        }
                    }
                }

                if (animationGroup.targetedAnimations.Count > 0)
                {
                    animationGroups.Add(animationGroup);
                }
            }

            return(animationGroups);
        }
        /// <summary>
        /// Create a gltf node from the babylon node.
        /// </summary>
        /// <param name="babylonNode"></param>
        /// <param name="gltf"></param>
        /// <param name="babylonScene"></param>
        /// <param name="gltfParentNode">The parent of the glTF node that will be created.</param>
        /// <returns>The gltf node created.</returns>
        private GLTFNode ExportNode(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode)
        {
            RaiseMessage($"GLTFExporter | ExportNode {babylonNode.name}", 1);
            GLTFNode gltfNode = null;
            var      type     = babylonNode.GetType();

            var nodeNodePair = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id));

            if (nodeNodePair.Key != null)
            {
                return(nodeNodePair.Value);
            }

            var boneNodePair = boneToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id));

            if (boneNodePair.Key != null)
            {
                return(boneNodePair.Value);
            }

            // Node
            gltfNode = new GLTFNode
            {
                name  = GetUniqueNodeName(babylonNode.name),
                index = gltf.NodesList.Count
            };
            gltf.NodesList.Add(gltfNode);                 // add the node to the gltf list
            nodeToGltfNodeMap.Add(babylonNode, gltfNode); // add the node to the global map

            // Hierarchy
            if (gltfParentNode != null)
            {
                RaiseMessage("GLTFExporter.Node| Add " + babylonNode.name + " as child to " + gltfParentNode.name, 2);
                gltfParentNode.ChildrenList.Add(gltfNode.index);
                gltfNode.parent = gltfParentNode;
            }
            else
            {
                // It's a root node
                // Only root nodes are listed in a gltf scene
                RaiseMessage("GLTFExporter.Node | Add " + babylonNode.name + " as root node to scene", 2);
                gltf.scenes[0].NodesList.Add(gltfNode.index);
            }

            // Transform
            // Position
            gltfNode.translation = babylonNode.position;

            // Rotation
            if (babylonNode.rotationQuaternion != null)
            {
                gltfNode.rotation = babylonNode.rotationQuaternion;
            }
            else
            {
                // Convert rotation vector to quaternion
                BabylonVector3 rotationVector3 = new BabylonVector3
                {
                    X = babylonNode.rotation[0],
                    Y = babylonNode.rotation[1],
                    Z = babylonNode.rotation[2]
                };
                gltfNode.rotation = rotationVector3.toQuaternion().ToArray();
            }

            // Scale
            gltfNode.scale = babylonNode.scaling;

            // Switch coordinate system at object level
            gltfNode.translation[2] *= -1;
            gltfNode.rotation[0]    *= -1;
            gltfNode.rotation[1]    *= -1;

            // Kuesa layers
            if (exportParameters.kuesaExportLayers && babylonNode.kuesaLayers != null)
            {
                Dictionary <string, List <int> > layers = new Dictionary <string, List <int> >();
                List <int> indexes = babylonNode.kuesaLayers.ToList <int>();
                layers["layers"] = new List <int>();
                layers["layers"].AddRange(indexes);
                if (gltfNode.extensions == null)
                {
                    gltfNode.extensions = new GLTFExtensions();
                }
                gltfNode.extensions["KDAB_Kuesa_Layers"] = layers;
            }

            return(gltfNode);
        }
Esempio n. 23
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="mDagPath">DAG path to the transform above light</param>
        /// <param name="babylonScene"></param>
        /// <returns></returns>
        private BabylonNode ExportLight(MDagPath mDagPath, BabylonScene babylonScene)
        {
            RaiseMessage(mDagPath.partialPathName, 1);

            // Transform above light
            MFnTransform mFnLightTransform = new MFnTransform(mDagPath);

            // Light direct child of the transform
            MFnLight mFnLight    = null;
            bool     createDummy = false;

            for (uint i = 0; i < mFnLightTransform.childCount; i++)
            {
                MObject childObject = mFnLightTransform.child(i);
                if (childObject.hasFn(MFn.Type.kLight))
                {
                    var _mFnLight = new MFnLight(childObject);
                    if (!_mFnLight.isIntermediateObject)
                    {
                        mFnLight = _mFnLight;
                    }
                }
                else
                {
                    if (childObject.hasFn(MFn.Type.kTransform))
                    {
                        createDummy = true;
                    }
                }
            }
            if (mFnLight == null)
            {
                RaiseError("No light found has child of " + mDagPath.fullPathName);
                return(null);
            }

            RaiseMessage("mFnLight.fullPathName=" + mFnLight.fullPathName, 2);
            if (createDummy)
            {
                RaiseWarning("Adding a dummy node to this light so that the children have proper NodeTransform hierarchy.", 2);
            }

            // --- prints ---
            #region prints

            // MFnLight
            RaiseVerbose("BabylonExporter.Light | mFnLight data", 2);
            RaiseVerbose("BabylonExporter.Light | mFnLight.color.toString()=" + mFnLight.color.toString(), 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.intensity=" + mFnLight.intensity, 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.useRayTraceShadows=" + mFnLight.useRayTraceShadows, 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.shadowColor.toString()=" + mFnLight.shadowColor.toString(), 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.centerOfIllumination=" + mFnLight.centerOfIllumination, 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.numShadowSamples=" + mFnLight.numShadowSamples, 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.rayDepthLimit=" + mFnLight.rayDepthLimit, 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.opticalFXvisibility.toString()=" + mFnLight.opticalFXvisibility.toString(), 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.lightIntensity.toString()=" + mFnLight.lightIntensity.toString(), 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.instanceCount(true)=" + mFnLight.instanceCount(true), 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.lightDirection(0).toString()=" + mFnLight.lightDirection(0).toString(), 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.lightAmbient=" + mFnLight.lightAmbient, 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.lightDiffuse=" + mFnLight.lightDiffuse, 3);
            RaiseVerbose("BabylonExporter.Light | mFnLight.lightSpecular=" + mFnLight.lightSpecular, 3);

            switch (mFnLight.objectProperty.apiType)
            {
            case MFn.Type.kSpotLight:
                MFnSpotLight mFnSpotLight = new MFnSpotLight(mFnLight.objectProperty);
                // MFnNonAmbientLight
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.decayRate=" + mFnSpotLight.decayRate, 3);     // dropdown enum value
                // MFnNonExtendedLight
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.shadowRadius=" + mFnSpotLight.shadowRadius, 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.castSoftShadows=" + mFnSpotLight.castSoftShadows, 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.useDepthMapShadows=" + mFnSpotLight.useDepthMapShadows, 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.depthMapFilterSize()=" + mFnSpotLight.depthMapFilterSize(), 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.depthMapResolution()=" + mFnSpotLight.depthMapResolution(), 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.depthMapBias()=" + mFnSpotLight.depthMapBias(), 3);
                // MFnSpotLight
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.coneAngle=" + mFnSpotLight.coneAngle, 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.penumbraAngle=" + mFnSpotLight.penumbraAngle, 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.dropOff=" + mFnSpotLight.dropOff, 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.barnDoors=" + mFnSpotLight.barnDoors, 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.useDecayRegions=" + mFnSpotLight.useDecayRegions, 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kFirst)=" + mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kFirst), 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kFirst)=" + mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kFirst), 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kSecond)=" + mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kSecond), 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kSecond)=" + mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kSecond), 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kThird)=" + mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kThird), 3);
                RaiseVerbose("BabylonExporter.Light | mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kThird)=" + mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kThird), 3);
                break;
            }

            Print(mFnLightTransform, 2, "Print ExportLight mFnTransform");

            Print(mFnLight, 2, "Print ExportLight mFnLight");

            #endregion

            if (IsLightExportable(mFnLight, mDagPath) == false)
            {
                return(null);
            }

            var babylonLight = new BabylonLight {
                name = mFnLightTransform.name, id = mFnLightTransform.uuid().asString()
            };
            // User custom attributes
            babylonLight.metadata = ExportCustomAttributeFromTransform(mFnLightTransform);

            MVector vDir = new MVector(0, 0, -1);
            MTransformationMatrix transformationMatrix;

            // Animations
            if (exportParameters.exportAnimations)
            {
                if (exportParameters.bakeAnimationFrames)
                {
                    ExportNodeAnimationFrameByFrame(babylonLight, mFnLightTransform);
                }
                else
                {
                    ExportNodeAnimation(babylonLight, mFnLightTransform);
                }
            }

            if (babylonLight.animations != null && !createDummy)
            {
                RaiseWarning("Adding a dummy node to the light so that it can be animated properly.", 2);
                createDummy = true;
            }

            // Hierarchy
            BabylonNode dummy = null;
            if (createDummy)
            {
                dummy                 = ExportDummy(mDagPath, babylonScene);
                dummy.name            = "_" + dummy.name + "_";
                dummy.animations      = babylonLight.animations;
                dummy.extraAnimations = babylonLight.extraAnimations;
                dummy.autoAnimate     = babylonLight.autoAnimate;
                dummy.autoAnimateFrom = babylonLight.autoAnimateFrom;
                dummy.autoAnimateTo   = babylonLight.autoAnimateTo;

                babylonLight.animations      = null;
                babylonLight.extraAnimations = null;

                babylonLight.id       = Guid.NewGuid().ToString();
                babylonLight.parentId = dummy.id;
                babylonLight.hasDummy = true;

                // The position is stored by the dummy parent and the default direction is downward and it is updated by the rotation of the parent dummy
                babylonLight.position  = new[] { 0f, 0f, 0f };
                babylonLight.direction = new[] { 0f, 0f, 1f };
            }
            else
            {
                ExportHierarchy(babylonLight, mFnLightTransform);
                // Position / rotation / scaling
                ExportTransform(babylonLight, mFnLightTransform);

                // Direction
                vDir = new MVector(0, 0, -1);
                transformationMatrix = new MTransformationMatrix(mFnLightTransform.transformationMatrix);
                vDir = vDir.multiply(transformationMatrix.asMatrixProperty);
                vDir.normalize();
                babylonLight.direction = new[] { (float)vDir.x, (float)vDir.y, -(float)vDir.z };
            }

            // Common fields
            babylonLight.intensity = mFnLight.intensity;
            babylonLight.diffuse   = mFnLight.lightDiffuse ? mFnLight.color.toArrayRGB() : new float[] { 0, 0, 0 };
            babylonLight.specular  = mFnLight.lightSpecular ? mFnLight.color.toArrayRGB() : new float[] { 0, 0, 0 };

            // Type
            switch (mFnLight.objectProperty.apiType)
            {
            case MFn.Type.kPointLight:
                babylonLight.type = 0;
                break;

            case MFn.Type.kSpotLight:
                MFnSpotLight mFnSpotLight = new MFnSpotLight(mFnLight.objectProperty);
                babylonLight.type     = 2;
                babylonLight.angle    = (float)mFnSpotLight.coneAngle;
                babylonLight.exponent = 1;

                if (mFnSpotLight.useDecayRegions)
                {
                    babylonLight.range = mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kThird);     // Max distance
                }
                break;

            case MFn.Type.kDirectionalLight:
                babylonLight.type = 1;
                break;

            case MFn.Type.kAmbientLight:
                babylonLight.type        = 3;
                babylonLight.groundColor = new float[] { 0, 0, 0 };

                // No emit diffuse /specular checkbox for ambient light
                babylonLight.diffuse  = mFnLight.color.toArrayRGB();
                babylonLight.specular = babylonLight.diffuse;

                // Direction
                if (!createDummy)
                {
                    vDir = new MVector(0, 1, 0);
                    transformationMatrix = new MTransformationMatrix(mFnLightTransform.transformationMatrix);
                    vDir = vDir.multiply(transformationMatrix.asMatrixProperty);
                    vDir.normalize();
                    babylonLight.direction = new[] { (float)vDir.x, (float)vDir.y, -(float)vDir.z };
                }
                break;

            case MFn.Type.kAreaLight:
            case MFn.Type.kVolumeLight:
                RaiseError("Unsupported light type '" + mFnLight.objectProperty.apiType + "' for DAG path '" + mFnLight.fullPathName + "'. Light is ignored. Supported light types are: ambient, directional, point and spot.", 1);
                return(null);

            default:
                RaiseWarning("Unknown light type '" + mFnLight.objectProperty.apiType + "' for DAG path '" + mFnLight.fullPathName + "'. Light is ignored.", 1);
                return(null);
            }

            // TODO - Shadows

            //Variable declaration
            MStringArray  enlightedMeshesFullPathNames = new MStringArray();
            List <string> includeMeshesIds             = new List <string>();
            MStringArray  kTransMesh = new MStringArray();
            String        typeMesh   = null;
            MStringArray  UUIDMesh   = new MStringArray();

            //MEL Command that get the enlighted mesh for a given light
            MGlobal.executeCommand($@"lightlink -query -light {mFnLightTransform.fullPathName};", enlightedMeshesFullPathNames);

            //For each enlighted mesh
            foreach (String Mesh in enlightedMeshesFullPathNames)
            {
                //MEL Command use to get the type of each mesh
                typeMesh = MGlobal.executeCommandStringResult($@"nodeType -api {Mesh};");

                //We are targeting the type kMesh and not kTransform (for parenting)
                if (typeMesh == "kMesh")
                {
                    MGlobal.executeCommand($@"listRelatives -parent -fullPath {Mesh};", kTransMesh);

                    //And finally the MEL Command for the uuid of each mesh
                    MGlobal.executeCommand($@"ls -uuid {kTransMesh[0]};", UUIDMesh);
                    includeMeshesIds.Add(UUIDMesh[0]);
                }
            }

            babylonLight.includedOnlyMeshesIds = includeMeshesIds.ToArray();

            babylonScene.LightsList.Add(babylonLight);

            return(babylonLight);
        }
Esempio n. 24
0
        /// <summary>
        /// Create a gltf node from the babylon node.
        /// </summary>
        /// <param name="babylonNode"></param>
        /// <param name="gltf"></param>
        /// <param name="babylonScene"></param>
        /// <param name="gltfParentNode">The parent of the glTF node that will be created.</param>
        /// <returns>The gltf node created.</returns>
        private GLTFNode ExportNode(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode)
        {
            logger.RaiseMessage($"GLTFExporter | ExportNode {babylonNode.name}", 1);
            GLTFNode gltfNode = null;
            var      type     = babylonNode.GetType();

            var nodeNodePair = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id));

            if (nodeNodePair.Key != null)
            {
                return(nodeNodePair.Value);
            }

            // Node
            gltfNode = new GLTFNode
            {
                name  = GetUniqueNodeName(babylonNode.name),
                index = gltf.NodesList.Count
            };

            // User Custom Attributes
            if (babylonNode.metadata != null && babylonNode.metadata.Count != 0)
            {
                gltfNode.extras = babylonNode.metadata;
            }

            gltf.NodesList.Add(gltfNode);                 // add the node to the gltf list
            nodeToGltfNodeMap.Add(babylonNode, gltfNode); // add the node to the global map

            // Hierarchy
            if (gltfParentNode != null)
            {
                logger.RaiseMessage("GLTFExporter.Node| Add " + babylonNode.name + " as child to " + gltfParentNode.name, 2);
                gltfParentNode.ChildrenList.Add(gltfNode.index);
                gltfNode.parent = gltfParentNode;
            }
            else
            {
                // It's a root node
                // Only root nodes are listed in a gltf scene
                logger.RaiseMessage("GLTFExporter.Node | Add " + babylonNode.name + " as root node to scene", 2);
                gltf.scenes[0].NodesList.Add(gltfNode.index);
            }

            // TRS
            if (exportParameters.exportAnimationsOnly == false)
            {
                // Position
                gltfNode.translation = babylonNode.position;

                // Rotation
                if (babylonNode.rotationQuaternion != null)
                {
                    gltfNode.rotation = babylonNode.rotationQuaternion;
                }
                else
                {
                    // Convert rotation vector to quaternion
                    BabylonVector3 rotationVector3 = new BabylonVector3
                    {
                        X = babylonNode.rotation[0],
                        Y = babylonNode.rotation[1],
                        Z = babylonNode.rotation[2]
                    };
                    gltfNode.rotation = rotationVector3.toQuaternion().ToArray();
                }

                // Scale
                gltfNode.scale = babylonNode.scaling;

                // Switch coordinate system at object level
                gltfNode.translation[2] *= -1;
                gltfNode.rotation[0]    *= -1;
                gltfNode.rotation[1]    *= -1;
            }

            ExportGLTFExtension(babylonNode, ref gltfNode, gltf);

            return(gltfNode);
        }
Esempio n. 25
0
        private BabylonNode ExportLight(IIGameScene scene, IIGameNode lightNode, BabylonScene babylonScene)
        {
            if (IsLightExportable(lightNode) == false)
            {
                return(null);
            }

            var gameLight    = lightNode.IGameObject.AsGameLight();
            var initialized  = gameLight.InitializeData;
            var babylonLight = new BabylonLight();

            RaiseMessage(lightNode.Name, 1);
            babylonLight.name = lightNode.Name;

            // Export the custom attributes of this light
            babylonLight.metadata = ExportExtraAttributes(lightNode, babylonScene);

            // To preserve the position/rotation and the hierarchy, we create a dummy that will contains as direct children the light and the light children
            // The light will have no children. The dummy will contains the position and rotation animations.
            bool        createDummy = lightNode.ChildCount > 0;
            BabylonNode dummy       = null;

            if (createDummy)
            {
                dummy                 = ExportDummy(scene, lightNode, babylonScene);
                dummy.name            = "_" + dummy.name + "_";
                babylonLight.id       = Guid.NewGuid().ToString();
                babylonLight.parentId = dummy.id;
                babylonLight.hasDummy = true;
            }
            else
            {
                babylonLight.id = lightNode.MaxNode.GetGuid().ToString();
                if (lightNode.NodeParent != null)
                {
                    babylonLight.parentId = lightNode.NodeParent.MaxNode.GetGuid().ToString();
                }
            }

            // Type
            var maxLight   = (lightNode.MaxNode.ObjectRef as ILightObject);
            var lightState = Loader.Global.LightState.Create();

            maxLight.EvalLightState(0, Tools.Forever, lightState);

            switch (lightState.Type)
            {
            case LightType.OmniLgt:
                babylonLight.type = 0;
                break;

            case LightType.SpotLgt:
                babylonLight.type     = 2;
                babylonLight.angle    = (float)(maxLight.GetFallsize(0, Tools.Forever) * Math.PI / 180.0f);
                babylonLight.exponent = 1;
                break;

            case LightType.DirectLgt:
                babylonLight.type = 1;
                break;

            case LightType.AmbientLgt:
                babylonLight.type        = 3;
                babylonLight.groundColor = new float[] { 0, 0, 0 };
                break;
            }


            // Shadows
            if (maxLight.ShadowMethod == 1)
            {
                if (lightState.Type == LightType.DirectLgt || lightState.Type == LightType.SpotLgt || lightState.Type == LightType.OmniLgt)
                {
                    ExportShadowGenerator(lightNode.MaxNode, babylonScene, babylonLight);
                }
                else
                {
                    RaiseWarning("Shadows maps are only supported for point, directional and spot lights", 2);
                }
            }

            // Position / rotation / scaling
            if (createDummy)
            {
                // The position is stored by the dummy parent and the default direction is downward and it is updated by the rotation of the parent dummy
                babylonLight.position  = new[] { 0f, 0f, 0f };
                babylonLight.direction = new[] { 0f, -1f, 0f };
            }
            else
            {
                exportTransform(babylonLight, lightNode);

                // Position
                var localMatrix = lightNode.GetLocalTM(0);
                var position    = localMatrix.Translation;

                // Direction
                var target = gameLight.LightTarget;
                if (target != null)
                {
                    var targetWm       = target.GetObjectTM(0);
                    var targetPosition = targetWm.Translation;

                    var direction = targetPosition.Subtract(position).Normalize;
                    babylonLight.direction = new[]   { direction.X, direction.Y, direction.Z };
                }
                else
                {
                    var vDir = Loader.Global.Point3.Create(0, -1, 0);
                    vDir = localMatrix.ExtractMatrix3().VectorTransform(vDir).Normalize;
                    babylonLight.direction = new[] { vDir.X, vDir.Y, vDir.Z };
                }
            }

            // The HemisphericLight simulates the ambient environment light, so the passed direction is the light reflection direction, not the incoming direction.
            // So we need the opposite direction
            if (babylonLight.type == 3)
            {
                var worldRotation            = lightNode.GetWorldTM(0).Rotation;
                BabylonQuaternion quaternion = new BabylonQuaternion(worldRotation.X, worldRotation.Y, worldRotation.Z, worldRotation.W);

                babylonLight.direction = quaternion.Rotate(new BabylonVector3(0f, 1f, 0f)).ToArray();
            }


            var maxScene = Loader.Core.RootNode;

            // Exclusion
            try
            {
                var inclusion          = maxLight.ExclList.TestFlag(1); //NT_INCLUDE
                var checkExclusionList = maxLight.ExclList.TestFlag(2); //NT_AFFECT_ILLUM

                if (checkExclusionList)
                {
                    var excllist = new List <string>();
                    var incllist = new List <string>();

                    foreach (var meshNode in maxScene.NodesListBySuperClass(SClass_ID.Geomobject))
                    {
#if MAX2017 || MAX2018 || MAX2019 || MAX2020
                        if (meshNode.CastShadows)
#else
                        if (meshNode.CastShadows == 1)
#endif
                        {
                            var inList = maxLight.ExclList.FindNode(meshNode) != -1;

                            if (inList)
                            {
                                if (inclusion)
                                {
                                    incllist.Add(meshNode.GetGuid().ToString());
                                }
                                else
                                {
                                    excllist.Add(meshNode.GetGuid().ToString());
                                }
                            }
                        }
                    }

                    babylonLight.includedOnlyMeshesIds = incllist.ToArray();
                    babylonLight.excludedMeshesIds     = excllist.ToArray();
                }
            }
            catch (Exception e)
            {
                RaiseMessage("Light exclusion not supported", 2);
            }

            // Other fields
            babylonLight.intensity = maxLight.GetIntensity(0, Tools.Forever);


            babylonLight.diffuse  = lightState.AffectDiffuse ? maxLight.GetRGBColor(0, Tools.Forever).ToArray() : new float[] { 0, 0, 0 };
            babylonLight.specular = lightState.AffectDiffuse ? maxLight.GetRGBColor(0, Tools.Forever).ToArray() : new float[] { 0, 0, 0 };


            if (maxLight.UseAtten)
            {
                babylonLight.range = maxLight.GetAtten(0, 3, Tools.Forever);
            }

            if (exportParameters.exportAnimations)
            {
                // Animations
                var animations = new List <BabylonAnimation>();

                if (createDummy)
                {
                    // Position and rotation animations are stored by the parent (the dummy). The direction result from the parent rotation except for the HemisphericLight.
                    if (babylonLight.type == 3)
                    {
                        BabylonVector3 direction = new BabylonVector3(0, 1, 0);
                        ExportVector3Animation("direction", animations, key =>
                        {
                            var worldRotation            = lightNode.GetWorldTM(key).Rotation;
                            BabylonQuaternion quaternion = new BabylonQuaternion(worldRotation.X, worldRotation.Y, worldRotation.Z, worldRotation.W);

                            return(quaternion.Rotate(direction).ToArray());
                        });
                    }
                }
                else
                {
                    GeneratePositionAnimation(lightNode, animations);

                    ExportVector3Animation("direction", animations, key =>
                    {
                        var localMatrixAnimDir = lightNode.GetLocalTM(key);

                        var positionLight = localMatrixAnimDir.Translation;
                        var lightTarget   = gameLight.LightTarget;
                        if (lightTarget != null)
                        {
                            var targetWm       = lightTarget.GetObjectTM(key);
                            var targetPosition = targetWm.Translation;

                            var direction = targetPosition.Subtract(positionLight).Normalize;
                            return(new[] { direction.X, direction.Y, direction.Z });
                        }
                        else
                        {
                            var vDir = Loader.Global.Point3.Create(0, -1, 0);
                            vDir     = localMatrixAnimDir.ExtractMatrix3().VectorTransform(vDir).Normalize;

                            // The HemisphericLight (type == 3) simulates the ambient environment light, so the passed direction is the light reflection direction, not the incoming direction.
                            // So we need the opposite direction
                            return(babylonLight.type != 3 ? new[] { vDir.X, vDir.Y, vDir.Z } : new[] { -vDir.X, -vDir.Y, -vDir.Z });
                        }
                    });

                    // Animation temporary stored for gltf but not exported for babylon
                    // TODO - Will cause an issue when externalizing the glTF export process
                    var extraAnimations = new List <BabylonAnimation>();
                    // Do not check if node rotation properties are animated
                    GenerateRotationAnimation(lightNode, extraAnimations, true);
                    babylonLight.extraAnimations = extraAnimations;
                }

                ExportFloatAnimation("intensity", animations, key => new[] { maxLight.GetIntensity(key, Tools.Forever) });

                ExportColor3Animation("diffuse", animations, key =>
                {
                    return(lightState.AffectDiffuse? maxLight.GetRGBColor(key, Tools.Forever).ToArray() : new float[] { 0, 0, 0 });
                });

                babylonLight.animations = animations.ToArray();

                if (lightNode.MaxNode.GetBoolProperty("babylonjs_autoanimate"))
                {
                    babylonLight.autoAnimate     = true;
                    babylonLight.autoAnimateFrom = (int)lightNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from");
                    babylonLight.autoAnimateTo   = (int)lightNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to");
                    babylonLight.autoAnimateLoop = lightNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop");
                }
            }

            babylonScene.LightsList.Add(babylonLight);

            return(createDummy ? dummy : babylonLight);
        }
Esempio n. 26
0
 private List <BabylonNode> getDescendants(BabylonNode babylonNode)
 {
     return(babylonNodes.FindAll(node => node.parentId == babylonNode.id));
 }
Esempio n. 27
0
        private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene)
        {
            // Retreive and parse animation group data
            var animationGroupList  = babylonScene.animationGroups;
            var animationGroupCount = animationGroupList == null ? 0 : animationGroupList.Count;

            gltf.AnimationsList.Clear();
            gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationGroupCount);

            if (animationGroupCount <= 0)
            {
                logger.RaiseMessage("GLTFExporter.Animation | No AnimationGroups: exporting all animations together.", 1);
                GLTFAnimation gltfAnimation = new GLTFAnimation();
                gltfAnimation.name = "All Animations";

                int startFrame = babylonScene.TimelineStartFrame;
                int endFrame   = babylonScene.TimelineEndFrame;

                foreach (var pair in nodeToGltfNodeMap)
                {
                    BabylonNode node                             = pair.Key;
                    GLTFNode    gltfNode                         = pair.Value;
                    bool        nodeHasAnimations                = node.animations != null && node.animations.Length > 0 && node.animations[0] != null;
                    bool        nodeHasExtraAnimations           = node.extraAnimations != null && node.extraAnimations.Count > 0 && node.extraAnimations[0] != null;
                    BabylonMesh meshNode                         = node as BabylonMesh;
                    BabylonMorphTargetManager morphTargetManager = null;
                    bool nodeHasAnimatedMorphTargets             = false;
                    if (meshNode != null && meshNode.morphTargetManagerId != null)
                    {
                        morphTargetManager = GetBabylonMorphTargetManager(babylonScene, meshNode);
                        if (morphTargetManager != null)
                        {
                            nodeHasAnimatedMorphTargets = morphTargetManager.targets.Any(target => target.animations != null && target.animations.Length > 0 && target.animations[0] != null);
                        }
                    }

                    if (!nodeHasAnimations && !nodeHasExtraAnimations && !nodeHasAnimatedMorphTargets)
                    {
                        continue;
                    }
                    if (nodeHasAnimations && node.animations[0].property == "_matrix")
                    {
                        ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, node, pair.Value);
                    }
                    else
                    {
                        ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, node, gltfNode, babylonScene);
                    }

                    if (nodeHasAnimatedMorphTargets)
                    {
                        ExportMorphTargetWeightAnimation(morphTargetManager, gltf, gltfNode, gltfAnimation.ChannelList, gltfAnimation.SamplerList, startFrame, endFrame, babylonScene);
                    }
                }

                if (gltfAnimation.ChannelList.Count > 0)
                {
                    gltf.AnimationsList.Add(gltfAnimation);
                }
                else
                {
                    logger.RaiseMessage("GLTFExporter.Animation | No animation data for this animation, it is ignored.", 2);
                }
            }
            else
            {
                foreach (BabylonAnimationGroup animGroup in animationGroupList)
                {
                    logger.RaiseMessage("GLTFExporter.Animation | " + animGroup.name, 1);

                    GLTFAnimation gltfAnimation = new GLTFAnimation();
                    gltfAnimation.name = animGroup.name;

                    int startFrame = MathUtilities.RoundToInt(animGroup.from);
                    int endFrame   = MathUtilities.RoundToInt(animGroup.to);

                    var uniqueNodeIds = animGroup.targetedAnimations.Select(targetAnim => targetAnim.targetId).Distinct();
                    foreach (var id in uniqueNodeIds)
                    {
                        BabylonNode babylonNode = babylonNodes.Find(node => node.id.Equals(id));
                        GLTFNode    gltfNode    = null;
                        // search the babylon scene id map for the babylon node that matches this id
                        if (babylonNode != null)
                        {
                            BabylonMorphTargetManager morphTargetManager = null;

                            // search our babylon->gltf node mapping to see if this node is included in the exported gltf scene
                            if (!nodeToGltfNodeMap.TryGetValue(babylonNode, out gltfNode))
                            {
                                continue;
                            }

                            bool nodeHasAnimations      = babylonNode.animations != null && babylonNode.animations.Length > 0 && babylonNode.animations[0] != null;
                            bool nodeHasExtraAnimations = babylonNode.extraAnimations != null && babylonNode.extraAnimations.Count > 0 && babylonNode.extraAnimations[0] != null;
                            if (!nodeHasAnimations && !nodeHasExtraAnimations)
                            {
                                continue;
                            }

                            if (nodeHasAnimations && babylonNode.animations[0].property == "_matrix") //TODO: Is this check accurate for deciphering between bones and nodes?
                            {
                                ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, animGroup);
                            }
                            else
                            {
                                ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, babylonScene, animGroup);
                            }
                        }
                        else
                        {
                            // if the node isn't found in the scene id map, check if it is the id for a morph target
                            BabylonMorphTargetManager morphTargetManager = babylonScene.morphTargetManagers.FirstOrDefault(mtm => mtm.targets.Any(target => target.animations != null && target.animations.Length > 0 && target.animations[0] != null));
                            if (morphTargetManager != null)
                            {
                                BabylonMesh mesh = morphTargetManager.sourceMesh;
                                if (mesh != null && nodeToGltfNodeMap.TryGetValue(mesh, out gltfNode))
                                {
                                    ExportMorphTargetWeightAnimation(morphTargetManager, gltf, gltfNode, gltfAnimation.ChannelList, gltfAnimation.SamplerList, startFrame, endFrame, babylonScene);
                                }
                            }
                        }
                    }

                    if (gltfAnimation.ChannelList.Count > 0)
                    {
                        gltf.AnimationsList.Add(gltfAnimation);
                    }
                    else
                    {
                        logger.RaiseMessage("No data exported for this animation, it is ignored.", 2);
                    }
                    // clear the exported morph target cache, since we are exporting a new animation group. //TODO: we should probably do this more elegantly.
                    exportedMorphTargets.Clear();
                }
            }
        }
Esempio n. 28
0
        /// <summary>
        /// Create a gltf node from the babylon node.
        /// </summary>
        /// <param name="babylonNode"></param>
        /// <param name="gltf"></param>
        /// <param name="babylonScene"></param>
        /// <param name="gltfParentNode">The parent of the glTF node that will be created.</param>
        /// <returns>The gltf node created.</returns>
        private GLTFNode ExportNode(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode)
        {
            RaiseMessage($"GLTFExporter | ExportNode {babylonNode.name}", 1);
            GLTFNode gltfNode = null;
            var      type     = babylonNode.GetType();

            var nodeNodePair = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id));

            if (nodeNodePair.Key != null)
            {
                return(nodeNodePair.Value);
            }

            var boneNodePair = boneToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id));

            if (boneNodePair.Key != null)
            {
                return(boneNodePair.Value);
            }

            // Node
            gltfNode = new GLTFNode
            {
                name  = GetUniqueNodeName(babylonNode.name),
                index = gltf.NodesList.Count
            };
            gltf.NodesList.Add(gltfNode);                 // add the node to the gltf list
            nodeToGltfNodeMap.Add(babylonNode, gltfNode); // add the node to the global map

            // Hierarchy
            if (gltfParentNode != null)
            {
                RaiseMessage("GLTFExporter.Node| Add " + babylonNode.name + " as child to " + gltfParentNode.name, 2);
                gltfParentNode.ChildrenList.Add(gltfNode.index);
                gltfNode.parent = gltfParentNode;
            }
            else
            {
                // It's a root node
                // Only root nodes are listed in a gltf scene
                RaiseMessage("GLTFExporter.Node | Add " + babylonNode.name + " as root node to scene", 2);
                gltf.scenes[0].NodesList.Add(gltfNode.index);
            }

            // Transform
            // Position
            gltfNode.translation = babylonNode.position;

            // Rotation
            if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh)) || type == typeof(BabylonCamera))
            {
                if (babylonNode.rotationQuaternion != null)
                {
                    gltfNode.rotation = babylonNode.rotationQuaternion;
                }
                else
                {
                    // Convert rotation vector to quaternion
                    BabylonVector3 rotationVector3 = new BabylonVector3
                    {
                        X = babylonNode.rotation[0],
                        Y = babylonNode.rotation[1],
                        Z = babylonNode.rotation[2]
                    };
                    gltfNode.rotation = rotationVector3.toQuaternion().ToArray();
                }
            }
            else // Light
            {
                gltfNode.rotation = new float[4] {
                    0, 0, 0, 1
                };
            }

            // Scale
            if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh)))
            {
                gltfNode.scale = babylonNode.scaling;
            }
            else // Camera and light
            {
                gltfNode.scale = new float[3] {
                    1, 1, 1
                };
            }

            // Switch coordinate system at object level
            gltfNode.translation[2] *= -1;
            gltfNode.rotation[0]    *= -1;
            gltfNode.rotation[1]    *= -1;

            return(gltfNode);
        }
Esempio n. 29
0
        public void Export(string outputDirectory, string outputFileName, string outputFormat, bool generateManifest,
                           bool onlySelected, bool autoSaveMayaFile, bool exportHiddenObjects, bool copyTexturesToOutput,
                           bool optimizeVertices, bool exportTangents, string scaleFactor, bool exportSkin)
        {
            // Chekc if the animation is running
            MGlobal.executeCommand("play -q - state", out int isPlayed);
            if (isPlayed == 1)
            {
                RaiseError("Stop the animation before exporting.");
                return;
            }

            // Check input text is valid
            var scaleFactorFloat = 1.0f;

            try
            {
                scaleFactor      = scaleFactor.Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator);
                scaleFactor      = scaleFactor.Replace(",", System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator);
                scaleFactorFloat = float.Parse(scaleFactor);
            }
            catch
            {
                RaiseError("Scale factor is not a valid number.");
                return;
            }

            RaiseMessage("Exportation started", Color.Blue);
            var progression = 0.0f;

            ReportProgressChanged(progression);

            // Store export options
            _onlySelected        = onlySelected;
            _exportHiddenObjects = exportHiddenObjects;
            _optimizeVertices    = optimizeVertices;
            _exportTangents      = exportTangents;
            CopyTexturesToOutput = copyTexturesToOutput;
            isBabylonExported    = outputFormat == "babylon" || outputFormat == "binary babylon";
            _exportSkin          = exportSkin;

            // Check directory exists
            if (!Directory.Exists(outputDirectory))
            {
                RaiseError("Exportation stopped: Output folder does not exist");
                ReportProgressChanged(100);
                return;
            }

            var watch = new Stopwatch();

            watch.Start();

            var outputBabylonDirectory = outputDirectory;
            var babylonScene           = new BabylonScene(outputBabylonDirectory);

            // Save scene
            if (autoSaveMayaFile)
            {
                RaiseMessage("Saving Maya file");

                // Query expand file name
                string fileName = MGlobal.executeCommandStringResult($@"file -q -exn;");

                // If scene has already been saved previously
                if (fileName.EndsWith(".ma") || fileName.EndsWith(".mb"))
                {
                    // Name is already specified and this line will not fail
                    MFileIO.save();
                }
                else
                {
                    // Open SaveAs dialog window
                    MGlobal.executeCommand($@"fileDialog2;");
                }
            }

            // Force output file extension to be babylon
            outputFileName = Path.ChangeExtension(outputFileName, "babylon");

            // Store selected nodes
            MSelectionList selectedNodes = new MSelectionList();

            MGlobal.getActiveSelectionList(selectedNodes);
            selectedNodeFullPaths = new List <string>();
            MItSelectionList mItSelectionList = new MItSelectionList(selectedNodes);

            while (!mItSelectionList.isDone)
            {
                MDagPath mDagPath = new MDagPath();
                try
                {
                    mItSelectionList.getDagPath(mDagPath);
                    selectedNodeFullPaths.Add(mDagPath.fullPathName);
                } catch
                {
                    // selected object is not a DAG object
                    // fail silently
                }

                mItSelectionList.next();
            }
            if (selectedNodeFullPaths.Count > 0)
            {
                RaiseMessage("Selected nodes full path");
                foreach (string selectedNodeFullPath in selectedNodeFullPaths)
                {
                    RaiseMessage(selectedNodeFullPath, 1);
                }
            }

            // Producer
            babylonScene.producer = new BabylonProducer
            {
                name             = "Maya",
                version          = "2018",
                exporter_version = exporterVersion,
                file             = outputFileName
            };

            // Global
            babylonScene.autoClear = true;
            // TODO - Retreive colors from Maya
            //babylonScene.clearColor = Loader.Core.GetBackGround(0, Tools.Forever).ToArray();
            //babylonScene.ambientColor = Loader.Core.GetAmbient(0, Tools.Forever).ToArray();

            // TODO - Add custom properties
            _exportQuaternionsInsteadOfEulers = true;

            PrintDAG(true);
            PrintDAG(false);

            // --------------------
            // ------ Nodes -------
            // --------------------
            RaiseMessage("Exporting nodes");

            // Clear materials
            referencedMaterials.Clear();
            multiMaterials.Clear();

            // Get all nodes
            var             dagIterator = new MItDag(MItDag.TraversalType.kDepthFirst, MFn.Type.kTransform);
            List <MDagPath> nodes       = new List <MDagPath>();

            while (!dagIterator.isDone)
            {
                MDagPath mDagPath = new MDagPath();
                dagIterator.getPath(mDagPath);

                // Check if one of its descendant (direct or not) is a mesh/camera/light/locator
                if (isNodeRelevantToExportRec(mDagPath)
                    // Ensure it's not one of the default cameras used as viewports in Maya
                    && defaultCameraNames.Contains(mDagPath.partialPathName) == false)
                {
                    nodes.Add(mDagPath);
                }
                else
                {
                    // Skip descendants
                    dagIterator.prune();
                }

                dagIterator.next();
            }
            // Export all nodes
            var progressionStep = 100.0f / nodes.Count;

            foreach (MDagPath mDagPath in nodes)
            {
                BabylonNode babylonNode = null;

                switch (getApiTypeOfDirectDescendants(mDagPath))
                {
                case MFn.Type.kMesh:
                    babylonNode = ExportMesh(mDagPath, babylonScene);
                    break;

                case MFn.Type.kCamera:
                    babylonNode = ExportCamera(mDagPath, babylonScene);
                    break;

                case MFn.Type.kLight:     // Lights api type are actually kPointLight, kSpotLight...
                    babylonNode = ExportLight(mDagPath, babylonScene);
                    break;

                case MFn.Type.kLocator:     // Camera target
                    babylonNode = ExportDummy(mDagPath, babylonScene);
                    break;
                }

                // If node is not exported successfully
                if (babylonNode == null)
                {
                    // Create a dummy (empty mesh)
                    babylonNode = ExportDummy(mDagPath, babylonScene);
                }
                ;

                // Update progress bar
                progression += progressionStep;
                ReportProgressChanged(progression);

                CheckCancelled();
            }
            RaiseMessage(string.Format("Total meshes: {0}", babylonScene.MeshesList.Count), Color.Gray, 1);


            // if nothing is enlightened, exclude all meshes
            foreach (BabylonLight light in babylonScene.LightsList)
            {
                if (light.includedOnlyMeshesIds.Length == 0)
                {
                    light.excludedMeshesIds = babylonScene.MeshesList.Select(m => m.id).ToArray();
                }
            }

            /*
             * Switch coordinate system at global level
             *
             * Add a root node with negative scaling
             * Pros - It's safer to use a root node
             * Cons - It's cleaner to switch at object level (as it is done now)
             * Use root node method when you want to be 100% sure of the output
             * Don't forget to also inverse winding order of mesh indices
             */
            //// Switch from right to left handed coordinate system
            //MUuid mUuid = new MUuid();
            //mUuid.generate();
            //var rootNode = new BabylonMesh
            //{
            //    name = "root",
            //    id = mUuid.asString(),
            //    scaling = new float[] { 1, 1, -1 }
            //};
            //foreach(var babylonMesh in babylonScene.MeshesList)
            //{
            //    // Add root meshes as child to root node
            //    if (babylonMesh.parentId == null)
            //    {
            //        babylonMesh.parentId = rootNode.id;
            //    }
            //}
            //babylonScene.MeshesList.Add(rootNode);

            // Main camera
            BabylonCamera babylonMainCamera = null;

            if (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);
            }

            if (babylonMainCamera == null)
            {
                RaiseWarning("No camera defined", 1);
            }
            else
            {
                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 ambient light was added for your convenience", 1);
                ExportDefaultLight(babylonScene);
            }
            else
            {
                RaiseMessage(string.Format("Total lights: {0}", babylonScene.LightsList.Count), Color.Gray, 1);
            }

            if (scaleFactorFloat != 1.0f)
            {
                RaiseMessage("A root node is added for scaling", 1);

                // Create root node for scaling
                BabylonMesh rootNode = new BabylonMesh {
                    name = "root", id = Tools.GenerateUUID()
                };
                rootNode.isDummy = true;
                float rootNodeScale = 1.0f / scaleFactorFloat;
                rootNode.scaling = new float[3] {
                    rootNodeScale, rootNodeScale, rootNodeScale
                };

                // Update all top nodes
                var babylonNodes = new List <BabylonNode>();
                babylonNodes.AddRange(babylonScene.MeshesList);
                babylonNodes.AddRange(babylonScene.CamerasList);
                babylonNodes.AddRange(babylonScene.LightsList);
                foreach (BabylonNode babylonNode in babylonNodes)
                {
                    if (babylonNode.parentId == null)
                    {
                        babylonNode.parentId = rootNode.id;
                    }
                }

                // Store root node
                babylonScene.MeshesList.Add(rootNode);
            }

            // --------------------
            // ----- Materials ----
            // --------------------
            RaiseMessage("Exporting materials");
            GenerateMaterialDuplicationDatas(babylonScene);
            foreach (var mat in referencedMaterials)
            {
                ExportMaterial(mat, babylonScene);
                CheckCancelled();
            }
            foreach (var mat in multiMaterials)
            {
                ExportMultiMaterial(mat.Key, mat.Value, babylonScene);
                CheckCancelled();
            }
            UpdateMeshesMaterialId(babylonScene);
            RaiseMessage(string.Format("Total: {0}", babylonScene.MaterialsList.Count + babylonScene.MultiMaterialsList.Count), Color.Gray, 1);


            // Export skeletons
            if (_exportSkin && skins.Count > 0)
            {
                progressSkin     = 0;
                progressSkinStep = 100 / skins.Count;
                ReportProgressChanged(progressSkin);
                RaiseMessage("Exporting skeletons");
                foreach (var skin in skins)
                {
                    ExportSkin(skin, babylonScene);
                }
            }

            // Output
            babylonScene.Prepare(false, false);
            if (isBabylonExported)
            {
                Write(babylonScene, outputBabylonDirectory, outputFileName, outputFormat, generateManifest);
            }

            ReportProgressChanged(100);

            // Export glTF
            if (outputFormat == "gltf" || outputFormat == "glb")
            {
                bool generateBinary = outputFormat == "glb";
                ExportGltf(babylonScene, outputDirectory, outputFileName, generateBinary);
            }

            watch.Stop();
            RaiseMessage(string.Format("Exportation done in {0:0.00}s", watch.ElapsedMilliseconds / 1000.0), Color.Blue);
        }
Esempio n. 30
0
        private void ExportNodeAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonNode babylonNode, GLTFNode gltfNode, BabylonScene babylonScene, BabylonAnimationGroup animationGroup = null)
        {
            var channelList = gltfAnimation.ChannelList;
            var samplerList = gltfAnimation.SamplerList;

            bool exportNonAnimated = exportParameters.animgroupExportNonAnimated;

            // Combine babylon animations from .babylon file and cached ones
            var babylonAnimations = new List <BabylonAnimation>();

            if (animationGroup != null)
            {
                var targetedAnimations = animationGroup.targetedAnimations.Where(animation => animation.targetId == babylonNode.id);
                foreach (var targetedAnimation in targetedAnimations)
                {
                    babylonAnimations.Add(targetedAnimation.animation);
                }
            }

            // Do not include the node animations if a provided animation group already includes them.
            if (babylonAnimations.Count <= 0)
            {
                if (babylonNode.animations != null)
                {
                    babylonAnimations.AddRange(babylonNode.animations);
                }

                if (babylonNode.extraAnimations != null)
                {
                    babylonAnimations.AddRange(babylonNode.extraAnimations);
                }
            }

            // Filter animations to only keep TRS ones
            babylonAnimations = babylonAnimations.FindAll(babylonAnimation => _getTargetPath(babylonAnimation.property) != null);

            if (babylonAnimations.Count > 0 || exportNonAnimated)
            {
                if (babylonAnimations.Count > 0)
                {
                    logger.RaiseMessage("GLTFExporter.Animation | Export animations of node named: " + babylonNode.name, 2);
                }
                else if (exportNonAnimated)
                {
                    logger.RaiseMessage("GLTFExporter.Animation | Export dummy animation for node named: " + babylonNode.name, 2);
                    // Export a dummy animation
                    babylonAnimations.Add(GetDummyAnimation(gltfNode, startFrame, endFrame, babylonScene));
                }


                foreach (BabylonAnimation babylonAnimation in babylonAnimations)
                {
                    var babylonAnimationKeysInRange = babylonAnimation.keys.Where(key => key.frame >= startFrame && key.frame <= endFrame);
                    if (babylonAnimationKeysInRange.Count() <= 0)
                    {
                        continue;
                    }

                    // Target
                    var gltfTarget = new GLTFChannelTarget
                    {
                        node = gltfNode.index
                    };
                    gltfTarget.path = _getTargetPath(babylonAnimation.property);

                    // --- Input ---
                    var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame);
                    if (accessorInput == null)
                    {
                        continue;
                    }

                    // --- Output ---
                    GLTFAccessor accessorOutput = _createAccessorOfPath(gltfTarget.path, gltf);

                    // Populate accessor
                    int numKeys = 0;
                    foreach (var babylonAnimationKey in babylonAnimationKeysInRange)
                    {
                        numKeys++;

                        // copy data before changing it in case animation groups overlap
                        float[] outputValues = new float[babylonAnimationKey.values.Length];
                        babylonAnimationKey.values.CopyTo(outputValues, 0);

                        // Switch coordinate system at object level
                        if (babylonAnimation.property == "position")
                        {
                            outputValues[2] *= -1;
                        }
                        else if (babylonAnimation.property == "rotationQuaternion")
                        {
                            outputValues[0] *= -1;
                            outputValues[1] *= -1;
                        }

                        // Store values as bytes
                        foreach (var outputValue in outputValues)
                        {
                            accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue));
                        }
                    }
                    ;
                    accessorOutput.count = numKeys;
                    if (accessorOutput.count == 0)
                    {
                        logger.RaiseWarning(String.Format("GLTFExporter.Animation | No frames to export in node animation \"{1}\" of node named \"{0}\". This will cause an error in the output gltf.", babylonNode.name, babylonAnimation.name));
                    }

                    // Animation sampler
                    var gltfAnimationSampler = new GLTFAnimationSampler
                    {
                        input  = accessorInput.index,
                        output = accessorOutput.index
                    };
                    gltfAnimationSampler.index = samplerList.Count;
                    samplerList.Add(gltfAnimationSampler);

                    // Channel
                    var gltfChannel = new GLTFChannel
                    {
                        sampler = gltfAnimationSampler.index,
                        target  = gltfTarget
                    };
                    channelList.Add(gltfChannel);
                }
            }

            ExportGLTFExtension(babylonNode, ref gltfAnimation, gltf);
        }