Пример #1
0
        private BabylonSkeleton ConvertUnitySkeletonToBabylon(Transform[] bones, Matrix4x4[] bindPoses, Transform transform, GameObject gameObject, float progress)
        {
            ExporterWindow.ReportProgress(progress, "Exporting Skeleton: " + gameObject.name);
            BabylonSkeleton babylonSkeleton = new BabylonSkeleton();

            babylonSkeleton.name = gameObject.name;
            babylonSkeleton.id   = Math.Abs(GetID(transform.gameObject).GetHashCode());
            babylonSkeleton.needInitialSkinMatrix = false;

            // Prefilled to keep order and track parents.
            var transformToBoneMap = new Dictionary <Transform, BabylonBone>();

            for (var i = 0; i < bones.Length; i++)
            {
                var unityBone = bones[i];
                ExporterWindow.ReportProgress(progress, "Exporting bone: " + unityBone.name + " at index " + i);

                var babylonBone = new BabylonBone();
                babylonBone.name  = unityBone.name;
                babylonBone.index = i;

                transformToBoneMap.Add(unityBone, babylonBone);
            }

            // Attaches Matrix and parent.
            for (var i = 0; i < bones.Length; i++)
            {
                var       unityBone   = bones[i];
                var       babylonBone = transformToBoneMap[unityBone];
                Matrix4x4 localTransform;

                // Unity BindPose is already inverse so take the inverse again :-)
                if (transformToBoneMap.ContainsKey(unityBone.parent))
                {
                    var babylonParentBone = transformToBoneMap[unityBone.parent];
                    babylonBone.parentBoneIndex = babylonParentBone.index;
                    localTransform = bindPoses[babylonBone.parentBoneIndex] * bindPoses[i].inverse;
                }
                else
                {
                    babylonBone.parentBoneIndex = -1;
                    localTransform = bindPoses[i].inverse;
                }

                transformToBoneMap[unityBone].matrix = new[] {
                    localTransform[0, 0], localTransform[1, 0], localTransform[2, 0], localTransform[3, 0],
                    localTransform[0, 1], localTransform[1, 1], localTransform[2, 1], localTransform[3, 1],
                    localTransform[0, 2], localTransform[1, 2], localTransform[2, 2], localTransform[3, 2],
                    localTransform[0, 3], localTransform[1, 3], localTransform[2, 3], localTransform[3, 3]
                };
            }

            // Reorder and attach the skeleton.
            babylonSkeleton.bones = transformToBoneMap.Values.OrderBy(b => b.index).ToArray();
            babylonScene.SkeletonsList.Add(babylonSkeleton);

            return(babylonSkeleton);
        }
Пример #2
0
        private GLTFNode _exportBone(BabylonBone babylonBone, GLTF gltf, BabylonSkeleton babylonSkeleton, List <BabylonBone> bones)
        {
            if (alreadyExportedBones.ContainsKey(babylonBone))
            {
                return(alreadyExportedBones[babylonBone]);
            }

            // Node
            var gltfNode = new GLTFNode
            {
                name = babylonBone.name
            };

            gltfNode.index = gltf.NodesList.Count;
            gltf.NodesList.Add(gltfNode);
            alreadyExportedBones.Add(babylonBone, gltfNode);
            boneToGltfNodeMap.Add(babylonBone, gltfNode);

            // Hierarchy
            if (babylonBone.parentBoneIndex >= 0)
            {
                var babylonParentBone = bones.Find(_babylonBone => _babylonBone.index == babylonBone.parentBoneIndex);
                var gltfParentNode    = _exportBone(babylonParentBone, gltf, babylonSkeleton, bones);
                RaiseMessage("GLTFExporter.Skin | Add " + babylonBone.name + " as child to " + gltfParentNode.name, 3);
                gltfParentNode.ChildrenList.Add(gltfNode.index);
                gltfNode.parent = gltfParentNode;
            }
            else
            {
                // It's a root node
                // Only root nodes are listed in a gltf scene
                RaiseMessage("GLTFExporter.Skin | Add " + babylonBone.name + " as root node to scene", 3);
                gltf.scenes[0].NodesList.Add(gltfNode.index);
            }

            // Transform
            // Bones transform are exported through translation/rotation/scale (TRS) rather than matrix
            // Because gltf node animation can only target TRS properties, not the matrix one
            // Create matrix from array
            var babylonMatrix = new BabylonMatrix();

            babylonMatrix.m = babylonBone.matrix;
            // Decompose matrix into TRS
            var translationBabylon  = new BabylonVector3();
            var rotationQuatBabylon = new BabylonQuaternion();
            var scaleBabylon        = new BabylonVector3();

            babylonMatrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon);
            // Store TRS values
            gltfNode.translation = translationBabylon.ToArray();
            gltfNode.rotation    = rotationQuatBabylon.ToArray();
            gltfNode.scale       = scaleBabylon.ToArray();

            // Animations
            //ExportBoneAnimation(babylonBone, gltf, gltfNode);

            return(gltfNode);
        }
Пример #3
0
        private static void ExportSkeletonAnimationClips(Animator animator, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, UnityEditor.AnimationState animationState, ref UnityMetaData metaData)
        {
            List <AnimationClip> states = Tools.GetAnimationClips(animator);

            if (states != null && states.Count > 0)
            {
                ExportSkeletonAnimationClipData(animator.gameObject, skeleton, skinnedMesh, babylonMesh, animationState, ref states, ref metaData, animator);
            }
        }
        private static void ExportSkeletonAnimationClips(Animator animator, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, UnityEditor.AnimationState animationState, ref UnityMetaData metaData)
        {
            List <UnityEditor.AnimationParameters> aparams = Tools.GetAnimationParameters(animator);
            List <AnimationClip> clips = Tools.GetAnimationClips(animator);

            if (clips != null && clips.Count > 0)
            {
                ExportSkeletonAnimationClipData(animator.gameObject, skeleton, skinnedMesh, babylonMesh, ref clips, ref metaData, aparams);
            }
        }
Пример #5
0
        /// <summary>
        /// Create the BabylonSkeleton from the Maya MFnSkinCluster.
        /// And add it to the BabylonScene.
        /// </summary>
        /// <param name="skin">The maya skin cluster</param>
        /// <param name="babylonScene">The scene to export</param>
        /// <returns></returns>
        private void ExportSkin(MFnSkinCluster skin, BabylonScene babylonScene)
        {
            int    logRank   = 1;
            int    skinIndex = GetSkeletonIndex(skin);
            string name      = "skeleton #" + skinIndex;

            RaiseMessage(name, logRank);

            BabylonSkeleton babylonSkeleton = new BabylonSkeleton {
                id    = skinIndex,
                name  = name,
                bones = ExportBones(skin),
                needInitialSkinMatrix = true
            };

            babylonScene.SkeletonsList.Add(babylonSkeleton);
        }
