private BabylonActions ExportNodeAction(IIGameNode node)
        {
            string prop;
            if (node != null)
                prop = node.MaxNode.GetStringProperty("babylon_actionsbuilder", "");
            else
                prop = Loader.Core.RootNode.GetStringProperty("babylon_actionsbuilder", "");

            if (String.IsNullOrEmpty(prop))
                return null;

            var result = JsonConvert.DeserializeObject<BabylonActions>(prop);

            return result;
        }
        private string GetParentID(IIGameNode parentNode, BabylonScene babylonScene, IIGameScene scene)
        {
            var parentType = parentNode.IGameObject.IGameType;
            var parentId = parentNode.MaxNode.GetGuid().ToString();
            switch (parentType)
            {
                case Autodesk.Max.IGameObject.ObjectTypes.Light:
                case Autodesk.Max.IGameObject.ObjectTypes.Mesh:
                case Autodesk.Max.IGameObject.ObjectTypes.Camera:
                    break;


                default:
                    if (babylonScene.MeshesList.All(m => m.id != parentId))
                    {
                        ExportMesh(scene, parentNode, babylonScene);
                    }
                    break;
            }
            return parentId;
        }
Exemplo n.º 3
0
        private List <GlobalVertex> ExtractVertices(BabylonAbstractMesh babylonAbstractMesh, IIGameNode maxMorphTarget, bool optimizeVertices, List <int> faceIndexes)
        {
            var  gameMesh    = maxMorphTarget.IGameObject.AsGameMesh();
            bool initialized = gameMesh.InitializeData; // needed, the property is in fact a method initializing the exporter that has wrongly been auto
                                                        // translated into a property because it has no parameters

            var mtl            = maxMorphTarget.NodeMaterial;
            var multiMatsCount = 1;

            if (mtl != null)
            {
                multiMatsCount = Math.Max(mtl.SubMaterialCount, 1);
            }

            var invertedWorldMatrix = GetInvertWorldTM(maxMorphTarget, 0);
            var offsetTM            = GetOffsetTM(maxMorphTarget, 0);

            var vertices = new List <GlobalVertex>();

            ExtractGeometry(babylonAbstractMesh, vertices, new List <int>(), new List <BabylonSubMesh>(), null, null, gameMesh, invertedWorldMatrix, offsetTM, false, false, false, false, optimizeVertices, multiMatsCount, maxMorphTarget, ref faceIndexes);
            return(vertices);
        }
Exemplo n.º 4
0
 private bool IsMeshExportable(IIGameNode meshNode)
 {
     return(IsNodeExportable(meshNode));
 }
Exemplo n.º 5
0
 public void GenerateCoordinatesAnimations(IIGameNode meshNode, List <BabylonAnimation> animations)
 {
     GeneratePositionAnimation(meshNode, animations);
     GenerateRotationAnimation(meshNode, animations);
     GenerateScalingAnimation(meshNode, animations);
 }
Exemplo n.º 6
0
        private void ExtractFace(IIGameNode meshNode, IIGameSkin skin, IIGameMesh unskinnedMesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, List <GlobalVertex> vertices, List <int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List <int> boneIds)
        {
            int a, b, c;

            // parity is TRUE, if determinant negative ( counter-intuitive convention of 3ds max, see docs... :/ )

            // fix for cesium: currently, cesium does not expect a reversed winding order for negative scales
            //if (false)

            // for threejs and babylonjs (handle negative scales correctly (reversed winding order expected)
            if (invertedWorldMatrix.Parity)
            {
                // flipped case: reverse winding order
                a = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
                b = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
                c = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
            }
            else
            {
                // normal case
                a = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
                b = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
                c = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
            }

            indices.Add(a);
            indices.Add(b);
            indices.Add(c);

            if (a < minVertexIndex)
            {
                minVertexIndex = a;
            }

            if (b < minVertexIndex)
            {
                minVertexIndex = b;
            }

            if (c < minVertexIndex)
            {
                minVertexIndex = c;
            }

            if (a > maxVertexIndex)
            {
                maxVertexIndex = a;
            }

            if (b > maxVertexIndex)
            {
                maxVertexIndex = b;
            }

            if (c > maxVertexIndex)
            {
                maxVertexIndex = c;
            }


            indexCount += 3;
            CheckCancelled();
        }
Exemplo n.º 7
0
        private void ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene)
        {
            if (meshNode.MaxNode.IsInstance())
            {
                return;
            }

            if (meshNode.MaxNode.GetBoolProperty("babylonjs_noexport"))
            {
                return;
            }

            if (!ExportHiddenObjects && meshNode.MaxNode.IsHidden(NodeHideFlags.None, false))
            {
                return;
            }

            var  gameMesh    = meshNode.IGameObject.AsGameMesh();
            bool initialized = gameMesh.InitializeData; //needed, the property is in fact a method initializing the exporter that has wrongly been auto
            // translated into a property because it has no parameters

            var babylonMesh = new BabylonMesh {
                name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString()
            };

            if (meshNode.NodeParent != null)
            {
                babylonMesh.parentId = GetParentID(meshNode.NodeParent, babylonScene, scene);
            }

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

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

                var meshSound = new BabylonSound
                {
                    name            = filename,
                    autoplay        = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_autoplay", 1),
                    loop            = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_loop", 1),
                    volume          = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_volume", 1.0f),
                    playbackRate    = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_playbackrate", 1.0f),
                    connectedMeshId = babylonMesh.id,
                    isDirectional   = false,
                    spatialSound    = false,
                    distanceModel   = meshNode.MaxNode.GetStringProperty("babylonjs_sound_distancemodel", "linear"),
                    maxDistance     = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_maxdistance", 100f),
                    rolloffFactor   = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_rolloff", 1.0f),
                    refDistance     = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f),
                };

                var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional");

                if (isDirectional)
                {
                    meshSound.isDirectional  = true;
                    meshSound.coneInnerAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneinnerangle", 360f);
                    meshSound.coneOuterAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneouterangle", 360f);
                    meshSound.coneOuterGain  = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneoutergain", 1.0f);
                }

                babylonScene.SoundsList.Add(meshSound);

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

            // Misc.
#if MAX2017
            babylonMesh.isVisible      = meshNode.MaxNode.Renderable;
            babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows;
            babylonMesh.applyFog       = meshNode.MaxNode.ApplyAtmospherics;
#else
            babylonMesh.isVisible      = meshNode.MaxNode.Renderable == 1;
            babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1;
            babylonMesh.applyFog       = meshNode.MaxNode.ApplyAtmospherics == 1;
