IKSolverFABRIK.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using UnityEngine;
  2. using System.Collections;
  3. using System;
  4. namespace RootMotion.FinalIK {
  5. /// <summary>
  6. /// Forward and Backward Reaching Inverse Kinematics solver.
  7. ///
  8. /// This class is based on the "FABRIK: A fast, iterative solver for the inverse kinematics problem." paper by Aristidou, A., Lasenby, J.
  9. /// </summary>
  10. [System.Serializable]
  11. public class IKSolverFABRIK : IKSolverHeuristic {
  12. #region Main Interface
  13. /// <summary>
  14. /// Solving stage 1 of the %FABRIK algorithm.
  15. /// </summary>
  16. public void SolveForward(Vector3 position) {
  17. if (!initiated) {
  18. if (!Warning.logged) LogWarning("Trying to solve uninitiated FABRIK chain.");
  19. return;
  20. }
  21. OnPreSolve();
  22. ForwardReach(position);
  23. }
  24. /// <summary>
  25. /// Solving stage 2 of the %FABRIK algorithm.
  26. /// </summary>
  27. public void SolveBackward(Vector3 position) {
  28. if (!initiated) {
  29. if (!Warning.logged) LogWarning("Trying to solve uninitiated FABRIK chain.");
  30. return;
  31. }
  32. BackwardReach(position);
  33. OnPostSolve();
  34. }
  35. public override Vector3 GetIKPosition() {
  36. if (target != null) return target.position;
  37. return IKPosition;
  38. }
  39. /// <summary>
  40. /// Called before each iteration of the solver.
  41. /// </summary>
  42. public IterationDelegate OnPreIteration;
  43. #endregion Main Interface
  44. private bool[] limitedBones = new bool[0];
  45. private Vector3[] solverLocalPositions = new Vector3[0];
  46. protected override void OnInitiate() {
  47. if (firstInitiation || !Application.isPlaying) IKPosition = bones[bones.Length - 1].transform.position;
  48. for (int i = 0; i < bones.Length; i++) {
  49. bones[i].solverPosition = bones[i].transform.position;
  50. bones[i].solverRotation = bones[i].transform.rotation;
  51. }
  52. limitedBones = new bool[bones.Length];
  53. solverLocalPositions = new Vector3[bones.Length];
  54. InitiateBones();
  55. for (int i = 0; i < bones.Length; i++) {
  56. solverLocalPositions[i] = Quaternion.Inverse(GetParentSolverRotation(i)) * (bones[i].transform.position - GetParentSolverPosition(i));
  57. }
  58. }
  59. protected override void OnUpdate() {
  60. if (IKPositionWeight <= 0) return;
  61. IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
  62. OnPreSolve();
  63. if (target != null) IKPosition = target.position;
  64. if (XY) IKPosition.z = bones[0].transform.position.z;
  65. Vector3 singularityOffset = maxIterations > 1? GetSingularityOffset(): Vector3.zero;
  66. // Iterating the solver
  67. for (int i = 0; i < maxIterations; i++) {
  68. // Optimizations
  69. if (singularityOffset == Vector3.zero && i >= 1 && tolerance > 0 && positionOffset < tolerance * tolerance) break;
  70. lastLocalDirection = localDirection;
  71. if (OnPreIteration != null) OnPreIteration(i);
  72. Solve(IKPosition + (i == 0? singularityOffset: Vector3.zero));
  73. }
  74. OnPostSolve();
  75. }
  76. /*
  77. * If true, the solver will work with 0 length bones
  78. * */
  79. protected override bool boneLengthCanBeZero { get { return false; }} // Returning false here also ensures that the bone lengths will be calculated
  80. /*
  81. * Interpolates the joint position to match the bone's length
  82. */
  83. private Vector3 SolveJoint(Vector3 pos1, Vector3 pos2, float length) {
  84. if (XY) pos1.z = pos2.z;
  85. return pos2 + (pos1 - pos2).normalized * length;
  86. }
  87. /*
  88. * Check if bones have moved from last solved positions
  89. * */
  90. private void OnPreSolve() {
  91. chainLength = 0;
  92. for (int i = 0; i < bones.Length; i++) {
  93. bones[i].solverPosition = bones[i].transform.position;
  94. bones[i].solverRotation = bones[i].transform.rotation;
  95. if (i < bones.Length - 1) {
  96. bones[i].length = (bones[i].transform.position - bones[i + 1].transform.position).magnitude;
  97. bones[i].axis = Quaternion.Inverse(bones[i].transform.rotation) * (bones[i + 1].transform.position - bones[i].transform.position);
  98. chainLength += bones[i].length;
  99. }
  100. if (useRotationLimits) solverLocalPositions[i] = Quaternion.Inverse(GetParentSolverRotation(i)) * (bones[i].transform.position - GetParentSolverPosition(i));
  101. }
  102. }
  103. /*
  104. * After solving the chain
  105. * */
  106. private void OnPostSolve() {
  107. // Rotating bones to match the solver positions
  108. if (!useRotationLimits) MapToSolverPositions();
  109. else MapToSolverPositionsLimited();
  110. lastLocalDirection = localDirection;
  111. }
  112. private void Solve(Vector3 targetPosition) {
  113. // Forward reaching
  114. ForwardReach(targetPosition);
  115. // Backward reaching
  116. BackwardReach(bones[0].transform.position);
  117. }
  118. /*
  119. * Stage 1 of FABRIK algorithm
  120. * */
  121. private void ForwardReach(Vector3 position) {
  122. // Lerp last bone's solverPosition to position
  123. bones[bones.Length - 1].solverPosition = Vector3.Lerp(bones[bones.Length - 1].solverPosition, position, IKPositionWeight);
  124. for (int i = 0; i < limitedBones.Length; i++) limitedBones[i] = false;
  125. for (int i = bones.Length - 2; i > -1; i--) {
  126. // Finding joint positions
  127. bones[i].solverPosition = SolveJoint(bones[i].solverPosition, bones[i + 1].solverPosition, bones[i].length);
  128. // Limiting bone rotation forward
  129. LimitForward(i, i + 1);
  130. }
  131. // Limiting the first bone's rotation
  132. LimitForward(0, 0);
  133. }
  134. private void SolverMove(int index, Vector3 offset) {
  135. for (int i = index; i < bones.Length; i++) {
  136. bones[i].solverPosition += offset;
  137. }
  138. }
  139. private void SolverRotate(int index, Quaternion rotation, bool recursive) {
  140. for (int i = index; i < bones.Length; i++) {
  141. bones[i].solverRotation = rotation * bones[i].solverRotation;
  142. if (!recursive) return;
  143. }
  144. }
  145. private void SolverRotateChildren(int index, Quaternion rotation) {
  146. for (int i = index + 1; i < bones.Length; i++) {
  147. bones[i].solverRotation = rotation * bones[i].solverRotation;
  148. }
  149. }
  150. private void SolverMoveChildrenAroundPoint(int index, Quaternion rotation) {
  151. for (int i = index + 1; i < bones.Length; i++) {
  152. Vector3 dir = bones[i].solverPosition - bones[index].solverPosition;
  153. bones[i].solverPosition = bones[index].solverPosition + rotation * dir;
  154. }
  155. }
  156. private Quaternion GetParentSolverRotation(int index) {
  157. if (index > 0) return bones[index - 1].solverRotation;
  158. if (bones[0].transform.parent == null) return Quaternion.identity;
  159. return bones[0].transform.parent.rotation;
  160. }
  161. private Vector3 GetParentSolverPosition(int index) {
  162. if (index > 0) return bones[index - 1].solverPosition;
  163. if (bones[0].transform.parent == null) return Vector3.zero;
  164. return bones[0].transform.parent.position;
  165. }
  166. private Quaternion GetLimitedRotation(int index, Quaternion q, out bool changed) {
  167. changed = false;
  168. Quaternion parentRotation = GetParentSolverRotation(index);
  169. Quaternion localRotation = Quaternion.Inverse(parentRotation) * q;
  170. Quaternion limitedLocalRotation = bones[index].rotationLimit.GetLimitedLocalRotation(localRotation, out changed);
  171. if (!changed) return q;
  172. return parentRotation * limitedLocalRotation;
  173. }
  174. /*
  175. * Applying rotation limit to a bone in stage 1 in a more stable way
  176. * */
  177. private void LimitForward(int rotateBone, int limitBone) {
  178. if (!useRotationLimits) return;
  179. if (bones[limitBone].rotationLimit == null) return;
  180. // Storing last bone's position before applying the limit
  181. Vector3 lastBoneBeforeLimit = bones[bones.Length - 1].solverPosition;
  182. // Moving and rotating this bone and all its children to their solver positions
  183. for (int i = rotateBone; i < bones.Length - 1; i++) {
  184. if (limitedBones[i]) break;
  185. Quaternion fromTo = Quaternion.FromToRotation(bones[i].solverRotation * bones[i].axis, bones[i + 1].solverPosition - bones[i].solverPosition);
  186. SolverRotate(i, fromTo, false);
  187. }
  188. // Limit the bone's rotation
  189. bool changed = false;
  190. Quaternion afterLimit = GetLimitedRotation(limitBone, bones[limitBone].solverRotation, out changed);
  191. if (changed) {
  192. // Rotating and positioning the hierarchy so that the last bone's position is maintained
  193. if (limitBone < bones.Length - 1) {
  194. Quaternion change = QuaTools.FromToRotation(bones[limitBone].solverRotation, afterLimit);
  195. bones[limitBone].solverRotation = afterLimit;
  196. SolverRotateChildren(limitBone, change);
  197. SolverMoveChildrenAroundPoint(limitBone, change);
  198. // Rotating to compensate for the limit
  199. Quaternion fromTo = Quaternion.FromToRotation(bones[bones.Length - 1].solverPosition - bones[rotateBone].solverPosition, lastBoneBeforeLimit - bones[rotateBone].solverPosition);
  200. SolverRotate(rotateBone, fromTo, true);
  201. SolverMoveChildrenAroundPoint(rotateBone, fromTo);
  202. // Moving the bone so that last bone maintains it's initial position
  203. SolverMove(rotateBone, lastBoneBeforeLimit - bones[bones.Length - 1].solverPosition);
  204. } else {
  205. // last bone
  206. bones[limitBone].solverRotation = afterLimit;
  207. }
  208. }
  209. limitedBones[limitBone] = true;
  210. }
  211. /*
  212. * Stage 2 of FABRIK algorithm
  213. * */
  214. private void BackwardReach(Vector3 position) {
  215. if (useRotationLimits) BackwardReachLimited(position);
  216. else BackwardReachUnlimited(position);
  217. }
  218. /*
  219. * Stage 2 of FABRIK algorithm without rotation limits
  220. * */
  221. private void BackwardReachUnlimited(Vector3 position) {
  222. // Move first bone to position
  223. bones[0].solverPosition = position;
  224. // Finding joint positions
  225. for (int i = 1; i < bones.Length; i++) {
  226. bones[i].solverPosition = SolveJoint(bones[i].solverPosition, bones[i - 1].solverPosition, bones[i - 1].length);
  227. }
  228. }
  229. /*
  230. * Stage 2 of FABRIK algorithm with limited rotations
  231. * */
  232. private void BackwardReachLimited(Vector3 position) {
  233. // Move first bone to position
  234. bones[0].solverPosition = position;
  235. // Applying rotation limits bone by bone
  236. for (int i = 0; i < bones.Length - 1; i++) {
  237. // Rotating bone to look at the solved joint position
  238. Vector3 nextPosition = SolveJoint(bones[i + 1].solverPosition, bones[i].solverPosition, bones[i].length);
  239. Quaternion swing = Quaternion.FromToRotation(bones[i].solverRotation * bones[i].axis, nextPosition - bones[i].solverPosition);
  240. Quaternion targetRotation = swing * bones[i].solverRotation;
  241. // Rotation Constraints
  242. if (bones[i].rotationLimit != null) {
  243. bool changed = false;
  244. targetRotation = GetLimitedRotation(i, targetRotation, out changed);
  245. }
  246. Quaternion fromTo = QuaTools.FromToRotation(bones[i].solverRotation, targetRotation);
  247. bones[i].solverRotation = targetRotation;
  248. SolverRotateChildren(i, fromTo);
  249. // Positioning the next bone to its default local position
  250. bones[i + 1].solverPosition = bones[i].solverPosition + bones[i].solverRotation * solverLocalPositions[i + 1];
  251. }
  252. // Reconstruct solver rotations to protect from invalid Quaternions
  253. for (int i = 0; i < bones.Length; i++) {
  254. bones[i].solverRotation = Quaternion.LookRotation(bones[i].solverRotation * Vector3.forward, bones[i].solverRotation * Vector3.up);
  255. }
  256. }
  257. /*
  258. * Rotate bones to match the solver positions when not using Rotation Limits
  259. * */
  260. private void MapToSolverPositions() {
  261. bones[0].transform.position = bones[0].solverPosition;
  262. for (int i = 0; i < bones.Length - 1; i++) {
  263. if (XY) {
  264. bones[i].Swing2D(bones[i + 1].solverPosition);
  265. } else {
  266. bones[i].Swing(bones[i + 1].solverPosition);
  267. }
  268. }
  269. }
  270. /*
  271. * Rotate bones to match the solver positions when using Rotation Limits
  272. * */
  273. private void MapToSolverPositionsLimited() {
  274. bones[0].transform.position = bones[0].solverPosition;
  275. for (int i = 0; i < bones.Length; i++) {
  276. if (i < bones.Length - 1) bones[i].transform.rotation = bones[i].solverRotation;
  277. }
  278. }
  279. }
  280. }