VelocityHandler.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. using SC.XR.Unity.Module_InputSystem;
  2. using System.Collections;
  3. using UnityEngine;
  4. using UnityEngine.EventSystems;
  5. namespace SC.XR.Unity.Module_Sphere
  6. {
  7. [RequireComponent(typeof(ManipulationHandler))]
  8. [RequireComponent(typeof(Rigidbody))]
  9. public class VelocityHandler : MonoBehaviour
  10. {
  11. /// <summary>
  12. /// Whether to enable rigidbody's velocity.
  13. /// </summary>
  14. [SerializeField]
  15. private bool useVelocity = true;
  16. /// <summary>
  17. /// Whether to enable rigidbody's angular velocity.
  18. /// </summary>
  19. [SerializeField]
  20. private bool useAngularVelocity = true;
  21. /// <summary>
  22. /// The type of rigidbody's origin.
  23. /// </summary>
  24. [SerializeField]
  25. protected OriginType originType = OriginType.Pointer;
  26. // The number of frames to estimating velocity.
  27. public int velocityAverageFrames = 5;
  28. // The number of frames to estimating angular velocity.
  29. public int angularVelocityAverageFrames = 10;
  30. public float velocityIntensity = 1.0f;
  31. public float angularVelocityIntensity = 1.0f;
  32. /// <summary>
  33. /// Enable velocity handling.
  34. /// </summary>
  35. protected void OnEnable()
  36. {
  37. InitCaches();
  38. ManipulationHandler.PointerDown.AddListener(OnPointerDown);
  39. ManipulationHandler.PointerUp.AddListener(OnPointerUp);
  40. }
  41. /// <summary>
  42. /// Disable velocity handling.
  43. /// </summary>
  44. protected void OnDisable()
  45. {
  46. ManipulationHandler.PointerDown.RemoveListener(OnPointerDown);
  47. ManipulationHandler.PointerUp.RemoveListener(OnPointerUp);
  48. }
  49. private ManipulationHandler _manipulationHandler;
  50. public ManipulationHandler ManipulationHandler
  51. {
  52. get
  53. {
  54. if (_manipulationHandler == null)
  55. _manipulationHandler = GetComponent<ManipulationHandler>();
  56. return _manipulationHandler;
  57. }
  58. }
  59. private Rigidbody _rigidbody;
  60. public Rigidbody RigidBody
  61. {
  62. get
  63. {
  64. if (_rigidbody == null)
  65. _rigidbody = GetComponent<Rigidbody>();
  66. return _rigidbody;
  67. }
  68. }
  69. public Transform Origin
  70. {
  71. get
  72. {
  73. switch (originType)
  74. {
  75. case OriginType.Pointer:
  76. return Pointer;
  77. case OriginType.Cursor:
  78. return Cursor;
  79. case OriginType.Transform:
  80. return transform;
  81. }
  82. return null;
  83. }
  84. }
  85. private DetectorBase _detectorBase;
  86. public DetectorBase DetectorBase { get => _detectorBase;set => _detectorBase = value; }
  87. public Transform Pointer {get => DetectorBase.currentPointer.transform;}
  88. public Transform Cursor { get => DetectorBase.currentPointer.cursorBase.transform; }
  89. public void OnPointerDown(PointerEventData eventData)
  90. {
  91. DetectorBase = (eventData as SCPointEventData).inputDevicePartBase.detectorBase;
  92. HandleVelocityStarted();
  93. }
  94. public void OnPointerUp(PointerEventData eventData)
  95. {
  96. HandleVelocityEnded();
  97. }
  98. /// <summary>
  99. /// Start handle rigidbody's velocity and angular velocity.
  100. /// </summary>
  101. private void HandleVelocityStarted()
  102. {
  103. estimateVelocity = StartCoroutine(EstimateVelocity());
  104. }
  105. /// <summary>
  106. /// End handle rigidbody's velocity and angular velocity.
  107. /// </summary>
  108. private void HandleVelocityEnded()
  109. {
  110. if (useVelocity) RigidBody.velocity = GetVelocityEstimate();
  111. if (useAngularVelocity) RigidBody.angularVelocity = GetAngularVelocityEstimate();
  112. StopCoroutine(estimateVelocity);
  113. estimateVelocity = null;
  114. DetectorBase = null;
  115. }
  116. private Vector3[] velocityCaches;
  117. private Vector3[] angularVelocityCaches;
  118. private int currentCacheCount;
  119. // The coroutine to estimate velocity and angular velocity.
  120. private Coroutine estimateVelocity;
  121. //Initialize the velocity caches and angular velocity caches.
  122. public void InitCaches()
  123. {
  124. velocityCaches = new Vector3[velocityAverageFrames];
  125. angularVelocityCaches = new Vector3[angularVelocityAverageFrames];
  126. }
  127. /// <summary>
  128. /// Estimate the average velocity based on the velocities in the cache.
  129. /// </summary>
  130. public Vector3 GetVelocityEstimate()
  131. {
  132. Vector3 averageVelocity = Vector3.zero;
  133. int velocityCacheCount = Mathf.Min(currentCacheCount, velocityCaches.Length);
  134. if (velocityCacheCount != 0)
  135. {
  136. for (int i = 0; i < velocityCacheCount; i++)
  137. {
  138. averageVelocity += velocityCaches[i];
  139. }
  140. averageVelocity *= (1.0f / velocityCacheCount);
  141. }
  142. return averageVelocity * velocityIntensity;
  143. }
  144. /// <summary>
  145. /// Estimate the average angular velocity based on the angular velocities in the cache.
  146. /// </summary>
  147. public Vector3 GetAngularVelocityEstimate()
  148. {
  149. Vector3 angularVelocity = Vector3.zero;
  150. int angularVelocitySampleCount = Mathf.Min(currentCacheCount, angularVelocityCaches.Length);
  151. if (angularVelocitySampleCount != 0)
  152. {
  153. for (int i = 0; i < angularVelocitySampleCount; i++)
  154. {
  155. angularVelocity += angularVelocityCaches[i];
  156. }
  157. angularVelocity *= (1.0f / angularVelocitySampleCount);
  158. }
  159. return angularVelocity * angularVelocityIntensity;
  160. }
  161. /// <summary>
  162. /// Estimate the velocity and angular velocity of the object in this frame,
  163. /// based on the position and rotation of the previous frame and this frame, and store it in cache.
  164. /// </summary>
  165. public IEnumerator EstimateVelocity()
  166. {
  167. currentCacheCount = 0;
  168. Vector3 previousPosition = Origin.position;
  169. Quaternion previousRotation = Origin.rotation;
  170. while (true)
  171. {
  172. yield return new WaitForEndOfFrame();
  173. int v = currentCacheCount % velocityCaches.Length;
  174. int w = currentCacheCount % angularVelocityCaches.Length;
  175. currentCacheCount++;
  176. float velocityFactor = 1.0f / Time.deltaTime;
  177. velocityCaches[v] = CalculateVelocity(Origin.position, previousPosition, velocityFactor);
  178. angularVelocityCaches[w] = CalculateAngularVelocity(Origin.rotation, previousRotation, velocityFactor);
  179. previousPosition = Origin.position;
  180. previousRotation = Origin.rotation;
  181. }
  182. }
  183. /// <summary>
  184. /// Calculate the rigidbody's velocity.
  185. /// </summary>
  186. /// <param name="presentRotation">Rigidbody's position in present frame.</param>
  187. /// <param name="previousPosition">Rigidbody's position in previous frame.</param>
  188. /// <param name="velocityFactor"></param>
  189. public Vector3 CalculateVelocity(Vector3 presentRotation, Vector3 previousPosition, float velocityFactor)
  190. {
  191. Vector3 velocity = (presentRotation - previousPosition) * velocityFactor;
  192. return velocity;
  193. }
  194. /// <summary>
  195. /// Using Quaternion to Calculate the rigidbody's angular velocity.
  196. /// </summary>
  197. /// <param name="presentRotation">Rigidbody's rotation(Quaternion) in present frame.</param>
  198. /// <param name="previousRotation">Rigidbody's rotation(Quaternion) in previous frame.</param>
  199. /// <param name="velocityFactor"></param>
  200. /// <returns></returns>
  201. public Vector3 CalculateAngularVelocity(Quaternion presentRotation, Quaternion previousRotation, float velocityFactor)
  202. {
  203. Quaternion deltaRotation = presentRotation * Quaternion.Inverse(previousRotation);
  204. float theta = 2.0f * Mathf.Acos(Mathf.Clamp(deltaRotation.w, -1.0f, 1.0f));
  205. if (theta > Mathf.PI)
  206. {
  207. theta -= 2.0f * Mathf.PI;
  208. }
  209. Vector3 angularAxis = new Vector3(deltaRotation.x, deltaRotation.y, deltaRotation.z).normalized;
  210. Vector3 angularVelocity = Vector3.zero;
  211. if (angularAxis.sqrMagnitude > 0.0f)
  212. {
  213. angularVelocity = theta * angularAxis * velocityFactor;
  214. }
  215. return angularVelocity;
  216. }
  217. }
  218. }