OVRRaycaster.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /************************************************************************************
  2. Copyright : Copyright 2017 Oculus VR, LLC. All Rights reserved.
  3. Licensed under the Oculus VR Rift SDK License Version 3.4.1 (the "License");
  4. you may not use the Oculus VR Rift SDK except in compliance with the License,
  5. which is provided at the time of installation or download, or which
  6. otherwise accompanies this software in either electronic or hard copy form.
  7. You may obtain a copy of the License at
  8. https://developer.oculus.com/licenses/sdk-3.4.1
  9. Unless required by applicable law or agreed to in writing, the Oculus VR SDK
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ************************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Text;
  19. using UnityEngine;
  20. using UnityEngine.UI;
  21. using UnityEngine.EventSystems;
  22. using UnityEngine.Serialization;
  23. /// <summary>
  24. /// Extension of GraphicRaycaster to support ray casting with world space rays instead of just screen-space
  25. /// pointer positions
  26. /// </summary>
  27. [RequireComponent(typeof(Canvas))]
  28. public class OVRRaycaster : GraphicRaycaster, IPointerEnterHandler
  29. {
  30. [Tooltip("A world space pointer for this canvas")]
  31. public GameObject pointer;
  32. public int sortOrder = 0;
  33. protected OVRRaycaster()
  34. { }
  35. [NonSerialized]
  36. private Canvas m_Canvas;
  37. private Canvas canvas
  38. {
  39. get
  40. {
  41. if (m_Canvas != null)
  42. return m_Canvas;
  43. m_Canvas = GetComponent<Canvas>();
  44. return m_Canvas;
  45. }
  46. }
  47. public override Camera eventCamera
  48. {
  49. get
  50. {
  51. return canvas.worldCamera;
  52. }
  53. }
  54. public override int sortOrderPriority
  55. {
  56. get
  57. {
  58. return sortOrder;
  59. }
  60. }
  61. /// <summary>
  62. /// For the given ray, find graphics on this canvas which it intersects and are not blocked by other
  63. /// world objects
  64. /// </summary>
  65. [NonSerialized]
  66. private List<RaycastHit> m_RaycastResults = new List<RaycastHit>();
  67. private void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList, Ray ray, bool checkForBlocking)
  68. {
  69. //This function is closely based on
  70. //void GraphicRaycaster.Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
  71. if (canvas == null)
  72. return;
  73. float hitDistance = float.MaxValue;
  74. if (checkForBlocking && blockingObjects != BlockingObjects.None)
  75. {
  76. float dist = eventCamera.farClipPlane;
  77. if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
  78. {
  79. var hits = Physics.RaycastAll(ray, dist, m_BlockingMask);
  80. if (hits.Length > 0 && hits[0].distance < hitDistance)
  81. {
  82. hitDistance = hits[0].distance;
  83. }
  84. }
  85. if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
  86. {
  87. var hits = Physics2D.GetRayIntersectionAll(ray, dist, m_BlockingMask);
  88. if (hits.Length > 0 && hits[0].fraction * dist < hitDistance)
  89. {
  90. hitDistance = hits[0].fraction * dist;
  91. }
  92. }
  93. }
  94. m_RaycastResults.Clear();
  95. GraphicRaycast(canvas, ray, m_RaycastResults);
  96. for (var index = 0; index < m_RaycastResults.Count; index++)
  97. {
  98. var go = m_RaycastResults[index].graphic.gameObject;
  99. bool appendGraphic = true;
  100. if (ignoreReversedGraphics)
  101. {
  102. // If we have a camera compare the direction against the cameras forward.
  103. var cameraFoward = ray.direction;
  104. var dir = go.transform.rotation * Vector3.forward;
  105. appendGraphic = Vector3.Dot(cameraFoward, dir) > 0;
  106. }
  107. // Ignore points behind us (can happen with a canvas pointer)
  108. if (eventCamera.transform.InverseTransformPoint(m_RaycastResults[index].worldPos).z <= 0)
  109. {
  110. appendGraphic = false;
  111. }
  112. if (appendGraphic)
  113. {
  114. float distance = Vector3.Distance(ray.origin, m_RaycastResults[index].worldPos);
  115. if (distance >= hitDistance)
  116. {
  117. continue;
  118. }
  119. var castResult = new RaycastResult
  120. {
  121. gameObject = go,
  122. module = this,
  123. distance = distance,
  124. index = resultAppendList.Count,
  125. depth = m_RaycastResults[index].graphic.depth,
  126. worldPosition = m_RaycastResults[index].worldPos
  127. };
  128. resultAppendList.Add(castResult);
  129. }
  130. }
  131. }
  132. /// <summary>
  133. /// Performs a raycast using eventData.worldSpaceRay
  134. /// </summary>
  135. /// <param name="eventData"></param>
  136. /// <param name="resultAppendList"></param>
  137. public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
  138. {
  139. if (eventData.IsVRPointer())
  140. {
  141. Raycast(eventData, resultAppendList, eventData.GetRay(), true);
  142. }
  143. }
  144. /// <summary>
  145. /// Performs a raycast using the pointer object attached to this OVRRaycaster
  146. /// </summary>
  147. /// <param name="eventData"></param>
  148. /// <param name="resultAppendList"></param>
  149. public void RaycastPointer(PointerEventData eventData, List<RaycastResult> resultAppendList)
  150. {
  151. if (pointer != null && pointer.activeInHierarchy)
  152. {
  153. Raycast(eventData, resultAppendList, new Ray(eventCamera.transform.position, (pointer.transform.position - eventCamera.transform.position).normalized), false);
  154. }
  155. }
  156. /// <summary>
  157. /// Perform a raycast into the screen and collect all graphics underneath it.
  158. /// </summary>
  159. [NonSerialized]
  160. static readonly List<RaycastHit> s_SortedGraphics = new List<RaycastHit>();
  161. private void GraphicRaycast(Canvas canvas, Ray ray, List<RaycastHit> results)
  162. {
  163. //This function is based closely on :
  164. // void GraphicRaycaster.Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, List<Graphic> results)
  165. // But modified to take a Ray instead of a canvas pointer, and also to explicitly ignore
  166. // the graphic associated with the pointer
  167. // Necessary for the event system
  168. var foundGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas);
  169. s_SortedGraphics.Clear();
  170. for (int i = 0; i < foundGraphics.Count; ++i)
  171. {
  172. Graphic graphic = foundGraphics[i];
  173. // -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
  174. if (graphic.depth == -1 || (pointer == graphic.gameObject))
  175. continue;
  176. Vector3 worldPos;
  177. if (RayIntersectsRectTransform(graphic.rectTransform, ray, out worldPos))
  178. {
  179. //Work out where this is on the screen for compatibility with existing Unity UI code
  180. Vector2 screenPos = eventCamera.WorldToScreenPoint(worldPos);
  181. // mask/image intersection - See Unity docs on eventAlphaThreshold for when this does anything
  182. if (graphic.Raycast(screenPos, eventCamera))
  183. {
  184. RaycastHit hit;
  185. hit.graphic = graphic;
  186. hit.worldPos = worldPos;
  187. hit.fromMouse = false;
  188. s_SortedGraphics.Add(hit);
  189. }
  190. }
  191. }
  192. s_SortedGraphics.Sort((g1, g2) => g2.graphic.depth.CompareTo(g1.graphic.depth));
  193. for (int i = 0; i < s_SortedGraphics.Count; ++i)
  194. {
  195. results.Add(s_SortedGraphics[i]);
  196. }
  197. }
  198. /// <summary>
  199. /// Get screen position of worldPosition contained in this RaycastResult
  200. /// </summary>
  201. /// <param name="worldPosition"></param>
  202. /// <returns></returns>
  203. public Vector2 GetScreenPosition(RaycastResult raycastResult)
  204. {
  205. // In future versions of Uinty RaycastResult will contain screenPosition so this will not be necessary
  206. return eventCamera.WorldToScreenPoint(raycastResult.worldPosition);
  207. }
  208. /// <summary>
  209. /// Detects whether a ray intersects a RectTransform and if it does also
  210. /// returns the world position of the intersection.
  211. /// </summary>
  212. /// <param name="rectTransform"></param>
  213. /// <param name="ray"></param>
  214. /// <param name="worldPos"></param>
  215. /// <returns></returns>
  216. static bool RayIntersectsRectTransform(RectTransform rectTransform, Ray ray, out Vector3 worldPos)
  217. {
  218. Vector3[] corners = new Vector3[4];
  219. rectTransform.GetWorldCorners(corners);
  220. Plane plane = new Plane(corners[0], corners[1], corners[2]);
  221. float enter;
  222. if (!plane.Raycast(ray, out enter))
  223. {
  224. worldPos = Vector3.zero;
  225. return false;
  226. }
  227. Vector3 intersection = ray.GetPoint(enter);
  228. Vector3 BottomEdge = corners[3] - corners[0];
  229. Vector3 LeftEdge = corners[1] - corners[0];
  230. float BottomDot = Vector3.Dot(intersection - corners[0], BottomEdge);
  231. float LeftDot = Vector3.Dot(intersection - corners[0], LeftEdge);
  232. if (BottomDot < BottomEdge.sqrMagnitude && // Can use sqrMag because BottomEdge is not normalized
  233. LeftDot < LeftEdge.sqrMagnitude &&
  234. BottomDot >= 0 &&
  235. LeftDot >= 0)
  236. {
  237. worldPos = corners[0] + LeftDot * LeftEdge / LeftEdge.sqrMagnitude + BottomDot * BottomEdge / BottomEdge.sqrMagnitude;
  238. return true;
  239. }
  240. else
  241. {
  242. worldPos = Vector3.zero;
  243. return false;
  244. }
  245. }
  246. struct RaycastHit
  247. {
  248. public Graphic graphic;
  249. public Vector3 worldPos;
  250. public bool fromMouse;
  251. };
  252. /// <summary>
  253. /// Is this the currently focussed Raycaster according to the InputModule
  254. /// </summary>
  255. /// <returns></returns>
  256. public bool IsFocussed()
  257. {
  258. OVRInputModule inputModule = EventSystem.current.currentInputModule as OVRInputModule;
  259. return inputModule && inputModule.activeGraphicRaycaster == this;
  260. }
  261. public void OnPointerEnter(PointerEventData e)
  262. {
  263. if (e.IsVRPointer())
  264. {
  265. // Gaze has entered this canvas. We'll make it the active one so that canvas-mouse pointer can be used.
  266. OVRInputModule inputModule = EventSystem.current.currentInputModule as OVRInputModule;
  267. inputModule.activeGraphicRaycaster = this;
  268. }
  269. }
  270. }