123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- using UnityEngine;
- using System.Collections;
- using System;
- namespace RootMotion.FinalIK {
- /// <summary>
- /// Analytic %IK solver based on the Law of Cosines.
- /// </summary>
- [System.Serializable]
- public class IKSolverTrigonometric: IKSolver {
-
- #region Main Interface
- /// <summary>
- /// The target Transform.
- /// </summary>
- public Transform target;
- /// <summary>
- /// The %IK rotation weight (rotation of the last bone).
- /// </summary>
- [Range(0f, 1f)]
- public float IKRotationWeight = 1f;
- /// <summary>
- /// The %IK rotation target.
- /// </summary>
- public Quaternion IKRotation = Quaternion.identity;
- /// <summary>
- /// The bend plane normal.
- /// </summary>
- public Vector3 bendNormal = Vector3.right;
- /// <summary>
- /// The first bone (upper arm or thigh).
- /// </summary>
- public TrigonometricBone bone1 = new TrigonometricBone();
- /// <summary>
- /// The second bone (forearm or calf).
- /// </summary>
- public TrigonometricBone bone2 = new TrigonometricBone();
- /// <summary>
- /// The third bone (hand or foot).
- /// </summary>
- public TrigonometricBone bone3 = new TrigonometricBone();
-
- /// <summary>
- /// Sets the bend goal position.
- /// </summary>
- /// <param name='goalPosition'>
- /// Goal position.
- /// </param>
- public void SetBendGoalPosition(Vector3 goalPosition, float weight) {
- if (!initiated) return;
- if (weight <= 0f) return;
-
- Vector3 normal = Vector3.Cross(goalPosition - bone1.transform.position, IKPosition - bone1.transform.position);
- if (normal != Vector3.zero) {
- if (weight >= 1f) {
- bendNormal = normal;
- return;
- }
- bendNormal = Vector3.Lerp(bendNormal, normal, weight);
- }
- }
-
- /// <summary>
- /// Sets the bend plane to match current bone rotations.
- /// </summary>
- public void SetBendPlaneToCurrent() {
- if (!initiated) return;
-
- Vector3 normal = Vector3.Cross(bone2.transform.position - bone1.transform.position, bone3.transform.position - bone2.transform.position);
- if (normal != Vector3.zero) bendNormal = normal;
- }
-
- /// <summary>
- /// Sets the %IK rotation.
- /// </summary>
- public void SetIKRotation(Quaternion rotation) {
- IKRotation = rotation;
- }
-
- /// <summary>
- /// Sets the %IK rotation weight.
- /// </summary>
- public void SetIKRotationWeight(float weight) {
- IKRotationWeight = Mathf.Clamp(weight, 0f, 1f);
- }
-
- /// <summary>
- /// Gets the %IK rotation.
- /// </summary>
- public Quaternion GetIKRotation() {
- return IKRotation;
- }
-
- /// <summary>
- /// Gets the %IK rotation weight.
- /// </summary>
- public float GetIKRotationWeight() {
- return IKRotationWeight;
- }
-
- public override IKSolver.Point[] GetPoints() {
- return new IKSolver.Point[3] { (IKSolver.Point)bone1, (IKSolver.Point)bone2, (IKSolver.Point)bone3 };
- }
-
- public override IKSolver.Point GetPoint(Transform transform) {
- if (bone1.transform == transform) return (IKSolver.Point)bone1;
- if (bone2.transform == transform) return (IKSolver.Point)bone2;
- if (bone3.transform == transform) return (IKSolver.Point)bone3;
- return null;
- }
- public override void StoreDefaultLocalState() {
- bone1.StoreDefaultLocalState();
- bone2.StoreDefaultLocalState();
- bone3.StoreDefaultLocalState();
- }
-
- public override void FixTransforms() {
- if (!initiated) return;
- bone1.FixTransform();
- bone2.FixTransform();
- bone3.FixTransform();
- }
-
- public override bool IsValid(ref string message) {
- if (bone1.transform == null || bone2.transform == null || bone3.transform == null) {
- message = "Please assign all Bones to the IK solver.";
- return false;
- }
- Transform duplicate = (Transform)Hierarchy.ContainsDuplicate(new Transform[3] { bone1.transform, bone2.transform, bone3.transform });
- if (duplicate != null) {
- message = duplicate.name + " is represented multiple times in the Bones.";
- return false;
- }
- if (bone1.transform.position == bone2.transform.position) {
- message = "first bone position is the same as second bone position.";
- return false;
- }
- if (bone2.transform.position == bone3.transform.position) {
- message = "second bone position is the same as third bone position.";
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// Bone type used by IKSolverTrigonometric.
- /// </summary>
- [System.Serializable]
- public class TrigonometricBone: IKSolver.Bone {
-
- private Quaternion targetToLocalSpace;
- private Vector3 defaultLocalBendNormal;
-
- #region Public methods
-
- /*
- * Initiates the bone, precalculates values.
- * */
- public void Initiate(Vector3 childPosition, Vector3 bendNormal) {
- // Get default target rotation that looks at child position with bendNormal as up
- Quaternion defaultTargetRotation = Quaternion.LookRotation(childPosition - transform.position, bendNormal);
-
- // Covert default target rotation to local space
- targetToLocalSpace = QuaTools.RotationToLocalSpace(transform.rotation, defaultTargetRotation);
-
- defaultLocalBendNormal = Quaternion.Inverse(transform.rotation) * bendNormal;
- }
-
- /*
- * Calculates the rotation of this bone to targetPosition.
- * */
- public Quaternion GetRotation(Vector3 direction, Vector3 bendNormal) {
- return Quaternion.LookRotation(direction, bendNormal) * targetToLocalSpace;
- }
-
- /*
- * Gets the bend normal from current bone rotation.
- * */
- public Vector3 GetBendNormalFromCurrentRotation() {
- return transform.rotation * defaultLocalBendNormal;
- }
-
- #endregion Public methods
- }
- /// <summary>
- /// Reinitiate the solver with new bone Transforms.
- /// </summary>
- /// <returns>
- /// Returns true if the new chain is valid.
- /// </returns>
- public bool SetChain(Transform bone1, Transform bone2, Transform bone3, Transform root) {
- this.bone1.transform = bone1;
- this.bone2.transform = bone2;
- this.bone3.transform = bone3;
-
- Initiate(root);
- return initiated;
- }
-
- #endregion Main Interface
- #region Class Methods
- /// <summary>
- /// Solve the bone chain.
- /// </summary>
- public static void Solve(Transform bone1, Transform bone2, Transform bone3, Vector3 targetPosition, Vector3 bendNormal, float weight) {
- if (weight <= 0f) return;
- // Direction of the limb in solver
- targetPosition = Vector3.Lerp(bone3.position, targetPosition, weight);
-
- Vector3 dir = targetPosition - bone1.position;
-
- // Distance between the first and the last node solver positions
- float length = dir.magnitude;
- if (length == 0f) return;
-
- float sqrMag1 = (bone2.position - bone1.position).sqrMagnitude;
- float sqrMag2 = (bone3.position - bone2.position).sqrMagnitude;
-
- // Get the general world space bending direction
- Vector3 bendDir = Vector3.Cross(dir, bendNormal);
-
- // Get the direction to the trigonometrically solved position of the second node
- Vector3 toBendPoint = GetDirectionToBendPoint(dir, length, bendDir, sqrMag1, sqrMag2);
-
- // Position the second node
- Quaternion q1 = Quaternion.FromToRotation(bone2.position - bone1.position, toBendPoint);
- if (weight < 1f) q1 = Quaternion.Lerp(Quaternion.identity, q1, weight);
- bone1.rotation = q1 * bone1.rotation;
- Quaternion q2 = Quaternion.FromToRotation(bone3.position - bone2.position, targetPosition - bone2.position);
- if (weight < 1f) q2 = Quaternion.Lerp(Quaternion.identity, q2, weight);
- bone2.rotation = q2 * bone2.rotation;
- }
- //Calculates the bend direction based on the law of cosines. NB! Magnitude of the returned vector does not equal to the length of the first bone!
- private static Vector3 GetDirectionToBendPoint(Vector3 direction, float directionMag, Vector3 bendDirection, float sqrMag1, float sqrMag2) {
- float x = ((directionMag * directionMag) + (sqrMag1 - sqrMag2)) / 2f / directionMag;
- float y = (float)Math.Sqrt(Mathf.Clamp(sqrMag1 - x * x, 0, Mathf.Infinity));
-
- if (direction == Vector3.zero) return Vector3.zero;
- return Quaternion.LookRotation(direction, bendDirection) * new Vector3(0f, y, x);
- }
- #endregion Class Methods
-
- protected override void OnInitiate() {
- if (bendNormal == Vector3.zero) bendNormal = Vector3.right;
-
- OnInitiateVirtual();
-
- IKPosition = bone3.transform.position;
- IKRotation = bone3.transform.rotation;
-
- // Initiating bones
- InitiateBones();
- directHierarchy = IsDirectHierarchy();
- }
- // Are the bones parented directly to each other?
- private bool IsDirectHierarchy() {
- if (bone3.transform.parent != bone2.transform) return false;
- if (bone2.transform.parent != bone1.transform) return false;
- return true;
- }
- // Set the defaults for the bones
- private void InitiateBones() {
- bone1.Initiate(bone2.transform.position, bendNormal);
- bone2.Initiate(bone3.transform.position, bendNormal);
- SetBendPlaneToCurrent();
- }
-
- protected override void OnUpdate() {
- IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
- IKRotationWeight = Mathf.Clamp(IKRotationWeight, 0f, 1f);
- if (target != null) {
- IKPosition = target.position;
- IKRotation = target.rotation;
- }
- OnUpdateVirtual();
-
- if (IKPositionWeight > 0) {
- // Reinitiating the bones when the hierarchy is not direct. This allows for skipping animated bones in the hierarchy.
- if (!directHierarchy) {
- bone1.Initiate(bone2.transform.position, bendNormal);
- bone2.Initiate(bone3.transform.position, bendNormal);
- }
- // Find out if bone lengths should be updated
- bone1.sqrMag = (bone2.transform.position - bone1.transform.position).sqrMagnitude;
- bone2.sqrMag = (bone3.transform.position - bone2.transform.position).sqrMagnitude;
-
- if (bendNormal == Vector3.zero && !Warning.logged) LogWarning("IKSolverTrigonometric Bend Normal is Vector3.zero.");
-
- weightIKPosition = Vector3.Lerp(bone3.transform.position, IKPosition, IKPositionWeight);
-
- // Interpolating bend normal
- Vector3 currentBendNormal = Vector3.Lerp(bone1.GetBendNormalFromCurrentRotation(), bendNormal, IKPositionWeight);
-
- // Calculating and interpolating bend direction
- Vector3 bendDirection = Vector3.Lerp(bone2.transform.position - bone1.transform.position, GetBendDirection(weightIKPosition, currentBendNormal), IKPositionWeight);
-
- if (bendDirection == Vector3.zero) bendDirection = bone2.transform.position - bone1.transform.position;
-
- // Rotating bone1
- bone1.transform.rotation = bone1.GetRotation(bendDirection, currentBendNormal);
-
- // Rotating bone 2
- bone2.transform.rotation = bone2.GetRotation(weightIKPosition - bone2.transform.position, bone2.GetBendNormalFromCurrentRotation());
- }
-
- // Rotating bone3
- if (IKRotationWeight > 0) {
- bone3.transform.rotation = Quaternion.Slerp(bone3.transform.rotation, IKRotation, IKRotationWeight);
- }
-
- OnPostSolveVirtual();
- }
-
- protected Vector3 weightIKPosition;
- protected virtual void OnInitiateVirtual() {}
- protected virtual void OnUpdateVirtual() {}
- protected virtual void OnPostSolveVirtual() {}
- protected bool directHierarchy = true;
-
- /*
- * Calculates the bend direction based on the Law of Cosines.
- * */
- protected Vector3 GetBendDirection(Vector3 IKPosition, Vector3 bendNormal) {
- Vector3 direction = IKPosition - bone1.transform.position;
- if (direction == Vector3.zero) return Vector3.zero;
-
- float directionSqrMag = direction.sqrMagnitude;
- float directionMagnitude = (float)Math.Sqrt(directionSqrMag);
-
- float x = (directionSqrMag + bone1.sqrMag - bone2.sqrMag) / 2f / directionMagnitude;
- float y = (float)Math.Sqrt(Mathf.Clamp(bone1.sqrMag - x * x, 0, Mathf.Infinity));
-
- Vector3 yDirection = Vector3.Cross(direction / directionMagnitude, bendNormal);
- return Quaternion.LookRotation(direction, yDirection) * new Vector3(0f, y, x);
- }
- }
- }
-
|