Example #1
0
        private static void ExportTransformAnimationClipData(GameObject source, Transform transform, BabylonIAnimatable animatable, UnityEditor.AnimationState animationState, ref List <AnimationClip> states, ref UnityMetaData metaData, Animator animator)
        {
            ExporterWindow.ReportProgress(1, "Exporting transform clips: " + transform.gameObject.name);
            int                     frameRate       = 0;
            int                     firstClipEnd    = 0;
            int                     totalFrameCount = 0;
            List <string>           stateNameCache  = new List <string>();
            List <BabylonAnimation> animations      = new List <BabylonAnimation>();

            var positionX = new List <BabylonAnimationKey>();
            var positionY = new List <BabylonAnimationKey>();
            var positionZ = new List <BabylonAnimationKey>();

            var rotationX = new List <BabylonAnimationKey>();
            var rotationY = new List <BabylonAnimationKey>();
            var rotationZ = new List <BabylonAnimationKey>();
            var rotationW = new List <BabylonAnimationKey>();

            var scaleX = new List <BabylonAnimationKey>();
            var scaleY = new List <BabylonAnimationKey>();
            var scaleZ = new List <BabylonAnimationKey>();

            int   frameOffest   = 0;
            float playbackSpeed = (animationState != null) ? animationState.playbackSpeed : 1.0f;

            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, "Transforming: " + transform.gameObject.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", "transform");
                    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);
                }

                // Animation Curve Bindings
                var curveBindings = AnimationUtility.GetCurveBindings(clip);
                foreach (var binding in curveBindings)
                {
                    var curve = AnimationUtility.GetEditorCurve(clip, binding);
                    switch (binding.propertyName)
                    {
                    //Position
                    case "m_LocalPosition.x":
                        IEnumerable <BabylonAnimationKey> px_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        positionX.AddRange(px_keys);
                        break;

                    case "m_LocalPosition.y":
                        IEnumerable <BabylonAnimationKey> py_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        positionY.AddRange(py_keys);
                        break;

                    case "m_LocalPosition.z":
                        IEnumerable <BabylonAnimationKey> pz_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        positionZ.AddRange(pz_keys);
                        break;

                    // Rotation
                    case "localEulerAnglesRaw.x":
                        IEnumerable <BabylonAnimationKey> rx_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value *(float)Math.PI / 180 }
                        });
                        rotationX.AddRange(rx_keys);
                        break;

                    case "localEulerAnglesRaw.y":
                        IEnumerable <BabylonAnimationKey> ry_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value *(float)Math.PI / 180 }
                        });
                        rotationY.AddRange(ry_keys);
                        break;

                    case "localEulerAnglesRaw.z":
                        IEnumerable <BabylonAnimationKey> rz_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value *(float)Math.PI / 180 }
                        });
                        rotationZ.AddRange(rz_keys);
                        break;

                    case "localEulerAnglesRaw.w":
                        IEnumerable <BabylonAnimationKey> rw_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value *(float)Math.PI / 180 }
                        });
                        rotationW.AddRange(rw_keys);
                        break;

                    // Scaling
                    case "m_LocalScale.x":
                        IEnumerable <BabylonAnimationKey> sx_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        scaleX.AddRange(sx_keys);
                        break;

                    case "m_LocalScale.y":
                        IEnumerable <BabylonAnimationKey> sy_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        scaleY.AddRange(sy_keys);
                        break;

                    case "m_LocalScale.z":
                        IEnumerable <BabylonAnimationKey> sz_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        scaleZ.AddRange(sz_keys);
                        break;

                    default:
                        continue;
                    }
                }
                frameOffest     += clipFrameCount;
                totalFrameCount += clipFrameCount;
            }

            // Position properties
            string property = "none";

            if (positionX.Count > 0)
            {
                property = "position.x";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = positionX.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }
            property = "none";
            if (positionY.Count > 0)
            {
                property = "position.y";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = positionY.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }
            property = "none";
            if (positionZ.Count > 0)
            {
                property = "position.z";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = positionZ.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }

            // Rotation properties
            property = "none";
            if (rotationX.Count > 0)
            {
                property = "rotation.x";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = rotationX.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }
            property = "none";
            if (rotationY.Count > 0)
            {
                property = "rotation.y";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = rotationY.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }
            property = "none";
            if (rotationZ.Count > 0)
            {
                property = "rotation.z";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = rotationZ.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }
            property = "none";
            if (rotationW.Count > 0)
            {
                property = "rotation.w";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = rotationW.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }

            // Scale properties
            property = "none";
            if (scaleX.Count > 0)
            {
                property = "scaling.x";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = scaleX.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }
            property = "none";
            if (scaleY.Count > 0)
            {
                property = "scaling.y";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = scaleY.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }
            property = "none";
            if (scaleZ.Count > 0)
            {
                property = "scaling.z";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = property + " animation",
                    keys           = scaleZ.ToArray(),
                    framePerSecond = frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Relative,
                    property       = property
                });
            }
            if (animations.Count > 0)
            {
                animatable.animations = animations.ToArray();
            }
        }
        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);
                }
            }
        }
