// Copyright Unity Technologies 2019 // https://github.com/Unity-Technologies/animation-jobs-samples // // This file has not been modified other to put it in the Animancer.Samples.Jobs namespace and add this message and the comment on the struct. using UnityEngine; using UnityEngine.Animations; namespace Animancer.Samples.Jobs { /// An which executes two bone Inverse Kinematics. /// /// /// Sample: /// /// Two Bone IK /// /// /// https://kybernetik.com.au/animancer/api/Animancer.Samples.Jobs/TwoBoneIKJob /// public struct TwoBoneIKJob : IAnimationJob { public TransformSceneHandle effector; public TransformStreamHandle top; public TransformStreamHandle mid; public TransformStreamHandle low; public void Setup(Animator animator, Transform topX, Transform midX, Transform lowX, Transform effectorX) { top = animator.BindStreamTransform(topX); mid = animator.BindStreamTransform(midX); low = animator.BindStreamTransform(lowX); effector = animator.BindSceneTransform(effectorX); } public readonly void ProcessRootMotion(AnimationStream stream) { } public readonly void ProcessAnimation(AnimationStream stream) { Solve(stream, top, mid, low, effector); } /// /// Returns the angle needed between v1 and v2 so that their extremities are /// spaced with a specific length. /// /// The angle between v1 and v2. /// The desired length between the extremities of v1 and v2. /// First triangle edge. /// Second triangle edge. private static float TriangleAngle(float aLen, Vector3 v1, Vector3 v2) { float aLen1 = v1.magnitude; float aLen2 = v2.magnitude; float c = Mathf.Clamp((aLen1 * aLen1 + aLen2 * aLen2 - aLen * aLen) / (aLen1 * aLen2) / 2.0f, -1.0f, 1.0f); return Mathf.Acos(c); } private static void Solve(AnimationStream stream, TransformStreamHandle topHandle, TransformStreamHandle midHandle, TransformStreamHandle lowHandle, TransformSceneHandle effectorHandle) { Quaternion aRotation = topHandle.GetRotation(stream); Quaternion bRotation = midHandle.GetRotation(stream); Quaternion eRotation = effectorHandle.GetRotation(stream); Vector3 aPosition = topHandle.GetPosition(stream); Vector3 bPosition = midHandle.GetPosition(stream); Vector3 cPosition = lowHandle.GetPosition(stream); Vector3 ePosition = effectorHandle.GetPosition(stream); Vector3 ab = bPosition - aPosition; Vector3 bc = cPosition - bPosition; Vector3 ac = cPosition - aPosition; Vector3 ae = ePosition - aPosition; float abcAngle = TriangleAngle(ac.magnitude, ab, bc); float abeAngle = TriangleAngle(ae.magnitude, ab, bc); float angle = (abcAngle - abeAngle) * Mathf.Rad2Deg; Vector3 axis = Vector3.Cross(ab, bc).normalized; Quaternion fromToRotation = Quaternion.AngleAxis(angle, axis); Quaternion worldQ = fromToRotation * bRotation; midHandle.SetRotation(stream, worldQ); cPosition = lowHandle.GetPosition(stream); ac = cPosition - aPosition; Quaternion fromTo = Quaternion.FromToRotation(ac, ae); topHandle.SetRotation(stream, fromTo * aRotation); lowHandle.SetRotation(stream, eRotation); } } }