IKSolverCCD.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. using UnityEngine;
  2. using System.Collections;
  3. using System;
  4. namespace RootMotion.FinalIK {
  5. /// <summary>
  6. /// CCD (Cyclic Coordinate Descent) constrainable heuristic inverse kinematics algorithm.
  7. /// </summary>
  8. [System.Serializable]
  9. public class IKSolverCCD : IKSolverHeuristic {
  10. #region Main Interface
  11. /// <summary>
  12. /// CCD tends to overemphasise the rotations of the bones closer to the target position. Reducing bone weight down the hierarchy will compensate for this effect.
  13. /// </summary>
  14. public void FadeOutBoneWeights() {
  15. if (bones.Length < 2) return;
  16. bones[0].weight = 1f;
  17. float step = 1f / (bones.Length - 1);
  18. for (int i = 1; i < bones.Length; i++) {
  19. bones[i].weight = step * (bones.Length - 1 - i);
  20. }
  21. }
  22. /// <summary>
  23. /// Called before each iteration of the solver.
  24. /// </summary>
  25. public IterationDelegate OnPreIteration;
  26. #endregion Main Interface
  27. protected override void OnInitiate() {
  28. if (firstInitiation || !Application.isPlaying) IKPosition = bones[bones.Length - 1].transform.position;
  29. InitiateBones();
  30. }
  31. protected override void OnUpdate() {
  32. if (IKPositionWeight <= 0) return;
  33. IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
  34. if (target != null) IKPosition = target.position;
  35. if (XY) IKPosition.z = bones[0].transform.position.z;
  36. Vector3 singularityOffset = maxIterations > 1? GetSingularityOffset(): Vector3.zero;
  37. // Iterating the solver
  38. for (int i = 0; i < maxIterations; i++) {
  39. // Optimizations
  40. if (singularityOffset == Vector3.zero && i >= 1 && tolerance > 0 && positionOffset < tolerance * tolerance) break;
  41. lastLocalDirection = localDirection;
  42. if (OnPreIteration != null) OnPreIteration(i);
  43. Solve(IKPosition + (i == 0? singularityOffset: Vector3.zero));
  44. }
  45. lastLocalDirection = localDirection;
  46. }
  47. /*
  48. * Solve the CCD algorithm
  49. * */
  50. protected void Solve(Vector3 targetPosition) {
  51. // 2D
  52. if (XY) {
  53. for (int i = bones.Length - 2; i > -1; i--) {
  54. //CCD tends to overemphasise the rotations of the bones closer to the target position. Reducing bone weight down the hierarchy will compensate for this effect.
  55. float w = bones[i].weight * IKPositionWeight;
  56. if (w > 0f) {
  57. Vector3 toLastBone = bones[bones.Length - 1].transform.position - bones[i].transform.position;
  58. Vector3 toTarget = targetPosition - bones[i].transform.position;
  59. float angleToLastBone = Mathf.Atan2(toLastBone.x, toLastBone.y) * Mathf.Rad2Deg;
  60. float angleToTarget = Mathf.Atan2(toTarget.x, toTarget.y) * Mathf.Rad2Deg;
  61. // Rotation to direct the last bone to the target
  62. bones[i].transform.rotation = Quaternion.AngleAxis(Mathf.DeltaAngle(angleToLastBone, angleToTarget) * w, Vector3.back) * bones[i].transform.rotation;
  63. }
  64. // Rotation Constraints
  65. if (useRotationLimits && bones[i].rotationLimit != null) bones[i].rotationLimit.Apply();
  66. }
  67. // 3D
  68. } else {
  69. for (int i = bones.Length - 2; i > -1; i--) {
  70. // Slerp if weight is < 0
  71. //CCD tends to overemphasise the rotations of the bones closer to the target position. Reducing bone weight down the hierarchy will compensate for this effect.
  72. float w = bones[i].weight * IKPositionWeight;
  73. if (w > 0f) {
  74. Vector3 toLastBone = bones[bones.Length - 1].transform.position - bones[i].transform.position;
  75. Vector3 toTarget = targetPosition - bones[i].transform.position;
  76. // Get the rotation to direct the last bone to the target
  77. Quaternion targetRotation = Quaternion.FromToRotation(toLastBone, toTarget) * bones[i].transform.rotation;
  78. if (w >= 1) bones[i].transform.rotation = targetRotation;
  79. else bones[i].transform.rotation = Quaternion.Lerp(bones[i].transform.rotation, targetRotation, w);
  80. }
  81. // Rotation Constraints
  82. if (useRotationLimits && bones[i].rotationLimit != null) bones[i].rotationLimit.Apply();
  83. }
  84. }
  85. }
  86. }
  87. }