Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
4.7 KiB

  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: Simple two bone ik solver.
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. public class SteamVR_IK : MonoBehaviour
  8. {
  9. public Transform target;
  10. public Transform start, joint, end;
  11. public Transform poleVector, upVector;
  12. public float blendPct = 1.0f;
  13. [HideInInspector]
  14. public Transform startXform, jointXform, endXform;
  15. void LateUpdate()
  16. {
  17. const float epsilon = 0.001f;
  18. if (blendPct < epsilon)
  19. return;
  20. var preUp = upVector ? upVector.up : Vector3.Cross(end.position - start.position, joint.position - start.position).normalized;
  21. var targetPosition = target.position;
  22. var targetRotation = target.rotation;
  23. Vector3 forward, up, result = joint.position;
  24. Solve(start.position, targetPosition, poleVector.position,
  25. (joint.position - start.position).magnitude,
  26. (end.position - joint.position).magnitude,
  27. ref result, out forward, out up);
  28. if (up == Vector3.zero)
  29. return;
  30. var startPosition = start.position;
  31. var jointPosition = joint.position;
  32. var endPosition = end.position;
  33. var startRotationLocal = start.localRotation;
  34. var jointRotationLocal = joint.localRotation;
  35. var endRotationLocal = end.localRotation;
  36. var startParent = start.parent;
  37. var jointParent = joint.parent;
  38. var endParent = end.parent;
  39. var startScale = start.localScale;
  40. var jointScale = joint.localScale;
  41. var endScale = end.localScale;
  42. if (startXform == null)
  43. {
  44. startXform = new GameObject("startXform").transform;
  45. startXform.parent = transform;
  46. }
  47. startXform.position = startPosition;
  48. startXform.LookAt(joint, preUp);
  49. start.parent = startXform;
  50. if (jointXform == null)
  51. {
  52. jointXform = new GameObject("jointXform").transform;
  53. jointXform.parent = startXform;
  54. }
  55. jointXform.position = jointPosition;
  56. jointXform.LookAt(end, preUp);
  57. joint.parent = jointXform;
  58. if (endXform == null)
  59. {
  60. endXform = new GameObject("endXform").transform;
  61. endXform.parent = jointXform;
  62. }
  63. endXform.position = endPosition;
  64. end.parent = endXform;
  65. startXform.LookAt(result, up);
  66. jointXform.LookAt(targetPosition, up);
  67. endXform.rotation = targetRotation;
  68. start.parent = startParent;
  69. joint.parent = jointParent;
  70. end.parent = endParent;
  71. end.rotation = targetRotation; // optionally blend?
  72. // handle blending in/out
  73. if (blendPct < 1.0f)
  74. {
  75. start.localRotation = Quaternion.Slerp(startRotationLocal, start.localRotation, blendPct);
  76. joint.localRotation = Quaternion.Slerp(jointRotationLocal, joint.localRotation, blendPct);
  77. end.localRotation = Quaternion.Slerp(endRotationLocal, end.localRotation, blendPct);
  78. }
  79. // restore scale so it doesn't blow out
  80. start.localScale = startScale;
  81. joint.localScale = jointScale;
  82. end.localScale = endScale;
  83. }
  84. public static bool Solve(
  85. Vector3 start, // shoulder / hip
  86. Vector3 end, // desired hand / foot position
  87. Vector3 poleVector, // point to aim elbow / knee toward
  88. float jointDist, // distance from start to elbow / knee
  89. float targetDist, // distance from joint to hand / ankle
  90. ref Vector3 result, // original and output elbow / knee position
  91. out Vector3 forward, out Vector3 up) // plane formed by root, joint and target
  92. {
  93. var totalDist = jointDist + targetDist;
  94. var start2end = end - start;
  95. var poleVectorDir = (poleVector - start).normalized;
  96. var baseDist = start2end.magnitude;
  97. result = start;
  98. const float epsilon = 0.001f;
  99. if (baseDist < epsilon)
  100. {
  101. // move jointDist toward jointTarget
  102. result += poleVectorDir * jointDist;
  103. forward = Vector3.Cross(poleVectorDir, Vector3.up);
  104. up = Vector3.Cross(forward, poleVectorDir).normalized;
  105. }
  106. else
  107. {
  108. forward = start2end * (1.0f / baseDist);
  109. up = Vector3.Cross(forward, poleVectorDir).normalized;
  110. if (baseDist + epsilon < totalDist)
  111. {
  112. // calculate the area of the triangle to determine its height
  113. var p = (totalDist + baseDist) * 0.5f; // half perimeter
  114. if (p > jointDist + epsilon && p > targetDist + epsilon)
  115. {
  116. var A = Mathf.Sqrt(p * (p - jointDist) * (p - targetDist) * (p - baseDist));
  117. var height = 2.0f * A / baseDist; // distance of joint from line between root and target
  118. var dist = Mathf.Sqrt((jointDist * jointDist) - (height * height));
  119. var right = Vector3.Cross(up, forward); // no need to normalized - already orthonormal
  120. result += (forward * dist) + (right * height);
  121. return true; // in range
  122. }
  123. else
  124. {
  125. // move jointDist toward jointTarget
  126. result += poleVectorDir * jointDist;
  127. }
  128. }
  129. else
  130. {
  131. // move elboDist toward target
  132. result += forward * jointDist;
  133. }
  134. }
  135. return false; // edge cases
  136. }
  137. }