#endif
            babylonMesh.pickable                 = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable");
            babylonMesh.showBoundingBox          = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox");
            babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox");
            babylonMesh.alphaIndex               = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000);

            // Actions
            babylonMesh.actions = ExportNodeAction(meshNode);

            // Collisions
            babylonMesh.checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions");

            var        isSkinned          = gameMesh.IsObjectSkinned;
            var        skin               = gameMesh.IGameSkin;
            var        unskinnedMesh      = gameMesh;
            IGMatrix   skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true));
            List <int> boneIds            = null;
            int        maxNbBones         = 0;
            if (isSkinned)
            {
                bonesCount = skin.TotalSkinBoneCount;
                skins.Add(skin);

                skinnedNodes.Add(meshNode);
                babylonMesh.skeletonId = skins.IndexOf(skin);
                skin.GetInitSkinTM(skinInitPoseMatrix);
                boneIds = SortBones(skin);
                skinSortedBones[skin] = boneIds;
            }

            // Position / rotation / scaling
            var localTM = meshNode.GetObjectTM(0);
            if (meshNode.NodeParent != null)
            {
                var parentWorld = meshNode.NodeParent.GetObjectTM(0);
                localTM.MultiplyBy(parentWorld.Inverse);
            }

            var meshTrans         = localTM.Translation;
            var meshRotation      = localTM.Rotation;
            var meshScale         = localTM.Scaling;
            var exportQuaternions = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportquaternions");

            babylonMesh.position = new[] { meshTrans.X, meshTrans.Y, meshTrans.Z };

            if (exportQuaternions)
            {
                babylonMesh.rotationQuaternion = new[] { meshRotation.X, meshRotation.Y, meshRotation.Z, -meshRotation.W };
            }
            else
            {
                babylonMesh.rotation = QuaternionToEulerAngles(meshRotation);
            }

            babylonMesh.scaling = new[] { meshScale.X, meshScale.Y, meshScale.Z };

            // Mesh
            RaiseMessage(meshNode.Name, 1);

            if (unskinnedMesh.IGameType == Autodesk.Max.IGameObject.ObjectTypes.Mesh && unskinnedMesh.MaxMesh != null)
            {
                if (unskinnedMesh.NumberOfFaces < 1)
                {
                    RaiseError($"Mesh {babylonMesh.name} has no face", 2);
                }

                if (unskinnedMesh.NumberOfVerts < 3)
                {
                    RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2);
                }

                if (unskinnedMesh.NumberOfVerts >= 65536)
                {
                    RaiseWarning($"Mesh {babylonMesh.name} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2);
                }

                if (skin != null)
                {
                    for (var vertexIndex = 0; vertexIndex < unskinnedMesh.NumberOfVerts; vertexIndex++)
                    {
                        maxNbBones = Math.Max(maxNbBones, skin.GetNumberOfBones(vertexIndex));
                    }
                }

                // Physics
                var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None");

                if (impostorText != "None")
                {
                    switch (impostorText)
                    {
                    case "Sphere":
                        babylonMesh.physicsImpostor = 1;
                        break;

                    case "Box":
                        babylonMesh.physicsImpostor = 2;
                        break;

                    case "Plane":
                        babylonMesh.physicsImpostor = 3;
                        break;

                    default:
                        babylonMesh.physicsImpostor = 0;
                        break;
                    }

                    babylonMesh.physicsMass        = meshNode.MaxNode.GetFloatProperty("babylonjs_mass");
                    babylonMesh.physicsFriction    = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f);
                    babylonMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f);
                }

                // Material
                var mtl            = meshNode.NodeMaterial;
                var multiMatsCount = 1;

                if (mtl != null)
                {
                    babylonMesh.materialId = mtl.MaxMaterial.GetGuid().ToString();

                    if (!referencedMaterials.Contains(mtl))
                    {
                        referencedMaterials.Add(mtl);
                    }

                    multiMatsCount = Math.Max(mtl.SubMaterialCount, 1);
                }

                babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever);

                var  vertices        = new List <GlobalVertex>();
                var  indices         = new List <int>();
                var  mappingChannels = unskinnedMesh.ActiveMapChannelNum;
                bool hasUV           = false;
                bool hasUV2          = false;
                for (int i = 0; i < mappingChannels.Count; ++i)
                {
#if MAX2017
                    var indexer = i;
#else
                    var indexer = new IntPtr(i);
#endif
                    var channelNum = mappingChannels[indexer];
                    if (channelNum == 1)
                    {
                        hasUV = true;
                    }
                    else if (channelNum == 2)
                    {
                        hasUV2 = true;
                    }
                }
                var hasColor = unskinnedMesh.NumberOfColorVerts > 0;
                var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0;

                var optimizeVertices = meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices");

                // Compute normals
                List <GlobalVertex>[] verticesAlreadyExported = null;

                if (optimizeVertices)
                {
                    verticesAlreadyExported = new List <GlobalVertex> [unskinnedMesh.NumberOfVerts];
                }

                var subMeshes  = new List <BabylonSubMesh>();
                var indexStart = 0;


                for (int i = 0; i < multiMatsCount; ++i)
                {
                    int materialId     = meshNode.NodeMaterial?.GetMaterialID(i) ?? 0;
                    var indexCount     = 0;
                    var minVertexIndex = int.MaxValue;
                    var maxVertexIndex = int.MinValue;
                    var subMesh        = new BabylonSubMesh {
                        indexStart = indexStart, materialIndex = i
                    };

                    if (multiMatsCount == 1)
                    {
                        for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j)
                        {
                            var face = unskinnedMesh.GetFace(j);
                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                        }
                    }
                    else
                    {
                        ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId);
                        for (int j = 0; j < materialFaces.Count; ++j)
                        {
#if MAX2017
                            var faceIndexer = j;
#else
                            var faceIndexer = new IntPtr(j);
#endif
                            var face = materialFaces[faceIndexer];

#if !MAX2017
                            Marshal.FreeHGlobal(faceIndexer);
#endif
                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                        }
                    }

                    if (indexCount != 0)
                    {
                        subMesh.indexCount    = indexCount;
                        subMesh.verticesStart = minVertexIndex;
                        subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1;

                        indexStart += indexCount;

                        subMeshes.Add(subMesh);
                    }
                }

                if (vertices.Count >= 65536)
                {
                    RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2);

                    if (!optimizeVertices)
                    {
                        RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2);
                    }
                }

                RaiseMessage($"{vertices.Count} vertices, {indices.Count/3} faces", 2);

                // Buffers
                babylonMesh.positions = vertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();
                babylonMesh.normals   = vertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();
                if (hasUV)
                {
                    babylonMesh.uvs = vertices.SelectMany(v => new[] { v.UV.X, 1 - v.UV.Y }).ToArray();
                }
                if (hasUV2)
                {
                    babylonMesh.uvs2 = vertices.SelectMany(v => new[] { v.UV2.X, 1 - v.UV2.Y }).ToArray();
                }

                if (skin != null)
                {
                    babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray();
                    babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray();

                    babylonMesh.numBoneInfluencers = maxNbBones;
                    if (maxNbBones > 4)
                    {
                        babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra != null ? v.WeightsExtra.ToArray() : new[] { 0.0f, 0.0f, 0.0f, 0.0f }).ToArray();
                        babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray();
                    }
                }

                if (hasColor)
                {
                    babylonMesh.colors         = vertices.SelectMany(v => v.Color.ToArray()).ToArray();
                    babylonMesh.hasVertexAlpha = hasAlpha;
                }

                babylonMesh.subMeshes = subMeshes.ToArray();

                // Buffers - Indices
                babylonMesh.indices = indices.ToArray();
            }

            // Instances
            var tabs = Loader.Global.NodeTab.Create();

            Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs);
            var instances = new List <BabylonAbstractMesh>();

            for (var index = 0; index < tabs.Count; index++)
            {
#if MAX2017
                var indexer = index;
#else
                var indexer = new IntPtr(index);
#endif
                var tab = tabs[indexer];

#if !MAX2017
                Marshal.FreeHGlobal(indexer);
#endif

                if (meshNode.MaxNode.GetGuid() == tab.GetGuid())
                {
                    continue;
                }
                var instanceGameNode = scene.GetIGameNode(tab);
                if (instanceGameNode == null)
                {
                    continue;
                }
                tab.MarkAsInstance();

                var instance = new BabylonAbstractMesh {
                    name = tab.Name
                };
                {
                    var instanceLocalTM = instanceGameNode.GetObjectTM(0);

                    var instanceTrans    = instanceLocalTM.Translation;
                    var instanceRotation = instanceLocalTM.Rotation;
                    var instanceScale    = instanceLocalTM.Scaling;

                    instance.id       = instanceGameNode.MaxNode.GetGuid().ToString();
                    instance.position = new[] { instanceTrans.X, instanceTrans.Y, instanceTrans.Z };

                    if (exportQuaternions)
                    {
                        instance.rotationQuaternion = new[] { instanceRotation.X, instanceRotation.Y, instanceRotation.Z, -instanceRotation.W };
                    }
                    else
                    {
                        instance.rotation = QuaternionToEulerAngles(instanceRotation);
                    }

                    instance.scaling = new[] { instanceScale.X, instanceScale.Y, instanceScale.Z };

                    if (instanceGameNode.NodeParent != null)
                    {
                        instance.parentId = GetParentID(instanceGameNode.NodeParent, babylonScene, scene);
                    }
                }
                var instanceAnimations = new List <BabylonAnimation>();
                GenerateCoordinatesAnimations(meshNode, instanceAnimations);
                instance.animations = instanceAnimations.ToArray();

                instances.Add(instance);
            }

            babylonMesh.instances = instances.ToArray();

            // Animations
            var animations = new List <BabylonAnimation>();

            GenerateCoordinatesAnimations(meshNode, animations);

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

            babylonMesh.animations = animations.ToArray();

            if (meshNode.MaxNode.GetBoolProperty("babylonjs_autoanimate", 1))
            {
                babylonMesh.autoAnimate     = true;
                babylonMesh.autoAnimateFrom = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from");
                babylonMesh.autoAnimateTo   = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to", 100);
                babylonMesh.autoAnimateLoop = meshNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop", 1);
            }

            babylonScene.MeshesList.Add(babylonMesh);
        }
        private List <IIGameNode> GetRelevantNodes(IIGameSkin skin)
        {
            int logRank = 2;

            // For optimization
            if (relevantNodesBySkin.ContainsKey(skin))
            {
                return(relevantNodesBySkin[skin]);
            }

            List <IIGameNode> bones = GetBones(skin);

            if (bones.Count == 0)
            {
                RaiseWarning("Skin has no bones.", logRank);
                return(new List <IIGameNode>());
            }

            if (bones.Contains(null))
            {
                RaiseError("Skin has bones that are outside of the exported hierarchy.", logRank);
                RaiseError("The skin cannot be exported", logRank);
                return(new List <IIGameNode>());
            }

            List <IIGameNode> allHierarchyNodes    = null;
            IIGameNode        lowestCommonAncestor = GetLowestCommonAncestor(bones, ref allHierarchyNodes);

            if (lowestCommonAncestor == null)
            {
                RaiseError($"More than one root node for the skin. The skeleton bones need to be part of the same hierarchy.", logRank);
                RaiseError($"The skin cannot be exported", logRank);

                return(new List <IIGameNode>());
            }

            // starting from the root, sort the nodes by depth first (add the children before the siblings)
            List <IIGameNode>  sorted   = new List <IIGameNode>();
            Stack <IIGameNode> siblings = new Stack <IIGameNode>();   // keep the siblings in a LIFO list to add them after the children

            siblings.Push(lowestCommonAncestor);

            // add the skeletonroot:
            // - as a fallback for vertices without any joint weights (although invalid joints could also be "ok"?)
            // - to have easy access to the root node for the gltf's [skin.skeleton] property (skeleton root node)
            // [##onlyBones] commented for now because uncertain if it will work with babylon bone exports
            //sorted.Add(lowestCommonAncestor);

            while (siblings.Count > 0)
            {
                IIGameNode currentNode = siblings.Pop();

                if (allHierarchyNodes.Contains(currentNode))    // The node is part of the skeleton hierarchy
                {
                    // only add if the node is an actual bone (to keep the joint list small)
                    // [##onlyBones] commented for now because uncertain if it will work with babylon bone exports
                    //if (bones.Contains(currentNode))
                    sorted.Add(currentNode);

                    // Add its children to the stack (in reverse order because it's a LIFO)
                    int childCount = currentNode.ChildCount;
                    for (int index = 0; index < childCount; index++)
                    {
                        siblings.Push(currentNode.GetNodeChild(childCount - 1 - index));
                    }
                }
            }

            relevantNodesBySkin.Add(skin, sorted);   // Stock the result for optimization

            return(sorted);
        }
        private void ExportCamera(IIGameScene scene,  IIGameNode cameraNode, BabylonScene babylonScene)
        {
            if (cameraNode.MaxNode.GetBoolProperty("babylonjs_noexport"))
            {
                return;
            }
            var gameCamera = cameraNode.IGameObject.AsGameCamera();
            var maxCamera = gameCamera.MaxObject as ICameraObject;
            var initialized = gameCamera.InitializeData;
            var babylonCamera = new BabylonCamera();

            RaiseMessage(cameraNode.Name, 1);
            babylonCamera.name = cameraNode.Name;
            babylonCamera.id = cameraNode.MaxNode.GetGuid().ToString();
            if (cameraNode.NodeParent != null)
            {
                babylonCamera.parentId = GetParentID(cameraNode.NodeParent, babylonScene, scene);
            }

            babylonCamera.fov = Tools.ConvertFov(maxCamera.GetFOV(0, Tools.Forever));

            if (maxCamera.ManualClip == 1)
            {
                babylonCamera.minZ = maxCamera.GetClipDist(0, 1, Tools.Forever);
                babylonCamera.maxZ = maxCamera.GetClipDist(0, 2, Tools.Forever);
            }
            else
            {
                babylonCamera.minZ = 0.1f;
                babylonCamera.maxZ = 10000.0f;
            }

            if (babylonCamera.minZ == 0.0f)
            {
                babylonCamera.minZ = 0.1f;
            }

            // Type
            babylonCamera.type = cameraNode.MaxNode.GetStringProperty("babylonjs_type", "FreeCamera");

            // Control
            babylonCamera.speed = cameraNode.MaxNode.GetFloatProperty("babylonjs_speed", 1.0f);
            babylonCamera.inertia = cameraNode.MaxNode.GetFloatProperty("babylonjs_inertia", 0.9f);

            // Collisions
            babylonCamera.checkCollisions = cameraNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions");
            babylonCamera.applyGravity = cameraNode.MaxNode.GetBoolProperty("babylonjs_applygravity");
            babylonCamera.ellipsoid = cameraNode.MaxNode.GetVector3Property("babylonjs_ellipsoid");

            // Position
            var wm = cameraNode.GetLocalTM(0);
            if (cameraNode.NodeParent != null)
            {
                var parentWorld = cameraNode.NodeParent.GetObjectTM(0);
                wm.MultiplyBy(parentWorld.Inverse);
            }
            var position = wm.Translation;
            babylonCamera.position = new [] { position.X, position.Y, position.Z };

            // Target
            var target = gameCamera.CameraTarget;
            if (target != null)
            {
                babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString();
            }
            else
            {
                var dir = wm.GetRow(3);
                babylonCamera.target = new [] { position.X - dir.X, position.Y - dir.Y, position.Z - dir.Z };
            }

            // Animations
            var animations = new List<BabylonAnimation>();

            ExportVector3Animation("position", animations, key =>
            {
                var tm = cameraNode.GetLocalTM(key);
                if (cameraNode.NodeParent != null)
                {
                    var parentWorld = cameraNode.NodeParent.GetObjectTM(key);
                    tm.MultiplyBy(parentWorld.Inverse);
                }
                var translation = tm.Translation;
                return new [] { translation.X, translation.Y, translation.Z };
            });

            if (gameCamera.CameraTarget == null)
            {
                ExportVector3Animation("target", animations, key =>
                {
                    var tm = cameraNode.GetLocalTM(key);
                    if (cameraNode.NodeParent != null)
                    {
                        var parentWorld = cameraNode.NodeParent.GetObjectTM(key);
                        tm.MultiplyBy(parentWorld.Inverse);
                    }
                    var translation = tm.Translation;
                    var dir = tm.GetRow(3);
                    return new float[] { translation.X - dir.X, translation.Y - dir.Y, translation.Z - dir.Z };
                });
            }

            ExportFloatAnimation("fov", animations, key => new[] { Tools.ConvertFov((gameCamera.MaxObject as ICameraObject).GetFOV(key, Tools.Forever)) });

            babylonCamera.animations = animations.ToArray();

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

            babylonScene.CamerasList.Add(babylonCamera);
        }
Exemplo n.º 10
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;

            // If the light has a children and the export is to babylon, add a dummy
            // 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 = isBabylonExported && (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;
            }
            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);
                }
                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
                        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);
            }


            // 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);
        }
Exemplo n.º 11
0
        private void ExtractGeometry(List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, List <int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, IMatrix3 invertedWorldMatrix, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode)
        {
            List <GlobalVertex>[] verticesAlreadyExported = null;

            if (optimizeVertices)
            {
                verticesAlreadyExported = new List <GlobalVertex> [unskinnedMesh.NumberOfVerts];
            }

            var indexStart = 0;


            for (int i = 0; i < multiMatsCount; ++i)
            {
                int materialId     = meshNode.NodeMaterial?.GetMaterialID(i) ?? 0;
                var indexCount     = 0;
                var minVertexIndex = int.MaxValue;
                var maxVertexIndex = int.MinValue;
                var subMesh        = new BabylonSubMesh {
                    indexStart = indexStart, materialIndex = i
                };

                if (multiMatsCount == 1)
                {
                    for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j)
                    {
                        var face = unskinnedMesh.GetFace(j);
                        ExtractFace(skin, unskinnedMesh, invertedWorldMatrix, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                    }
                }
                else
                {
                    ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId);
                    for (int j = 0; j < materialFaces.Count; ++j)
                    {
#if MAX2017 || MAX2018
                        var faceIndexer = j;
#else
                        var faceIndexer = new IntPtr(j);
#endif
                        var face = materialFaces[faceIndexer];

#if !MAX2017 && !MAX2018
                        Marshal.FreeHGlobal(faceIndexer);
#endif
                        ExtractFace(skin, unskinnedMesh, invertedWorldMatrix, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                    }
                }

                if (indexCount != 0)
                {
                    subMesh.indexCount    = indexCount;
                    subMesh.verticesStart = minVertexIndex;
                    subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1;

                    indexStart += indexCount;

                    subMeshes.Add(subMesh);
                }
            }
        }