Пример #6
0
        void ParseModel(Model model, BabylonScene scene)
        {
            var             effects         = model.Meshes.SelectMany(m => m.Effects).ToList();
            var             meshes          = model.Meshes.ToList();
            var             total           = effects.Count + meshes.Count;
            var             progress        = 0;
            SkinningData    skinningData    = null;
            BabylonSkeleton currentSkeleton = null;

            if (model.Tag != null)
            {
                skinningData = model.Tag as SkinningData;
            }

            if (skinningData != null)
            {
                var skeleton = new BabylonSkeleton();
                skeleton.id   = scene.SkeletonsList.Count;
                skeleton.name = "Skeleton" + scene.SkeletonsList.Count;
                ParseBones(skinningData, skeleton);

                // Animations
                ParseAnimationClip(skinningData, skeleton);

                scene.SkeletonsList.Add(skeleton);
                currentSkeleton = skeleton;
            }

            foreach (Effect effect in effects)
            {
                ParseEffect(effect, scene);
                if (OnImportProgressChanged != null)
                {
                    OnImportProgressChanged(((progress++) * 100) / total);
                }
            }

            foreach (var mesh in meshes)
            {
                ParseMesh(mesh, scene, currentSkeleton);
                if (OnImportProgressChanged != null)
                {
                    OnImportProgressChanged(((progress++) * 100) / total);
                }
            }
        }
Пример #7
0
        void ParseMesh(ModelMesh modelMesh, BabylonScene scene, BabylonSkeleton skeleton)
        {
            var proxyID   = ProxyMesh.CreateBabylonMesh(modelMesh.Name, scene);
            int indexName = 0;

            foreach (var part in modelMesh.MeshParts)
            {
                var material = exportedMaterials.First(m => m.Name == part.Effect.GetHashCode().ToString());

                var indices = new ushort[part.PrimitiveCount * 3];
                part.IndexBuffer.GetData(part.StartIndex * 2, indices, 0, indices.Length);

                if (part.VertexBuffer.VertexDeclaration.VertexStride > PositionNormalTextured.Stride)
                {
                    var mesh     = new Mesh <PositionNormalTexturedWeights>(material);
                    var vertices = new PositionNormalTexturedWeights[part.NumVertices];

                    part.VertexBuffer.GetData(part.VertexOffset * part.VertexBuffer.VertexDeclaration.VertexStride, vertices, 0, vertices.Length, part.VertexBuffer.VertexDeclaration.VertexStride);

                    for (int index = 0; index < vertices.Length; index++)
                    {
                        vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y;
                    }

                    mesh.AddPart(indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList());
                    mesh.CreateBabylonMesh(scene, proxyID, skeleton);
                }
                else
                {
                    var mesh     = new Mesh <PositionNormalTextured>(material);
                    var vertices = new PositionNormalTextured[part.NumVertices];
                    part.VertexBuffer.GetData(part.VertexOffset * PositionNormalTextured.Stride, vertices, 0, vertices.Length, PositionNormalTextured.Stride);

                    for (int index = 0; index < vertices.Length; index++)
                    {
                        vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y;
                    }

                    mesh.AddPart(indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList());
                    mesh.CreateBabylonMesh(scene, proxyID, skeleton);
                }

                indexName++;
            }
        }
Пример #8
0
        private void ParseBones(SkinningData skinningData, BabylonSkeleton skeleton)
        {
            // Bones
            var bones = new List <BabylonBone>();

            for (int boneIndex = 0; boneIndex < skinningData.BindPose.Count; boneIndex++)
            {
                var newBone = new BabylonBone();
                bones.Add(newBone);

                newBone.name            = "bone" + boneIndex;
                newBone.index           = boneIndex;
                newBone.matrix          = skinningData.BindPose[boneIndex].ToMatrix().ToArray();
                newBone.parentBoneIndex = skinningData.SkeletonHierarchy[boneIndex];
            }

            skeleton.bones = bones.ToArray();
        }
Пример #9
0
        /// <summary>
        /// Export the skeleton
        /// </summary>
        /// <param name="skin">The skin to export</param>
        /// <param name="babylonScene">The exported scene that will contain the skeleton</param>
        private void ExportSkin(IIGameSkin skin, BabylonScene babylonScene)
        {
            int    logRank   = 1;
            int    skinIndex = skins.IndexOf(skin);
            string name      = "skeleton #" + skinIndex;

            logger?.RaiseMessage(name, logRank);
            IIGameNode      skinNode        = skinNodeMap[skin];
            BabylonSkeleton babylonSkeleton = new BabylonSkeleton
            {
                id    = skinIndex,
                name  = name,
                bones = ExportBones(skin, skinNode),
                needInitialSkinMatrix = true
            };

            babylonScene.SkeletonsList.Add(babylonSkeleton);
        }
Пример #10
0
        private void ParseAnimationClip(SkinningData skinningData, BabylonSkeleton skeleton)
        {
            foreach (var clipKey in skinningData.AnimationClips.Keys)
            {
                var clip     = skinningData.AnimationClips[clipKey];
                var duration = clip.Duration.TotalMilliseconds;
                var dic      = new Dictionary <int, List <BabylonAnimationKey> >();

                foreach (var keyframe in clip.Keyframes)
                {
                    if (!dic.ContainsKey(keyframe.Bone))
                    {
                        dic.Add(keyframe.Bone, new List <BabylonAnimationKey>());
                    }

                    var currentTime = (float)(keyframe.Time.TotalMilliseconds * 100.0 / duration);

                    dic[keyframe.Bone].Add(new BabylonAnimationKey
                    {
                        frame  = currentTime,
                        values = keyframe.Transform.ToMatrix().ToArray()
                    });
                }

                foreach (var index in dic.Keys)
                {
                    var bone             = skeleton.bones[index];
                    var babylonAnimation = new BabylonAnimation {
                        name = bone.name + "Animation", property = "_matrix", dataType = BabylonAnimation.DataType.Matrix, loopBehavior = InterpolationLoop.Cycle, framePerSecond = 60
                    };
                    babylonAnimation.keys = dic[index].ToArray();
                    bone.animation        = babylonAnimation;
                }

                return; // Only one animation track
            }
        }
