using Unity.Collections; namespace UnityEngine.Animations.Rigging { /// /// The MultiReferential constraint job. /// [Unity.Burst.BurstCompile] public struct MultiReferentialConstraintJob : IWeightedAnimationJob { /// The driver index. public IntProperty driver; /// The list of Transforms that are affected by the specified driver. public NativeArray sources; /// Cache of AffineTransform representing the source objects initial positions. public NativeArray sourceBindTx; /// List of AffineTransform to apply to driven source objects. public NativeArray offsetTx; private int m_PrevDriverIdx; /// 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) { var driverIdx = driver.Get(stream); if (driverIdx != m_PrevDriverIdx) UpdateOffsets(driverIdx); sources[driverIdx].GetGlobalTR(stream, out Vector3 driverWPos, out Quaternion driverWRot); var driverTx = new AffineTransform(driverWPos, driverWRot); int offset = 0; for (int i = 0; i < sources.Length; ++i) { if (i == driverIdx) continue; var tx = driverTx * offsetTx[offset]; var src = sources[i]; src.GetGlobalTR(stream, out Vector3 srcWPos, out Quaternion srcWRot); src.SetGlobalTR(stream, Vector3.Lerp(srcWPos, tx.translation, w), Quaternion.Lerp(srcWRot, tx.rotation, w)); offset++; sources[i] = src; } AnimationRuntimeUtils.PassThrough(stream, sources[driverIdx]); } else { for (int i = 0; i < sources.Length; ++i) AnimationRuntimeUtils.PassThrough(stream, sources[i]); } } internal void UpdateOffsets(int driver) { driver = Mathf.Clamp(driver, 0, sources.Length - 1); int offset = 0; var invDriverTx = sourceBindTx[driver].Inverse(); for (int i = 0; i < sourceBindTx.Length; ++i) { if (i == driver) continue; offsetTx[offset] = invDriverTx * sourceBindTx[i]; offset++; } m_PrevDriverIdx = driver; } } /// /// This interface defines the data mapping for the MultiReferential constraint. /// public interface IMultiReferentialConstraintData { /// The driver index. This is a value in between 0 and the number of sourceObjects. int driverValue { get; } /// The path to the driver property in the constraint component. string driverIntProperty { get; } /// The list of Transforms that can act as drivers for the constrained Transform. Transform[] sourceObjects { get; } } /// /// The MultiReferential constraint job binder. /// /// The constraint data type public class MultiReferentialConstraintJobBinder : AnimationJobBinder where T : struct, IAnimationJobData, IMultiReferentialConstraintData { /// public override MultiReferentialConstraintJob Create(Animator animator, ref T data, Component component) { var job = new MultiReferentialConstraintJob(); var sources = data.sourceObjects; job.driver = IntProperty.Bind(animator, component, data.driverIntProperty); job.sources = new NativeArray(sources.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); job.sourceBindTx = new NativeArray(sources.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); job.offsetTx = new NativeArray(sources.Length - 1, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); for (int i = 0; i < sources.Length; ++i) { job.sources[i] = ReadWriteTransformHandle.Bind(animator, sources[i].transform); job.sourceBindTx[i] = new AffineTransform(sources[i].position, sources[i].rotation); } job.UpdateOffsets(data.driverValue); return job; } /// public override void Destroy(MultiReferentialConstraintJob job) { job.sources.Dispose(); job.sourceBindTx.Dispose(); job.offsetTx.Dispose(); } } }