protected virtual void LateUpdate()
 {
     if (_resetCenterOfMassOnLastUpdate)
     {
         ObsCenterOfMass = JointHelper002.GetCenterOfMassRelativeToRoot(BodyParts);
         //ObsCenterOfMass = GetCenterOfMass();
         _lastCenterOfMass = ObsCenterOfMass;
         _resetCenterOfMassOnLastUpdate = false;
     }
             #if UNITY_EDITOR
     VisualizeTargetPose();
             #endif
 }
    // Prepares an animation step. Records the positions, rotations, velocities
    // of the rigid bodies forming an animation into the animation step structure.
    void UpdateAnimationStep(float timeStep)
    {
        // HACK deal with two of first frame
        if (NormalizedTime == 0f && AnimationSteps.FirstOrDefault(x => x.NormalizedTime == 0f) != null)
        {
            return;
        }

        // var c = _master.Muscles.Count;
        var c        = BodyParts.Count;
        var animStep = new AnimationStep();

        animStep.TimeStep               = timeStep;
        animStep.NormalizedTime         = NormalizedTime;
        animStep.Velocities             = Enumerable.Repeat(Vector3.zero, c).ToList();
        animStep.VelocitiesLocal        = Enumerable.Repeat(Vector3.zero, c).ToList();
        animStep.AngularVelocities      = Enumerable.Repeat(Vector3.zero, c).ToList();
        animStep.AngularVelocitiesLocal = Enumerable.Repeat(Vector3.zero, c).ToList();
        animStep.Positions              = Enumerable.Repeat(Vector3.zero, c).ToList();
        animStep.Rotations              = Enumerable.Repeat(Quaternion.identity, c).ToList();
        animStep.CenterOfMass           = JointHelper002.GetCenterOfMassRelativeToRoot(BodyParts);
        //animStep.CenterOfMass = GetCenterOfMass();

        animStep.CenterOfMassVelocity = animStep.CenterOfMass - _lastCenterOfMass;
        animStep.Names           = BodyParts.Select(x => x.Name).ToList();
        animStep.SensorIsInTouch = new List <float>(SensorIsInTouch);
        _lastCenterOfMass        = animStep.CenterOfMass;

        var rootBone = BodyParts[0];

        foreach (var bodyPart in BodyParts)
        {
            var i = BodyParts.IndexOf(bodyPart);
            if (i == 0)
            {
                animStep.Rotations[i] = Quaternion.Inverse(bodyPart.InitialRootRotation) * bodyPart.Transform.rotation;
                animStep.Positions[i] = bodyPart.Transform.position - bodyPart.InitialRootPosition;
            }
            else
            {
                animStep.Rotations[i] = Quaternion.Inverse(rootBone.Transform.rotation) * bodyPart.Transform.rotation;
                animStep.Positions[i] = bodyPart.Transform.position - rootBone.Transform.position;
            }

            if (NormalizedTime != 0f)
            {
                animStep.Velocities[i]             = (bodyPart.Transform.position - _lastPosition[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);;
                animStep.AngularVelocities[i]      = JointHelper002.CalcDeltaRotationNormalizedEuler(_lastRotation[i], bodyPart.Transform.rotation) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);;
                animStep.VelocitiesLocal[i]        = (animStep.Positions[i] - _lastPositionLocal[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);;
                animStep.AngularVelocitiesLocal[i] = JointHelper002.CalcDeltaRotationNormalizedEuler(_lastRotationLocal[i], animStep.Rotations[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);
            }

            if (bodyPart.Rigidbody != null)
            {
                bodyPart.Rigidbody.angularVelocity    = JointHelper002.CalcDeltaRotationNormalizedEuler(bodyPart.Transform.rotation, _lastRotation[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);
                bodyPart.Rigidbody.velocity           = (bodyPart.Transform.position - _lastPosition[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);
                bodyPart.Rigidbody.transform.position = bodyPart.Transform.position;
                bodyPart.Rigidbody.transform.rotation = bodyPart.Transform.rotation;
            }

            _lastPosition[i] = bodyPart.Transform.position;
            _lastRotation[i] = bodyPart.Transform.rotation;

            _lastPositionLocal[i] = animStep.Positions[i];
            _lastRotationLocal[i] = animStep.Rotations[i];
        }
        animStep.TransformPosition = transform.position;
        animStep.TransformRotation = transform.rotation;
        animStep.AngularMoment     = JointHelper002.GetAngularMoment(BodyParts);
        AnimationSteps.Add(animStep);
    }
    // Calculate values like ObsCenterOfMass, ObsAngularMoment that will be used
    // as observations fed into the neural network for training and inference. Also
    // calculates the distance metrics between animation's and agents' body parts.
    // The distances are used by the Agent to calculate rewards. The max distances
    // can be used to manually tune the denominators in exp(-distance/denominator)
    // when forming reward function.
    StyleTransfer002Animator.AnimationStep UpdateObservations()
    {
        if (DebugMode)
        {
            AnimationIndex = 0;
        }
        var debugStepIdx = AnimationIndex;

        StyleTransfer002Animator.AnimationStep animStep      = null;
        StyleTransfer002Animator.AnimationStep debugAnimStep = null;
        if (_phaseIsRunning)
        {
            debugStepIdx += DebugAnimOffset;
            if (DebugShowWithOffset)
            {
                debugStepIdx  = Mathf.Clamp(debugStepIdx, 0, _muscleAnimator.AnimationSteps.Count);
                debugAnimStep = _muscleAnimator.AnimationSteps[debugStepIdx];
            }
            animStep = _muscleAnimator.AnimationSteps[AnimationIndex];
        }
        EndEffectorDistance               = 0f;
        EndEffectorVelocityDistance       = 0;
        JointAngularVelocityDistance      = 0;
        JointAngularVelocityDistanceWorld = 0;
        RotationDistance             = 0f;
        CenterOfMassVelocityDistance = 0f;
        CenterOfMassDistance         = 0f;
        AngularMomentDistance        = 0f;
        SensorDistance = 0f;
        if (_phaseIsRunning && DebugShowWithOffset)
        {
            MimicAnimationFrame(debugAnimStep);
        }
        else if (_phaseIsRunning)
        {
            CompareAnimationFrame(animStep);
        }
        foreach (var muscle in Muscles)
        {
            var i = Muscles.IndexOf(muscle);
            muscle.UpdateObservations();
            if (!DebugShowWithOffset && !DebugDisableMotor)
            {
                muscle.UpdateMotor();
            }
            if (!muscle.Rigidbody.useGravity)
            {
                continue;                 // skip sub joints
            }
        }
        foreach (var bodyPart in BodyParts)
        {
            if (_phaseIsRunning)
            {
                bodyPart.UpdateObservations();

                var rotDistance       = bodyPart.ObsAngleDeltaFromAnimationRotation;
                var squareRotDistance = Mathf.Pow(rotDistance, 2);
                RotationDistance += squareRotDistance;

                JointAngularVelocityDistance      += bodyPart.ObsDeltaFromAnimationAngularVelocity.sqrMagnitude;
                JointAngularVelocityDistanceWorld += bodyPart.ObsDeltaFromAnimationAngularVelocityWorld.sqrMagnitude;

                if (bodyPart.Group == BodyHelper002.BodyPartGroup.Hand ||
                    bodyPart.Group == BodyHelper002.BodyPartGroup.Torso ||
                    bodyPart.Group == BodyHelper002.BodyPartGroup.Foot)
                {
                    EndEffectorDistance         += bodyPart.ObsDeltaFromAnimationPosition.sqrMagnitude;
                    EndEffectorVelocityDistance += bodyPart.ObsDeltaFromAnimationVelocity.sqrMagnitude;
                }
            }
        }

        ObsCenterOfMass  = JointHelper002.GetCenterOfMassRelativeToRoot(BodyParts);
        ObsAngularMoment = JointHelper002.GetAngularMoment(BodyParts);

        if (_phaseIsRunning)
        {
            CenterOfMassDistance  = (animStep.CenterOfMass - ObsCenterOfMass).sqrMagnitude;
            AngularMomentDistance = (animStep.AngularMoment - ObsAngularMoment).sqrMagnitude;
        }

        ObsVelocity = ObsCenterOfMass - _lastCenterOfMass;
        if (_fakeVelocity)
        {
            ObsVelocity = animStep.CenterOfMassVelocity;
        }
        _lastCenterOfMass = ObsCenterOfMass;

        if (!_resetCenterOfMassOnLastUpdate)
        {
            _fakeVelocity = false;
        }

        if (_phaseIsRunning)
        {
            var animVelocity = animStep.CenterOfMassVelocity / (Time.fixedDeltaTime * _decisionRequester.DecisionPeriod);
            ObsVelocity /= (Time.fixedDeltaTime * _decisionRequester.DecisionPeriod);

            CenterOfMassVelocityDistance = (ObsVelocity - animVelocity).sqrMagnitude;

            SensorDistance = 0.0f;
            var sensorDistanceStep = 1.0f / _agent.SensorIsInTouch.Count;
            for (int i = 0; i < _agent.SensorIsInTouch.Count; i++)
            {
                if (animStep.SensorIsInTouch[i] != _agent.SensorIsInTouch[i])
                {
                    SensorDistance += sensorDistanceStep;
                }
            }
        }

        if (!IgnorRewardUntilObservation)
        {
            MaxEndEffectorDistance               = Mathf.Max(MaxEndEffectorDistance, EndEffectorDistance);
            MaxEndEffectorVelocityDistance       = Mathf.Max(MaxEndEffectorVelocityDistance, EndEffectorVelocityDistance);
            MaxRotationDistance                  = Mathf.Max(MaxRotationDistance, RotationDistance);
            MaxCenterOfMassVelocityDistance      = Mathf.Max(MaxCenterOfMassVelocityDistance, CenterOfMassVelocityDistance);
            MaxEndEffectorVelocityDistance       = Mathf.Max(MaxEndEffectorVelocityDistance, EndEffectorVelocityDistance);
            MaxJointAngularVelocityDistance      = Mathf.Max(MaxJointAngularVelocityDistance, JointAngularVelocityDistance);
            MaxJointAngularVelocityDistanceWorld = Mathf.Max(MaxJointAngularVelocityDistanceWorld, JointAngularVelocityDistanceWorld);
            MaxCenterOfMassDistance              = Mathf.Max(MaxCenterOfMassDistance, CenterOfMassDistance);
            MaxAngularMomentDistance             = Mathf.Max(MaxAngularMomentDistance, AngularMomentDistance);
            MaxSensorDistance = Mathf.Max(MaxSensorDistance, SensorDistance);
        }

        if (IgnorRewardUntilObservation)
        {
            IgnorRewardUntilObservation = false;
        }
        ObsPhase = _muscleAnimator.AnimationSteps[AnimationIndex].NormalizedTime % 1f;
        return(animStep);
    }