IKSolverHeuristic.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. using UnityEngine;
  2. using System.Collections;
  3. using System;
  4. namespace RootMotion.FinalIK {
  5. /// <summary>
  6. /// Contains methods common for all heuristic solvers.
  7. /// </summary>
  8. [System.Serializable]
  9. public class IKSolverHeuristic: IKSolver {
  10. #region Main Interface
  11. /// <summary>
  12. /// The target Transform. Solver IKPosition will be automatically set to the position of the target.
  13. /// </summary>
  14. public Transform target;
  15. /// <summary>
  16. /// Minimum distance from last reached position. Will stop solving if difference from previous reached position is less than tolerance. If tolerance is zero, will iterate until maxIterations.
  17. /// </summary>
  18. public float tolerance = 0f;
  19. /// <summary>
  20. /// Max iterations per frame
  21. /// </summary>
  22. public int maxIterations = 4;
  23. /// <summary>
  24. /// If true, rotation limits (if existing) will be applied on each iteration.
  25. /// </summary>
  26. public bool useRotationLimits = true;
  27. /// <summary>
  28. /// Solve in 2D?
  29. /// </summary>
  30. public bool XY;
  31. /// <summary>
  32. /// The hierarchy of bones.
  33. /// </summary>
  34. public Bone[] bones = new Bone[0];
  35. /// <summary>
  36. /// Rebuild the bone hierarcy and reinitiate the solver.
  37. /// </summary>
  38. /// <returns>
  39. /// Returns true if the new chain is valid.
  40. /// </returns>
  41. public bool SetChain(Transform[] hierarchy, Transform root) {
  42. if (bones == null || bones.Length != hierarchy.Length) bones = new Bone[hierarchy.Length];
  43. for (int i = 0; i < hierarchy.Length; i++) {
  44. if (bones[i] == null) bones[i] = new IKSolver.Bone();
  45. bones[i].transform = hierarchy[i];
  46. }
  47. Initiate(root);
  48. return initiated;
  49. }
  50. /// <summary>
  51. /// Adds a bone to the chain.
  52. /// </summary>
  53. public void AddBone(Transform bone) {
  54. Transform[] newBones = new Transform[bones.Length + 1];
  55. for (int i = 0; i < bones.Length; i++) {
  56. newBones[i] = bones[i].transform;
  57. }
  58. newBones[newBones.Length - 1] = bone;
  59. SetChain(newBones, root);
  60. }
  61. public override void StoreDefaultLocalState() {
  62. for (int i = 0; i < bones.Length; i++) bones[i].StoreDefaultLocalState();
  63. }
  64. public override void FixTransforms() {
  65. if (!initiated) return;
  66. if (IKPositionWeight <= 0f) return;
  67. for (int i = 0; i < bones.Length; i++) bones[i].FixTransform();
  68. }
  69. public override bool IsValid(ref string message) {
  70. if (bones.Length == 0) {
  71. message = "IK chain has no Bones.";
  72. return false;
  73. }
  74. if (bones.Length < minBones) {
  75. message = "IK chain has less than " + minBones + " Bones.";
  76. return false;
  77. }
  78. foreach (Bone bone in bones) {
  79. if (bone.transform == null) {
  80. message = "One of the Bones is null.";
  81. return false;
  82. }
  83. }
  84. Transform duplicate = ContainsDuplicateBone(bones);
  85. if (duplicate != null) {
  86. message = duplicate.name + " is represented multiple times in the Bones.";
  87. return false;
  88. }
  89. if (!allowCommonParent && !HierarchyIsValid(bones)) {
  90. message = "Invalid bone hierarchy detected. IK requires for it's bones to be parented to each other in descending order.";
  91. return false;
  92. }
  93. if (!boneLengthCanBeZero) {
  94. for (int i = 0; i < bones.Length - 1; i++) {
  95. float l = (bones[i].transform.position - bones[i + 1].transform.position).magnitude;
  96. if (l == 0) {
  97. message = "Bone " + i + " length is zero.";
  98. return false;
  99. }
  100. }
  101. }
  102. return true;
  103. }
  104. public override IKSolver.Point[] GetPoints() {
  105. return bones as IKSolver.Point[];
  106. }
  107. public override IKSolver.Point GetPoint(Transform transform) {
  108. for (int i = 0; i < bones.Length; i++) if (bones[i].transform == transform) return bones[i] as IKSolver.Point;
  109. return null;
  110. }
  111. #endregion Main Interface
  112. protected virtual int minBones { get { return 2; }}
  113. protected virtual bool boneLengthCanBeZero { get { return true; }}
  114. protected virtual bool allowCommonParent { get { return false; }}
  115. protected override void OnInitiate() {}
  116. protected override void OnUpdate() {}
  117. protected Vector3 lastLocalDirection;
  118. protected float chainLength;
  119. /*
  120. * Initiates all bones to match their current state
  121. * */
  122. protected void InitiateBones() {
  123. chainLength = 0;
  124. for (int i = 0; i < bones.Length; i++) {
  125. // Find out which local axis is directed at child/target position
  126. if (i < bones.Length - 1) {
  127. bones[i].length = (bones[i].transform.position - bones[i + 1].transform.position).magnitude;
  128. chainLength += bones[i].length;
  129. Vector3 nextPosition = bones[i + 1].transform.position;
  130. bones[i].axis = Quaternion.Inverse(bones[i].transform.rotation) * (nextPosition - bones[i].transform.position);
  131. // Disable Rotation Limits from updating to take control of their execution order
  132. if (bones[i].rotationLimit != null) {
  133. if (XY) {
  134. if (bones[i].rotationLimit is RotationLimitHinge) {
  135. } else Warning.Log("Only Hinge Rotation Limits should be used on 2D IK solvers.", bones[i].transform);
  136. }
  137. bones[i].rotationLimit.Disable();
  138. }
  139. } else {
  140. bones[i].axis = Quaternion.Inverse(bones[i].transform.rotation) * (bones[bones.Length - 1].transform.position - bones[0].transform.position);
  141. }
  142. }
  143. }
  144. #region Optimizations
  145. /*
  146. * Gets the direction from last bone to first bone in first bone's local space.
  147. * */
  148. protected virtual Vector3 localDirection {
  149. get {
  150. return bones[0].transform.InverseTransformDirection(bones[bones.Length - 1].transform.position - bones[0].transform.position);
  151. }
  152. }
  153. /*
  154. * Gets the offset from last position of the last bone to its current position.
  155. * */
  156. protected float positionOffset {
  157. get {
  158. return Vector3.SqrMagnitude(localDirection - lastLocalDirection);
  159. }
  160. }
  161. #endregion Optimizations
  162. /*
  163. * Get target offset to break out of the linear singularity issue
  164. * */
  165. protected Vector3 GetSingularityOffset() {
  166. if (!SingularityDetected()) return Vector3.zero;
  167. Vector3 IKDirection = (IKPosition - bones[0].transform.position).normalized;
  168. Vector3 secondaryDirection = new Vector3(IKDirection.y, IKDirection.z, IKDirection.x);
  169. // Avoiding getting locked by the Hinge Rotation Limit
  170. if (useRotationLimits && bones[bones.Length - 2].rotationLimit != null && bones[bones.Length - 2].rotationLimit is RotationLimitHinge) {
  171. secondaryDirection = bones[bones.Length - 2].transform.rotation * bones[bones.Length - 2].rotationLimit.axis;
  172. }
  173. return Vector3.Cross(IKDirection, secondaryDirection) * bones[bones.Length - 2].length * 0.5f;
  174. }
  175. /*
  176. * Detects linear singularity issue when the direction from first bone to IKPosition matches the direction from first bone to the last bone.
  177. * */
  178. private bool SingularityDetected() {
  179. if (!initiated) return false;
  180. Vector3 toLastBone = bones[bones.Length - 1].transform.position - bones[0].transform.position;
  181. Vector3 toIKPosition = IKPosition - bones[0].transform.position;
  182. float toLastBoneDistance = toLastBone.magnitude;
  183. float toIKPositionDistance = toIKPosition.magnitude;
  184. if (toLastBoneDistance < toIKPositionDistance) return false;
  185. if (toLastBoneDistance < chainLength - (bones[bones.Length - 2].length * 0.1f)) return false;
  186. if (toLastBoneDistance == 0) return false;
  187. if (toIKPositionDistance == 0) return false;
  188. if (toIKPositionDistance > toLastBoneDistance) return false;
  189. float dot = Vector3.Dot(toLastBone / toLastBoneDistance, toIKPosition / toIKPositionDistance);
  190. if (dot < 0.999f) return false;
  191. return true;
  192. }
  193. }
  194. }