123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- /************************************************************************************
- Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
- Licensed under the Oculus Utilities SDK License Version 1.31 (the "License"); you may not use
- the Utilities SDK except in compliance with the License, which is provided at the time of installation
- or download, or which otherwise accompanies this software in either electronic or hard copy form.
- You may obtain a copy of the License at
- https://developer.oculus.com/licenses/utilities-1.31
- Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
- under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
- ANY KIND, either express or implied. See the License for the specific language governing
- permissions and limitations under the License.
- ************************************************************************************/
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Text;
- using UnityEngine;
- using UnityEngine.UI;
- using UnityEngine.EventSystems;
- using UnityEngine.Serialization;
- /// <summary>
- /// Extension of GraphicRaycaster to support ray casting with world space rays instead of just screen-space
- /// pointer positions
- /// </summary>
- [RequireComponent(typeof(Canvas))]
- public class OVRRaycaster : GraphicRaycaster, IPointerEnterHandler
- {
- [Tooltip("A world space pointer for this canvas")]
- public GameObject pointer;
- public int sortOrder = 0;
- protected OVRRaycaster()
- { }
- [NonSerialized]
- private Canvas m_Canvas;
- private Canvas canvas
- {
- get
- {
- if (m_Canvas != null)
- return m_Canvas;
- m_Canvas = GetComponent<Canvas>();
- return m_Canvas;
- }
- }
- public override Camera eventCamera
- {
- get
- {
- return canvas.worldCamera;
- }
- }
- public override int sortOrderPriority
- {
- get
- {
- return sortOrder;
- }
- }
- protected override void Start()
- {
- if(!canvas.worldCamera)
- {
- Debug.Log("Canvas does not have an event camera attached. Attaching OVRCameraRig.centerEyeAnchor as default.");
- OVRCameraRig rig = FindObjectOfType<OVRCameraRig>();
- canvas.worldCamera = rig.centerEyeAnchor.gameObject.GetComponent<Camera>();
- }
- }
- /// <summary>
- /// For the given ray, find graphics on this canvas which it intersects and are not blocked by other
- /// world objects
- /// </summary>
- [NonSerialized]
- private List<RaycastHit> m_RaycastResults = new List<RaycastHit>();
- private void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList, Ray ray, bool checkForBlocking)
- {
- //This function is closely based on
- //void GraphicRaycaster.Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
- if (canvas == null)
- return;
- float hitDistance = float.MaxValue;
- if (checkForBlocking && blockingObjects != BlockingObjects.None)
- {
- float dist = eventCamera.farClipPlane;
- if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
- {
- var hits = Physics.RaycastAll(ray, dist, m_BlockingMask);
- if (hits.Length > 0 && hits[0].distance < hitDistance)
- {
- hitDistance = hits[0].distance;
- }
- }
- if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
- {
- var hits = Physics2D.GetRayIntersectionAll(ray, dist, m_BlockingMask);
- if (hits.Length > 0 && hits[0].fraction * dist < hitDistance)
- {
- hitDistance = hits[0].fraction * dist;
- }
- }
- }
- m_RaycastResults.Clear();
- GraphicRaycast(canvas, ray, m_RaycastResults);
- for (var index = 0; index < m_RaycastResults.Count; index++)
- {
- var go = m_RaycastResults[index].graphic.gameObject;
- bool appendGraphic = true;
- if (ignoreReversedGraphics)
- {
- // If we have a camera compare the direction against the cameras forward.
- var cameraFoward = ray.direction;
- var dir = go.transform.rotation * Vector3.forward;
- appendGraphic = Vector3.Dot(cameraFoward, dir) > 0;
- }
- // Ignore points behind us (can happen with a canvas pointer)
- if (eventCamera.transform.InverseTransformPoint(m_RaycastResults[index].worldPos).z <= 0)
- {
- appendGraphic = false;
- }
- if (appendGraphic)
- {
- float distance = Vector3.Distance(ray.origin, m_RaycastResults[index].worldPos);
- if (distance >= hitDistance)
- {
- continue;
- }
- var castResult = new RaycastResult
- {
- gameObject = go,
- module = this,
- distance = distance,
- index = resultAppendList.Count,
- depth = m_RaycastResults[index].graphic.depth,
- worldPosition = m_RaycastResults[index].worldPos
- };
- resultAppendList.Add(castResult);
- }
- }
- }
- /// <summary>
- /// Performs a raycast using eventData.worldSpaceRay
- /// </summary>
- /// <param name="eventData"></param>
- /// <param name="resultAppendList"></param>
- public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
- {
- if (eventData.IsVRPointer())
- {
- Raycast(eventData, resultAppendList, eventData.GetRay(), true);
- }
- }
- /// <summary>
- /// Performs a raycast using the pointer object attached to this OVRRaycaster
- /// </summary>
- /// <param name="eventData"></param>
- /// <param name="resultAppendList"></param>
- public void RaycastPointer(PointerEventData eventData, List<RaycastResult> resultAppendList)
- {
- if (pointer != null && pointer.activeInHierarchy)
- {
- Raycast(eventData, resultAppendList, new Ray(eventCamera.transform.position, (pointer.transform.position - eventCamera.transform.position).normalized), false);
- }
- }
- /// <summary>
- /// Perform a raycast into the screen and collect all graphics underneath it.
- /// </summary>
- [NonSerialized]
- static readonly List<RaycastHit> s_SortedGraphics = new List<RaycastHit>();
- private void GraphicRaycast(Canvas canvas, Ray ray, List<RaycastHit> results)
- {
- //This function is based closely on :
- // void GraphicRaycaster.Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, List<Graphic> results)
- // But modified to take a Ray instead of a canvas pointer, and also to explicitly ignore
- // the graphic associated with the pointer
- // Necessary for the event system
- var foundGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas);
- s_SortedGraphics.Clear();
- for (int i = 0; i < foundGraphics.Count; ++i)
- {
- Graphic graphic = foundGraphics[i];
- // -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
- if (graphic.depth == -1 || (pointer == graphic.gameObject))
- continue;
- Vector3 worldPos;
- if (RayIntersectsRectTransform(graphic.rectTransform, ray, out worldPos))
- {
- //Work out where this is on the screen for compatibility with existing Unity UI code
- Vector2 screenPos = eventCamera.WorldToScreenPoint(worldPos);
- // mask/image intersection - See Unity docs on eventAlphaThreshold for when this does anything
- if (graphic.Raycast(screenPos, eventCamera))
- {
- RaycastHit hit;
- hit.graphic = graphic;
- hit.worldPos = worldPos;
- hit.fromMouse = false;
- s_SortedGraphics.Add(hit);
- }
- }
- }
- s_SortedGraphics.Sort((g1, g2) => g2.graphic.depth.CompareTo(g1.graphic.depth));
- for (int i = 0; i < s_SortedGraphics.Count; ++i)
- {
- results.Add(s_SortedGraphics[i]);
- }
- }
- /// <summary>
- /// Get screen position of worldPosition contained in this RaycastResult
- /// </summary>
- /// <param name="worldPosition"></param>
- /// <returns></returns>
- public Vector2 GetScreenPosition(RaycastResult raycastResult)
- {
- // In future versions of Uinty RaycastResult will contain screenPosition so this will not be necessary
- return eventCamera.WorldToScreenPoint(raycastResult.worldPosition);
- }
- /// <summary>
- /// Detects whether a ray intersects a RectTransform and if it does also
- /// returns the world position of the intersection.
- /// </summary>
- /// <param name="rectTransform"></param>
- /// <param name="ray"></param>
- /// <param name="worldPos"></param>
- /// <returns></returns>
- static bool RayIntersectsRectTransform(RectTransform rectTransform, Ray ray, out Vector3 worldPos)
- {
- Vector3[] corners = new Vector3[4];
- rectTransform.GetWorldCorners(corners);
- Plane plane = new Plane(corners[0], corners[1], corners[2]);
- float enter;
- if (!plane.Raycast(ray, out enter))
- {
- worldPos = Vector3.zero;
- return false;
- }
- Vector3 intersection = ray.GetPoint(enter);
- Vector3 BottomEdge = corners[3] - corners[0];
- Vector3 LeftEdge = corners[1] - corners[0];
- float BottomDot = Vector3.Dot(intersection - corners[0], BottomEdge);
- float LeftDot = Vector3.Dot(intersection - corners[0], LeftEdge);
- if (BottomDot < BottomEdge.sqrMagnitude && // Can use sqrMag because BottomEdge is not normalized
- LeftDot < LeftEdge.sqrMagnitude &&
- BottomDot >= 0 &&
- LeftDot >= 0)
- {
- worldPos = corners[0] + LeftDot * LeftEdge / LeftEdge.sqrMagnitude + BottomDot * BottomEdge / BottomEdge.sqrMagnitude;
- return true;
- }
- else
- {
- worldPos = Vector3.zero;
- return false;
- }
- }
- struct RaycastHit
- {
- public Graphic graphic;
- public Vector3 worldPos;
- public bool fromMouse;
- };
- /// <summary>
- /// Is this the currently focussed Raycaster according to the InputModule
- /// </summary>
- /// <returns></returns>
- public bool IsFocussed()
- {
- OVRInputModule inputModule = EventSystem.current.currentInputModule as OVRInputModule;
- return inputModule && inputModule.activeGraphicRaycaster == this;
- }
- public void OnPointerEnter(PointerEventData e)
- {
- if (e.IsVRPointer())
- {
- // Gaze has entered this canvas. We'll make it the active one so that canvas-mouse pointer can be used.
- OVRInputModule inputModule = EventSystem.current.currentInputModule as OVRInputModule;
- if(inputModule != null)
- {
- inputModule.activeGraphicRaycaster = this;
- }
- }
- }
- }
|