GrabableAlignToXRAnchor.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.XR.Interaction.Toolkit;
  5. using Ximmerse.XR.InputSystems;
  6. namespace Ximmerse.XR.Interactions
  7. {
  8. /// <summary>
  9. /// Grabable algin to front body anchor when updating grabbing behaviour.
  10. /// 在 Grab 事件更新的时候, 更新位置到 Front Body Anchor的位置
  11. /// </summary>
  12. public class GrabableAlignToXRAnchor : XRBaseInteractable
  13. {
  14. #if UNITY_ANDROID || UNITY_EDITOR
  15. public enum AnchorType
  16. {
  17. BodyAnchor,
  18. PalmPosition,
  19. }
  20. public AnchorType anchorType = AnchorType.BodyAnchor;
  21. Camera m_MainCamera;
  22. public Transform mainCameraT
  23. {
  24. get
  25. {
  26. if (!m_MainCamera)
  27. {
  28. m_MainCamera = Camera.main;
  29. }
  30. return m_MainCamera.transform;
  31. }
  32. }
  33. [SerializeField]
  34. [Tooltip("Distance along Z-axis from head to dock position.")]
  35. float m_ZDepth = 4;
  36. /// <summary>
  37. /// Z-depth : canvas z-distance to head.
  38. /// </summary>
  39. /// <value>The z depth.</value>
  40. public float Z_Depth
  41. {
  42. get
  43. {
  44. return m_ZDepth;
  45. }
  46. set
  47. {
  48. m_ZDepth = value;
  49. }
  50. }
  51. [SerializeField, Tooltip("Force docking every frame")]
  52. bool m_ForceEveryFrame;
  53. /// <summary>
  54. /// Gets or sets a value indicating whether this <see cref="T:Ximmerse.RhinoX.FrontDocker"/> force every frame.
  55. /// </summary>
  56. /// <value><c>true</c> if force every frame; otherwise, <c>false</c>.</value>
  57. public bool ForceEveryFrame
  58. {
  59. get
  60. {
  61. return m_ForceEveryFrame;
  62. }
  63. set
  64. {
  65. m_ForceEveryFrame = value;
  66. }
  67. }
  68. [SerializeField, Tooltip("Angular diff error to re-dock canvas, smaller values cause frequent re-docking.")]
  69. float m_AngleDiffError = 35;
  70. /// <summary>
  71. /// Angular diff error to re-dock canvas, smaller values cause frequent re-docking.
  72. /// </summary>
  73. /// <value>The diff error.</value>
  74. public float AngleDiffError
  75. {
  76. get
  77. {
  78. return m_AngleDiffError;
  79. }
  80. set
  81. {
  82. m_AngleDiffError = value;
  83. }
  84. }
  85. [SerializeField, Tooltip("Delay time to start docking.")]
  86. float m_DockDelay = 0.5f;
  87. /// <summary>
  88. /// Gets or sets the dock delay.
  89. /// </summary>
  90. /// <value>The dock delay.</value>
  91. public float DockDelay
  92. {
  93. get
  94. {
  95. return m_DockDelay;
  96. }
  97. set
  98. {
  99. m_DockDelay = value;
  100. }
  101. }
  102. [SerializeField, Min(0.1f)]
  103. [Tooltip("The smaller damp time is , the faster the transform dock to eye's front.")]
  104. float m_DampTime = 0.3f;
  105. /// <summary>
  106. /// Gets or sets the damp time. The smaller damp time is , the faster the transform dock to eye's front.
  107. /// </summary>
  108. /// <value>The damp time.</value>
  109. public float DampTime
  110. {
  111. get
  112. {
  113. return m_DampTime;
  114. }
  115. set
  116. {
  117. m_DampTime = value;
  118. }
  119. }
  120. [SerializeField]
  121. //[Tooltip("The smaller damp time is , the faster the transform dock to eye's front.")]
  122. bool m_FollowWithPitch;
  123. /// <summary>
  124. /// Gets or sets the damp time. The smaller damp time is , the faster the transform dock to eye's front.
  125. /// </summary>
  126. /// <value>The damp time.</value>
  127. public bool FollowWithPitch
  128. {
  129. get
  130. {
  131. return m_FollowWithPitch;
  132. }
  133. set
  134. {
  135. m_FollowWithPitch = value;
  136. }
  137. }
  138. /// <summary>
  139. /// offset of docker position.
  140. /// modify by leaf.2019.08.30
  141. /// </summary>
  142. public Vector3 PositionOffset;
  143. bool m_Docking = false;
  144. float dampSpeed = 0;
  145. float dockEulerY;
  146. float dockEulerX;
  147. float? m_DockingStartTime;
  148. Transform m_HeadTransform;
  149. Transform headTransform
  150. {
  151. get
  152. {
  153. if (m_HeadTransform == null)
  154. {
  155. m_HeadTransform = Camera.main.transform;
  156. }
  157. return m_HeadTransform;
  158. }
  159. }
  160. public bool UpdatePosition = true;
  161. public bool UpdateRotation = true;
  162. // Start is called before the first frame update
  163. IEnumerator Start()
  164. {
  165. while (!mainCameraT)
  166. {
  167. yield return null;
  168. }
  169. //calculate dock position and rotation:
  170. //Quaternion fwdQ = Quaternion.Euler(0, headTransform.eulerAngles.y, 0);
  171. //transform.position = headTransform.position + fwdQ * Vector3.forward * m_ZDepth;
  172. //transform.rotation = fwdQ;
  173. m_Docking = false;
  174. dockEulerY = headTransform.eulerAngles.y;
  175. dockEulerX = headTransform.eulerAngles.x;
  176. }
  177. public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase)
  178. {
  179. if (!isSelected)
  180. {
  181. return;
  182. }
  183. switch (updatePhase)
  184. {
  185. case XRInteractionUpdateOrder.UpdatePhase.Fixed:
  186. if (anchorType == AnchorType.BodyAnchor)
  187. {
  188. UpdateTargetToBodyAnchor(updatePhase);
  189. }
  190. else if (anchorType == AnchorType.PalmPosition)
  191. {
  192. UpdateToPalmPosition();
  193. }
  194. break;
  195. }
  196. }
  197. private void UpdateToPalmPosition()
  198. {
  199. if (HandTracking.HandTrackingInfo.IsValid)
  200. {
  201. transform.position = HandTracking.HandTrackingInfo.PalmPosition;
  202. if (this.UpdateRotation)
  203. {
  204. transform.rotation = HandTracking.HandTrackingInfo.PalmRotation;
  205. }
  206. }
  207. }
  208. void UpdateTargetToBodyAnchor(XRInteractionUpdateOrder.UpdatePhase updatePhase)
  209. {
  210. //Debug.LogFormat("Update grabable target event : {0}", updatePhase);
  211. var headT = headTransform;
  212. float headEulerY = headT.eulerAngles.y;
  213. float headEulerX = headT.eulerAngles.x;
  214. float eulerYDiff = Quaternion.Angle(Quaternion.Euler(0, headEulerY, 0), Quaternion.Euler(0, dockEulerY, 0));
  215. float eulerXDiff = Quaternion.Angle(Quaternion.Euler(headEulerX, 0, 0), Quaternion.Euler(dockEulerX, 0, 0));
  216. if (m_ForceEveryFrame || Mathf.Abs(eulerYDiff) >= m_AngleDiffError || (FollowWithPitch && Mathf.Abs(eulerXDiff) >= m_AngleDiffError))
  217. {
  218. m_Docking = true;
  219. if (m_DockingStartTime.HasValue == false)
  220. {
  221. m_DockingStartTime = Time.time + m_DockDelay;
  222. }
  223. }
  224. if (m_Docking)
  225. {
  226. if (m_DockingStartTime.HasValue && Time.time < m_DockingStartTime.Value)
  227. {
  228. //wait for delay time
  229. }
  230. else
  231. {
  232. if (m_DampTime > 0)
  233. {
  234. dockEulerY = Mathf.SmoothDampAngle(dockEulerY, headEulerY, ref dampSpeed, m_DampTime);
  235. float eulerYDiff2 = Quaternion.Angle(Quaternion.Euler(0, headEulerY, 0), Quaternion.Euler(0, dockEulerY, 0));
  236. if (eulerYDiff2 <= 2.5f)
  237. {
  238. m_Docking = false;
  239. m_DockingStartTime = null;//reset docking start time
  240. }
  241. if (FollowWithPitch)
  242. {
  243. dockEulerX = Mathf.SmoothDampAngle(dockEulerX, headEulerX, ref dampSpeed, m_DampTime);
  244. float eulerXDiff2 = Quaternion.Angle(Quaternion.Euler(0, headEulerX, 0), Quaternion.Euler(0, dockEulerX, 0));
  245. if (eulerXDiff2 <= 2.5f)
  246. {
  247. m_Docking = false;
  248. m_DockingStartTime = null;//reset docking start time
  249. }
  250. }
  251. }
  252. else
  253. {
  254. dockEulerY = headEulerY;
  255. if (FollowWithPitch)
  256. {
  257. dockEulerX = headEulerX;
  258. }
  259. }
  260. }
  261. if (UpdateRotation)
  262. if (FollowWithPitch)
  263. {
  264. transform.rotation = Quaternion.Euler(dockEulerX, dockEulerY, 0);
  265. }
  266. else
  267. {
  268. transform.rotation = Quaternion.Euler(0, dockEulerY, 0);
  269. }
  270. }
  271. //Update pos:
  272. // modify by leaf.2019.08.30
  273. if (UpdatePosition)
  274. if (FollowWithPitch)
  275. {
  276. transform.position = headTransform.position + Quaternion.Euler(dockEulerX, dockEulerY, 0) * Vector3.forward * m_ZDepth + PositionOffset;
  277. }
  278. else
  279. {
  280. transform.position = headTransform.position + Quaternion.Euler(0, dockEulerY, 0) * Vector3.forward * m_ZDepth + PositionOffset;
  281. }
  282. }
  283. #endif
  284. }
  285. }