#define VRIF using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; #if UNITY_2018_4_OR_NEWER using UnityEngine.XR; #endif #if STEAM_VR_SDK using Valve.VR; #endif namespace BNG { #region Enums public enum ControllerHand { Left, Right, None } /// /// Controller Options available to bind buttons to via Inspector. You can use GetControllerBindingValue() to determine if that button has been pressed. /// public enum ControllerBinding { None, AButton, AButtonDown, BButton, BButtonDown, XButton, XButtonDown, YButton, YButtonDown, LeftTrigger, LeftTriggerDown, LeftGrip, LeftGripDown, LeftThumbstick, LeftThumbstickDown, RightTrigger, RightTriggerDown, RightGrip, RightGripDown, RightThumbstick, RightThumbstickDown, StartButton, StartButtonDown, BackButton, BackButtonDown } /// /// Controller Options available to bind buttons to via Inspector. Input is relative to the controller holding it. /// Ex : Button 1 = Button A if held in Right controller, Button X if held in Left. /// public enum GrabbedControllerBinding { None, Button1, // Button A, X Button1Down, Button2, // Button B, Y Button2Down, Trigger, TriggerDown, Grip, GripDown } public enum InputAxis { None, LeftThumbStickAxis, LeftTouchPadAxis, RightThumbStickAxis, RightTouchPadAxis } public enum ControllerType { None, Unknown, OculusTouch, Wand, Knuckles } public enum HandControl { LeftGrip, RightGrip, LeftTrigger, RightTrigger, None } public enum GrabButton { Grip, Trigger, Inherit } public enum HoldType { HoldDown, // Hold down the grab button Toggle, // Click the grab button down to switch between hold and release Inherit // Inherit from Grabber } public enum XRInputSource { XRInput, OVRInput, SteamVR, Pico, UnityInput } public enum SDKProvider { Unknown, OculusSDK, OpenVR } #endregion /// /// A proxy for handling input from various input providers such as OVRInput, XRInput, and SteamVR. /// public class InputBridge : MonoBehaviour { #region Singleton /// /// Instance of our Singleton /// public static InputBridge Instance { get { if (_instance == null) { _instance = FindObjectOfType(); if (_instance == null) { _instance = new GameObject("InputBridge").AddComponent(); } } return _instance; } } private static InputBridge _instance; #endregion #region Input Properties [Header("Input Settings")] [SerializeField] public XRInputSource InputSource = XRInputSource.XRInput; [SerializeField] [Tooltip("Specify an InputActionSet for when using the Unity Input system. These actions will be enabled on load.")] public UnityEngine.InputSystem.InputActionAsset actionSet; [Header("Tracking Origin")] [SerializeField] [Tooltip("Set the TrackingOriginModeFlags on device connect. Should typically be set to 'Floor'")] public TrackingOriginModeFlags TrackingOrigin = TrackingOriginModeFlags.Floor; [Header("Thumbstick Deadzone")] /// /// Thumbstick X must be greater than this amount to be considered valid /// [Tooltip("Thumbstick X must be greater than this amount to be considered valid")] public float ThumbstickDeadzoneX = 0.001f; /// /// Thumbstick Y must be greater than this amount to be considered valid /// [Tooltip("Thumbstick Y must be greater than this amount to be considered valid")] public float ThumbstickDeadzoneY = 0.001f; #endregion #region Raw Inputs [Header("Grip")] /// /// How far Left Grip is Held down. Values : 0 - 1 (Fully Open / Closed) /// public float LeftGrip = 0; /// /// Left Grip was pressed down this frame, but not last /// public bool LeftGripDown = false; /// /// How far Right Grip is Held down. Values : 0 - 1 (Fully Open / Closed) /// public float RightGrip = 0; /// /// Right Grip was pressed down this frame, but not last /// public bool RightGripDown = false; [Header("Trigger")] /// /// How far Left Trigger is Held down. Values : 0 - 1 (Fully Open / Closed) /// public float LeftTrigger = 0; public bool LeftTriggerNear = false; public bool LeftTriggerUp = false; /// /// Returns true if Left Trigger was held down this frame but not the last /// public bool LeftTriggerDown = false; /// /// How far Left Trigger is Held down. Values : 0 - 1 (Fully Open / Closed) /// public float RightTrigger = 0; /// /// Returns true if Right Trigger is all the way up this frame but not last /// public bool RightTriggerUp = false; /// /// Returns true if Right Trigger was held down this frame but not the last /// public bool RightTriggerDown = false; public bool RightTriggerNear = false; public bool LeftThumbNear = false; public bool RightThumbNear = false; [Header("Thumbstick")] /// /// Pressed down this frame, but not last /// public bool LeftThumbstickDown = false; /// /// Released this frame but not last /// public bool LeftThumbstickUp = false; /// /// Pressed down this frame, but not last /// public bool RightThumbstickDown = false; /// /// Released this frame but not last /// public bool RightThumbstickUp = false; /// /// Currently Held Down /// public bool LeftThumbstick = false; public bool RightThumbstick = false; [Header("Buttons")] /// /// Is the A button currently being held down /// public bool AButton = false; /// /// Returns true if the A Button was pressed down this frame but not last /// [Tooltip("Returns true if the A Button was pressed down this frame but not last")] public bool AButtonDown = false; // A Button Up this frame but down the last public bool AButtonUp = false; /// /// Is the B button currently being held down /// public bool BButton = false; /// /// Returns true if the B Button was pressed down this frame but not last /// [Tooltip("Returns true if the B Button was pressed down this frame but not last")] public bool BButtonDown = false; // B Button Up this frame but down the last public bool BButtonUp = false; public bool XButton = false; /// /// Returns true if the X Button was pressed down this frame but not last /// [Tooltip("Returns true if the X Button was pressed down this frame but not last")] public bool XButtonDown = false; // X Button Up this frame but down the last public bool XButtonUp = false; public bool YButton = false; /// /// Returns true if the Y Button was pressed down this frame but not last /// public bool YButtonDown = false; public bool YButtonUp = false; public bool StartButton = false; public bool StartButtonDown = false; public bool BackButton = false; public bool BackButtonDown = false; [Header("Axis")] public Vector2 LeftThumbstickAxis; public Vector2 RightThumbstickAxis; public Vector2 LeftTouchPadAxis; public Vector2 RightTouchPadAxis; [Header("Finger Tracking")] [Tooltip("SteamVR Only - Shows the curl value of the thumb. 0 = Fully extended, 1 = Fully Curled")] public float LeftThumbCurl = 0f; public float LeftIndexCurl = 0f; public float LeftMiddleCurl = 0f; public float LeftRingCurl = 0f; public float LeftPinkyCurl = 0f; [Tooltip("SteamVR Only - Shows the curl value of the thumb. 0 = Fully extended, 1 = Fully Curled")] public float RightThumbCurl = 0f; public float RightIndexCurl = 0f; public float RightMiddleCurl = 0f; public float RightRingCurl = 0f; public float RightPinkyCurl = 0f; #endregion #region Device Properties /// /// What threshold constitutes a "down" event. /// For example, pushing the trigger down 20% (0.2) of the way considered starting a trigger down event /// This is used in XRInput /// public float DownThreshold { get { return _downThreshold; } } private float _downThreshold = 0.2f; bool SteamVRSupport = false; [Header("HMD / Hardware")] public ControllerType ConnectedControllerType; [Tooltip("Is there an HMD present and in use.")] public bool HMDActive = false; public SDKProvider LoadedSDK { get; private set; } public bool IsOculusDevice { get; private set; } public bool IsOculusQuest { get; private set; } public bool IsHTCDevice { get; private set; } public bool IsPicoDevice { get; private set; } public bool IsValveIndexController { get; private set; } /// /// Returns true if the controller has both a Touchpad and a Joystick. Currently only the Valve Index has both. /// [Tooltip("Returns true if the controller has both a Touchpad and a Joystick. Currently on the Valve Index has both.")] public bool SupportsBothTouchPadAndJoystick; /// /// Returns true if the controllers support the 'indexTouch' (or 'near trigger') XR input mapping. Currently only Oculus devices on the Oculus SDK support index touch. OpenVR is not supported. /// [Tooltip("Returns true if the controllers support the 'indexTouch' XR input mapping. Currently only Oculus devices on the Oculus SDK support thumb touch. OpenVR is not supported.")] public bool SupportsIndexTouch; /// /// Returns true if the controllers support the 'ThumbTouch' (or near thumbstick) XR input mapping. Currently only Oculus devices on the Oculus SDK support index touch. OpenVR is not supported. /// [Tooltip("Returns true if the controllers support the 'ThumbTouch' (or near thumbstick) XR input mapping. Currently only Oculus devices on the Oculus SDK support thumb touch. OpenVR is not supported.")] public bool SupportsThumbTouch; #if UNITY_2019_3_OR_NEWER static List devices = new List(); #endif #endregion #region Events // Events /// /// Called after update loop. /// public delegate void InputsUpdatedAction(); public static event InputsUpdatedAction OnInputsUpdated; /// /// Called once a controller has been successfully detected /// public delegate void ControllerFoundAction(); public static event ControllerFoundAction OnControllerFound; #endregion #region Unity Input Actions UnityEngine.InputSystem.InputAction leftGrip; UnityEngine.InputSystem.InputAction leftTrigger; UnityEngine.InputSystem.InputAction leftTriggerNear; UnityEngine.InputSystem.InputAction rightGrip; UnityEngine.InputSystem.InputAction rightTrigger; UnityEngine.InputSystem.InputAction rightTriggerNear; UnityEngine.InputSystem.InputAction leftThumbstick; UnityEngine.InputSystem.InputAction leftThumbstickDown; UnityEngine.InputSystem.InputAction leftThumbNear; UnityEngine.InputSystem.InputAction leftTouchpad; UnityEngine.InputSystem.InputAction leftTouchpadDown; UnityEngine.InputSystem.InputAction rightThumbstick; UnityEngine.InputSystem.InputAction rightThumbstickDown; UnityEngine.InputSystem.InputAction rightThumbNear; UnityEngine.InputSystem.InputAction rightTouchpad; UnityEngine.InputSystem.InputAction rightTouchpadDown; UnityEngine.InputSystem.InputAction aButton; UnityEngine.InputSystem.InputAction bButton; UnityEngine.InputSystem.InputAction xButton; UnityEngine.InputSystem.InputAction yButton; UnityEngine.InputSystem.InputAction startButton; UnityEngine.InputSystem.InputAction backButton; #endregion #region Input Debugging // Used for showing a custom inspector [HideInInspector] public bool ShowInputDebugger = false; #endregion private void Awake() { // Destroy any duplicate instances that may have been created if (_instance != null && _instance != this) { Destroy(this); return; } _instance = this; // Update all device properties List devices = new List(); InputDevices.GetDevices(devices); setDeviceProperties(); } void Start() { SetTrackingOriginMode(TrackingOrigin); #if STEAM_VR_SDK SteamVRSupport = true; // Warn that input source has not been set, even though the SteamVR SDK is present. if(InputSource != XRInputSource.SteamVR) { Debug.Log("SteamVR SDK detected, but not set as source on InputBridge. Recommend switching input Source from " + InputSource.ToString() + " to SteamVR."); } // Set the default action set if not provided SteamVR_ActivateActionSetOnLoad VRIFLoader = FindObjectOfType(); if (VRIFLoader == null) { Debug.Log("SteamVR_ActivateActionSetOnLoad component not found - adding VRIF custom actions default."); VRIFLoader = gameObject.AddComponent(); VRIFLoader.actionSet = SteamVR_Actions.VRIF; } SteamVR.Initialize(); #endif } void OnEnable() { #if UNITY_2019_3_OR_NEWER InputDevices.deviceConfigChanged += onDeviceChanged; InputDevices.deviceConnected += onDeviceChanged; InputDevices.deviceDisconnected += onDeviceChanged; #endif CreateUnityInputActions(); EnableActions(); } void OnDisable() { #if UNITY_2019_3_OR_NEWER InputDevices.deviceConfigChanged -= onDeviceChanged; InputDevices.deviceConnected -= onDeviceChanged; InputDevices.deviceDisconnected -= onDeviceChanged; #endif DisableActions(); } void Update() { UpdateDeviceActive(); UpdateInputs(); } public virtual void UpdateInputs() { // SteamVR uses an action system. Only update if HMD is reported as Active if (InputSource == XRInputSource.SteamVR && SteamVRSupport && HMDActive) { UpdateSteamInput(); } // Use OVRInput to get more Oculus Specific inputs, such as "Near Touch" else if (InputSource == XRInputSource.OVRInput) { UpdateOVRInput(); } // Use XRInput else if(InputSource == XRInputSource.XRInput) { UpdateXRInput(); } // New Unity Input System else if(InputSource == XRInputSource.UnityInput) { UpdateUnityInput(); } // Pico else if(InputSource == XRInputSource.Pico) { UpdatePicoInput(); } // Call events OnInputsUpdated?.Invoke(); } #region SteamVR Action Input public virtual void UpdateSteamInput() { #if STEAM_VR_SDK LeftThumbstickAxis = ApplyDeadZones(SteamVR_Actions.vRIF_LeftThumbstickAxis.axis, ThumbstickDeadzoneX, ThumbstickDeadzoneY); RightThumbstickAxis = ApplyDeadZones(SteamVR_Actions.vRIF_RightThumbstickAxis.axis, ThumbstickDeadzoneX, ThumbstickDeadzoneY); var prevBool = LeftThumbstick; LeftThumbstick = SteamVR_Actions.vRIF_LeftThumbstickDown.state; // LeftThumbstickDown = SteamVR_Actions.vRIF_LeftThumbstickDown.stateDown; LeftThumbstickDown = prevBool == false && LeftThumbstick == true; LeftThumbstickUp = prevBool == true && LeftThumbstick == false; prevBool = RightThumbstick; RightThumbstick = SteamVR_Actions.vRIF_RightThumbstickDown.state; // RightThumbstickDown = SteamVR_Actions.vRIF_RightThumbstickDown.stateDown; RightThumbstickDown = prevBool == false && RightThumbstick == true; RightThumbstickUp = prevBool == true && RightThumbstick == false; LeftThumbNear = SteamVR_Actions.vRIF_LeftThumbstickNear.state; //LeftThumbNear = SteamVR_Actions.vRIF_LeftTrackpadNear.state; RightThumbNear = SteamVR_Actions.vRIF_RightThumbstickNear.state; //RightThumbNear = SteamVR_Actions.vRIF_RightTrackpadNear.state; var prevVal = LeftGrip; LeftGrip = LeftGrip = correctValue(SteamVR_Actions.vRIF_LeftGrip.axis); LeftGripDown = prevVal < _downThreshold && LeftGrip >= _downThreshold; prevVal = RightGrip; RightGrip = correctValue(SteamVR_Actions.vRIF_RightGrip.axis); RightGripDown = prevVal < _downThreshold && RightGrip >= _downThreshold; prevVal = LeftTrigger; LeftTrigger = correctValue(SteamVR_Actions.vRIF_LeftTrigger.axis); LeftTriggerDown = prevVal < _downThreshold && LeftTrigger >= _downThreshold; LeftTriggerUp = prevVal > _downThreshold && LeftTrigger < _downThreshold; LeftTriggerNear = SteamVR_Actions.vRIF_LeftTriggerNear.state; prevVal = RightTrigger; RightTrigger = correctValue(SteamVR_Actions.vRIF_RightTrigger.axis); RightTriggerDown = prevVal < _downThreshold && RightTrigger >= _downThreshold; RightTriggerUp = prevVal > _downThreshold && RightTrigger < _downThreshold; RightTriggerNear = SteamVR_Actions.vRIF_RightTriggerNear.state; AButton = SteamVR_Actions.vRIF_AButton.state; AButtonDown = SteamVR_Actions.vRIF_AButton.stateDown; AButtonUp = SteamVR_Actions.vRIF_AButton.stateUp; BButton = SteamVR_Actions.vRIF_BButton.state; BButtonDown = SteamVR_Actions.vRIF_BButton.stateDown; BButtonUp = SteamVR_Actions.vRIF_AButton.stateUp; XButton = SteamVR_Actions.vRIF_XButton.state; XButtonDown = SteamVR_Actions.vRIF_XButton.stateDown; XButtonUp = SteamVR_Actions.vRIF_XButton.stateUp; YButton = SteamVR_Actions.vRIF_YButton.state; YButtonDown = SteamVR_Actions.vRIF_YButton.stateDown; YButtonUp = SteamVR_Actions.vRIF_YButton.stateUp; // Hand Tracking (Ie Valve Knuckles Finger Tracking) LeftThumbCurl = SteamVR_Actions.vRIF_SkeletonLeftHand.thumbCurl; LeftIndexCurl = SteamVR_Actions.vRIF_SkeletonLeftHand.indexCurl; LeftMiddleCurl = SteamVR_Actions.vRIF_SkeletonLeftHand.middleCurl; LeftRingCurl = SteamVR_Actions.vRIF_SkeletonLeftHand.ringCurl; LeftPinkyCurl = SteamVR_Actions.vRIF_SkeletonLeftHand.pinkyCurl; RightThumbCurl = SteamVR_Actions.vRIF_SkeletonRightHand.thumbCurl; RightIndexCurl = SteamVR_Actions.vRIF_SkeletonRightHand.indexCurl; RightMiddleCurl = SteamVR_Actions.vRIF_SkeletonRightHand.middleCurl; RightRingCurl = SteamVR_Actions.vRIF_SkeletonRightHand.ringCurl; RightPinkyCurl = SteamVR_Actions.vRIF_SkeletonRightHand.pinkyCurl; //prevBool = StartButton; //StartButton = SteamVR_Actions.vRIF_StartButton.state; //StartButtonDown = prevBool == false && StartButton == true; //prevBool = BackButton; //BackButton = SteamVR_Actions.vRIF_BackButton.state; //BackButtonDown = prevBool == false && BackButton == true; #endif } #endregion #region XR Input #if UNITY_2019_3_OR_NEWER #region XRInputVariables InputDevice primaryLeftController; InputDevice primaryRightController; InputFeatureUsage thumbstickAxis; InputFeatureUsage thumbstickAxisSecondary; InputFeatureUsage thumbstickAxisClick; #endregion #endif public virtual void UpdateXRInput() { #if UNITY_2019_3_OR_NEWER // Refresh XR devices InputDevices.GetDevices(devices); // Left XR Controller primaryLeftController = GetLeftController(); // Right XR Controller primaryRightController = GetRightController(); // For most cases thumbstick is on the primary2DAxis // However, if the Controller has both a touchpad and a controller on it (i.e. Valve Index Knuckles) then the thumbstick axis is actually on the secondary axis, not the primary axis thumbstickAxis = SupportsBothTouchPadAndJoystick ? CommonUsages.secondary2DAxis : CommonUsages.primary2DAxis; thumbstickAxisSecondary = SupportsBothTouchPadAndJoystick ? CommonUsages.primary2DAxis : CommonUsages.secondary2DAxis; thumbstickAxisClick = SupportsBothTouchPadAndJoystick ? CommonUsages.secondary2DAxisClick : CommonUsages.primary2DAxisClick; var prevBool = LeftThumbstick; LeftThumbstick = getFeatureUsage(primaryLeftController, thumbstickAxisClick); LeftThumbstickDown = prevBool == false && LeftThumbstick == true; LeftThumbstickUp = prevBool == true && LeftThumbstick == false; prevBool = RightThumbstick; RightThumbstick = getFeatureUsage(primaryRightController, thumbstickAxisClick); RightThumbstickDown = prevBool == false && RightThumbstick == true; RightThumbstickUp = prevBool == true && RightThumbstick == false; LeftTouchPadAxis = ApplyDeadZones(getFeatureUsage(primaryLeftController, thumbstickAxisSecondary), ThumbstickDeadzoneX, ThumbstickDeadzoneY); LeftThumbstickAxis = ApplyDeadZones(getFeatureUsage(primaryLeftController, thumbstickAxis), ThumbstickDeadzoneX, ThumbstickDeadzoneY); RightTouchPadAxis = ApplyDeadZones(getFeatureUsage(primaryRightController, thumbstickAxisSecondary), ThumbstickDeadzoneX, ThumbstickDeadzoneY); RightThumbstickAxis = ApplyDeadZones(getFeatureUsage(primaryRightController, thumbstickAxis), ThumbstickDeadzoneX, ThumbstickDeadzoneY); // Store copy of previous value so we can determine if we need to call OnDownEvent var prevVal = LeftGrip; LeftGrip = correctValue(getFeatureUsage(primaryLeftController, CommonUsages.grip)); LeftGripDown = prevVal < _downThreshold && LeftGrip >= _downThreshold; prevVal = RightGrip; RightGrip = correctValue(getFeatureUsage(primaryRightController, CommonUsages.grip)); RightGripDown = prevVal < _downThreshold && RightGrip >= _downThreshold; prevVal = LeftTrigger; LeftTrigger = correctValue(getFeatureUsage(primaryLeftController, CommonUsages.trigger)); LeftTriggerUp = prevVal > _downThreshold && LeftTrigger < _downThreshold; LeftTriggerDown = prevVal < _downThreshold && LeftTrigger >= _downThreshold; prevVal = RightTrigger; RightTrigger = correctValue(getFeatureUsage(primaryRightController, CommonUsages.trigger)); RightTriggerUp = prevVal > _downThreshold && RightTrigger < _downThreshold; RightTriggerDown = prevVal < _downThreshold && RightTrigger >= _downThreshold; // While OculusUsages.indexTouch is recommended, only CommonUsages.indexTouch is currently providing proper values on certain platforms // OculusUsages.indexTouch is returning proper values in Oculus XR plugin >= v1.6.0 // Oculus Desktop / Android packages require CommonUsages, not OculusUsages ¯\_(ツ)_/¯ #pragma warning disable 0618 LeftTriggerNear = getFeatureUsage(primaryLeftController, CommonUsages.indexTouch) > 0; // Check Oculus Usage if not found with CommonUsages if (!LeftTriggerNear) { #if OCULUS_XR_PLUGIN LeftTriggerNear = getFeatureUsage(primaryLeftController, Unity.XR.Oculus.OculusUsages.indexTouch); #endif } // Fallback to checking UnityInput if available if (!LeftTriggerNear && leftTriggerNear != null && correctValue(leftTriggerNear.ReadValue()) > 0) { LeftTriggerNear = true; }; LeftThumbNear = getFeatureUsage(primaryLeftController, CommonUsages.thumbTouch) > 0 || getFeatureUsage(primaryLeftController, CommonUsages.primaryTouch) || getFeatureUsage(primaryLeftController, CommonUsages.secondaryTouch) || getFeatureUsage(primaryLeftController, CommonUsages.primary2DAxisTouch); if (!LeftThumbNear) { #if OCULUS_XR_PLUGIN LeftThumbNear = getFeatureUsage(primaryLeftController, Unity.XR.Oculus.OculusUsages.thumbTouch); #endif } RightTriggerNear = getFeatureUsage(primaryRightController, CommonUsages.indexTouch) > 0; // Try Oculus Usages if not found with Common if (!RightTriggerNear) { #if OCULUS_XR_PLUGIN RightTriggerNear = getFeatureUsage(primaryRightController, Unity.XR.Oculus.OculusUsages.indexTouch); #endif } // Fallback to checking UnityInput for trigger near if available if (!RightTriggerNear && rightTriggerNear != null && correctValue(rightTriggerNear.ReadValue()) > 0) { RightTriggerNear = true; }; RightThumbNear = getFeatureUsage(primaryRightController, CommonUsages.thumbTouch) > 0 || getFeatureUsage(primaryRightController, CommonUsages.primaryTouch) || getFeatureUsage(primaryRightController, CommonUsages.secondaryTouch) || getFeatureUsage(primaryRightController, CommonUsages.primary2DAxisTouch); if (!RightThumbNear) { #if OCULUS_XR_PLUGIN RightThumbNear = getFeatureUsage(primaryRightController, Unity.XR.Oculus.OculusUsages.thumbTouch); #endif } #pragma warning restore 0618 prevBool = AButton; AButton = getFeatureUsage(primaryRightController, CommonUsages.primaryButton); AButtonDown = prevBool == false && AButton == true; AButtonUp = prevBool == true && AButton == false; prevBool = BButton; BButton = getFeatureUsage(primaryRightController, CommonUsages.secondaryButton); BButtonDown = prevBool == false && BButton == true; BButtonUp = prevBool == true && BButton == false; prevBool = XButton; XButton = getFeatureUsage(primaryLeftController, CommonUsages.primaryButton); XButtonDown = prevBool == false && XButton == true; XButtonUp = prevBool == true && XButton == false; prevBool = YButton; YButton = getFeatureUsage(primaryLeftController, CommonUsages.secondaryButton); YButtonDown = prevBool == false && YButton == true; YButtonUp = prevBool == true && YButton == false; prevBool = StartButton; StartButton = getFeatureUsage(primaryRightController, CommonUsages.menuButton); StartButtonDown = prevBool == false && StartButton == true; prevBool = BackButton; BackButton = getFeatureUsage(primaryLeftController, CommonUsages.menuButton); BackButtonDown = prevBool == false && BackButton == true; #endif } #endregion #region Unity Input public virtual void UpdateUnityInput() { var prevVal = LeftGrip; LeftGrip = correctValue(leftGrip.ReadValue()); LeftGripDown = prevVal < _downThreshold && LeftGrip >= _downThreshold; prevVal = LeftTrigger; LeftTrigger = correctValue(leftTrigger.ReadValue()); LeftTriggerDown = prevVal < _downThreshold && LeftTrigger >= _downThreshold; LeftTriggerNear = correctValue(leftTriggerNear.ReadValue()) > 0; LeftTriggerUp = prevVal > _downThreshold && LeftTrigger < _downThreshold; prevVal = RightGrip; RightGrip = correctValue(rightGrip.ReadValue()); RightGripDown = prevVal < _downThreshold && RightGrip >= _downThreshold; prevVal = RightTrigger; RightTrigger = correctValue(rightTrigger.ReadValue()); RightTriggerDown = prevVal < _downThreshold && RightTrigger >= _downThreshold; RightTriggerNear = correctValue(rightTriggerNear.ReadValue()) > 0; RightTriggerUp = prevVal > _downThreshold && RightTrigger < _downThreshold; LeftThumbstickAxis = leftThumbstick.ReadValue(); var prevBool = LeftThumbstick; LeftThumbstick = correctValue(leftThumbstickDown.ReadValue()) > 0; LeftThumbstickDown = prevBool == false && LeftThumbstick == true; LeftThumbstickUp = prevBool == true && LeftThumbstick == false; LeftThumbNear = correctValue(leftThumbNear.ReadValue()) > 0; LeftTouchPadAxis = leftTouchpad.ReadValue(); RightThumbstickAxis = rightThumbstick.ReadValue(); prevBool = RightThumbstick; RightThumbstick = correctValue(rightThumbstickDown.ReadValue()) > 0; RightThumbstickDown = prevBool == false && RightThumbstick == true; RightThumbstickUp = prevBool == true && RightThumbstick == false; RightThumbNear = correctValue(rightThumbNear.ReadValue()) > 0; RightTouchPadAxis = rightTouchpad.ReadValue(); prevBool = AButton; AButton = correctValue(aButton.ReadValue()) > 0; AButtonDown = prevBool == false && AButton == true; AButtonUp = prevBool == true && AButton == false; prevBool = BButton; BButton = correctValue(bButton.ReadValue()) > 0; BButtonDown = prevBool == false && BButton == true; BButtonUp = prevBool == true && BButton == false; prevBool = XButton; XButton = correctValue(xButton.ReadValue()) > 0; XButtonDown = prevBool == false && XButton == true; XButtonUp = prevBool == true && XButton == false; prevBool = YButton; YButton = correctValue(yButton.ReadValue()) > 0; YButtonDown = prevBool == false && YButton == true; YButtonUp = prevBool == true && YButton == false; prevBool = StartButton; StartButton = correctValue(startButton.ReadValue()) > 0; StartButtonDown = prevBool == false && StartButton == true; prevBool = BackButton; BackButton = correctValue(backButton.ReadValue()) > 0; BackButtonDown = prevBool == false && BackButton == true; } public virtual void CreateUnityInputActions() { leftGrip = CreateInputAction("leftGrip", "{LeftHand}/{grip}", true); leftTrigger = CreateInputAction("leftTrigger", "{LeftHand}/{trigger}", true); leftTriggerNear = CreateInputAction("leftTriggerNear", "{LeftHand}/triggerTouched", true); rightGrip = CreateInputAction("rightGrip", "{RightHand}/{grip}", true); rightTrigger = CreateInputAction("rightTrigger", "{RightHand}/{trigger}", true); rightTriggerNear = CreateInputAction("rightTriggerNear", "{RightHand}/triggerTouched", true); leftThumbstick = CreateInputAction("leftThumbstick", "{LeftHand}/{primary2DAxis}", true); leftThumbstickDown = CreateInputAction("leftThumbstickDown", "{LeftHand}/{primary2DAxisClick}", false); leftThumbNear = CreateInputAction("leftThumbNear", "{LeftHand}/thumbstickTouched", true); leftTouchpad = CreateInputAction("leftTouchpad", "{LeftHand}/{secondary2DAxis}", true); leftTouchpadDown = CreateInputAction("leftTouchpadDown", "{LeftHand}/{secondary2DAxisClick}", false); rightThumbstick = CreateInputAction("rightThumbstick", "{RightHand}/{primary2DAxis}", true); rightThumbstickDown = CreateInputAction("rightThumbstickDown", "{RightHand}/{primary2DAxisClick}", false); rightThumbNear = CreateInputAction("rightThumbNear", "{RightHand}/thumbstickTouched", true); rightTouchpad = CreateInputAction("rightTouchpad", "{RightHand}/{secondary2DAxis}", true); rightTouchpadDown = CreateInputAction("rightTouchpadDown", "{RightHand}/{secondary2DAxisClick}", false); aButton = CreateInputAction("aButton", "{RightHand}/{primaryButton}", false); bButton = CreateInputAction("bButton", "{RightHand}/{secondaryButton}", false); xButton = CreateInputAction("xButton", "{LeftHand}/{primaryButton}", false); yButton = CreateInputAction("yButton", "{LeftHand}/{secondaryButton}", false); startButton = CreateInputAction("startButton", "{RightHand}/{menu}", false); backButton = CreateInputAction("backButton", "{LeftHand}/{menu}", false); } public virtual void EnableActions() { if (actionSet != null) { foreach (var map in actionSet.actionMaps) { foreach (var action in map) { action.Enable(); } } } } public virtual void DisableActions() { if (actionSet != null) { foreach (var map in actionSet.actionMaps) { foreach (var action in map) { action.Disable(); } } } } public UnityEngine.InputSystem.InputAction CreateInputAction(string actionName, string binding, bool valueType) { var act = new UnityEngine.InputSystem.InputAction(actionName, valueType ? UnityEngine.InputSystem.InputActionType.Value : UnityEngine.InputSystem.InputActionType.Button, binding); // Automatically enable this binding act.Enable(); return act; } #endregion #region OVR Input public virtual void UpdateOVRInput() { #if OCULUS_INTEGRATION LeftThumbstickAxis = ApplyDeadZones(OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick), ThumbstickDeadzoneX, ThumbstickDeadzoneY); RightThumbstickAxis = ApplyDeadZones(OVRInput.Get(OVRInput.Axis2D.SecondaryThumbstick), ThumbstickDeadzoneX, ThumbstickDeadzoneY); LeftGrip = correctValue(OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, OVRInput.Controller.LTouch)); LeftGripDown = OVRInput.GetDown(OVRInput.Button.PrimaryHandTrigger, OVRInput.Controller.LTouch); RightGrip = correctValue(OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, OVRInput.Controller.RTouch)); RightGripDown = OVRInput.GetDown(OVRInput.Button.PrimaryHandTrigger, OVRInput.Controller.RTouch); LeftTrigger = correctValue(OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger, OVRInput.Controller.LTouch)); LeftTriggerUp = OVRInput.GetUp(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.LTouch); LeftTriggerDown = OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.LTouch); RightTrigger = correctValue(OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger, OVRInput.Controller.RTouch)); RightTriggerUp = OVRInput.GetUp(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.RTouch); RightTriggerDown = OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.RTouch); LeftTriggerNear = OVRInput.Get(OVRInput.NearTouch.PrimaryIndexTrigger, OVRInput.Controller.LTouch); LeftThumbNear = OVRInput.Get(OVRInput.NearTouch.PrimaryThumbButtons, OVRInput.Controller.LTouch); RightTriggerNear = OVRInput.Get(OVRInput.NearTouch.PrimaryIndexTrigger, OVRInput.Controller.RTouch); RightThumbNear = OVRInput.Get(OVRInput.NearTouch.PrimaryThumbButtons, OVRInput.Controller.RTouch); AButton = OVRInput.Get(OVRInput.Button.One, OVRInput.Controller.RTouch); AButtonDown = OVRInput.GetDown(OVRInput.Button.One, OVRInput.Controller.RTouch); AButtonUp = OVRInput.GetUp(OVRInput.Button.One, OVRInput.Controller.RTouch); BButton = OVRInput.Get(OVRInput.Button.Two); BButtonDown = OVRInput.GetDown(OVRInput.Button.Two); BButtonUp = OVRInput.GetUp(OVRInput.Button.Two); XButton = OVRInput.Get(OVRInput.Button.Three); XButtonDown = OVRInput.GetDown(OVRInput.Button.Three); XButtonUp = OVRInput.GetUp(OVRInput.Button.Three); YButton = OVRInput.Get(OVRInput.Button.Four); YButtonDown = OVRInput.GetDown(OVRInput.Button.Four); YButtonUp = OVRInput.GetUp(OVRInput.Button.Four); StartButton = OVRInput.Get(OVRInput.Button.Start); StartButtonDown = OVRInput.GetDown(OVRInput.Button.Start); BackButton = OVRInput.Get(OVRInput.Button.Back); BackButtonDown = OVRInput.GetDown(OVRInput.Button.Back); LeftThumbstickDown = OVRInput.GetDown(OVRInput.Button.PrimaryThumbstick, OVRInput.Controller.LTouch); LeftThumbstickUp = OVRInput.GetUp(OVRInput.Button.PrimaryThumbstick, OVRInput.Controller.LTouch); RightThumbstickDown = OVRInput.GetDown(OVRInput.Button.PrimaryThumbstick, OVRInput.Controller.RTouch); RightThumbstickUp = OVRInput.GetUp(OVRInput.Button.PrimaryThumbstick, OVRInput.Controller.RTouch); LeftThumbstick = OVRInput.Get(OVRInput.Button.PrimaryThumbstick, OVRInput.Controller.LTouch); RightThumbstick = OVRInput.Get(OVRInput.Button.PrimaryThumbstick, OVRInput.Controller.RTouch); #endif // Call events OnInputsUpdated?.Invoke(); } #endregion #region Pico Input public virtual void UpdatePicoInput() { #if PICO_SDK int rightHand = 1; int leftHand = 0; var prevBool = LeftThumbstick; var prevVal = LeftGrip; LeftThumbstickAxis = ApplyDeadZones(Pvr_UnitySDKAPI.Controller.UPvr_GetAxis2D(leftHand), ThumbstickDeadzoneX, ThumbstickDeadzoneY); RightThumbstickAxis = ApplyDeadZones(Pvr_UnitySDKAPI.Controller.UPvr_GetAxis2D(rightHand), ThumbstickDeadzoneX, ThumbstickDeadzoneY); prevBool = LeftThumbstick; LeftThumbstick = Pvr_UnitySDKAPI.Controller.UPvr_GetKey(leftHand, Pvr_UnitySDKAPI.Pvr_KeyCode.TOUCHPAD); LeftThumbstickDown = Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(leftHand, Pvr_UnitySDKAPI.Pvr_KeyCode.TOUCHPAD); LeftThumbstickUp = prevBool == true && LeftThumbstick == false; prevBool = RightThumbstick; RightThumbstick = Pvr_UnitySDKAPI.Controller.UPvr_GetKey(rightHand, Pvr_UnitySDKAPI.Pvr_KeyCode.TOUCHPAD); RightThumbstickDown = Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(rightHand, Pvr_UnitySDKAPI.Pvr_KeyCode.TOUCHPAD); RightThumbstickUp = prevBool == true && RightThumbstick == false; LeftThumbNear = Pvr_UnitySDKAPI.Controller.UPvr_IsTouching(leftHand); RightThumbNear = Pvr_UnitySDKAPI.Controller.UPvr_IsTouching(rightHand); prevVal = LeftGrip; LeftGrip = Pvr_UnitySDKAPI.Controller.UPvr_GetKey(leftHand, Pvr_UnitySDKAPI.Pvr_KeyCode.Left) ? 1f : 0; LeftGripDown = Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(leftHand, Pvr_UnitySDKAPI.Pvr_KeyCode.Left); prevVal = RightGrip; RightGrip = Pvr_UnitySDKAPI.Controller.UPvr_GetKey(rightHand, Pvr_UnitySDKAPI.Pvr_KeyCode.Right) ? 1f : 0; RightGripDown = Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(rightHand, Pvr_UnitySDKAPI.Pvr_KeyCode.Right); prevVal = LeftTrigger; LeftTrigger = Pvr_UnitySDKAPI.Controller.UPvr_GetControllerTriggerValue(leftHand) / 255f; LeftTriggerUp = prevVal > _downThreshold && LeftTrigger < _downThreshold; LeftTriggerDown = prevVal < _downThreshold && LeftTrigger >= _downThreshold; prevVal = RightTrigger; RightTrigger = Pvr_UnitySDKAPI.Controller.UPvr_GetControllerTriggerValue(rightHand) / 255f; RightTriggerUp = prevVal > _downThreshold && RightTrigger < _downThreshold; RightTriggerDown = prevVal < _downThreshold && RightTrigger >= _downThreshold; prevBool = AButton; AButton = Pvr_UnitySDKAPI.Controller.UPvr_GetKey(rightHand, Pvr_UnitySDKAPI.Pvr_KeyCode.A); AButtonDown = Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(rightHand, Pvr_UnitySDKAPI.Pvr_KeyCode.A); AButtonUp = prevBool == true && AButton == false; prevBool = BButton; BButton = Pvr_UnitySDKAPI.Controller.UPvr_GetKey(rightHand, Pvr_UnitySDKAPI.Pvr_KeyCode.B); BButtonDown = Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(rightHand, Pvr_UnitySDKAPI.Pvr_KeyCode.B); BButtonUp = prevBool == true && BButton == false; prevBool = XButton; XButton = Pvr_UnitySDKAPI.Controller.UPvr_GetKey(leftHand, Pvr_UnitySDKAPI.Pvr_KeyCode.X); XButtonDown = Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(leftHand, Pvr_UnitySDKAPI.Pvr_KeyCode.X); XButtonUp = prevBool == true && XButton == false; prevBool = YButton; YButton = Pvr_UnitySDKAPI.Controller.UPvr_GetKey(leftHand, Pvr_UnitySDKAPI.Pvr_KeyCode.Y); YButtonDown = prevBool == false && YButton == true; // Alternatively : // YButtonDown = Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(leftHand, Pvr_UnitySDKAPI.Pvr_KeyCode.Y); YButtonUp = prevBool == true && YButton == false; #endif } #endregion public virtual void UpdateDeviceActive() { InputDevice hmd = GetHMD(); // Can bail early if (hmd.isValid == false) { HMDActive = false; return; } // Make sure the device supports the presence feature bool userPresent = false; bool presenceFeatureSupported = hmd.TryGetFeatureValue(CommonUsages.userPresence, out userPresent); if(presenceFeatureSupported) { HMDActive = userPresent; } else { HMDActive = XRSettings.isDeviceActive; } } /// /// Round to nearest thousandth. This can alleviate some floating point precision errors found when using certain inputs. /// /// /// float correctValue(float inputValue) { return (float)System.Math.Round(inputValue * 1000f) / 1000f; } /// /// Returns true if the given binding is pressed /// public bool GetControllerBindingValue(ControllerBinding val) { if (val == ControllerBinding.AButton && AButton) { return true; } if (val == ControllerBinding.AButtonDown && AButtonDown) { return true; } if (val == ControllerBinding.BButton && BButton) { return true; } if (val == ControllerBinding.BButtonDown && BButtonDown) { return true; } if (val == ControllerBinding.XButton && XButton) { return true; } if (val == ControllerBinding.XButtonDown && XButtonDown) { return true; } if (val == ControllerBinding.YButton && YButton) { return true; } if (val == ControllerBinding.YButtonDown && YButtonDown) { return true; } if (val == ControllerBinding.LeftTrigger && LeftTrigger > _downThreshold) { return true; } if (val == ControllerBinding.LeftTriggerDown && LeftTriggerDown) { return true; } if (val == ControllerBinding.LeftGrip && LeftGrip > _downThreshold) { return true; } if (val == ControllerBinding.LeftGripDown && LeftGripDown) { return true; } if (val == ControllerBinding.LeftThumbstick && LeftThumbstick) { return true; } if (val == ControllerBinding.LeftThumbstickDown && LeftThumbstickDown) { return true; } if (val == ControllerBinding.RightTrigger && RightTrigger > _downThreshold) { return true; } if (val == ControllerBinding.RightTriggerDown && RightTriggerDown) { return true; } if (val == ControllerBinding.RightGrip && RightGrip > _downThreshold) { return true; } if (val == ControllerBinding.RightGripDown && RightGripDown) { return true; } if (val == ControllerBinding.RightThumbstick && RightThumbstick) { return true; } if (val == ControllerBinding.RightThumbstickDown && RightThumbstickDown) { return true; } if (val == ControllerBinding.StartButton && StartButton) { return true; } if (val == ControllerBinding.StartButtonDown && StartButtonDown) { return true; } if (val == ControllerBinding.BackButton && BackButton) { return true; } if (val == ControllerBinding.BackButtonDown && BackButtonDown) { return true; } return false; } public bool GetGrabbedControllerBinding(GrabbedControllerBinding val, ControllerHand hand) { if(hand == ControllerHand.Right) { if (val == GrabbedControllerBinding.Button1 && AButton) { return true; } if (val == GrabbedControllerBinding.Button1Down && AButtonDown) { return true; } if (val == GrabbedControllerBinding.Button2 && BButton) { return true; } if (val == GrabbedControllerBinding.Button2Down && BButtonDown) { return true; } if (val == GrabbedControllerBinding.Grip && RightGrip > _downThreshold) { return true; } if (val == GrabbedControllerBinding.GripDown && RightGripDown) { return true; } if (val == GrabbedControllerBinding.Trigger && RightTrigger > _downThreshold) { return true; } if (val == GrabbedControllerBinding.TriggerDown && RightTriggerDown) { return true; } } else if (hand == ControllerHand.Left) { if (val == GrabbedControllerBinding.Button1 && XButton) { return true; } if (val == GrabbedControllerBinding.Button1Down && XButtonDown) { return true; } if (val == GrabbedControllerBinding.Button2 && YButton) { return true; } if (val == GrabbedControllerBinding.Button2Down && YButtonDown) { return true; } if (val == GrabbedControllerBinding.Grip && LeftGrip > _downThreshold) { return true; } if (val == GrabbedControllerBinding.GripDown && LeftGripDown) { return true; } if (val == GrabbedControllerBinding.Trigger && LeftTrigger > _downThreshold) { return true; } if (val == GrabbedControllerBinding.TriggerDown && LeftTriggerDown) { return true; } } return false; } public Vector2 GetInputAxisValue(InputAxis val) { if (val == InputAxis.LeftThumbStickAxis) { return LeftThumbstickAxis; } if (val == InputAxis.RightThumbStickAxis) { return RightThumbstickAxis; } if (val == InputAxis.LeftTouchPadAxis) { return LeftTouchPadAxis; } if (val == InputAxis.RightTouchPadAxis) { return RightTouchPadAxis; } return Vector3.zero; } Vector2 ApplyDeadZones(Vector2 pos, float deadZoneX, float deadZoneY) { if (Mathf.Abs(pos.x) < deadZoneX) { pos.x = 0f; } if (Mathf.Abs(pos.y) < deadZoneY) { pos.y = 0f; } return pos; } // Called when an input device has changed (connect / disconnect, etc.) void onDeviceChanged(InputDevice inputDevice) { setDeviceProperties(); SetTrackingOriginMode(TrackingOrigin); } void setDeviceProperties() { // Update device properties such as device name, controller properties, etc. // We only want to update this information if a device has changed in order to skip unnecessary checks every frame IsOculusDevice = GetIsOculusDevice(); IsOculusQuest = GetIsOculusQuest(); IsHTCDevice = GetIsHTCDevice(); IsPicoDevice = GetIsPicoDevice(); IsValveIndexController = GetIsValveIndexController(); // Set the SDK we are using LoadedSDK = GetLoadedSDK(); // Get specific device support SupportsIndexTouch = GetSupportsIndexTouch(); SupportsThumbTouch = GetSupportsThumbTouch(); // Currently only the Valve Index has both a touchpad and a joystick on the same controller SupportsBothTouchPadAndJoystick = IsValveIndexController; // Update Controller Type ConnectedControllerType = GetControllerType(); // Call any events if(!string.IsNullOrEmpty(InputBridge.Instance.GetControllerName())) { OnControllerFound?.Invoke(); } } /// /// Returns true if the controllers support the 'indexTouch' XR input mapping.Currently only Oculus devices on the Oculus SDK support index touch. OpenVR is not supported. /// /// public virtual bool GetSupportsIndexTouch() { //if(IsOculusDevice && LoadedSDK == SDKProvider.OculusSDK) { // return true; //} return true; } public virtual SDKProvider GetLoadedSDK() { // Can exit early if no device name has been picked up yet if (XRSettings.loadedDeviceName == null) { return SDKProvider.Unknown; } string deviceName = XRSettings.loadedDeviceName.ToLower(); // Example : "oculus display" if (deviceName.StartsWith("oculus")) { return SDKProvider.OculusSDK; } // Example : "OpenVR Display" else if (deviceName.StartsWith("openvr")) { return SDKProvider.OpenVR; } return SDKProvider.Unknown; } public virtual bool GetSupportsThumbTouch() { //if (IsOculusDevice && LoadedSDK == SDKProvider.OculusSDK) { // return true; //} return true; } public virtual bool GetIsOculusDevice() { var primaryHMD = GetHMD(); // OpenVR Format if (primaryHMD != null && primaryHMD.manufacturer == "Oculus") { return true; } #if UNITY_2019_2_OR_NEWER return XRSettings.loadedDeviceName != null && XRSettings.loadedDeviceName.ToLower().Contains("oculus"); #else return true; #endif } public virtual bool GetIsOculusQuest() { #if UNITY_2019_2_OR_NEWER var primaryHMD = GetHMD(); // Example : "OpenVR Headset(Oculus Quest)" if (primaryHMD != null && primaryHMD.name != null && primaryHMD.name.EndsWith("(Oculus Quest)")) { return true; } // Non-OpenVR version use "contains" on string. else if (primaryHMD != null && primaryHMD.name != null && primaryHMD.name.Contains("Oculus Quest")) { return true; } // Fallback to refresh rate return GetIsOculusDevice() && XRDevice.refreshRate == 72f; #else if (Application.platform == RuntimePlatform.Android) { return true; } return false; #endif } public virtual bool GetIsHTCDevice() { // Is HTC Device #if UNITY_2019_2_OR_NEWER var primaryHMD = GetHMD(); // OpenVR Format if (primaryHMD != null && primaryHMD.manufacturer == "HTC") { return true; } return XRSettings.loadedDeviceName.StartsWith("HTC"); #else return false; #endif } public virtual bool GetIsPicoDevice() { #if UNITY_2019_2_OR_NEWER return InputSource == XRInputSource.Pico || XRSettings.loadedDeviceName.StartsWith("Pico"); #else return InputSource == XRInputSource.Pico; #endif } public InputDevice GetHMD() { InputDevices.GetDevices(devices); var hmds = new List(); var dc1 = InputDeviceCharacteristics.HeadMounted; InputDevices.GetDevicesWithCharacteristics(dc1, hmds); return hmds.FirstOrDefault(); } /// /// Returns the name of the InputDevice if found. Returns String.empty if not found /// /// The name of the InputDevice if found, or String.empty if not found public string GetHMDName() { var device = GetHMD(); if(device != null) { return device.name; } return string.Empty; } public Vector3 GetHMDLocalPosition() { Vector3 localPosition; GetHMD().TryGetFeatureValue(CommonUsages.devicePosition, out localPosition); return localPosition; } public Quaternion GetHMDLocalRotation() { Quaternion localRotation; GetHMD().TryGetFeatureValue(CommonUsages.deviceRotation, out localRotation); return localRotation; } public InputDevice GetLeftController() { InputDevices.GetDevices(devices); var leftHandedControllers = new List(); var dc = InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.Left | InputDeviceCharacteristics.Controller; InputDevices.GetDevicesWithCharacteristics(dc, leftHandedControllers); return leftHandedControllers.FirstOrDefault(); } public InputDevice GetRightController() { InputDevices.GetDevices(devices); var rightHandedControllers = new List(); var dc = InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.Right | InputDeviceCharacteristics.Controller; InputDevices.GetDevicesWithCharacteristics(dc, rightHandedControllers); return rightHandedControllers.FirstOrDefault(); } public Vector3 GetControllerLocalPosition(ControllerHand handSide) { Vector3 localPosition = Vector3.zero; if (handSide == ControllerHand.Left) { GetLeftController().TryGetFeatureValue(CommonUsages.devicePosition, out localPosition); } else if (handSide == ControllerHand.Right) { GetRightController().TryGetFeatureValue(CommonUsages.devicePosition, out localPosition); } return localPosition; } public Quaternion GetControllerLocalRotation(ControllerHand handSide) { Quaternion localRotation = Quaternion.identity; if (handSide == ControllerHand.Left) { GetLeftController().TryGetFeatureValue(CommonUsages.deviceRotation, out localRotation); } else if (handSide == ControllerHand.Right) { GetRightController().TryGetFeatureValue(CommonUsages.deviceRotation, out localRotation); } return localRotation; } public virtual ControllerType GetControllerType() { if (IsValveIndexController) { return ControllerType.Knuckles; } else if (IsOculusDevice) { return ControllerType.OculusTouch; } else if (IsHTCDevice) { return ControllerType.Wand; } return ControllerType.Unknown; } public Vector3 GetControllerVelocity(ControllerHand hand) { InputDevice inputDevice = hand == ControllerHand.Left ? GetLeftController() : GetRightController(); return getFeatureUsage(inputDevice, CommonUsages.deviceVelocity); } public Vector3 GetControllerAngularVelocity(ControllerHand hand) { InputDevice inputDevice = hand == ControllerHand.Left ? GetLeftController() : GetRightController(); return getFeatureUsage(inputDevice, CommonUsages.deviceAngularVelocity); } /// /// Get the name of the primary controller /// /// The name of the primary controller. Returns empty if no controller found public virtual string GetControllerName() { // First try right controller var rightHandedControllers = new List(); var dc = InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.Right | InputDeviceCharacteristics.Controller; InputDevices.GetDevicesWithCharacteristics(dc, rightHandedControllers); var primaryRightController = rightHandedControllers.FirstOrDefault(); // Return name of the found controller if (primaryRightController != null && !System.String.IsNullOrEmpty(primaryRightController.name)) { return primaryRightController.name; } // No right controller found, try the left var leftHandedControllers = new List(); dc = InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.Left | InputDeviceCharacteristics.Controller; InputDevices.GetDevicesWithCharacteristics(dc, leftHandedControllers); var primaryLeftController = leftHandedControllers.FirstOrDefault(); // Return name of the found controller if (primaryLeftController != null && !System.String.IsNullOrEmpty(primaryLeftController.name)) { return primaryLeftController.name; } return string.Empty; } public virtual bool GetIsValveIndexController() { var rightHandedControllers = new List(); var dc = InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.Right | InputDeviceCharacteristics.Controller; InputDevices.GetDevicesWithCharacteristics(dc, rightHandedControllers); var primaryRightController = rightHandedControllers.FirstOrDefault(); // Are we using Valve Index Controllers? if (primaryRightController != null && !System.String.IsNullOrEmpty(primaryRightController.name)) { return primaryRightController.name.Contains("Knuckles"); } return false; } #if UNITY_2019_2_OR_NEWER float getFeatureUsage(InputDevice device, InputFeatureUsage usage, bool clamp = true) { float val; device.TryGetFeatureValue(usage, out val); return Mathf.Clamp01(val); } bool getFeatureUsage(InputDevice device, InputFeatureUsage usage) { bool val; if (device.TryGetFeatureValue(usage, out val)) { return val; } return val; } Vector2 getFeatureUsage(InputDevice device, InputFeatureUsage usage) { Vector2 val; if (device.TryGetFeatureValue(usage, out val)) { return val; } return val; } Vector3 getFeatureUsage(InputDevice device, InputFeatureUsage usage) { Vector3 val; if (device.TryGetFeatureValue(usage, out val)) { return val; } return val; } #endif bool setTrackingOrigin = false; public virtual void SetTrackingOriginMode(TrackingOriginModeFlags trackingOrigin) { // 2019.4 Needs to use XRDevice.SetTrackingSpaceType; TrySetTrackingOriginMode does not function properly. // *Removed from VRIF v1.6 as XR plugin should properly set tracking space #if UNITY_2019_4 if (trackingOrigin == TrackingOriginModeFlags.Floor) { #pragma warning disable XRDevice.SetTrackingSpaceType(TrackingSpaceType.RoomScale); #pragma warning restore } #endif StartCoroutine(changeOriginModeRoutine(trackingOrigin)); } IEnumerator changeOriginModeRoutine(TrackingOriginModeFlags trackingOrigin) { // Wait one frame as Unity has an issue with calling this immediately yield return null; if(!setTrackingOrigin) { List subsystems = new List(); SubsystemManager.GetInstances(subsystems); int subSystemsCount = subsystems.Count; if (subSystemsCount > 0) { for (int x = 0; x < subSystemsCount; x++) { if (subsystems[x].TrySetTrackingOriginMode(trackingOrigin)) { setTrackingOrigin = true; // Debug.Log("Successfully set TrackingOriginMode to " + trackingOrigin); } else { Debug.LogWarning("Failed to set TrackingOriginMode to " + trackingOrigin); } } } else { // Debug.LogWarning("No subsystems detected. Unable to set Tracking Origin to " + trackingOrigin); } } } // Start Vibration on controller public void VibrateController(float frequency, float amplitude, float duration, ControllerHand hand) { if (InputSource == XRInputSource.OVRInput) { StartCoroutine(Vibrate(frequency, amplitude, duration, hand)); } else if (InputSource == XRInputSource.SteamVR && SteamVRSupport && HMDActive) { #if STEAM_VR_SDK if (hand == ControllerHand.Right) { if(SteamVR_Actions.vRIF_Haptic != null) { SteamVR_Actions.vRIF_Haptic.Execute(0, duration, frequency, amplitude, SteamVR_Input_Sources.RightHand); } } else { if (SteamVR_Actions.vRIF_Haptic != null) { SteamVR_Actions.vRIF_Haptic.Execute(0, duration, frequency, amplitude, SteamVR_Input_Sources.LeftHand); } } #endif } // Default / Fallback to XRInput else { if (hand == ControllerHand.Right) { InputDevices.GetDevicesWithCharacteristics(InputDeviceCharacteristics.Right, devices); } else { InputDevices.GetDevicesWithCharacteristics(InputDeviceCharacteristics.Left, devices); } for (int x = 0; x < devices.Count; x++) { HapticCapabilities capabilities; if (devices[x].TryGetHapticCapabilities(out capabilities)) { if (capabilities.supportsImpulse) { uint channel = 0; devices[x].SendHapticImpulse(channel, amplitude, duration); } } } } } IEnumerator Vibrate(float frequency, float amplitude, float duration, ControllerHand hand) { #if OCULUS_INTEGRATION // Start vibration if (hand == ControllerHand.Right) { OVRInput.SetControllerVibration(frequency, amplitude, OVRInput.Controller.RTouch); } else if (hand == ControllerHand.Left) { OVRInput.SetControllerVibration(frequency, amplitude, OVRInput.Controller.LTouch); } yield return new WaitForSeconds(duration); // Stop vibration if (hand == ControllerHand.Right) { OVRInput.SetControllerVibration(0, 0, OVRInput.Controller.RTouch); } else if (hand == ControllerHand.Left) { OVRInput.SetControllerVibration(0, 0, OVRInput.Controller.LTouch); } #else yield return new WaitForSeconds(duration); #endif } } }