Exemplo n.º 12
0
        private BabylonCamera ExportCamera(IIGameScene scene, IIGameNode cameraNode, BabylonScene babylonScene)
        {
            if (IsCameraExportable(cameraNode) == false)
            {
                return(null);
            }

            var gameCamera    = cameraNode.IGameObject.AsGameCamera();
            var maxCamera     = gameCamera.MaxObject as ICameraObject;
            var initialized   = gameCamera.InitializeData;
            var babylonCamera = new BabylonCamera();

            RaiseMessage(cameraNode.Name, 1);
            babylonCamera.name = cameraNode.Name;
            babylonCamera.id   = cameraNode.MaxNode.GetGuid().ToString();
            if (cameraNode.NodeParent != null)
            {
                babylonCamera.parentId = cameraNode.NodeParent.MaxNode.GetGuid().ToString();
            }

            babylonCamera.fov = Tools.ConvertFov(maxCamera.GetFOV(0, Tools.Forever));

            if (maxCamera.ManualClip == 1)
            {
                babylonCamera.minZ = maxCamera.GetClipDist(0, 1, Tools.Forever);
                babylonCamera.maxZ = maxCamera.GetClipDist(0, 2, Tools.Forever);
            }
            else
            {
                babylonCamera.minZ = 0.1f;
                babylonCamera.maxZ = 10000.0f;
            }

            if (babylonCamera.minZ == 0.0f)
            {
                babylonCamera.minZ = 0.1f;
            }

            // Type
            babylonCamera.type = cameraNode.MaxNode.GetStringProperty("babylonjs_type", "FreeCamera");

            // Control
            babylonCamera.speed   = cameraNode.MaxNode.GetFloatProperty("babylonjs_speed", 1.0f);
            babylonCamera.inertia = cameraNode.MaxNode.GetFloatProperty("babylonjs_inertia", 0.9f);

            // Collisions
            babylonCamera.checkCollisions = cameraNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions");
            babylonCamera.applyGravity    = cameraNode.MaxNode.GetBoolProperty("babylonjs_applygravity");
            babylonCamera.ellipsoid       = cameraNode.MaxNode.GetVector3Property("babylonjs_ellipsoid");

            // Position / rotation
            var localTM = GetLocalTM(cameraNode, 0);

            var position = localTM.Translation;
            var rotation = localTM.Rotation;

            babylonCamera.position = new[] { position.X, position.Y, position.Z };

            var rotationQuaternion = new BabylonQuaternion {
                X = rotation.X, Y = rotation.Y, Z = rotation.Z, W = -rotation.W
            };

            if (ExportQuaternionsInsteadOfEulers)
            {
                babylonCamera.rotationQuaternion = rotationQuaternion.ToArray();
            }
            else
            {
                babylonCamera.rotation = rotationQuaternion.toEulerAngles().ToArray();
            }

            // Target
            var target = gameCamera.CameraTarget;

            if (target != null)
            {
                babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString();
            }
            else
            {
                // TODO - Check if should be local or world
                var vDir = Loader.Global.Point3.Create(0, -1, 0);
                vDir = localTM.ExtractMatrix3().VectorTransform(vDir).Normalize;
                vDir = vDir.Add(position);
                babylonCamera.target = new[] { vDir.X, vDir.Y, vDir.Z };
            }

            // Animations
            var animations = new List <BabylonAnimation>();

            GeneratePositionAnimation(cameraNode, animations);

            if (target == null)
            {
                // Export rotation animation
                //GenerateRotationAnimation(cameraNode, animations);

                ExportVector3Animation("target", animations, key =>
                {
                    var wmCam       = GetLocalTM(cameraNode, key);
                    var positionCam = wmCam.Translation;
                    var vDir        = Loader.Global.Point3.Create(0, -1, 0);
                    vDir            = wmCam.ExtractMatrix3().VectorTransform(vDir).Normalize;
                    vDir            = vDir.Add(positionCam);
                    return(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(cameraNode, extraAnimations, true);
            babylonCamera.extraAnimations = extraAnimations;

            ExportFloatAnimation("fov", animations, key => new[] { Tools.ConvertFov((gameCamera.MaxObject as ICameraObject).GetFOV(key, Tools.Forever)) });

            babylonCamera.animations = animations.ToArray();

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

            babylonScene.CamerasList.Add(babylonCamera);

            return(babylonCamera);
        }
Exemplo n.º 13
0
        int CreateGlobalVertex(IIGameNode meshNode, IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds)
        {
            var vertexIndex = (int)face.Vert[facePart];

            // Position can by retreived in world space or object space
            // Unfortunately, this value can't be retreived in local space
            var vertex = new GlobalVertex
            {
                BaseIndex = vertexIndex,
                Position  = mesh.GetVertex(vertexIndex, false),             // world space
                Normal    = mesh.GetNormal((int)face.Norm[facePart], false) // world space
            };

            if (exportParameters.exportTangents)
            {
                int     indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1);
                IPoint3 normal    = vertex.Normal.Normalize;
                IPoint3 tangent   = mesh.GetTangent(indexTangentBinormal, 1).Normalize;
                IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize;
                int     w         = GetW(normal, tangent, bitangent);
                vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w };
            }

            // Convert position and normal to local space
            vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position);

            vertex.Normal = invertedWorldMatrix.VectorTransform(vertex.Normal);
            // 1. scale normals with node scales
            var nodeScaling = BabylonVector3.FromArray(babylonAbstractMesh.scaling);

            vertex.Normal = vertex.Normal.Multiply(Loader.Global.Point3.Create(Math.Abs(nodeScaling.X), Math.Abs(nodeScaling.Y), Math.Abs(nodeScaling.Z)));

            // 2. scale normals with objectOffsetScales (unrotate by objectOffsetRot, then scale, then rotate again)
            // note: LH coordinate system => flip y and z
            var objOffsetScale          = Loader.Global.Point3.Create(meshNode.MaxNode.ObjOffsetScale.S);
            var scaleX                  = Math.Abs(objOffsetScale.X);
            var scaleY                  = Math.Abs(objOffsetScale.Y);
            var scaleZ                  = Math.Abs(objOffsetScale.Z);
            var objOffsetScaleFlipYZInv = Loader.Global.Point3.Create(1 / scaleX, 1 / scaleZ, 1 / scaleY);

            var objOffsetQuat = meshNode.MaxNode.ObjOffsetRot;
            var qFlippedYZ    = objOffsetQuat;
            var tmpSwap       = objOffsetQuat.Y;

            qFlippedYZ.Y = objOffsetQuat.Z;
            qFlippedYZ.Z = tmpSwap;

            var nUnrotated       = RotateVectorByQuaternion(vertex.Normal, qFlippedYZ);
            var nUnrotatedScaled = nUnrotated.Multiply(objOffsetScaleFlipYZInv);

            nUnrotatedScaled = nUnrotatedScaled.Normalize;
            var nRerotatedScaled = RotateVectorByQuaternion(nUnrotatedScaled, qFlippedYZ.Conjugate);

            vertex.Normal = nRerotatedScaled;

            if (hasUV)
            {
                var indices = new int[3];
                unsafe
                {
                    fixed(int *indicesPtr = indices)
                    {
                        mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr));
                    }
                }
                var texCoord = mesh.GetMapVertex(1, indices[facePart]);
                vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y);
            }

            if (hasUV2)
            {
                var indices = new int[3];
                unsafe
                {
                    fixed(int *indicesPtr = indices)
                    {
                        mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr));
                    }
                }
                var texCoord = mesh.GetMapVertex(2, indices[facePart]);
                vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y);
            }

            if (hasColor)
            {
                var   vertexColorIndex = (int)face.Color[facePart];
                var   vertexColor      = mesh.GetColorVertex(vertexColorIndex);
                float alpha            = 1;
                if (hasAlpha)
                {
                    var indices = new int[3];
                    unsafe
                    {
                        fixed(int *indicesPtr = indices)
                        {
                            mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr));
                        }
                    }
                    var color = mesh.GetMapVertex(-2, indices[facePart]);

                    alpha = color.X;
                }

                vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha };
            }

            if (skin != null)
            {
                float weight0 = 0;
                float weight1 = 0;
                float weight2 = 0;
                float weight3 = 0;
                int   bone0   = bonesCount;
                int   bone1   = bonesCount;
                int   bone2   = bonesCount;
                int   bone3   = bonesCount;
                var   nbBones = skin.GetNumberOfBones(vertexIndex);

                if (nbBones > 0)
                {
                    bone0   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 0).NodeID);
                    weight0 = skin.GetWeight(vertexIndex, 0);
                }

                if (nbBones > 1)
                {
                    bone1   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 1).NodeID);
                    weight1 = skin.GetWeight(vertexIndex, 1);
                }

                if (nbBones > 2)
                {
                    bone2   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 2).NodeID);
                    weight2 = skin.GetWeight(vertexIndex, 2);
                }

                if (nbBones > 3)
                {
                    bone3   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 3).NodeID);
                    weight3 = skin.GetWeight(vertexIndex, 3);
                }

                if (nbBones == 0)
                {
                    weight0 = 1.0f;
                    bone0   = bonesCount;
                }

                vertex.Weights      = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3);
                vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0;

                if (nbBones > 4)
                {
                    bone0   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 4).NodeID);
                    weight0 = skin.GetWeight(vertexIndex, 4);

                    weight1 = 0;
                    weight2 = 0;
                    weight3 = 0;

                    if (nbBones > 5)
                    {
                        bone1   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 5).NodeID);
                        weight1 = skin.GetWeight(vertexIndex, 5);
                    }

                    if (nbBones > 6)
                    {
                        bone2   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 6).NodeID);
                        weight2 = skin.GetWeight(vertexIndex, 6);
                    }

                    if (nbBones > 7)
                    {
                        bone3   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 7).NodeID);
                        weight3 = skin.GetWeight(vertexIndex, 7);
                    }

                    vertex.WeightsExtra      = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3);
                    vertex.BonesIndicesExtra = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0;

                    if (nbBones > 8)
                    {
                        RaiseError("Too many bones influences per vertex: " + nbBones + ". Babylon.js only support 8 bones influences per vertex.", 2);
                    }
                }
            }

            if (verticesAlreadyExported != null)
            {
                if (verticesAlreadyExported[vertexIndex] != null)
                {
                    var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex);

                    if (index > -1)
                    {
                        return(verticesAlreadyExported[vertexIndex][index].CurrentIndex);
                    }
                }
                else
                {
                    verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>();
                }

                vertex.CurrentIndex = vertices.Count;
                verticesAlreadyExported[vertexIndex].Add(vertex);
            }

            vertices.Add(vertex);

            return(vertices.Count - 1);
        }
 public bool isAnimated(IIGameNode gameNode)
 {
     return(isPositionAnimated(gameNode) ||
            isRotationAnimated(gameNode) ||
            isScaleAnimated(gameNode));
 }
 public bool isScaleAnimated(IIGameNode gameNode)
 {
     return(gameNode.IGameControl != null && gameNode.IGameControl.IsAnimated(IGameControlType.Scale));
 }
Exemplo n.º 16
0
        private BabylonNode ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene)
        {
            if (IsMeshExportable(meshNode) == false)
            {
                return(null);
            }

            RaiseMessage(meshNode.Name, 1);

            // Instances
            var tabs = Loader.Global.NodeTab.Create();

            Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs);
            if (tabs.Count > 1)
            {
                // For a mesh with instances, we distinguish between master and instance meshes:
                //      - a master mesh stores all the info of the mesh (transform, hierarchy, animations + vertices, indices, materials, bones...)
                //      - an instance mesh only stores the info of the node (transform, hierarchy, animations)

                // Check if this mesh has already been exported
                BabylonMesh babylonMasterMesh = null;
                var         index             = 0;
                while (babylonMasterMesh == null &&
                       index < tabs.Count)
                {
#if MAX2017 || MAX2018
                    var tab = tabs[index];
#else
                    var tab = tabs[new IntPtr(index)];
#endif

                    babylonMasterMesh = babylonScene.MeshesList.Find(_babylonMesh => {
                        // Same id
                        return(_babylonMesh.id == tab.GetGuid().ToString() &&
                               // Mesh is not a dummy
                               _babylonMesh.isDummy == false);
                    });

                    index++;
                }

                if (babylonMasterMesh != null)
                {
                    // Mesh already exported
                    // Export this node as instance

                    meshNode.MaxNode.MarkAsInstance();

                    var babylonInstanceMesh = new BabylonAbstractMesh {
                        name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString()
                    };

                    // Add instance to master mesh
                    List <BabylonAbstractMesh> list = babylonMasterMesh.instances != null?babylonMasterMesh.instances.ToList() : new List <BabylonAbstractMesh>();

                    list.Add(babylonInstanceMesh);
                    babylonMasterMesh.instances = list.ToArray();

                    // Export transform / hierarchy / animations
                    exportNode(babylonInstanceMesh, meshNode, scene, babylonScene);

                    // Animations
                    exportAnimation(babylonInstanceMesh, meshNode);

                    return(babylonInstanceMesh);
                }
            }

            var  gameMesh    = meshNode.IGameObject.AsGameMesh();
            bool initialized = gameMesh.InitializeData; // needed, the property is in fact a method initializing the exporter that has wrongly been auto
                                                        // translated into a property because it has no parameters

            var babylonMesh = new BabylonMesh {
                name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString()
            };

            // Position / rotation / scaling / hierarchy
            exportNode(babylonMesh, meshNode, scene, babylonScene);

            // Sounds
            var soundName = meshNode.MaxNode.GetStringProperty("babylonjs_sound_filename", "");
            if (!string.IsNullOrEmpty(soundName))
            {
                var filename = Path.GetFileName(soundName);

                var meshSound = new BabylonSound
                {
                    name            = filename,
                    autoplay        = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_autoplay", 1),
                    loop            = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_loop", 1),
                    volume          = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_volume", 1.0f),
                    playbackRate    = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_playbackrate", 1.0f),
                    connectedMeshId = babylonMesh.id,
                    isDirectional   = false,
                    spatialSound    = false,
                    distanceModel   = meshNode.MaxNode.GetStringProperty("babylonjs_sound_distancemodel", "linear"),
                    maxDistance     = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_maxdistance", 100f),
                    rolloffFactor   = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_rolloff", 1.0f),
                    refDistance     = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f),
                };

                var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional");

                if (isDirectional)
                {
                    meshSound.isDirectional  = true;
                    meshSound.coneInnerAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneinnerangle", 360f);
                    meshSound.coneOuterAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneouterangle", 360f);
                    meshSound.coneOuterGain  = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneoutergain", 1.0f);
                }

                babylonScene.SoundsList.Add(meshSound);

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

            // Misc.
#if MAX2017 || MAX2018
            babylonMesh.isVisible      = meshNode.MaxNode.Renderable;
            babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows;
            babylonMesh.applyFog       = meshNode.MaxNode.ApplyAtmospherics;
#else
            babylonMesh.isVisible      = meshNode.MaxNode.Renderable == 1;
            babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1;
            babylonMesh.applyFog       = meshNode.MaxNode.ApplyAtmospherics == 1;
