Files
dungeons/Assets/UltimateXR/Runtime/Scripts/Devices/Integrations/UxrUnityXRControllerInput.cs
2024-08-06 21:58:35 +02:00

946 lines
46 KiB
C#

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UxrUnityXRControllerInput.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using UltimateXR.Core;
using UltimateXR.Core.Settings;
using UltimateXR.Haptics;
using UnityEngine;
using UnityEngine.XR;
#if ULTIMATEXR_UNITY_XR_OCULUS
using Unity.XR.Oculus;
#endif
namespace UltimateXR.Devices.Integrations
{
/// <summary>
/// Generic base class for left-right input devices that can be handled through the new
/// generic Unity XR input interface. Before, we had to manually support each SDK individually.
/// </summary>
public abstract partial class UxrUnityXRControllerInput : UxrControllerInput
{
#region Public Types & Data
/// <summary>
/// Gets list of controller names that the component can handle
/// </summary>
public abstract IEnumerable<string> ControllerNames { get; }
/// <summary>
/// We use this when we are implementing new controllers that we don't know the name of, in order to
/// show the controller names in the UxrDebugControllerPanel.
/// Returning true will register the controllers in <see cref="InputDevices_DeviceConnected" /> no
/// matter which input device gets connected. Then using the UxrDebugControllerPanel we can see which
/// devices got connected.
/// This is mostly useful for untethered devices that cannot be tested directly in Unity.
/// </summary>
public virtual bool ForceUseAlways => false;
#endregion
#region Public Overrides UxrControllerInput
/// <inheritdoc />
public override string LeftControllerName => _deviceLeft.isValid ? _deviceLeft.name : string.Empty;
/// <inheritdoc />
public override string RightControllerName => _deviceRight.isValid ? _deviceRight.name : string.Empty;
/// <inheritdoc />
public override bool IsControllerEnabled(UxrHandSide handSide)
{
return GetInputDevice(handSide).isValid;
}
/// <inheritdoc />
public override float GetInput1D(UxrHandSide handSide, UxrInput1D input1D, bool getIgnoredInput = false)
{
if (ShouldIgnoreInput(handSide, getIgnoredInput))
{
return 0.0f;
}
InputDevice inputDevice = GetInputDevice(handSide);
if (inputDevice.isValid)
{
float value;
switch (input1D)
{
case UxrInput1D.Grip:
if (inputDevice.TryGetFeatureValue(CommonUsages.grip, out value))
{
return value;
}
break;
case UxrInput1D.Trigger:
if (inputDevice.TryGetFeatureValue(CommonUsages.trigger, out value))
{
return value;
}
break;
case UxrInput1D.Trigger2: break;
}
}
return 0.0f;
}
/// <inheritdoc />
public override Vector2 GetInput2D(UxrHandSide handSide, UxrInput2D input2D, bool getIgnoredInput = false)
{
if (ShouldIgnoreInput(handSide, getIgnoredInput))
{
return Vector2.zero;
}
InputDevice inputDevice = GetInputDevice(handSide);
if (inputDevice.isValid)
{
Vector2 value;
switch (input2D)
{
case UxrInput2D.Joystick:
if (inputDevice.TryGetFeatureValue(CommonUsages.primary2DAxis, out value))
{
return FilterTwoAxesDeadZone(value, JoystickDeadZone);
}
break;
case UxrInput2D.Joystick2:
if (inputDevice.TryGetFeatureValue(CommonUsages.secondary2DAxis, out value))
{
return FilterTwoAxesDeadZone(value, JoystickDeadZone);
}
break;
}
}
return Vector2.zero;
}
/// <inheritdoc />
public override UxrControllerInputCapabilities GetControllerCapabilities(UxrHandSide handSide)
{
UxrControllerInputCapabilities capabilities = 0;
InputDevice inputDevice = GetInputDevice(handSide);
if (!inputDevice.isValid)
{
return capabilities;
}
if (inputDevice.TryGetHapticCapabilities(out HapticCapabilities hapticCapabilities))
{
if (hapticCapabilities.supportsBuffer)
{
capabilities |= UxrControllerInputCapabilities.HapticClips;
}
if (hapticCapabilities.supportsImpulse)
{
capabilities |= UxrControllerInputCapabilities.HapticImpulses;
}
}
return capabilities;
}
/// <inheritdoc />
public override void SendHapticFeedback(UxrHandSide handSide, UxrHapticClip hapticClip)
{
InputDevice inputDevice = GetInputDevice(handSide);
if (!inputDevice.isValid)
{
return;
}
if (hapticClip == null)
{
return;
}
if (!inputDevice.TryGetHapticCapabilities(out HapticCapabilities hapticCapabilities))
{
return;
}
if (hapticClip.Clip == null || !hapticCapabilities.supportsBuffer)
{
SendHapticFeedback(handSide,
hapticClip.FallbackClipType,
hapticClip.FallbackAmplitude,
hapticClip.FallbackDurationSeconds,
hapticClip.HapticMode);
return;
}
// Create haptics clip from audio
byte[] hapticBuffer = CreateHapticBufferFromAudioClip(inputDevice, hapticClip.Clip);
if (hapticBuffer == null)
{
SendHapticFeedback(handSide,
hapticClip.FallbackClipType,
hapticClip.FallbackAmplitude,
hapticClip.FallbackDurationSeconds,
hapticClip.HapticMode);
return;
}
// Readjust amplitude?
if (Mathf.Approximately(hapticClip.ClipAmplitude, 1.0f) == false)
{
for (int i = 0; i < hapticBuffer.Length; ++i)
{
hapticBuffer[i] = (byte)Mathf.Clamp(Mathf.RoundToInt(hapticBuffer[i] * hapticClip.ClipAmplitude), 0, 255);
}
}
// Send using replace or mix
uint channel = 0;
if (hapticClip.HapticMode == UxrHapticMode.Mix && hapticCapabilities.numChannels > 0)
{
if (handSide == UxrHandSide.Left)
{
_leftHapticChannel = (_leftHapticChannel + 1) % hapticCapabilities.numChannels;
channel = _leftHapticChannel;
}
else
{
_rightHapticChannel = (_rightHapticChannel + 1) % hapticCapabilities.numChannels;
channel = _rightHapticChannel;
}
}
else
{
inputDevice.StopHaptics();
_leftHapticChannel = 0;
_rightHapticChannel = 0;
}
inputDevice.SendHapticBuffer(channel, hapticBuffer);
}
/// <inheritdoc />
public override void SendHapticFeedback(UxrHandSide handSide,
float frequency,
float amplitude,
float durationSeconds,
UxrHapticMode hapticMode = UxrHapticMode.Mix)
{
InputDevice inputDevice = GetInputDevice(handSide);
if (!inputDevice.isValid)
{
return;
}
if (!inputDevice.TryGetHapticCapabilities(out HapticCapabilities hapticCapabilities))
{
return;
}
// Setup using replace or mix
uint channel = 0;
if (hapticMode == UxrHapticMode.Mix && hapticCapabilities.numChannels > 0)
{
if (handSide == UxrHandSide.Left)
{
_leftHapticChannel = (_leftHapticChannel + 1) % hapticCapabilities.numChannels;
channel = _leftHapticChannel;
}
else
{
_rightHapticChannel = (_rightHapticChannel + 1) % hapticCapabilities.numChannels;
channel = _rightHapticChannel;
}
}
else
{
inputDevice.StopHaptics();
_leftHapticChannel = 0;
_rightHapticChannel = 0;
}
// Send
if (hapticCapabilities.supportsBuffer)
{
byte[] samples = new byte[(int)(hapticCapabilities.bufferFrequencyHz * durationSeconds)];
int steps = frequency > 0.0f ? Mathf.RoundToInt(hapticCapabilities.bufferFrequencyHz / frequency) : -1;
byte sample = (byte)Mathf.Clamp(amplitude * 255.0f, 0, 255.0f);
for (int i = 0; i < samples.Length; ++i)
{
if (steps < 2)
{
samples[i] = sample;
}
else
{
samples[i] = i % steps == 0 ? sample : (byte)0;
}
}
inputDevice.SendHapticBuffer(channel, samples);
}
else if (hapticCapabilities.supportsImpulse)
{
inputDevice.SendHapticImpulse(channel, amplitude, durationSeconds);
}
}
/// <inheritdoc />
public override void StopHapticFeedback(UxrHandSide handSide)
{
InputDevice inputDevice = GetInputDevice(handSide);
if (!inputDevice.isValid)
{
return;
}
inputDevice.StopHaptics();
}
#endregion
#region Unity
/// <summary>
/// Initializes variables and subscribes to events.
/// If the controllers were already initialized, enables the component. Otherwise it begins disabled until devices are
/// connected.
/// </summary>
protected override void Awake()
{
base.Awake();
_leftHapticChannel = 0;
_rightHapticChannel = 0;
if (enabled)
{
InputDevices.deviceConnected += InputDevices_DeviceConnected;
InputDevices.deviceDisconnected += InputDevices_DeviceDisconnected;
// Check if the device is already connected. This may happen if a new scene was loaded, because
// the connection events were already triggered and processed. We should have them registered in
// our static fields.
_deviceLeft = s_activeInputDevices.FirstOrDefault(d => ControllerNames.Any(n => string.Equals(d.name, n)) && IsLeftController(d));
_deviceRight = s_activeInputDevices.FirstOrDefault(d => ControllerNames.Any(n => string.Equals(d.name, n)) && IsRightController(d));
List<InputDevice> devices = new List<InputDevice>();
InputDevices.GetDevices(devices);
if (!_deviceLeft.isValid)
{
_deviceLeft = devices.FirstOrDefault(d => ControllerNames.Any(n => string.Equals(d.name, n)) && IsLeftController(d));
}
if (!_deviceRight.isValid)
{
_deviceRight = devices.FirstOrDefault(d => ControllerNames.Any(n => string.Equals(d.name, n)) && IsRightController(d));
}
enabled = _deviceLeft.isValid || _deviceRight.isValid;
RaiseConnectOnStart = enabled;
}
}
/// <summary>
/// Unsubscribes from device events.
/// </summary>
protected override void OnDestroy()
{
base.OnDestroy();
InputDevices.deviceConnected -= InputDevices_DeviceConnected;
InputDevices.deviceDisconnected -= InputDevices_DeviceDisconnected;
}
#endregion
#region Event Handling Methods
/// <summary>
/// Event called when a device is connected. Check for compatible devices.
/// </summary>
/// <param name="inputDevice">The device that was connected</param>
private void InputDevices_DeviceConnected(InputDevice inputDevice)
{
// Check if device is compatible with component
if (ForceUseAlways || ControllerNames.Any(n => string.Equals(n, inputDevice.name)))
{
// Found compatible device. Look for features.
List<InputFeatureUsage> listFeatures = new List<InputFeatureUsage>();
bool isController = false;
// Check for controllers and side
if (IsLeftController(inputDevice))
{
// Left controller
if (UxrGlobalSettings.Instance.LogLevelDevices >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.DevicesModule} {InputClassName}::{nameof(InputDevices_DeviceConnected)}: Device name {inputDevice.name} was registered by {InputClassName} and is being processed as left controller. InputDevice.isValid={inputDevice.isValid}");
}
_deviceLeft = inputDevice;
isController = true;
}
else if (IsRightController(inputDevice))
{
// Right controller
if (UxrGlobalSettings.Instance.LogLevelDevices >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.DevicesModule} {InputClassName}::{nameof(InputDevices_DeviceConnected)}: Device name {inputDevice.name} was registered by {InputClassName} and is being processed as right controller. InputDevice.isValid={inputDevice.isValid}");
}
_deviceRight = inputDevice;
isController = true;
}
if (isController)
{
// Register active device
s_activeInputDevices.Add(inputDevice);
if (!enabled)
{
// Component is disabled. Enable it and send Connected event.
enabled = true;
OnDeviceConnected(new UxrDeviceConnectEventArgs(true));
}
}
}
else
{
// Check for controllers and side
if (IsLeftController(inputDevice))
{
// Left controller
if (UxrGlobalSettings.Instance.LogLevelDevices >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.DevicesModule} {InputClassName}::{nameof(InputDevices_DeviceConnected)}: Left device connected unknown: {inputDevice.name}. InputDevice.isValid={inputDevice.isValid}");
}
}
else if (IsRightController(inputDevice))
{
// Right controller
if (UxrGlobalSettings.Instance.LogLevelDevices >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.DevicesModule} {InputClassName}::{nameof(InputDevices_DeviceConnected)}: Right device connected unknown: {inputDevice.name}. InputDevice.isValid={inputDevice.isValid}");
}
}
}
}
/// <summary>
/// Event called when a device is disconnected. We use it to update our internal lists.
/// </summary>
/// <param name="inputDevice">The device that was disconnected</param>
private void InputDevices_DeviceDisconnected(InputDevice inputDevice)
{
// Check if device is compatible with component
if (ForceUseAlways || ControllerNames.Any(n => string.Equals(n, inputDevice.name)))
{
if (string.Equals(inputDevice.serialNumber, _deviceLeft.serialNumber) || string.Equals(inputDevice.serialNumber, _deviceRight.serialNumber))
{
if (UxrGlobalSettings.Instance.LogLevelDevices >= UxrLogLevel.Relevant)
{
Debug.Log($"{UxrConstants.DevicesModule} {InputClassName}::{nameof(InputDevices_DeviceDisconnected)}: Device name {inputDevice.name} was registered by {InputClassName} and is being disconnected. InputDevice.isValid={inputDevice.isValid}");
}
}
// Unregister device
s_activeInputDevices.RemoveAll(i => string.Equals(i.name, inputDevice.name));
// If last device was disconnected, disable component. Component will be re-enabled using connection event.
if (enabled && !_deviceLeft.isValid && !_deviceRight.isValid)
{
enabled = false;
OnDeviceConnected(new UxrDeviceConnectEventArgs(false));
}
}
}
#endregion
#region Protected Overrides UxrControllerInput
/// <summary>
/// Updates the input state. This should not be called by the user since it is called by the framework already.
/// </summary>
protected override void UpdateInput()
{
base.UpdateInput();
bool buttonPressTriggerLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Trigger, ButtonContact.Press);
bool buttonPressTriggerRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Trigger, ButtonContact.Press);
bool buttonPressTrigger2Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Trigger2, ButtonContact.Press);
bool buttonPressTrigger2Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Trigger2, ButtonContact.Press);
bool buttonPressJoystickLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Joystick, ButtonContact.Press);
bool buttonPressJoystickRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Joystick, ButtonContact.Press);
bool buttonPressJoystick2Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Joystick2, ButtonContact.Press);
bool buttonPressJoystick2Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Joystick2, ButtonContact.Press);
bool buttonPressButton1Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Button1, ButtonContact.Press);
bool buttonPressButton1Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Button1, ButtonContact.Press);
bool buttonPressButton2Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Button2, ButtonContact.Press);
bool buttonPressButton2Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Button2, ButtonContact.Press);
bool buttonPressButton3Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Button3, ButtonContact.Press);
bool buttonPressButton3Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Button3, ButtonContact.Press);
bool buttonPressButton4Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Button4, ButtonContact.Press);
bool buttonPressButton4Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Button4, ButtonContact.Press);
bool buttonPressBumperLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Bumper, ButtonContact.Press);
bool buttonPressBumperRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Bumper, ButtonContact.Press);
bool buttonPressBumper2Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Bumper2, ButtonContact.Press);
bool buttonPressBumper2Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Bumper2, ButtonContact.Press);
bool buttonPressMenuLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Menu, ButtonContact.Press);
bool buttonPressMenuRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Menu, ButtonContact.Press);
bool buttonPressGripLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Grip, ButtonContact.Press);
bool buttonPressGripRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Grip, ButtonContact.Press);
bool buttonPressThumbCapSenseLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.ThumbCapSense, ButtonContact.Press);
bool buttonPressThumbCapSenseRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.ThumbCapSense, ButtonContact.Press);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Trigger, buttonPressTriggerLeft);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Trigger, buttonPressTriggerRight);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Trigger2, buttonPressTrigger2Left);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Trigger2, buttonPressTrigger2Right);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Joystick, buttonPressJoystickLeft);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Joystick, buttonPressJoystickRight);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Joystick2, buttonPressJoystick2Left);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Joystick2, buttonPressJoystick2Right);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Button1, buttonPressButton1Left);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Button1, buttonPressButton1Right);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Button2, buttonPressButton2Left);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Button2, buttonPressButton2Right);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Button3, buttonPressButton3Left);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Button3, buttonPressButton3Right);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Button4, buttonPressButton4Left);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Button4, buttonPressButton4Right);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Bumper, buttonPressBumperLeft);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Bumper, buttonPressBumperRight);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Bumper2, buttonPressBumper2Left);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Bumper2, buttonPressBumper2Right);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Menu, buttonPressMenuLeft);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Menu, buttonPressMenuRight);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.Grip, buttonPressGripLeft);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.Grip, buttonPressGripRight);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.ThumbCapSense, buttonPressThumbCapSenseLeft);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.ThumbCapSense, buttonPressThumbCapSenseRight);
bool buttonTouchTriggerLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Trigger, ButtonContact.Touch);
bool buttonTouchTriggerRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Trigger, ButtonContact.Touch);
bool buttonTouchTrigger2Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Trigger2, ButtonContact.Touch);
bool buttonTouchTrigger2Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Trigger2, ButtonContact.Touch);
bool buttonTouchJoystickLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Joystick, ButtonContact.Touch);
bool buttonTouchJoystickRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Joystick, ButtonContact.Touch);
bool buttonTouchJoystick2Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Joystick2, ButtonContact.Touch);
bool buttonTouchJoystick2Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Joystick2, ButtonContact.Touch);
bool buttonTouchButton1Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Button1, ButtonContact.Touch);
bool buttonTouchButton1Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Button1, ButtonContact.Touch);
bool buttonTouchButton2Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Button2, ButtonContact.Touch);
bool buttonTouchButton2Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Button2, ButtonContact.Touch);
bool buttonTouchButton3Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Button3, ButtonContact.Touch);
bool buttonTouchButton3Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Button3, ButtonContact.Touch);
bool buttonTouchButton4Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Button4, ButtonContact.Touch);
bool buttonTouchButton4Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Button4, ButtonContact.Touch);
bool buttonTouchBumperLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Bumper, ButtonContact.Touch);
bool buttonTouchBumperRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Bumper, ButtonContact.Touch);
bool buttonTouchBumper2Left = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Bumper2, ButtonContact.Touch);
bool buttonTouchBumper2Right = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Bumper2, ButtonContact.Touch);
bool buttonTouchMenuLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Menu, ButtonContact.Touch);
bool buttonTouchMenuRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Menu, ButtonContact.Touch);
bool buttonTouchGripLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.Grip, ButtonContact.Touch);
bool buttonTouchGripRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.Grip, ButtonContact.Touch);
bool buttonTouchThumbCapSenseLeft = HasButtonContact(UxrHandSide.Left, UxrInputButtons.ThumbCapSense, ButtonContact.Touch);
bool buttonTouchThumbCapSenseRight = HasButtonContact(UxrHandSide.Right, UxrInputButtons.ThumbCapSense, ButtonContact.Touch);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Trigger, buttonTouchTriggerLeft);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Trigger, buttonTouchTriggerRight);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Trigger2, buttonTouchTrigger2Left);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Trigger2, buttonTouchTrigger2Right);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Joystick, buttonTouchJoystickLeft);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Joystick, buttonTouchJoystickRight);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Joystick2, buttonTouchJoystick2Left);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Joystick2, buttonTouchJoystick2Right);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Button1, buttonTouchButton1Left);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Button1, buttonTouchButton1Right);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Button2, buttonTouchButton2Left);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Button2, buttonTouchButton2Right);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Button3, buttonTouchButton3Left);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Button3, buttonTouchButton3Right);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Button4, buttonTouchButton4Left);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Button4, buttonTouchButton4Right);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Bumper, buttonTouchBumperLeft);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Bumper, buttonTouchBumperRight);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Bumper2, buttonTouchBumper2Left);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Bumper2, buttonTouchBumper2Right);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Menu, buttonTouchMenuLeft);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Menu, buttonTouchMenuRight);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.Grip, buttonTouchGripLeft);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.Grip, buttonTouchGripRight);
SetButtonFlags(ButtonFlags.TouchFlagsLeft, UxrInputButtons.ThumbCapSense, buttonTouchThumbCapSenseLeft);
SetButtonFlags(ButtonFlags.TouchFlagsRight, UxrInputButtons.ThumbCapSense, buttonTouchThumbCapSenseRight);
Vector2 leftJoystick = GetInput2D(UxrHandSide.Left, UxrInput2D.Joystick, true);
Vector2 leftDPad = leftJoystick; // Mapped to joystick by default
if (leftJoystick != Vector2.zero && leftJoystick.magnitude > AnalogAsDPadThreshold)
{
float joystickAngle = Input2DToAngle(leftJoystick);
bool touchPadFix = !MainJoystickIsTouchpad || buttonPressJoystickLeft;
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.JoystickLeft, touchPadFix && IsInput2dDPadLeft(joystickAngle));
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.JoystickRight, touchPadFix && IsInput2dDPadRight(joystickAngle));
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.JoystickUp, touchPadFix && IsInput2dDPadUp(joystickAngle));
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.JoystickDown, touchPadFix && IsInput2dDPadDown(joystickAngle));
}
else
{
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.JoystickLeft, false);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.JoystickRight, false);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.JoystickUp, false);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.JoystickDown, false);
}
if (leftDPad != Vector2.zero && leftDPad.magnitude > AnalogAsDPadThreshold)
{
float dPadAngle = Input2DToAngle(leftDPad);
bool touchPadFix = !MainJoystickIsTouchpad || buttonPressJoystickLeft;
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.DPadLeft, touchPadFix && IsInput2dDPadLeft(dPadAngle));
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.DPadRight, touchPadFix && IsInput2dDPadRight(dPadAngle));
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.DPadUp, touchPadFix && IsInput2dDPadUp(dPadAngle));
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.DPadDown, touchPadFix && IsInput2dDPadDown(dPadAngle));
}
else
{
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.DPadLeft, false);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.DPadRight, false);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.DPadUp, false);
SetButtonFlags(ButtonFlags.PressFlagsLeft, UxrInputButtons.DPadDown, false);
}
Vector2 rightJoystick = GetInput2D(UxrHandSide.Right, UxrInput2D.Joystick, true);
Vector2 rightDPad = rightJoystick; // Mapped to joystick by default
if (rightJoystick != Vector2.zero && rightJoystick.magnitude > AnalogAsDPadThreshold)
{
float joystickAngle = Input2DToAngle(rightJoystick);
bool touchPadFix = !MainJoystickIsTouchpad || buttonPressJoystickRight;
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.JoystickLeft, touchPadFix && IsInput2dDPadLeft(joystickAngle));
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.JoystickRight, touchPadFix && IsInput2dDPadRight(joystickAngle));
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.JoystickUp, touchPadFix && IsInput2dDPadUp(joystickAngle));
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.JoystickDown, touchPadFix && IsInput2dDPadDown(joystickAngle));
}
else
{
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.JoystickLeft, false);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.JoystickRight, false);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.JoystickUp, false);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.JoystickDown, false);
}
if (rightDPad != Vector2.zero && rightDPad.magnitude > AnalogAsDPadThreshold)
{
float dPadAngle = Input2DToAngle(rightDPad);
bool touchPadFix = !MainJoystickIsTouchpad || buttonPressJoystickRight;
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.DPadLeft, touchPadFix && IsInput2dDPadLeft(dPadAngle));
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.DPadRight, touchPadFix && IsInput2dDPadRight(dPadAngle));
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.DPadUp, touchPadFix && IsInput2dDPadUp(dPadAngle));
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.DPadDown, touchPadFix && IsInput2dDPadDown(dPadAngle));
}
else
{
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.DPadLeft, false);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.DPadRight, false);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.DPadUp, false);
SetButtonFlags(ButtonFlags.PressFlagsRight, UxrInputButtons.DPadDown, false);
}
}
#endregion
#region Protected Methods
/// <summary>
/// Checks whether a non-standard button in a controller is currently being touched or pressed.
/// </summary>
/// <param name="handSide">Which controller side to check</param>
/// <param name="button">Button to check</param>
/// <param name="buttonContact">Type of contact to check for (touch or press)</param>
/// <returns>Boolean telling whether the specified button has contact</returns>
protected virtual bool HasButtonContactOther(UxrHandSide handSide, UxrInputButtons button, ButtonContact buttonContact)
{
return false;
}
/// <summary>
/// Gets the input device interface in Unity's input system for a given hand.
/// Usually if it is a left+right setup it will give a list with a single entry but the system is very generic
/// so it is prepared to handle different setups.
/// Normally we get the list and just use the first entry if available.
/// </summary>
/// <param name="handSide">Hand to get the input devices for</param>
/// <returns><see cref="InputDevice" /> representing the input device</returns>
protected InputDevice GetInputDevice(UxrHandSide handSide)
{
return handSide == UxrHandSide.Left ? _deviceLeft : _deviceRight;
}
/// <summary>
/// Using an audio file, creates a haptic samples buffer that can be sent for feedback.
/// This code is based on the Oculus SDK (OVRHaptics.cs).
/// </summary>
/// <param name="inputDevice">Unity input device that will be the feedback target</param>
/// <param name="audioClip">Audio clip whose audio sample will be used to create haptics</param>
/// <returns>Buffer that can be sent to the device as haptic feedback</returns>
protected byte[] CreateHapticBufferFromAudioClip(InputDevice inputDevice, AudioClip audioClip)
{
float[] audioData = new float[audioClip.samples * audioClip.channels];
audioClip.GetData(audioData, 0);
if (!inputDevice.TryGetHapticCapabilities(out HapticCapabilities hapticCapabilities) || !hapticCapabilities.supportsBuffer)
{
return null;
}
if (hapticCapabilities.bufferFrequencyHz == 0)
{
return null;
}
double stepSizePrecise = (audioClip.frequency + 1e-6) / hapticCapabilities.bufferFrequencyHz;
if (stepSizePrecise < 1.0)
{
return null;
}
int stepSize = (int)stepSizePrecise;
double stepSizeError = stepSizePrecise - stepSize;
int length = audioData.Length;
double accumStepSizeError = 0.0f;
byte[] samples = new byte[length];
int i = 0;
int s = 0;
while (i < length)
{
byte sample = (byte)(Mathf.Clamp01(Mathf.Abs(audioData[i])) * byte.MaxValue);
if (s < samples.Length)
{
samples[s] = sample;
s++;
}
else
{
break;
}
i += stepSize * audioClip.channels;
accumStepSizeError += stepSizeError;
if ((int)accumStepSizeError > 0)
{
i += (int)accumStepSizeError * audioClip.channels;
accumStepSizeError = accumStepSizeError - (int)accumStepSizeError;
}
}
return samples;
}
#endregion
#region Private Methods
/// <summary>
/// Gets if the given input device is a left side VR controller
/// </summary>
/// <param name="inputDevice">Device to check</param>
/// <returns>Whether the given input device is a left side VR controller</returns>
private static bool IsLeftController(InputDevice inputDevice)
{
return inputDevice.characteristics.HasFlag(InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left);
}
/// <summary>
/// Gets if the given input device is a right side VR controller
/// </summary>
/// <param name="inputDevice">Device to check</param>
/// <returns>Whether the given input device is a right side VR controller</returns>
private static bool IsRightController(InputDevice inputDevice)
{
return inputDevice.characteristics.HasFlag(InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right);
}
/// <summary>
/// Checks whether the given button in a controller is currently being touched or pressed.
/// </summary>
/// <param name="handSide">Which controller side to check</param>
/// <param name="button">Button to check</param>
/// <param name="buttonContact">Type of contact to check for (touch or press)</param>
/// <returns>Boolean telling whether the specified button has contact</returns>
private bool HasButtonContact(UxrHandSide handSide, UxrInputButtons button, ButtonContact buttonContact)
{
InputDevice inputDevice = GetInputDevice(handSide);
if (!inputDevice.isValid)
{
return false;
}
if (button == UxrInputButtons.Joystick)
{
var featureUsage = buttonContact == ButtonContact.Press ? CommonUsages.primary2DAxisClick : CommonUsages.primary2DAxisTouch;
if (inputDevice.TryGetFeatureValue(featureUsage, out bool value))
{
return value;
}
}
else if (button == UxrInputButtons.Joystick2)
{
return false;
}
else if (button == UxrInputButtons.Trigger)
{
if (buttonContact == ButtonContact.Touch)
{
#if ULTIMATEXR_UNITY_XR_OCULUS
if (inputDevice.TryGetFeatureValue(OculusUsages.indexTouch, out bool valueTouch))
{
return valueTouch;
}
#endif
}
if (inputDevice.TryGetFeatureValue(CommonUsages.trigger, out float valueFloat))
{
// We try getting the float value first because in analog buttons like the oculus it will trigger too early with the bool version.
return valueFloat > AnalogAsDPadThreshold;
}
if (inputDevice.TryGetFeatureValue(CommonUsages.triggerButton, out bool value))
{
return value;
}
}
else if (button == UxrInputButtons.Trigger2)
{
return false;
}
else if (button == UxrInputButtons.Grip)
{
if (inputDevice.TryGetFeatureValue(CommonUsages.gripButton, out bool value))
{
return value;
}
}
else if (button == UxrInputButtons.Button1)
{
var featureUsage = buttonContact == ButtonContact.Press ? CommonUsages.primaryButton : CommonUsages.primaryTouch;
if (inputDevice.TryGetFeatureValue(featureUsage, out bool value))
{
return value;
}
}
else if (button == UxrInputButtons.Button2)
{
var featureUsage = buttonContact == ButtonContact.Press ? CommonUsages.secondaryButton : CommonUsages.secondaryTouch;
if (inputDevice.TryGetFeatureValue(featureUsage, out bool value))
{
return value;
}
}
else if (button == UxrInputButtons.Menu)
{
if (inputDevice.TryGetFeatureValue(CommonUsages.menuButton, out bool value))
{
return value;
}
}
else if (button == UxrInputButtons.ThumbCapSense)
{
if (buttonContact == ButtonContact.Press)
{
#if ULTIMATEXR_UNITY_XR_OCULUS
if (inputDevice.TryGetFeatureValue(OculusUsages.thumbrest, out bool value))
{
return value;
}
#endif
}
if (buttonContact == ButtonContact.Touch)
{
#if ULTIMATEXR_UNITY_XR_OCULUS
if (inputDevice.TryGetFeatureValue(OculusUsages.thumbTouch, out bool value))
{
return value;
}
#endif
}
}
else if (button == UxrInputButtons.IndexCapSense)
{
}
else if (button == UxrInputButtons.MiddleCapSense)
{
}
else if (button == UxrInputButtons.RingCapSense)
{
}
else if (button == UxrInputButtons.LittleCapSense)
{
}
return false;
}
#endregion
#region Private Types & Data
private string InputClassName => GetType().Name;
private static readonly List<InputDevice> s_activeInputDevices = new List<InputDevice>();
private InputDevice _deviceLeft;
private InputDevice _deviceRight;
private uint _leftHapticChannel;
private uint _rightHapticChannel;
#endregion
}
}