using Unity.Collections;
namespace UnityEngine.Animations.Rigging
{
///
/// The TwistCorrection job.
///
[Unity.Burst.BurstCompile]
public struct TwistCorrectionJob : IWeightedAnimationJob
{
/// The Transform handle for the source object Transform.
public ReadOnlyTransformHandle source;
/// Cached inverse local rotation for source Transform.
public Quaternion sourceInverseBindRotation;
/// The local twist axis
public Vector3 axisMask;
/// List of Transform handles for the twist nodes.
public NativeArray twistTransforms;
/// List of weights for the twist nodes.
public NativeArray twistWeights;
/// List of cached local rotation for twist nodes.
public NativeArray twistBindRotations;
/// Buffer used to store weights during job execution.
public NativeArray weightBuffer;
///
public FloatProperty jobWeight { get; set; }
///
/// Defines what to do when processing the root motion.
///
/// The animation stream to work on.
public void ProcessRootMotion(AnimationStream stream) { }
///
/// Defines what to do when processing the animation.
///
/// The animation stream to work on.
public void ProcessAnimation(AnimationStream stream)
{
float w = jobWeight.Get(stream);
if (w > 0f)
{
if (twistTransforms.Length == 0)
return;
AnimationStreamHandleUtility.ReadFloats(stream, twistWeights, weightBuffer);
Quaternion twistRot = TwistRotation(axisMask, sourceInverseBindRotation * source.GetLocalRotation(stream));
Quaternion invTwistRot = Quaternion.Inverse(twistRot);
for (int i = 0; i < twistTransforms.Length; ++i)
{
ReadWriteTransformHandle twistTransform = twistTransforms[i];
float twistWeight = Mathf.Clamp(weightBuffer[i], -1f, 1f);
Quaternion rot = Quaternion.Lerp(Quaternion.identity, Mathf.Sign(twistWeight) < 0f ? invTwistRot : twistRot, Mathf.Abs(twistWeight));
twistTransform.SetLocalRotation(stream, Quaternion.Lerp(twistBindRotations[i], rot, w));
// Required to update handles with binding info.
twistTransforms[i] = twistTransform;
}
}
else
{
for (int i = 0; i < twistTransforms.Length; ++i)
AnimationRuntimeUtils.PassThrough(stream, twistTransforms[i]);
}
}
static Quaternion TwistRotation(Vector3 axis, Quaternion rot)
{
return new Quaternion(axis.x * rot.x, axis.y * rot.y, axis.z * rot.z, rot.w);
}
}
///
/// This interface defines the data mapping for TwistCorrection.
///
public interface ITwistCorrectionData
{
/// The source Transform that influences the twist nodes.
Transform source { get; }
///
/// The list of Transforms on which to apply twist corrections.
/// Each twist node has a weight that ranges from -1 to 1 to control
/// how closely a twist node follows source rotation (from 0 to 1),
/// or opposite rotation (from -1 to 0).
///
WeightedTransformArray twistNodes { get; }
/// The local twist axis of the source Transform on which to evaluate twist rotation.
Vector3 twistAxis { get; }
/// The path to the twist nodes property in the constraint component.
string twistNodesProperty { get; }
}
///
/// The TwistCorrection job binder.
///
/// The constraint data type
public class TwistCorrectionJobBinder : AnimationJobBinder
where T : struct, IAnimationJobData, ITwistCorrectionData
{
///
public override TwistCorrectionJob Create(Animator animator, ref T data, Component component)
{
var job = new TwistCorrectionJob();
job.source = ReadOnlyTransformHandle.Bind(animator, data.source);
job.sourceInverseBindRotation = Quaternion.Inverse(data.source.localRotation);
job.axisMask = data.twistAxis;
WeightedTransformArray twistNodes = data.twistNodes;
WeightedTransformArrayBinder.BindReadWriteTransforms(animator, component, twistNodes, out job.twistTransforms);
WeightedTransformArrayBinder.BindWeights(animator, component, twistNodes, data.twistNodesProperty, out job.twistWeights);
job.weightBuffer = new NativeArray(twistNodes.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
job.twistBindRotations = new NativeArray(twistNodes.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
for (int i = 0; i < twistNodes.Count; ++i)
{
var sourceTransform = twistNodes[i].transform;
job.twistBindRotations[i] = sourceTransform.localRotation;
}
return job;
}
///
public override void Destroy(TwistCorrectionJob job)
{
job.twistTransforms.Dispose();
job.twistWeights.Dispose();
job.twistBindRotations.Dispose();
job.weightBuffer.Dispose();
}
}
}