示例#1
0
        /// <summary>
        /// Export the bones and their animation for the given skin
        /// </summary>
        /// <param name="skin">The skin to export</param>
        /// <returns></returns>
        private BabylonBone[] ExportBones(IIGameSkin skin)
        {
            List <BabylonBone> bones         = new List <BabylonBone>();
            List <int>         nodeIndices   = GetNodeIndices(skin);
            List <IIGameNode>  revelantNodes = GetSkinnedBones(skin);

            foreach (IIGameNode node in revelantNodes)
            {
                int parentIndex = (node.NodeParent == null) ? -1 : nodeIndices.IndexOf(node.NodeParent.NodeID);

                string boneId = node.MaxNode.GetGuid().ToString();
                // create the bone
                BabylonBone bone = new BabylonBone()
                {
                    id              = (isGltfExported)?boneId:boneId + "-bone",// the suffix "-bone" is added in babylon export format to assure the uniqueness of IDs
                    parentNodeId    = (parentIndex != -1)?node.NodeParent.MaxNode.GetGuid().ToString():null,
                    name            = node.Name,
                    index           = nodeIndices.IndexOf(node.NodeID),
                    parentBoneIndex = parentIndex,
                    matrix          = (parentIndex == -1)?node.GetWorldTM(0).ToArray():node.GetLocalTM(0).ToArray()
                };

                // Apply unit conversion factor to meter
                // Affect translation only
                bone.matrix[12] *= scaleFactorToMeters;
                bone.matrix[13] *= scaleFactorToMeters;
                bone.matrix[14] *= scaleFactorToMeters;

                if (exportParameters.exportAnimations)
                {
                    // export its animation
                    var babylonAnimation = ExportMatrixAnimation("_matrix", key =>
                    {
                        IGMatrix mat = node.GetLocalTM(key);

                        float[] matrix = mat.ToArray();

                        // Apply unit conversion factor to meter
                        // Affect translation only
                        matrix[12] *= scaleFactorToMeters;
                        matrix[13] *= scaleFactorToMeters;
                        matrix[14] *= scaleFactorToMeters;

                        return(matrix);
                    },
                                                                 false); // Do not remove linear animation keys for bones

                    if (babylonAnimation != null)
                    {
                        babylonAnimation.name = node.Name + "Animation"; // override default animation name
                        bone.animation        = babylonAnimation;
                    }
                }

                bones.Add(bone);
            }

            return(bones.ToArray());
        }