Пример #11
0
        private IList <BabylonAnimationGroup> ExportAnimationGroups(BabylonScene babylonScene)
        {
            IList <BabylonAnimationGroup> animationGroups = new List <BabylonAnimationGroup>();

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

            foreach (AnimationGroup animGroup in animationList)
            {
                logger?.RaiseMessage("Exporter.animationGroups | " + animGroup.Name, 1);
                BabylonAnimationGroup animationGroup = new BabylonAnimationGroup
                {
                    name               = animGroup.Name,
                    from               = animGroup.FrameStart,
                    to                 = animGroup.FrameEnd,
                    keepNonAnimated    = animGroup.KeepNonAnimated,
                    targetedAnimations = new List <BabylonTargetedAnimation>()
                };

                // add animations of each nodes contained in the animGroup
                foreach (Guid guid in animGroup.NodeGuids)
                {
                    IINode maxNode = Tools.GetINodeByGuid(guid);

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

                    if (exportParameters.exportAsSubmodel && !maxNode.Selected)
                    {
                        continue;
                    }


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

                    // Node
                    BabylonNode node = null;
                    babylonScene.NodeMap.TryGetValue(nodeId, out node);
                    if (node != null)
                    {
                        if (node.animations != null && node.animations.Length != 0)
                        {
                            IList <BabylonAnimation> animations = GetSubAnimations(node, animationGroup.from, animationGroup.to);

                            if (!animGroup.KeepStaticAnimation)
                            {
                                RemoveStaticAnimations(ref animations);
                            }

                            foreach (BabylonAnimation animation in animations)
                            {
                                BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation
                                {
                                    animation = animation,
                                    targetId  = nodeId
                                };

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

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

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

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

                // add animations of each nodes contained in the animGroup
                foreach (Guid guid in animGroup.MaterialGuids)
                {
                    IMtl maxMtl = Tools.GetIMtlByGuid(guid);

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

                    string matId = maxMtl.GetGuid().ToString();

                    // Material
                    BabylonMaterial material = null;
                    material = babylonScene.MaterialsList.FirstOrDefault(x => x.id == matId);
                    if (material != null)
                    {
                        if (material.animations != null && material.animations.Length != 0)
                        {
                            IList <BabylonAnimation> animations = GetSubAnimations(material, animationGroup.from, animationGroup.to);
                            foreach (BabylonAnimation animation in animations)
                            {
                                BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation
                                {
                                    animation = animation,
                                    targetId  = matId
                                };

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

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

            return(animationGroups);
        }
Пример #12
0
        private static void ExportSkeletonAnimationClipData(GameObject source, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, UnityEditor.AnimationState animationState, ref List <AnimationClip> states, ref UnityMetaData metaData, Animator animator)
        {
            ExporterWindow.ReportProgress(1, "Exporting skeleton clips: " + skinnedMesh.name);
            //string sourceId = GetID(source);
            int frameRate       = 0;
            int firstClipEnd    = 0;
            int totalFrameCount = 0;

            Transform[]   bones          = skinnedMesh.bones;
            List <string> stateNameCache = new List <string>();

            if (!AnimationMode.InAnimationMode())
            {
                AnimationMode.StartAnimationMode();
            }
            //var anims = new List<BabylonAnimation>();
            //var pxkeys = new List<BabylonAnimationKey>();
            float playbackSpeed      = (animationState != null) ? animationState.playbackSpeed : 1.0f;
            float clampFeetPositions = (animationState != null) ? animationState.clampFeetPositions : 0.0f;
            BabylonAnimationBaking bakeRootTransforms = (animationState != null) ? animationState.bakeRootTransforms : BabylonAnimationBaking.GameBlend;

            foreach (var bone in skeleton.bones)
            {
                int       frameOffest = 0;
                var       keys        = new List <BabylonAnimationKey>();
                Transform transform   = bones.Single(b => b.name == bone.name);
                foreach (var state in states)
                {
                    if (state == null)
                    {
                        continue;
                    }
                    AnimationClip clip = state as AnimationClip;
                    if (frameRate <= 0)
                    {
                        frameRate = (int)clip.frameRate;
                    }
                    var frameTime      = 1.0f / frameRate;
                    int clipFrameCount = (int)(clip.length * frameRate);
                    if (firstClipEnd <= 0)
                    {
                        firstClipEnd = (clipFrameCount - 1);
                    }
                    var settings = AnimationUtility.GetAnimationClipSettings(clip);
                    BabylonLoopBehavior behavior = (settings.loopTime) ? BabylonLoopBehavior.Cycle : BabylonLoopBehavior.Constant;
                    if (settings.loopTime && settings.loopBlend)
                    {
                        behavior = BabylonLoopBehavior.Relative;
                    }
                    ExporterWindow.ReportProgress(1, "Sampling: " + babylonMesh.name + " - " + bone.name + " - " + clip.name);
                    // Set Animation State Meta Data
                    if (!stateNameCache.Contains(clip.name))
                    {
                        stateNameCache.Add(clip.name);
                        // Animation Clip Information
                        Dictionary <string, object> animStateInfo = new Dictionary <string, object>();
                        animStateInfo.Add("type", "skeleton");
                        animStateInfo.Add("name", clip.name);
                        animStateInfo.Add("start", frameOffest);
                        animStateInfo.Add("stop", (frameOffest + clipFrameCount - 1));
                        animStateInfo.Add("rate", frameRate);
                        animStateInfo.Add("behavior", (int)behavior);
                        animStateInfo.Add("playback", playbackSpeed);
                        metaData.animationClips.Add(animStateInfo);
                    }
                    AnimationMode.BeginSampling();
                    for (var i = 0; i < clipFrameCount; i++)
                    {
                        Matrix4x4 local;
                        int       frameIndex = (i + frameOffest);
                        float     originalPX = transform.localPosition.x;
                        float     originalPY = transform.localPosition.y;
                        float     originalPZ = transform.localPosition.z;
                        float     originalRY = transform.localRotation.eulerAngles.y;
                        clip.SampleAnimation(source, i * frameTime);
                        if (transform == skinnedMesh.rootBone)
                        {
                            float      positionX  = transform.localPosition.x;
                            float      positionY  = transform.localPosition.y;
                            float      positionZ  = transform.localPosition.z;
                            Quaternion rotationQT = transform.localRotation;
                            if (settings.loopBlendOrientation || settings.keepOriginalOrientation)
                            {
                                if (settings.keepOriginalOrientation)
                                {
                                    // Original Rotation - ???
                                    rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, originalRY, rotationQT.eulerAngles.z);
                                }
                                else
                                {
                                    // Body Orientation - ???
                                    rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, settings.orientationOffsetY, rotationQT.eulerAngles.z);
                                }
                            }
                            if (settings.loopBlendPositionY || settings.keepOriginalPositionY)
                            {
                                if (settings.keepOriginalPositionY)
                                {
                                    // Original Position Y
                                    positionY = originalPY;
                                }
                                else if (settings.heightFromFeet)
                                {
                                    // Feet Position Y
                                    positionY = (settings.level + clampFeetPositions);
                                }
                                else
                                {
                                    // Center Of Mass
                                    positionY = 0.0f;
                                }
                            }
                            if (settings.loopBlendPositionXZ || settings.keepOriginalPositionXZ)
                            {
                                if (settings.keepOriginalPositionXZ)
                                {
                                    // Original Position XZ
                                    positionX = originalPX;
                                    positionZ = originalPZ;
                                }
                                else
                                {
                                    // Center Of Mass
                                    positionX = 0.0f;
                                    positionZ = 0.0f;
                                }
                            }
                            if (bakeRootTransforms == BabylonAnimationBaking.GameBlend)
                            {
                                positionX = 0.0f;
                                positionZ = 0.0f;
                            }
                            local = Matrix4x4.TRS(new Vector3(positionX, positionY, positionZ), rotationQT, transform.localScale);
                        }
                        else
                        {
                            // DEPRECIATED: local = (transform.parent.localToWorldMatrix.inverse * transform.localToWorldMatrix);
                            local = Matrix4x4.TRS(transform.localPosition, transform.localRotation, transform.localScale);
                        }
                        float[] matrix = new[] {
                            local[0, 0], local[1, 0], local[2, 0], local[3, 0],
                            local[0, 1], local[1, 1], local[2, 1], local[3, 1],
                            local[0, 2], local[1, 2], local[2, 2], local[3, 2],
                            local[0, 3], local[1, 3], local[2, 3], local[3, 3]
                        };
                        var key = new BabylonAnimationKey
                        {
                            frame  = frameIndex,
                            values = matrix
                        };
                        keys.Add(key);
                    }
                    AnimationMode.EndSampling();
                    frameOffest     += clipFrameCount;
                    totalFrameCount += clipFrameCount;
                }
                var babylonAnimation = new BabylonAnimation
                {
                    name           = bone.name + "Animation",
                    property       = "_matrix",
                    dataType       = (int)BabylonAnimation.DataType.Matrix,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    framePerSecond = frameRate,
                    keys           = keys.ToArray()
                };
                bone.animation = babylonAnimation;
            }
            if (AnimationMode.InAnimationMode())
            {
                AnimationMode.StopAnimationMode();
            }

            /*
             * //
             * // TODO: Format Custom Curve Keys
             * //
             * string property = "none";
             * if (pxkeys.Count > 0)
             * {
             *  property = "metadata.state.animPosition.x";
             *  anims.Add(new BabylonAnimation
             *  {
             *      dataType = (int)BabylonAnimation.DataType.Float,
             *      name = property + " animation",
             *      keys = pxkeys.ToArray(),
             *      framePerSecond = frameRate,
             *      enableBlending = false,
             *      blendingSpeed = 0.0f,
             *      loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle,
             *      property = property
             *  });
             * }
             * //
             * // Cache Babylon Animation Keys
             * //
             * if (anims.Count > 0)
             * {
             *  List<BabylonAnimation> sourceAnimiamtions = null;
             *  if (SceneBuilder.AnimationCurveKeys.ContainsKey(sourceId)) {
             *      sourceAnimiamtions = SceneBuilder.AnimationCurveKeys[sourceId];
             *  } else {
             *      sourceAnimiamtions = new List<BabylonAnimation>();
             *      SceneBuilder.AnimationCurveKeys.Add(sourceId, sourceAnimiamtions);
             *  }
             *  foreach (var anim in anims) {
             *      sourceAnimiamtions.Add(anim);
             *  }
             * }
             */
        }
        private void ExportSkin(IISkin skin, BabylonScene babylonScene)
        {
            var babylonSkeleton = new BabylonSkeleton {
                id = skins.IndexOf(skin)
            };

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

            RaiseMessage(babylonSkeleton.name, 1);

            var bones = new List <BabylonBone>();

            for (var index = 0; index < skin.NumBones; index++)
            {
                var bone = new BabylonBone {
                    name = skin.GetBoneName(index), index = index
                };

                var maxBone    = skin.GetBone(index);
                var parentNode = maxBone.ParentNode;

                if (parentNode != null)
                {
                    for (var recurseIndex = 0; recurseIndex < index; recurseIndex++)
                    {
                        if (skin.GetBone(recurseIndex).GetGuid() == parentNode.GetGuid())
                        {
                            bone.parentBoneIndex = recurseIndex;
                            break;
                        }
                    }
                }
                var hasParent = bone.parentBoneIndex != -1;
                bone.matrix = GetBoneMatrix(skin, maxBone, 0, hasParent);

                // Animation
                var babylonAnimation = new BabylonAnimation
                {
                    name           = bone.name + "Animation",
                    property       = "_matrix",
                    dataType       = BabylonAnimation.DataType.Matrix,
                    loopBehavior   = 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 current = GetBoneMatrix(skin, maxBone, key, hasParent);

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

                    previous = current;
                }

                babylonAnimation.keys = keys.ToArray();
                bone.animation        = babylonAnimation;

                bones.Add(bone);
            }

            babylonSkeleton.bones = bones.ToArray();

            babylonScene.SkeletonsList.Add(babylonSkeleton);
        }
Пример #14
0
        void ParseModel(Model model, BabylonScene scene, bool rightToLeft)
        {
            var effects = model.Meshes.SelectMany(m => m.Effects).ToList();
            var meshes = model.Meshes.ToList();
            var total = effects.Count + meshes.Count;
            var progress = 0;
            SkinningData skinningData = null;
            BabylonSkeleton currentSkeleton = null;

            if (model.Tag != null)
            {
                skinningData = model.Tag as SkinningData;
            }

            if (skinningData != null)
            {
                var skeleton = new BabylonSkeleton();
                skeleton.id = scene.SkeletonsList.Count;
                skeleton.name = "Skeleton" + scene.SkeletonsList.Count;
                ParseBones(skinningData, skeleton);

                // Animations
                ParseAnimationClip(skinningData, skeleton);

                scene.SkeletonsList.Add(skeleton);
                currentSkeleton = skeleton;
            }

            foreach (Effect effect in effects)
            {
                ParseEffect(effect, scene);
                if (OnImportProgressChanged != null)
                    OnImportProgressChanged(((progress++) * 100) / total);
            }

            foreach (var mesh in meshes)
            {
                ParseMesh(mesh, scene, currentSkeleton, rightToLeft);
                if (OnImportProgressChanged != null)
                    OnImportProgressChanged(((progress++) * 100) / total);
            }
        }
        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);
        }
Пример #16
0
        private void ParseBones(SkinningData skinningData, BabylonSkeleton skeleton)
        {
            // Bones
            var bones = new List<BabylonBone>();
            for (int boneIndex = 0; boneIndex < skinningData.BindPose.Count; boneIndex++)
            {
                var newBone = new BabylonBone();
                bones.Add(newBone);

                newBone.name = "bone" + boneIndex;
                newBone.index = boneIndex;
                newBone.matrix = skinningData.BindPose[boneIndex].ToMatrix().ToArray();
                newBone.parentBoneIndex = skinningData.SkeletonHierarchy[boneIndex];
            }

            skeleton.bones = bones.ToArray();
        }
Пример #17
0
        private static void ExportSkeletonAnimationClipData(Animator animator, bool autoPlay, BabylonSkeleton skeleton, Transform[] bones, BabylonMesh babylonMesh, AnimationClip clip)
        {
            var frameTime           = 1.0f / clip.frameRate;
            int animationFrameCount = (int)(clip.length * clip.frameRate);

            if (autoPlay)
            {
                babylonMesh.autoAnimate     = true;
                babylonMesh.autoAnimateFrom = 0;
                babylonMesh.autoAnimateTo   = animationFrameCount;
                babylonMesh.autoAnimateLoop = true;
            }

            foreach (var bone in skeleton.bones)
            {
                var keys      = new List <BabylonAnimationKey>();
                var transform = bones.Single(b => b.name == bone.name);

                AnimationMode.BeginSampling();
                for (var i = 0; i < animationFrameCount; i++)
                {
                    clip.SampleAnimation(animator.gameObject, i * frameTime);

                    var     local  = (transform.parent.localToWorldMatrix.inverse * transform.localToWorldMatrix);
                    float[] matrix = new[] {
                        local[0, 0], local[1, 0], local[2, 0], local[3, 0],
                        local[0, 1], local[1, 1], local[2, 1], local[3, 1],
                        local[0, 2], local[1, 2], local[2, 2], local[3, 2],
                        local[0, 3], local[1, 3], local[2, 3], local[3, 3]
                    };

                    var key = new BabylonAnimationKey
                    {
                        frame  = i,
                        values = matrix,
                    };
                    keys.Add(key);
                }
                AnimationMode.EndSampling();

                var babylonAnimation = new BabylonAnimation
                {
                    name           = bone.name + "Animation",
                    property       = "_matrix",
                    dataType       = (int)BabylonAnimation.DataType.Matrix,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    framePerSecond = (int)clip.frameRate,
                    keys           = keys.ToArray()
                };

                bone.animation = babylonAnimation;
            }
        }
        private static void ExportSkeletonAnimationClipData(GameObject source, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, ref List <AnimationClip> clips, ref UnityMetaData metaData, List <UnityEditor.AnimationParameters> aparams)
        {
            ExporterWindow.ReportProgress(1, "Baking " + skinnedMesh.name.ToLower() + " skeleton... This may take a while.");
            string sourceId  = GetID(source);
            int    frameRate = 0;

            Transform[]         bones          = skinnedMesh.bones;
            var                 anims          = new List <BabylonAnimation>();
            List <BabylonRange> ranges         = new List <BabylonRange>();
            List <string>       stateNameCache = new List <string>();

            if (!AnimationMode.InAnimationMode())
            {
                AnimationMode.StartAnimationMode();
            }
            foreach (var bone in skeleton.bones)
            {
                int       frameOffest = 0;
                var       keys        = new List <BabylonAnimationKey>();
                Transform transform   = bones.Single(b => b.name == bone.name);
                foreach (var clip in clips)
                {
                    if (clip == null)
                    {
                        continue;
                    }
                    string clipName = FormatSafeClipName(clip.name);
                    var    settings = AnimationUtility.GetAnimationClipSettings(clip);
                    BabylonLoopBehavior behavior = (settings.loopTime) ? BabylonLoopBehavior.Relative : BabylonLoopBehavior.Constant;
                    if (settings.loopTime && settings.loopBlend)
                    {
                        behavior = BabylonLoopBehavior.Cycle;
                    }
                    // ..
                    int   framePadding = 1;
                    float deltaTime    = 1.0f / clip.frameRate;
                    if (frameRate <= 0)
                    {
                        frameRate = (int)clip.frameRate;
                    }
                    float clipFrameTotal = clip.length * clip.frameRate;
                    int   clipFrameCount = (int)clipFrameTotal + framePadding;
                    int   lastFrameCount = clipFrameCount - 1;
                    // ..
                    AnimationMode.BeginSampling();
                    for (int i = 0; i < clipFrameCount; i++)
                    {
                        Matrix4x4 local;
                        int       frameIndex = (int)(i + frameOffest);
                        float     sampleTime = (i < lastFrameCount) ? (i * deltaTime) : clip.length;
                        clip.SampleAnimation(source, sampleTime);
                        if (transform == skinnedMesh.rootBone)
                        {
                            float      positionX  = transform.localPosition.x;
                            float      positionY  = transform.localPosition.y;
                            float      positionZ  = transform.localPosition.z;
                            Quaternion rotationQT = transform.localRotation;
                            if (settings.loopBlendOrientation)
                            {
                                if (settings.keepOriginalOrientation)
                                {
                                    rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, (rotationQT.eulerAngles.y + settings.orientationOffsetY), rotationQT.eulerAngles.z);
                                }
                                else
                                {
                                    rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, settings.orientationOffsetY, rotationQT.eulerAngles.z);
                                }
                            }
                            if (settings.loopBlendPositionY)
                            {
                                if (settings.keepOriginalPositionY || settings.heightFromFeet)
                                {
                                    positionY += settings.level;
                                }
                                else
                                {
                                    positionY = settings.level;
                                }
                            }
                            if (settings.loopBlendPositionXZ)
                            {
                                positionX = 0.0f;
                                positionZ = 0.0f;
                            }
                            local = Matrix4x4.TRS(new Vector3(positionX, positionY, positionZ), rotationQT, transform.localScale);
                        }
                        else
                        {
                            local = Matrix4x4.TRS(transform.localPosition, transform.localRotation, transform.localScale);
                        }
                        float[] matrix = new[] {
                            local[0, 0], local[1, 0], local[2, 0], local[3, 0],
                            local[0, 1], local[1, 1], local[2, 1], local[3, 1],
                            local[0, 2], local[1, 2], local[2, 2], local[3, 2],
                            local[0, 3], local[1, 3], local[2, 3], local[3, 3]
                        };
                        var key = new BabylonAnimationKey
                        {
                            frame  = frameIndex,
                            values = matrix
                        };
                        keys.Add(key);
                    }
                    AnimationMode.EndSampling();
                    // ..
                    // Set Animation State Meta Data
                    // ..
                    if (!stateNameCache.Contains(clipName))
                    {
                        stateNameCache.Add(clipName);
                        // Animation Clip Information
                        int fromFrame = frameOffest, toFrame = frameOffest + lastFrameCount;
                        Dictionary <string, object> animStateInfo = new Dictionary <string, object>();
                        animStateInfo.Add("type", "skeleton");
                        animStateInfo.Add("wrap", clip.wrapMode);
                        animStateInfo.Add("name", clipName);
                        animStateInfo.Add("start", fromFrame);
                        animStateInfo.Add("stop", toFrame);
                        animStateInfo.Add("rate", clip.frameRate);
                        animStateInfo.Add("frames", clipFrameCount);
                        animStateInfo.Add("weight", 1.0f);
                        animStateInfo.Add("behavior", (int)behavior);
                        animStateInfo.Add("apparentSpeed", clip.apparentSpeed);
                        animStateInfo.Add("averageSpeed", clip.averageSpeed.ToFloat());
                        animStateInfo.Add("averageDuration", clip.averageDuration);
                        animStateInfo.Add("averageAngularSpeed", clip.averageAngularSpeed);
                        List <string> customCurveKeyNames = new List <string>();
                        // ..
                        // UnityEditor.AnimationParameters;
                        // ..
                        if (aparams != null && aparams.Count > 0)
                        {
                            foreach (var aparam in aparams)
                            {
                                if (aparam.curve == true)
                                {
                                    var curve = Tools.GetAnimationCurve(clip, aparam.name);
                                    if (curve != null)
                                    {
                                        IEnumerable <BabylonAnimationKey> cx_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                                            frame  = (int)(keyFrame.time * frameRate),
                                            values = new[] { keyFrame.value }
                                        });
                                        BabylonAnimationKey[] xkeys = (cx_keys != null && cx_keys.Count() > 0) ? cx_keys.ToArray() : null;
                                        if (xkeys != null && xkeys.Length > 0)
                                        {
                                            string xkey  = aparam.name;
                                            string xprop = "metadata.state.floats." + xkey;
                                            string xname = "curve:" + clipName.Replace(" ", "") + ":" + System.Guid.NewGuid().ToString();
                                            customCurveKeyNames.Add(xname);
                                            anims.Add(new BabylonAnimation
                                            {
                                                dataType       = (int)BabylonAnimation.DataType.Float,
                                                name           = xname,
                                                keys           = xkeys,
                                                framePerSecond = frameRate,
                                                enableBlending = false,
                                                blendingSpeed  = 0.0f,
                                                loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                                                property       = xprop
                                            });
                                        }
                                    }
                                }
                            }
                        }
                        animStateInfo.Add("customCurveKeyNames", (customCurveKeyNames.Count > 0) ? customCurveKeyNames.ToArray() : null);
                        metaData.animationClips.Add(animStateInfo);
                        ranges.Add(new BabylonRange {
                            name = clipName, from = fromFrame, to = toFrame
                        });
                    }
                    // ..
                    frameOffest += clipFrameCount;
                }
                var babylonAnimation = new BabylonAnimation
                {
                    name           = "skeleton:" + bone.name.ToLower() + ":animation",
                    property       = "_matrix",
                    dataType       = (int)BabylonAnimation.DataType.Matrix,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    framePerSecond = frameRate,
                    keys           = keys.ToArray()
                };
                bone.animation = babylonAnimation;
            }
            if (AnimationMode.InAnimationMode())
            {
                AnimationMode.StopAnimationMode();
            }
            //
            // Serialize Skeleton Clip Ranges
            //
            skeleton.ranges = (ranges.Count > 0) ? ranges.ToArray() : null;
            //
            // Cache Babylon Animation Keys
            //
            if (anims.Count > 0)
            {
                List <BabylonAnimation> sourceAnimiamtions = null;
                if (SceneBuilder.AnimationCurveKeys.ContainsKey(sourceId))
                {
                    sourceAnimiamtions = SceneBuilder.AnimationCurveKeys[sourceId];
                }
                else
                {
                    sourceAnimiamtions = new List <BabylonAnimation>();
                    SceneBuilder.AnimationCurveKeys.Add(sourceId, sourceAnimiamtions);
                }
                foreach (var anim in anims)
                {
                    sourceAnimiamtions.Add(anim);
                }
            }
        }
        private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode)
        {
            // Skin
            GLTFSkin gltfSkin = new GLTFSkin
            {
                name = babylonSkeleton.name
            };

            gltfSkin.index = gltf.SkinsList.Count;
            gltf.SkinsList.Add(gltfSkin);

            var bones = new List <BabylonBone>(babylonSkeleton.bones);

            // Compute and store world matrix of each bone
            var bonesWorldMatrices = new Dictionary <int, BabylonMatrix>();

            foreach (var babylonBone in babylonSkeleton.bones)
            {
                if (!bonesWorldMatrices.ContainsKey(babylonBone.index))
                {
                    BabylonMatrix boneWorldMatrix = _getBoneWorldMatrix(babylonBone, bones);
                    bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix);
                }
            }

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

            // Accessor - InverseBindMatrices
            var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor(
                gltf,
                GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer),
                "accessorInverseBindMatrices",
                GLTFAccessor.ComponentType.FLOAT,
                GLTFAccessor.TypeEnum.MAT4
                );

            gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index;

            // World matrix of the node
            var invNodeWorldMatrix = BabylonMatrix.Invert(_getNodeWorldMatrix(gltfNode)); // inverted

            var gltfJoints = new List <int>();

            alreadyExportedBones = new Dictionary <BabylonBone, GLTFNode>();
            foreach (var babylonBone in babylonSkeleton.bones)
            {
                // Export bone as a new node
                var gltfBoneNode = _exportBone(babylonBone, gltf, babylonSkeleton, bones);
                gltfJoints.Add(gltfBoneNode.index);

                // Set this bone as skeleton if it is a root
                if (babylonBone.parentBoneIndex == -1)
                {
                    gltfSkin.skeleton = gltfBoneNode.index;
                }
                // Compute inverseBindMatrice for this bone when attached to this node
                var boneLocalMatrix = new BabylonMatrix();
                boneLocalMatrix.m = babylonBone.matrix;

                BabylonMatrix boneWorldMatrix = null;
                if (babylonBone.parentBoneIndex == -1)
                {
                    boneWorldMatrix = boneLocalMatrix;
                }
                else
                {
                    var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex];
                    // Remove scale of parent
                    // This actually enable to take into account the scale of the bones, except for the root one
                    parentWorldMatrix = _removeScale(parentWorldMatrix);

                    boneWorldMatrix = boneLocalMatrix * parentWorldMatrix;
                }

                var inverseBindMatrices = BabylonMatrix.Invert(boneWorldMatrix * invNodeWorldMatrix);

                // Populate accessor
                List <float> matrix = new List <float>(inverseBindMatrices.m);
                matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n)));
                accessorInverseBindMatrices.count++;
            }
            gltfSkin.joints = gltfJoints.ToArray();

            return(gltfSkin);
        }