#endif
            babylonMesh.pickable                 = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable");
            babylonMesh.showBoundingBox          = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox");
            babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox");
            babylonMesh.alphaIndex               = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000);

            // Collisions
            babylonMesh.checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions");

            var        isSkinned          = gameMesh.IsObjectSkinned;
            var        skin               = gameMesh.IGameSkin;
            var        unskinnedMesh      = gameMesh;
            IGMatrix   skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true));
            List <int> boneIds            = null;
            int        maxNbBones         = 0;
            if (isSkinned)
            {
                bonesCount = skin.TotalSkinBoneCount;
                var skinAlreadyStored = skins.Find(_skin => IsSkinEqualTo(_skin, skin));
                if (skinAlreadyStored == null)
                {
                    skins.Add(skin);
                }
                else
                {
                    skin = skinAlreadyStored;
                }

                skinnedNodes.Add(meshNode);
                babylonMesh.skeletonId = skins.IndexOf(skin);
                skin.GetInitSkinTM(skinInitPoseMatrix);
                boneIds = SortBones(skin);
                skinSortedBones[skin] = boneIds;
            }

            // Mesh

            if (unskinnedMesh.IGameType == Autodesk.Max.IGameObject.ObjectTypes.Mesh && unskinnedMesh.MaxMesh != null)
            {
                if (unskinnedMesh.NumberOfFaces < 1)
                {
                    RaiseError($"Mesh {babylonMesh.name} has no face", 2);
                }

                if (unskinnedMesh.NumberOfVerts < 3)
                {
                    RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2);
                }

                if (unskinnedMesh.NumberOfVerts >= 65536)
                {
                    RaiseWarning($"Mesh {babylonMesh.name} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2);
                }

                if (skin != null)
                {
                    for (var vertexIndex = 0; vertexIndex < unskinnedMesh.NumberOfVerts; vertexIndex++)
                    {
                        maxNbBones = Math.Max(maxNbBones, skin.GetNumberOfBones(vertexIndex));
                    }
                }

                // Physics
                var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None");

                if (impostorText != "None")
                {
                    switch (impostorText)
                    {
                    case "Sphere":
                        babylonMesh.physicsImpostor = 1;
                        break;

                    case "Box":
                        babylonMesh.physicsImpostor = 2;
                        break;

                    case "Plane":
                        babylonMesh.physicsImpostor = 3;
                        break;

                    default:
                        babylonMesh.physicsImpostor = 0;
                        break;
                    }

                    babylonMesh.physicsMass        = meshNode.MaxNode.GetFloatProperty("babylonjs_mass");
                    babylonMesh.physicsFriction    = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f);
                    babylonMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f);
                }

                // Material
                var mtl            = meshNode.NodeMaterial;
                var multiMatsCount = 1;

                if (mtl != null)
                {
                    babylonMesh.materialId = mtl.MaxMaterial.GetGuid().ToString();

                    if (!referencedMaterials.Contains(mtl))
                    {
                        referencedMaterials.Add(mtl);
                    }

                    multiMatsCount = Math.Max(mtl.SubMaterialCount, 1);
                }

                babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever);

                var  vertices        = new List <GlobalVertex>();
                var  indices         = new List <int>();
                var  mappingChannels = unskinnedMesh.ActiveMapChannelNum;
                bool hasUV           = false;
                bool hasUV2          = false;
                for (int i = 0; i < mappingChannels.Count; ++i)
                {
#if MAX2017 || MAX2018
                    var channelNum = mappingChannels[i];
#else
                    var channelNum = mappingChannels[new IntPtr(i)];
#endif
                    if (channelNum == 1)
                    {
                        hasUV = true;
                    }
                    else if (channelNum == 2)
                    {
                        hasUV2 = true;
                    }
                }
                var hasColor = unskinnedMesh.NumberOfColorVerts > 0;
                var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0;

                var optimizeVertices = meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices");

                var invertedWorldMatrix = GetInvertWorldTM(meshNode, 0);

                // Compute normals
                var        subMeshes   = new List <BabylonSubMesh>();
                List <int> faceIndexes = null;
                ExtractGeometry(vertices, indices, subMeshes, boneIds, skin, unskinnedMesh, invertedWorldMatrix, hasUV, hasUV2, hasColor, hasAlpha, optimizeVertices, multiMatsCount, meshNode, ref faceIndexes);

                if (vertices.Count >= 65536)
                {
                    RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2);

                    if (!optimizeVertices)
                    {
                        RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2);
                    }
                }

                RaiseMessage($"{vertices.Count} vertices, {indices.Count / 3} faces", 2);

                // Buffers
                babylonMesh.positions = vertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();

                // flip normals depending on parity
                var parityObject = meshNode.GetObjectTM(0).ExtractMatrix3().Parity;

                // for cesium, threejs and babylonjs (all the same)
                if (parityObject)
                {
                    // flipped case: reverse normals
                    babylonMesh.normals = vertices.SelectMany(v => new[] { -v.Normal.X, -v.Normal.Y, -v.Normal.Z }).ToArray();
                }
                else
                {
                    // normal case
                    babylonMesh.normals = vertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();
                }

                if (hasUV)
                {
                    babylonMesh.uvs = vertices.SelectMany(v => new[] { v.UV.X, 1 - v.UV.Y }).ToArray();
                }
                if (hasUV2)
                {
                    babylonMesh.uvs2 = vertices.SelectMany(v => new[] { v.UV2.X, 1 - v.UV2.Y }).ToArray();
                }

                if (skin != null)
                {
                    babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray();
                    babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray();

                    babylonMesh.numBoneInfluencers = maxNbBones;
                    if (maxNbBones > 4)
                    {
                        babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra != null ? v.WeightsExtra.ToArray() : new[] { 0.0f, 0.0f, 0.0f, 0.0f }).ToArray();
                        babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray();
                    }
                }

                if (hasColor)
                {
                    babylonMesh.colors         = vertices.SelectMany(v => v.Color.ToArray()).ToArray();
                    babylonMesh.hasVertexAlpha = hasAlpha;
                }

                babylonMesh.subMeshes = subMeshes.ToArray();

                // Buffers - Indices
                babylonMesh.indices = indices.ToArray();

                // ------------------------
                // ---- Morph targets -----
                // ------------------------

                // Retreive modifiers with morpher flag
                List <IIGameModifier> modifiers = new List <IIGameModifier>();
                for (int i = 0; i < meshNode.IGameObject.NumModifiers; i++)
                {
                    var modifier = meshNode.IGameObject.GetIGameModifier(i);
                    if (modifier.ModifierType == Autodesk.Max.IGameModifier.ModType.Morpher)
                    {
                        modifiers.Add(modifier);
                    }
                }

                // Cast modifiers to morphers
                List <IIGameMorpher> morphers = modifiers.ConvertAll(new Converter <IIGameModifier, IIGameMorpher>(modifier => modifier.AsGameMorpher()));

                var hasMorphTarget = false;
                morphers.ForEach(morpher =>
                {
                    if (morpher.NumberOfMorphTargets > 0)
                    {
                        hasMorphTarget = true;
                    }
                });

                if (hasMorphTarget)
                {
                    RaiseMessage("Export morph targets", 2);

                    // Morph Target Manager
                    var babylonMorphTargetManager = new BabylonMorphTargetManager();
                    babylonScene.MorphTargetManagersList.Add(babylonMorphTargetManager);
                    babylonMesh.morphTargetManagerId = babylonMorphTargetManager.id;

                    // Morph Targets
                    var babylonMorphTargets = new List <BabylonMorphTarget>();
                    // All morphers are considered identical
                    // Their targets are concatenated
                    morphers.ForEach(morpher =>
                    {
                        for (int i = 0; i < morpher.NumberOfMorphTargets; i++)
                        {
                            // Morph target
                            var maxMorphTarget = morpher.GetMorphTarget(i);

                            // Ensure target still exists (green color legend)
                            if (maxMorphTarget != null)
                            {
                                var babylonMorphTarget = new BabylonMorphTarget
                                {
                                    name = maxMorphTarget.Name
                                };
                                babylonMorphTargets.Add(babylonMorphTarget);

                                // TODO - Influence
                                babylonMorphTarget.influence = 0f;

                                // Target geometry
                                var targetVertices           = ExtractVertices(maxMorphTarget, optimizeVertices, faceIndexes);
                                babylonMorphTarget.positions = targetVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();
                                babylonMorphTarget.normals   = targetVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();

                                // Animations
                                var animations  = new List <BabylonAnimation>();
                                var morphWeight = morpher.GetMorphWeight(i);
                                ExportFloatGameController(morphWeight, "influence", animations);
                                if (animations.Count > 0)
                                {
                                    babylonMorphTarget.animations = animations.ToArray();
                                }
                            }
                        }
                    });

                    babylonMorphTargetManager.targets = babylonMorphTargets.ToArray();
                }
            }

            // Animations
            // Done last to avoid '0 vertex found' error (unkown cause)
            exportAnimation(babylonMesh, meshNode);

            babylonScene.MeshesList.Add(babylonMesh);

            return(babylonMesh);
        }
Exemplo n.º 17
0
        private IIGameNode GetLowestCommonAncestor(IIGameNode nodeA, IIGameNode nodeB, List <IIGameNode> nodeHierarchyA = null, List <IIGameNode> nodeHierarchyB = null)
        {
            if (nodeA == nodeB || nodeB == null)
            {
                return(nodeA);
            }
            if (nodeA == null)
            {
                return(nodeB);
            }

            if (nodeHierarchyA == null)
            {
                nodeHierarchyA = new List <IIGameNode>();
            }
            else
            {
                nodeHierarchyA.Clear();
            }
            if (nodeHierarchyB == null)
            {
                nodeHierarchyB = new List <IIGameNode>();
            }
            else
            {
                nodeHierarchyB.Clear();
            }

            nodeHierarchyA.Add(nodeA);
            nodeHierarchyB.Add(nodeB);

            int hierarchyAIndex = 0;
            int hierarchyBIndex = 0;

            // build hierarchies up to the root node
            while (true)
            {
                if (nodeHierarchyA[hierarchyAIndex].NodeParent != null)
                {
                    nodeHierarchyA.Add(nodeHierarchyA[hierarchyAIndex].NodeParent);
                    ++hierarchyAIndex;
                }

                if (nodeHierarchyB[hierarchyBIndex].NodeParent != null)
                {
                    nodeHierarchyB.Add(nodeHierarchyB[hierarchyBIndex].NodeParent);
                    ++hierarchyBIndex;
                }

                if (nodeHierarchyA[hierarchyAIndex].NodeParent == null && nodeHierarchyB[hierarchyBIndex].NodeParent == null)
                {
                    break;
                }
            }

            // check whether the nodes exist in the same hierarchy
            IIGameNode topA = nodeHierarchyA[hierarchyAIndex];
            IIGameNode topB = nodeHierarchyB[hierarchyBIndex];

            if (topA.MaxNode.Handle != topB.MaxNode.Handle)
            {
                return(null);
            }

            // traverse down the two hierarchies until they diverge
            IIGameNode lowestCommonAncestor = null;

            while (true)
            {
                --hierarchyAIndex;
                --hierarchyBIndex;

                // in the case that one is a child of the other, other will be invalid when one still has more nodes to go
                if (hierarchyAIndex == -1)
                {
                    lowestCommonAncestor = nodeHierarchyA[0];
                    break;
                }
                if (hierarchyBIndex == -1)
                {
                    lowestCommonAncestor = nodeHierarchyB[0];
                    break;
                }

                // if the current nodes differ, the parent was the last shared node
                if (nodeHierarchyA[hierarchyAIndex].MaxNode.Handle != nodeHierarchyB[hierarchyBIndex].MaxNode.Handle)
                {
                    lowestCommonAncestor = nodeHierarchyA[hierarchyAIndex + 1];
                    break;
                }
            }

            // fix up hierarchies to only contain nodes between the nodes we got (which have index 0) and the common ancestor we found
            int ancestorIndexA  = hierarchyAIndex + 1;
            int ancestorIndexB  = hierarchyBIndex + 1;
            int numNodesToKeepA = ancestorIndexA + 1;
            int numNodesToKeepB = ancestorIndexB + 1;

            if (nodeHierarchyA.Count > numNodesToKeepA)
            {
                nodeHierarchyA.RemoveRange(numNodesToKeepA, nodeHierarchyA.Count - numNodesToKeepA);
            }
            if (nodeHierarchyB.Count > numNodesToKeepB)
            {
                nodeHierarchyB.RemoveRange(numNodesToKeepB, nodeHierarchyB.Count - numNodesToKeepB);
            }

            return(lowestCommonAncestor);
        }
Exemplo n.º 18
0
 private bool IsLightExportable(IIGameNode lightNode)
 {
     return(IsNodeExportable(lightNode));
 }
Exemplo n.º 19
0
        private void ExportLight(IIGameScene scene, IIGameNode lightNode, BabylonScene babylonScene)
        {
            if (lightNode.MaxNode.GetBoolProperty("babylonjs_noexport"))
            {
                return;
            }

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

            RaiseMessage(lightNode.Name, 1);
            babylonLight.name = lightNode.Name;
            babylonLight.id = lightNode.MaxNode.GetGuid().ToString();
            if (lightNode.NodeParent != null)
            {
                babylonLight.parentId = GetParentID(lightNode.NodeParent, babylonScene, scene);
            }

            // 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)
                {
                    ExportShadowGenerator(lightNode.MaxNode, babylonScene);
                }
                else
                {
                    RaiseWarning("Shadows maps are only supported for directional and spot lights", 2);
                }
            }

            // Position
            var wm = lightNode.GetObjectTM(0);
            if (lightNode.NodeParent != null)
            {
                var parentWorld = lightNode.NodeParent.GetObjectTM(0);
                wm.MultiplyBy(parentWorld.Inverse);
            }
            var position = wm.Translation;
            babylonLight.position = new[] { position.X, position.Y, position.Z };

            // 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 = wm.ExtractMatrix3().VectorTransform(vDir).Normalize;
                babylonLight.direction = new[] { vDir.X, vDir.Y, vDir.Z };
            }

            var maxScene = Loader.Core.RootNode;

            // Exclusion
            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 (meshNode.CastShadows == 1)
                    {
                        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();
            }

            // 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, 1, Tools.Forever);
            }


            // Animations
            var animations = new List<BabylonAnimation>();

            ExportVector3Animation("position", animations, key =>
            {
                var mat = lightNode.GetObjectTM(key);
                if (lightNode.NodeParent != null)
                {
                    var parentWorld = lightNode.NodeParent.GetObjectTM(key);
                    mat.MultiplyBy(parentWorld.Inverse);
                }
                var pos = mat.Translation;
                return new[] { pos.X, pos.Y, pos.Z };
            });

            ExportVector3Animation("direction", animations, key =>
            {
                var wmLight = lightNode.GetObjectTM(key);
                if (lightNode.NodeParent != null)
                {
                    var parentWorld = lightNode.NodeParent.GetObjectTM(key);
                    wmLight.MultiplyBy(parentWorld.Inverse);
                }
                var positionLight = wmLight.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 = wmLight.ExtractMatrix3().VectorTransform(vDir).Normalize;
                    return new[] { vDir.X, vDir.Y, vDir.Z };
                }
            });

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

            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);
        }
