private static void GenerateClip(List <SkinBone> bones, AnimationClip clip, string file)
    {
        EditorCurveBinding[] curveDatas = AnimationUtility.GetCurveBindings(clip);

        float        frequency   = 1.0f / 30.0f;
        int          keyframeLen = (int)Mathf.Ceil(clip.length / frequency) + 1;
        List <float> times       = new List <float>();

        for (int i = 0; i < keyframeLen; ++i)
        {
            times.Add(i * frequency >= clip.length ? clip.length : i * frequency);
        }

        SkinAnimationClip newClip = ScriptableObject.CreateInstance <SkinAnimationClip>();

        newClip.frequency = frequency;
        newClip.times     = times.ToArray();
        newClip.datas     = new float[bones.Count * keyframeLen * SkinAnimationClip.STRIDE];
        newClip.length    = clip.length;
        newClip.animName  = clip.name;
        newClip.nodeCount = bones.Count;

        Dictionary <string, int> boneIndexMap = new Dictionary <string, int>();

        for (int i = 0; i < bones.Count; ++i)
        {
            SkinBone   bone = bones[i];
            Vector3    pos  = bone.transform.localPosition;
            Quaternion rot  = bone.transform.localRotation;

            int frameIndex = i * keyframeLen * SkinAnimationClip.STRIDE;
            for (int j = 0; j < keyframeLen; ++j)
            {
                int curveIndex = j * SkinAnimationClip.STRIDE;
                newClip.datas[frameIndex + curveIndex + 0] = pos.x;
                newClip.datas[frameIndex + curveIndex + 1] = pos.y;
                newClip.datas[frameIndex + curveIndex + 2] = pos.z;

                newClip.datas[frameIndex + curveIndex + 3] = rot.x;
                newClip.datas[frameIndex + curveIndex + 4] = rot.y;
                newClip.datas[frameIndex + curveIndex + 5] = rot.z;
                newClip.datas[frameIndex + curveIndex + 6] = rot.w;
            }

            boneIndexMap[bone.transform.name] = i;
        }

        float[] values = new float[keyframeLen];

        for (int i = 0; i < curveDatas.Length; ++i)
        {
            EditorCurveBinding binding = curveDatas[i];
            string             name    = binding.path.Substring(binding.path.LastIndexOf('/') + 1);
            AnimationCurve     curve   = AnimationUtility.GetEditorCurve(clip, binding);

            int offset = -1;
            switch (binding.propertyName)
            {
            case "m_LocalPosition.x": offset = 0; break;

            case "m_LocalPosition.y": offset = 1; break;

            case "m_LocalPosition.z": offset = 2; break;

            case "m_LocalRotation.x": offset = 3; break;

            case "m_LocalRotation.y": offset = 4; break;

            case "m_LocalRotation.z": offset = 5; break;

            case "m_LocalRotation.w": offset = 6; break;
            }

            if (offset == -1)
            {
                continue;
            }

            if (!boneIndexMap.ContainsKey(name))
            {
                continue;
            }

            for (int j = 0; j < keyframeLen; ++j)
            {
                values[j] = curve.Evaluate(times[j]);
            }

            int frameIndex = boneIndexMap[name] * keyframeLen * SkinAnimationClip.STRIDE;
            for (int j = 0; j < keyframeLen; ++j)
            {
                int curveIndex = j * SkinAnimationClip.STRIDE;
                newClip.datas[frameIndex + curveIndex + offset] = values[j];
            }
        }

        string basePath = Path.GetDirectoryName(file) + "/" + Path.GetFileNameWithoutExtension(file);

        string clipPath = basePath + clip.name + ".anim.asset";

        AssetDatabase.CreateAsset(newClip, clipPath);

        AnimationClip  emptyClip  = new AnimationClip();
        AnimationCurve emptyCurve = new AnimationCurve();

        emptyCurve.AddKey(new Keyframe(0.0f, 0.0f));
        emptyCurve.AddKey(new Keyframe(clip.length, clip.length));
        emptyClip.SetCurve("", typeof(SkinFloat), "value", emptyCurve);
        AssetDatabase.CreateAsset(emptyClip, basePath + clip.name + ".anim");
    }
    private float[] CalculateCurrentPose()
    {
        SkinAnimationClip currClip = null;
        float             currBias = 1.0f;
        float             currTime = 0.0f;

        if (m_Animator.GetCurrentAnimatorClipInfoCount(0) > 0)
        {
            var clipInfo  = m_Animator.GetCurrentAnimatorClipInfo(0)[0];
            var stateInfo = m_Animator.GetCurrentAnimatorStateInfo(0);
            currTime = stateInfo.normalizedTime * clipInfo.clip.length;
            currClip = m_ClipsMap[clipInfo.clip.name];
            currBias = clipInfo.weight;
        }

        SkinAnimationClip nextClip = null;
        float             nextBias = 1.0f;
        float             nextTime = 0.0f;

        if (m_Animator.GetNextAnimatorClipInfoCount(0) > 0)
        {
            var clipInfo  = m_Animator.GetNextAnimatorClipInfo(0)[0];
            var stateInfo = m_Animator.GetNextAnimatorStateInfo(0);
            nextTime = stateInfo.normalizedTime * clipInfo.clip.length;
            nextClip = m_ClipsMap[clipInfo.clip.name];
            nextBias = clipInfo.weight;
        }

        float[] finalPose = null;

        if (currClip != null && currBias > 0.0f)
        {
            currClip.Evaluate(Mathf.Repeat(currTime, currClip.length), TEMP_POSE_DATA0, false);
            finalPose = TEMP_POSE_DATA0;
        }

        if (nextClip != null && nextBias > 0.0f)
        {
            nextClip.Evaluate(Mathf.Repeat(nextTime, nextClip.length), TEMP_POSE_DATA1, false);
            finalPose = TEMP_POSE_DATA1;
        }

        if ((currClip != null && currBias > 0.0f) && (nextClip != null && nextBias > 0.0f))
        {
            float total = currBias + nextBias;
            currBias /= total;
            nextBias /= total;
            finalPose = TEMP_POSE_DATA0;
            for (int i = 1; i < avatar.bonesName.Length; ++i)
            {
                int idx = i * SkinAnimationClip.STRIDE;
                for (int j = 0; j < SkinAnimationClip.STRIDE; ++j)
                {
                    float a = TEMP_POSE_DATA0[idx + j];
                    float b = TEMP_POSE_DATA1[idx + j];
                    finalPose[idx + j] = a * currBias + b * nextBias;
                }
            }
        }

        return(finalPose);
    }