forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			329 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
/************************************************************************************
 | 
						|
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
 | 
						|
 | 
						|
Licensed under the Oculus Master SDK License Version 1.0 (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/oculusmastersdk-1.0/
 | 
						|
 | 
						|
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;
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |