// ENABLE_VR is not defined on Game Core but the assembly is available with limited features when the XR module is enabled.
// These are the guards that Input System uses in GenericXRDevice.cs to define the XRController and XRHMD classes.
#if ENABLE_VR || UNITY_GAMECORE
#define XR_INPUT_DEVICES_AVAILABLE
#endif
using System.Collections.Generic;
using UnityEngine.InputSystem;
#if XR_INPUT_DEVICES_AVAILABLE
using UnityEngine.InputSystem.XR;
#endif
#if XR_HANDS_1_1_OR_NEWER
using UnityEngine.XR.Hands;
#endif
#if OPENXR_1_6_OR_NEWER
using UnityEngine.XR.OpenXR.Features.Interactions;
#endif
namespace UnityEngine.XR.Interaction.Toolkit.Inputs
{
///
/// Tracking status of the device in a unified format.
///
///
public struct TrackingStatus
{
///
/// Whether the device is available.
///
///
///
///
public bool isConnected { get; set; }
///
/// Whether the device is tracked.
///
///
///
///
///
///
public bool isTracked { get; set; }
///
/// Whether the device tracking values are valid.
///
///
///
///
///
///
public InputTrackingState trackingState { get; set; }
}
///
/// Provides methods for obtaining the tracking status of XR devices registered with Unity
/// without needing to know the input system it is sourced from.
///
///
/// XR devices may be added to Unity through different mechanisms, such as native XR devices registered
/// with the XR module, real or simulated devices registered with the Input System package, or the
/// Hand Tracking Subsystem of OpenXR.
///
public static class XRInputTrackingAggregator
{
///
/// Provides shortcut properties for describing XR module input device characteristics for common XR devices.
///
public static class Characteristics
{
///
/// HMD characteristics.
/// |
///
public static InputDeviceCharacteristics hmd => InputDeviceCharacteristics.HeadMounted | InputDeviceCharacteristics.TrackedDevice;
///
/// Eye gaze characteristics.
/// | |
///
public static InputDeviceCharacteristics eyeGaze => InputDeviceCharacteristics.HeadMounted | InputDeviceCharacteristics.EyeTracking | InputDeviceCharacteristics.TrackedDevice;
///
/// Left controller characteristics.
/// | | |
///
public static InputDeviceCharacteristics leftController => InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left;
///
/// Right controller characteristics.
/// | | |
///
public static InputDeviceCharacteristics rightController => InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right;
///
/// Left tracked hand characteristics.
/// | |
///
public static InputDeviceCharacteristics leftTrackedHand => InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Left;
///
/// Right tracked hand characteristics.
/// | |
///
public static InputDeviceCharacteristics rightTrackedHand => InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Right;
///
/// Left hand interaction characteristics, as in Hand Interaction (OpenXR).
/// | | |
///
internal static InputDeviceCharacteristics leftHandInteraction => InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Left;
///
/// Right hand interaction characteristics, as in Hand Interaction (OpenXR).
/// | | |
///
internal static InputDeviceCharacteristics rightHandInteraction => InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Right;
///
/// Left Microsoft hand interaction characteristics, as in Hololens Hand (OpenXR).
/// | | | |
///
internal static InputDeviceCharacteristics leftMicrosoftHandInteraction => InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left;
///
/// Right Microsoft hand interaction characteristics, as in Hololens Hand (OpenXR).
/// | | | |
///
internal static InputDeviceCharacteristics rightMicrosoftHandInteraction => InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right;
}
///
/// Temporary list used when getting the XR module devices.
///
static List s_XRInputDevices;
#if XR_HANDS_1_1_OR_NEWER
///
/// Temporary list used when getting the hand subsystems.
///
static List s_HandSubsystems;
#endif
///
/// Gets the tracking status of the HMD device for this frame.
///
/// Returns a snapshot of the tracking status for this frame.
public static TrackingStatus GetHMDStatus()
{
if (!Application.isPlaying)
return default;
#if XR_INPUT_DEVICES_AVAILABLE
// Try Input System devices
var currentDevice = InputSystem.InputSystem.GetDevice();
if (currentDevice != null)
return GetTrackingStatus(currentDevice);
#endif
// Try XR module devices
if (TryGetDeviceWithExactCharacteristics(Characteristics.hmd, out var xrInputDevice))
return GetTrackingStatus(xrInputDevice);
return default;
}
///
/// Gets the tracking status of the eye gaze device for this frame.
///
/// Returns a snapshot of the tracking status for this frame.
public static TrackingStatus GetEyeGazeStatus()
{
if (!Application.isPlaying)
return default;
#if OPENXR_1_6_OR_NEWER
// Try Input System devices
var currentDevice = InputSystem.InputSystem.GetDevice();
if (currentDevice != null)
return GetTrackingStatus(currentDevice);
#endif
// Try XR module devices
if (TryGetDeviceWithExactCharacteristics(Characteristics.eyeGaze, out var xrInputDevice))
return GetTrackingStatus(xrInputDevice);
return default;
}
///
/// Gets the tracking status of the left motion controller device for this frame.
///
/// Returns a snapshot of the tracking status for this frame.
public static TrackingStatus GetLeftControllerStatus()
{
if (!Application.isPlaying)
return default;
#if XR_INPUT_DEVICES_AVAILABLE
// Try Input System devices
var currentDevice = InputSystem.InputSystem.GetDevice(InputSystem.CommonUsages.LeftHand);
if (currentDevice != null)
return GetTrackingStatus(currentDevice);
#endif
// Try XR module devices
if (TryGetDeviceWithExactCharacteristics(Characteristics.leftController, out var xrInputDevice))
return GetTrackingStatus(xrInputDevice);
return default;
}
///
/// Gets the tracking status of the right motion controller device for this frame.
///
/// Returns a snapshot of the tracking status for this frame.
public static TrackingStatus GetRightControllerStatus()
{
if (!Application.isPlaying)
return default;
#if XR_INPUT_DEVICES_AVAILABLE
// Try Input System devices
var currentDevice = InputSystem.InputSystem.GetDevice(InputSystem.CommonUsages.RightHand);
if (currentDevice != null)
return GetTrackingStatus(currentDevice);
#endif
// Try XR module devices
if (TryGetDeviceWithExactCharacteristics(Characteristics.rightController, out var xrInputDevice))
return GetTrackingStatus(xrInputDevice);
return default;
}
///
/// Gets the tracking status of the left tracked hand device for this frame.
///
/// Returns a snapshot of the tracking status for this frame.
public static TrackingStatus GetLeftTrackedHandStatus()
{
if (!Application.isPlaying)
return default;
#if XR_HANDS_1_1_OR_NEWER
// Try XR Hand Subsystem devices
if (TryGetHandSubsystem(out var handSubsystem))
return GetTrackingStatus(handSubsystem.leftHand);
#if XR_INPUT_DEVICES_AVAILABLE
// Try Input System devices
var currentDevice = InputSystem.InputSystem.GetDevice(InputSystem.CommonUsages.LeftHand);
if (currentDevice != null)
return GetTrackingStatus(currentDevice);
#endif
#endif
// Try XR module devices
if (TryGetDeviceWithExactCharacteristics(Characteristics.leftTrackedHand, out var xrInputDevice))
return GetTrackingStatus(xrInputDevice);
return default;
}
///
/// Gets the tracking status of the right tracked hand device for this frame.
///
/// Returns a snapshot of the tracking status for this frame.
public static TrackingStatus GetRightTrackedHandStatus()
{
if (!Application.isPlaying)
return default;
#if XR_HANDS_1_1_OR_NEWER
// Try XR Hand Subsystem devices
if (TryGetHandSubsystem(out var handSubsystem))
return GetTrackingStatus(handSubsystem.rightHand);
#if XR_INPUT_DEVICES_AVAILABLE
// Try Input System devices
var currentDevice = InputSystem.InputSystem.GetDevice(InputSystem.CommonUsages.RightHand);
if (currentDevice != null)
return GetTrackingStatus(currentDevice);
#endif
#endif
// Try XR module devices
if (TryGetDeviceWithExactCharacteristics(Characteristics.rightTrackedHand, out var xrInputDevice))
return GetTrackingStatus(xrInputDevice);
return default;
}
///
/// Gets the tracking status of the left Meta Hand Tracking Aim device for this frame.
///
/// Returns a snapshot of the tracking status for this frame.
public static TrackingStatus GetLeftMetaAimHandStatus()
{
if (!Application.isPlaying)
return default;
#if XR_HANDS_1_1_OR_NEWER && XR_INPUT_DEVICES_AVAILABLE
// Try Input System devices
var currentDevice = InputSystem.InputSystem.GetDevice(InputSystem.CommonUsages.LeftHand);
if (currentDevice != null)
return GetTrackingStatus(currentDevice);
#endif
return default;
}
///
/// Gets the tracking status of the right Meta Hand Tracking Aim device for this frame.
///
/// Returns a snapshot of the tracking status for this frame.
public static TrackingStatus GetRightMetaAimHandStatus()
{
if (!Application.isPlaying)
return default;
#if XR_HANDS_1_1_OR_NEWER && XR_INPUT_DEVICES_AVAILABLE
// Try Input System devices
var currentDevice = InputSystem.InputSystem.GetDevice(InputSystem.CommonUsages.RightHand);
if (currentDevice != null)
return GetTrackingStatus(currentDevice);
#endif
return default;
}
#if XR_HANDS_1_1_OR_NEWER
///
/// Gets the first hand subsystem. If there are multiple, returns the first running subsystem.
///
/// When this method returns, contains the hand subsystem if found.
/// Returns if a hand subsystem was found. Otherwise, returns .
///
internal static bool TryGetHandSubsystem(out XRHandSubsystem handSubsystem)
{
s_HandSubsystems ??= new List();
SubsystemManager.GetSubsystems(s_HandSubsystems);
if (s_HandSubsystems.Count == 0)
{
handSubsystem = default;
return false;
}
if (s_HandSubsystems.Count > 1)
{
for (var i = 0; i < s_HandSubsystems.Count; ++i)
{
handSubsystem = s_HandSubsystems[i];
if (handSubsystem.running)
return true;
}
}
handSubsystem = s_HandSubsystems[0];
return true;
}
#endif
///
/// Gets the first active XR input device that matches the specified exactly.
///
/// A bitwise combination of the exact characteristics you are looking for.
/// When this method returns, contains the input device if a match was found.
/// Returns if a match was found. Otherwise, returns .
///
/// This function finds any input devices available to the XR Subsystem that match the specified
/// bitmask exactly. The function does not include devices that only provide some of the desired characteristics or capabilities.
///
///
internal static bool TryGetDeviceWithExactCharacteristics(InputDeviceCharacteristics desiredCharacteristics, out InputDevice inputDevice)
{
s_XRInputDevices ??= new List();
// The InputDevices.GetDevicesWithCharacteristics method does a bitwise comparison, not an equal check,
// so it may return devices that have additional characteristic flags (HMD characteristics is a subset
// of Eye Gaze characteristics, so this additional filtering ensures the correct device is returned if both are added).
// Instead, get all devices and use equals to make sure the characteristics matches exactly.
InputDevices.GetDevices(s_XRInputDevices);
for (var index = 0; index < s_XRInputDevices.Count; ++index)
{
inputDevice = s_XRInputDevices[index];
if (inputDevice.characteristics == desiredCharacteristics)
return true;
}
inputDevice = default;
return false;
}
static TrackingStatus GetTrackingStatus(TrackedDevice device)
{
if (device == null)
return default;
return new TrackingStatus
{
isConnected = device.added,
isTracked = device.isTracked.isPressed,
trackingState = (InputTrackingState)device.trackingState.value,
};
}
#if OPENXR_1_6_OR_NEWER
static TrackingStatus GetTrackingStatus(EyeGazeInteraction.EyeGazeDevice device)
{
if (device == null)
return default;
return new TrackingStatus
{
isConnected = device.added,
isTracked = device.pose.isTracked.isPressed,
trackingState = (InputTrackingState)device.pose.trackingState.value,
};
}
#endif
static TrackingStatus GetTrackingStatus(InputDevice device)
{
return new TrackingStatus
{
isConnected = device.isValid,
isTracked = device.TryGetFeatureValue(CommonUsages.isTracked, out var isTracked) && isTracked,
trackingState = device.TryGetFeatureValue(CommonUsages.trackingState, out var trackingState) ? trackingState : InputTrackingState.None,
};
}
#if XR_HANDS_1_1_OR_NEWER
static TrackingStatus GetTrackingStatus(XRHand hand)
{
return new TrackingStatus
{
isConnected = true,
isTracked = hand.isTracked,
trackingState = hand.isTracked ? InputTrackingState.Position | InputTrackingState.Rotation : InputTrackingState.None,
};
}
#endif
}
}