示例#2
0
        /// <summary>
        /// Export the bones and their animation for the given skin
        /// </summary>
        /// <param name="skin">The skin to export</param>
        /// <returns></returns>
        private BabylonBone[] ExportBones(IIGameSkin skin, IIGameNode meshNode)
        {
            List <BabylonBone> bones         = new List <BabylonBone>();
            List <int>         nodeIndices   = GetNodeIndices(skin, meshNode);
            List <IIGameNode>  revelantNodes = GetSkinnedBones(skin, meshNode);

            foreach (IIGameNode node in revelantNodes)
            {
                int parentIndex = (node.NodeParent == null) ? -1 : nodeIndices.IndexOf(node.NodeParent.NodeID);

                string boneId            = node.MaxNode.GetGuid().ToString();
                string animationTargetID = node.MaxNode.GetUniqueID();
                // create the bone
                BabylonBone bone = new BabylonBone()
                {
                    id = (isGltfExported)?boneId:boneId + "-bone",// the suffix "-bone" is added in babylon export format to assure the uniqueness of IDs
                    AnimationTargetId = animationTargetID,
                    parentNodeId      = (parentIndex != -1)?node.NodeParent.MaxNode.GetGuid().ToString():null,
                    name            = node.Name,
                    index           = nodeIndices.IndexOf(node.NodeID),
                    parentBoneIndex = parentIndex,
                    matrix          = (parentIndex == -1)?node.GetWorldTM(0).ToArray():node.GetLocalTM(0).ToArray()
                };

                // export its animation
                var babylonAnimation = ExportMatrixAnimation("_matrix", key =>
                {
                    var objectTM   = node.GetObjectTM(key);
                    var parentNode = node.NodeParent;
                    IGMatrix mat;
                    if (parentNode == null || bone.parentBoneIndex == -1)
                    {
                        mat = objectTM;
                    }
                    else
                    {
                        mat = node.GetLocalTM(key);
                    }
                    return(mat.ToArray());
                },
                                                             false, true, node.Name); // Do not remove linear animation keys for bones; Always optimize animations

                if (babylonAnimation != null)
                {
                    babylonAnimation.name = node.Name + "Animation"; // override default animation name
                    bone.animation        = babylonAnimation;
                }

                bones.Add(bone);
            }

            return(bones.ToArray());
        }
        List<int> SortBones(IIGameSkin skin)
        {
            var boneIds = new List<int>();
            var boneIndex = new Dictionary<int, IIGameNode>();
            for (var index = 0; index < skin.TotalSkinBoneCount; index++)
            {
                var bone = skin.GetIGameBone(index, false);
                if (bone == null)
                {
                    // non bone in skeletton
                    boneIds.Add(-2);

                }
                else
                {
                    boneIds.Add(bone.NodeID);
                    boneIndex[bone.NodeID] = bone;
                }
            }
            while (true)
            {
                bool foundMisMatch = false;
                for (int i = 0; i < boneIds.Count; ++i)
                {
                    var id = boneIds[i];
                    if (id == -2)
                    {
                        continue;
                    }
                    var parent = boneIndex[id].NodeParent;
                    if (parent != null)
                    {
                        var parentId = parent.NodeID;
                        if (boneIds.IndexOf(parentId) > i)
                        {
                            boneIds.RemoveAt(i);
                            boneIds.Insert(boneIds.IndexOf(parentId) + 1, id);
                            foundMisMatch = true;
                            break;
                        }
                    }
                }
                if (!foundMisMatch)
                {
                    break;
                }
            }
            return boneIds;

        }
        int CreateGlobalVertex(IIGameMesh mesh, 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];

            var vertex = new GlobalVertex
            {
                BaseIndex = vertexIndex,
                Position = mesh.GetVertex(vertexIndex, true),
                Normal = mesh.GetNormal((int)face.Norm[facePart], true)
            };

            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;
                int bone0 = bonesCount;
                int bone1 = bonesCount;
                int bone2 = bonesCount;
                int bone3 = bonesCount;
                int 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);
                }

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

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

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

            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;
        }
        private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, List<GlobalVertex> vertices, List<int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List<int> boneIds)
        {
            var a = CreateGlobalVertex(unskinnedMesh, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
            var b = CreateGlobalVertex(unskinnedMesh, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
            var c = CreateGlobalVertex(unskinnedMesh, 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();
        }
        private void ExportSkin(IIGameSkin skin, BabylonScene babylonScene)
        {
            var babylonSkeleton = new BabylonSkeleton { id = skins.IndexOf(skin) };
            babylonSkeleton.name = "skeleton #" + babylonSkeleton.id;

            RaiseMessage(babylonSkeleton.name, 1);

            var skinIndex = skins.IndexOf(skin);

            var bones = new List<BabylonBone>();
            var gameBones = new List<IIGameNode>();
            var boneIds = new List<int>();
            var bindPoseInfos = new List<BonePoseInfo>();
            for(int i = 0; i < skin.TotalSkinBoneCount; ++i)
            {
                bones.Add(null);
                gameBones.Add(null);
                boneIds.Add(-1);
                bindPoseInfos.Add(null);
            }
            for (var index = 0; index < skin.TotalSkinBoneCount; index++)
            {
                var gameBone = skin.GetIGameBone(index, false);

                var sortedIndex = skinSortedBones[skin].IndexOf(gameBone.NodeID);

                gameBones[sortedIndex] = (gameBone);
                boneIds[sortedIndex] =(gameBone.NodeID);
                bones[sortedIndex]=(new BabylonBone { index = sortedIndex, name = gameBone.Name });

                var boneInitMatrix = gameBone.GetObjectTM(0);
                bindPoseInfos[sortedIndex] = (new BonePoseInfo { AbsoluteTransform = boneInitMatrix });
            }

            // fix hierarchy an generate animation keys
            var exportNonOptimizedAnimations = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportnonoptimizedanimations");

            for (var index = 0; index < skin.TotalSkinBoneCount; index++)
            {
                var gameBone = gameBones[index];
                var parent = gameBone.NodeParent;
                var babBone = bones[index];
                if (parent != null)
                {
                    babBone.parentBoneIndex = boneIds.IndexOf(parent.NodeID);
                }

                if (babBone.parentBoneIndex == -1)
                {
                    bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform;
                }
                else
                {
                    var parentBindPoseInfos = bindPoseInfos[babBone.parentBoneIndex];
                    bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform.Multiply(parentBindPoseInfos.AbsoluteTransform.Inverse);
                }

                babBone.matrix = bindPoseInfos[index].LocalTransform.ToArray();

                var babylonAnimation = new BabylonAnimation
                {
                    name = gameBone.Name + "Animation",
                    property = "_matrix",
                    dataType = (int)BabylonAnimation.DataType.Matrix,
                    loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle,
                    framePerSecond = Loader.Global.FrameRate
                };

                var start = Loader.Core.AnimRange.Start;
                var end = Loader.Core.AnimRange.End;

                float[] previous = null;
                var keys = new List<BabylonAnimationKey>();
                for (var key = start; key <= end; key += Ticks)
                {
                    var objectTM = gameBone.GetObjectTM(key);
                    var parentNode = gameBone.NodeParent;
                    IGMatrix mat;
                    if (parentNode == null || babBone.parentBoneIndex == -1)
                    {
                        mat = objectTM;
                    }
                    else
                    {
                        mat = objectTM.Multiply(parentNode.GetObjectTM(key).Inverse);
                    }

                    var current = mat.ToArray();
                    if (key == start || key == end || exportNonOptimizedAnimations || !(previous.IsEqualTo(current)))
                    {
                        keys.Add(new BabylonAnimationKey
                        {
                            frame = key / Ticks,
                            values = current
                        });
                    }

                    previous = current;
                }

                babylonAnimation.keys = keys.ToArray();
                babBone.animation = babylonAnimation;
            }

            babylonSkeleton.needInitialSkinMatrix = true;
            babylonSkeleton.bones = bones.ToArray();

            babylonScene.SkeletonsList.Add(babylonSkeleton);
        }
        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);
        }
示例#8
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);
                }
            }
        }
        private void ExportSkin(IIGameSkin skin, BabylonScene babylonScene)
        {
            var babylonSkeleton = new BabylonSkeleton {
                id = skins.IndexOf(skin)
            };

            babylonSkeleton.name = "skeleton #" + babylonSkeleton.id;

            RaiseMessage(babylonSkeleton.name, 1);

            var skinIndex = skins.IndexOf(skin);

            var bones         = new List <BabylonBone>();
            var gameBones     = new List <IIGameNode>();
            var boneIds       = new List <int>();
            var bindPoseInfos = new List <BonePoseInfo>();

            for (int i = 0; i < skin.TotalSkinBoneCount; ++i)
            {
                bones.Add(null);
                gameBones.Add(null);
                boneIds.Add(-1);
                bindPoseInfos.Add(null);
            }
            for (var index = 0; index < skin.TotalSkinBoneCount; index++)
            {
                var gameBone = skin.GetIGameBone(index, false);

                var sortedIndex = skinSortedBones[skin].IndexOf(gameBone.NodeID);

                gameBones[sortedIndex] = (gameBone);
                boneIds[sortedIndex]   = (gameBone.NodeID);
                bones[sortedIndex]     = (new BabylonBone {
                    index = sortedIndex, name = gameBone.Name
                });

                var boneInitMatrix = gameBone.GetObjectTM(0);
                bindPoseInfos[sortedIndex] = (new BonePoseInfo {
                    AbsoluteTransform = boneInitMatrix
                });
            }

            // fix hierarchy an generate animation keys
            var exportNonOptimizedAnimations = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportnonoptimizedanimations");

            for (var index = 0; index < skin.TotalSkinBoneCount; index++)
            {
                var gameBone = gameBones[index];
                var parent   = gameBone.NodeParent;
                var babBone  = bones[index];
                if (parent != null)
                {
                    babBone.parentBoneIndex = boneIds.IndexOf(parent.NodeID);
                }

                if (babBone.parentBoneIndex == -1)
                {
                    bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform;
                }
                else
                {
                    var parentBindPoseInfos = bindPoseInfos[babBone.parentBoneIndex];
                    bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform.Multiply(parentBindPoseInfos.AbsoluteTransform.Inverse);
                }

                babBone.matrix = bindPoseInfos[index].LocalTransform.ToArray();

                var babylonAnimation = new BabylonAnimation
                {
                    name           = gameBone.Name + "Animation",
                    property       = "_matrix",
                    dataType       = (int)BabylonAnimation.DataType.Matrix,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    framePerSecond = Loader.Global.FrameRate
                };

                var start = Loader.Core.AnimRange.Start;
                var end   = Loader.Core.AnimRange.End;

                float[] previous = null;
                var     keys     = new List <BabylonAnimationKey>();
                for (var key = start; key <= end; key += Ticks)
                {
                    var      objectTM   = gameBone.GetObjectTM(key);
                    var      parentNode = gameBone.NodeParent;
                    IGMatrix mat;
                    if (parentNode == null || babBone.parentBoneIndex == -1)
                    {
                        mat = objectTM;
                    }
                    else
                    {
                        mat = objectTM.Multiply(parentNode.GetObjectTM(key).Inverse);
                    }

                    var current = mat.ToArray();
                    if (key == start || key == end || exportNonOptimizedAnimations || !(previous.IsEqualTo(current)))
                    {
                        keys.Add(new BabylonAnimationKey
                        {
                            frame  = key / Ticks,
                            values = current
                        });
                    }

                    previous = current;
                }

                babylonAnimation.keys = keys.ToArray();
                babBone.animation     = babylonAnimation;
            }

            babylonSkeleton.needInitialSkinMatrix = true;
            babylonSkeleton.bones = bones.ToArray();

            babylonScene.SkeletonsList.Add(babylonSkeleton);
        }
        private void ExportSkin(IIGameSkin skin, BabylonScene babylonScene)
        {
            var babylonSkeleton = new BabylonSkeleton {
                id = skins.IndexOf(skin)
            };

            babylonSkeleton.name = "skeleton #" + babylonSkeleton.id;

            RaiseMessage(babylonSkeleton.name, 1);

            var skinIndex = skins.IndexOf(skin);

            var bones         = new List <BabylonBone>();
            var gameBones     = new List <IIGameNode>();
            var boneIds       = new List <int>();
            var bindPoseInfos = new List <BonePoseInfo>();

            for (int i = 0; i < skin.TotalSkinBoneCount; ++i)
            {
                bones.Add(null);
                gameBones.Add(null);
                boneIds.Add(-1);
                bindPoseInfos.Add(null);
            }
            for (var index = 0; index < skin.TotalSkinBoneCount; index++)
            {
                var gameBone = skin.GetIGameBone(index, false);

                var sortedIndex = skinSortedBones[skin].IndexOf(gameBone.NodeID);

                gameBones[sortedIndex] = (gameBone);
                boneIds[sortedIndex]   = (gameBone.NodeID);
                bones[sortedIndex]     = (new BabylonBone {
                    index = sortedIndex, name = gameBone.Name
                });

                var boneInitMatrix = gameBone.GetObjectTM(0);
                bindPoseInfos[sortedIndex] = (new BonePoseInfo {
                    AbsoluteTransform = boneInitMatrix
                });
            }

            // fix hierarchy and generate animation keys
            for (var index = 0; index < skin.TotalSkinBoneCount; index++)
            {
                var gameBone = gameBones[index];
                var parent   = gameBone.NodeParent;
                var babBone  = bones[index];
                if (parent != null)
                {
                    babBone.parentBoneIndex = boneIds.IndexOf(parent.NodeID);
                }

                if (babBone.parentBoneIndex == -1)
                {
                    bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform;
                }
                else
                {
                    var parentBindPoseInfos = bindPoseInfos[babBone.parentBoneIndex];
                    bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform.Multiply(parentBindPoseInfos.AbsoluteTransform.Inverse);
                }

                babBone.matrix = bindPoseInfos[index].LocalTransform.ToArray();

                var babylonAnimation = ExportMatrixAnimation("_matrix", key =>
                {
                    var objectTM   = gameBone.GetObjectTM(key);
                    var parentNode = gameBone.NodeParent;
                    IGMatrix mat;
                    if (parentNode == null || babBone.parentBoneIndex == -1)
                    {
                        mat = objectTM;
                    }
                    else
                    {
                        mat = objectTM.Multiply(parentNode.GetObjectTM(key).Inverse);
                    }
                    return(mat.ToArray());
                },
                                                             false); // Do not remove linear animation keys for bones

                if (babylonAnimation != null)
                {
                    babylonAnimation.name = gameBone.Name + "Animation"; // override default animation name
                    babBone.animation     = babylonAnimation;
                }
            }

            babylonSkeleton.needInitialSkinMatrix = true;
            babylonSkeleton.bones = bones.ToArray();

            babylonScene.SkeletonsList.Add(babylonSkeleton);
        }