123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894 |
- /************************************************************************************
- 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.Generic;
- namespace UnityEngine.EventSystems
- {
- /// <summary>
- /// VR extension of PointerInputModule which supports gaze and controller pointing.
- /// </summary>
- public class OVRInputModule : PointerInputModule
- {
- [Tooltip("Object which points with Z axis. E.g. CentreEyeAnchor from OVRCameraRig")]
- public Transform rayTransform;
- public OVRCursor m_Cursor;
- [Tooltip("Gamepad button to act as gaze click")]
- public OVRInput.Button joyPadClickButton = OVRInput.Button.One;
- [Tooltip("Keyboard button to act as gaze click")]
- public KeyCode gazeClickKey = KeyCode.Space;
- [Header("Physics")]
- [Tooltip("Perform an sphere cast to determine correct depth for gaze pointer")]
- public bool performSphereCastForGazepointer;
- [Header("Gamepad Stick Scroll")]
- [Tooltip("Enable scrolling with the right stick on a gamepad")]
- public bool useRightStickScroll = true;
- [Tooltip("Deadzone for right stick to prevent accidental scrolling")]
- public float rightStickDeadZone = 0.15f;
- [Header("Touchpad Swipe Scroll")]
- [Tooltip("Enable scrolling by swiping the GearVR touchpad")]
- public bool useSwipeScroll = true;
- [Tooltip("Minimum trackpad movement in pixels to start swiping")]
- public float swipeDragThreshold = 2;
- [Tooltip("Distance scrolled when swipe scroll occurs")]
- public float swipeDragScale = 1f;
- /* It's debatable which way left and right are on the Gear VR touchpad since it's facing away from you
- * the following bool allows this to be swapped*/
- [Tooltip("Invert X axis on touchpad")]
- public bool InvertSwipeXAxis = false;
- // The raycaster that gets to do pointer interaction (e.g. with a mouse), gaze interaction always works
- [NonSerialized]
- public OVRRaycaster activeGraphicRaycaster;
- [Header("Dragging")]
- [Tooltip("Minimum pointer movement in degrees to start dragging")]
- public float angleDragThreshold = 1;
- [SerializeField]
- private float m_SpherecastRadius = 1.0f;
- // The following region contains code exactly the same as the implementation
- // of StandaloneInputModule. It is copied here rather than inheriting from StandaloneInputModule
- // because most of StandaloneInputModule is private so it isn't possible to easily derive from.
- // Future changes from Unity to StandaloneInputModule will make it possible for this class to
- // derive from StandaloneInputModule instead of PointerInput module.
- //
- // The following functions are not present in the following region since they have modified
- // versions in the next region:
- // Process
- // ProcessMouseEvent
- // UseMouse
- #region StandaloneInputModule code
- private float m_NextAction;
- private Vector2 m_LastMousePosition;
- private Vector2 m_MousePosition;
- protected OVRInputModule()
- {}
- #if UNITY_EDITOR
- protected override void Reset()
- {
- allowActivationOnMobileDevice = true;
- }
- #endif
- [Obsolete("Mode is no longer needed on input module as it handles both mouse and keyboard simultaneously.", false)]
- public enum InputMode
- {
- Mouse,
- Buttons
- }
- [Obsolete("Mode is no longer needed on input module as it handles both mouse and keyboard simultaneously.", false)]
- public InputMode inputMode
- {
- get { return InputMode.Mouse; }
- }
- [Header("Standalone Input Module")]
- [SerializeField]
- private string m_HorizontalAxis = "Horizontal";
- /// <summary>
- /// Name of the vertical axis for movement (if axis events are used).
- /// </summary>
- [SerializeField]
- private string m_VerticalAxis = "Vertical";
- /// <summary>
- /// Name of the submit button.
- /// </summary>
- [SerializeField]
- private string m_SubmitButton = "Submit";
- /// <summary>
- /// Name of the submit button.
- /// </summary>
- [SerializeField]
- private string m_CancelButton = "Cancel";
- [SerializeField]
- private float m_InputActionsPerSecond = 10;
- [SerializeField]
- private bool m_AllowActivationOnMobileDevice;
- public bool allowActivationOnMobileDevice
- {
- get { return m_AllowActivationOnMobileDevice; }
- set { m_AllowActivationOnMobileDevice = value; }
- }
- public float inputActionsPerSecond
- {
- get { return m_InputActionsPerSecond; }
- set { m_InputActionsPerSecond = value; }
- }
- /// <summary>
- /// Name of the horizontal axis for movement (if axis events are used).
- /// </summary>
- public string horizontalAxis
- {
- get { return m_HorizontalAxis; }
- set { m_HorizontalAxis = value; }
- }
- /// <summary>
- /// Name of the vertical axis for movement (if axis events are used).
- /// </summary>
- public string verticalAxis
- {
- get { return m_VerticalAxis; }
- set { m_VerticalAxis = value; }
- }
- public string submitButton
- {
- get { return m_SubmitButton; }
- set { m_SubmitButton = value; }
- }
- public string cancelButton
- {
- get { return m_CancelButton; }
- set { m_CancelButton = value; }
- }
- public override void UpdateModule()
- {
- m_LastMousePosition = m_MousePosition;
- m_MousePosition = Input.mousePosition;
- }
- public override bool IsModuleSupported()
- {
- // Check for mouse presence instead of whether touch is supported,
- // as you can connect mouse to a tablet and in that case we'd want
- // to use StandaloneInputModule for non-touch input events.
- return m_AllowActivationOnMobileDevice || Input.mousePresent;
- }
- public override bool ShouldActivateModule()
- {
- if (!base.ShouldActivateModule())
- return false;
- var shouldActivate = Input.GetButtonDown(m_SubmitButton);
- shouldActivate |= Input.GetButtonDown(m_CancelButton);
- shouldActivate |= !Mathf.Approximately(Input.GetAxisRaw(m_HorizontalAxis), 0.0f);
- shouldActivate |= !Mathf.Approximately(Input.GetAxisRaw(m_VerticalAxis), 0.0f);
- shouldActivate |= (m_MousePosition - m_LastMousePosition).sqrMagnitude > 0.0f;
- shouldActivate |= Input.GetMouseButtonDown(0);
- return shouldActivate;
- }
- public override void ActivateModule()
- {
- base.ActivateModule();
- m_MousePosition = Input.mousePosition;
- m_LastMousePosition = Input.mousePosition;
- var toSelect = eventSystem.currentSelectedGameObject;
- if (toSelect == null)
- toSelect = eventSystem.firstSelectedGameObject;
- eventSystem.SetSelectedGameObject(toSelect, GetBaseEventData());
- }
- public override void DeactivateModule()
- {
- base.DeactivateModule();
- ClearSelection();
- }
- /// <summary>
- /// Process submit keys.
- /// </summary>
- private bool SendSubmitEventToSelectedObject()
- {
- if (eventSystem.currentSelectedGameObject == null)
- return false;
- var data = GetBaseEventData();
- if (Input.GetButtonDown(m_SubmitButton))
- ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.submitHandler);
- if (Input.GetButtonDown(m_CancelButton))
- ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.cancelHandler);
- return data.used;
- }
- private bool AllowMoveEventProcessing(float time)
- {
- bool allow = Input.GetButtonDown(m_HorizontalAxis);
- allow |= Input.GetButtonDown(m_VerticalAxis);
- allow |= (time > m_NextAction);
- return allow;
- }
- private Vector2 GetRawMoveVector()
- {
- Vector2 move = Vector2.zero;
- move.x = Input.GetAxisRaw(m_HorizontalAxis);
- move.y = Input.GetAxisRaw(m_VerticalAxis);
- if (Input.GetButtonDown(m_HorizontalAxis))
- {
- if (move.x < 0)
- move.x = -1f;
- if (move.x > 0)
- move.x = 1f;
- }
- if (Input.GetButtonDown(m_VerticalAxis))
- {
- if (move.y < 0)
- move.y = -1f;
- if (move.y > 0)
- move.y = 1f;
- }
- return move;
- }
- /// <summary>
- /// Process keyboard events.
- /// </summary>
- private bool SendMoveEventToSelectedObject()
- {
- float time = Time.unscaledTime;
- if (!AllowMoveEventProcessing(time))
- return false;
- Vector2 movement = GetRawMoveVector();
- // Debug.Log(m_ProcessingEvent.rawType + " axis:" + m_AllowAxisEvents + " value:" + "(" + x + "," + y + ")");
- var axisEventData = GetAxisEventData(movement.x, movement.y, 0.6f);
- if (!Mathf.Approximately(axisEventData.moveVector.x, 0f)
- || !Mathf.Approximately(axisEventData.moveVector.y, 0f))
- {
- ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, axisEventData, ExecuteEvents.moveHandler);
- }
- m_NextAction = time + 1f / m_InputActionsPerSecond;
- return axisEventData.used;
- }
- private bool SendUpdateEventToSelectedObject()
- {
- if (eventSystem.currentSelectedGameObject == null)
- return false;
- var data = GetBaseEventData();
- ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler);
- return data.used;
- }
- /// <summary>
- /// Process the current mouse press.
- /// </summary>
- private void ProcessMousePress(MouseButtonEventData data)
- {
- var pointerEvent = data.buttonData;
- var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;
- // PointerDown notification
- if (data.PressedThisFrame())
- {
- pointerEvent.eligibleForClick = true;
- pointerEvent.delta = Vector2.zero;
- pointerEvent.dragging = false;
- pointerEvent.useDragThreshold = true;
- pointerEvent.pressPosition = pointerEvent.position;
- if (pointerEvent.IsVRPointer())
- {
- pointerEvent.SetSwipeStart(Input.mousePosition);
- }
- pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;
- DeselectIfSelectionChanged(currentOverGo, pointerEvent);
- // search for the control that will receive the press
- // if we can't find a press handler set the press
- // handler to be what would receive a click.
- var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);
- // didnt find a press handler... search for a click handler
- if (newPressed == null)
- newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
- // Debug.Log("Pressed: " + newPressed);
- float time = Time.unscaledTime;
- if (newPressed == pointerEvent.lastPress)
- {
- var diffTime = time - pointerEvent.clickTime;
- if (diffTime < 0.3f)
- ++pointerEvent.clickCount;
- else
- pointerEvent.clickCount = 1;
- pointerEvent.clickTime = time;
- }
- else
- {
- pointerEvent.clickCount = 1;
- }
- pointerEvent.pointerPress = newPressed;
- pointerEvent.rawPointerPress = currentOverGo;
- pointerEvent.clickTime = time;
- // Save the drag handler as well
- pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
- if (pointerEvent.pointerDrag != null)
- ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag);
- }
- // PointerUp notification
- if (data.ReleasedThisFrame())
- {
- // Debug.Log("Executing pressup on: " + pointer.pointerPress);
- ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
- // Debug.Log("KeyCode: " + pointer.eventData.keyCode);
- // see if we mouse up on the same element that we clicked on...
- var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
- // PointerClick and Drop events
- if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick)
- {
- ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler);
- }
- else if (pointerEvent.pointerDrag != null)
- {
- ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
- }
- pointerEvent.eligibleForClick = false;
- pointerEvent.pointerPress = null;
- pointerEvent.rawPointerPress = null;
- if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
- ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);
- pointerEvent.dragging = false;
- pointerEvent.pointerDrag = null;
- // redo pointer enter / exit to refresh state
- // so that if we moused over somethign that ignored it before
- // due to having pressed on something else
- // it now gets it.
- if (currentOverGo != pointerEvent.pointerEnter)
- {
- HandlePointerExitAndEnter(pointerEvent, null);
- HandlePointerExitAndEnter(pointerEvent, currentOverGo);
- }
- }
- }
- #endregion
- #region Modified StandaloneInputModule methods
- /// <summary>
- /// Process all mouse events. This is the same as the StandaloneInputModule version except that
- /// it takes MouseState as a parameter, allowing it to be used for both Gaze and Mouse
- /// pointerss.
- /// </summary>
- private void ProcessMouseEvent(MouseState mouseData)
- {
- var pressed = mouseData.AnyPressesThisFrame();
- var released = mouseData.AnyReleasesThisFrame();
- var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;
- if (!UseMouse(pressed, released, leftButtonData.buttonData))
- return;
- // Process the first mouse button fully
- ProcessMousePress(leftButtonData);
- ProcessMove(leftButtonData.buttonData);
- ProcessDrag(leftButtonData.buttonData);
- // Now process right / middle clicks
- ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData);
- ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData.buttonData);
- ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData);
- ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData.buttonData);
- if (!Mathf.Approximately(leftButtonData.buttonData.scrollDelta.sqrMagnitude, 0.0f))
- {
- var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(leftButtonData.buttonData.pointerCurrentRaycast.gameObject);
- ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler);
- }
- }
- /// <summary>
- /// Process this InputModule. Same as the StandaloneInputModule version, except that it calls
- /// ProcessMouseEvent twice, once for gaze pointers, and once for mouse pointers.
- /// </summary>
- public override void Process()
- {
- bool usedEvent = SendUpdateEventToSelectedObject();
- if (eventSystem.sendNavigationEvents)
- {
- if (!usedEvent)
- usedEvent |= SendMoveEventToSelectedObject();
- if (!usedEvent)
- SendSubmitEventToSelectedObject();
- }
- ProcessMouseEvent(GetGazePointerData());
- #if !UNITY_ANDROID
- ProcessMouseEvent(GetCanvasPointerData());
- #endif
- }
- /// <summary>
- /// Decide if mouse events need to be processed this frame. Same as StandloneInputModule except
- /// that the IsPointerMoving method from this class is used, instead of the method on PointerEventData
- /// </summary>
- private static bool UseMouse(bool pressed, bool released, PointerEventData pointerData)
- {
- if (pressed || released || IsPointerMoving(pointerData) || pointerData.IsScrolling())
- return true;
- return false;
- }
- #endregion
- /// <summary>
- /// Convenience function for cloning PointerEventData
- /// </summary>
- /// <param name="from">Copy this value</param>
- /// <param name="to">to this object</param>
- protected void CopyFromTo(OVRPointerEventData @from, OVRPointerEventData @to)
- {
- @to.position = @from.position;
- @to.delta = @from.delta;
- @to.scrollDelta = @from.scrollDelta;
- @to.pointerCurrentRaycast = @from.pointerCurrentRaycast;
- @to.pointerEnter = @from.pointerEnter;
- @to.worldSpaceRay = @from.worldSpaceRay;
- }
- /// <summary>
- /// Convenience function for cloning PointerEventData
- /// </summary>
- /// <param name="from">Copy this value</param>
- /// <param name="to">to this object</param>
- protected new void CopyFromTo(PointerEventData @from, PointerEventData @to)
- {
- @to.position = @from.position;
- @to.delta = @from.delta;
- @to.scrollDelta = @from.scrollDelta;
- @to.pointerCurrentRaycast = @from.pointerCurrentRaycast;
- @to.pointerEnter = @from.pointerEnter;
- }
- // In the following region we extend the PointerEventData system implemented in PointerInputModule
- // We define an additional dictionary for ray(e.g. gaze) based pointers. Mouse pointers still use the dictionary
- // in PointerInputModule
- #region PointerEventData pool
- // Pool for OVRRayPointerEventData for ray based pointers
- protected Dictionary<int, OVRPointerEventData> m_VRRayPointerData = new Dictionary<int, OVRPointerEventData>();
- protected bool GetPointerData(int id, out OVRPointerEventData data, bool create)
- {
- if (!m_VRRayPointerData.TryGetValue(id, out data) && create)
- {
- data = new OVRPointerEventData(eventSystem)
- {
- pointerId = id,
- };
- m_VRRayPointerData.Add(id, data);
- return true;
- }
- return false;
- }
- /// <summary>
- /// Clear pointer state for both types of pointer
- /// </summary>
- protected new void ClearSelection()
- {
- var baseEventData = GetBaseEventData();
- foreach (var pointer in m_PointerData.Values)
- {
- // clear all selection
- HandlePointerExitAndEnter(pointer, null);
- }
- foreach (var pointer in m_VRRayPointerData.Values)
- {
- // clear all selection
- HandlePointerExitAndEnter(pointer, null);
- }
- m_PointerData.Clear();
- eventSystem.SetSelectedGameObject(null, baseEventData);
- }
- #endregion
- /// <summary>
- /// For RectTransform, calculate it's normal in world space
- /// </summary>
- static Vector3 GetRectTransformNormal(RectTransform rectTransform)
- {
- Vector3[] corners = new Vector3[4];
- rectTransform.GetWorldCorners(corners);
- Vector3 BottomEdge = corners[3] - corners[0];
- Vector3 LeftEdge = corners[1] - corners[0];
- rectTransform.GetWorldCorners(corners);
- return Vector3.Cross(BottomEdge, LeftEdge).normalized;
- }
- private readonly MouseState m_MouseState = new MouseState();
- // The following 2 functions are equivalent to PointerInputModule.GetMousePointerEventData but are customized to
- // get data for ray pointers and canvas mouse pointers.
- /// <summary>
- /// State for a pointer controlled by a world space ray. E.g. gaze pointer
- /// </summary>
- /// <returns></returns>
- virtual protected MouseState GetGazePointerData()
- {
- // Get the OVRRayPointerEventData reference
- OVRPointerEventData leftData;
- GetPointerData(kMouseLeftId, out leftData, true );
- leftData.Reset();
- //Now set the world space ray. This ray is what the user uses to point at UI elements
- leftData.worldSpaceRay = new Ray(rayTransform.position, rayTransform.forward);
- leftData.scrollDelta = GetExtraScrollDelta();
- //Populate some default values
- leftData.button = PointerEventData.InputButton.Left;
- leftData.useDragThreshold = true;
- // Perform raycast to find intersections with world
- eventSystem.RaycastAll(leftData, m_RaycastResultCache);
- var raycast = FindFirstRaycast(m_RaycastResultCache);
- leftData.pointerCurrentRaycast = raycast;
- m_RaycastResultCache.Clear();
- m_Cursor.SetCursorRay(rayTransform);
- OVRRaycaster ovrRaycaster = raycast.module as OVRRaycaster;
- // We're only interested in intersections from OVRRaycasters
- if (ovrRaycaster)
- {
- // The Unity UI system expects event data to have a screen position
- // so even though this raycast came from a world space ray we must get a screen
- // space position for the camera attached to this raycaster for compatability
- leftData.position = ovrRaycaster.GetScreenPosition(raycast);
- // Find the world position and normal the Graphic the ray intersected
- RectTransform graphicRect = raycast.gameObject.GetComponent<RectTransform>();
- if (graphicRect != null)
- {
- // Set are gaze indicator with this world position and normal
- Vector3 worldPos = raycast.worldPosition;
- Vector3 normal = GetRectTransformNormal(graphicRect);
- m_Cursor.SetCursorStartDest(rayTransform.position, worldPos, normal);
- }
- }
- // Now process physical raycast intersections
- OVRPhysicsRaycaster physicsRaycaster = raycast.module as OVRPhysicsRaycaster;
- if (physicsRaycaster)
- {
- Vector3 position = raycast.worldPosition;
- if (performSphereCastForGazepointer)
- {
- // Here we cast a sphere into the scene rather than a ray. This gives a more accurate depth
- // for positioning a circular gaze pointer
- List<RaycastResult> results = new List<RaycastResult>();
- physicsRaycaster.Spherecast(leftData, results, m_SpherecastRadius);
- if (results.Count > 0 && results[0].distance < raycast.distance)
- {
- position = results[0].worldPosition;
- }
- }
- leftData.position = physicsRaycaster.GetScreenPos(raycast.worldPosition);
- m_Cursor.SetCursorStartDest(rayTransform.position, position, raycast.worldNormal);
- }
- // Stick default data values in right and middle slots for compatability
- // copy the apropriate data into right and middle slots
- OVRPointerEventData rightData;
- GetPointerData(kMouseRightId, out rightData, true );
- CopyFromTo(leftData, rightData);
- rightData.button = PointerEventData.InputButton.Right;
- OVRPointerEventData middleData;
- GetPointerData(kMouseMiddleId, out middleData, true );
- CopyFromTo(leftData, middleData);
- middleData.button = PointerEventData.InputButton.Middle;
- m_MouseState.SetButtonState(PointerEventData.InputButton.Left, GetGazeButtonState(), leftData);
- m_MouseState.SetButtonState(PointerEventData.InputButton.Right, PointerEventData.FramePressState.NotChanged, rightData);
- m_MouseState.SetButtonState(PointerEventData.InputButton.Middle, PointerEventData.FramePressState.NotChanged, middleData);
- return m_MouseState;
- }
- /// <summary>
- /// Get state for pointer which is a pointer moving in world space across the surface of a world space canvas.
- /// </summary>
- /// <returns></returns>
- protected MouseState GetCanvasPointerData()
- {
- // Get the OVRRayPointerEventData reference
- PointerEventData leftData;
- GetPointerData(kMouseLeftId, out leftData, true );
- leftData.Reset();
- // Setup default values here. Set position to zero because we don't actually know the pointer
- // positions. Each canvas knows the position of its canvas pointer.
- leftData.position = Vector2.zero;
- leftData.scrollDelta = Input.mouseScrollDelta;
- leftData.button = PointerEventData.InputButton.Left;
- if (activeGraphicRaycaster)
- {
- // Let the active raycaster find intersections on its canvas
- activeGraphicRaycaster.RaycastPointer(leftData, m_RaycastResultCache);
- var raycast = FindFirstRaycast(m_RaycastResultCache);
- leftData.pointerCurrentRaycast = raycast;
- m_RaycastResultCache.Clear();
- OVRRaycaster ovrRaycaster = raycast.module as OVRRaycaster;
- if (ovrRaycaster) // raycast may not actually contain a result
- {
- // The Unity UI system expects event data to have a screen position
- // so even though this raycast came from a world space ray we must get a screen
- // space position for the camera attached to this raycaster for compatability
- Vector2 position = ovrRaycaster.GetScreenPosition(raycast);
- leftData.delta = position - leftData.position;
- leftData.position = position;
- }
- }
- // copy the apropriate data into right and middle slots
- PointerEventData rightData;
- GetPointerData(kMouseRightId, out rightData, true );
- CopyFromTo(leftData, rightData);
- rightData.button = PointerEventData.InputButton.Right;
- PointerEventData middleData;
- GetPointerData(kMouseMiddleId, out middleData, true );
- CopyFromTo(leftData, middleData);
- middleData.button = PointerEventData.InputButton.Middle;
- m_MouseState.SetButtonState(PointerEventData.InputButton.Left, StateForMouseButton(0), leftData);
- m_MouseState.SetButtonState(PointerEventData.InputButton.Right, StateForMouseButton(1), rightData);
- m_MouseState.SetButtonState(PointerEventData.InputButton.Middle, StateForMouseButton(2), middleData);
- return m_MouseState;
- }
- /// <summary>
- /// New version of ShouldStartDrag implemented first in PointerInputModule. This version differs in that
- /// for ray based pointers it makes a decision about whether a drag should start based on the angular change
- /// the pointer has made so far, as seen from the camera. This also works when the world space ray is
- /// translated rather than rotated, since the beginning and end of the movement are considered as angle from
- /// the same point.
- /// </summary>
- private bool ShouldStartDrag(PointerEventData pointerEvent)
- {
- if (!pointerEvent.useDragThreshold)
- return true;
- if (!pointerEvent.IsVRPointer())
- {
- // Same as original behaviour for canvas based pointers
- return (pointerEvent.pressPosition - pointerEvent.position).sqrMagnitude >= eventSystem.pixelDragThreshold * eventSystem.pixelDragThreshold;
- }
- else
- {
- #if UNITY_ANDROID && !UNITY_EDITOR // On android allow swiping to start drag
- if (useSwipeScroll && ((Vector3)pointerEvent.GetSwipeStart() - Input.mousePosition).magnitude > swipeDragThreshold)
- {
- return true;
- }
- #endif
- // When it's not a screen space pointer we have to look at the angle it moved rather than the pixels distance
- // For gaze based pointing screen-space distance moved will always be near 0
- Vector3 cameraPos = pointerEvent.pressEventCamera.transform.position;
- Vector3 pressDir = (pointerEvent.pointerPressRaycast.worldPosition - cameraPos).normalized;
- Vector3 currentDir = (pointerEvent.pointerCurrentRaycast.worldPosition - cameraPos).normalized;
- return Vector3.Dot(pressDir, currentDir) < Mathf.Cos(Mathf.Deg2Rad * (angleDragThreshold));
- }
- }
- /// <summary>
- /// The purpose of this function is to allow us to switch between using the standard IsPointerMoving
- /// method for mouse driven pointers, but to always return true when it's a ray based pointer.
- /// All real-world ray-based input devices are always moving so for simplicity we just return true
- /// for them.
- ///
- /// If PointerEventData.IsPointerMoving was virtual we could just override that in
- /// OVRRayPointerEventData.
- /// </summary>
- /// <param name="pointerEvent"></param>
- /// <returns></returns>
- static bool IsPointerMoving(PointerEventData pointerEvent)
- {
- if (pointerEvent.IsVRPointer())
- return true;
- else
- return pointerEvent.IsPointerMoving();
- }
- protected Vector2 SwipeAdjustedPosition(Vector2 originalPosition, PointerEventData pointerEvent)
- {
- #if UNITY_ANDROID && !UNITY_EDITOR
- // On android we use the touchpad position (accessed through Input.mousePosition) to modify
- // the effective cursor position for events related to dragging. This allows the user to
- // use the touchpad to drag draggable UI elements
- if (useSwipeScroll)
- {
- Vector2 delta = (Vector2)Input.mousePosition - pointerEvent.GetSwipeStart();
- if (InvertSwipeXAxis)
- delta.x *= -1;
- return originalPosition + delta * swipeDragScale;
- }
- #endif
- // If not Gear VR or swipe scroll isn't enabled just return original position
- return originalPosition;
- }
- /// <summary>
- /// Exactly the same as the code from PointerInputModule, except that we call our own
- /// IsPointerMoving.
- ///
- /// This would also not be necessary if PointerEventData.IsPointerMoving was virtual
- /// </summary>
- /// <param name="pointerEvent"></param>
- protected override void ProcessDrag(PointerEventData pointerEvent)
- {
- Vector2 originalPosition = pointerEvent.position;
- bool moving = IsPointerMoving(pointerEvent);
- if (moving && pointerEvent.pointerDrag != null
- && !pointerEvent.dragging
- && ShouldStartDrag(pointerEvent))
- {
- if (pointerEvent.IsVRPointer())
- {
- //adjust the position used based on swiping action. Allowing the user to
- //drag items by swiping on the GearVR touchpad
- pointerEvent.position = SwipeAdjustedPosition (originalPosition, pointerEvent);
- }
- ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.beginDragHandler);
- pointerEvent.dragging = true;
- }
- // Drag notification
- if (pointerEvent.dragging && moving && pointerEvent.pointerDrag != null)
- {
- if (pointerEvent.IsVRPointer())
- {
- pointerEvent.position = SwipeAdjustedPosition(originalPosition, pointerEvent);
- }
- // Before doing drag we should cancel any pointer down state
- // And clear selection!
- if (pointerEvent.pointerPress != pointerEvent.pointerDrag)
- {
- ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
- pointerEvent.eligibleForClick = false;
- pointerEvent.pointerPress = null;
- pointerEvent.rawPointerPress = null;
- }
- ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.dragHandler);
- }
- }
- /// <summary>
- /// Get state of button corresponding to gaze pointer
- /// </summary>
- /// <returns></returns>
- virtual protected PointerEventData.FramePressState GetGazeButtonState()
- {
- var pressed = Input.GetKeyDown(gazeClickKey) || OVRInput.GetDown(joyPadClickButton);
- var released = Input.GetKeyUp(gazeClickKey) || OVRInput.GetUp(joyPadClickButton);
- #if UNITY_ANDROID && !UNITY_EDITOR
- // On Gear VR the mouse button events correspond to touch pad events. We only use these as gaze pointer clicks
- // on Gear VR because on PC the mouse clicks are used for actual mouse pointer interactions.
- pressed |= Input.GetMouseButtonDown(0);
- released |= Input.GetMouseButtonUp(0);
- #endif
- if (pressed && released)
- return PointerEventData.FramePressState.PressedAndReleased;
- if (pressed)
- return PointerEventData.FramePressState.Pressed;
- if (released)
- return PointerEventData.FramePressState.Released;
- return PointerEventData.FramePressState.NotChanged;
- }
- /// <summary>
- /// Get extra scroll delta from gamepad
- /// </summary>
- protected Vector2 GetExtraScrollDelta()
- {
- Vector2 scrollDelta = new Vector2();
- if (useRightStickScroll)
- {
- Vector2 s = OVRInput.Get(OVRInput.Axis2D.SecondaryThumbstick);
- if (Mathf.Abs(s.x) < rightStickDeadZone) s.x = 0;
- if (Mathf.Abs(s.y) < rightStickDeadZone) s.y = 0;
- scrollDelta = s;
- }
- return scrollDelta;
- }
- };
- }
|