namespace UnityEngine.Animations.Rigging { /// /// The OverrideTransform job. /// [Unity.Burst.BurstCompile] public struct OverrideTransformJob : IWeightedAnimationJob { /// /// The override space controls how the override source Transform /// is copied unto constrained Transform. /// public enum Space { /// Copy override world TR components into world TR components of the constrained Transform. World = 0, /// Copy override local TR components into local TR components of the constrained Transform. Local = 1, /// Add override local TR components to local TR components of the constrained Transform. Pivot = 2 } /// The Transform handle for the constrained object Transform. public ReadWriteTransformHandle driven; /// The Transform handle for the override source object Transform. public ReadOnlyTransformHandle source; /// Cached inverse local TR used in space switching calculations. public AffineTransform sourceInvLocalBindTx; /// Cached source to world coordinates quaternion. Used when Space is set to World. public Quaternion sourceToWorldRot; /// Cached source to local coordinates quaternion. Used when Space is set to Local. public Quaternion sourceToLocalRot; /// Cached source to pivot coordinates quaternion. Used when Space is set to Pivot. public Quaternion sourceToPivotRot; /// CacheIndex to the override space. /// public CacheIndex spaceIdx; /// CacheIndex to source to space quaternion used in current space. /// public CacheIndex sourceToCurrSpaceRotIdx; /// The override position. public Vector3Property position; /// The override rotation. public Vector3Property rotation; /// The weight for which override position has an effect on the constrained Transform. This is a value in between 0 and 1. public FloatProperty positionWeight; /// The weight for which override rotation has an effect on the constrained Transform. This is a value in between 0 and 1. public FloatProperty rotationWeight; /// Cache for static properties in the job. public AnimationJobCache cache; /// 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) { AffineTransform overrideTx; if (source.IsValid(stream)) { source.GetLocalTRS(stream, out Vector3 srcLPos, out Quaternion srcLRot, out _); var sourceLocalTx = new AffineTransform(srcLPos, srcLRot); var sourceToSpaceRot = cache.Get(sourceToCurrSpaceRotIdx); overrideTx = Quaternion.Inverse(sourceToSpaceRot) * (sourceInvLocalBindTx * sourceLocalTx) * sourceToSpaceRot; } else overrideTx = new AffineTransform(position.Get(stream), Quaternion.Euler(rotation.Get(stream))); Space overrideSpace = (Space)cache.GetRaw(spaceIdx); var posW = positionWeight.Get(stream) * w; var rotW = rotationWeight.Get(stream) * w; switch (overrideSpace) { case Space.World: { driven.GetGlobalTR(stream, out Vector3 drivenWPos, out Quaternion drivenWRot); driven.SetGlobalTR( stream, Vector3.Lerp(drivenWPos, overrideTx.translation, posW), Quaternion.Lerp(drivenWRot, overrideTx.rotation, rotW) ); } break; case Space.Local: { driven.GetLocalTRS(stream, out Vector3 drivenLPos, out Quaternion drivenLRot, out Vector3 drivenLScale); driven.SetLocalTRS( stream, Vector3.Lerp(drivenLPos, overrideTx.translation, posW), Quaternion.Lerp(drivenLRot, overrideTx.rotation, rotW), drivenLScale ); } break; case Space.Pivot: { driven.GetLocalTRS(stream, out Vector3 drivenLPos, out Quaternion drivenLRot, out Vector3 drivenLScale); var drivenLocalTx = new AffineTransform(drivenLPos, drivenLRot); overrideTx = drivenLocalTx * overrideTx; driven.SetLocalTRS( stream, Vector3.Lerp(drivenLocalTx.translation, overrideTx.translation, posW), Quaternion.Lerp(drivenLocalTx.rotation, overrideTx.rotation, rotW), drivenLScale ); } break; default: break; } } else AnimationRuntimeUtils.PassThrough(stream, driven); } internal void UpdateSpace(int space) { if ((int)cache.GetRaw(spaceIdx) == space) return; cache.SetRaw(space, spaceIdx); Space currSpace = (Space)space; if (currSpace == Space.Pivot) cache.Set(sourceToPivotRot, sourceToCurrSpaceRotIdx); else if (currSpace == Space.Local) cache.Set(sourceToLocalRot, sourceToCurrSpaceRotIdx); else cache.Set(sourceToWorldRot, sourceToCurrSpaceRotIdx); } } /// /// This interface defines the data mapping for OverrideTransform. /// public interface IOverrideTransformData { /// The Transform affected by the constraint source Transform. Transform constrainedObject { get; } /// The override source Transform. Transform sourceObject { get; } /// The override space. int space { get; } /// The path to the position weight property in the constraint component. string positionWeightFloatProperty { get; } /// The path to the rotation weight property in the constraint component. string rotationWeightFloatProperty { get; } /// The path to the override position property in the constraint component. string positionVector3Property { get; } /// The path to the override rotation property in the constraint component. string rotationVector3Property { get; } } /// /// The OverrideTransform job binder. /// /// The constraint data type public class OverrideTransformJobBinder : AnimationJobBinder where T : struct, IAnimationJobData, IOverrideTransformData { /// public override OverrideTransformJob Create(Animator animator, ref T data, Component component) { var job = new OverrideTransformJob(); var cacheBuilder = new AnimationJobCacheBuilder(); job.driven = ReadWriteTransformHandle.Bind(animator, data.constrainedObject); if (data.sourceObject != null) { // Cache source to possible space rotation offsets (world, local and pivot) // at bind time so we can switch dynamically between them at runtime. job.source = ReadOnlyTransformHandle.Bind(animator, data.sourceObject); var sourceLocalTx = new AffineTransform(data.sourceObject.localPosition, data.sourceObject.localRotation); job.sourceInvLocalBindTx = sourceLocalTx.Inverse(); var sourceWorldTx = new AffineTransform(data.sourceObject.position, data.sourceObject.rotation); var drivenWorldTx = new AffineTransform(data.constrainedObject.position, data.constrainedObject.rotation); job.sourceToWorldRot = sourceWorldTx.Inverse().rotation; job.sourceToPivotRot = sourceWorldTx.InverseMul(drivenWorldTx).rotation; var drivenParent = data.constrainedObject.parent; if (drivenParent != null) { var drivenParentWorldTx = new AffineTransform(drivenParent.position, drivenParent.rotation); job.sourceToLocalRot = sourceWorldTx.InverseMul(drivenParentWorldTx).rotation; } else job.sourceToLocalRot = job.sourceToPivotRot; } job.spaceIdx = cacheBuilder.Add(data.space); if (data.space == (int)OverrideTransformJob.Space.Pivot) job.sourceToCurrSpaceRotIdx = cacheBuilder.Add(job.sourceToPivotRot); else if (data.space == (int)OverrideTransformJob.Space.Local) job.sourceToCurrSpaceRotIdx = cacheBuilder.Add(job.sourceToLocalRot); else job.sourceToCurrSpaceRotIdx = cacheBuilder.Add(job.sourceToWorldRot); job.position = Vector3Property.Bind(animator, component, data.positionVector3Property); job.rotation = Vector3Property.Bind(animator, component, data.rotationVector3Property); job.positionWeight = FloatProperty.Bind(animator, component, data.positionWeightFloatProperty); job.rotationWeight = FloatProperty.Bind(animator, component, data.rotationWeightFloatProperty); job.cache = cacheBuilder.Build(); return job; } /// public override void Destroy(OverrideTransformJob job) { job.cache.Dispose(); } /// public override void Update(OverrideTransformJob job, ref T data) { job.UpdateSpace(data.space); } } }