Пример #20
0
        private static void ExportSkeletonAnimation(SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, BabylonSkeleton skeleton)
        {
            var animator = skinnedMesh.rootBone.gameObject.GetComponent <Animator>();

            if (animator != null)
            {
                ExportSkeletonAnimationClips(animator, true, skeleton, skinnedMesh.bones, babylonMesh);
            }
            else
            {
                var parent = skinnedMesh.rootBone.parent;
                while (parent != null)
                {
                    animator = parent.gameObject.GetComponent <Animator>();
                    if (animator != null)
                    {
                        ExportSkeletonAnimationClips(animator, true, skeleton, skinnedMesh.bones, babylonMesh);
                        break;
                    }

                    parent = parent.parent;
                }
            }
        }
Пример #21
0
        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);
        }
Пример #22
0
        private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode, GLTFMesh gltfMesh)
        {
            logger.RaiseMessage("GLTFExporter.Skin | Export skin of node '" + gltfNode.name + "' based on skeleton '" + babylonSkeleton.name + "'", 2);

            // Retreive gltf skeleton data if babylon skeleton has already been exported
            if (!alreadyExportedSkeletons.ContainsKey(babylonSkeleton))
            {
                alreadyExportedSkeletons.Add(babylonSkeleton, new BabylonSkeletonExportData());

                // Switch coordinate system at object level
                foreach (var babylonBone in babylonSkeleton.bones)
                {
                    var boneLocalMatrix = new BabylonMatrix();
                    boneLocalMatrix.m = babylonBone.matrix;

                    var translationBabylon  = new BabylonVector3();
                    var rotationQuatBabylon = new BabylonQuaternion();
                    var scale = new BabylonVector3();
                    boneLocalMatrix.decompose(scale, rotationQuatBabylon, translationBabylon);
                    translationBabylon    *= exportParameters.scaleFactor;
                    translationBabylon.Z  *= -1;
                    rotationQuatBabylon.X *= -1;
                    rotationQuatBabylon.Y *= -1;
                    boneLocalMatrix        = BabylonMatrix.Compose(scale, rotationQuatBabylon, translationBabylon);

                    babylonBone.matrix = boneLocalMatrix.m;
                }
            }
            var babylonSkeletonExportData = alreadyExportedSkeletons[babylonSkeleton];

            // Skin

            // if this mesh is sharing a skin with another mesh, use the already exported skin
            var sharedSkinnedMeshesByOriginalPair = sharedSkinnedMeshesByOriginal.Where(skinSharingMeshPair => skinSharingMeshPair.Value.Contains(gltfMesh)).Select(kvp => (KeyValuePair <GLTFMesh, List <GLTFMesh> >?)kvp).FirstOrDefault();

            if (sharedSkinnedMeshesByOriginalPair != null)
            {
                logger.RaiseMessage("GLTFExporter.Skin | Sharing skinning information from mesh '" + sharedSkinnedMeshesByOriginalPair.Value.Key.name + "'", 3);
                var skeletonExportData = alreadyExportedSkeletons[babylonSkeleton];
                gltfNode.skin = skeletonExportData.skinIndex;
                return(gltf.SkinsList[(int)gltfNode.skin]);
            }

            // otherwise create a new GLTFSkin
            var      nameSuffix = babylonSkeletonExportData.nb != 0 ? "_" + babylonSkeletonExportData.nb : "";
            GLTFSkin gltfSkin   = new GLTFSkin
            {
                name = babylonSkeleton.name + nameSuffix
            };

            gltfSkin.index = gltf.SkinsList.Count;
            gltf.SkinsList.Add(gltfSkin);
            babylonSkeletonExportData.nb++;
            babylonSkeletonExportData.skinIndex = gltfSkin.index;

            var bones = new List <BabylonBone>(babylonSkeleton.bones);

            // Compute and store world matrix of each bone
            var bonesWorldMatrices = new Dictionary <int, BabylonMatrix>();

            foreach (var babylonBone in babylonSkeleton.bones)
            {
                if (!bonesWorldMatrices.ContainsKey(babylonBone.index))
                {
                    var           nodePair        = nodeToGltfNodeMap.First(pair => pair.Key.id.Equals(babylonBone.id));
                    BabylonMatrix boneWorldMatrix = _getNodeWorldMatrix(nodePair.Value);
                    bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix);
                }
            }

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

            // Accessor - InverseBindMatrices
            var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor(
                gltf,
                GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer),
                "accessorInverseBindMatrices",
                GLTFAccessor.ComponentType.FLOAT,
                GLTFAccessor.TypeEnum.MAT4
                );

            gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index;

            // World matrix of the node
            var nodeWorldMatrix = _getNodeWorldMatrix(gltfNode);

            var gltfJoints = new List <int>();

            foreach (var babylonBone in babylonSkeleton.bones)
            {
                GLTFNode gltfBoneNode = null;
                if (!babylonSkeletonExportData.nodeByBone.ContainsKey(babylonBone))
                {
                    // Export bone as a new node
                    gltfBoneNode = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonBone.id)).Value;//_exportBone(babylonBone, gltf, babylonSkeleton, bones);
                    babylonSkeletonExportData.nodeByBone.Add(babylonBone, gltfBoneNode);
                }
                gltfBoneNode = babylonSkeletonExportData.nodeByBone[babylonBone];

                gltfJoints.Add(gltfBoneNode.index);

                // Set this bone as skeleton if it is a root
                // Meaning of 'skeleton' here is the top root bone
                if (babylonBone.parentBoneIndex == -1)
                {
                    gltfSkin.skeleton = gltfBoneNode.index;
                }

                // Compute inverseBindMatrice for this bone when attached to this node
                var boneLocalMatrix = new BabylonMatrix();
                boneLocalMatrix.m = babylonBone.matrix;
                //printMatrix("boneLocalMatrix[" + babylonBone.name + "]", boneLocalMatrix);

                BabylonMatrix boneWorldMatrix = null;
                if (babylonBone.parentBoneIndex == -1)
                {
                    boneWorldMatrix = boneLocalMatrix;
                }
                else
                {
                    var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex];
                    // Remove scale of parent
                    // This actually enable to take into account the scale of the bones, except for the root one
                    parentWorldMatrix = _removeScale(parentWorldMatrix);

                    boneWorldMatrix = boneLocalMatrix * parentWorldMatrix;
                }
                //printMatrix("boneWorldMatrix[" + babylonBone.name + "]", boneWorldMatrix);

                var inverseBindMatrices = nodeWorldMatrix * BabylonMatrix.Invert(boneWorldMatrix);

                // Populate accessor
                List <float> matrix = new List <float>(inverseBindMatrices.m);
                matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n)));
                accessorInverseBindMatrices.count++;
            }
            gltfSkin.joints = gltfJoints.ToArray();

            ExportGLTFExtension(babylonSkeleton, ref gltfNode, gltf);

            return(gltfSkin);
        }
