123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- using UnityEngine;
- using System.Collections;
- namespace RootMotion.FinalIK {
- /// <summary>
- /// Extends IKSolverTrigonometric to add automatic bend and rotation modes.
- /// </summary>
- [System.Serializable]
- public class IKSolverLimb : IKSolverTrigonometric {
-
- #region Main Interface
-
- /// <summary>
- /// The AvatarIKGoal of this solver.
- /// </summary>
- public AvatarIKGoal goal;
- /// <summary>
- /// Bend normal modifier.
- /// </summary>
- public BendModifier bendModifier;
- /// <summary>
- /// Weight of maintaining the rotation of the third bone as it was before solving.
- /// </summary>
- [Range(0f, 1f)]
- public float maintainRotationWeight;
- /// <summary>
- /// Weight of bend normal modifier.
- /// </summary>
- [Range(0f, 1f)]
- public float bendModifierWeight = 1f;
- /// <summary>
- /// The bend goal Transform.
- /// </summary>
- public Transform bendGoal;
- /// <summary>
- /// Used to record rotation of the last bone for one frame.
- /// If MaintainRotation is not called and maintainRotationWeight > 0, the solver will maintain the rotation of the last bone as it was before solving the %IK.
- /// You will probably need this if you wanted to maintain the animated rotation of a foot despite of any other %IK solver that manipulates it's parents' rotation.
- /// So you would call %MaintainRotation() in LateUpdate() after animation and before updating the Spine %IK solver that would change the foot's rotation.
- /// </summary>
- public void MaintainRotation() {
- if (!initiated) return;
-
- maintainRotation = bone3.transform.rotation;
- maintainRotationFor1Frame = true;
- }
-
- /// <summary>
- /// If Auto Bend is on "Animation', %MaintainBend() can be used to set the bend axis relative to the first bone's rotation.
- /// </summary>
- public void MaintainBend() {
- if (!initiated) return;
-
- animationNormal = bone1.GetBendNormalFromCurrentRotation();
-
- maintainBendFor1Frame = true;
- }
-
- /// <summary>
- /// Automatic bend modes.
- /// </summary>
- [System.Serializable]
- public enum BendModifier {
- Animation, // Bending relative to the animated rotation of the first bone
- Target, // Bending relative to IKRotation
- Parent, // Bending relative to parentBone
- Arm, // Arm modifier tries to find the most biometrically natural and relaxed arm bend plane
- Goal // Use the bend goal Transform
- }
-
- #endregion Main Interface
-
- /*
- * Override before Initiate
- * */
- protected override void OnInitiateVirtual() {
- defaultRootRotation = root.rotation;
-
- if (bone1.transform.parent != null) {
- parentDefaultRotation = Quaternion.Inverse(defaultRootRotation) * bone1.transform.parent.rotation;
- }
-
- if (bone3.rotationLimit != null) bone3.rotationLimit.Disable();
- bone3DefaultRotation = bone3.transform.rotation;
-
- // Set bend plane to current (cant use the public SetBendPlaneToCurrent() method here because the solver has not initiated yet)
- Vector3 normal = Vector3.Cross(bone2.transform.position - bone1.transform.position, bone3.transform.position - bone2.transform.position);
- if (normal != Vector3.zero) bendNormal = normal;
-
- animationNormal = bendNormal;
-
- StoreAxisDirections(ref axisDirectionsLeft);
- StoreAxisDirections(ref axisDirectionsRight);
- }
-
- /*
- * Changing stuff in Update() before solving
- * */
- protected override void OnUpdateVirtual() {
- if (IKPositionWeight > 0) {
- // Clamping weights
- bendModifierWeight = Mathf.Clamp(bendModifierWeight, 0f, 1f);
- maintainRotationWeight = Mathf.Clamp(maintainRotationWeight, 0f, 1f);
-
- // Storing the bendNormal for reverting after solving
- _bendNormal = bendNormal;
-
- // Modifying bendNormal
- bendNormal = GetModifiedBendNormal();
- }
-
- if (maintainRotationWeight * IKPositionWeight > 0) {
- // Storing bone3 rotation
- bone3RotationBeforeSolve = maintainRotationFor1Frame? maintainRotation : bone3.transform.rotation;
- maintainRotationFor1Frame = false;
- }
- }
-
- /*
- * Changing stuff in Update() after solving
- * */
- protected override void OnPostSolveVirtual() {
- // Revert bendNormal to what it was before solving
- if (IKPositionWeight > 0) bendNormal = _bendNormal;
-
- // Auto rotation modes
- if (maintainRotationWeight * IKPositionWeight > 0) {
- bone3.transform.rotation = Quaternion.Slerp(bone3.transform.rotation, bone3RotationBeforeSolve, maintainRotationWeight * IKPositionWeight);
- }
- }
-
- /*
- * Axis direction contains an arm bend axis for a specific IKPosition direction from the first bone. Used in Arm BendModifier mode.
- * */
- [System.Serializable]
- public struct AxisDirection {
- public Vector3 direction;
- public Vector3 axis;
- public float dot;
-
- public AxisDirection(Vector3 direction, Vector3 axis) {
- this.direction = direction.normalized;
- this.axis = axis.normalized;
- this.dot = 0;
- }
- }
-
- public IKSolverLimb() {}
-
- public IKSolverLimb(AvatarIKGoal goal) {
- this.goal = goal;
- }
-
- private bool maintainBendFor1Frame, maintainRotationFor1Frame;
- private Quaternion defaultRootRotation, parentDefaultRotation, bone3RotationBeforeSolve, maintainRotation, bone3DefaultRotation;
- private Vector3 _bendNormal, animationNormal;
- private AxisDirection[] axisDirectionsLeft = new AxisDirection[4];
- private AxisDirection[] axisDirectionsRight = new AxisDirection[4];
- private AxisDirection[] axisDirections {
- get {
- if (goal == AvatarIKGoal.LeftHand) return axisDirectionsLeft;
- return axisDirectionsRight;
- }
- }
-
- /*
- * Storing Bend axes for arm directions. The directions and axes here were found empirically, feel free to edit, add or remove.
- * All vectors are in default character rotation space, where x is right and z is forward.
- * */
- private void StoreAxisDirections(ref AxisDirection[] axisDirections) {
- axisDirections[0] = new AxisDirection(Vector3.zero, new Vector3(-1f, 0f, 0f)); // default
- axisDirections[1] = new AxisDirection(new Vector3(0.5f, 0f, -0.2f), new Vector3(-0.5f, -1f, 1f)); // behind head
- axisDirections[2] = new AxisDirection(new Vector3(-0.5f, -1f, -0.2f), new Vector3(0f, 0.5f, -1f)); // arm twist
- axisDirections[3] = new AxisDirection(new Vector3(-0.5f, -0.5f, 1f), new Vector3(-1f, -1f, -1f)); // cross heart
- }
-
- /*
- * Modifying bendNormal
- * */
- private Vector3 GetModifiedBendNormal() {
- float weight = bendModifierWeight;
- if (weight <= 0) return bendNormal;
-
- switch(bendModifier) {
- // Animation Bend Mode attempts to maintain the bend axis as it is in the animation
- case BendModifier.Animation:
- if (!maintainBendFor1Frame) MaintainBend();
- maintainBendFor1Frame = false;
- return Vector3.Lerp(bendNormal, animationNormal, weight);
-
- // Bending relative to the parent of the first bone
- case BendModifier.Parent:
- if (bone1.transform.parent == null) return bendNormal;
-
- Quaternion parentRotation = bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation);
- return Quaternion.Slerp(Quaternion.identity, parentRotation * Quaternion.Inverse(defaultRootRotation), weight) * bendNormal;
-
- // Bending relative to IKRotation
- case BendModifier.Target:
- Quaternion targetRotation = IKRotation * Quaternion.Inverse(bone3DefaultRotation);
- return Quaternion.Slerp(Quaternion.identity, targetRotation, weight) * bendNormal;
- // Anatomic Arm
- case BendModifier.Arm:
- if (bone1.transform.parent == null) return bendNormal;
-
- // Disabling this for legs
- if (goal == AvatarIKGoal.LeftFoot || goal == AvatarIKGoal.RightFoot) {
- if (!Warning.logged) LogWarning("Trying to use the 'Arm' bend modifier on a leg.");
- return bendNormal;
- }
-
- Vector3 direction = (IKPosition - bone1.transform.position).normalized;
-
- // Converting direction to default world space
- direction = Quaternion.Inverse(bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation)) * direction;
-
- // Inverting direction for left hand
- if (goal == AvatarIKGoal.LeftHand) direction.x = -direction.x;
-
- // Calculating dot products for all AxisDirections
- for (int i = 1; i < axisDirections.Length; i++) {
- axisDirections[i].dot = Mathf.Clamp(Vector3.Dot(axisDirections[i].direction, direction), 0f, 1f);
- axisDirections[i].dot = Interp.Float(axisDirections[i].dot, InterpolationMode.InOutQuintic);
- }
-
- // Summing up the arm bend axis
- Vector3 sum = axisDirections[0].axis;
-
- //for (int i = 1; i < axisDirections.Length; i++) sum = Vector3.Lerp(sum, axisDirections[i].axis, axisDirections[i].dot);
- for (int i = 1; i < axisDirections.Length; i++) sum = Vector3.Slerp(sum, axisDirections[i].axis, axisDirections[i].dot);
-
- // Inverting sum for left hand
- if (goal == AvatarIKGoal.LeftHand) {
- sum.x = -sum.x;
- sum = -sum;
- }
-
- // Converting sum back to parent space
- Vector3 armBendNormal = bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation) * sum;
-
- if (weight >= 1) return armBendNormal;
- return Vector3.Lerp(bendNormal, armBendNormal, weight);
-
- // Bending towards the bend goal Transform
- case BendModifier.Goal:
- if (bendGoal == null) {
- if (!Warning.logged) LogWarning("Trying to use the 'Goal' Bend Modifier, but the Bend Goal is unassigned.");
- return bendNormal;
- }
- Vector3 normal = Vector3.Cross(bendGoal.position - bone1.transform.position, IKPosition - bone1.transform.position);
- if (normal == Vector3.zero) return bendNormal;
- if (weight >= 1f) return normal;
- return Vector3.Lerp(bendNormal, normal, weight);
- default: return bendNormal;
- }
- }
- }
- }
|