#if ENABLE_VR || UNITY_GAMECORE || PACKAGE_DOCS_GENERATION using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; using UnityEngine.Scripting; namespace UnityEngine.XR.Hands { /// /// The flags in the extension for each hand that can be read from /// and casting to this type. /// [Flags] [Preserve] public enum MetaAimFlags : ulong { /// /// No flags are valid. /// None = 0, /// /// Data for this hand has been computed. /// Computed = 1 << 0, /// /// The aim pose is generally pointed away from the face and is valid /// for use with UI. /// Valid = 1 << 1, /// /// Indicates whether the index finger is pinching with the thumb. /// Only valid when the pinch strength retrieved from /// is /// at a full strength of 1.0. /// IndexPinching = 1 << 2, /// /// Indicates whether the middle finger is pinching with the thumb. /// Only valid when the pinch strength retrieved from /// is /// at a full strength of 1.0. /// MiddlePinching = 1 << 3, /// /// Indicates whether the ring finger is pinching with the thumb. /// Only valid when the pinch strength retrieved from /// is /// at a full strength of 1.0. /// RingPinching = 1 << 4, /// /// Indicates whether the little finger is pinching with the thumb. /// Only valid when the pinch strength retrieved from /// is /// at a full strength of 1.0. /// LittlePinching = 1 << 5, /// /// Indicates whether a system gesture is being performed (when the /// palm of the hand is facing the headset). /// SystemGesture = 1 << 6, /// /// Indicates whether the hand these flags were retrieved from is /// the dominant hand. /// DominantHand = 1 << 7, /// /// Indicates whether the menu gesture button is pressed. /// MenuPressed = 1 << 8, } /// /// A based off the data exposed in the /// /// Meta Hand Tracking Aim extension. Enabled through [Meta Hand Tracking Aim](xref:UnityEngine.XR.Hands.OpenXR.MetaHandTrackingAim) /// or by enabling hand-tracking in the Oculus plug-in if the Input System /// back-end is enabled. /// /// /// For this type to function, you must enable hand-tracking and be running /// with either the OpenXR or Oculus plug-in. /// /// The and /// inherited from /// represent the aim pose. You can use these values to discover the target for pinch gestures, /// when appropriate. /// /// Use the [XROrigin](xref:Unity.XR.CoreUtils.XROrigin) in the scene to position and orient /// the device properly. If you are using this data to set the Transform of a GameObject in /// the scene hierarchy, you can set the local position and rotation of the Transform and make /// it a child of the CameraOffset object below the XROrigin. Otherwise, you can use the /// Transform of the CameraOffset to transform the data into world space. /// #if UNITY_EDITOR [UnityEditor.InitializeOnLoad] #endif [Preserve, InputControlLayout(displayName = "Meta Aim Hand", commonUsages = new[] { "LeftHand", "RightHand" })] public partial class MetaAimHand : TrackedDevice { /// /// The left-hand that contains /// s that surface data in the Meta Hand /// Tracking Aim extension. /// /// /// It is recommended that you treat this as read-only, and do not set /// it yourself. It will be set for you if hand-tracking has been /// enabled and if you are running with either the OpenXR or Oculus /// plug-in. /// public static MetaAimHand left { get; set; } /// /// The right-hand that contains /// s that surface data in the Meta Hand /// Tracking Aim extension. /// /// /// It is recommended that you treat this as read-only, and do not set /// it yourself. It will be set for you if hand-tracking has been /// enabled and if you are running with either the OpenXR or Oculus /// plug-in. /// public static MetaAimHand right { get; set; } /// /// The pinch amount required to register as being pressed for the /// purposes of , , /// , and . /// public const float pressThreshold = 0.8f; /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) /// that represents whether the pinch between the index finger and /// the thumb is mostly pressed (greater than a threshold of 0.8 /// contained in ). /// [Preserve, InputControl(offset = 0)] public ButtonControl indexPressed { get; private set; } /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) /// that represents whether the pinch between the middle finger and /// the thumb is mostly pressed (greater than a threshold of 0.8 /// contained in ). /// [Preserve, InputControl(offset = 1)] public ButtonControl middlePressed { get; private set; } /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) /// that represents whether the pinch between the ring finger and /// the thumb is mostly pressed (greater than a threshold of 0.8 /// contained in ). /// [Preserve, InputControl(offset = 2)] public ButtonControl ringPressed { get; private set; } /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) /// that represents whether the pinch between the little finger and /// the thumb is mostly pressed (greater than a threshold of 0.8 /// contained in ). /// [Preserve, InputControl(offset = 3)] public ButtonControl littlePressed { get; private set; } /// /// Cast the result of reading this to to examine the value. /// [Preserve, InputControl] public IntegerControl aimFlags { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) /// that represents the pinch strength between the index finger and /// the thumb. /// /// /// A value of 0 denotes no pinch at all, while a value of /// 1 denotes a full pinch. /// [Preserve, InputControl] public AxisControl pinchStrengthIndex { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) /// that represents the pinch strength between the middle finger and /// the thumb. /// /// /// A value of 0 denotes no pinch at all, while a value of /// 1 denotes a full pinch. /// [Preserve, InputControl] public AxisControl pinchStrengthMiddle { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) /// that represents the pinch strength between the ring finger and /// the thumb. /// /// /// A value of 0 denotes no pinch at all, while a value of /// 1 denotes a full pinch. /// [Preserve, InputControl] public AxisControl pinchStrengthRing { get; private set; } /// /// An [AxisControl](xref:UnityEngine.InputSystem.Controls.AxisControl) /// that represents the pinch strength between the little finger and /// the thumb. /// /// /// A value of 0 denotes no pinch at all, while a value of /// 1 denotes a full pinch. /// [Preserve, InputControl] public AxisControl pinchStrengthLittle { get; private set; } /// /// Perform final initialization tasks after the control hierarchy has been put into place. /// protected override void FinishSetup() { base.FinishSetup(); indexPressed = GetChildControl(nameof(indexPressed)); middlePressed = GetChildControl(nameof(middlePressed)); ringPressed = GetChildControl(nameof(ringPressed)); littlePressed = GetChildControl(nameof(littlePressed)); aimFlags = GetChildControl(nameof(aimFlags)); pinchStrengthIndex = GetChildControl(nameof(pinchStrengthIndex)); pinchStrengthMiddle = GetChildControl(nameof(pinchStrengthMiddle)); pinchStrengthRing = GetChildControl(nameof(pinchStrengthRing)); pinchStrengthLittle = GetChildControl(nameof(pinchStrengthLittle)); var deviceDescriptor = XRDeviceDescriptor.FromJson(description.capabilities); if (deviceDescriptor != null) { if ((deviceDescriptor.characteristics & InputDeviceCharacteristics.Left) != 0) InputSystem.InputSystem.SetDeviceUsage(this, InputSystem.CommonUsages.LeftHand); else if ((deviceDescriptor.characteristics & InputDeviceCharacteristics.Right) != 0) InputSystem.InputSystem.SetDeviceUsage(this, InputSystem.CommonUsages.RightHand); } } /// /// Creates a and adds it to the Input System. /// /// /// Additional characteristics to build the hand device with besides /// and . /// /// /// A retrieved from /// . /// /// /// It is recommended that you do not call this yourself. It will be /// called for you at the appropriate time if hand-tracking has been /// enabled and if you are running with either the OpenXR or Oculus /// plug-in. /// public static MetaAimHand CreateHand(InputDeviceCharacteristics extraCharacteristics) { var desc = new InputDeviceDescription { product = k_MetaAimHandDeviceProductName, manufacturer = k_MetaAimHandDeviceManufacturerName, capabilities = new XRDeviceDescriptor { characteristics = InputDeviceCharacteristics.HandTracking | InputDeviceCharacteristics.TrackedDevice | extraCharacteristics, inputFeatures = new List { new XRFeatureDescriptor { name = "index_pressed", featureType = FeatureType.Binary }, new XRFeatureDescriptor { name = "middle_pressed", featureType = FeatureType.Binary }, new XRFeatureDescriptor { name = "ring_pressed", featureType = FeatureType.Binary }, new XRFeatureDescriptor { name = "little_pressed", featureType = FeatureType.Binary }, new XRFeatureDescriptor { name = "aim_flags", featureType = FeatureType.DiscreteStates }, new XRFeatureDescriptor { name = "aim_pose_position", featureType = FeatureType.Axis3D }, new XRFeatureDescriptor { name = "aim_pose_rotation", featureType = FeatureType.Rotation }, new XRFeatureDescriptor { name = "pinch_strength_index", featureType = FeatureType.Axis1D }, new XRFeatureDescriptor { name = "pinch_strength_middle", featureType = FeatureType.Axis1D }, new XRFeatureDescriptor { name = "pinch_strength_ring", featureType = FeatureType.Axis1D }, new XRFeatureDescriptor { name = "pinch_strength_little", featureType = FeatureType.Axis1D } } }.ToJson() }; return InputSystem.InputSystem.AddDevice(desc) as MetaAimHand; } /// /// Queues update events in the Input System based on the supplied hand. /// It is not recommended that you call this directly. This will be called /// for you when appropriate. /// /// /// Whether the hand root pose is valid. /// /// /// The aim flags to update in the Input System. /// /// /// The aim pose to update in the Input System. Used if the hand root is tracked. /// /// /// The pinch strength for the index finger to update in the Input System. /// /// /// The pinch strength for the middle finger to update in the Input System. /// /// /// The pinch strength for the ring finger to update in the Input System. /// /// /// The pinch strength for the little finger to update in the Input System. /// public void UpdateHand( bool isHandRootTracked, MetaAimFlags aimFlags, Pose aimPose, float pinchIndex, float pinchMiddle, float pinchRing, float pinchLittle) { if (aimFlags != m_PreviousFlags) { InputSystem.InputSystem.QueueDeltaStateEvent(this.aimFlags, (int)aimFlags); m_PreviousFlags = aimFlags; } bool isIndexPressed = pinchIndex > pressThreshold; if (isIndexPressed != m_WasIndexPressed) { InputSystem.InputSystem.QueueDeltaStateEvent(indexPressed, isIndexPressed); m_WasIndexPressed = isIndexPressed; } bool isMiddlePressed = pinchMiddle > pressThreshold; if (isMiddlePressed != m_WasMiddlePressed) { InputSystem.InputSystem.QueueDeltaStateEvent(middlePressed, isMiddlePressed); m_WasMiddlePressed = isMiddlePressed; } bool isRingPressed = pinchRing > pressThreshold; if (isRingPressed != m_WasRingPressed) { InputSystem.InputSystem.QueueDeltaStateEvent(ringPressed, isRingPressed); m_WasRingPressed = isRingPressed; } bool isLittlePressed = pinchLittle > pressThreshold; if (isLittlePressed != m_WasLittlePressed) { InputSystem.InputSystem.QueueDeltaStateEvent(littlePressed, isLittlePressed); m_WasLittlePressed = isLittlePressed; } InputSystem.InputSystem.QueueDeltaStateEvent(pinchStrengthIndex, pinchIndex); InputSystem.InputSystem.QueueDeltaStateEvent(pinchStrengthMiddle, pinchMiddle); InputSystem.InputSystem.QueueDeltaStateEvent(pinchStrengthRing, pinchRing); InputSystem.InputSystem.QueueDeltaStateEvent(pinchStrengthLittle, pinchLittle); if ((aimFlags & MetaAimFlags.Computed) == MetaAimFlags.None) { if (m_WasTracked) { InputSystem.InputSystem.QueueDeltaStateEvent(isTracked, false); InputSystem.InputSystem.QueueDeltaStateEvent(trackingState, InputTrackingState.None); m_WasTracked = false; } return; } if (isHandRootTracked) { InputSystem.InputSystem.QueueDeltaStateEvent(devicePosition, aimPose.position); InputSystem.InputSystem.QueueDeltaStateEvent(deviceRotation, aimPose.rotation); if (!m_WasTracked) { InputSystem.InputSystem.QueueDeltaStateEvent(trackingState, InputTrackingState.Position | InputTrackingState.Rotation); InputSystem.InputSystem.QueueDeltaStateEvent(isTracked, true); } m_WasTracked = true; } else if (m_WasTracked) { InputSystem.InputSystem.QueueDeltaStateEvent(trackingState, InputTrackingState.None); InputSystem.InputSystem.QueueDeltaStateEvent(isTracked, false); m_WasTracked = false; } } internal void UpdateHand(bool isLeft, bool isHandRootTracked) { UnityOpenXRHands_RetrieveMetaAim( isLeft, out MetaAimFlags aimFlags, out Vector3 aimPosePosition, out Quaternion aimPoseRotation, out float pinchIndex, out float pinchMiddle, out float pinchRing, out float pinchLittle); UpdateHand( isHandRootTracked, aimFlags, new Pose(aimPosePosition, aimPoseRotation), pinchIndex, pinchMiddle, pinchRing, pinchLittle); } [DllImport("UnityOpenXRHands")] static extern void UnityOpenXRHands_RetrieveMetaAim( bool isLeft, out MetaAimFlags aimFlags, out Vector3 aimPosePosition, out Quaternion aimPoseRotation, out float pinchStrengthIndex, out float pinchStrengthMiddle, out float pinchStrengthRing, out float pinchStrengthLittle); #if UNITY_EDITOR static MetaAimHand() => RegisterLayout(); #endif [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void RegisterLayout() { InputSystem.InputSystem.RegisterLayout( matches: new InputDeviceMatcher() .WithProduct(k_MetaAimHandDeviceProductName) .WithManufacturer(k_MetaAimHandDeviceManufacturerName)); } const string k_MetaAimHandDeviceProductName = "Meta Aim Hand Tracking"; const string k_MetaAimHandDeviceManufacturerName = "OpenXR Meta"; MetaAimFlags m_PreviousFlags; bool m_WasTracked; bool m_WasIndexPressed; bool m_WasMiddlePressed; bool m_WasRingPressed; bool m_WasLittlePressed; } } #endif // ENABLE_VR || UNITY_GAMECORE || PACKAGE_DOCS_GENERATION