Example #3
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 static void ExportTransformAnimationClipData(GameObject source, Transform transform, BabylonIAnimatable animatable, ref List <AnimationClip> clips, ref UnityMetaData metaData, List <UnityEditor.AnimationParameters> aparams)
        {
            ExporterWindow.ReportProgress(1, "Baking " + transform.gameObject.name.ToLower() + " transform... This may take a while.");
            string sourceId  = GetID(source);
            int    frameRate = 0;
            var    anims     = new List <BabylonAnimation>();
            List <BabylonRange>     ranges         = new List <BabylonRange>();
            List <string>           stateNameCache = new List <string>();
            List <BabylonAnimation> animations     = new List <BabylonAnimation>();

            var positionX = new List <BabylonAnimationKey>();
            var positionY = new List <BabylonAnimationKey>();
            var positionZ = new List <BabylonAnimationKey>();

            var rotationX = new List <BabylonAnimationKey>();
            var rotationY = new List <BabylonAnimationKey>();
            var rotationZ = new List <BabylonAnimationKey>();

            var quaternionX = new List <BabylonAnimationKey>();
            var quaternionY = new List <BabylonAnimationKey>();
            var quaternionZ = new List <BabylonAnimationKey>();
            var quaternionW = new List <BabylonAnimationKey>();

            var scalingX = new List <BabylonAnimationKey>();
            var scalingY = new List <BabylonAnimationKey>();
            var scalingZ = new List <BabylonAnimationKey>();

            int frameOffest = 0;

            foreach (var clip in clips)
            {
                if (clip == null)
                {
                    continue;
                }
                string clipName = FormatSafeClipName(clip.name);
                BabylonLoopBehavior behavior = (clip.wrapMode == WrapMode.Loop) ? BabylonLoopBehavior.Cycle : BabylonLoopBehavior.Constant;
                if (clip.wrapMode == WrapMode.PingPong)
                {
                    UnityEngine.Debug.LogWarning("PingPong transform animation wrap mode not supported for clip: " + clip.name);
                }
                // ..
                // Sample Animation Frame
                // ..
                if (frameRate <= 0)
                {
                    frameRate = (int)clip.frameRate;
                }
                int clipFrameCount = (int)(clip.length * clip.frameRate);
                int lastFrameCount = clipFrameCount - 1;
                // ..
                // Animation Curve Bindings
                // ..
                var curveBindings = AnimationUtility.GetCurveBindings(clip);
                foreach (var binding in curveBindings)
                {
                    var curve = AnimationUtility.GetEditorCurve(clip, binding);
                    switch (binding.propertyName)
                    {
                    //Positions
                    case "m_LocalPosition.x":
                        IEnumerable <BabylonAnimationKey> px_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        positionX.AddRange(px_keys);
                        break;

                    case "m_LocalPosition.y":
                        IEnumerable <BabylonAnimationKey> py_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        positionY.AddRange(py_keys);
                        break;

                    case "m_LocalPosition.z":
                        IEnumerable <BabylonAnimationKey> pz_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        positionZ.AddRange(pz_keys);
                        break;

                    // Rotations
                    case "localEuler.x":
                    case "localEulerAnglesRaw.x":
                    case "localEulerAnglesBaked.x":
                        IEnumerable <BabylonAnimationKey> rx_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value *(float)Math.PI / 180 }
                        });
                        rotationX.AddRange(rx_keys);
                        break;

                    case "localEuler.y":
                    case "localEulerAnglesRaw.y":
                    case "localEulerAnglesBaked.y":
                        IEnumerable <BabylonAnimationKey> ry_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value *(float)Math.PI / 180 }
                        });
                        rotationY.AddRange(ry_keys);
                        break;

                    case "localEuler.z":
                    case "localEulerAnglesRaw.z":
                    case "localEulerAnglesBaked.z":
                        IEnumerable <BabylonAnimationKey> rz_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value *(float)Math.PI / 180 }
                        });
                        rotationZ.AddRange(rz_keys);
                        break;

                    // Quaternions
                    case "localRotation.x":
                    case "m_LocalRotation.x":
                        IEnumerable <BabylonAnimationKey> qx_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        quaternionX.AddRange(qx_keys);
                        break;

                    case "localRotation.y":
                    case "m_LocalRotation.y":
                        IEnumerable <BabylonAnimationKey> qy_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        quaternionY.AddRange(qy_keys);
                        break;

                    case "localRotation.z":
                    case "m_LocalRotation.z":
                        IEnumerable <BabylonAnimationKey> qz_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        quaternionZ.AddRange(qz_keys);
                        break;

                    case "localRotation.w":
                    case "m_LocalRotation.w":
                        IEnumerable <BabylonAnimationKey> qw_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        quaternionW.AddRange(qw_keys);
                        break;

                    // Scaling
                    case "m_LocalScale.x":
                        IEnumerable <BabylonAnimationKey> sx_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        scalingX.AddRange(sx_keys);
                        break;

                    case "m_LocalScale.y":
                        IEnumerable <BabylonAnimationKey> sy_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        scalingY.AddRange(sy_keys);
                        break;

                    case "m_LocalScale.z":
                        IEnumerable <BabylonAnimationKey> sz_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey {
                            frame  = (int)((keyFrame.time * frameRate) + frameOffest),
                            values = new[] { keyFrame.value }
                        });
                        scalingZ.AddRange(sz_keys);
                        break;

                    default:
                        continue;
                    }
                }
                // ..
                // 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", "transform");
                    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 = "custom:" + 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;
            }

            // Position properties
            string prefix   = "transform:";
            string suffix   = ":animation";
            string property = "none";

            if (positionX.Count > 0)
            {
                property = "position.x";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = positionX.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (positionY.Count > 0)
            {
                property = "position.y";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = positionY.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (positionZ.Count > 0)
            {
                property = "position.z";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = positionZ.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }

            // Rotation properties
            property = "none";
            if (rotationX.Count > 0)
            {
                property = "rotation.x";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = rotationX.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (rotationY.Count > 0)
            {
                property = "rotation.y";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = rotationY.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (rotationZ.Count > 0)
            {
                property = "rotation.z";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = rotationZ.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }

            // Quaternion properties
            property = "none";
            if (quaternionX.Count > 0)
            {
                property = "rotationQuaternion.x";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = quaternionX.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (quaternionY.Count > 0)
            {
                property = "rotationQuaternion.y";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = quaternionY.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (quaternionZ.Count > 0)
            {
                property = "rotationQuaternion.z";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = quaternionZ.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (quaternionW.Count > 0)
            {
                property = "rotationQuaternion.w";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = quaternionW.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }

            // Scaling properties
            property = "none";
            if (scalingX.Count > 0)
            {
                property = "scaling.x";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = scalingX.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (scalingY.Count > 0)
            {
                property = "scaling.y";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = scalingY.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            property = "none";
            if (scalingZ.Count > 0)
            {
                property = "scaling.z";
                animations.Add(new BabylonAnimation
                {
                    dataType       = (int)BabylonAnimation.DataType.Float,
                    name           = prefix + property.ToLower() + suffix,
                    keys           = scalingZ.ToArray(),
                    framePerSecond = (int)frameRate,
                    enableBlending = false,
                    blendingSpeed  = 0.0f,
                    loopBehavior   = (int)BabylonAnimation.LoopBehavior.Cycle,
                    property       = property
                });
            }
            // Serialize animations
            if (animations.Count > 0)
            {
                animatable.animations = animations.ToArray();
            }
            //
            // 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);
                }
            }
        }