using System.Collections.Generic; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; using UnityEngine.Scripting; using UnityEngine.XR.OpenXR.Input; #if UNITY_EDITOR using UnityEditor; #endif #if USE_INPUT_SYSTEM_POSE_CONTROL using PoseControl = UnityEngine.InputSystem.XR.PoseControl; #else using PoseControl = UnityEngine.XR.OpenXR.Input.PoseControl; #endif namespace UnityEngine.XR.OpenXR.Features.Interactions { /// /// This enables the use of Microsoft hand interaction profiles in OpenXR. It enables XR_MSFT_hand_interaction in the underyling runtime. /// This creates a new with the characteristic. /// #if UNITY_EDITOR [UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Microsoft Hand Interaction Profile", BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android }, Company = "Unity", Desc = "Allows for mapping input to the hand interaction profile. Will register the controller map for hand interaction if enabled.", DocumentationLink = Constants.k_DocumentationManualURL + "features/microsofthandinteraction.html", Version = "0.0.1", OpenxrExtensionStrings = extensionString, Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction, FeatureId = featureId)] #endif public class MicrosoftHandInteraction : OpenXRInteractionFeature { /// /// The feature id string. This is used to give the feature a well known id for reference. /// public const string featureId = "com.unity.openxr.feature.input.handtracking"; /// /// An Input System device based off the hand interaction profile in the Hand Interaction Extension. Enabled through . /// [Preserve, InputControlLayout(displayName = "Hololens Hand (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })] public class HoloLensHand : XRController { /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "PrimaryAxis")] public AxisControl select { get; private set; } /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. /// [Preserve, InputControl(aliases = new[] { "Primary", "selectbutton" }, usages = new[] { "PrimaryButton" })] public ButtonControl selectPressed { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. /// [Preserve, InputControl(alias = "Secondary", usage = "Grip")] public AxisControl squeeze { get; private set; } /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. /// [Preserve, InputControl(aliases = new[] { "GripButton", "squeezeClicked" }, usages = new[] { "GripButton" })] public ButtonControl squeezePressed { get; private set; } /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the Microsoft Hand Interaction devicePose OpenXR binding. /// [Preserve, InputControl(offset = 0, alias = "device", usage = "Device")] public PoseControl devicePose { get; private set; } /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the Microsoft Hand Interaction pointer OpenXR binding. /// [Preserve, InputControl(offset = 0, usage = "Pointer")] public PoseControl pointer { get; private set; } /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked. /// [Preserve, InputControl(offset = 132)] new public ButtonControl isTracked { get; private set; } /// /// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set to indicate what data is valid. This value is equivalent to mapping devicePose/trackingState. /// [Preserve, InputControl(offset = 136)] new public IntegerControl trackingState { get; private set; } /// /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position, or grip position. This value is equivalent to mapping devicePose/position. /// [Preserve, InputControl(offset = 20, alias = "gripPosition")] new public Vector3Control devicePosition { get; private set; } /// /// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation, or grip orientation. This value is equivalent to mapping devicePose/rotation. /// [Preserve, InputControl(offset = 32, alias = "gripOrientation")] new public QuaternionControl deviceRotation { get; private set; } /// /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position. /// [Preserve, InputControl(offset = 80)] public Vector3Control pointerPosition { get; private set; } /// /// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pointer rotation. This value is equivalent to mapping pointerPose/rotation. /// [Preserve, InputControl(offset = 92, alias = "pointerOrientation")] public QuaternionControl pointerRotation { get; private set; } /// /// Internal call used to assign controls to the the correct element. /// protected override void FinishSetup() { base.FinishSetup(); select = GetChildControl("select"); selectPressed = GetChildControl("selectPressed"); squeeze = GetChildControl("squeeze"); squeezePressed = GetChildControl("squeezePressed"); devicePose = GetChildControl("devicePose"); pointer = GetChildControl("pointer"); isTracked = GetChildControl("isTracked"); trackingState = GetChildControl("trackingState"); devicePosition = GetChildControl("devicePosition"); deviceRotation = GetChildControl("deviceRotation"); pointerPosition = GetChildControl("pointerPosition"); pointerRotation = GetChildControl("pointerRotation"); } } /// The OpenXR Extension string. OpenXR uses this to check if this extension is available or enabled. See hand interaction extension documentation for more information on this OpenXR extension. public const string extensionString = "XR_MSFT_hand_interaction"; /// /// OpenXR string that represents the hand interaction profile. /// public const string profile = "/interaction_profiles/microsoft/hand_interaction"; /// /// Constant for a float interaction binding '.../input/select/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string select = "/input/select/value"; /// /// Constant for a float interaction binding '.../input/menu/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string squeeze = "/input/squeeze/value"; /// /// Constant for a pose interaction binding '.../input/grip/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string grip = "/input/grip/pose"; /// /// Constant for a pose interaction binding '.../input/aim/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string aim = "/input/aim/pose"; private const string kDeviceLocalizedName = "HoloLens Hand OpenXR"; /// /// Registers the layout with the Input System. /// protected override void RegisterDeviceLayout() { #if UNITY_EDITOR if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup)) return; #endif InputSystem.InputSystem.RegisterLayout(typeof(HoloLensHand), matches: new InputDeviceMatcher() .WithInterface(XRUtilities.InterfaceMatchAnyVersion) .WithProduct(kDeviceLocalizedName)); } /// /// Removes the layout from the Input System. /// protected override void UnregisterDeviceLayout() { #if UNITY_EDITOR if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup)) return; #endif InputSystem.InputSystem.RemoveLayout(nameof(HoloLensHand)); } /// /// Return device layout string that used for registering device for the Input System. /// /// Device layout string. protected override string GetDeviceLayoutName() { return nameof(HoloLensHand); } /// protected override void RegisterActionMapsWithRuntime() { ActionMapConfig actionMap = new ActionMapConfig() { name = "microsofthandinteraction", localizedName = kDeviceLocalizedName, desiredInteractionProfile = profile, manufacturer = "Microsoft", serialNumber = "", deviceInfos = new List() { new DeviceConfig() { characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left), userPath = UserPaths.leftHand }, new DeviceConfig() { characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right), userPath = UserPaths.rightHand } }, actions = new List() { // Select new ActionConfig() { name = "select", localizedName = "Select", type = ActionType.Axis1D, usages = new List() { "PrimaryAxis" }, bindings = new List() { new ActionBinding() { interactionPath = select, interactionProfileName = profile, } } }, // Select Pressed new ActionConfig() { name = "selectPressed", localizedName = "Select Pressed", type = ActionType.Binary, usages = new List() { "PrimaryButton" }, bindings = new List() { new ActionBinding() { interactionPath = select, interactionProfileName = profile, } } }, // Squeeze new ActionConfig() { name = "squeeze", localizedName = "Squeeze", type = ActionType.Axis1D, usages = new List() { "Grip" }, bindings = new List() { new ActionBinding() { interactionPath = squeeze, interactionProfileName = profile, } } }, // Squeeze Pressed new ActionConfig() { name = "squeezePressed", localizedName = "Squeeze Pressed", type = ActionType.Binary, usages = new List() { "GripButton" }, bindings = new List() { new ActionBinding() { interactionPath = squeeze, interactionProfileName = profile, } } }, // Device Pose new ActionConfig() { name = "devicePose", localizedName = "Device Pose", type = ActionType.Pose, usages = new List() { "Device" }, bindings = new List() { new ActionBinding() { interactionPath = grip, interactionProfileName = profile, } } }, // Pointer Pose new ActionConfig() { name = "pointer", localizedName = "Pointer Pose", type = ActionType.Pose, usages = new List() { "Pointer" }, bindings = new List() { new ActionBinding() { interactionPath = aim, interactionProfileName = profile, } } } } }; AddActionMap(actionMap); } } }