// 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 } }