123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- //#define OCULUS_SDK //<-- define this in your project settings if you have the OVR API
- #if UNITY_2017_2_OR_NEWER
- using System;
- using System.Collections.Generic;
- using System.Text;
- using UnityEngine;
- using UnityEngine.XR;
- using ZenFulcrum.VR.OpenVRBinding;
- namespace ZenFulcrum.EmbeddedBrowser.VR {
- public enum InputAxis {
- MainTrigger,//main index finger trigger
- Grip,//Vive squeeze/Oculus hand trigger
- JoypadX, JoypadY,//touchpad/joystick x/y position
- Joypad,//touchpad/joystick click/press
- Application,//application meanu/start button
- }
- public enum JoyPadType {
- None,
- Joystick,
- TouchPad,
- }
- /// <summary>
- /// Unity's VR input system is sorely lacking in usefulness.
- /// We can finally get pose data in a more-or-less straightforward manner (InputTracking.GetNodeStates,
- /// state.TryGetPosition, etc.), but getting the axis and buttons is an awful mess.
- ///
- /// This page https://docs.unity3d.com/Manual/OpenVRControllers.html says we can access the inputs via
- /// the input system! And, you can look at (of all things) the Joystick names to see which joystick
- /// is a VR controller at runtime. But guess what? There's no Unity API to fetch the value of a given
- /// axis on a given controller! Extra-stupid, right? You can define a specific input in the input menu,
- /// but that doesn't help when you switch to a different computer and the joystick numbers change!
- /// Short of defining an "input" for every possible controller * every axis you use, there's not a
- /// way to get the controller axis inputs in a way that will work reliably across different machines.
- ///
- /// (We can fetch buttons manually via Input.GetKey(KeyCode.Joystick1Button0 + buttonId + joystickIdx * 20),
- /// but this don't address the axis issue at all.)
- ///
- /// Anyway, this is a workaround. Around another Unity problem. Implemented by hooking into backend APIs directly.
- /// </summary>
- public class VRInput {
- private static VRInput impl;
- public static void Init() {
- if (impl == null) impl = GetImpl();
- }
- /// <summary>
- /// Returns the state of the given button/axis.
- /// </summary>
- /// <param name="node"></param>
- /// <param name="axis"></param>
- /// <returns></returns>
- public static float GetAxis(XRNodeState node, InputAxis axis) {
- if (impl == null) impl = GetImpl();
- return impl.GetAxisValue(node, axis);
- }
- /// <summary>
- /// If the controller is capable, returns if (and sometimes how closely) the player is touching
- /// the given control.
- /// </summary>
- /// <param name="node"></param>
- /// <param name="axis"></param>
- /// <returns></returns>
- public static float GetTouch(XRNodeState node, InputAxis axis) {
- if (impl == null) impl = GetImpl();
- return impl.GetTouchValue(node, axis);
- }
- protected virtual float GetAxisValue(XRNodeState node, InputAxis axis) {
- return 0;
- }
- protected virtual float GetTouchValue(XRNodeState node, InputAxis axis) {
- return 0;
- }
- private static Dictionary<ulong, JoyPadType> nodeTypes = new Dictionary<ulong, JoyPadType>();
- public static JoyPadType GetJoypadType(XRNodeState node) {
- JoyPadType ret;
- if (!nodeTypes.TryGetValue(node.uniqueID, out ret)) {
- ret = JoyPadType.None;
- if (impl == null) impl = GetImpl();
- var name = impl.GetNodeName(node);
- if (name.Contains("Oculus Touch Controller") || name.StartsWith("Oculus Rift CV1")) {
- //OpenVR gives us "Oculus Rift CV1 (Left Controller)" etc. where I wish it would mention the type of controller (Touch)
- ret = JoyPadType.Joystick;
- } else if (name.StartsWith("Vive Controller")) {
- ret = JoyPadType.TouchPad;
- } else {
- Debug.LogWarning("Unknown controller type: " + name);
- }
- nodeTypes[node.uniqueID] = ret;
- }
- return ret;
- }
- public virtual string GetNodeName(XRNodeState node) {
- return InputTracking.GetNodeName(node.uniqueID);
- }
- // public virtual JoyPadType JoypadTypeValue(XRNodeState node) { return JoyPadType.None; }
- private static VRInput GetImpl() {
- if (XRSettings.loadedDeviceName == "OpenVR") {
- return new OpenVRInput();
- } else if (XRSettings.loadedDeviceName == "Oculus") {
- #if OCULUS_SDK
- return new OculusVRInput();
- #else
- Debug.LogError("To use the Oculus API for input, import the Oculus SDK and define OCULUS_SDK");
- return new VRInput();
- #endif
- } else {
- Debug.LogError("Unknown VR input system: " + XRSettings.loadedDeviceName);
- return new VRInput();
- }
- }
- }
- class OpenVRInput : VRInput {
- protected VRControllerState_t lastState;
- public static string GetStringProperty(uint deviceId, ETrackedDeviceProperty prop) {
- var buffer = new StringBuilder((int)OpenVR.k_unMaxPropertyStringSize);
- ETrackedPropertyError err = ETrackedPropertyError.TrackedProp_Success;
- OpenVR.System.GetStringTrackedDeviceProperty(
- deviceId, prop,
- buffer, OpenVR.k_unMaxPropertyStringSize, ref err
- );
- if (err != ETrackedPropertyError.TrackedProp_Success) {
- throw new Exception("Failed to get property " + prop + " on device " + deviceId + ": " + err);
- }
- return buffer.ToString();
- }
- public override string GetNodeName(XRNodeState node) {
- var deviceId = (uint)GetDeviceId(node);
- try {
- return GetStringProperty(deviceId, ETrackedDeviceProperty.Prop_ModelNumber_String);
- } catch (Exception ex) {
- Debug.LogError("Failed to get device name for device " + deviceId + ": " + ex.Message);
- return base.GetNodeName(node);
- }
- }
- protected void ReadState(XRNodeState node) {
- if (OpenVR.System == null) {
- Debug.LogWarning("OpenVR not active");
- lastState = default(VRControllerState_t);
- return;
- }
- var controllerId = GetDeviceId(node);
- if (controllerId < 0) {
- lastState = default(VRControllerState_t);
- return;
- }
- //Debug.Log("Id is " + controllerId);
- var res = OpenVR.System.GetControllerState(
- (uint)controllerId, ref lastState,
- (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VRControllerState_t))
- );
- if (!res) {
- Debug.LogWarning("Failed to get controller state");
- }
- }
- protected override float GetAxisValue(XRNodeState node, InputAxis axis) {
- ReadState(node);
- switch (axis) {
- case InputAxis.MainTrigger:
- return lastState.rAxis1.x;
- case InputAxis.Grip:
- return (lastState.ulButtonPressed & (1ul << (int)EVRButtonId.k_EButton_Grip)) != 0 ? 1 : 0;
- case InputAxis.JoypadX:
- return lastState.rAxis0.x;
- case InputAxis.JoypadY:
- return lastState.rAxis0.y;
- case InputAxis.Joypad:
- return (lastState.ulButtonPressed & (1ul << (int)EVRButtonId.k_EButton_SteamVR_Touchpad)) != 0 ? 1 : 0;
- case InputAxis.Application:
- return (lastState.ulButtonPressed & (1ul << (int)EVRButtonId.k_EButton_ApplicationMenu)) != 0 ? 1 : 0;
- default:
- throw new ArgumentOutOfRangeException("axis", axis, null);
- }
- }
- protected override float GetTouchValue(XRNodeState node, InputAxis axis) {
- ReadState(node);
- switch (axis) {
- case InputAxis.Joypad:
- return (lastState.ulButtonTouched & (1ul << (int)EVRButtonId.k_EButton_SteamVR_Touchpad)) != 0 ? 1 : 0;
- default:
- return 0;
- }
- }
- private int GetDeviceId(XRNodeState node) {
- var targetRole = node.nodeType == XRNode.LeftHand ? ETrackedControllerRole.LeftHand : ETrackedControllerRole.RightHand;
- for (uint i = 0; i < OpenVR.k_unMaxTrackedDeviceCount; i++) {
- var role = OpenVR.System.GetControllerRoleForTrackedDeviceIndex(i);
- if (role == targetRole) return (int)i;
- }
- return -1;
- }
- }
- #if OCULUS_SDK
- class OculusVRInput : VRInput {
- protected override float GetAxisValue(XRNodeState node, InputAxis axis) {
- var controller = GetController(node);
- OVRPlugin.ControllerState4 state = OVRPlugin.GetControllerState4((uint)controller);
- switch (axis) {
- case InputAxis.MainTrigger:
- return controller == OVRInput.Controller.LTouch ? state.LIndexTrigger : state.RIndexTrigger;
- case InputAxis.Grip:
- return controller == OVRInput.Controller.LTouch ? state.LHandTrigger : state.RHandTrigger;
- case InputAxis.JoypadX:
- case InputAxis.JoypadY: {
- var joy = controller == OVRInput.Controller.LTouch ? state.LThumbstick : state.RThumbstick;
- return axis == InputAxis.JoypadX ? joy.x : joy.y;
- }
- case InputAxis.Joypad: {
- var buttonId = controller == OVRInput.Controller.LTouch ? 0x00000400 : 0x00000004; //see enum ovrButton_ in OVER_CAPI.h
- return (state.Buttons & buttonId) != 0 ? 1 : 0;
- }
- default:
- return 0;
- }
- }
- private OVRInput.Controller GetController(XRNodeState node) {
- switch (node.nodeType) {
- case XRNode.LeftHand:
- return OVRInput.Controller.LTouch;
- case XRNode.RightHand:
- return OVRInput.Controller.RTouch;
- default:
- return OVRInput.Controller.None;
- }
- }
- protected override float GetTouchValue(XRNodeState node, InputAxis axis) {
- //nothing touch-related is presently used for Oculus Touch controllers, so nothing is all we need here
- return 0;
- }
- }
- #endif
- }
- #endif
|