using System; using System.Collections.Generic; using System.Linq; using UnityEngine.Scripting; using UnityEngine.XR.OpenXR.Input; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.XR; #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 New Hand interaction profiles in OpenXR. /// #if UNITY_EDITOR [UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Hand Interaction Profile", BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android}, Company = "Unity", Desc = "Add hand interaction profile for hand tracking input device.", DocumentationLink = Constants.k_DocumentationManualURL + "features/handinteractionprofile.html", OpenxrExtensionStrings = extensionString, Version = "0.0.1", Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction, FeatureId = featureId)] #endif public class HandInteractionProfile : 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.handinteraction"; /// /// A new interaction profile for hand tracking input device to provide actions through the OpenXR action system. /// [Preserve, InputControlLayout(displayName = "Hand Interaction (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })] public class HandInteraction : XRController { /// /// A that represents the OpenXR binding. /// [Preserve, InputControl(offset = 0, aliases = new[] { "device", "gripPose" }, usage = "Device")] public PoseControl devicePose { get; private set; } /// /// A that represents the OpenXR binding. /// [Preserve, InputControl(offset = 0, alias = "aimPose", usage = "Pointer")] public PoseControl pointer { get; private set; } /// /// A that represents the OpenXR binding. /// [Preserve, InputControl(offset = 0, usage = "Poke")] public PoseControl pokePose { get; private set; } /// /// A that represents the OpenXR binding. /// [Preserve, InputControl(offset = 0, usage = "Pinch")] public PoseControl pinchPose { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "PinchValue")] public AxisControl pinchValue { get; private set; } /// /// An [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "PinchTouched")] public ButtonControl pinchTouched { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "PinchReady")] public ButtonControl pinchReady { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "PointerActivateValue")] public AxisControl pointerActivateValue { get; private set; } /// /// An [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "PointerActivated")] public ButtonControl pointerActivated { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "PointerActivateReady")] public ButtonControl pointerActivateReady { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "GraspValue")] public AxisControl graspValue { get; private set; } /// /// An [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "GraspFirm")] public ButtonControl graspFirm { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents the OpenXR binding. /// [Preserve, InputControl(usage = "GraspReady")] public ButtonControl graspReady { 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 gripPose/isTracked. /// [Preserve, InputControl(offset = 2)] 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 gripPose/trackingState. /// [Preserve, InputControl(offset = 4)] 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. This value is equivalent to mapping gripPose/position. /// [Preserve, InputControl(offset = 8, noisy = true, 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. This value is equivalent to mapping gripPose/rotation. /// [Preserve, InputControl(offset = 20, noisy = true, alias = "gripRotation")] 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 aim position. This value is equivalent to mapping aimPose/position. /// [Preserve, InputControl(offset = 68, noisy = true)] public Vector3Control pointerPosition { get; private set; } /// /// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the aim orientation. This value is equivalent to mapping aimPose/rotation. /// [Preserve, InputControl(offset = 80, noisy = true)] public QuaternionControl pointerRotation { get; private set; } /// /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the poke position. This value is equivalent to mapping pokePose/position. /// [Preserve, InputControl(offset = 128, noisy = true)] public Vector3Control pokePosition { get; private set; } /// /// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the poke orientation. This value is equivalent to mapping pokePose/rotation. /// [Preserve, InputControl(offset = 140, noisy = true)] public QuaternionControl pokeRotation { get; private set; } /// /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the pinch position. This value is equivalent to mapping pinchPose/position. /// [Preserve, InputControl(offset = 188, noisy = true)] public Vector3Control pinchPosition { get; private set; } /// /// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the pinch orientation. This value is equivalent to mapping pinchPose/rotation. /// [Preserve, InputControl(offset = 200, noisy = true)] public QuaternionControl pinchRotation { get; private set; } /// /// Internal call used to assign controls to the the correct element. /// protected override void FinishSetup() { base.FinishSetup(); devicePose = GetChildControl("devicePose"); pointer = GetChildControl("pointer"); pokePose = GetChildControl("pokePose"); pinchPose = GetChildControl("pinchPose"); pinchValue = GetChildControl("pinchValue"); pinchTouched = GetChildControl("pinchTouched"); pinchReady = GetChildControl("pinchReady"); pointerActivateValue = GetChildControl("pointerActivateValue"); pointerActivated = GetChildControl("pointerActivated"); pointerActivateReady = GetChildControl("pointerActivateReady"); graspValue = GetChildControl("graspValue"); graspFirm = GetChildControl("graspFirm"); graspReady = GetChildControl("graspReady"); isTracked = GetChildControl("isTracked"); trackingState = GetChildControl("trackingState"); devicePosition = GetChildControl("devicePosition"); deviceRotation = GetChildControl("deviceRotation"); pointerPosition = GetChildControl("pointerPosition"); pointerRotation = GetChildControl("pointerRotation"); pokePosition = GetChildControl("pokePosition"); pokeRotation = GetChildControl("pokeRotation"); pinchPosition = GetChildControl("pinchPosition"); pinchRotation = GetChildControl("pinchRotation"); } } /// /// The interaction profile string used to reference Hand Interaction Profile. /// public const string profile = "/interaction_profiles/ext/hand_interaction_ext"; // Available Bindings // Both Hands /// /// 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"; /// /// Constant for a pose interaction binding '.../input/poke_ext/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string poke = "/input/poke_ext/pose"; /// /// Constant for a pose interaction binding '.../input/pinch_ext/pose' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string pinch = "/input/pinch_ext/pose"; /// /// Constant for a float interaction binding '.../input/pinch_ext/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string pinchValue = "/input/pinch_ext/value"; /// /// Constant for a boolean interaction binding '.../input/pinch_ext/ready_ext' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string pinchReady = "/input/pinch_ext/ready_ext"; /// /// Constant for a float interaction binding '.../input/aim_activate_ext/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string pointerActivateValue = "/input/aim_activate_ext/value"; /// /// Constant for a boolean interaction binding '.../input/aim_activate_ext/ready_ext' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string pointerActivateReady = "/input/aim_activate_ext/ready_ext"; /// /// Constant for a float interaction binding '.../input/grasp_ext/value' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string graspValue = "/input/grasp_ext/value"; /// /// Constant for a boolean interaction binding '.../input/grasp_ext/ready_ext' OpenXR Input Binding. Used by input subsystem to bind actions to physical inputs. /// public const string graspReady = "/input/grasp_ext/ready_ext"; private const string kDeviceLocalizedName = "Hand Interaction OpenXR"; /// /// The OpenXR Extension string. This is used by OpenXR to check if this extension is available or enabled. /// public const string extensionString = "XR_EXT_hand_interaction"; /// protected internal override bool OnInstanceCreate(ulong instance) { // Requires hand tracking extension if (!OpenXRRuntime.IsExtensionEnabled(extensionString)) return false; return base.OnInstanceCreate(instance); } /// /// Registers the layout with the Input System. /// protected override void RegisterDeviceLayout() { #if UNITY_EDITOR if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup)) return; #endif InputSystem.InputSystem.RegisterLayout(typeof(HandInteraction), matches: new InputDeviceMatcher() .WithInterface(XRUtilities.InterfaceMatchAnyVersion) .WithProduct(kDeviceLocalizedName)); } /// /// Removes the layout with the Input System. /// protected override void UnregisterDeviceLayout() { #if UNITY_EDITOR if (!OpenXRLoaderEnabledForSelectedBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup)) return; #endif InputSystem.InputSystem.RemoveLayout(nameof(HandInteraction)); } /// /// Return device layout string that used for registering device in InputSystem. /// /// Device layout string. protected override string GetDeviceLayoutName() { return nameof(HandInteraction); } /// protected override void RegisterActionMapsWithRuntime() { ActionMapConfig actionMap = new ActionMapConfig() { name = "handinteraction", localizedName = kDeviceLocalizedName, desiredInteractionProfile = profile, manufacturer = "", serialNumber = "", deviceInfos = new List() { new DeviceConfig() { characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Left), userPath = UserPaths.leftHand }, new DeviceConfig() { characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Right), userPath = UserPaths.rightHand } }, actions = new List() { // Device Pose new ActionConfig() { name = "devicePose", localizedName = "Grip 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, } } }, //Poke Pose new ActionConfig() { name = "PokePose", localizedName = "Poke Pose", type = ActionType.Pose, usages = new List() { "Poke" }, bindings = new List() { new ActionBinding() { interactionPath = poke, interactionProfileName = profile, } } }, //Pinch Pose new ActionConfig() { name = "PinchPose", localizedName = "Pinch Pose", type = ActionType.Pose, usages = new List() { "Pinch" }, bindings = new List() { new ActionBinding() { interactionPath = pinch, interactionProfileName = profile, } } }, //Pinch Value new ActionConfig() { name = "PinchValue", localizedName = "Pinch Value", type = ActionType.Axis1D, usages = new List() { "PinchValue" }, bindings = new List() { new ActionBinding() { interactionPath = pinchValue, interactionProfileName = profile, } } }, //Pinch Touched new ActionConfig() { name = "PinchTouched", localizedName = "Pinch Touched", type = ActionType.Binary, usages = new List() { "PinchTouched" }, bindings = new List() { new ActionBinding() { interactionPath = pinchValue, interactionProfileName = profile, } } }, //Pinch Ready new ActionConfig() { name = "PinchReady", localizedName = "Pinch Ready", type = ActionType.Binary, usages = new List() { "PinchReady" }, bindings = new List() { new ActionBinding() { interactionPath = pinchReady, interactionProfileName = profile, } } }, //Pointer Activate Value new ActionConfig() { name = "PointerActivateValue", localizedName = "Pointer Activate Value", type = ActionType.Axis1D, usages = new List() { "PointerActivateValue" }, bindings = new List() { new ActionBinding() { interactionPath = pointerActivateValue, interactionProfileName = profile, } } }, //Pointer Activated new ActionConfig() { name = "PointerActivated", localizedName = "Pointer Activated", type = ActionType.Binary, usages = new List() { "PointerActivated" }, bindings = new List() { new ActionBinding() { interactionPath = pointerActivateValue, interactionProfileName = profile, } } }, //Pointer Activate Ready new ActionConfig() { name = "PointerActivateReady", localizedName = "Pointer Activate Ready", type = ActionType.Binary, usages = new List() { "PointerActivateReady" }, bindings = new List() { new ActionBinding() { interactionPath = pointerActivateReady, interactionProfileName = profile, } } }, // Grasp Value new ActionConfig() { name = "GraspValue", localizedName = "Grasp Value", type = ActionType.Axis1D, usages = new List() { "GraspValue" }, bindings = new List() { new ActionBinding() { interactionPath = graspValue, interactionProfileName = profile, } } }, //Grasp Firm new ActionConfig() { name = "GraspFirm", localizedName = "Grasp Firm", type = ActionType.Binary, usages = new List() { "GraspFirm" }, bindings = new List() { new ActionBinding() { interactionPath = graspValue, interactionProfileName = profile, } } }, //Grasp Ready new ActionConfig() { name = "GraspReady", localizedName = "Grasp Ready", type = ActionType.Binary, usages = new List() { "GraspReady" }, bindings = new List() { new ActionBinding() { interactionPath = graspReady, interactionProfileName = profile, } } } } }; AddActionMap(actionMap); } } }