Пример #23
0
        private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode)
        {
            RaiseMessage("GLTFExporter.Skin | Export skin of node '" + gltfNode.name + "' based on skeleton '" + babylonSkeleton.name + "'", 2);

            // Retreive gltf skeleton data if babylon skeleton has already been exported
            if (!alreadyExportedSkeletons.ContainsKey(babylonSkeleton))
            {
                alreadyExportedSkeletons.Add(babylonSkeleton, new BabylonSkeletonExportData());
            }
            var babylonSkeletonExportData = alreadyExportedSkeletons[babylonSkeleton];

            // Skin
            var      nameSuffix = babylonSkeletonExportData.nb != 0 ? "_" + babylonSkeletonExportData.nb : "";
            GLTFSkin gltfSkin   = new GLTFSkin
            {
                name = babylonSkeleton.name + nameSuffix
            };

            gltfSkin.index = gltf.SkinsList.Count;
            gltf.SkinsList.Add(gltfSkin);
            babylonSkeletonExportData.nb++;

            var bones = new List <BabylonBone>(babylonSkeleton.bones);

            // Compute and store world matrix of each bone
            var bonesWorldMatrices = new Dictionary <int, BabylonMatrix>();

            foreach (var babylonBone in babylonSkeleton.bones)
            {
                if (!bonesWorldMatrices.ContainsKey(babylonBone.index))
                {
                    BabylonMatrix boneWorldMatrix = _getBoneWorldMatrix(babylonBone, bones);
                    bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix);
                }
            }

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

            // Accessor - InverseBindMatrices
            var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor(
                gltf,
                GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer),
                "accessorInverseBindMatrices",
                GLTFAccessor.ComponentType.FLOAT,
                GLTFAccessor.TypeEnum.MAT4
                );

            gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index;

            // World matrix of the node
            var nodeWorldMatrix = _getNodeWorldMatrix(gltfNode);
            //printMatrix("nodeWorldMatrix[" + gltfNode.name + "]", nodeWorldMatrix);

            var gltfJoints = new List <int>();

            alreadyExportedBones = new Dictionary <BabylonBone, GLTFNode>();
            foreach (var babylonBone in babylonSkeleton.bones)
            {
                GLTFNode gltfBoneNode = null;
                if (!babylonSkeletonExportData.nodeByBone.ContainsKey(babylonBone))
                {
                    // Export bone as a new node
                    gltfBoneNode = _exportBone(babylonBone, gltf, babylonSkeleton, bones);
                    babylonSkeletonExportData.nodeByBone.Add(babylonBone, gltfBoneNode);
                }
                gltfBoneNode = babylonSkeletonExportData.nodeByBone[babylonBone];

                gltfJoints.Add(gltfBoneNode.index);

                // Set this bone as skeleton if it is a root
                // Meaning of 'skeleton' here is the top root bone
                if (babylonBone.parentBoneIndex == -1)
                {
                    gltfSkin.skeleton = gltfBoneNode.index;
                }

                // Compute inverseBindMatrice for this bone when attached to this node
                var boneLocalMatrix = new BabylonMatrix();
                boneLocalMatrix.m = babylonBone.matrix;
                //printMatrix("boneLocalMatrix[" + babylonBone.name + "]", boneLocalMatrix);

                BabylonMatrix boneWorldMatrix = null;
                if (babylonBone.parentBoneIndex == -1)
                {
                    boneWorldMatrix = boneLocalMatrix;
                }
                else
                {
                    var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex];
                    // Remove scale of parent
                    // This actually enable to take into account the scale of the bones, except for the root one
                    parentWorldMatrix = _removeScale(parentWorldMatrix);

                    boneWorldMatrix = boneLocalMatrix * parentWorldMatrix;
                }
                //printMatrix("boneWorldMatrix[" + babylonBone.name + "]", boneWorldMatrix);

                var inverseBindMatrices = nodeWorldMatrix * BabylonMatrix.Invert(boneWorldMatrix);

                // Populate accessor
                List <float> matrix = new List <float>(inverseBindMatrices.m);
                matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n)));
                accessorInverseBindMatrices.count++;
            }
            gltfSkin.joints = gltfJoints.ToArray();

            return(gltfSkin);
        }
