IKEffector.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion.FinalIK {
  4. /// <summary>
  5. /// Effector for manipulating node based %IK solvers.
  6. /// </summary>
  7. [System.Serializable]
  8. public class IKEffector {
  9. #region Main Interface
  10. /// <summary>
  11. /// Gets the main node.
  12. /// </summary>
  13. public IKSolver.Node GetNode(IKSolverFullBody solver) {
  14. return solver.chain[chainIndex].nodes[nodeIndex];
  15. }
  16. /// <summary>
  17. /// The node transform used by this effector.
  18. /// </summary>
  19. public Transform bone;
  20. /// <summary>
  21. /// The target Transform (optional, you can use just the position and rotation instead).
  22. /// </summary>
  23. public Transform target;
  24. /// <summary>
  25. /// The position weight.
  26. /// </summary>
  27. [Range(0f, 1f)]
  28. public float positionWeight;
  29. /// <summary>
  30. /// The rotation weight.
  31. /// </summary>
  32. [Range(0f, 1f)]
  33. public float rotationWeight;
  34. /// <summary>
  35. /// The effector position in world space.
  36. /// </summary>
  37. public Vector3 position = Vector3.zero;
  38. /// <summary>
  39. /// The effector rotation relative to default rotation in world space.
  40. /// </summary>
  41. public Quaternion rotation = Quaternion.identity;
  42. /// <summary>
  43. /// The position offset in world space. positionOffset will be reset to Vector3.zero each frame after the solver is complete.
  44. /// </summary>
  45. public Vector3 positionOffset;
  46. /// <summary>
  47. /// Is this the last effector of a node chain?
  48. /// </summary>
  49. public bool isEndEffector { get; private set; }
  50. /// <summary>
  51. /// If false, child nodes will be ignored by this effector (if it has any).
  52. /// </summary>
  53. public bool effectChildNodes = true;
  54. /// <summary>
  55. /// Keeps the node position relative to the triangle defined by the plane bones (applies only to end-effectors).
  56. /// </summary>
  57. [Range(0f, 1f)]
  58. public float maintainRelativePositionWeight;
  59. /// <summary>
  60. /// Pins the effector to the animated position of it's bone.
  61. /// </summary>
  62. public void PinToBone(float positionWeight, float rotationWeight) {
  63. position = bone.position;
  64. this.positionWeight = Mathf.Clamp(positionWeight, 0f, 1f);
  65. rotation = bone.rotation;
  66. this.rotationWeight = Mathf.Clamp(rotationWeight, 0f, 1f);
  67. }
  68. #endregion Main Interface
  69. public Transform[] childBones = new Transform[0]; // The optional list of other bones that positionOffset and position of this effector will be applied to.
  70. public Transform planeBone1; // The first bone defining the parent plane.
  71. public Transform planeBone2; // The second bone defining the parent plane.
  72. public Transform planeBone3; // The third bone defining the parent plane.
  73. public Quaternion planeRotationOffset = Quaternion.identity; // Used by Bend Constraints
  74. private float posW, rotW;
  75. private Vector3[] localPositions = new Vector3[0];
  76. private bool usePlaneNodes;
  77. private Quaternion animatedPlaneRotation = Quaternion.identity;
  78. private Vector3 animatedPosition;
  79. private bool firstUpdate;
  80. private int chainIndex = -1;
  81. private int nodeIndex = -1;
  82. private int plane1ChainIndex;
  83. private int plane1NodeIndex = -1;
  84. private int plane2ChainIndex = -1;
  85. private int plane2NodeIndex = -1;
  86. private int plane3ChainIndex = -1;
  87. private int plane3NodeIndex = -1;
  88. private int[] childChainIndexes = new int[0];
  89. private int[] childNodeIndexes = new int[0];
  90. public IKEffector() {}
  91. public IKEffector (Transform bone, Transform[] childBones) {
  92. this.bone = bone;
  93. this.childBones = childBones;
  94. }
  95. /*
  96. * Determines whether this IKEffector is valid or not.
  97. * */
  98. public bool IsValid(IKSolver solver, ref string message) {
  99. if (bone == null) {
  100. message = "IK Effector bone is null.";
  101. return false;
  102. }
  103. if (solver.GetPoint(bone) == null) {
  104. message = "IK Effector is referencing to a bone '" + bone.name + "' that does not excist in the Node Chain.";
  105. return false;
  106. }
  107. foreach (Transform b in childBones) {
  108. if (b == null) {
  109. message = "IK Effector contains a null reference.";
  110. return false;
  111. }
  112. }
  113. foreach (Transform b in childBones) {
  114. if (solver.GetPoint(b) == null) {
  115. message = "IK Effector is referencing to a bone '" + b.name + "' that does not excist in the Node Chain.";
  116. return false;
  117. }
  118. }
  119. if (planeBone1 != null && solver.GetPoint(planeBone1) == null) {
  120. message = "IK Effector is referencing to a bone '" + planeBone1.name + "' that does not excist in the Node Chain.";
  121. return false;
  122. }
  123. if (planeBone2 != null && solver.GetPoint(planeBone2) == null) {
  124. message = "IK Effector is referencing to a bone '" + planeBone2.name + "' that does not excist in the Node Chain.";
  125. return false;
  126. }
  127. if (planeBone3 != null && solver.GetPoint(planeBone3) == null) {
  128. message = "IK Effector is referencing to a bone '" + planeBone3.name + "' that does not excist in the Node Chain.";
  129. return false;
  130. }
  131. return true;
  132. }
  133. /*
  134. * Initiate the effector, set default values
  135. * */
  136. public void Initiate(IKSolverFullBody solver) {
  137. position = bone.position;
  138. rotation = bone.rotation;
  139. animatedPlaneRotation = Quaternion.identity;
  140. // Getting the node
  141. solver.GetChainAndNodeIndexes(bone, out chainIndex, out nodeIndex);
  142. // Child nodes
  143. childChainIndexes = new int[childBones.Length];
  144. childNodeIndexes = new int[childBones.Length];
  145. for (int i = 0; i < childBones.Length; i++) {
  146. solver.GetChainAndNodeIndexes(childBones[i], out childChainIndexes[i], out childNodeIndexes[i]);
  147. }
  148. localPositions = new Vector3[childBones.Length];
  149. // Plane nodes
  150. usePlaneNodes = false;
  151. if (planeBone1 != null) {
  152. solver.GetChainAndNodeIndexes(planeBone1, out plane1ChainIndex, out plane1NodeIndex);
  153. if (planeBone2 != null) {
  154. solver.GetChainAndNodeIndexes(planeBone2, out plane2ChainIndex, out plane2NodeIndex);
  155. if (planeBone3 != null) {
  156. solver.GetChainAndNodeIndexes(planeBone3, out plane3ChainIndex, out plane3NodeIndex);
  157. usePlaneNodes = true;
  158. }
  159. }
  160. isEndEffector = true;
  161. } else isEndEffector = false;
  162. }
  163. /*
  164. * Clear node offset
  165. * */
  166. public void ResetOffset(IKSolverFullBody solver) {
  167. solver.GetNode(chainIndex, nodeIndex).offset = Vector3.zero;
  168. for (int i = 0; i < childChainIndexes.Length; i++) {
  169. solver.GetNode(childChainIndexes[i], childNodeIndexes[i]).offset = Vector3.zero;
  170. }
  171. }
  172. /*
  173. * Set the position and rotation to match the target
  174. * */
  175. public void SetToTarget() {
  176. if (target == null) return;
  177. position = target.position;
  178. rotation = target.rotation;
  179. }
  180. /*
  181. * Presolving, applying offset
  182. * */
  183. public void OnPreSolve(IKSolverFullBody solver) {
  184. positionWeight = Mathf.Clamp(positionWeight, 0f, 1f);
  185. rotationWeight = Mathf.Clamp(rotationWeight, 0f, 1f);
  186. maintainRelativePositionWeight = Mathf.Clamp(maintainRelativePositionWeight, 0f, 1f);
  187. // Calculating weights
  188. posW = positionWeight * solver.IKPositionWeight;
  189. rotW = rotationWeight * solver.IKPositionWeight;
  190. solver.GetNode(chainIndex, nodeIndex).effectorPositionWeight = posW;
  191. solver.GetNode(chainIndex, nodeIndex).effectorRotationWeight = rotW;
  192. solver.GetNode(chainIndex, nodeIndex).solverRotation = rotation;
  193. if (float.IsInfinity(positionOffset.x) ||
  194. float.IsInfinity(positionOffset.y) ||
  195. float.IsInfinity(positionOffset.z)
  196. ) Debug.LogError("Invalid IKEffector.positionOffset (contains Infinity)! Please make sure not to set IKEffector.positionOffset to infinite values.", bone);
  197. if (float.IsNaN(positionOffset.x) ||
  198. float.IsNaN(positionOffset.y) ||
  199. float.IsNaN(positionOffset.z)
  200. ) Debug.LogError("Invalid IKEffector.positionOffset (contains NaN)! Please make sure not to set IKEffector.positionOffset to NaN values.", bone);
  201. if (positionOffset.sqrMagnitude > 10000000000f) Debug.LogError("Additive effector positionOffset detected in Full Body IK (extremely large value). Make sure you are not circularily adding to effector positionOffset each frame.", bone);
  202. if (float.IsInfinity(position.x) ||
  203. float.IsInfinity(position.y) ||
  204. float.IsInfinity(position.z)
  205. ) Debug.LogError("Invalid IKEffector.position (contains Infinity)!");
  206. solver.GetNode(chainIndex, nodeIndex).offset += positionOffset * solver.IKPositionWeight;
  207. if (effectChildNodes && solver.iterations > 0) {
  208. for (int i = 0; i < childBones.Length; i++) {
  209. localPositions[i] = childBones[i].transform.position - bone.transform.position;
  210. solver.GetNode(childChainIndexes[i], childNodeIndexes[i]).offset += positionOffset * solver.IKPositionWeight;
  211. }
  212. }
  213. // Relative to Plane
  214. if (usePlaneNodes && maintainRelativePositionWeight > 0f) {
  215. animatedPlaneRotation = Quaternion.LookRotation(planeBone2.position - planeBone1.position, planeBone3.position - planeBone1.position);;
  216. }
  217. firstUpdate = true;
  218. }
  219. /*
  220. * Called after writing the pose
  221. * */
  222. public void OnPostWrite() {
  223. positionOffset = Vector3.zero;
  224. }
  225. /*
  226. * Rotation of plane nodes in the solver
  227. * */
  228. private Quaternion GetPlaneRotation(IKSolverFullBody solver) {
  229. Vector3 p1 = solver.GetNode(plane1ChainIndex, plane1NodeIndex).solverPosition;
  230. Vector3 p2 = solver.GetNode(plane2ChainIndex, plane2NodeIndex).solverPosition;
  231. Vector3 p3 = solver.GetNode(plane3ChainIndex, plane3NodeIndex).solverPosition;
  232. Vector3 viewingVector = p2 - p1;
  233. Vector3 upVector = p3 - p1;
  234. if (viewingVector == Vector3.zero) {
  235. Warning.Log("Make sure you are not placing 2 or more FBBIK effectors of the same chain to exactly the same position.", bone);
  236. return Quaternion.identity;
  237. }
  238. return Quaternion.LookRotation(viewingVector, upVector);
  239. }
  240. /*
  241. * Manipulating node solverPosition
  242. * */
  243. public void Update(IKSolverFullBody solver) {
  244. if (firstUpdate) {
  245. animatedPosition = bone.position + solver.GetNode(chainIndex, nodeIndex).offset;
  246. firstUpdate = false;
  247. }
  248. solver.GetNode(chainIndex, nodeIndex).solverPosition = Vector3.Lerp(GetPosition(solver, out planeRotationOffset), position, posW);
  249. // Child nodes
  250. if (!effectChildNodes) return;
  251. for (int i = 0; i < childBones.Length; i++) {
  252. solver.GetNode(childChainIndexes[i], childNodeIndexes[i]).solverPosition = Vector3.Lerp(solver.GetNode(childChainIndexes[i], childNodeIndexes[i]).solverPosition, solver.GetNode(chainIndex, nodeIndex).solverPosition + localPositions[i], posW);
  253. }
  254. }
  255. /*
  256. * Gets the starting position of the iteration
  257. * */
  258. private Vector3 GetPosition(IKSolverFullBody solver, out Quaternion planeRotationOffset) {
  259. planeRotationOffset = Quaternion.identity;
  260. if (!isEndEffector) return solver.GetNode(chainIndex, nodeIndex).solverPosition; // non end-effectors are always free
  261. if (maintainRelativePositionWeight <= 0f) return animatedPosition;
  262. // Maintain relative position
  263. Vector3 p = bone.position;
  264. Vector3 dir = p - planeBone1.position;
  265. planeRotationOffset = GetPlaneRotation(solver) * Quaternion.Inverse(animatedPlaneRotation);
  266. p = solver.GetNode(plane1ChainIndex, plane1NodeIndex).solverPosition + planeRotationOffset * dir;
  267. // Interpolate the rotation offset
  268. planeRotationOffset = Quaternion.Lerp(Quaternion.identity, planeRotationOffset, maintainRelativePositionWeight);
  269. return Vector3.Lerp(animatedPosition, p + solver.GetNode(chainIndex, nodeIndex).offset, maintainRelativePositionWeight);
  270. }
  271. }
  272. }