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);
}
}
}