Exemplo n.º 20
0
        private BabylonLight 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;
            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);
                }
                else
                {
                    RaiseWarning("Shadows maps are only supported for point, directional and spot lights", 2);
                }
            }

            // Position
            var wm = GetLocalTM(lightNode, 0);

            var position = wm.Translation;

            babylonLight.position = new[] { position.X, position.Y, position.Z };

            // 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 = wm.ExtractMatrix3().VectorTransform(vDir).Normalize;
                babylonLight.direction = new[] { vDir.X, vDir.Y, vDir.Z };
            }

            var maxScene = Loader.Core.RootNode;

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

            // 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);
            }


            // Animations
            var animations = new List <BabylonAnimation>();

            GeneratePositionAnimation(lightNode, animations);

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

                var positionLight = wmLight.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     = wmLight.ExtractMatrix3().VectorTransform(vDir).Normalize;
                    return(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(babylonLight);
        }
Exemplo n.º 21
0
        private void ExtractGeometry(BabylonAbstractMesh babylonAbstractMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, List <int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, IMatrix3 invertedWorldMatrix, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode, ref List <int> faceIndexes)
        {
            List <GlobalVertex>[] verticesAlreadyExported = null;

            if (optimizeVertices)
            {
                verticesAlreadyExported = new List <GlobalVertex> [unskinnedMesh.NumberOfVerts];
            }

            var indexStart = 0;

            // Whether or not to store order in which faces are exported
            // Storage is used when exporting Morph Targets geometry
            // To ensure face order is identical, especially with multimaterials involved
            bool storeFaceIndexes = faceIndexes == null;

            if (storeFaceIndexes)
            {
                faceIndexes = new List <int>();
            }
            int indexInFaceIndexesArray = 0;

            for (int i = 0; i < multiMatsCount; ++i)
            {
                int materialId     = meshNode.NodeMaterial?.GetMaterialID(i) ?? 0;
                var indexCount     = 0;
                var minVertexIndex = int.MaxValue;
                var maxVertexIndex = int.MinValue;
                var subMesh        = new BabylonSubMesh {
                    indexStart = indexStart, materialIndex = i
                };

                if (multiMatsCount == 1)
                {
                    for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j)
                    {
                        IFaceEx face = null;
                        if (storeFaceIndexes)
                        {
                            face = unskinnedMesh.GetFace(j);
                            // Store face index (j = face.MeshFaceIndex)
                            faceIndexes.Add(j);
                        }
                        else
                        {
                            face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]);
                        }
                        ExtractFace(meshNode, skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                    }
                }
                else
                {
                    ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId);
                    for (int j = 0; j < materialFaces.Count; ++j)
                    {
                        IFaceEx face = null;
                        if (storeFaceIndexes)
                        {
                            // Retreive face
#if MAX2017 || MAX2018 || MAX2019
                            face = materialFaces[j];
#else
                            face = materialFaces[new IntPtr(j)];
#endif

                            // Store face index
                            faceIndexes.Add(face.MeshFaceIndex);
                        }
                        else
                        {
                            face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]);
                        }
                        ExtractFace(meshNode, skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                    }
                }

                if (indexCount != 0)
                {
                    subMesh.indexCount    = indexCount;
                    subMesh.verticesStart = minVertexIndex;
                    subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1;

                    indexStart += indexCount;

                    subMeshes.Add(subMesh);
                }
            }
        }
Exemplo n.º 22
0
        private BabylonCamera ExportCamera(IIGameScene scene, IIGameNode cameraNode, BabylonScene babylonScene)
        {
            if (IsCameraExportable(cameraNode) == false)
            {
                return(null);
            }

            var gameCamera    = cameraNode.IGameObject.AsGameCamera();
            var maxCamera     = gameCamera.MaxObject as ICameraObject;
            var initialized   = gameCamera.InitializeData;
            var babylonCamera = new BabylonCamera();

            RaiseMessage(cameraNode.Name, 1);
            babylonCamera.name = cameraNode.Name;
            babylonCamera.id   = cameraNode.MaxNode.GetGuid().ToString();
            if (cameraNode.NodeParent != null)
            {
                babylonCamera.parentId = cameraNode.NodeParent.MaxNode.GetGuid().ToString();
            }

            // Export the custom attributes of this camera
            babylonCamera.metadata = ExportExtraAttributes(cameraNode, babylonScene);

            babylonCamera.fov = Tools.ConvertFov(maxCamera.GetFOV(0, Tools.Forever));

            if (maxCamera.ManualClip == 1)
            {
                babylonCamera.minZ = maxCamera.GetClipDist(0, 1, Tools.Forever);
                babylonCamera.maxZ = maxCamera.GetClipDist(0, 2, Tools.Forever);
            }
            else
            {
                babylonCamera.minZ = 0.1f;
                babylonCamera.maxZ = 10000.0f;
            }

            if (babylonCamera.minZ == 0.0f)
            {
                babylonCamera.minZ = 0.1f;
            }

            // Type
            babylonCamera.type = cameraNode.MaxNode.GetStringProperty("babylonjs_type", "FreeCamera");

            // Control
            babylonCamera.speed   = cameraNode.MaxNode.GetFloatProperty("babylonjs_speed", 1.0f);
            babylonCamera.inertia = cameraNode.MaxNode.GetFloatProperty("babylonjs_inertia", 0.9f);

            // Collisions
            babylonCamera.checkCollisions = cameraNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions");
            babylonCamera.applyGravity    = cameraNode.MaxNode.GetBoolProperty("babylonjs_applygravity");
            babylonCamera.ellipsoid       = cameraNode.MaxNode.GetVector3Property("babylonjs_ellipsoid");

            // Position / rotation
            exportTransform(babylonCamera, cameraNode);

            // Target
            var target = gameCamera.CameraTarget;

            if (target != null)
            {
                babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString();
            }

            // Animations
            var animations = new List <BabylonAnimation>();

            GeneratePositionAnimation(cameraNode, animations);

            if (target == null)
            {
                // Export rotation animation
                GenerateRotationAnimation(cameraNode, animations);
            }
            else
            {
                // 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(cameraNode, extraAnimations, true);
                babylonCamera.extraAnimations = extraAnimations;
            }

            ExportFloatAnimation("fov", animations, key => new[] { Tools.ConvertFov((gameCamera.MaxObject as ICameraObject).GetFOV(key, Tools.Forever)) });

            babylonCamera.animations = animations.ToArray();

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

            babylonScene.CamerasList.Add(babylonCamera);

            return(babylonCamera);
        }
Exemplo n.º 23
0
        int CreateGlobalVertex(IIGameNode meshNode, IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds)
        {
            var vertexIndex = (int)face.Vert[facePart];

            // Position can by retreived in world space or object space
            // Unfortunately, this value can't be retreived in local space
            var vertex = new GlobalVertex
            {
                BaseIndex = vertexIndex,
                Position  = mesh.GetVertex(vertexIndex, false),             // world space
                Normal    = mesh.GetNormal((int)face.Norm[facePart], false) // world space
            };

            if (exportParameters.exportTangents)
            {
                int     indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1);
                IPoint3 normal    = vertex.Normal.Normalize;
                IPoint3 tangent   = mesh.GetTangent(indexTangentBinormal, 1).Normalize;
                IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize;
                int     w         = GetW(normal, tangent, bitangent);
                vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w };
            }

            // Convert position and normal to local space
            vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position);

            vertex.Normal = invertedWorldMatrix.VectorTransform(vertex.Normal);
            // 1. scale normals with node scales
            var nodeScaling = BabylonVector3.FromArray(babylonAbstractMesh.scaling);

            vertex.Normal = vertex.Normal.Multiply(Loader.Global.Point3.Create(Math.Abs(nodeScaling.X), Math.Abs(nodeScaling.Y), Math.Abs(nodeScaling.Z)));

            // 2. scale normals with objectOffsetScales (unrotate by objectOffsetRot, then scale, then rotate again)
            // note: LH coordinate system => flip y and z
            var objOffsetScale          = Loader.Global.Point3.Create(meshNode.MaxNode.ObjOffsetScale.S);
            var scaleX                  = Math.Abs(objOffsetScale.X);
            var scaleY                  = Math.Abs(objOffsetScale.Y);
            var scaleZ                  = Math.Abs(objOffsetScale.Z);
            var objOffsetScaleFlipYZInv = Loader.Global.Point3.Create(1 / scaleX, 1 / scaleZ, 1 / scaleY);

            var objOffsetQuat = meshNode.MaxNode.ObjOffsetRot;
            var qFlippedYZ    = objOffsetQuat;
            var tmpSwap       = objOffsetQuat.Y;

            qFlippedYZ.Y = objOffsetQuat.Z;
            qFlippedYZ.Z = tmpSwap;

            var nUnrotated       = RotateVectorByQuaternion(vertex.Normal, qFlippedYZ);
            var nUnrotatedScaled = nUnrotated.Multiply(objOffsetScaleFlipYZInv);

            nUnrotatedScaled = nUnrotatedScaled.Normalize;
            var nRerotatedScaled = RotateVectorByQuaternion(nUnrotatedScaled, qFlippedYZ.Conjugate);

            vertex.Normal = nRerotatedScaled;

            if (hasUV)
            {
                var indices = new int[3];
                unsafe
                {
                    fixed(int *indicesPtr = indices)
                    {
                        mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr));
                    }
                }
                var texCoord = mesh.GetMapVertex(1, indices[facePart]);
                vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y);
            }

            if (hasUV2)
            {
                var indices = new int[3];
                unsafe
                {
                    fixed(int *indicesPtr = indices)
                    {
                        mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr));
                    }
                }
                var texCoord = mesh.GetMapVertex(2, indices[facePart]);
                vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y);
            }

            if (hasColor)
            {
                var   vertexColorIndex = (int)face.Color[facePart];
                var   vertexColor      = mesh.GetColorVertex(vertexColorIndex);
                float alpha            = 1;
                if (hasAlpha)
                {
                    var indices = new int[3];
                    unsafe
                    {
                        fixed(int *indicesPtr = indices)
                        {
                            mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr));
                        }
                    }
                    var color = mesh.GetMapVertex(-2, indices[facePart]);

                    alpha = color.X;
                }

                vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha };
            }

            if (skin != null)
            {
                float[] weight = new float[4] {
                    0, 0, 0, 0
                };
                int[] bone = new int[4] {
                    bonesCount, bonesCount, bonesCount, bonesCount
                };
                var nbBones = skin.GetNumberOfBones(vertexIndex);

                int currentVtxBone  = 0;
                int currentSkinBone = 0;

                // process skin bones until we have 4 bones for this vertex or we run out of skin bones
                for (currentSkinBone = 0; currentSkinBone < nbBones && currentVtxBone < 4; ++currentSkinBone)
                {
                    float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone);
                    if (boneWeight <= 0)
                    {
                        continue;
                    }

                    bone[currentVtxBone]   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID);
                    weight[currentVtxBone] = skin.GetWeight(vertexIndex, currentSkinBone);
                    ++currentVtxBone;
                }

                // if we didnt have any bones with a weight > 0
                if (currentVtxBone == 0)
                {
                    weight[0] = 1.0f;
                    bone[0]   = bonesCount;
                }

                vertex.Weights      = Loader.Global.Point4.Create(weight);
                vertex.BonesIndices = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0];

                if (currentVtxBone >= 4 && currentSkinBone < nbBones)
                {
                    weight = new float[4] {
                        0, 0, 0, 0
                    };
                    bone = new int[4] {
                        bonesCount, bonesCount, bonesCount, bonesCount
                    };

                    // process remaining skin bones until we have a total of 8 bones for this vertex or we run out of skin bones
                    for (; currentSkinBone < nbBones && currentVtxBone < 8; ++currentSkinBone)
                    {
                        float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone);
                        if (boneWeight <= 0)
                        {
                            continue;
                        }

                        if (isGltfExported)
                        {
                            RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". glTF only supports up to 4 bone influences per vertex.", 2);
                            break;
                        }

                        bone[currentVtxBone - 4]   = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID);
                        weight[currentVtxBone - 4] = skin.GetWeight(vertexIndex, currentSkinBone);
                        ++currentVtxBone;
                    }

                    // if we have any extra bone weights
                    if (currentVtxBone > 4)
                    {
                        vertex.WeightsExtra      = Loader.Global.Point4.Create(weight);
                        vertex.BonesIndicesExtra = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0];

                        if (currentSkinBone < nbBones)
                        {
                            // if we have more skin bones left, this means we have used up all our bones for this vertex
                            // check if any of the remaining bones has a weight > 0
                            for (; currentSkinBone < nbBones; ++currentSkinBone)
                            {
                                float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone);
                                if (boneWeight <= 0)
                                {
                                    continue;
                                }
                                RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". Babylon.js only supports up to 8 bone influences per vertex.", 2);
                                break;
                            }
                        }
                    }
                }
            }

            if (verticesAlreadyExported != null)
            {
                if (verticesAlreadyExported[vertexIndex] != null)
                {
                    var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex);

                    if (index > -1)
                    {
                        return(verticesAlreadyExported[vertexIndex][index].CurrentIndex);
                    }
                }
                else
                {
                    verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>();
                }

                vertex.CurrentIndex = vertices.Count;
                verticesAlreadyExported[vertexIndex].Add(vertex);
            }

            vertices.Add(vertex);

            return(vertices.Count - 1);
        }
Exemplo n.º 24
0
 private bool IsCameraExportable(IIGameNode cameraNode)
 {
     return(IsNodeExportable(cameraNode));
 }
