Support for hand tracking

This commit is contained in:
tverona1
2020-05-02 19:29:40 -07:00
parent e13af7dc78
commit ddea5d2acd
16 changed files with 1663 additions and 374 deletions
@@ -0,0 +1,372 @@
/************************************************************************************
Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
See SampleFramework license.txt for license terms. Unless required by applicable law
or agreed to in writing, the sample code is provided “AS IS” WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the license for specific
language governing permissions and limitations under the license.
************************************************************************************/
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
namespace ControllerSelection
{
public class HandsManager : MonoBehaviour
{
private const string SKELETON_VISUALIZER_NAME = "SkeletonRenderer";
[SerializeField] GameObject _leftHand = null;
[SerializeField] GameObject _rightHand = null;
public HandsVisualMode VisualMode = HandsVisualMode.Mesh;
private OVRHand[] _hand = new OVRHand[(int)OVRHand.Hand.HandRight + 1];
private OVRSkeleton[] _handSkeleton = new OVRSkeleton[(int)OVRHand.Hand.HandRight + 1];
private OVRSkeletonRenderer[] _handSkeletonRenderer = new OVRSkeletonRenderer[(int)OVRHand.Hand.HandRight + 1];
private OVRMesh[] _handMesh = new OVRMesh[(int)OVRHand.Hand.HandRight + 1];
private OVRMeshRenderer[] _handMeshRenderer = new OVRMeshRenderer[(int)OVRHand.Hand.HandRight + 1];
private SkinnedMeshRenderer _leftMeshRenderer = null;
private SkinnedMeshRenderer _rightMeshRenderer = null;
private GameObject _leftSkeletonVisual = null;
private GameObject _rightSkeletonVisual = null;
private float _currentHandAlpha = 1.0f;
private int HandAlphaId = Shader.PropertyToID("_HandAlpha");
private PinchMode[] _pinchModeLeft = new PinchMode[(int)OVRHand.HandFinger.Max];
private PinchMode[] _pinchModeRight = new PinchMode[(int)OVRHand.HandFinger.Max];
private PinchMode[] _lastPinchModeLeft = new PinchMode[(int)OVRHand.HandFinger.Max];
private PinchMode[] _lastPinchModeRight = new PinchMode[(int)OVRHand.HandFinger.Max];
// Pinch strength to be considered a pinch
static readonly float PinchMaxStrength = 0.9f;
public enum HandsVisualMode
{
Mesh = 0, Skeleton = 1, Both = 2
}
public enum PinchMode
{
Pinched = 0, NotPinched = 1
}
public OVRHand RightHand
{
get
{
return _hand[(int)OVRHand.Hand.HandRight];
}
private set
{
_hand[(int)OVRHand.Hand.HandRight] = value;
}
}
public OVRSkeleton RightHandSkeleton
{
get
{
return _handSkeleton[(int)OVRHand.Hand.HandRight];
}
private set
{
_handSkeleton[(int)OVRHand.Hand.HandRight] = value;
}
}
public OVRSkeletonRenderer RightHandSkeletonRenderer
{
get
{
return _handSkeletonRenderer[(int)OVRHand.Hand.HandRight];
}
private set
{
_handSkeletonRenderer[(int)OVRHand.Hand.HandRight] = value;
}
}
public OVRMesh RightHandMesh
{
get
{
return _handMesh[(int)OVRHand.Hand.HandRight];
}
private set
{
_handMesh[(int)OVRHand.Hand.HandRight] = value;
}
}
public OVRMeshRenderer RightHandMeshRenderer
{
get
{
return _handMeshRenderer[(int)OVRHand.Hand.HandRight];
}
private set
{
_handMeshRenderer[(int)OVRHand.Hand.HandRight] = value;
}
}
public OVRHand LeftHand
{
get
{
return _hand[(int)OVRHand.Hand.HandLeft];
}
private set
{
_hand[(int)OVRHand.Hand.HandLeft] = value;
}
}
public OVRSkeleton LeftHandSkeleton
{
get
{
return _handSkeleton[(int)OVRHand.Hand.HandLeft];
}
private set
{
_handSkeleton[(int)OVRHand.Hand.HandLeft] = value;
}
}
public OVRSkeletonRenderer LeftHandSkeletonRenderer
{
get
{
return _handSkeletonRenderer[(int)OVRHand.Hand.HandLeft];
}
private set
{
_handSkeletonRenderer[(int)OVRHand.Hand.HandLeft] = value;
}
}
public OVRMesh LeftHandMesh
{
get
{
return _handMesh[(int)OVRHand.Hand.HandLeft];
}
private set
{
_handMesh[(int)OVRHand.Hand.HandLeft] = value;
}
}
public OVRMeshRenderer LeftHandMeshRenderer
{
get
{
return _handMeshRenderer[(int)OVRHand.Hand.HandLeft];
}
private set
{
_handMeshRenderer[(int)OVRHand.Hand.HandLeft] = value;
}
}
public static HandsManager Instance { get; private set; }
private void Awake()
{
if (Instance && Instance != this)
{
Destroy(this);
return;
}
Instance = this;
Assert.IsNotNull(_leftHand);
Assert.IsNotNull(_rightHand);
LeftHand = _leftHand.GetComponent<OVRHand>();
LeftHandSkeleton = _leftHand.GetComponent<OVRSkeleton>();
LeftHandSkeletonRenderer = _leftHand.GetComponent<OVRSkeletonRenderer>();
LeftHandMesh = _leftHand.GetComponent<OVRMesh>();
LeftHandMeshRenderer = _leftHand.GetComponent<OVRMeshRenderer>();
RightHand = _rightHand.GetComponent<OVRHand>();
RightHandSkeleton = _rightHand.GetComponent<OVRSkeleton>();
RightHandSkeletonRenderer = _rightHand.GetComponent<OVRSkeletonRenderer>();
RightHandMesh = _rightHand.GetComponent<OVRMesh>();
RightHandMeshRenderer = _rightHand.GetComponent<OVRMeshRenderer>();
_leftMeshRenderer = LeftHand.GetComponent<SkinnedMeshRenderer>();
_rightMeshRenderer = RightHand.GetComponent<SkinnedMeshRenderer>();
StartCoroutine(FindSkeletonVisualGameObjects());
}
private void Update()
{
switch (VisualMode)
{
case HandsVisualMode.Mesh:
case HandsVisualMode.Skeleton:
_currentHandAlpha = 1.0f;
break;
case HandsVisualMode.Both:
_currentHandAlpha = 0.6f;
break;
default:
_currentHandAlpha = 1.0f;
break;
}
_rightMeshRenderer.sharedMaterial.SetFloat(HandAlphaId, _currentHandAlpha);
_leftMeshRenderer.sharedMaterial.SetFloat(HandAlphaId, _currentHandAlpha);
UpdateFingerPinching(LeftHand, _pinchModeLeft, _lastPinchModeLeft);
UpdateFingerPinching(RightHand, _pinchModeRight, _lastPinchModeRight);
}
private IEnumerator FindSkeletonVisualGameObjects()
{
while (!_leftSkeletonVisual || !_rightSkeletonVisual)
{
if (!_leftSkeletonVisual)
{
Transform leftSkeletonVisualTransform = LeftHand.transform.Find(SKELETON_VISUALIZER_NAME);
if (leftSkeletonVisualTransform)
{
_leftSkeletonVisual = leftSkeletonVisualTransform.gameObject;
}
}
if (!_rightSkeletonVisual)
{
Transform rightSkeletonVisualTransform = RightHand.transform.Find(SKELETON_VISUALIZER_NAME);
if (rightSkeletonVisualTransform)
{
_rightSkeletonVisual = rightSkeletonVisualTransform.gameObject;
}
}
yield return null;
}
SetToCurrentVisualMode();
}
public void SwitchVisualization()
{
if (!_leftSkeletonVisual || !_rightSkeletonVisual)
{
return;
}
VisualMode = (HandsVisualMode)(((int)VisualMode + 1) % ((int)HandsVisualMode.Both + 1));
SetToCurrentVisualMode();
}
private void SetToCurrentVisualMode()
{
switch (VisualMode)
{
case HandsVisualMode.Mesh:
RightHandMeshRenderer.enabled = true;
_rightMeshRenderer.enabled = true;
_rightSkeletonVisual.gameObject.SetActive(false);
LeftHandMeshRenderer.enabled = true;
_leftMeshRenderer.enabled = true;
_leftSkeletonVisual.gameObject.SetActive(false);
break;
case HandsVisualMode.Skeleton:
RightHandMeshRenderer.enabled = false;
_rightMeshRenderer.enabled = false;
_rightSkeletonVisual.gameObject.SetActive(true);
LeftHandMeshRenderer.enabled = false;
_leftMeshRenderer.enabled = false;
_leftSkeletonVisual.gameObject.SetActive(true);
break;
case HandsVisualMode.Both:
RightHandMeshRenderer.enabled = true;
_rightMeshRenderer.enabled = true;
_rightSkeletonVisual.gameObject.SetActive(true);
LeftHandMeshRenderer.enabled = true;
_leftMeshRenderer.enabled = true;
_leftSkeletonVisual.gameObject.SetActive(true);
break;
default:
break;
}
}
public static List<OVRBoneCapsule> GetCapsulesPerBone(OVRSkeleton skeleton, OVRSkeleton.BoneId boneId)
{
List<OVRBoneCapsule> boneCapsules = new List<OVRBoneCapsule>();
var capsules = skeleton.Capsules;
for (int i = 0; i < capsules.Count; ++i)
{
if (capsules[i].BoneIndex == (short)boneId)
{
boneCapsules.Add(capsules[i]);
}
}
return boneCapsules;
}
public PinchMode GetLeftPinchMode(OVRHand.HandFinger finger)
{
return _pinchModeLeft[(int)finger];
}
public PinchMode GetRightPinchMode(OVRHand.HandFinger finger)
{
return _pinchModeRight[(int)finger];
}
public PinchMode GetLastLeftPinchMode(OVRHand.HandFinger finger)
{
return _lastPinchModeLeft[(int)finger];
}
public PinchMode GetLastRightPinchMode(OVRHand.HandFinger finger)
{
return _lastPinchModeRight[(int)finger];
}
public bool IsInitialized()
{
return LeftHandSkeleton && LeftHandSkeleton.IsInitialized &&
RightHandSkeleton && RightHandSkeleton.IsInitialized &&
LeftHandMesh && LeftHandMesh.IsInitialized &&
RightHandMesh && RightHandMesh.IsInitialized;
}
static private bool IsFingerPinched(OVRHand hand, OVRHand.HandFinger finger)
{
return hand.GetFingerConfidence(finger) == OVRHand.TrackingConfidence.High && hand.GetFingerIsPinching(finger) && hand.GetFingerPinchStrength(finger) >= PinchMaxStrength;
}
/// <summary>
/// Enumerates all fingers and updates pinching status. Also tracks last pinch status.
/// </summary>
/// <param name="hand"></param>
/// <param name="pinchModes"></param>
/// <param name="lastPinchModes"></param>
private void UpdateFingerPinching(OVRHand hand, PinchMode[] pinchModes, PinchMode[] lastPinchModes)
{
for (int i = 0; i < pinchModes.Length; i++)
{
lastPinchModes[i] = pinchModes[i];
pinchModes[i] = PinchMode.NotPinched;
}
var isReliable = hand.IsTracked && hand.HandConfidence == OVRHand.TrackingConfidence.High;
if (!isReliable)
{
return;
}
pinchModes[(int)OVRHand.HandFinger.Thumb] = IsFingerPinched(hand, OVRHand.HandFinger.Thumb) ? PinchMode.Pinched : PinchMode.NotPinched;
pinchModes[(int)OVRHand.HandFinger.Index] = IsFingerPinched(hand, OVRHand.HandFinger.Index) ? PinchMode.Pinched : PinchMode.NotPinched;
pinchModes[(int)OVRHand.HandFinger.Middle] = IsFingerPinched(hand, OVRHand.HandFinger.Middle) ? PinchMode.Pinched : PinchMode.NotPinched;
pinchModes[(int)OVRHand.HandFinger.Ring] = IsFingerPinched(hand, OVRHand.HandFinger.Ring) ? PinchMode.Pinched : PinchMode.NotPinched;
pinchModes[(int)OVRHand.HandFinger.Pinky] = IsFingerPinched(hand, OVRHand.HandFinger.Pinky) ? PinchMode.Pinched : PinchMode.NotPinched;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 355b24382fa1a9c4f8b72fd2095581f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -19,37 +19,137 @@ limitations under the License.
************************************************************************************/
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using UnityEngine;
namespace ControllerSelection {
public class OVRInputHelpers {
// Given a controller and tracking spcae, return the ray that controller uses.
namespace ControllerSelection
{
public class OVRInputHelpers
{
public enum HandFilter
{
None = 0,
Left = 1,
Right = 2,
Both = Left | Right
}
/// <summary>
/// Indicates whether hand started pinching in this frame
/// </summary>
/// <param name="activeController"></param>
/// <param name="finger"></param>
/// <returns></returns>
public static bool IsFingerStartPinching(OVRInput.Controller activeController, OVRHand.HandFinger finger)
{
bool isLeftHand = (activeController & OVRInput.Controller.LHand) == OVRInput.Controller.LHand;
var curPinch = isLeftHand ? HandsManager.Instance.GetLeftPinchMode(finger) : HandsManager.Instance.GetRightPinchMode(finger);
var lastPinch = isLeftHand ? HandsManager.Instance.GetLastLeftPinchMode(finger) : HandsManager.Instance.GetLastRightPinchMode(finger);
return (lastPinch == HandsManager.PinchMode.NotPinched && curPinch == HandsManager.PinchMode.Pinched);
}
/// <summary>
/// Indicates whether hand stopped pinching in this frame
/// </summary>
/// <param name="activeController"></param>
/// <param name="finger"></param>
/// <returns></returns>
public static bool IsFingerStopPinching(OVRInput.Controller activeController, OVRHand.HandFinger finger)
{
bool isLeftHand = (activeController & OVRInput.Controller.LHand) == OVRInput.Controller.LHand;
var curPinch = isLeftHand ? HandsManager.Instance.GetLeftPinchMode(finger) : HandsManager.Instance.GetRightPinchMode(finger);
var lastPinch = isLeftHand ? HandsManager.Instance.GetLastLeftPinchMode(finger) : HandsManager.Instance.GetLastRightPinchMode(finger);
return (lastPinch == HandsManager.PinchMode.Pinched && curPinch == HandsManager.PinchMode.NotPinched);
}
/// <summary>
/// Indicates whether hand is pinching
/// </summary>
/// <param name="activeController"></param>
/// <param name="finger"></param>
/// <returns></returns>
public static bool IsFingerPinching(OVRInput.Controller activeController, OVRHand.HandFinger finger)
{
bool isLeftHand = (activeController & OVRInput.Controller.LHand) == OVRInput.Controller.LHand;
var curPinch = isLeftHand ? HandsManager.Instance.GetLeftPinchMode(finger) : HandsManager.Instance.GetRightPinchMode(finger);
return (curPinch == HandsManager.PinchMode.Pinched);
}
/// <summary>
/// Returns hand orientation
/// </summary>
/// <param name="factiveController"></param>
/// <param name="orientation"></param>
/// <param name="position"></param>
/// <returns></returns>
public static bool GetHandOrientation(OVRInput.Controller activeController, out Quaternion orientation, out Vector3 position)
{
orientation = default(Quaternion);
position = default(Vector3);
if ((activeController & OVRInput.Controller.Hands) == OVRInput.Controller.None)
{
return false;
}
if (!HandsManager.Instance || !HandsManager.Instance.IsInitialized())
{
return false;
}
var hand = (activeController & OVRInput.Controller.RHand) == OVRInput.Controller.RHand ? HandsManager.Instance.RightHand : HandsManager.Instance.LeftHand;
var isReliable = hand.IsTracked && hand.HandConfidence == OVRHand.TrackingConfidence.High;
if (!isReliable || !hand.IsPointerPoseValid)
{
return false;
}
var pointer = hand.PointerPose;
orientation = pointer.rotation;
position = pointer.position;
return true;
}
// Given a controller and tracking space, return the ray that controller uses.
// Will fall back to center eye or camera on Gear if no controller is present.
public static Ray GetSelectionRay(OVRInput.Controller activeController, Transform trackingSpace) {
if (trackingSpace != null && activeController != OVRInput.Controller.None) {
Quaternion orientation = OVRInput.GetLocalControllerRotation(activeController);
Vector3 localStartPoint = OVRInput.GetLocalControllerPosition(activeController);
public static bool GetSelectionRay(OVRInput.Controller activeController, Transform trackingSpace, out Ray ray)
{
ray = default(Ray);
if (trackingSpace != null && activeController != OVRInput.Controller.None)
{
Quaternion orientation = default(Quaternion);
Vector3 localStartPoint = default(Vector3);
if ((activeController & OVRInput.Controller.Hands) != OVRInput.Controller.None)
{
if (!GetHandOrientation(activeController, out orientation, out localStartPoint))
{
return false;
}
}
else
{
orientation = OVRInput.GetLocalControllerRotation(activeController);
localStartPoint = OVRInput.GetLocalControllerPosition(activeController);
}
Matrix4x4 localToWorld = trackingSpace.localToWorldMatrix;
Vector3 worldStartPoint = localToWorld.MultiplyPoint(localStartPoint);
Vector3 worldOrientation = localToWorld.MultiplyVector(orientation * Vector3.forward);
return new Ray(worldStartPoint, worldOrientation);
ray = new Ray(worldStartPoint, worldOrientation);
return true;
}
Transform cameraTransform = Camera.main.transform;
if (OVRManager.instance != null) {
OVRCameraRig cameraRig = OVRManager.instance.GetComponent<OVRCameraRig>();
if (cameraRig != null) {
cameraTransform = cameraRig.centerEyeAnchor;
}
}
return new Ray(cameraTransform.position, cameraTransform.forward);
return false;
}
// Search the scene to find a tracking spce. This method can be expensive! Try to avoid it if possible.
// Search the scene to find a tracking space. This method can be expensive! Try to avoid it if possible.
public static Transform FindTrackingSpace() {
// There should be an OVRManager in the scene
if (OVRManager.instance != null) {
@@ -82,39 +182,53 @@ namespace ControllerSelection {
return null;
}
// Find the current active controller, based on last time a certain button was hit. Needs to know the previous active controller.
public static OVRInput.Controller GetControllerForButton(OVRInput.Button joyPadClickButton, OVRInput.Controller oldController) {
/// <summary>
/// Returns connected controllers
/// </summary>
/// <param name="filter"></param>
/// <returns></returns>
public static OVRInput.Controller GetConnectedControllers(HandFilter filter = HandFilter.Both)
{
OVRInput.Controller controller = OVRInput.GetConnectedControllers();
if ((controller & OVRInput.Controller.RTouch) == OVRInput.Controller.RTouch) {
if (OVRInput.Get(joyPadClickButton, OVRInput.Controller.RTouch) || oldController == OVRInput.Controller.None) {
return OVRInput.Controller.RTouch;
if (((filter & HandFilter.Right) == HandFilter.Right) && (controller & OVRInput.Controller.RTouch) == OVRInput.Controller.RTouch)
{
return OVRInput.Controller.RTouch;
}
if (((filter & HandFilter.Left) == HandFilter.Left) && (controller & OVRInput.Controller.LTouch) == OVRInput.Controller.LTouch)
{
return OVRInput.Controller.LTouch;
}
if (((filter & HandFilter.Right) == HandFilter.Right) && (controller & OVRInput.Controller.RTrackedRemote) == OVRInput.Controller.RTrackedRemote)
{
return OVRInput.Controller.RTrackedRemote;
}
if (((filter & HandFilter.Left) == HandFilter.Left) && (controller & OVRInput.Controller.LTrackedRemote) == OVRInput.Controller.LTrackedRemote)
{
return OVRInput.Controller.LTrackedRemote;
}
controller = OVRInput.Controller.None;
if (OVRPlugin.GetHandTrackingEnabled())
{
if ((filter & HandFilter.Both) == HandFilter.Both)
{
return OVRInput.Controller.Hands;
}
else if ((filter & HandFilter.Right) == HandFilter.Right)
{
return OVRInput.Controller.RHand;
}
else
{
return OVRInput.Controller.LHand;
}
}
if ((controller & OVRInput.Controller.LTouch) == OVRInput.Controller.LTouch) {
if (OVRInput.Get(joyPadClickButton, OVRInput.Controller.LTouch) || oldController == OVRInput.Controller.None) {
return OVRInput.Controller.LTouch;
}
}
if ((controller & OVRInput.Controller.RTrackedRemote) == OVRInput.Controller.RTrackedRemote) {
if (OVRInput.Get(joyPadClickButton, OVRInput.Controller.RTrackedRemote) || oldController == OVRInput.Controller.None) {
return OVRInput.Controller.RTrackedRemote;
}
}
if ((controller & OVRInput.Controller.LTrackedRemote) == OVRInput.Controller.LTrackedRemote) {
if (OVRInput.Get(joyPadClickButton, OVRInput.Controller.LTrackedRemote) || oldController == OVRInput.Controller.None) {
return OVRInput.Controller.LTrackedRemote;
}
}
if ((controller & oldController) != oldController) {
return OVRInput.Controller.None;
}
return oldController;
return OVRInput.Controller.None;
}
}
}
}
@@ -28,7 +28,14 @@ namespace ControllerSelection
{
public class OVRInputModule : UnityEngine.EventSystems.PointerInputModule
{
protected override void Awake() {
public enum Hand
{
None,
Left = OVRInputHelpers.HandFilter.Left,
Right = OVRInputHelpers.HandFilter.Right
}
protected override void Awake() {
base.Awake();
if (trackingSpace == null) {
Debug.LogWarning ("OVRInputModule did not have a tracking space set. Looking for one");
@@ -65,6 +72,9 @@ namespace ControllerSelection
[Tooltip("Primary selection button")]
public OVRInput.Button joyPadClickButton = OVRInput.Button.PrimaryIndexTrigger;
[Tooltip("Primary hand pinch finger")]
public OVRHand.HandFinger pinchFinger = OVRHand.HandFinger.Index;
[Header("Physics")]
[Tooltip("Perform an sphere cast to determine correct depth for gaze pointer")]
public bool performSphereCastForGazepointer;
@@ -87,8 +97,7 @@ namespace ControllerSelection
[Tooltip("Distance scrolled when swipe scroll occurs")]
public float swipeScrollScale = 4f;
[HideInInspector]
[HideInInspector]
public OVRInput.Controller activeController = OVRInput.Controller.None;
public delegate void RayHitDelegate(Vector3 hitPosition, Vector3 hitNormal);
@@ -101,13 +110,15 @@ namespace ControllerSelection
#endregion
// The raycaster that gets to do pointer interaction (e.g. with a mouse), gaze interaction always works
// private OVRRaycaster _activeGraphicRaycaster;
[NonSerialized]
public OVRRaycaster activeGraphicRaycaster;
[Header("Dragging")]
[Tooltip("Minimum pointer movement in degrees to start dragging")]
public float angleDragThreshold = 1;
[Tooltip("Suppress click while dragging")]
public bool suppressClickOnDrag = true;
// 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.
@@ -120,8 +131,8 @@ namespace ControllerSelection
// ProcessMouseEvent
// UseMouse
#region StandaloneInputModule code
private float m_NextAction;
private float m_NextAction;
private Vector2 m_LastMousePosition;
private Vector2 m_MousePosition;
@@ -218,8 +229,6 @@ namespace ControllerSelection
public override void UpdateModule()
{
activeController = OVRInputHelpers.GetControllerForButton (OVRInput.Button.PrimaryIndexTrigger, activeController);
m_LastMousePosition = m_MousePosition;
m_MousePosition = Input.mousePosition;
}
@@ -243,6 +252,7 @@ namespace ControllerSelection
shouldActivate |= !Mathf.Approximately(Input.GetAxisRaw(m_VerticalAxis), 0.0f);
shouldActivate |= (m_MousePosition - m_LastMousePosition).sqrMagnitude > 0.0f;
shouldActivate |= Input.GetMouseButtonDown(0);
return shouldActivate;
}
@@ -265,8 +275,6 @@ namespace ControllerSelection
ClearSelection();
}
/// <summary>
/// Process submit keys.
/// </summary>
@@ -337,10 +345,6 @@ namespace ControllerSelection
return axisEventData.used;
}
private bool SendUpdateEventToSelectedObject()
{
if (eventSystem.currentSelectedGameObject == null)
@@ -380,8 +384,6 @@ namespace ControllerSelection
if (newPressed == null)
newPressed = UnityEngine.EventSystems.ExecuteEvents.GetEventHandler<UnityEngine.EventSystems.IPointerClickHandler>(currentOverGo);
// Debug.Log("Pressed: " + newPressed);
float time = Time.unscaledTime;
if (newPressed == pointerEvent.lastPress)
@@ -408,22 +410,25 @@ namespace ControllerSelection
pointerEvent.pointerDrag = UnityEngine.EventSystems.ExecuteEvents.GetEventHandler<UnityEngine.EventSystems.IDragHandler>(currentOverGo);
if (pointerEvent.pointerDrag != null)
{
UnityEngine.EventSystems.ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, UnityEngine.EventSystems.ExecuteEvents.initializePotentialDrag);
}
}
// PointerUp notification
if (data.ReleasedThisFrame())
{
// Debug.Log("Executing pressup on: " + pointer.pointerPress);
UnityEngine.EventSystems.ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, UnityEngine.EventSystems.ExecuteEvents.pointerUpHandler);
// Debug.Log("KeyCode: " + pointer.eventData.keyCode);
// see if we mouse up on the same element that we clicked on...
var pointerUpHandler = UnityEngine.EventSystems.ExecuteEvents.GetEventHandler<UnityEngine.EventSystems.IPointerClickHandler>(currentOverGo);
// Determine if we're dragging and if we want to suppress click while dragging
var isDragging = pointerEvent.pointerDrag != null && pointerEvent.dragging;
var suppressClick = suppressClickOnDrag && isDragging;
// PointerClick and Drop events
if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick)
if (!suppressClick && pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick)
{
UnityEngine.EventSystems.ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, UnityEngine.EventSystems.ExecuteEvents.pointerClickHandler);
}
@@ -436,8 +441,10 @@ namespace ControllerSelection
pointerEvent.pointerPress = null;
pointerEvent.rawPointerPress = null;
if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
if (isDragging)
{
UnityEngine.EventSystems.ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, UnityEngine.EventSystems.ExecuteEvents.endDragHandler);
}
pointerEvent.dragging = false;
pointerEvent.pointerDrag = null;
@@ -506,10 +513,8 @@ namespace ControllerSelection
SendSubmitEventToSelectedObject();
}
ProcessMouseEvent(GetGazePointerData());
#if !UNITY_ANDROID
ProcessMouseEvent(GetCanvasPointerData());
#endif
ProcessMouseEvent(GetGazePointerData(true));
ProcessMouseEvent(GetGazePointerData(false));
}
/// <summary>
/// Decide if mouse events need to be processed this frame. Same as StandloneInputModule except
@@ -560,19 +565,20 @@ namespace ControllerSelection
#region PointerEventData pool
// Pool for OVRRayPointerEventData for ray based pointers
protected Dictionary<int, OVRRayPointerEventData> m_VRRayPointerData = new Dictionary<int, OVRRayPointerEventData>();
protected Dictionary<int, OVRRayPointerEventData> m_VRRayPointerDataLeft = new Dictionary<int, OVRRayPointerEventData>();
protected Dictionary<int, OVRRayPointerEventData> m_VRRayPointerDataRight = new Dictionary<int, OVRRayPointerEventData>();
protected bool GetPointerData(int id, out OVRRayPointerEventData data, bool create)
protected bool GetPointerData(int id, out OVRRayPointerEventData data, bool create, bool isLeft)
{
if (!m_VRRayPointerData.TryGetValue(id, out data) && create)
var rayPointerData = isLeft ? m_VRRayPointerDataLeft : m_VRRayPointerDataRight;
if (!rayPointerData.TryGetValue(id, out data) && create)
{
data = new OVRRayPointerEventData(eventSystem)
{
pointerId = id,
};
m_VRRayPointerData.Add(id, data);
rayPointerData.Add(id, data);
return true;
}
return false;
@@ -590,7 +596,12 @@ namespace ControllerSelection
// clear all selection
HandlePointerExitAndEnter(pointer, null);
}
foreach (var pointer in m_VRRayPointerData.Values)
foreach (var pointer in m_VRRayPointerDataLeft.Values)
{
// clear all selection
HandlePointerExitAndEnter(pointer, null);
}
foreach (var pointer in m_VRRayPointerDataRight.Values)
{
// clear all selection
HandlePointerExitAndEnter(pointer, null);
@@ -614,26 +625,28 @@ namespace ControllerSelection
return Vector3.Cross(LeftEdge, BottomEdge).normalized;
}
private readonly MouseState m_MouseState = new MouseState();
// Overridden so that we can process the two types of pointer separately
private readonly MouseState m_MouseStateLeft = new MouseState();
private readonly MouseState m_MouseStateRight = new MouseState();
// Overridden so that we can process the two types of pointer separately
// 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>
protected MouseState GetGazePointerData()
protected MouseState GetGazePointerData(bool isLeft)
{
// Get the OVRRayPointerEventData reference
OVRRayPointerEventData leftData;
GetPointerData(kMouseLeftId, out leftData, true );
GetPointerData(kMouseLeftId, out leftData, true, isLeft);
leftData.Reset();
leftData.worldSpaceRay = OVRInputHelpers.GetSelectionRay(activeController, trackingSpace);
leftData.worldSpaceRay = default(Ray);
var activeController = OVRInputHelpers.GetConnectedControllers(isLeft ? OVRInputHelpers.HandFilter.Left : OVRInputHelpers.HandFilter.Right);
bool gotRay = OVRInputHelpers.GetSelectionRay(activeController, trackingSpace, out leftData.worldSpaceRay);
leftData.scrollDelta = GetExtraScrollDelta();
//Populate some default values
@@ -654,7 +667,6 @@ namespace ControllerSelection
// 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)
@@ -682,75 +694,21 @@ namespace ControllerSelection
// copy the apropriate data into right and middle slots
OVRRayPointerEventData rightData;
GetPointerData(kMouseRightId, out rightData, true );
GetPointerData(kMouseRightId, out rightData, true, isLeft);
CopyFromTo(leftData, rightData);
rightData.button = UnityEngine.EventSystems.PointerEventData.InputButton.Right;
OVRRayPointerEventData middleData;
GetPointerData(kMouseMiddleId, out middleData, true );
GetPointerData(kMouseMiddleId, out middleData, true, isLeft);
CopyFromTo(leftData, middleData);
middleData.button = UnityEngine.EventSystems.PointerEventData.InputButton.Middle;
var mouseState = isLeft ? m_MouseStateLeft : m_MouseStateRight;
m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Left, GetGazeButtonState(), leftData);
m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Right, UnityEngine.EventSystems.PointerEventData.FramePressState.NotChanged, rightData);
m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Middle, UnityEngine.EventSystems.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
UnityEngine.EventSystems.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 = UnityEngine.EventSystems.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
UnityEngine.EventSystems.PointerEventData rightData;
GetPointerData(kMouseRightId, out rightData, true );
CopyFromTo(leftData, rightData);
rightData.button = UnityEngine.EventSystems.PointerEventData.InputButton.Right;
UnityEngine.EventSystems.PointerEventData middleData;
GetPointerData(kMouseMiddleId, out middleData, true );
CopyFromTo(leftData, middleData);
middleData.button = UnityEngine.EventSystems.PointerEventData.InputButton.Middle;
m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Left, StateForMouseButton(0), leftData);
m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Right, StateForMouseButton(1), rightData);
m_MouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Middle, StateForMouseButton(2), middleData);
return m_MouseState;
mouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Left, GetGazeButtonState(activeController), leftData);
mouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Right, UnityEngine.EventSystems.PointerEventData.FramePressState.NotChanged, rightData);
mouseState.SetButtonState(UnityEngine.EventSystems.PointerEventData.InputButton.Middle, UnityEngine.EventSystems.PointerEventData.FramePressState.NotChanged, middleData);
return mouseState;
}
/// <summary>
@@ -840,16 +798,27 @@ namespace ControllerSelection
/// Get state of button corresponding to gaze pointer
/// </summary>
/// <returns></returns>
protected UnityEngine.EventSystems.PointerEventData.FramePressState GetGazeButtonState()
protected UnityEngine.EventSystems.PointerEventData.FramePressState GetGazeButtonState(OVRInput.Controller activeController)
{
var pressed = false;
var released = false;
if (activeController != OVRInput.Controller.None) {
pressed = OVRInput.GetDown(joyPadClickButton, activeController);
released = OVRInput.GetUp(joyPadClickButton, activeController);
if ((activeController & OVRInput.Controller.Touch) != OVRInput.Controller.None)
{
// Handle touch controllers
pressed = OVRInput.GetDown(joyPadClickButton, activeController);
released = OVRInput.GetUp(joyPadClickButton, activeController);
}
else if ((activeController & OVRInput.Controller.Hands) != OVRInput.Controller.None &&
OVRPlugin.GetHandTrackingEnabled() && null != HandsManager.Instance && HandsManager.Instance.IsInitialized())
{
pressed = OVRInputHelpers.IsFingerStartPinching(activeController, pinchFinger);
released = OVRInputHelpers.IsFingerStopPinching(activeController, pinchFinger);
}
}
else {
else
{
pressed = OVRInput.GetDown(joyPadClickButton);
released = OVRInput.GetUp(joyPadClickButton);
}
@@ -860,16 +829,13 @@ namespace ControllerSelection
#endif
if (pressed && released) {
//Debug.Log ("pressed & released");
return UnityEngine.EventSystems.PointerEventData.FramePressState.PressedAndReleased;
}
if (pressed) {
//Debug.Log ("pressed");
return UnityEngine.EventSystems.PointerEventData.FramePressState.Pressed;
return UnityEngine.EventSystems.PointerEventData.FramePressState.Pressed;
}
if (released) {
//Debug.Log ("released");
return UnityEngine.EventSystems.PointerEventData.FramePressState.Released;
return UnityEngine.EventSystems.PointerEventData.FramePressState.Released;
}
return UnityEngine.EventSystems.PointerEventData.FramePressState.NotChanged;
@@ -23,7 +23,15 @@ using UnityEngine.SceneManagement;
namespace ControllerSelection {
public class OVRPointerVisualizer : MonoBehaviour {
public class OVRPointerVisualizer : MonoBehaviour
{
public enum Hand
{
None,
Left = OVRInputHelpers.HandFilter.Left,
Right = OVRInputHelpers.HandFilter.Right
}
[Header("(Optional) Tracking space")]
[Tooltip("Tracking space of the OVRCameraRig.\nIf tracking space is not set, the scene will be searched.\nThis search is expensive.")]
public Transform trackingSpace = null;
@@ -38,36 +46,42 @@ namespace ControllerSelection {
public float gazeDrawDistance = 3;
[Tooltip("Show gaze pointer as ray pointer.")]
public bool showRayPointer = true;
[Tooltip("Left or Right hand / controller")]
public Hand handType = Hand.None;
// Start ray draw distance
private const float StartRayDrawDistance = 0.032f;
[HideInInspector]
public OVRInput.Controller activeController = OVRInput.Controller.None;
void Awake() {
if (trackingSpace == null) {
void Awake()
{
if (trackingSpace == null)
{
Debug.LogWarning("OVRPointerVisualizer did not have a tracking space set. Looking for one");
trackingSpace = OVRInputHelpers.FindTrackingSpace();
}
}
void OnEnable() {
void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnDisable() {
void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
if (trackingSpace == null) {
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (trackingSpace == null)
{
Debug.LogWarning("OVRPointerVisualizer did not have a tracking space set. Looking for one");
trackingSpace = OVRInputHelpers.FindTrackingSpace();
}
}
public void SetPointer(Ray ray) {
void SetPointer(Ray ray)
{
float hitRayDrawDistance = rayDrawDistance;
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
@@ -75,40 +89,54 @@ namespace ControllerSelection {
hitRayDrawDistance = hit.distance;
}
if (linePointer != null) {
if (linePointer != null)
{
linePointer.SetPosition(0, ray.origin + ray.direction * StartRayDrawDistance);
linePointer.SetPosition(1, ray.origin + ray.direction * hitRayDrawDistance);
}
if (gazePointer != null) {
if (gazePointer != null)
{
gazePointer.position = ray.origin + ray.direction * (showRayPointer ? hitRayDrawDistance : gazeDrawDistance);
}
}
public void SetPointerVisibility() {
if (trackingSpace != null && activeController != OVRInput.Controller.None) {
if (linePointer != null) {
void SetPointerVisibility(bool show)
{
if (show) {
if (linePointer != null)
{
linePointer.enabled = true;
}
if (gazePointer != null) {
if (gazePointer != null)
{
gazePointer.gameObject.SetActive(showRayPointer ? true : false);
}
}
else {
if (linePointer != null) {
if (linePointer != null)
{
linePointer.enabled = false;
}
if (gazePointer != null) {
if (gazePointer != null)
{
gazePointer.gameObject.SetActive(showRayPointer ? false : true);
}
}
}
void Update() {
activeController = OVRInputHelpers.GetControllerForButton(OVRInput.Button.PrimaryIndexTrigger, activeController);
Ray selectionRay = OVRInputHelpers.GetSelectionRay(activeController, trackingSpace);
SetPointerVisibility();
SetPointer(selectionRay);
void Update()
{
var activeController = OVRInputHelpers.GetConnectedControllers((OVRInputHelpers.HandFilter)handType);
Ray selectionRay;
bool gotRay = OVRInputHelpers.GetSelectionRay(activeController, trackingSpace, out selectionRay);
SetPointerVisibility(gotRay && trackingSpace != null && activeController != OVRInput.Controller.None);
if (gotRay)
{
SetPointer(selectionRay);
}
}
}
}
@@ -19,12 +19,25 @@ limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using System.Security.Permissions;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
namespace ControllerSelection {
public class OVRRawRaycaster : MonoBehaviour {
namespace ControllerSelection
{
public class OVRRawRaycaster : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
public enum Hand
{
None,
Left = OVRInputHelpers.HandFilter.Left,
Right = OVRInputHelpers.HandFilter.Right
}
[System.Serializable]
public class HoverCallback : UnityEvent<Transform> { }
[System.Serializable]
@@ -34,7 +47,6 @@ namespace ControllerSelection {
[Tooltip("Tracking space of the OVRCameraRig.\nIf tracking space is not set, the scene will be searched.\nThis search is expensive.")]
public Transform trackingSpace = null;
[Header("Selection")]
[Tooltip("Primary selection button")]
public OVRInput.Button primaryButton = OVRInput.Button.PrimaryIndexTrigger;
@@ -42,6 +54,12 @@ namespace ControllerSelection {
public OVRInput.Button secondaryButton = OVRInput.Button.PrimaryTouchpad;
[Tooltip("Tertiary selection button")]
public OVRInput.Button tertiaryButton = OVRInput.Button.One;
[Tooltip("Primary pinch finger")]
public OVRHand.HandFinger primaryPinchFinger = OVRHand.HandFinger.Index;
[Tooltip("Secondary pinch finger")]
public OVRHand.HandFinger secondaryPinchFinger = OVRHand.HandFinger.Middle;
[Tooltip("Tertiary pinch finger")]
public OVRHand.HandFinger tertiaryPinchFinger = OVRHand.HandFinger.Ring;
[Tooltip("Layers to exclude from raycast")]
public LayerMask excludeLayers;
[Tooltip("Maximum raycast distance")]
@@ -58,137 +76,338 @@ namespace ControllerSelection {
public OVRRawRaycaster.SelectionCallback onTertiarySelect;
//protected Ray pointer;
protected Transform lastHit = null;
protected Transform triggerDown = null;
protected Transform padDown = null;
protected Transform tertiaryDown = null;
protected Transform lastHitLeft = null;
protected Transform lastHitRight = null;
protected Transform lastButtonHitLeft = null;
protected Transform lastButtonHitRight = null;
protected Transform triggerButtonDownLeft = null;
protected Transform triggerButtonDownRight = null;
protected Transform padButtonDownLeft = null;
protected Transform padButtonDownRight = null;
protected Transform tertiaryButtonDownLeft = null;
protected Transform tertiaryButtonDownRight = null;
protected Transform triggerFingerDownLeft = null;
protected Transform triggerFingerDownRight = null;
protected Transform padFingerDownLeft = null;
protected Transform padFingerDownRight = null;
protected Transform tertiaryFingerDownLeft = null;
protected Transform tertiaryFingerDownRight = null;
[HideInInspector]
public OVRInput.Controller activeController = OVRInput.Controller.None;
// Hover actions
[Flags]
enum HoverAction
{
None = 0,
Exit = 1,
Enter = 2,
Stay = 4,
Hover = 8
}
void Awake() {
if (trackingSpace == null) {
// Map of transform to hover actions. We allocate this once & reuse to avoid memory allocations in Update().
Dictionary<Transform, HoverAction> _actions = new Dictionary<Transform, HoverAction>(4);
// Whether we're dragging
bool _isDragging = false;
void Awake()
{
if (trackingSpace == null)
{
Debug.LogWarning("OVRRawRaycaster did not have a tracking space set. Looking for one");
trackingSpace = OVRInputHelpers.FindTrackingSpace();
}
}
void OnEnable() {
void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnDisable() {
void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
if (trackingSpace == null) {
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (trackingSpace == null)
{
Debug.LogWarning("OVRRawRaycaster did not have a tracking space set. Looking for one");
trackingSpace = OVRInputHelpers.FindTrackingSpace();
}
}
void Update() {
activeController = OVRInputHelpers.GetControllerForButton(OVRInput.Button.PrimaryIndexTrigger, activeController);
Ray pointer = OVRInputHelpers.GetSelectionRay(activeController, trackingSpace);
public void OnBeginDrag(PointerEventData eventData)
{
_isDragging = true;
}
RaycastHit hit; // Was anything hit?
if (Physics.Raycast(pointer, out hit, raycastDistance, ~excludeLayers)) {
if (lastHit != null && lastHit != hit.transform) {
if (onHoverExit != null) {
onHoverExit.Invoke(lastHit);
}
lastHit = null;
}
public void OnEndDrag(PointerEventData eventData)
{
_isDragging = false;
}
if (lastHit == null) {
if (onHoverEnter != null) {
onHoverEnter.Invoke(hit.transform);
}
}
if (onHover != null) {
onHover.Invoke(hit.transform);
}
lastHit = hit.transform;
// Handle selection callbacks. An object is selected if the button selecting it was
// pressed AND released while hovering over the object.
if (activeController != OVRInput.Controller.None) {
if (OVRInput.GetDown(tertiaryButton, activeController))
static void ProcessHit(Dictionary<Transform, HoverAction> actions, bool isHit, Transform hit, ref Transform lastHit)
{
if (isHit)
{
if (lastHit != null)
{
if (lastHit != hit)
{
tertiaryDown = lastHit;
// We move out of the last hit, so action is Exit
actions[lastHit] |= HoverAction.Exit;
lastHit = null;
}
else if (OVRInput.GetUp(tertiaryButton, activeController))
else
{
if (tertiaryDown != null && tertiaryDown == lastHit)
{
if (onTertiarySelect != null)
{
onTertiarySelect.Invoke(tertiaryDown);
}
}
}
if (!OVRInput.Get(tertiaryButton, activeController))
{
tertiaryDown = null;
// We have not moved from last hit, so action is Stay
actions[lastHit] |= HoverAction.Stay;
}
}
if (OVRInput.GetDown(secondaryButton, activeController)) {
padDown = lastHit;
}
else if (OVRInput.GetUp(secondaryButton, activeController)) {
if (padDown != null && padDown == lastHit) {
if (onSecondarySelect != null) {
onSecondarySelect.Invoke(padDown);
}
}
}
if (!OVRInput.Get(secondaryButton, activeController)) {
padDown = null;
}
if (lastHit == null)
{
// We moved into a new hit, so action is Enter
actions[hit] |= HoverAction.Enter;
}
if (OVRInput.GetDown(primaryButton, activeController)) {
triggerDown = lastHit;
}
else if (OVRInput.GetUp(primaryButton, activeController)) {
if (triggerDown != null && triggerDown == lastHit) {
if (onPrimarySelect != null) {
onPrimarySelect.Invoke(triggerDown);
}
}
}
if (!OVRInput.Get(primaryButton, activeController)) {
triggerDown = null;
}
}
#if UNITY_ANDROID && !UNITY_EDITOR
// Gaze pointer fallback
else {
if (Input.GetMouseButtonDown(0) ) {
triggerDown = lastHit;
}
else if (Input.GetMouseButtonUp(0) ) {
if (triggerDown != null && triggerDown == lastHit) {
if (onPrimarySelect != null) {
onPrimarySelect.Invoke(triggerDown);
}
}
}
if (!Input.GetMouseButton(0)) {
triggerDown = null;
}
// We hit something, so action is hover
actions[hit] |= HoverAction.Hover;
lastHit = hit;
}
#endif
}
// Nothing was hit, handle exit callback
else if (lastHit != null) {
if (onHoverExit != null) {
onHoverExit.Invoke(lastHit);
}
else if (lastHit != null)
{
// Nothing was hit, handle exit callback
actions[lastHit] |= HoverAction.Exit;
lastHit = null;
}
}
void ProcessButtonPresses(OVRInput.Controller activeController, bool isLeft, Transform lastHit,
ref Transform triggerDown, ref Transform padDown, ref Transform tertiaryDown)
{
// Handle selection callbacks. An object is selected if the button selecting it was
// pressed AND released while hovering over the object.
if (isLeft && (activeController & OVRInput.Controller.LTouch) != OVRInput.Controller.LTouch ||
!isLeft && (activeController & OVRInput.Controller.RTouch) != OVRInput.Controller.RTouch)
{
return;
}
if (OVRInput.GetDown(tertiaryButton, activeController))
{
tertiaryDown = lastHit;
}
else if (OVRInput.GetUp(tertiaryButton, activeController))
{
if (tertiaryDown != null && tertiaryDown == lastHit)
{
if (onTertiarySelect != null)
{
onTertiarySelect.Invoke(tertiaryDown);
}
}
}
if (!OVRInput.Get(tertiaryButton, activeController))
{
tertiaryDown = null;
}
if (OVRInput.GetDown(secondaryButton, activeController))
{
padDown = lastHit;
}
else if (OVRInput.GetUp(secondaryButton, activeController))
{
if (padDown != null && padDown == lastHit)
{
if (onSecondarySelect != null)
{
onSecondarySelect.Invoke(padDown);
}
}
}
if (!OVRInput.Get(secondaryButton, activeController))
{
padDown = null;
}
if (OVRInput.GetDown(primaryButton, activeController))
{
triggerDown = lastHit;
}
else if (OVRInput.GetUp(primaryButton, activeController))
{
if (triggerDown != null && triggerDown == lastHit)
{
if (onPrimarySelect != null)
{
onPrimarySelect.Invoke(triggerDown);
}
}
}
if (!OVRInput.Get(primaryButton, activeController))
{
triggerDown = null;
}
}
void ProcessHandPinch(OVRInput.Controller activeController, Transform lastHit,
ref Transform triggerDown, ref Transform padDown, ref Transform tertiaryDown)
{
// Handle selection callbacks. An object is selected if the button selecting it was
// pressed AND released while hovering over the object.
if ((activeController & OVRInput.Controller.Hands) == OVRInput.Controller.None)
{
return;
}
if (!OVRPlugin.GetHandTrackingEnabled() || !HandsManager.Instance || !HandsManager.Instance.IsInitialized())
{
return;
}
if (OVRInputHelpers.IsFingerStartPinching(activeController, tertiaryPinchFinger))
{
tertiaryDown = lastHit;
}
else if (OVRInputHelpers.IsFingerStopPinching(activeController, tertiaryPinchFinger))
{
if (tertiaryDown != null && tertiaryDown == lastHit)
{
if (onTertiarySelect != null)
{
onTertiarySelect.Invoke(tertiaryDown);
}
}
}
else if (!OVRInputHelpers.IsFingerPinching(activeController, tertiaryPinchFinger))
{
tertiaryDown = null;
}
if (OVRInputHelpers.IsFingerStartPinching(activeController, secondaryPinchFinger))
{
padDown = lastHit;
}
else if (OVRInputHelpers.IsFingerStopPinching(activeController, secondaryPinchFinger))
{
if (padDown != null && padDown == lastHit)
{
if (onSecondarySelect != null)
{
onSecondarySelect.Invoke(padDown);
}
}
}
else if (!OVRInputHelpers.IsFingerPinching(activeController, secondaryPinchFinger))
{
padDown = null;
}
if (OVRInputHelpers.IsFingerStartPinching(activeController, primaryPinchFinger))
{
triggerDown = lastHit;
}
else if (OVRInputHelpers.IsFingerStopPinching(activeController, primaryPinchFinger))
{
if (triggerDown != null && triggerDown == lastHit)
{
if (onPrimarySelect != null)
{
onPrimarySelect.Invoke(triggerDown);
}
}
}
else if (!OVRInputHelpers.IsFingerPinching(activeController, primaryPinchFinger))
{
triggerDown = null;
}
}
void Update()
{
Ray pointerLeft;
Ray pointerRight;
// Get left & right rays
var activeControllerLeft = OVRInputHelpers.GetConnectedControllers(OVRInputHelpers.HandFilter.Left);
var activeControllerRight = OVRInputHelpers.GetConnectedControllers(OVRInputHelpers.HandFilter.Right);
bool gotRayLeft = OVRInputHelpers.GetSelectionRay(activeControllerLeft, trackingSpace, out pointerLeft);
bool gotRayRight = OVRInputHelpers.GetSelectionRay(activeControllerRight, trackingSpace, out pointerRight);
// Cast the rays
RaycastHit hitLeft = default(RaycastHit);
RaycastHit hitRight = default(RaycastHit);
bool isHitLeft = gotRayLeft && Physics.Raycast(pointerLeft, out hitLeft, raycastDistance, ~excludeLayers);
bool isHitRight = gotRayRight && Physics.Raycast(pointerRight, out hitRight, raycastDistance, ~excludeLayers);
// Process the hits
_actions.Clear();
if (lastHitLeft != null)
{
_actions[lastHitLeft] = HoverAction.None;
}
if (lastHitRight != null)
{
_actions[lastHitRight] = HoverAction.None;
}
if (isHitLeft)
{
_actions[hitLeft.transform] = HoverAction.None;
}
if (isHitRight)
{
_actions[hitRight.transform] = HoverAction.None;
}
ProcessHit(_actions, isHitLeft, hitLeft.transform, ref lastHitLeft);
ProcessHit(_actions, isHitRight, hitRight.transform, ref lastHitRight);
// Perform the actions
foreach (KeyValuePair<Transform, HoverAction> kvp in _actions)
{
// Only perform enter / exit actions if we are not staying on existing transform
// and if we're not both entering & exiting
if ((kvp.Value & HoverAction.Stay) == HoverAction.None &&
(kvp.Value & (HoverAction.Enter | HoverAction.Exit)) != (HoverAction.Enter | HoverAction.Exit))
{
if ((kvp.Value & HoverAction.Enter) != HoverAction.None && onHoverEnter != null)
{
onHoverEnter.Invoke(kvp.Key);
}
if ((kvp.Value & HoverAction.Exit) != HoverAction.None && onHoverExit != null)
{
onHoverExit.Invoke(kvp.Key);
}
}
if ((kvp.Value & HoverAction.Hover) != HoverAction.None && onHover != null)
{
// Hovering over transform
onHover.Invoke(kvp.Key);
}
}
if (isHitLeft && !_isDragging)
{
ProcessButtonPresses(activeControllerLeft, true, lastHitLeft, ref triggerButtonDownLeft, ref padButtonDownLeft, ref tertiaryButtonDownLeft);
ProcessHandPinch(activeControllerLeft, lastHitLeft, ref triggerFingerDownLeft, ref padFingerDownLeft, ref tertiaryFingerDownLeft);
}
if (isHitRight && !_isDragging)
{
ProcessButtonPresses(activeControllerRight, false, lastHitRight, ref triggerButtonDownRight, ref padButtonDownRight, ref tertiaryButtonDownRight);
ProcessHandPinch(activeControllerRight, lastHitRight, ref triggerFingerDownRight, ref padFingerDownRight, ref tertiaryFingerDownRight);
}
}
}
}