Пример #24
0
        void ParseMesh(ModelMesh modelMesh, BabylonScene scene, BabylonSkeleton skeleton, bool rightToLeft)
        {
            var proxyID = ProxyMesh.CreateBabylonMesh(modelMesh.Name, scene);
            int indexName = 0;

            foreach (var part in modelMesh.MeshParts)
            {
                var material = exportedMaterials.First(m => m.Name == part.Effect.GetHashCode().ToString());

                var indices = new ushort[part.PrimitiveCount * 3];
                part.IndexBuffer.GetData(part.StartIndex * 2, indices, 0, indices.Length);

                if (rightToLeft)
                {
                    for (int ib = 0; ib < indices.Length; ib += 3) // reverse winding of triangles
                    {
                        ushort ti = indices[ib];
                        indices[ib] = indices[ib + 2];
                        indices[ib + 2] = ti;
                    }
                }

                if (part.VertexBuffer.VertexDeclaration.VertexStride >= PositionNormalTexturedWeights.Stride)
                {
                    var mesh = new Mesh<PositionNormalTexturedWeights>(material);
                    var vertices = new PositionNormalTexturedWeights[part.NumVertices];

                    part.VertexBuffer.GetData(part.VertexOffset * part.VertexBuffer.VertexDeclaration.VertexStride, vertices, 0, vertices.Length, part.VertexBuffer.VertexDeclaration.VertexStride);

                    for (int index = 0; index < vertices.Length; index++)
                    {
                        vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y;
                        if (rightToLeft)
                        {
                            vertices[index].Position.Z = -vertices[index].Position.Z;
                            vertices[index].Normal.Z = -vertices[index].Normal.Z;
                        }
                    }

                    mesh.AddPart(modelMesh.Name+"#"+indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList());
                    mesh.CreateBabylonMesh(scene, proxyID, skeleton);
                }
                else
                {
                    if (part.VertexBuffer.VertexDeclaration.VertexStride < PositionNormalTextured.Stride) return; // Error: Not a PositionNormalTextured mesh!
                    var mesh = new Mesh<PositionNormalTextured>(material);
                    var vertices = new PositionNormalTextured[part.NumVertices];
                    part.VertexBuffer.GetData(part.VertexOffset * part.VertexBuffer.VertexDeclaration.VertexStride, vertices, 0, vertices.Length, part.VertexBuffer.VertexDeclaration.VertexStride);

                    for (int index = 0; index < vertices.Length; index++)
                    {
                        vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y;
                        if (rightToLeft)
                        {
                            vertices[index].Position.Z = -vertices[index].Position.Z;
                            vertices[index].Normal.Z = -vertices[index].Normal.Z;
                        }
                    }

                    mesh.AddPart(modelMesh.Name + "#" + indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList());
                    mesh.CreateBabylonMesh(scene, proxyID, skeleton);
                }

                indexName++;
            }
        }