Exemplo n.º 25
0
        private BabylonNode ExportMasterMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene)
        {
            var gameMesh = meshNode.IGameObject.AsGameMesh();

            try
            {
                bool initialized = gameMesh.InitializeData; // needed, the property is in fact a method initializing the exporter that has wrongly been auto
                                                            // translated into a property because it has no parameters
            }
            catch (Exception e)
            {
                RaiseWarning($"Mesh {meshNode.Name} failed to initialize. Mesh is exported as dummy.", 2);
                return(ExportDummy(scene, meshNode, babylonScene));
            }

            var babylonMesh = new BabylonMesh {
                name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString()
            };

            // Position / rotation / scaling / hierarchy
            exportNode(babylonMesh, meshNode, scene, babylonScene);

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

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

                var meshSound = new BabylonSound
                {
                    name            = filename,
                    autoplay        = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_autoplay", 1),
                    loop            = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_loop", 1),
                    volume          = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_volume", 1.0f),
                    playbackRate    = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_playbackrate", 1.0f),
                    connectedMeshId = babylonMesh.id,
                    isDirectional   = false,
                    spatialSound    = false,
                    distanceModel   = meshNode.MaxNode.GetStringProperty("babylonjs_sound_distancemodel", "linear"),
                    maxDistance     = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_maxdistance", 100f),
                    rolloffFactor   = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_rolloff", 1.0f),
                    refDistance     = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f),
                };

                var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional");

                if (isDirectional)
                {
                    meshSound.isDirectional  = true;
                    meshSound.coneInnerAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneinnerangle", 360f);
                    meshSound.coneOuterAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneouterangle", 360f);
                    meshSound.coneOuterGain  = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneoutergain", 1.0f);
                }

                babylonScene.SoundsList.Add(meshSound);

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

            // Misc.
#if MAX2017 || MAX2018 || MAX2019
            babylonMesh.isVisible      = meshNode.MaxNode.Renderable;
            babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows;
            babylonMesh.applyFog       = meshNode.MaxNode.ApplyAtmospherics;
#else
            babylonMesh.isVisible      = meshNode.MaxNode.Renderable == 1;
            babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1;
            babylonMesh.applyFog       = meshNode.MaxNode.ApplyAtmospherics == 1;
#endif
            babylonMesh.pickable                 = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable");
            babylonMesh.showBoundingBox          = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox");
            babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox");
            babylonMesh.alphaIndex               = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000);

            // Collisions
            babylonMesh.checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions");

            // Skin
            var        isSkinned          = gameMesh.IsObjectSkinned;
            var        skin               = gameMesh.IGameSkin;
            var        unskinnedMesh      = gameMesh;
            IGMatrix   skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true));
            List <int> boneIds            = null;
            int        maxNbBones         = 0;
            if (isSkinned && GetRelevantNodes(skin).Count > 0)  // if the mesh has a skin with at least one bone
            {
                var skinAlreadyStored = skins.Find(_skin => IsSkinEqualTo(_skin, skin));
                if (skinAlreadyStored == null)
                {
                    skins.Add(skin);
                    babylonMesh.skeletonId = skins.IndexOf(skin);
                }
                else
                {
                    babylonMesh.skeletonId = skins.IndexOf(skinAlreadyStored);
                }

                skin.GetInitSkinTM(skinInitPoseMatrix);
                boneIds = GetNodeIndices(skin);
            }
            else
            {
                skin = null;
            }

            // Mesh

            if (unskinnedMesh.IGameType == Autodesk.Max.IGameObject.ObjectTypes.Mesh && unskinnedMesh.MaxMesh != null)
            {
                if (unskinnedMesh.NumberOfFaces < 1)
                {
                    RaiseError($"Mesh {babylonMesh.name} has no face", 2);
                }

                if (unskinnedMesh.NumberOfVerts < 3)
                {
                    RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2);
                }

                if (unskinnedMesh.NumberOfVerts >= 65536)
                {
                    RaiseWarning($"Mesh {babylonMesh.name} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2);
                }

                if (skin != null)
                {
                    for (var vertexIndex = 0; vertexIndex < unskinnedMesh.NumberOfVerts; vertexIndex++)
                    {
                        maxNbBones = Math.Max(maxNbBones, skin.GetNumberOfBones(vertexIndex));
                    }
                }

                // Physics
                var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None");

                if (impostorText != "None")
                {
                    switch (impostorText)
                    {
                    case "Sphere":
                        babylonMesh.physicsImpostor = 1;
                        break;

                    case "Box":
                        babylonMesh.physicsImpostor = 2;
                        break;

                    case "Plane":
                        babylonMesh.physicsImpostor = 3;
                        break;

                    default:
                        babylonMesh.physicsImpostor = 0;
                        break;
                    }

                    babylonMesh.physicsMass        = meshNode.MaxNode.GetFloatProperty("babylonjs_mass");
                    babylonMesh.physicsFriction    = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f);
                    babylonMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f);
                }

                // Material
                var mtl            = meshNode.NodeMaterial;
                var multiMatsCount = 1;

                // The DirectXShader material is a passthrough to its render material.
                // The shell material is a passthrough to its baked material.
                while (mtl != null && (isShellMaterial(mtl) || isDirectXShaderMaterial(mtl)))
                {
                    if (isShellMaterial(mtl))
                    {
                        // Retrieve the baked material from the shell material.
                        mtl = GetBakedMaterialFromShellMaterial(mtl);
                    }
                    else // isDirectXShaderMaterial(mtl)
                    {
                        // Retrieve the render material from the directX shader
                        mtl = GetRenderMaterialFromDirectXShader(mtl);
                    }
                }

                if (mtl != null)
                {
                    IIGameMaterial unsupportedMaterial = isMaterialSupported(mtl);
                    if (unsupportedMaterial == null)
                    {
                        babylonMesh.materialId = mtl.MaxMaterial.GetGuid().ToString();

                        if (!referencedMaterials.Contains(mtl))
                        {
                            referencedMaterials.Add(mtl);
                        }

                        multiMatsCount = Math.Max(mtl.SubMaterialCount, 1);
                    }
                    else
                    {
                        if (mtl.SubMaterialCount == 0 || mtl == unsupportedMaterial)
                        {
                            RaiseWarning("Unsupported material type '" + unsupportedMaterial.MaterialClass + "'. Material is ignored.", 2);
                        }
                        else
                        {
                            RaiseWarning("Unsupported sub-material type '" + unsupportedMaterial.MaterialClass + "'. Material is ignored.", 2);
                        }
                    }
                }

                babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever);

                var  vertices        = new List <GlobalVertex>();
                var  indices         = new List <int>();
                var  mappingChannels = unskinnedMesh.ActiveMapChannelNum;
                bool hasUV           = false;
                bool hasUV2          = false;
                for (int i = 0; i < mappingChannels.Count; ++i)
                {
                #if MAX2017 || MAX2018 || MAX2019
                    var channelNum = mappingChannels[i];
                #else
                    var channelNum = mappingChannels[new IntPtr(i)];
                #endif
                    if (channelNum == 1)
                    {
                        hasUV = true;
                    }
                    else if (channelNum == 2)
                    {
                        hasUV2 = true;
                    }
                }
                var hasColor = unskinnedMesh.NumberOfColorVerts > 0;
                var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0;

                var optimizeVertices = meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices");

                var invertedWorldMatrix = GetInvertWorldTM(meshNode, 0);
                var offsetTM            = GetOffsetTM(meshNode, 0);

                // Compute normals
                var        subMeshes   = new List <BabylonSubMesh>();
                List <int> faceIndexes = null;
                ExtractGeometry(babylonMesh, vertices, indices, subMeshes, boneIds, skin, unskinnedMesh, invertedWorldMatrix, offsetTM, hasUV, hasUV2, hasColor, hasAlpha, optimizeVertices, multiMatsCount, meshNode, ref faceIndexes);

                if (vertices.Count >= 65536)
                {
                    RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2);

                    if (!optimizeVertices)
                    {
                        RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2);
                    }
                }

                RaiseMessage($"{vertices.Count} vertices, {indices.Count / 3} faces", 2);

                // Buffers
                babylonMesh.positions = vertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();
                babylonMesh.normals   = vertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();

                // Export tangents if option is checked and mesh has tangents
                if (exportParameters.exportTangents)
                {
                    babylonMesh.tangents = vertices.SelectMany(v => v.Tangent).ToArray();
                }

                if (hasUV)
                {
                    babylonMesh.uvs = vertices.SelectMany(v => new[] { v.UV.X, 1 - v.UV.Y }).ToArray();
                }
                if (hasUV2)
                {
                    babylonMesh.uvs2 = vertices.SelectMany(v => new[] { v.UV2.X, 1 - v.UV2.Y }).ToArray();
                }

                if (skin != null)
                {
                    babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray();
                    babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray();

                    babylonMesh.numBoneInfluencers = maxNbBones;
                    if (maxNbBones > 4)
                    {
                        babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra != null ? v.WeightsExtra.ToArray() : new[] { 0.0f, 0.0f, 0.0f, 0.0f }).ToArray();
                        babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray();
                    }
                }

                if (hasColor)
                {
                    babylonMesh.colors         = vertices.SelectMany(v => v.Color.ToArray()).ToArray();
                    babylonMesh.hasVertexAlpha = hasAlpha;
                }

                babylonMesh.subMeshes = subMeshes.ToArray();

                // Buffers - Indices
                babylonMesh.indices = indices.ToArray();

                // ------------------------
                // ---- Morph targets -----
                // ------------------------

                // Retreive modifiers with morpher flag
                List <IIGameModifier> modifiers = new List <IIGameModifier>();
                for (int i = 0; i < meshNode.IGameObject.NumModifiers; i++)
                {
                    var modifier = meshNode.IGameObject.GetIGameModifier(i);
                    if (modifier.ModifierType == Autodesk.Max.IGameModifier.ModType.Morpher)
                    {
                        modifiers.Add(modifier);
                    }
                }

                // Cast modifiers to morphers
                List <IIGameMorpher> morphers = modifiers.ConvertAll(new Converter <IIGameModifier, IIGameMorpher>(modifier => modifier.AsGameMorpher()));

                var hasMorphTarget = false;
                morphers.ForEach(morpher =>
                {
                    if (morpher.NumberOfMorphTargets > 0)
                    {
                        hasMorphTarget = true;
                    }
                });

                if (hasMorphTarget)
                {
                    RaiseMessage("Export morph targets", 2);

                    var rawScene = Loader.Core.RootNode;

                    // Morph Target Manager
                    var babylonMorphTargetManager = new BabylonMorphTargetManager();
                    babylonScene.MorphTargetManagersList.Add(babylonMorphTargetManager);
                    babylonMesh.morphTargetManagerId = babylonMorphTargetManager.id;

                    // Morph Targets
                    var babylonMorphTargets = new List <BabylonMorphTarget>();
                    // All morphers are considered identical
                    // Their targets are concatenated
                    morphers.ForEach(morpher =>
                    {
                        for (int i = 0; i < morpher.NumberOfMorphTargets; i++)
                        {
                            // Morph target
                            var maxMorphTarget = morpher.GetMorphTarget(i);

                            // Ensure target still exists (green color legend)
                            if (maxMorphTarget != null)
                            {
                                var babylonMorphTarget = new BabylonMorphTarget
                                {
                                    name = maxMorphTarget.Name
                                };
                                babylonMorphTargets.Add(babylonMorphTarget);

                                // TODO - Influence
                                babylonMorphTarget.influence = 0f;

                                // Target geometry
                                var targetVertices           = ExtractVertices(babylonMesh, maxMorphTarget, optimizeVertices, faceIndexes);
                                babylonMorphTarget.positions = targetVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();

                                if (rawScene.GetBoolProperty("babylonjs_export_Morph_Normals", 1))
                                {
                                    babylonMorphTarget.normals = targetVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();
                                }

                                // Tangent
                                if (exportParameters.exportTangents && rawScene.GetBoolProperty("babylonjs_export_Morph_Tangents"))
                                {
                                    babylonMorphTarget.tangents = targetVertices.SelectMany(v => v.Tangent).ToArray();
                                }

                                // Animations
                                var animations  = new List <BabylonAnimation>();
                                var morphWeight = morpher.GetMorphWeight(i);
                                ExportFloatGameController(morphWeight, "influence", animations);
                                if (animations.Count > 0)
                                {
                                    babylonMorphTarget.animations = animations.ToArray();
                                }
                            }
                        }
                    });

                    babylonMorphTargetManager.targets = babylonMorphTargets.ToArray();
                }
            }

            // World Modifiers
            ExportWorldModifiers(meshNode, babylonScene, babylonMesh);

            // Animations
            // Done last to avoid '0 vertex found' error (unkown cause)
            exportAnimation(babylonMesh, meshNode);

            babylonScene.MeshesList.Add(babylonMesh);

            return(babylonMesh);
        }
