// Copyright Unity Technologies 2019 // https://github.com/Unity-Technologies/animation-jobs-samples //
// The original file can be downloaded from https://github.com/Unity-Technologies/animation-jobs-samples/blob/master/Assets/animation-jobs-samples/Runtime/AnimationJobs/DampingJob.cs
// This file has been modified:
// - Moved into the Animancer.Samples.Jobs namespace.
// - Removed the contents of ProcessRootMotion since it is unnecessary.
#pragma warning disable IDE0054 // Use compound assignment
using Unity.Collections;
using UnityEngine;
using UnityEngine.Animations;
namespace Animancer.Samples.Jobs
{
/// An which executes a simple damping effect.
///
///
/// Sample:
///
/// Damping
///
///
/// https://kybernetik.com.au/animancer/api/Animancer.Samples.Jobs/DampingJob
///
public struct DampingJob : IAnimationJob
{
public TransformStreamHandle rootHandle;
public NativeArray jointHandles;
public NativeArray localPositions;
public NativeArray localRotations;
public NativeArray positions;
public NativeArray velocities;
///
/// Transfer the root position and rotation through the graph.
///
/// The animation stream
public readonly void ProcessRootMotion(AnimationStream stream)
{
// This was in the original sample, but it causes problems if the character is a child of a moving object.
// There is no need for this method to do anything in order to support root motion.
//// Get root position and rotation.
//var rootPosition = rootHandle.GetPosition(stream);
//var rootRotation = rootHandle.GetRotation(stream);
//// The root always follow the given position and rotation.
//rootHandle.SetPosition(stream, rootPosition);
//rootHandle.SetRotation(stream, rootRotation);
}
///
/// Procedurally generate the joints rotation.
///
/// The animation stream
public void ProcessAnimation(AnimationStream stream)
{
if (jointHandles.Length < 2)
return;
ComputeDampedPositions(stream);
ComputeJointLocalRotations(stream);
}
///
/// Compute the new global positions of the joints.
///
/// The position of the first joint is driven by the root's position, and
/// then the other joints positions are recomputed in order to follow their
/// initial local positions, smoothly.
///
/// Algorithm breakdown:
/// 1. Compute the target position;
/// 2. Damp this target position based on the current position;
/// 3. Constrain the damped position to the joint initial length;
/// 4. Iterate on the next joint.
///
/// The animation stream
private void ComputeDampedPositions(AnimationStream stream)
{
// Get root position and rotation.
var rootPosition = rootHandle.GetPosition(stream);
var rootRotation = rootHandle.GetRotation(stream);
// The first non-root joint follows the root position,
// but its rotation is damped (see ComputeJointLocalRotations).
var parentPosition = rootPosition + rootRotation * localPositions[0];
var parentRotation = rootRotation * localRotations[0];
positions[0] = parentPosition;
for (var i = 1; i < jointHandles.Length; ++i)
{
// The target position is the global position, without damping.
var newPosition = parentPosition + (parentRotation * localPositions[i]);
// Apply damping on this target.
var velocity = velocities[i];
newPosition = Vector3.SmoothDamp(positions[i], newPosition, ref velocity, 0.15f, Mathf.Infinity, stream.deltaTime);
// Apply constraint: keep original length between joints.
newPosition = parentPosition + (newPosition - parentPosition).normalized * localPositions[i].magnitude;
// Save new velocity and position for next frame.
velocities[i] = velocity;
positions[i] = newPosition;
// Current joint is now the parent of the next joint.
parentPosition = newPosition;
parentRotation = parentRotation * localRotations[i];
}
}
///
/// Compute the new local rotations of the joints.
///
/// Based on the global positions computed in ComputeDampedPositions,
/// recompute the local rotation of each joint.
///
/// Algorithm breakdown:
/// 1. Compute the rotation between the current and new directions of the joint;
/// 2. Apply this rotation on the current joint rotation;
/// 3. Compute the local rotation and set it in the stream;
/// 4. Iterate on the next joint.
///
/// The animation stream
private void ComputeJointLocalRotations(AnimationStream stream)
{
var parentRotation = rootHandle.GetRotation(stream);
for (var i = 0; i < jointHandles.Length - 1; ++i)
{
// Get the current joint rotation.
var rotation = parentRotation * localRotations[i];
// Get the current joint direction.
var direction = (rotation * localPositions[i + 1]).normalized;
// Get the wanted joint direction.
var newDirection = (positions[i + 1] - positions[i]).normalized;
// Compute the rotation from the current direction to the new direction.
var currentToNewRotation = Quaternion.FromToRotation(direction, newDirection);
// Pre-rotate the current rotation, to get the new global rotation.
rotation = currentToNewRotation * rotation;
// Set the new local rotation.
var newLocalRotation = Quaternion.Inverse(parentRotation) * rotation;
jointHandles[i].SetLocalRotation(stream, newLocalRotation);
// Set the new parent for the next joint.
parentRotation = rotation;
}
}
}
}