IKSolverLookAt.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion.FinalIK {
  4. /// <summary>
  5. /// Rotates a hierarchy of bones to face a target.
  6. /// </summary>
  7. [System.Serializable]
  8. public class IKSolverLookAt : IKSolver {
  9. #region Main Interface
  10. /// <summary>
  11. /// The target Transform.
  12. /// </summary>
  13. public Transform target;
  14. /// <summary>
  15. /// The spine hierarchy.
  16. /// </summary>
  17. public LookAtBone[] spine = new LookAtBone[0];
  18. /// <summary>
  19. /// The head bone.
  20. /// </summary>
  21. public LookAtBone head = new LookAtBone();
  22. /// <summary>
  23. /// The eye bones.
  24. /// </summary>
  25. public LookAtBone[] eyes = new LookAtBone[0];
  26. /// <summary>
  27. /// The body weight.
  28. /// </summary>
  29. [Range(0f, 1f)]
  30. public float bodyWeight = 0.5f;
  31. /// <summary>
  32. /// The head weight.
  33. /// </summary>
  34. [Range(0f, 1f)]
  35. public float headWeight = 0.5f;
  36. /// <summary>
  37. /// The eyes weight.
  38. /// </summary>
  39. [Range(0f, 1f)]
  40. public float eyesWeight = 1f;
  41. /// <summary>
  42. /// Clamp weight for the body.
  43. /// </summary>
  44. [Range(0f, 1f)]
  45. public float clampWeight = 0.5f;
  46. /// <summary>
  47. /// Clamp weight for the head.
  48. /// </summary>
  49. [Range(0f, 1f)]
  50. public float clampWeightHead = 0.5f;
  51. /// <summary>
  52. /// Clamp weight for the eyes.
  53. /// </summary>
  54. [Range(0f, 1f)]
  55. public float clampWeightEyes = 0.5f;
  56. /// <summary>
  57. /// Number of sine smoothing iterations applied on clamping to make the clamping point smoother.
  58. /// </summary>
  59. [Range(0, 2)]
  60. public int clampSmoothing = 2;
  61. /// <summary>
  62. /// Weight distribution between the spine bones.
  63. /// </summary>
  64. public AnimationCurve spineWeightCurve = new AnimationCurve(new Keyframe[2] { new Keyframe(0f, 0.3f), new Keyframe(1f, 1f) });
  65. /// <summary>
  66. /// Offset for the spine target in world space..
  67. /// </summary>
  68. public Vector3 spineTargetOffset;
  69. /// <summary>
  70. /// Sets the look at weight. NOTE: You are welcome edit the weights directly, this method is here only to match the Unity's built in %IK API.
  71. /// </summary>
  72. public void SetLookAtWeight(float weight) {
  73. this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
  74. }
  75. /// <summary>
  76. /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
  77. /// </summary>
  78. public void SetLookAtWeight(float weight, float bodyWeight) {
  79. this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
  80. this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
  81. }
  82. /// <summary>
  83. /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
  84. /// </summary>
  85. public void SetLookAtWeight(float weight, float bodyWeight, float headWeight) {
  86. this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
  87. this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
  88. this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
  89. }
  90. /// <summary>
  91. /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
  92. /// </summary>
  93. public void SetLookAtWeight(float weight, float bodyWeight, float headWeight, float eyesWeight) {
  94. this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
  95. this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
  96. this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
  97. this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
  98. }
  99. /// <summary>
  100. /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
  101. /// </summary>
  102. public void SetLookAtWeight(float weight, float bodyWeight, float headWeight, float eyesWeight, float clampWeight) {
  103. this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
  104. this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
  105. this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
  106. this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
  107. this.clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
  108. this.clampWeightHead = this.clampWeight;
  109. this.clampWeightEyes = this.clampWeight;
  110. }
  111. /// <summary>
  112. /// Sets the look at weight. NOTE: You are welcome to edit the weights directly, this method is here only to match the Unity's built in %IK API.
  113. /// </summary>
  114. public void SetLookAtWeight(float weight, float bodyWeight = 0f, float headWeight = 1f, float eyesWeight = 0.5f, float clampWeight = 0.5f, float clampWeightHead = 0.5f, float clampWeightEyes = 0.3f) {
  115. this.IKPositionWeight = Mathf.Clamp(weight, 0f, 1f);
  116. this.bodyWeight = Mathf.Clamp(bodyWeight, 0f, 1f);
  117. this.headWeight = Mathf.Clamp(headWeight, 0f, 1f);
  118. this.eyesWeight = Mathf.Clamp(eyesWeight, 0f, 1f);
  119. this.clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
  120. this.clampWeightHead = Mathf.Clamp(clampWeightHead, 0f, 1f);
  121. this.clampWeightEyes = Mathf.Clamp(clampWeightEyes, 0f, 1f);
  122. }
  123. public override void StoreDefaultLocalState() {
  124. for (int i = 0; i < spine.Length; i++) spine[i].StoreDefaultLocalState();
  125. for (int i = 0; i < eyes.Length; i++) eyes[i].StoreDefaultLocalState();
  126. if (head != null && head.transform != null) head.StoreDefaultLocalState();
  127. }
  128. // Flag for Fix Transforms.
  129. public void SetDirty()
  130. {
  131. isDirty = true;
  132. }
  133. public override void FixTransforms() {
  134. if (!initiated) return;
  135. if (IKPositionWeight <= 0f && !isDirty) return;
  136. for (int i = 0; i < spine.Length; i++) spine[i].FixTransform();
  137. for (int i = 0; i < eyes.Length; i++) eyes[i].FixTransform();
  138. if (head != null && head.transform != null) head.FixTransform();
  139. isDirty = false;
  140. }
  141. public override bool IsValid (ref string message) {
  142. if (!spineIsValid) {
  143. message = "IKSolverLookAt spine setup is invalid. Can't initiate solver.";
  144. return false;
  145. }
  146. if (!headIsValid) {
  147. message = "IKSolverLookAt head transform is null. Can't initiate solver.";
  148. return false;
  149. }
  150. if (!eyesIsValid) {
  151. message = "IKSolverLookAt eyes setup is invalid. Can't initiate solver.";
  152. return false;
  153. }
  154. if (spineIsEmpty && headIsEmpty && eyesIsEmpty) {
  155. message = "IKSolverLookAt eyes setup is invalid. Can't initiate solver.";
  156. return false;
  157. }
  158. Transform spineDuplicate = ContainsDuplicateBone(spine);
  159. if (spineDuplicate != null) {
  160. message = spineDuplicate.name + " is represented multiple times in a single IK chain. Can't initiate solver.";
  161. return false;
  162. }
  163. Transform eyeDuplicate = ContainsDuplicateBone(eyes);
  164. if (eyeDuplicate != null) {
  165. message = eyeDuplicate.name + " is represented multiple times in a single IK chain. Can't initiate solver.";
  166. return false;
  167. }
  168. return true;
  169. }
  170. public override IKSolver.Point[] GetPoints() {
  171. IKSolver.Point[] allPoints = new IKSolver.Point[spine.Length + eyes.Length + (head.transform != null? 1: 0)];
  172. for (int i = 0; i < spine.Length; i++) allPoints[i] = spine[i] as IKSolver.Point;
  173. int eye = 0;
  174. for (int i = spine.Length; i < spine.Length + eyes.Length; i++)
  175. {
  176. allPoints[i] = eyes[eye] as IKSolver.Point;
  177. eye++;
  178. }
  179. if (head.transform != null) allPoints[allPoints.Length - 1] = head as IKSolver.Point;
  180. return allPoints;
  181. }
  182. public override IKSolver.Point GetPoint(Transform transform) {
  183. foreach (IKSolverLookAt.LookAtBone b in spine) if (b.transform == transform) return b as IKSolver.Point;
  184. foreach (IKSolverLookAt.LookAtBone b in eyes) if (b.transform == transform) return b as IKSolver.Point;
  185. if (head.transform == transform) return head as IKSolver.Point;
  186. return null;
  187. }
  188. /// <summary>
  189. /// Look At bone class.
  190. /// </summary>
  191. [System.Serializable]
  192. public class LookAtBone: IKSolver.Bone {
  193. #region Public methods
  194. public Vector3 baseForwardOffsetEuler;
  195. public LookAtBone() {}
  196. /*
  197. * Custom constructor
  198. * */
  199. public LookAtBone(Transform transform) {
  200. this.transform = transform;
  201. }
  202. /*
  203. * Initiates the bone, precalculates values.
  204. * */
  205. public void Initiate(Transform root) {
  206. if (transform == null) return;
  207. axis = Quaternion.Inverse(transform.rotation) * root.forward;
  208. }
  209. /*
  210. * Rotates the bone to look at a world direction.
  211. * */
  212. public void LookAt(Vector3 direction, float weight) {
  213. Quaternion fromTo = Quaternion.FromToRotation(forward, direction);
  214. Quaternion r = transform.rotation;
  215. transform.rotation = Quaternion.Lerp(r, fromTo * r, weight);
  216. }
  217. /*
  218. * Gets the local axis to goal in world space.
  219. * */
  220. public Vector3 forward {
  221. get {
  222. return transform.rotation * axis;
  223. }
  224. }
  225. #endregion Public methods
  226. }
  227. /// <summary>
  228. /// Reinitiate the solver with new bone Transforms.
  229. /// </summary>
  230. /// <returns>
  231. /// Returns true if the new chain is valid.
  232. /// </returns>
  233. public bool SetChain(Transform[] spine, Transform head, Transform[] eyes, Transform root) {
  234. // Spine
  235. SetBones(spine, ref this.spine);
  236. // Head
  237. this.head = new LookAtBone(head);
  238. // Eyes
  239. SetBones(eyes, ref this.eyes);
  240. Initiate(root);
  241. return initiated;
  242. }
  243. #endregion Main Interface
  244. protected Vector3[] spineForwards = new Vector3[0];
  245. protected Vector3[] headForwards = new Vector3[1];
  246. protected Vector3[] eyeForward = new Vector3[1];
  247. private bool isDirty;
  248. protected override void OnInitiate() {
  249. // Set IKPosition to default value
  250. if (firstInitiation || !Application.isPlaying) {
  251. if (spine.Length > 0) IKPosition = spine[spine.Length - 1].transform.position + root.forward * 3f;
  252. else if (head.transform != null) IKPosition = head.transform.position + root.forward * 3f;
  253. else if (eyes.Length > 0 && eyes[0].transform != null) IKPosition = eyes[0].transform.position + root.forward * 3f;
  254. }
  255. // Initiating the bones
  256. foreach (LookAtBone s in spine) s.Initiate(root);
  257. if (head != null) head.Initiate(root);
  258. foreach (LookAtBone eye in eyes) eye.Initiate(root);
  259. if (spineForwards == null || spineForwards.Length != spine.Length) spineForwards = new Vector3[spine.Length];
  260. if (headForwards == null) headForwards = new Vector3[1];
  261. if (eyeForward == null) eyeForward = new Vector3[1];
  262. }
  263. protected override void OnUpdate() {
  264. if (IKPositionWeight <= 0) return;
  265. IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
  266. if (target != null) IKPosition = target.position;
  267. // Solving the hierarchies
  268. SolveSpine();
  269. SolveHead();
  270. SolveEyes();
  271. }
  272. protected bool spineIsValid {
  273. get {
  274. if (spine == null) return false;
  275. if (spine.Length == 0) return true;
  276. for (int i = 0; i < spine.Length; i++) if (spine[i] == null || spine[i].transform == null) return false;
  277. return true;
  278. }
  279. }
  280. protected bool spineIsEmpty { get { return spine.Length == 0; }}
  281. // Solving the spine hierarchy
  282. protected void SolveSpine() {
  283. if (bodyWeight <= 0) return;
  284. if (spineIsEmpty) return;
  285. // Get the look at vectors for each bone
  286. //Vector3 targetForward = Vector3.Lerp(spine[0].forward, (IKPosition - spine[spine.Length - 1].transform.position).normalized, bodyWeight * IKPositionWeight).normalized;
  287. Vector3 targetForward = (IKPosition + spineTargetOffset - spine[spine.Length - 1].transform.position).normalized;
  288. GetForwards(ref spineForwards, spine[0].forward, targetForward, spine.Length, clampWeight);
  289. // Rotate each bone to face their look at vectors
  290. for (int i = 0; i < spine.Length; i++) {
  291. spine[i].LookAt(spineForwards[i], bodyWeight * IKPositionWeight);
  292. }
  293. }
  294. protected bool headIsValid {
  295. get {
  296. if (head == null) return false;
  297. return true;
  298. }
  299. }
  300. protected bool headIsEmpty { get { return head.transform == null; }}
  301. // Solving the head rotation
  302. protected void SolveHead() {
  303. if (headWeight <= 0) return;
  304. if (headIsEmpty) return;
  305. // Get the look at vector for the head
  306. Vector3 baseForward = spine.Length > 0 && spine[spine.Length - 1].transform != null? spine[spine.Length - 1].forward: head.forward;
  307. Vector3 targetForward = Vector3.Lerp(baseForward, (IKPosition - head.transform.position).normalized, headWeight * IKPositionWeight).normalized;
  308. GetForwards(ref headForwards, baseForward, targetForward, 1, clampWeightHead);
  309. // Rotate the head to face its look at vector
  310. head.LookAt(headForwards[0], headWeight * IKPositionWeight);
  311. }
  312. protected bool eyesIsValid {
  313. get {
  314. if (eyes == null) return false;
  315. if (eyes.Length == 0) return true;
  316. for (int i = 0; i < eyes.Length; i++) if (eyes[i] == null || eyes[i].transform == null) return false;
  317. return true;
  318. }
  319. }
  320. protected bool eyesIsEmpty { get { return eyes.Length == 0; }}
  321. // Solving the eye rotations
  322. protected void SolveEyes() {
  323. if (eyesWeight <= 0) return;
  324. if (eyesIsEmpty) return;
  325. for (int i = 0; i < eyes.Length; i++) {
  326. // Get the look at vector for the eye
  327. Quaternion baseRotation = head.transform != null ? head.transform.rotation : spine.Length > 0? spine[spine.Length - 1].transform.rotation: root.rotation;
  328. Vector3 baseAxis = head.transform != null ? head.axis : spine.Length > 0 ? spine[spine.Length - 1].axis : root.forward;
  329. if (eyes[i].baseForwardOffsetEuler != Vector3.zero) baseRotation *= Quaternion.Euler(eyes[i].baseForwardOffsetEuler);
  330. Vector3 baseForward = baseRotation * baseAxis;
  331. GetForwards(ref eyeForward, baseForward, (IKPosition - eyes[i].transform.position).normalized, 1, clampWeightEyes);
  332. // Rotate the eye to face its look at vector
  333. eyes[i].LookAt(eyeForward[0], eyesWeight * IKPositionWeight);
  334. }
  335. }
  336. /*
  337. * Returns forwards for a number of bones rotating from baseForward to targetForward.
  338. * NB! Make sure baseForward and targetForward are normalized.
  339. * */
  340. protected Vector3[] GetForwards(ref Vector3[] forwards, Vector3 baseForward, Vector3 targetForward, int bones, float clamp) {
  341. // If clamp >= 1 make all the forwards match the base
  342. if (clamp >= 1 || IKPositionWeight <= 0) {
  343. for (int i = 0; i < forwards.Length; i++) forwards[i] = baseForward;
  344. return forwards;
  345. }
  346. // Get normalized dot product.
  347. float angle = Vector3.Angle(baseForward, targetForward);
  348. float dot = 1f - (angle / 180f);
  349. // Clamping the targetForward so it doesn't exceed clamp
  350. float targetClampMlp = clamp > 0? Mathf.Clamp(1f - ((clamp - dot) / (1f - dot)), 0f, 1f): 1f;
  351. // Calculating the clamp multiplier
  352. float clampMlp = clamp > 0? Mathf.Clamp(dot / clamp, 0f, 1f): 1f;
  353. for (int i = 0; i < clampSmoothing; i++) {
  354. float sinF = clampMlp * Mathf.PI * 0.5f;
  355. clampMlp = Mathf.Sin(sinF);
  356. }
  357. // Rotation amount for 1 bone
  358. if (forwards.Length == 1) {
  359. forwards[0] = Vector3.Slerp(baseForward, targetForward, clampMlp * targetClampMlp);
  360. } else {
  361. float step = 1f / (float)(forwards.Length - 1);
  362. // Calculate the forward for each bone
  363. for (int i = 0; i < forwards.Length; i++) {
  364. forwards[i] = Vector3.Slerp(baseForward, targetForward, spineWeightCurve.Evaluate(step * i) * clampMlp * targetClampMlp);
  365. }
  366. }
  367. return forwards;
  368. }
  369. /*
  370. * Build LookAtBone[] array of a Transform array
  371. * */
  372. protected void SetBones(Transform[] array, ref LookAtBone[] bones) {
  373. if (array == null) {
  374. bones = new LookAtBone[0];
  375. return;
  376. }
  377. if (bones.Length != array.Length) bones = new LookAtBone[array.Length];
  378. for (int i = 0; i < array.Length; i++) {
  379. if (bones[i] == null) bones[i] = new LookAtBone(array[i]);
  380. else bones[i].transform = array[i];
  381. }
  382. }
  383. }
  384. }