Exemplo n.º 26
0
        private List <IIGameNode> GetRevelantNodes(IIGameSkin skin)
        {
            int logRank = 2;

            // For optimization
            if (revelantNodesBySkin.ContainsKey(skin))
            {
                return(revelantNodesBySkin[skin]);
            }

            List <IIGameNode> revelantNodes = new List <IIGameNode>();
            List <IIGameNode> bones         = GetBones(skin);

            // for each bone of the skin, add their parents in the revelantNodes list.
            foreach (IIGameNode bone in bones)
            {
                if (!revelantNodes.Contains(bone))
                {
                    revelantNodes.Add(bone);
                    IIGameNode currentNode = bone.NodeParent;
                    while (currentNode != null)
                    {
                        if (!revelantNodes.Contains(currentNode))
                        {
                            revelantNodes.Add(currentNode);
                            currentNode = currentNode.NodeParent;
                        }
                        else // the node and its parents are already in the list
                        {
                            currentNode = null;
                        }
                    }
                }
            }

            // return an empty list if there is more than one root
            List <IIGameNode> rootNodes = revelantNodes.FindAll(node => node.NodeParent == null);    // should contains only one node

            if (rootNodes.Count > 1)
            {
                string rootNames = "";
                foreach (IIGameNode root in rootNodes)
                {
                    rootNames += $" {root.Name}";
                }
                RaiseError($"More than one root node for the skin: {rootNames}", logRank);
                RaiseError($"The skin cannot be exported", logRank);

                return(new List <IIGameNode>());
            }
            if (rootNodes.Count <= 0)
            {
                RaiseWarning("Skin has no bones.", logRank);
                return(new List <IIGameNode>());
            }

            // starting from the root, sort the nodes by depth first (add the children before the siblings)
            List <IIGameNode>  sorted   = new List <IIGameNode>();
            Stack <IIGameNode> siblings = new Stack <IIGameNode>();   // keep the siblings in a LIFO list to add them after the children

            siblings.Push(rootNodes[0]);
            while (siblings.Count > 0)
            {
                IIGameNode currentNode = siblings.Pop();

                if (revelantNodes.Contains(currentNode))    // The node is part of the skeleton
                {
                    // Add the current node to the sorted list
                    sorted.Add(currentNode);

                    // Add its children to the stack (in reverse order because it's a LIFO)
                    int childCount = currentNode.ChildCount;
                    for (int index = 0; index < childCount; index++)
                    {
                        siblings.Push(currentNode.GetNodeChild(childCount - 1 - index));
                    }
                }
            }

            revelantNodes = sorted;
            revelantNodesBySkin.Add(skin, revelantNodes);   // Stock the result for optimization

            return(revelantNodes);
        }
Exemplo n.º 27
0
        private BabylonNode ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene)
        {
            if (IsMeshExportable(meshNode) == false)
            {
                return(null);
            }

            RaiseMessage(meshNode.Name, 1);

            // Instances
            var tabs = Loader.Global.NodeTab.Create();

            Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs);
            if (tabs.Count > 1)
            {
                // For a mesh with instances, we distinguish between master and instance meshes:
                //      - a master mesh stores all the info of the mesh (transform, hierarchy, animations + vertices, indices, materials, bones...)
                //      - an instance mesh only stores the info of the node (transform, hierarchy, animations)

                // Check if this mesh has already been exported
                List <BabylonMesh> babylonMasterMeshes = new List <BabylonMesh>();
                var index = 0;
                while (index < tabs.Count)
                {
#if MAX2017 || MAX2018 || MAX2019
                    var tab = tabs[index];
#else
                    var tab = tabs[new IntPtr(index)];
#endif

                    babylonMasterMeshes.AddRange(babylonScene.MeshesList.FindAll(_babylonMesh => {
                        // Same id
                        return(_babylonMesh.id == tab.GetGuid().ToString() &&
                               // Mesh is not a dummy
                               _babylonMesh.isDummy == false);
                    }));

                    index++;
                }

                if (babylonMasterMeshes.Count > 0)
                {
                    // Mesh already exported
                    // Export this node as instance

                    // Check if we need to export this instance as an instance mesh.
                    // If there is already an exported mesh in the scene that shares this mesh's material, then export it as an instance.
                    BabylonMesh babylonMasterMesh = null;
                    foreach (var mesh in babylonMasterMeshes)
                    {
                        if (meshNode.NodeMaterial.MaxMaterial.GetGuid().ToString().Equals(mesh.materialId))
                        {
                            babylonMasterMesh = mesh;
                        }
                    }

                    if (babylonMasterMesh != null)
                    {
                        return(ExportInstanceMesh(scene, meshNode, babylonScene, babylonMasterMesh));
                    }

                    return(ExportMasterMesh(scene, meshNode, babylonScene));
                }
            }

            return(ExportMasterMesh(scene, meshNode, babylonScene));
        }
Exemplo n.º 28
0
        private BabylonNode ExportInstanceMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene, BabylonMesh babylonMasterMesh)
        {
            meshNode.MaxNode.MarkAsInstance();

            var babylonInstanceMesh = new BabylonAbstractMesh
            {
                id                       = meshNode.MaxNode.GetGuid().ToString(),
                name                     = meshNode.Name,
                pickable                 = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable"),
                checkCollisions          = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions"),
                showBoundingBox          = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox"),
                showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox"),
                alphaIndex               = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000)
            };

            // Export the custom attributes of this mesh
            babylonInstanceMesh.metadata = ExportExtraAttributes(meshNode, babylonScene);

            // Physics
            var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None");

            if (impostorText != "None")
            {
                switch (impostorText)
                {
                case "Sphere":
                    babylonInstanceMesh.physicsImpostor = 1;
                    break;

                case "Box":
                    babylonInstanceMesh.physicsImpostor = 2;
                    break;

                case "Plane":
                    babylonInstanceMesh.physicsImpostor = 3;
                    break;

                default:
                    babylonInstanceMesh.physicsImpostor = 0;
                    break;
                }

                babylonInstanceMesh.physicsMass        = meshNode.MaxNode.GetFloatProperty("babylonjs_mass");
                babylonInstanceMesh.physicsFriction    = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f);
                babylonInstanceMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f);
            }


            // Add instance to master mesh
            List <BabylonAbstractMesh> list = babylonMasterMesh.instances != null?babylonMasterMesh.instances.ToList() : new List <BabylonAbstractMesh>();

            list.Add(babylonInstanceMesh);
            babylonMasterMesh.instances = list.ToArray();

            // Export transform / hierarchy / animations
            exportNode(babylonInstanceMesh, meshNode, scene, babylonScene);

            // Animations
            exportAnimation(babylonInstanceMesh, meshNode);

            return(babylonInstanceMesh);
        }
Exemplo n.º 29
0
        public static void GenerateCoordinatesAnimations(IIGameNode meshNode, List<BabylonAnimation> animations)
        {
            if (meshNode.IGameControl.IsAnimated(IGameControlType.Pos) ||
                meshNode.IGameControl.IsAnimated(IGameControlType.PosX) ||
                meshNode.IGameControl.IsAnimated(IGameControlType.PosY) ||
                meshNode.IGameControl.IsAnimated(IGameControlType.PosZ))
            {
                ExportVector3Animation("position", animations, key =>
                {
                    var worldMatrix = meshNode.GetObjectTM(key);
                    if (meshNode.NodeParent != null)
                    {
                        var parentWorld = meshNode.NodeParent.GetObjectTM(key);
                        worldMatrix.MultiplyBy(parentWorld.Inverse);
                    }
                    var trans = worldMatrix.Translation;
                    return new[] { trans.X, trans.Y, trans.Z };
                });
            }

            if (meshNode.IGameControl.IsAnimated(IGameControlType.Rot) ||
                meshNode.IGameControl.IsAnimated(IGameControlType.EulerX) ||
                meshNode.IGameControl.IsAnimated(IGameControlType.EulerY) ||
                meshNode.IGameControl.IsAnimated(IGameControlType.EulerZ))
            {
                ExportQuaternionAnimation("rotationQuaternion", animations, key =>
                {
                    var worldMatrix = meshNode.GetObjectTM(key);
                    if (meshNode.NodeParent != null)
                    {
                        var parentWorld = meshNode.NodeParent.GetObjectTM(key);
                        worldMatrix.MultiplyBy(parentWorld.Inverse);
                    }


                    var rot = worldMatrix.Rotation;
                    return new[] { rot.X, rot.Y, rot.Z, -rot.W };
                });
            }

            if (meshNode.IGameControl.IsAnimated(IGameControlType.Scale))
            {
                ExportVector3Animation("scaling", animations, key =>
                {
                    var worldMatrix = meshNode.GetObjectTM(key);
                    if (meshNode.NodeParent != null)
                    {
                        var parentWorld = meshNode.NodeParent.GetObjectTM(key);
                        worldMatrix.MultiplyBy(parentWorld.Inverse);
                    }
                    var scale = worldMatrix.Scaling;

                    return new[] { scale.X, scale.Y, scale.Z };
                });
            }
        }
Exemplo n.º 30
0
        private void ExtractGeometry(BabylonAbstractMesh babylonAbstractMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, List <int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, IMatrix3 invertedWorldMatrix, IMatrix3 offsetTM, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode, ref List <int> faceIndexes)
        {
            Dictionary <GlobalVertex, List <GlobalVertex> > verticesAlreadyExported = null;

            if (optimizeVertices)
            {
                verticesAlreadyExported = new Dictionary <GlobalVertex, List <GlobalVertex> >();
            }

            var indexStart = 0;

            // Whether or not to store order in which faces are exported
            // Storage is used when exporting Morph Targets geometry
            // To ensure face order is identical, especially with multimaterials involved
            bool storeFaceIndexes = faceIndexes == null;

            if (storeFaceIndexes)
            {
                faceIndexes = new List <int>();
            }
            int indexInFaceIndexesArray = 0;

            for (int i = 0; i < multiMatsCount; ++i)
            {
                int materialId     = i;
                var indexCount     = 0;
                var minVertexIndex = int.MaxValue;
                var maxVertexIndex = int.MinValue;
                var subMesh        = new BabylonSubMesh {
                    indexStart = indexStart, materialIndex = i
                };

                if (multiMatsCount == 1)
                {
                    for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j)
                    {
                        IFaceEx face = null;
                        if (storeFaceIndexes)
                        {
                            face = unskinnedMesh.GetFace(j);
                            // Store face index (j = face.MeshFaceIndex)
                            faceIndexes.Add(j);
                        }
                        else
                        {
                            face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]);
                        }
                        ExtractFace(skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, offsetTM, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                    }
                }
                else
                {
                    if (i == 0 || isMaterialDoubleSided == false)
                    {
                        ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId);
                        for (int j = 0; j < materialFaces.Count; ++j)
                        {
                            IFaceEx face = null;
                            if (storeFaceIndexes)
                            {
                                // Retreive face
#if MAX2017 || MAX2018 || MAX2019 || MAX2020
                                face = materialFaces[j];
#else
                                face = materialFaces[new IntPtr(j)];
#endif

                                // Store face index
                                faceIndexes.Add(face.MeshFaceIndex);
                            }
                            else
                            {
                                face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]);
                            }
                            ExtractFace(skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, offsetTM, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                        }
                    }
                    else
                    {
                        // It's a double sided material
                        // The back faces are created at runtime

                        // WARNING - Nested multimaterial and double sided material are not supported

                        minVertexIndex = vertices.Count;
                        maxVertexIndex = vertices.Count * 2 - 1;

                        // Vertices
                        int nbVertices = vertices.Count;
                        for (int index = 0; index < nbVertices; index++)
                        {
                            GlobalVertex vertexOrg = vertices[index];

                            // Duplicate vertex
                            GlobalVertex vertexNew = new GlobalVertex(vertexOrg);

                            // Inverse back vertices normal
                            vertexNew.Normal  = vertexNew.Normal.MultiplyBy(-1);
                            vertexNew.Tangent = vertexNew.Tangent.MultiplyBy(-1);

                            vertices.Add(vertexNew);
                        }

                        // Faces
                        int nbIndices = indices.Count;
                        for (int index = 0; index < nbIndices; index += 3)
                        {
                            // Duplicate and flip faces
                            indices.Add(indices[index + 2] + nbIndices);
                            indices.Add(indices[index + 1] + nbIndices);
                            indices.Add(indices[index] + nbIndices);

                            indexCount += 3;
                        }
                    }
                }

                if (indexCount != 0)
                {
                    subMesh.indexCount    = indexCount;
                    subMesh.verticesStart = minVertexIndex;
                    subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1;

                    indexStart += indexCount;

                    subMeshes.Add(subMesh);
                }
            }
        }
