1241 lines
39 KiB
C#
1241 lines
39 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Assets.HurricaneVR.Framework.Shared.Utilities;
|
|
using HurricaneVR.Framework.Components;
|
|
using HurricaneVR.Framework.Core;
|
|
using HurricaneVR.Framework.Core.Grabbers;
|
|
using HurricaneVR.Framework.Shared;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.Serialization;
|
|
using UnityEngine.XR;
|
|
|
|
|
|
#if USING_OPENXR
|
|
using UnityEngine.XR.OpenXR;
|
|
#endif
|
|
|
|
#if USING_XR_MANAGEMENT
|
|
using UnityEngine.XR.Management;
|
|
#endif
|
|
|
|
#if HVR_OCULUS
|
|
using HurricaneVR.Framework.Oculus;
|
|
#endif
|
|
|
|
#if HVR_STEAMVR
|
|
using HurricaneVR.Framework.SteamVR;
|
|
using Valve.VR;
|
|
#endif
|
|
|
|
namespace HurricaneVR.Framework.ControllerInput
|
|
{
|
|
[Serializable]
|
|
public class HVRControllerEvent : UnityEvent<HVRController>
|
|
{
|
|
}
|
|
|
|
public class HVRInputManager : MonoBehaviour
|
|
{
|
|
public const string OpenVRController = "openvr";
|
|
public const string WindowsMR = "windowsmr";
|
|
public const string WindowsMROpenXR = "windows mr controller";
|
|
public const string Vive = "vive";
|
|
public const string Cosmos = "cosmos";
|
|
public const string Oculus = "oculus";
|
|
public const string Knuckles = "knuckles";
|
|
public const string KnucklesOpenXR = "index controller";
|
|
public const string WMRController = "spatial";
|
|
public const string HTC = "htc";
|
|
public const string Reverb = "reverb";
|
|
public const string G2 = "g2";
|
|
public const string OpenXR_G2 = "hp reverb g2 controller";
|
|
public const string Pico = "pico";
|
|
|
|
public const string LegacyOpenVRName = "OpenVR";
|
|
public const string LegacyOculusName = "Oculus";
|
|
public const string LegacyNone = "None";
|
|
|
|
|
|
[Header("XR Plugin Detection Names")]
|
|
public string OpenVRLoader = "Open VR Loader";
|
|
|
|
public string OculusLoader = "Oculus Loader";
|
|
public string OpenXRLoader = "Open XR Loader";
|
|
public string WMRLoader = "Windows MR Loader";
|
|
|
|
|
|
private const string ReverbG2 = "0x045E/0x066A";
|
|
private const string OdysseyPlus = "WindowsMR: 0x045E/0x065D"; //OpenVR Controller(WindowsMR: 0x045E/0x065D/0/1) - Left
|
|
|
|
public static HVRInputManager Instance { get; private set; }
|
|
|
|
|
|
[Header("Oculus - Requires Oculus Asset + Integration")]
|
|
[Tooltip("If true ovrinputs will be used")]
|
|
public bool UseOVRInputs;
|
|
|
|
[Tooltip("If set to true, OVRManager is required to be in your scene")]
|
|
public bool OVRHaptics;
|
|
|
|
[Tooltip("If using OVRInput for Oculus devices without OVRManager in the scene then set this to true.")]
|
|
public bool ForceOVRInputUpdate;
|
|
|
|
[Header("SteamVR - Requires SteamVR + Integration")]
|
|
public bool InitializeSteamVR = true;
|
|
|
|
public bool InitializeSteamVRActions = true;
|
|
|
|
[Header("Input Settings")]
|
|
[Tooltip("If true uses the new input system bindings")]
|
|
public bool UseNewInputSystem;
|
|
|
|
public TrackingOriginModeFlags TrackingSpace = TrackingOriginModeFlags.Floor;
|
|
|
|
|
|
[Tooltip("Finger Curl Settings, defaults created if not supplied")]
|
|
public HVRFingerSettings FingerSettings;
|
|
|
|
[Tooltip("Device / SDK controller offsets")]
|
|
public HVRControllerOffsets ControllerOffsets;
|
|
|
|
[Tooltip("Haptics Settings")]
|
|
public HVRGrabHaptics GrabHaptics;
|
|
|
|
[Header("Device Specific Settings")]
|
|
public HVRInputSettings WMRInputMap;
|
|
|
|
[FormerlySerializedAs("WMRWithButtonsInputMap")]
|
|
public HVRInputSettings ReverbG2InputMap;
|
|
|
|
public HVRInputSettings OculusInputMap;
|
|
public HVRInputSettings ViveInputMap;
|
|
public HVRInputSettings KnucklesInputMap;
|
|
public HVRInputSettings CosmosInputMap;
|
|
|
|
|
|
//public bool LeftHasTrackPad;
|
|
//public bool RightHasTrackPad;
|
|
[Header("Deadzones")]
|
|
[Tooltip("WMR device deadzone, if any.")]
|
|
public Vector2 WMRDeadzone = new Vector2(.15f, .15f);
|
|
|
|
[Tooltip("Oculus device deadzone, if any.")]
|
|
public Vector2 OculusDeadzone = new Vector2(.15f, .15f);
|
|
|
|
[Tooltip("Vive device deadzone, if any.")]
|
|
public Vector2 ViveDeadzone = new Vector2(0f, 0f);
|
|
|
|
[Tooltip("Knuckles device deadzone, if any.")]
|
|
public Vector2 KnucklesDeadzone = new Vector2(0f, 0f);
|
|
|
|
[Tooltip("Cosmos device deadzone, if any.")]
|
|
public Vector2 CosmosDeadzone = new Vector2(0f, 0f);
|
|
|
|
[Tooltip("Master deadzone, useful if you want the user to set.")]
|
|
public Vector2 DeadzoneOverride;
|
|
|
|
[Tooltip("Override provider level deadzone.")]
|
|
public bool OverrideDeadzone;
|
|
|
|
|
|
[Header("Debugging")]
|
|
public InputSDK CurrentSDK = InputSDK.None;
|
|
|
|
[SerializeField]
|
|
private string _xrPlugin;
|
|
|
|
public string XRPluginLoader
|
|
{
|
|
get { return _xrPlugin; }
|
|
set
|
|
{
|
|
_xrPlugin = value;
|
|
|
|
if (!string.IsNullOrWhiteSpace(value))
|
|
{
|
|
var loader = value.ToLower();
|
|
var loaderNoSpace = loader.Replace(" ", "");
|
|
if (loader == OpenXRLoader.ToLower() || loaderNoSpace == OpenXRLoader.ToLower().Replace(" ", ""))
|
|
{
|
|
IsOpenXR = true;
|
|
}
|
|
}
|
|
|
|
if (IsOpenXR)
|
|
{
|
|
#if USING_OPENXR
|
|
Debug.Log($"OpenXR Runtime: {OpenXRRuntime.name}");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool LegacyActive;
|
|
public bool XRPluginActive;
|
|
public VRMode VRPlugin;
|
|
public bool IsVRInitialized => LegacyActive || XRPluginActive;
|
|
|
|
public bool IsOpenXR { get; private set; }
|
|
|
|
public bool IsSteamVR { get; private set; }
|
|
|
|
public string HMDManufacturer;
|
|
public string HMDName;
|
|
|
|
public string LeftManufacturer;
|
|
public string LeftControllerName;
|
|
|
|
public string RightManufacturer;
|
|
public string RightControllerName;
|
|
|
|
[SerializeField]
|
|
private HVRControllerType LeftControllerType = HVRControllerType.None;
|
|
|
|
[SerializeField]
|
|
private HVRControllerType RightControllerType = HVRControllerType.None;
|
|
|
|
public List<string> LeftFeatures = new List<string>();
|
|
public List<string> RightFeatures = new List<string>();
|
|
public List<string> HMDFeatures = new List<string>();
|
|
|
|
public HVRController LeftController;
|
|
public HVRController RightController;
|
|
|
|
public HVRController LeftXRInputController;
|
|
public HVRController RightXRInputController;
|
|
|
|
public HVRController LeftOculusController;
|
|
public HVRController RightOculusController;
|
|
|
|
public HVRController LeftSteamController;
|
|
public HVRController RightSteamController;
|
|
|
|
public HVRController LeftInputSystemController;
|
|
public HVRController RightInputSystemController;
|
|
|
|
public UnityEvent HMDFirstActivation = new UnityEvent();
|
|
public UnityEvent HMDActivated = new UnityEvent();
|
|
public UnityEvent HMDDeactivated = new UnityEvent();
|
|
public UnityEvent HMDRecentered = new UnityEvent();
|
|
public UnityEvent UserSensed = new UnityEvent();
|
|
public UnityEvent UserNotSensed = new UnityEvent();
|
|
|
|
public HVRControllerEvent LeftControllerConnected = new HVRControllerEvent();
|
|
public HVRControllerEvent RightControllerConnected = new HVRControllerEvent();
|
|
|
|
private readonly List<XRDisplaySubsystem> _displaySubsystems = new List<XRDisplaySubsystem>();
|
|
private bool _applicationExiting;
|
|
private InputDevice _hmdDevice;
|
|
|
|
public InputDevice HMDDevice
|
|
{
|
|
get
|
|
{
|
|
if (_hmdDevice.isValid)
|
|
return _hmdDevice;
|
|
_hmdDevice = InputDevices.GetDeviceAtXRNode(XRNode.Head);
|
|
return _hmdDevice;
|
|
}
|
|
}
|
|
|
|
public bool PreviousHMDActive { get; private set; }
|
|
public bool PreviousUserPresent { get; private set; }
|
|
|
|
public bool HMDActive
|
|
{
|
|
get
|
|
{
|
|
if (HMDDevice.isValid)
|
|
return true;
|
|
//beware this still returns true if the device is not rendering
|
|
return XRSettings.isDeviceActive;
|
|
}
|
|
}
|
|
|
|
public bool UserPresent
|
|
{
|
|
get
|
|
{
|
|
if (!HMDActive)
|
|
return false;
|
|
|
|
#if USING_XR_MANAGEMENT
|
|
|
|
//https://forum.unity.com/threads/commonusages-userpresence-doesnt-report-correctly.818766/
|
|
//this features requires XR Management
|
|
//might be active but not rendering due to headset not on
|
|
if (HMDDevice.TryGetFeatureValue(CommonUsages.userPresence, out var present))
|
|
{
|
|
return present;
|
|
}
|
|
|
|
#elif !UNITY_2020_1_OR_NEWER
|
|
//https://stackoverflow.com/questions/51372771/how-to-check-if-a-hmd-in-unity-2018-is-in-use
|
|
#pragma warning disable 0618
|
|
//if xr management wasn't detected use the old API for legacy VR
|
|
if (XRDevice.userPresence == UserPresenceState.Present)
|
|
return true;
|
|
#pragma warning restore 0618
|
|
#endif
|
|
|
|
//todo, test if this is a xrplugin only thing
|
|
//https://docs.unity3d.com/ScriptReference/XR.XRDevice-isPresent.html
|
|
|
|
_displaySubsystems.Clear();
|
|
SubsystemManager.GetInstances<XRDisplaySubsystem>(_displaySubsystems);
|
|
foreach (var xrDisplay in _displaySubsystems)
|
|
{
|
|
if (xrDisplay.running)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private InputDevice _leftDevice;
|
|
|
|
public InputDevice LeftDevice
|
|
{
|
|
get
|
|
{
|
|
if (_leftDevice.isValid)
|
|
return _leftDevice;
|
|
_leftDevice = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
|
|
return _leftDevice;
|
|
}
|
|
}
|
|
|
|
private InputDevice _rightDevice;
|
|
|
|
public InputDevice RightDevice
|
|
{
|
|
get
|
|
{
|
|
if (_rightDevice.isValid)
|
|
return _rightDevice;
|
|
_rightDevice = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
|
|
return _rightDevice;
|
|
}
|
|
}
|
|
|
|
|
|
private bool _isHMDFirstActivationReported;
|
|
|
|
|
|
private void Awake()
|
|
{
|
|
if (!Instance)
|
|
{
|
|
Instance = this;
|
|
DontDestroyOnLoad(this.gameObject);
|
|
}
|
|
else
|
|
{
|
|
Destroy(this);
|
|
return;
|
|
}
|
|
|
|
InputDevices.deviceConfigChanged += OnDeviceConfigChanged;
|
|
InputDevices.deviceConnected += OnDeviceConnected;
|
|
InputDevices.deviceDisconnected += OnDeviceDisconnected;
|
|
|
|
if (!FingerSettings)
|
|
{
|
|
Debug.LogWarning($"HVRInputManager.FingerSettings not assigned, creating defaults.");
|
|
|
|
FingerSettings = ScriptableObject.CreateInstance<HVRFingerSettings>();
|
|
FingerSettings.Reset();
|
|
}
|
|
|
|
if (!GrabHaptics)
|
|
{
|
|
Debug.LogWarning($"HVRInputManager.GrabHaptics not assigned.");
|
|
//GrabHaptics = ScriptableObject.CreateInstance<HVRGrabHaptics>();
|
|
//GrabHaptics.Reset();
|
|
}
|
|
|
|
CheckXRStatus();
|
|
if (IsVRInitialized)
|
|
{
|
|
Initialize();
|
|
}
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
_applicationExiting = true;
|
|
StopXR();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
UpdateDevices();
|
|
}
|
|
|
|
private void UpdateDevices()
|
|
{
|
|
UpdateDeviceInformation(LeftDevice);
|
|
UpdateLeftController(LeftDevice);
|
|
UpdateDeviceInformation(RightDevice);
|
|
UpdateRightController(RightDevice);
|
|
|
|
UpdateDeviceInformation(HMDDevice);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
//events aren't firing in build for some headsets?
|
|
if (!HMDDevice.isValid)
|
|
{
|
|
UpdateHMD(HMDDevice);
|
|
}
|
|
|
|
CheckHMDEvents();
|
|
CheckUserPresentEvents();
|
|
|
|
PreviousHMDActive = HMDActive;
|
|
PreviousUserPresent = UserPresent;
|
|
|
|
#if HVR_OCULUS
|
|
|
|
if (UseOVRInputs && ForceOVRInputUpdate && (LeftControllerType == HVRControllerType.Oculus || RightControllerType == HVRControllerType.Oculus))
|
|
{
|
|
HVROculusController.UpdateOVRInput();
|
|
}
|
|
#endif
|
|
|
|
//#if USING_XR_MANAGEMENT
|
|
// if (Input.GetKeyDown(KeyCode.X))
|
|
// {
|
|
// StartXR();
|
|
// }
|
|
|
|
|
|
//#elif !UNITY_2020_1_OR_NEWER
|
|
// if (Input.GetKeyDown(KeyCode.X))
|
|
// {
|
|
// StartLegacyVR();
|
|
// }
|
|
//#endif
|
|
// if (Input.GetKeyDown(KeyCode.Z))
|
|
// {
|
|
// StopXR();
|
|
// }
|
|
}
|
|
|
|
private void CheckHMDEvents()
|
|
{
|
|
if (!PreviousHMDActive && HMDActive)
|
|
{
|
|
if (!_isHMDFirstActivationReported)
|
|
{
|
|
HMDFirstActivation.Invoke();
|
|
_isHMDFirstActivationReported = true;
|
|
}
|
|
|
|
HMDActivated.Invoke();
|
|
}
|
|
else if (PreviousHMDActive && !HMDActive)
|
|
{
|
|
HMDDeactivated.Invoke();
|
|
}
|
|
}
|
|
|
|
private void CheckUserPresentEvents()
|
|
{
|
|
if (!PreviousUserPresent && UserPresent)
|
|
{
|
|
//Debug.Log($"user present");
|
|
UserSensed.Invoke();
|
|
}
|
|
else if (PreviousUserPresent && !UserPresent)
|
|
{
|
|
//Debug.Log($"user not present");
|
|
UserNotSensed.Invoke();
|
|
}
|
|
}
|
|
|
|
|
|
private void Start()
|
|
{
|
|
UpdateDevices();
|
|
}
|
|
|
|
|
|
private void OnDeviceDisconnected(InputDevice device)
|
|
{
|
|
if (_applicationExiting)
|
|
return;
|
|
Debug.Log($"disconnected {device.name},{device.manufacturer}");
|
|
UpdateDeviceInformation(device);
|
|
}
|
|
|
|
private void OnDeviceConnected(InputDevice device)
|
|
{
|
|
if (_applicationExiting)
|
|
return;
|
|
Debug.Log($"connected {device.name},{device.manufacturer}");
|
|
//FYI: steamvr causes this to fire even if the controller is off if the controller was previously on.
|
|
UpdateDeviceInformation(device);
|
|
}
|
|
|
|
private void OnDeviceConfigChanged(InputDevice device)
|
|
{
|
|
if (_applicationExiting)
|
|
return;
|
|
Debug.Log($"config changed {device.name},{device.manufacturer}");
|
|
UpdateDeviceInformation(device);
|
|
}
|
|
|
|
|
|
private void UpdateDeviceInformation(InputDevice device)
|
|
{
|
|
if (device.characteristics.HasFlag(InputDeviceCharacteristics.Controller))
|
|
{
|
|
if (device.characteristics.HasFlag(InputDeviceCharacteristics.Left))
|
|
{
|
|
LeftControllerName = device.name;
|
|
LeftManufacturer = device.manufacturer;
|
|
|
|
UpdateLeftController(device);
|
|
}
|
|
|
|
if (device.characteristics.HasFlag(InputDeviceCharacteristics.Right))
|
|
{
|
|
RightControllerName = device.name;
|
|
RightManufacturer = device.manufacturer;
|
|
|
|
UpdateRightController(device);
|
|
}
|
|
}
|
|
|
|
if (device.characteristics.HasFlag(InputDeviceCharacteristics.HeadMounted))
|
|
{
|
|
UpdateHMD(device);
|
|
}
|
|
}
|
|
|
|
public Vector3 hmdpos;
|
|
|
|
private void UpdateHMD(InputDevice device)
|
|
{
|
|
if (device.isValid)
|
|
{
|
|
HMDName = device.name;
|
|
HMDManufacturer = device.manufacturer;
|
|
|
|
HMDFeatures.Clear();
|
|
var inputFeatures = new List<UnityEngine.XR.InputFeatureUsage>();
|
|
if (device.TryGetFeatureUsages(inputFeatures))
|
|
{
|
|
foreach (var feature in inputFeatures)
|
|
{
|
|
HMDFeatures.Add($"{feature.name}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateRightController(InputDevice device)
|
|
{
|
|
//RightHasTrackPad = false;
|
|
|
|
RightFeatures.Clear();
|
|
var inputFeatures = new List<UnityEngine.XR.InputFeatureUsage>();
|
|
if (device.TryGetFeatureUsages(inputFeatures))
|
|
{
|
|
foreach (var feature in inputFeatures)
|
|
{
|
|
RightFeatures.Add($"{feature.name}");
|
|
if (feature.name == "Secondary2DAxis")
|
|
{
|
|
//RightHasTrackPad = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
RightControllerType = GetController(RightDevice.manufacturer?.ToLower(), RightDevice.name?.ToLower());
|
|
RightController = UpdateController(RightControllerType, device, HVRHandSide.Right);
|
|
if (device.isValid)
|
|
RightControllerConnected.Invoke(RightController);
|
|
}
|
|
|
|
private void UpdateLeftController(InputDevice device)
|
|
{
|
|
// LeftHasTrackPad = false;
|
|
|
|
LeftFeatures.Clear();
|
|
|
|
var inputFeatures = new List<UnityEngine.XR.InputFeatureUsage>();
|
|
if (device.TryGetFeatureUsages(inputFeatures))
|
|
{
|
|
foreach (var feature in inputFeatures)
|
|
{
|
|
LeftFeatures.Add($"{feature.name}");
|
|
if (feature.name == "Secondary2DAxis")
|
|
{
|
|
// LeftHasTrackPad = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
LeftControllerType = GetController(LeftDevice.manufacturer?.ToLower(), LeftDevice.name?.ToLower());
|
|
LeftController = UpdateController(LeftControllerType, device, HVRHandSide.Left);
|
|
if (device.isValid)
|
|
LeftControllerConnected.Invoke(LeftController);
|
|
}
|
|
|
|
private HVRController UpdateController(HVRControllerType controllerType, InputDevice device, HVRHandSide side)
|
|
{
|
|
HVRInputSettings inputMap = null;
|
|
Vector2 deadZone;
|
|
|
|
//var hasTrackPad = side == HVRHandSide.Left ? LeftHasTrackPad : RightHasTrackPad;
|
|
var wasNone = CurrentSDK == InputSDK.None;
|
|
GetSDK();
|
|
|
|
if (CurrentSDK == InputSDK.Oculus)
|
|
{
|
|
#if !HVR_OCULUS
|
|
Debug.LogWarning($"OVRInputs cannot be used because HVR_OCULUS define symbol is missing. Oculus Asset and the Hurricane Oculus Integration is required to use OVRInputs." +
|
|
$"Falling back to XRInputs.");
|
|
CurrentSDK = InputSDK.XRInput;
|
|
#endif
|
|
}
|
|
|
|
if (wasNone && CurrentSDK != InputSDK.None)
|
|
{
|
|
Debug.Log($"InputSDK : {CurrentSDK}");
|
|
}
|
|
|
|
switch (controllerType)
|
|
{
|
|
case HVRControllerType.ReverbG2:
|
|
deadZone = WMRDeadzone;
|
|
inputMap = ReverbG2InputMap;
|
|
break;
|
|
case HVRControllerType.WMR:
|
|
inputMap = WMRInputMap;
|
|
deadZone = WMRDeadzone;
|
|
break;
|
|
case HVRControllerType.Pico:
|
|
case HVRControllerType.Oculus:
|
|
inputMap = OculusInputMap;
|
|
deadZone = OculusDeadzone;
|
|
break;
|
|
case HVRControllerType.Vive:
|
|
deadZone = ViveDeadzone;
|
|
inputMap = ViveInputMap;
|
|
break;
|
|
case HVRControllerType.Knuckles:
|
|
deadZone = KnucklesDeadzone;
|
|
inputMap = KnucklesInputMap;
|
|
break;
|
|
case HVRControllerType.Cosmos:
|
|
inputMap = CosmosInputMap;
|
|
deadZone = CosmosDeadzone;
|
|
break;
|
|
default:
|
|
inputMap = OculusInputMap;
|
|
deadZone = OculusDeadzone;
|
|
break;
|
|
}
|
|
|
|
|
|
if (!inputMap)
|
|
{
|
|
inputMap = OculusInputMap;
|
|
}
|
|
|
|
|
|
HVRController controller = null;
|
|
|
|
if (side == HVRHandSide.Left)
|
|
{
|
|
if (LeftOculusController) LeftOculusController.enabled = false;
|
|
if (LeftSteamController) LeftSteamController.enabled = false;
|
|
if (LeftXRInputController) LeftXRInputController.enabled = false;
|
|
if (LeftInputSystemController) LeftInputSystemController.enabled = false;
|
|
}
|
|
else
|
|
{
|
|
if (RightOculusController) RightOculusController.enabled = false;
|
|
if (RightSteamController) RightSteamController.enabled = false;
|
|
if (RightXRInputController) RightXRInputController.enabled = false;
|
|
if (RightInputSystemController) RightInputSystemController.enabled = false;
|
|
}
|
|
|
|
switch (CurrentSDK)
|
|
{
|
|
case InputSDK.None:
|
|
case InputSDK.XRInput:
|
|
|
|
controller = side == HVRHandSide.Left ? LeftXRInputController : RightXRInputController;
|
|
|
|
HVRXRInputController xrController = controller as HVRXRInputController;
|
|
|
|
if (!controller)
|
|
{
|
|
xrController = gameObject.AddComponent<HVRXRInputController>();
|
|
controller = xrController;
|
|
|
|
if (side == HVRHandSide.Left)
|
|
{
|
|
LeftXRInputController = controller;
|
|
}
|
|
else
|
|
{
|
|
RightXRInputController = controller;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case InputSDK.Oculus:
|
|
|
|
#if HVR_OCULUS
|
|
controller = side == HVRHandSide.Left ? LeftOculusController : RightOculusController;
|
|
|
|
if (!controller)
|
|
{
|
|
var oculusController = gameObject.AddComponent<HVROculusController>();
|
|
controller = oculusController;
|
|
oculusController.OVRHaptics = OVRHaptics;
|
|
|
|
if (side == HVRHandSide.Left)
|
|
{
|
|
LeftOculusController = controller;
|
|
}
|
|
else
|
|
{
|
|
RightOculusController = controller;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
break;
|
|
case InputSDK.SteamVR:
|
|
|
|
#if HVR_STEAMVR
|
|
controller = side == HVRHandSide.Left ? LeftSteamController : RightSteamController;
|
|
if (!controller)
|
|
{
|
|
var steamController = gameObject.AddComponent<HVRSteamVRController>();
|
|
controller = steamController;
|
|
if (side == HVRHandSide.Left)
|
|
LeftSteamController = steamController;
|
|
else
|
|
RightSteamController = steamController;
|
|
}
|
|
|
|
#endif
|
|
break;
|
|
case InputSDK.InputSystem:
|
|
#if ENABLE_INPUT_SYSTEM
|
|
controller = side == HVRHandSide.Left ? LeftInputSystemController : RightInputSystemController;
|
|
if (!controller)
|
|
{
|
|
var inputSystemController = gameObject.AddComponent<HVRInputSystemController>();
|
|
inputSystemController.IsOpenXR = IsOpenXR;
|
|
controller = inputSystemController;
|
|
if (side == HVRHandSide.Left)
|
|
{
|
|
LeftInputSystemController = inputSystemController;
|
|
}
|
|
else
|
|
{
|
|
RightInputSystemController = inputSystemController;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
if (OverrideDeadzone)
|
|
{
|
|
deadZone = DeadzoneOverride;
|
|
}
|
|
|
|
if (controller != null)
|
|
{
|
|
controller.XRNode = side == HVRHandSide.Left ? XRNode.LeftHand : XRNode.RightHand;
|
|
controller.ThumbstickDeadZone = deadZone;
|
|
controller.Side = side;
|
|
controller.InputMap = inputMap;
|
|
controller.enabled = true;
|
|
controller.ControllerType = controllerType;
|
|
}
|
|
|
|
controller.FingerSettings = FingerSettings;
|
|
|
|
return controller;
|
|
}
|
|
|
|
public HVRController GetController(HVRHandSide side)
|
|
{
|
|
return side == HVRHandSide.Left ? LeftController : RightController;
|
|
}
|
|
|
|
public InputDevice GetDevice(HVRHandSide side)
|
|
{
|
|
if (side == HVRHandSide.Left) return LeftDevice;
|
|
return RightDevice;
|
|
}
|
|
|
|
private HVRControllerType GetController(string manufaturerToLower, string nameToLower)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(manufaturerToLower) && string.IsNullOrWhiteSpace(nameToLower))
|
|
return HVRControllerType.None;
|
|
|
|
if (manufaturerToLower == null)
|
|
{
|
|
manufaturerToLower = "";
|
|
}
|
|
|
|
if (nameToLower == null)
|
|
{
|
|
nameToLower = "";
|
|
}
|
|
|
|
manufaturerToLower = manufaturerToLower.ToLower();
|
|
nameToLower = nameToLower.ToLower();
|
|
|
|
if (IsKnuckles(manufaturerToLower, nameToLower))
|
|
{
|
|
return HVRControllerType.Knuckles;
|
|
}
|
|
|
|
|
|
if (manufaturerToLower.Contains(Oculus))
|
|
{
|
|
return HVRControllerType.Oculus;
|
|
}
|
|
|
|
if (nameToLower.Contains(Cosmos))
|
|
return HVRControllerType.Cosmos;
|
|
|
|
if (manufaturerToLower.Contains(HTC) || nameToLower.Contains(Vive))
|
|
return HVRControllerType.Vive;
|
|
|
|
var isWindowsMixedReality = manufaturerToLower.Contains(WindowsMR) || nameToLower.Contains(WMRController) || nameToLower.Contains(WindowsMROpenXR.ToLower()) ||
|
|
nameToLower.Contains(OpenXR_G2);
|
|
|
|
if (isWindowsMixedReality)
|
|
{
|
|
if (nameToLower.Contains(ReverbG2.ToLower()) || nameToLower.Contains(OpenXR_G2.ToLower()))
|
|
{
|
|
return HVRControllerType.ReverbG2;
|
|
}
|
|
|
|
var hmd = HMDName.ToLower();
|
|
if (hmd.Contains(Reverb) && hmd.Contains(G2))
|
|
{
|
|
return HVRControllerType.ReverbG2;
|
|
}
|
|
|
|
return HVRControllerType.WMR;
|
|
}
|
|
|
|
if (nameToLower.Contains(Pico))
|
|
return HVRControllerType.Pico;
|
|
return HVRControllerType.None;
|
|
}
|
|
|
|
private bool IsKnuckles(string manufacturer, string controllerName)
|
|
{
|
|
if (controllerName.Contains(Knuckles))
|
|
return true;
|
|
if (controllerName.Contains(KnucklesOpenXR.ToLower())) //openxr
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
public void GetSDK()
|
|
{
|
|
if (!IsVRInitialized)
|
|
{
|
|
CurrentSDK = InputSDK.None;
|
|
return;
|
|
}
|
|
|
|
if (CurrentSDK != InputSDK.None && !UseOVRInputs)
|
|
return;
|
|
|
|
var isXRPlugin = VRPlugin == VRMode.XRPlugin;
|
|
var steamvrFound = CheckForSteamVR();
|
|
var isLegacy = !isXRPlugin;
|
|
|
|
if (steamvrFound && isLegacy)
|
|
{
|
|
CurrentSDK = InputSDK.SteamVR;
|
|
return;
|
|
}
|
|
|
|
#if ENABLE_INPUT_SYSTEM
|
|
if (UseNewInputSystem)
|
|
{
|
|
CurrentSDK = InputSDK.InputSystem;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (isXRPlugin)
|
|
{
|
|
var loader = XRPluginLoader.ToLower();
|
|
if (loader == OculusLoader.ToLower())
|
|
{
|
|
CurrentSDK = UseOVRInputs ? InputSDK.Oculus : InputSDK.XRInput;
|
|
return;
|
|
}
|
|
|
|
#if ENABLE_INPUT_SYSTEM
|
|
var loaderNoSpace = loader.Replace(" ", "");
|
|
|
|
if (loader == OpenXRLoader.ToLower() || loaderNoSpace == OpenXRLoader.ToLower().Replace(" ", ""))
|
|
{
|
|
CurrentSDK = InputSDK.InputSystem;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (loader == OpenVRLoader.ToLower())
|
|
{
|
|
if (!steamvrFound)
|
|
{
|
|
Debug.LogWarning($"HVR: {OpenVRLoader} active without SteamVR installed or HVR_STEAMVR define set.");
|
|
}
|
|
|
|
CurrentSDK = InputSDK.SteamVR;
|
|
return;
|
|
}
|
|
|
|
|
|
CurrentSDK = InputSDK.XRInput;
|
|
return;
|
|
}
|
|
|
|
//legacy vr
|
|
if (LeftControllerType == HVRControllerType.Oculus || RightControllerType == HVRControllerType.Oculus)
|
|
{
|
|
CurrentSDK = UseOVRInputs ? InputSDK.Oculus : InputSDK.XRInput;
|
|
return;
|
|
}
|
|
|
|
CurrentSDK = InputSDK.XRInput;
|
|
}
|
|
|
|
private bool CheckForSteamVR()
|
|
{
|
|
#if !HVR_STEAMVR
|
|
return false;
|
|
#else
|
|
|
|
//OPENVR_DESKTOP and OPENVR_XR from HurricaneVR.Framework asmdef version defines
|
|
|
|
#if !OPENVR_DESKTOP && !OPENVR_XR
|
|
return false;
|
|
#else
|
|
#if OPENVR_DESKTOP
|
|
|
|
if (!Valve.VR.SteamVR.usingNativeSupport)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
try
|
|
{
|
|
EVRInitError error = EVRInitError.None;
|
|
EVRInitError error1 = EVRInitError.None;
|
|
EVRInitError error2 = EVRInitError.None;
|
|
|
|
|
|
OpenVR.GetGenericInterface(OpenVR.IVRCompositor_Version, ref error);
|
|
OpenVR.GetGenericInterface(OpenVR.IVROverlay_Version, ref error1);
|
|
OpenVR.GetGenericInterface(OpenVR.IVRInput_Version, ref error2);
|
|
|
|
if (error != EVRInitError.None || error1 != EVRInitError.None || error2 != EVRInitError.None)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
private void CheckXRStatus()
|
|
{
|
|
#if USING_XR_MANAGEMENT
|
|
|
|
VRPlugin = VRMode.XRPlugin;
|
|
|
|
XRPluginLoader = XRGeneralSettings.Instance.Manager.activeLoader?.name ?? "";
|
|
|
|
string loaders = "";
|
|
|
|
#if USING_XR_MANAGEMENT_V4
|
|
loaders = string.Join(",", XRGeneralSettings.Instance.Manager.activeLoaders.Select(e => e.name));
|
|
#else
|
|
loaders = string.Join(",", XRGeneralSettings.Instance.Manager.loaders.Select(e => e.name));
|
|
#endif
|
|
//Debug.Log($"{XRGeneralSettings.Instance.Manager.automaticLoading}");
|
|
Debug.Log($"XRGeneralSettings.Instance.Manager.isInitializationComplete {XRGeneralSettings.Instance.Manager.isInitializationComplete}");
|
|
Debug.Log($"XRPlugin Detected | XRSettings.enabled {XRSettings.enabled} | Loader : {XRPluginLoader} | Loaders Enabled: {loaders}");
|
|
|
|
XRPluginActive = XRSettings.enabled && XRGeneralSettings.Instance.Manager.isInitializationComplete;
|
|
#elif !UNITY_2020_1_OR_NEWER
|
|
var legacyDevices = "";
|
|
foreach (var t in XRSettings.supportedDevices)
|
|
{
|
|
legacyDevices += t + ",";
|
|
}
|
|
|
|
Debug.Log($"Legacy VR Detected | XRSettings.enabled {XRSettings.enabled} | SDKs: {legacyDevices}");
|
|
|
|
VRPlugin = VRMode.Legacy;
|
|
|
|
LegacyActive = XRSettings.enabled && !string.IsNullOrWhiteSpace(XRSettings.loadedDeviceName);
|
|
#endif
|
|
}
|
|
|
|
public bool StartXR()
|
|
{
|
|
#if USING_XR_MANAGEMENT
|
|
Debug.Log($"Attempting to start XR Plugin");
|
|
XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
|
|
XRPluginLoader = XRGeneralSettings.Instance.Manager.activeLoader?.name ?? "";
|
|
if (!string.IsNullOrEmpty(XRPluginLoader))
|
|
{
|
|
Debug.Log($"{XRPluginLoader} loader started. Starting subsystems");
|
|
XRGeneralSettings.Instance.Manager.StartSubsystems();
|
|
XRPluginActive = XRSettings.enabled && XRGeneralSettings.Instance.Manager.isInitializationComplete;
|
|
Initialize();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
XRPluginActive = false;
|
|
Debug.LogWarning($"Failed to start the xr plugin manually");
|
|
return false;
|
|
}
|
|
#else
|
|
Debug.LogWarning($"XR Plugin not detected. Cannot start XR.");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
|
|
public void StopXR()
|
|
{
|
|
#if USING_XR_MANAGEMENT
|
|
Debug.Log($"stopping xr plugin");
|
|
if (XRGeneralSettings.Instance && XRGeneralSettings.Instance.Manager &&
|
|
XRGeneralSettings.Instance.Manager.activeLoader != null && XRGeneralSettings.Instance.Manager.isInitializationComplete)
|
|
{
|
|
XRGeneralSettings.Instance.Manager.StopSubsystems();
|
|
XRGeneralSettings.Instance.Manager.DeinitializeLoader();
|
|
}
|
|
|
|
OnXRStopped();
|
|
#elif !UNITY_2020_1_OR_NEWER
|
|
Debug.Log($"stopping legacy xr");
|
|
OnXRStopped();
|
|
XRSettings.enabled = false;
|
|
if (XRSettings.supportedDevices.Contains(LegacyNone))
|
|
{
|
|
Debug.Log($"loading legacy 'None'");
|
|
XRSettings.LoadDeviceByName(LegacyNone);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starting requires a coroutine, the callback will be fired with true for success, and false for failure
|
|
/// </summary>
|
|
public void StartLegacyVR(Action<bool> callback = null)
|
|
{
|
|
#if USING_XR_MANAGEMENT
|
|
Debug.LogWarning($"XR Plugin Management is in use. Cannot load Legacy VR");
|
|
return;
|
|
#elif !UNITY_2020_1_OR_NEWER
|
|
StartCoroutine(LoadLegacy(callback));
|
|
#endif
|
|
}
|
|
|
|
|
|
#if!UNITY_2020_1_OR_NEWER
|
|
|
|
private IEnumerator LoadLegacy(Action<bool> callback)
|
|
{
|
|
foreach (var sdkName in XRSettings.supportedDevices)
|
|
{
|
|
if (sdkName == "None")
|
|
continue;
|
|
|
|
Debug.Log($"Attempting to start {sdkName} SDK");
|
|
XRSettings.LoadDeviceByName(sdkName);
|
|
yield return null;
|
|
if (XRSettings.loadedDeviceName == sdkName)
|
|
{
|
|
XRSettings.enabled = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (XRSettings.loadedDeviceName != null && XRSettings.loadedDeviceName != LegacyNone)
|
|
{
|
|
Debug.Log($"Legacy VR Initialized with {XRSettings.loadedDeviceName}. Updating Devices.");
|
|
LegacyActive = true;
|
|
Initialize();
|
|
if (callback != null)
|
|
{
|
|
callback(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (callback != null)
|
|
{
|
|
callback(false);
|
|
}
|
|
|
|
LegacyActive = false;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// sets up steamvr if necessary, set up controller objects, applies tracking origin
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
InitSteamVR();
|
|
UpdateDevices();
|
|
ApplyTrackingOrigin();
|
|
}
|
|
|
|
private void OnXRStopped()
|
|
{
|
|
StopSteamVR();
|
|
LegacyActive = false;
|
|
XRPluginActive = false;
|
|
CurrentSDK = InputSDK.None;
|
|
UpdateDevices();
|
|
}
|
|
|
|
private void InitSteamVR()
|
|
{
|
|
if (!InitializeSteamVR || !CheckForSteamVR())
|
|
return;
|
|
|
|
#if !HVR_STEAMVR
|
|
return;
|
|
#else
|
|
|
|
if (!IsVRInitialized)
|
|
{
|
|
Debug.LogWarning($"Cannot InitSteamVR. VR not initialized yet.");
|
|
return;
|
|
}
|
|
|
|
Valve.VR.SteamVR.Initialize();
|
|
|
|
|
|
if (InitializeSteamVRActions)
|
|
{
|
|
var actionSet = SteamVR_Input.GetActionSet(@"\actions\HVR");
|
|
actionSet.Activate();
|
|
}
|
|
|
|
IsSteamVR = true;
|
|
#endif
|
|
}
|
|
|
|
private void StopSteamVR()
|
|
{
|
|
#if !HVR_STEAMVR
|
|
return;
|
|
#else
|
|
|
|
Valve.VR.SteamVR.SafeDispose();
|
|
|
|
|
|
if (InitializeSteamVRActions)
|
|
{
|
|
var actionSet = SteamVR_Input.GetActionSet(@"\actions\HVR");
|
|
actionSet.Deactivate();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public void ApplyTrackingOrigin()
|
|
{
|
|
if (IsVRInitialized)
|
|
{
|
|
StartCoroutine(UpdateTrackingOrigin(TrackingSpace));
|
|
}
|
|
}
|
|
|
|
private IEnumerator UpdateTrackingOrigin(TrackingOriginModeFlags originFlags)
|
|
{
|
|
yield return null;
|
|
|
|
#if USING_XR_MANAGEMENT
|
|
var subsystems = new List<XRInputSubsystem>();
|
|
SubsystemManager.GetInstances<XRInputSubsystem>(subsystems);
|
|
Debug.Log("Found " + subsystems.Count + " input subsystems.");
|
|
|
|
for (int i = 0; i < subsystems.Count; i++)
|
|
{
|
|
if (subsystems[i].TrySetTrackingOriginMode(originFlags))
|
|
Debug.Log("Successfully set TrackingOriginMode to Floor");
|
|
else
|
|
Debug.Log("Failed to set TrackingOriginMode to Floor");
|
|
}
|
|
|
|
#elif !UNITY_2020_1_OR_NEWER
|
|
if (originFlags == TrackingOriginModeFlags.Floor)
|
|
{
|
|
#pragma warning disable 0618
|
|
if (XRDevice.SetTrackingSpaceType(TrackingSpaceType.RoomScale))
|
|
{
|
|
Debug.Log("Tracking change to RoomScale.");
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("Failed Tracking change to RoomScale.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
XRDevice.SetTrackingSpaceType(TrackingSpaceType.Stationary);
|
|
#pragma warning restore 0618
|
|
Debug.Log("Tracking change to stationary.");
|
|
}
|
|
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public enum VRMode
|
|
{
|
|
Legacy,
|
|
XRPlugin
|
|
}
|
|
} |