Пример #25
0
        private void ParseAnimationClip(SkinningData skinningData, BabylonSkeleton skeleton)
        {
            foreach (var clipKey in skinningData.AnimationClips.Keys)
            {
                var clip = skinningData.AnimationClips[clipKey];
                var duration = clip.Duration.TotalMilliseconds;
                var dic = new Dictionary<int, List<BabylonAnimationKey>>();

                foreach (var keyframe in clip.Keyframes)
                {
                    if (!dic.ContainsKey(keyframe.Bone))
                    {
                        dic.Add(keyframe.Bone, new List<BabylonAnimationKey>());
                    }

                    var currentTime = (float)(keyframe.Time.TotalMilliseconds * 100.0 / duration);

                    dic[keyframe.Bone].Add(new BabylonAnimationKey
                    {
                        frame = currentTime,
                        values = keyframe.Transform.ToMatrix().ToArray()
                    });
                }

                foreach (var index in dic.Keys)
                {
                    var bone = skeleton.bones[index];
                    var babylonAnimation = new BabylonAnimation { name = bone.name + "Animation", property = "_matrix", dataType = BabylonAnimation.DataType.Matrix, loopBehavior = InterpolationLoop.Cycle, framePerSecond = 60 };
                    babylonAnimation.keys = dic[index].ToArray();
                    bone.animation = babylonAnimation;
                }

                return; // Only one animation track
            }
        }