Exemplo n.º 31
0
        private void ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene)
        {
            if (meshNode.MaxNode.IsInstance())
            {
                return;
            }

            if (meshNode.MaxNode.GetBoolProperty("babylonjs_noexport"))
            {
                return;
            }

            if (!ExportHiddenObjects && meshNode.MaxNode.IsHidden(NodeHideFlags.None, false))
            {
                return;
            }

            var gameMesh = meshNode.IGameObject.AsGameMesh();
            bool initialized = gameMesh.InitializeData; //needed, the property is in fact a method initializing the exporter that has wrongly been auto 
            // translated into a property because it has no parameters

            var babylonMesh = new BabylonMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() };

            if (meshNode.NodeParent != null)
            {
                babylonMesh.parentId = GetParentID(meshNode.NodeParent, babylonScene, scene);
            }

            // Sounds
            var soundName = meshNode.MaxNode.GetStringProperty("babylonjs_sound_filename", "");
            if (!string.IsNullOrEmpty(soundName))
            {
                var filename = Path.GetFileName(soundName);

                var meshSound = new BabylonSound
                {
                    name = filename,
                    autoplay = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_autoplay", 1),
                    loop = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_loop", 1),
                    volume = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_volume", 1.0f),
                    playbackRate = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_playbackrate", 1.0f),
                    connectedMeshId = babylonMesh.id,
                    isDirectional = false,
                    spatialSound = false,
                    distanceModel = meshNode.MaxNode.GetStringProperty("babylonjs_sound_distancemodel", "linear"),
                    maxDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_maxdistance", 100f),
                    rolloffFactor = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_rolloff", 1.0f),
                    refDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f),
                };

                var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional", 0);
                
                if (isDirectional)
                {
                    meshSound.isDirectional = true;
                    meshSound.coneInnerAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneinnerangle", 360f);
                    meshSound.coneOuterAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneouterangle", 360f);
                    meshSound.coneOuterGain = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneoutergain", 1.0f);
                }

                babylonScene.SoundsList.Add(meshSound);

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

            // Misc.
            babylonMesh.isVisible = meshNode.MaxNode.Renderable == 1;
            babylonMesh.pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable");
            babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1;
            babylonMesh.showBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox");
            babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox");
            babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1;
            babylonMesh.alphaIndex = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000);

            // Actions
            babylonMesh.actions = ExportNodeAction(meshNode);

            // Collisions
            babylonMesh.checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions");

            var isSkinned = gameMesh.IsObjectSkinned;
            var skin = gameMesh.IGameSkin;
            var unskinnedMesh = gameMesh;
            IGMatrix skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true));
            List<int> boneIds = null;
            if (isSkinned)
            {
                bonesCount = skin.TotalSkinBoneCount;
                skins.Add(skin);

                skinnedNodes.Add(meshNode);
                babylonMesh.skeletonId = skins.IndexOf(skin);
                skin.GetInitSkinTM(skinInitPoseMatrix);
                boneIds = SortBones(skin);
                skinSortedBones[skin] = boneIds;
            }

            // Position / rotation / scaling
            var localTM = meshNode.GetObjectTM(0);
            if (meshNode.NodeParent != null)
            {
                var parentWorld = meshNode.NodeParent.GetObjectTM(0);
                localTM.MultiplyBy(parentWorld.Inverse);
            }

            var meshTrans = localTM.Translation;
            var meshRotation = localTM.Rotation;
            var meshScale = localTM.Scaling;
            var exportQuaternions = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportquaternions");

            babylonMesh.position = new[] { meshTrans.X, meshTrans.Y, meshTrans.Z };

            if (exportQuaternions)
            {
                babylonMesh.rotationQuaternion = new[] { meshRotation.X, meshRotation.Y, meshRotation.Z, -meshRotation.W };
            }
            else
            {
                RotationToEulerAngles(babylonMesh, meshRotation);
            }

            babylonMesh.scaling = new[] { meshScale.X, meshScale.Y, meshScale.Z };

            // Mesh
            RaiseMessage(meshNode.Name, 1);

            if (unskinnedMesh.IGameType == Autodesk.Max.IGameObject.ObjectTypes.Mesh && unskinnedMesh.MaxMesh != null)
            {
                if (unskinnedMesh.NumberOfFaces < 1)
                {
                    RaiseError(string.Format("Mesh {0} has no face", babylonMesh.name), 2);
                }

                if (unskinnedMesh.NumberOfVerts < 3)
                {
                    RaiseError(string.Format("Mesh {0} has not enough vertices", babylonMesh.name), 2);
                }

                if (unskinnedMesh.NumberOfVerts >= 65536)
                {
                    RaiseWarning(string.Format("Mesh {0} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", babylonMesh.name), 2);
                }

                // Physics
                var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None");

                if (impostorText != "None")
                {
                    switch (impostorText)
                    {
                        case "Sphere":
                            babylonMesh.physicsImpostor = 1;
                            break;
                        case "Box":
                            babylonMesh.physicsImpostor = 2;
                            break;
                        case "Plane":
                            babylonMesh.physicsImpostor = 3;
                            break;
                        default:
                            babylonMesh.physicsImpostor = 0;
                            break;
                    }

                    babylonMesh.physicsMass = meshNode.MaxNode.GetFloatProperty("babylonjs_mass");
                    babylonMesh.physicsFriction = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f);
                    babylonMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f);
                }

                // Material
                var mtl = meshNode.NodeMaterial;
                var multiMatsCount = 1;

                if (mtl != null)
                {
                    babylonMesh.materialId = mtl.MaxMaterial.GetGuid().ToString();

                    if (!referencedMaterials.Contains(mtl))
                    {
                        referencedMaterials.Add(mtl);
                    }

                    multiMatsCount = Math.Max(mtl.SubMaterialCount, 1);
                }

                babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever);

                var vertices = new List<GlobalVertex>();
                var indices = new List<int>();
                var mappingChannels = unskinnedMesh.ActiveMapChannelNum;
                bool hasUV = false;
                bool hasUV2 = false;
                for (int i = 0; i < mappingChannels.Count; ++i)
                {
                    var indexer = new IntPtr(i);
                    var channelNum = mappingChannels[indexer];
                    if (channelNum == 1)
                    {
                        hasUV = true;
                    }
                    else if (channelNum == 2)
                    {
                        hasUV2 = true;
                    }
                }
                var hasColor = unskinnedMesh.NumberOfColorVerts > 0;
                var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0;

                var optimizeVertices = meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices");

                // Compute normals
                List<GlobalVertex>[] verticesAlreadyExported = null;

                if (optimizeVertices)
                {
                    verticesAlreadyExported = new List<GlobalVertex>[unskinnedMesh.NumberOfVerts];
                }

                var subMeshes = new List<BabylonSubMesh>();
                var indexStart = 0;


                for (int i = 0; i < multiMatsCount; ++i)
                {
                    int materialId = meshNode.NodeMaterial == null ? 0 : meshNode.NodeMaterial.GetMaterialID(i);
                    var indexCount = 0;
                    var minVertexIndex = int.MaxValue;
                    var maxVertexIndex = int.MinValue;
                    var subMesh = new BabylonSubMesh { indexStart = indexStart, materialIndex = i };

                    if (multiMatsCount == 1)
                    {
                        for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j)
                        {
                            var face = unskinnedMesh.GetFace(j);
                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                        }
                    }
                    else
                    {
                        ITab<IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId);
                        for (int j = 0; j < materialFaces.Count; ++j)
                        {
                            var faceIndexer = new IntPtr(j);
                            var face = materialFaces[faceIndexer];

                            Marshal.FreeHGlobal(faceIndexer);
                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                        }
                    }

                    if (indexCount != 0)
                    {

                        subMesh.indexCount = indexCount;
                        subMesh.verticesStart = minVertexIndex;
                        subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1;

                        indexStart += indexCount;

                        subMeshes.Add(subMesh);
                    }
                }

                if (vertices.Count >= 65536)
                {
                    RaiseWarning(string.Format("Mesh {0} has {1} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", babylonMesh.name, vertices.Count), 2);

                    if (!optimizeVertices)
                    {
                        RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2);
                    }
                }

                RaiseMessage(string.Format("{0} vertices, {1} faces", vertices.Count, indices.Count / 3), 2);

                // Buffers
                babylonMesh.positions = vertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();
                babylonMesh.normals = vertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();
                if (hasUV)
                {
                    babylonMesh.uvs = vertices.SelectMany(v => new[] { v.UV.X, 1 - v.UV.Y }).ToArray();
                }
                if (hasUV2)
                {
                    babylonMesh.uvs2 = vertices.SelectMany(v => new[] { v.UV2.X, 1 - v.UV2.Y }).ToArray();
                }

                if (skin != null)
                {
                    babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray();
                    babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray();
                }

                if (hasColor)
                {
                    babylonMesh.colors = vertices.SelectMany(v => v.Color.ToArray()).ToArray();
                    babylonMesh.hasVertexAlpha = hasAlpha;
                }

                babylonMesh.subMeshes = subMeshes.ToArray();

                // Buffers - Indices
                babylonMesh.indices = indices.ToArray();

            }

            // Instances
            var tabs = Loader.Global.NodeTab.Create();

            Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs);
            var instances = new List<BabylonAbstractMesh>();

            for (var index = 0; index < tabs.Count; index++)
            {
                var indexer = new IntPtr(index);
                var tab = tabs[indexer];

                Marshal.FreeHGlobal(indexer);

                if (meshNode.MaxNode.GetGuid() == tab.GetGuid())
                {
                    continue;
                }
                var instanceGameNode = scene.GetIGameNode(tab);
                if (instanceGameNode == null)
                {
                    continue;
                }
                tab.MarkAsInstance();

                var instance = new BabylonAbstractMesh { name = tab.Name };
                {
                    var instanceLocalTM = instanceGameNode.GetObjectTM(0);

                    var instanceTrans = instanceLocalTM.Translation;
                    var instanceRotation = instanceLocalTM.Rotation;
                    var instanceScale = instanceLocalTM.Scaling;

                    instance.position = new[] { instanceTrans.X, instanceTrans.Y, instanceTrans.Z };

                    if (exportQuaternions)
                    {
                        instance.rotationQuaternion = new[] { instanceRotation.X, instanceRotation.Y, instanceRotation.Z, -instanceRotation.W };
                    }
                    else
                    {
                        RotationToEulerAngles(instance, instanceRotation);
                    }

                    instance.scaling = new[] { instanceScale.X, instanceScale.Y, instanceScale.Z };
                }
                var instanceAnimations = new List<BabylonAnimation>();
                GenerateCoordinatesAnimations(meshNode, instanceAnimations);
                instance.animations = instanceAnimations.ToArray();

                instances.Add(instance);
            }

            babylonMesh.instances = instances.ToArray();

            // Animations
            var animations = new List<BabylonAnimation>();

            GenerateCoordinatesAnimations(meshNode, animations);

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

            babylonMesh.animations = animations.ToArray();

            if (meshNode.MaxNode.GetBoolProperty("babylonjs_autoanimate", 1))
            {
                babylonMesh.autoAnimate = true;
                babylonMesh.autoAnimateFrom = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from");
                babylonMesh.autoAnimateTo = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to", 100);
                babylonMesh.autoAnimateLoop = meshNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop", 1);
            }

            babylonScene.MeshesList.Add(babylonMesh);
        }
Exemplo n.º 32
0
        private BabylonCamera ExportCamera(IIGameScene scene, IIGameNode cameraNode, BabylonScene babylonScene)
        {
            if (IsCameraExportable(cameraNode) == false)
            {
                return(null);
            }

            var gameCamera    = cameraNode.IGameObject.AsGameCamera();
            var maxCamera     = gameCamera.MaxObject as ICameraObject;
            var initialized   = gameCamera.InitializeData;
            var babylonCamera = new BabylonCamera();

            RaiseMessage(cameraNode.Name, 1);
            babylonCamera.name = cameraNode.Name;
            babylonCamera.id   = cameraNode.MaxNode.GetGuid().ToString();
            if (cameraNode.NodeParent != null)
            {
                babylonCamera.parentId = cameraNode.NodeParent.MaxNode.GetGuid().ToString();
            }

            babylonCamera.fov = Tools.ConvertFov(maxCamera.GetFOV(0, Tools.Forever));

            if (maxCamera.ManualClip == 1)
            {
                babylonCamera.minZ = maxCamera.GetClipDist(0, 1, Tools.Forever);
                babylonCamera.maxZ = maxCamera.GetClipDist(0, 2, Tools.Forever);
            }
            else
            {
                babylonCamera.minZ = 0.1f;
                babylonCamera.maxZ = 10000.0f;
            }

            if (babylonCamera.minZ == 0.0f)
            {
                babylonCamera.minZ = 0.1f;
            }

            // Type
            babylonCamera.type = cameraNode.MaxNode.GetStringProperty("babylonjs_type", "FreeCamera");

            // Control
            babylonCamera.speed   = cameraNode.MaxNode.GetFloatProperty("babylonjs_speed", 1.0f);
            babylonCamera.inertia = cameraNode.MaxNode.GetFloatProperty("babylonjs_inertia", 0.9f);

            // Collisions
            babylonCamera.checkCollisions = cameraNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions");
            babylonCamera.applyGravity    = cameraNode.MaxNode.GetBoolProperty("babylonjs_applygravity");
            babylonCamera.ellipsoid       = cameraNode.MaxNode.GetVector3Property("babylonjs_ellipsoid");

            // Position / rotation
            var localTM = cameraNode.GetObjectTM(0);

            if (cameraNode.NodeParent != null)
            {
                var parentWorld = cameraNode.NodeParent.GetObjectTM(0);
                localTM.MultiplyBy(parentWorld.Inverse);
            }

            var position = localTM.Translation;
            var rotation = localTM.Rotation;

            babylonCamera.position = new[] { position.X, position.Y, position.Z };

            var rotationQuaternion = new BabylonQuaternion {
                X = rotation.X, Y = rotation.Y, Z = rotation.Z, W = -rotation.W
            };

            if (ExportQuaternionsInsteadOfEulers)
            {
                babylonCamera.rotationQuaternion = rotationQuaternion.ToArray();
            }
            else
            {
                babylonCamera.rotation = rotationQuaternion.toEulerAngles().ToArray();
            }

            // Target
            var target = gameCamera.CameraTarget;

            if (target != null)
            {
                babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString();
            }
            else
            {
                var dir = localTM.GetRow(3);
                babylonCamera.target = new [] { position.X - dir.X, position.Y - dir.Y, position.Z - dir.Z };
            }

            // Animations
            var animations = new List <BabylonAnimation>();

            ExportVector3Animation("position", animations, key =>
            {
                var tm = cameraNode.GetLocalTM(key);
                if (cameraNode.NodeParent != null)
                {
                    var parentWorld = cameraNode.NodeParent.GetObjectTM(key);
                    tm.MultiplyBy(parentWorld.Inverse);
                }
                var translation = tm.Translation;
                return(new [] { translation.X, translation.Y, translation.Z });
            });

            if (gameCamera.CameraTarget == null)
            {
                ExportVector3Animation("target", animations, key =>
                {
                    var tm = cameraNode.GetLocalTM(key);
                    if (cameraNode.NodeParent != null)
                    {
                        var parentWorld = cameraNode.NodeParent.GetObjectTM(key);
                        tm.MultiplyBy(parentWorld.Inverse);
                    }
                    var translation = tm.Translation;
                    var dir         = tm.GetRow(3);
                    return(new float[] { translation.X - dir.X, translation.Y - dir.Y, translation.Z - dir.Z });
                });
            }

            ExportFloatAnimation("fov", animations, key => new[] { Tools.ConvertFov((gameCamera.MaxObject as ICameraObject).GetFOV(key, Tools.Forever)) });

            babylonCamera.animations = animations.ToArray();

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

            babylonScene.CamerasList.Add(babylonCamera);

            return(babylonCamera);
        }