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.

328 lines
11 KiB

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