Пример #26
0
        private static void ExportSkeletonAnimationClips(Animator animator, bool autoPlay, BabylonSkeleton skeleton, Transform[] bones, BabylonMesh babylonMesh)
        {
            AnimationClip      clip = null;
            AnimatorController ac   = animator.runtimeAnimatorController as AnimatorController;

            if (ac == null)
            {
                return;
            }
            var layer = ac.layers[0];

            if (layer == null)
            {
                return;
            }
            AnimatorStateMachine sm = layer.stateMachine;

            if (sm.states.Length > 0)
            {
                // Only the first state is supported so far.
                var state = sm.states[0].state;
                clip = state.motion as AnimationClip;
            }

            if (clip == null)
            {
                return;
            }

            ExportSkeletonAnimationClipData(animator, autoPlay, skeleton, bones, babylonMesh, clip);
        }
Пример #27
0
        private IList <BabylonAnimationGroup> ExportAnimationGroups(BabylonScene babylonScene)
        {
            IList <BabylonAnimationGroup> animationGroups = new List <BabylonAnimationGroup>();

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

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

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

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

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


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


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

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

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

                            animationGroup.targetedAnimations.Add(targetedAnimation);
                        }
                    }

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

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

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

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

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

            return(animationGroups);
        }
Пример #28
0
        void ParseMesh(ModelMesh modelMesh, BabylonScene scene, BabylonSkeleton skeleton)
        {
            var proxyID = ProxyMesh.CreateBabylonMesh(modelMesh.Name, scene);
            int indexName = 0;

            foreach (var part in modelMesh.MeshParts)
            {
                var material = exportedMaterials.First(m => m.Name == part.Effect.GetHashCode().ToString());

                var indices = new ushort[part.PrimitiveCount * 3];
                part.IndexBuffer.GetData(part.StartIndex * 2, indices, 0, indices.Length);

                if (part.VertexBuffer.VertexDeclaration.VertexStride >= PositionNormalTexturedWeights.Stride)
                {
                    var mesh = new Mesh<PositionNormalTexturedWeights>(material);
                    var vertices = new PositionNormalTexturedWeights[part.NumVertices];

                    part.VertexBuffer.GetData(part.VertexOffset * part.VertexBuffer.VertexDeclaration.VertexStride, vertices, 0, vertices.Length, part.VertexBuffer.VertexDeclaration.VertexStride);

                    for (int index = 0; index < vertices.Length; index++)
                    {
                        vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y;
                    }

                    mesh.AddPart(indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList());
                    mesh.CreateBabylonMesh(scene, proxyID, skeleton);
                }
                else
                {
                    var mesh = new Mesh<PositionNormalTextured>(material);
                    var vertices = new PositionNormalTextured[part.NumVertices];
                    part.VertexBuffer.GetData(part.VertexOffset * PositionNormalTextured.Stride, vertices, 0, vertices.Length, PositionNormalTextured.Stride);

                    for (int index = 0; index < vertices.Length; index++)
                    {
                        vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y;
                    }

                    mesh.AddPart(indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList());
                    mesh.CreateBabylonMesh(scene, proxyID, skeleton);
                }

                indexName++;
            }
        }