IKSolverLimb.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion.FinalIK {
  4. /// <summary>
  5. /// Extends IKSolverTrigonometric to add automatic bend and rotation modes.
  6. /// </summary>
  7. [System.Serializable]
  8. public class IKSolverLimb : IKSolverTrigonometric {
  9. #region Main Interface
  10. /// <summary>
  11. /// The AvatarIKGoal of this solver.
  12. /// </summary>
  13. public AvatarIKGoal goal;
  14. /// <summary>
  15. /// Bend normal modifier.
  16. /// </summary>
  17. public BendModifier bendModifier;
  18. /// <summary>
  19. /// Weight of maintaining the rotation of the third bone as it was before solving.
  20. /// </summary>
  21. [Range(0f, 1f)]
  22. public float maintainRotationWeight;
  23. /// <summary>
  24. /// Weight of bend normal modifier.
  25. /// </summary>
  26. [Range(0f, 1f)]
  27. public float bendModifierWeight = 1f;
  28. /// <summary>
  29. /// The bend goal Transform.
  30. /// </summary>
  31. public Transform bendGoal;
  32. /// <summary>
  33. /// Used to record rotation of the last bone for one frame.
  34. /// 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.
  35. /// 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.
  36. /// So you would call %MaintainRotation() in LateUpdate() after animation and before updating the Spine %IK solver that would change the foot's rotation.
  37. /// </summary>
  38. public void MaintainRotation() {
  39. if (!initiated) return;
  40. maintainRotation = bone3.transform.rotation;
  41. maintainRotationFor1Frame = true;
  42. }
  43. /// <summary>
  44. /// If Auto Bend is on "Animation', %MaintainBend() can be used to set the bend axis relative to the first bone's rotation.
  45. /// </summary>
  46. public void MaintainBend() {
  47. if (!initiated) return;
  48. animationNormal = bone1.GetBendNormalFromCurrentRotation();
  49. maintainBendFor1Frame = true;
  50. }
  51. /// <summary>
  52. /// Automatic bend modes.
  53. /// </summary>
  54. [System.Serializable]
  55. public enum BendModifier {
  56. Animation, // Bending relative to the animated rotation of the first bone
  57. Target, // Bending relative to IKRotation
  58. Parent, // Bending relative to parentBone
  59. Arm, // Arm modifier tries to find the most biometrically natural and relaxed arm bend plane
  60. Goal // Use the bend goal Transform
  61. }
  62. #endregion Main Interface
  63. /*
  64. * Override before Initiate
  65. * */
  66. protected override void OnInitiateVirtual() {
  67. defaultRootRotation = root.rotation;
  68. if (bone1.transform.parent != null) {
  69. parentDefaultRotation = Quaternion.Inverse(defaultRootRotation) * bone1.transform.parent.rotation;
  70. }
  71. if (bone3.rotationLimit != null) bone3.rotationLimit.Disable();
  72. bone3DefaultRotation = bone3.transform.rotation;
  73. // Set bend plane to current (cant use the public SetBendPlaneToCurrent() method here because the solver has not initiated yet)
  74. Vector3 normal = Vector3.Cross(bone2.transform.position - bone1.transform.position, bone3.transform.position - bone2.transform.position);
  75. if (normal != Vector3.zero) bendNormal = normal;
  76. animationNormal = bendNormal;
  77. StoreAxisDirections(ref axisDirectionsLeft);
  78. StoreAxisDirections(ref axisDirectionsRight);
  79. }
  80. /*
  81. * Changing stuff in Update() before solving
  82. * */
  83. protected override void OnUpdateVirtual() {
  84. if (IKPositionWeight > 0) {
  85. // Clamping weights
  86. bendModifierWeight = Mathf.Clamp(bendModifierWeight, 0f, 1f);
  87. maintainRotationWeight = Mathf.Clamp(maintainRotationWeight, 0f, 1f);
  88. // Storing the bendNormal for reverting after solving
  89. _bendNormal = bendNormal;
  90. // Modifying bendNormal
  91. bendNormal = GetModifiedBendNormal();
  92. }
  93. if (maintainRotationWeight * IKPositionWeight > 0) {
  94. // Storing bone3 rotation
  95. bone3RotationBeforeSolve = maintainRotationFor1Frame? maintainRotation : bone3.transform.rotation;
  96. maintainRotationFor1Frame = false;
  97. }
  98. }
  99. /*
  100. * Changing stuff in Update() after solving
  101. * */
  102. protected override void OnPostSolveVirtual() {
  103. // Revert bendNormal to what it was before solving
  104. if (IKPositionWeight > 0) bendNormal = _bendNormal;
  105. // Auto rotation modes
  106. if (maintainRotationWeight * IKPositionWeight > 0) {
  107. bone3.transform.rotation = Quaternion.Slerp(bone3.transform.rotation, bone3RotationBeforeSolve, maintainRotationWeight * IKPositionWeight);
  108. }
  109. }
  110. /*
  111. * Axis direction contains an arm bend axis for a specific IKPosition direction from the first bone. Used in Arm BendModifier mode.
  112. * */
  113. [System.Serializable]
  114. public struct AxisDirection {
  115. public Vector3 direction;
  116. public Vector3 axis;
  117. public float dot;
  118. public AxisDirection(Vector3 direction, Vector3 axis) {
  119. this.direction = direction.normalized;
  120. this.axis = axis.normalized;
  121. this.dot = 0;
  122. }
  123. }
  124. public IKSolverLimb() {}
  125. public IKSolverLimb(AvatarIKGoal goal) {
  126. this.goal = goal;
  127. }
  128. private bool maintainBendFor1Frame, maintainRotationFor1Frame;
  129. private Quaternion defaultRootRotation, parentDefaultRotation, bone3RotationBeforeSolve, maintainRotation, bone3DefaultRotation;
  130. private Vector3 _bendNormal, animationNormal;
  131. private AxisDirection[] axisDirectionsLeft = new AxisDirection[4];
  132. private AxisDirection[] axisDirectionsRight = new AxisDirection[4];
  133. private AxisDirection[] axisDirections {
  134. get {
  135. if (goal == AvatarIKGoal.LeftHand) return axisDirectionsLeft;
  136. return axisDirectionsRight;
  137. }
  138. }
  139. /*
  140. * Storing Bend axes for arm directions. The directions and axes here were found empirically, feel free to edit, add or remove.
  141. * All vectors are in default character rotation space, where x is right and z is forward.
  142. * */
  143. private void StoreAxisDirections(ref AxisDirection[] axisDirections) {
  144. axisDirections[0] = new AxisDirection(Vector3.zero, new Vector3(-1f, 0f, 0f)); // default
  145. axisDirections[1] = new AxisDirection(new Vector3(0.5f, 0f, -0.2f), new Vector3(-0.5f, -1f, 1f)); // behind head
  146. axisDirections[2] = new AxisDirection(new Vector3(-0.5f, -1f, -0.2f), new Vector3(0f, 0.5f, -1f)); // arm twist
  147. axisDirections[3] = new AxisDirection(new Vector3(-0.5f, -0.5f, 1f), new Vector3(-1f, -1f, -1f)); // cross heart
  148. }
  149. /*
  150. * Modifying bendNormal
  151. * */
  152. private Vector3 GetModifiedBendNormal() {
  153. float weight = bendModifierWeight;
  154. if (weight <= 0) return bendNormal;
  155. switch(bendModifier) {
  156. // Animation Bend Mode attempts to maintain the bend axis as it is in the animation
  157. case BendModifier.Animation:
  158. if (!maintainBendFor1Frame) MaintainBend();
  159. maintainBendFor1Frame = false;
  160. return Vector3.Lerp(bendNormal, animationNormal, weight);
  161. // Bending relative to the parent of the first bone
  162. case BendModifier.Parent:
  163. if (bone1.transform.parent == null) return bendNormal;
  164. Quaternion parentRotation = bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation);
  165. return Quaternion.Slerp(Quaternion.identity, parentRotation * Quaternion.Inverse(defaultRootRotation), weight) * bendNormal;
  166. // Bending relative to IKRotation
  167. case BendModifier.Target:
  168. Quaternion targetRotation = IKRotation * Quaternion.Inverse(bone3DefaultRotation);
  169. return Quaternion.Slerp(Quaternion.identity, targetRotation, weight) * bendNormal;
  170. // Anatomic Arm
  171. case BendModifier.Arm:
  172. if (bone1.transform.parent == null) return bendNormal;
  173. // Disabling this for legs
  174. if (goal == AvatarIKGoal.LeftFoot || goal == AvatarIKGoal.RightFoot) {
  175. if (!Warning.logged) LogWarning("Trying to use the 'Arm' bend modifier on a leg.");
  176. return bendNormal;
  177. }
  178. Vector3 direction = (IKPosition - bone1.transform.position).normalized;
  179. // Converting direction to default world space
  180. direction = Quaternion.Inverse(bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation)) * direction;
  181. // Inverting direction for left hand
  182. if (goal == AvatarIKGoal.LeftHand) direction.x = -direction.x;
  183. // Calculating dot products for all AxisDirections
  184. for (int i = 1; i < axisDirections.Length; i++) {
  185. axisDirections[i].dot = Mathf.Clamp(Vector3.Dot(axisDirections[i].direction, direction), 0f, 1f);
  186. axisDirections[i].dot = Interp.Float(axisDirections[i].dot, InterpolationMode.InOutQuintic);
  187. }
  188. // Summing up the arm bend axis
  189. Vector3 sum = axisDirections[0].axis;
  190. //for (int i = 1; i < axisDirections.Length; i++) sum = Vector3.Lerp(sum, axisDirections[i].axis, axisDirections[i].dot);
  191. for (int i = 1; i < axisDirections.Length; i++) sum = Vector3.Slerp(sum, axisDirections[i].axis, axisDirections[i].dot);
  192. // Inverting sum for left hand
  193. if (goal == AvatarIKGoal.LeftHand) {
  194. sum.x = -sum.x;
  195. sum = -sum;
  196. }
  197. // Converting sum back to parent space
  198. Vector3 armBendNormal = bone1.transform.parent.rotation * Quaternion.Inverse(parentDefaultRotation) * sum;
  199. if (weight >= 1) return armBendNormal;
  200. return Vector3.Lerp(bendNormal, armBendNormal, weight);
  201. // Bending towards the bend goal Transform
  202. case BendModifier.Goal:
  203. if (bendGoal == null) {
  204. if (!Warning.logged) LogWarning("Trying to use the 'Goal' Bend Modifier, but the Bend Goal is unassigned.");
  205. return bendNormal;
  206. }
  207. Vector3 normal = Vector3.Cross(bendGoal.position - bone1.transform.position, IKPosition - bone1.transform.position);
  208. if (normal == Vector3.zero) return bendNormal;
  209. if (weight >= 1f) return normal;
  210. return Vector3.Lerp(bendNormal, normal, weight);
  211. default: return bendNormal;
  212. }
  213. }
  214. }
  215. }