IKMappingSpine.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion.FinalIK {
  4. /// <summary>
  5. /// Mapping a bone hierarchy to 2 triangles defined by the hip and chest planes.
  6. /// </summary>
  7. [System.Serializable]
  8. public class IKMappingSpine: IKMapping {
  9. #region Main Interface
  10. /// <summary>
  11. /// The spine bones.
  12. /// </summary>
  13. public Transform[] spineBones;
  14. /// <summary>
  15. /// The left upper arm bone.
  16. /// </summary>
  17. public Transform leftUpperArmBone;
  18. /// <summary>
  19. /// The right upper arm bone.
  20. /// </summary>
  21. public Transform rightUpperArmBone;
  22. /// <summary>
  23. /// The left thigh bone.
  24. /// </summary>
  25. public Transform leftThighBone;
  26. /// <summary>
  27. /// The right thigh bone.
  28. /// </summary>
  29. public Transform rightThighBone;
  30. /// <summary>
  31. /// The number of iterations of the %FABRIK algorithm. Not used if there are 2 bones assigned to Spine in the References.
  32. /// </summary>
  33. [Range(1, 3)]
  34. public int iterations = 3;
  35. /// <summary>
  36. /// The weight of twisting the spine bones gradually to the orientation of the chest triangle. Relatively expensive, so set this to 0 if there is not much spine twisting going on.
  37. /// </summary>
  38. [Range(0f, 1f)]
  39. public float twistWeight = 1f;
  40. /// <summary>
  41. /// Determines whether this IKMappingSpine is valid
  42. /// </summary>
  43. public override bool IsValid(IKSolver solver, ref string message) {
  44. if (!base.IsValid(solver, ref message)) return false;
  45. foreach (Transform spineBone in spineBones) if (spineBone == null) {
  46. message = "Spine bones contains a null reference.";
  47. return false;
  48. }
  49. int nodes = 0;
  50. for (int i = 0; i < spineBones.Length; i++) {
  51. if (solver.GetPoint(spineBones[i]) != null) nodes ++;
  52. }
  53. if (nodes == 0) {
  54. message = "IKMappingSpine does not contain any nodes.";
  55. return false;
  56. }
  57. if (leftUpperArmBone == null) {
  58. message = "IKMappingSpine is missing the left upper arm bone.";
  59. return false;
  60. }
  61. if (rightUpperArmBone == null) {
  62. message = "IKMappingSpine is missing the right upper arm bone.";
  63. return false;
  64. }
  65. if (leftThighBone == null) {
  66. message = "IKMappingSpine is missing the left thigh bone.";
  67. return false;
  68. }
  69. if (rightThighBone == null) {
  70. message = "IKMappingSpine is missing the right thigh bone.";
  71. return false;
  72. }
  73. if (solver.GetPoint(leftUpperArmBone) == null) {
  74. message = "Full Body IK is missing the left upper arm node.";
  75. return false;
  76. }
  77. if (solver.GetPoint(rightUpperArmBone) == null) {
  78. message = "Full Body IK is missing the right upper arm node.";
  79. return false;
  80. }
  81. if (solver.GetPoint(leftThighBone) == null) {
  82. message = "Full Body IK is missing the left thigh node.";
  83. return false;
  84. }
  85. if (solver.GetPoint(rightThighBone) == null) {
  86. message = "Full Body IK is missing the right thigh node.";
  87. return false;
  88. }
  89. return true;
  90. }
  91. #endregion Main Interface
  92. private int rootNodeIndex;
  93. private BoneMap[] spine = new BoneMap[0];
  94. private BoneMap leftUpperArm = new BoneMap(), rightUpperArm = new BoneMap(), leftThigh = new BoneMap(), rightThigh = new BoneMap();
  95. private bool useFABRIK;
  96. public IKMappingSpine() {}
  97. public IKMappingSpine(Transform[] spineBones, Transform leftUpperArmBone, Transform rightUpperArmBone, Transform leftThighBone, Transform rightThighBone) {
  98. SetBones(spineBones, leftUpperArmBone, rightUpperArmBone, leftThighBone, rightThighBone);
  99. }
  100. public void SetBones(Transform[] spineBones, Transform leftUpperArmBone, Transform rightUpperArmBone, Transform leftThighBone, Transform rightThighBone) {
  101. this.spineBones = spineBones;
  102. this.leftUpperArmBone = leftUpperArmBone;
  103. this.rightUpperArmBone = rightUpperArmBone;
  104. this.leftThighBone = leftThighBone;
  105. this.rightThighBone = rightThighBone;
  106. }
  107. public void StoreDefaultLocalState() {
  108. for (int i = 0; i < spine.Length; i++) {
  109. spine[i].StoreDefaultLocalState();
  110. }
  111. }
  112. public void FixTransforms() {
  113. for (int i = 0; i < spine.Length; i++) {
  114. spine[i].FixTransform(i == 0 || i == spine.Length - 1);
  115. }
  116. }
  117. /*
  118. * Initiating and setting defaults
  119. * */
  120. public override void Initiate(IKSolverFullBody solver) {
  121. if (iterations <= 0) iterations = 3;
  122. // Creating the bone maps
  123. if (spine == null || spine.Length != spineBones.Length) spine = new BoneMap[spineBones.Length];
  124. rootNodeIndex = -1;
  125. for (int i = 0; i < spineBones.Length; i++) {
  126. if (spine[i] == null) spine[i] = new BoneMap();
  127. spine[i].Initiate(spineBones[i], solver);
  128. // Finding the root node
  129. if (spine[i].isNodeBone) rootNodeIndex = i;
  130. }
  131. if (leftUpperArm == null) leftUpperArm = new BoneMap();
  132. if (rightUpperArm == null) rightUpperArm = new BoneMap();
  133. if (leftThigh == null) leftThigh = new BoneMap();
  134. if (rightThigh == null) rightThigh = new BoneMap();
  135. leftUpperArm.Initiate(leftUpperArmBone, solver);
  136. rightUpperArm.Initiate(rightUpperArmBone, solver);
  137. leftThigh.Initiate(leftThighBone, solver);
  138. rightThigh.Initiate(rightThighBone, solver);
  139. for (int i = 0; i < spine.Length; i++) spine[i].SetIKPosition();
  140. // Defining the plane for the first bone
  141. spine[0].SetPlane(solver, spine[rootNodeIndex].transform, leftThigh.transform, rightThigh.transform);
  142. // Finding bone lengths and axes
  143. for (int i = 0; i < spine.Length - 1; i++) {
  144. spine[i].SetLength(spine[i + 1]);
  145. spine[i].SetLocalSwingAxis(spine[i + 1]);
  146. spine[i].SetLocalTwistAxis(leftUpperArm.transform.position - rightUpperArm.transform.position, spine[i + 1].transform.position - spine[i].transform.position);
  147. }
  148. // Defining the plane for the last bone
  149. spine[spine.Length - 1].SetPlane(solver, spine[rootNodeIndex].transform, leftUpperArm.transform, rightUpperArm.transform);
  150. spine[spine.Length - 1].SetLocalSwingAxis(leftUpperArm, rightUpperArm);
  151. useFABRIK = UseFABRIK();
  152. }
  153. // Should the spine mapping use the FABRIK algorithm
  154. private bool UseFABRIK() {
  155. if (spine.Length > 3) return true;
  156. if (rootNodeIndex != 1) return true;
  157. return false;
  158. }
  159. /*
  160. * Updating the bone maps to the current animated state of the character
  161. * */
  162. public void ReadPose() {
  163. spine[0].UpdatePlane(true, true);
  164. for (int i = 0; i < spine.Length - 1; i++) {
  165. spine[i].SetLength(spine[i + 1]);
  166. spine[i].SetLocalSwingAxis(spine[i + 1]);
  167. spine[i].SetLocalTwistAxis(leftUpperArm.transform.position - rightUpperArm.transform.position, spine[i + 1].transform.position - spine[i].transform.position);
  168. }
  169. spine[spine.Length - 1].UpdatePlane(true, true);
  170. spine[spine.Length - 1].SetLocalSwingAxis(leftUpperArm, rightUpperArm);
  171. }
  172. /*
  173. * Mapping the spine to the hip and chest planes
  174. * */
  175. public void WritePose(IKSolverFullBody solver) {
  176. Vector3 firstPosition = spine[0].GetPlanePosition(solver);
  177. Vector3 rootPosition = solver.GetNode(spine[rootNodeIndex].chainIndex, spine[rootNodeIndex].nodeIndex).solverPosition;
  178. Vector3 lastPosition = spine[spine.Length - 1].GetPlanePosition(solver);
  179. // If we have more than 3 bones, use the FABRIK algorithm
  180. if (useFABRIK) {
  181. Vector3 offset = solver.GetNode(spine[rootNodeIndex].chainIndex, spine[rootNodeIndex].nodeIndex).solverPosition - spine[rootNodeIndex].transform.position;
  182. for (int i = 0; i < spine.Length; i++) {
  183. spine[i].ikPosition = spine[i].transform.position + offset;
  184. }
  185. // Iterating the FABRIK algorithm
  186. for (int i = 0; i < iterations; i++) {
  187. ForwardReach(lastPosition);
  188. BackwardReach(firstPosition);
  189. spine[rootNodeIndex].ikPosition = rootPosition;
  190. }
  191. } else {
  192. // When we have just 3 bones, we know their positions already
  193. spine[0].ikPosition = firstPosition;
  194. spine[rootNodeIndex].ikPosition = rootPosition;
  195. }
  196. spine[spine.Length - 1].ikPosition = lastPosition;
  197. // Mapping the spine bones to the solver
  198. MapToSolverPositions(solver);
  199. }
  200. /*
  201. * Stage 1 of the FABRIK algorithm.
  202. * */
  203. public void ForwardReach(Vector3 position) {
  204. // Lerp last bone's ikPosition to position
  205. spine[spineBones.Length - 1].ikPosition = position;
  206. for (int i = spine.Length - 2; i > -1; i--) {
  207. // Finding joint positions
  208. spine[i].ikPosition = SolveFABRIKJoint(spine[i].ikPosition, spine[i + 1].ikPosition, spine[i].length);
  209. }
  210. }
  211. /*
  212. * Stage 2 of the FABRIK algorithm
  213. * */
  214. private void BackwardReach(Vector3 position) {
  215. spine[0].ikPosition = position;
  216. // Finding joint positions
  217. for (int i = 1; i < spine.Length; i++) {
  218. spine[i].ikPosition = SolveFABRIKJoint(spine[i].ikPosition, spine[i - 1].ikPosition, spine[i - 1].length);
  219. }
  220. }
  221. /*
  222. * Positioning and rotating the spine bones to match the solver positions
  223. * */
  224. private void MapToSolverPositions(IKSolverFullBody solver) {
  225. // Translating the first bone
  226. // Note: spine here also includes the pelvis
  227. spine[0].SetToIKPosition();
  228. spine[0].RotateToPlane(solver, 1f);
  229. // Translating all the bones between the first and the last
  230. for (int i = 1; i < spine.Length - 1; i++) {
  231. spine[i].Swing(spine[i + 1].ikPosition, 1f);
  232. if (twistWeight > 0) {
  233. float bWeight = (float)i / ((float)spine.Length - 2);
  234. Vector3 s1 = solver.GetNode(leftUpperArm.chainIndex, leftUpperArm.nodeIndex).solverPosition;
  235. Vector3 s2 = solver.GetNode(rightUpperArm.chainIndex, rightUpperArm.nodeIndex).solverPosition;
  236. spine[i].Twist(s1 - s2, spine[i + 1].ikPosition - spine[i].transform.position, bWeight * twistWeight);
  237. }
  238. }
  239. // Translating the last bone
  240. spine[spine.Length - 1].SetToIKPosition();
  241. spine[spine.Length - 1].RotateToPlane(solver, 1f);
  242. }
  243. }
  244. }