public static string JsonToUnityBlendshapeName(string jsonName, JsonNamingVersion version,
                                                       string meshPrefix = null, RetargetingProfile profile = null)
        {
            string reformedShapeName = jsonName;

            if (profile != null)
            {
                var retargetNode = profile.retarget.
                                   FirstOrDefault((s) => s.jsonName == reformedShapeName);

                if (retargetNode != null)
                {
                    reformedShapeName = retargetNode.unityName;
                }
                else
                {
                    switch (profile.flag)
                    {
                    case RetargetFlag.REPLACE_R_L:
                        reformedShapeName = reformedShapeName.Replace("Right", "_R");
                        reformedShapeName = reformedShapeName.Replace("Left", "_L");
                        break;
                    }
                }
            }

            switch (version)
            {
            case JsonNamingVersion.LEGACY:
                reformedShapeName = jsonName.Replace('[', '.');
                reformedShapeName = reformedShapeName.Substring(0, reformedShapeName.Length - 1);
                break;

            case JsonNamingVersion.STANDARD:
                if (!string.IsNullOrEmpty(meshPrefix))
                {
                    reformedShapeName = meshPrefix + reformedShapeName;
                }
                break;
            }

            return(reformedShapeName);
        }
        public static AnimationClip JsonToAnimationClip(string json, Animator targetAnim,
                                                        SkinnedMeshRenderer targetMesh, string blendshapesPrefix, RetargetingProfile profile)
        {
            var parsedData = JsonToData(json);

            var   animationClip = new AnimationClip();
            float fps           = parsedData.fps != 0f ? parsedData.fps : DefaultFPS;

            animationClip.frameRate = DefaultFPS;

            var frames = parsedData.frames;

            var bsCurves = GenerateBlendshapeCurves(parsedData);
            var bsNames  = parsedData.blendShapesNames;

            for (int i = 0; i < bsNames.Length; i++)
            {
                // check if this blendshape exist
                var shapeName         = bsNames[i];
                var reformedShapeName = JsonParsing.JsonToUnityBlendshapeName(shapeName, JsonNamingVersion.STANDARD, blendshapesPrefix, profile);
                var index             = targetMesh.sharedMesh.GetBlendShapeIndex(reformedShapeName);
                if (index == -1)
                {
                    Debug.LogWarning($"Can't find blendshape {reformedShapeName} in {targetMesh} GameObject. " +
                                     $"Check blendshape prefix or change model blendshape name");
                    continue;
                }

                // check if atl used this blendshape
                if (!HasAnimationFrames(frames, i))
                {
                    continue;
                }

                var animationCurve = bsCurves[i];
                var unityShapeName = "blendShape." + reformedShapeName;

                var objPath = TraceParent(targetMesh.transform, targetAnim.transform);
                animationClip.SetCurve(objPath, typeof(SkinnedMeshRenderer), unityShapeName, animationCurve);
            }

            var boneCurves = GenerateBonesCurves(parsedData, targetAnim);
            var boneNames  = parsedData.bonesNames;

            for (int i = 0; i < boneCurves.Length; i++)
            {
                var boneName  = boneNames[i];
                var boneCurve = boneCurves[i];

                var targetBone = FindBone(targetAnim.transform, boneName);
                if (targetBone == null)
                {
                    continue;
                }

                var objPath = TraceParent(targetBone.transform, targetAnim.transform);

                animationClip.SetCurve(objPath, typeof(Transform), "localRotation.x", boneCurve.rotation[0]);
                animationClip.SetCurve(objPath, typeof(Transform), "localRotation.y", boneCurve.rotation[1]);
                animationClip.SetCurve(objPath, typeof(Transform), "localRotation.z", boneCurve.rotation[2]);
                animationClip.SetCurve(objPath, typeof(Transform), "localRotation.w", boneCurve.rotation[3]);
            }

            animationClip.EnsureQuaternionContinuity();
            return(animationClip);
        }