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(); } } }