//-------------------------------------------------------------------------
        // Clip Mixing
        //-------------------------------------------------------------------------

        /// <summary>
        /// Mix a single clip.
        /// </summary>
        /// <param name="playable">The track's playable</param>
        /// <param name="player">The player character</param>
        /// <param name="clipIndex">The index of the clip to mix</param>
        private void MixSingle(Playable playable, PlayerCharacter player, int clipIndex)
        {
            PoseInfo pose = TimelineTools.GetClipInfo <PoseInfo>(playable, clipIndex);

            player.gameObject.SetActive(pose.Active);

            if (PoseTools.IsAbsolute(pose)) // Absolute
            {
                MixAbsolute(player, (AbsolutePoseInfo)pose);
            }
            else // Relative
            {
                VirtualSnapshot.RestoreTransform(player);
                MixRelative(player, (RelativePoseInfo)pose);
            }

            StateDriver driver = GetDriver(pose);

            UpdateFacing(player, pose);
            UpdateState(player, driver, playable, pose);
        }
        //-------------------------------------------------------------------------
        // Playable API
        //-------------------------------------------------------------------------

        /// <summary>
        /// Process a single frame of animation.
        /// </summary>
        /// <param name="playable">The playable this mixer is for.</param>
        /// <param name="info">Extra information about this frame.</param>
        /// <param name="playerData">Data bound to the track that this mixer is on.</param>
        public override void ProcessFrame(Playable playable, FrameData info, object playerData)
        {
            PlayerCharacter player = playerData != null ? (PlayerCharacter)playerData : GameManager.Player;

            if (player == null)
            {
                player = GameObject.FindObjectOfType <PlayerCharacter>(true);
            }

            if (TimelineTools.IsSingleClipPlaying(playable, out int clipIndex))
            {
                MixSingle(playable, player, clipIndex);
            }
            else
            {
                TimelineTools.FindClipsToMix(playable, out int clipIndexA, out int clipIndexB);
                if (TimelineTools.ClipsAreValid(clipIndexA, clipIndexB))
                {
                    MixMultiple(playable, player, clipIndexA, clipIndexB);
                }
            }
        }
        /// <summary>
        /// Mix together two active clips based on what type of clips they are.
        /// </summary>
        /// <param name="playable">The track's playable</param>
        /// <param name="player">The player character</param>
        /// <param name="clipIndexA">The index of the first clip to mix</param>
        /// <param name="clipIndexB">The index of the second clip to mix</param>
        private void MixMultiple(Playable playable, PlayerCharacter player, int clipIndexA, int clipIndexB)
        {
            PoseInfo poseA   = TimelineTools.GetClipInfo <PoseInfo>(playable, clipIndexA);
            float    weightA = playable.GetInputWeight(clipIndexA);

            PoseInfo poseB   = TimelineTools.GetClipInfo <PoseInfo>(playable, clipIndexB);
            float    weightB = playable.GetInputWeight(clipIndexB);

            // Mix together clips based on their typing.
            if (PoseTools.IsAbsolute(poseA) && PoseTools.IsAbsolute(poseB))
            {
                MixAbsolute(player, (AbsolutePoseInfo)poseA, weightA);
                MixAbsolute(player, (AbsolutePoseInfo)poseB, weightB);
            }
            else if (PoseTools.IsRelative(poseA) && PoseTools.IsRelative(poseB))
            {
                VirtualSnapshot.RestoreTransform(player);

                RelativePoseInfo mixed = new RelativePoseInfo();
                mixed.Position = poseA.Position * weightA + poseB.Position * weightB;
                mixed.Rotation = poseA.Rotation * weightA + poseB.Rotation * weightB;
                mixed.Scale    = poseA.Scale * weightA + poseB.Scale * weightB;

                MixRelative(player, mixed);
            }
            else
            {
                MixAbsoluteRelative(player, poseA, weightA, poseB, weightB);
            }

            // Apply player facing and FSM state based on which pose is weighted heavier.
            PoseInfo dominantPose = weightA > weightB ? poseA : poseB;

            player.gameObject.SetActive(dominantPose.Active);
            StateDriver driver = GetDriver(dominantPose);

            UpdateFacing(player, dominantPose);
            UpdateState(player, driver, playable, dominantPose);
        }