// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma warning disable CS0618 // Suppress deprecation warnings using System; using System.Runtime.InteropServices; using UnityEngine; namespace Microsoft.MixedReality.OpenXR { /// /// Represents a user's hand and the ability to track hand joints from it. /// #if UNITY_ANDROID [Obsolete("Hand tracking on Android with the Mixed Reality OpenXR Plugin has been deprecated. " + "For apps using Android hand tracking, we recommend transitioning to the OpenXR plugins from Unity and Meta.", false)] #endif public class HandTracker { /// /// The user's left hand. /// public static HandTracker Left { get; } = new HandTracker(Handedness.Left); /// /// The user's right hand. /// public static HandTracker Right { get; } = new HandTracker(Handedness.Right); private HandTrackingFeaturePlugin Feature = OpenXRFeaturePlugin.Feature; private readonly Handedness m_handedness; internal HandTracker(Handedness trackerHandedness) { m_handedness = trackerHandedness; } /// /// The maximum number of hand joints that might be tracked. /// public const int JointCount = 26; /// /// Fills the passed-in array with current hand joint locations, if possible. /// /// Specify the to locate the hand joints. /// An array of HandJointLocations, indexed according to the HandJoint enum. /// Returns true when the hand tracker is actively tracking the hands. /// Returns false when the hand tracker is disabled or it's not properly set up. /// /// The return value matches the XrHandTrackingDataSourceStateEXT::isActive value in XR_EXT_hand_tracking_data_source extension. /// It returns true if the extension is not supported by OpenXR runtime because Unity cannot observe the hand tracker active state. /// public bool TryLocateHandJoints(FrameTime frameTime, HandJointLocation[] handJointLocations) { if (handJointLocations.Length != JointCount) { Debug.LogError($"LocateJoints requires an array of size {JointCount}. You can use HandTracker.JointCount for this."); return false; } return Feature.IsValidAndEnabled() && NativeLib.TryGetHandJointData(m_handedness, frameTime, handJointLocations); } /// /// Get or set the motion range for this hand tracker. /// /// /// Setting the motion range will take effect immediately for subsequent function calls. /// However, for Unity input system updates for hand joints, it will not take effect until next frame. /// /// If is used with an actual hand tracker, /// joints will still be returned. /// It's only valid when a runtime supports hand joints when using a physical controller. /// public HandJointsMotionRange MotionRange { get { return Feature.IsValidAndEnabled() && OpenXRContext.Current.IsSessionRunning ? NativeLib.GetHandJointsMotionRange(m_handedness) : HandJointsMotionRange.Unobstructed; } set { if (Feature.IsValidAndEnabled()) { NativeLib.SetHandJointsMotionRange(m_handedness, value); } } } } /// /// Represents locational data for a hand joint. /// [StructLayout(LayoutKind.Sequential, Pack = 8)] public readonly struct HandJointLocation { /// /// Whether the corresponding hand joint is actively tracked. /// /// If not actively tracked, the pose may be inferred or last-known but otherwise still meaningful. public bool IsTracked => Convert.ToBoolean(isTracked); // bool isn't blittable, so marshal a byte across the P/Invoke layer instead private readonly byte isTracked; /// /// The world-space pose of the corresponding hand joint. /// public Pose Pose { get; } /// /// The radius of the corresponding joint in units of meters. /// public float Radius { get; } } /// /// Describes which hand the current hand tracker represents. /// public enum Handedness { /// /// Represents the user's left hand. /// Left = 0, /// /// Represents the user's right hand. /// Right } /// /// The supported tracked hand joints in OpenXR. /// /// See https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrHandJointEXT for more information. public enum HandJoint { /// /// The palm. /// Palm, /// /// The wrist. /// Wrist, /// /// The lowest joint of the thumb. /// ThumbMetacarpal, /// /// The second joint of the thumb. /// ThumbProximal, /// /// The joint nearest the tip of the thumb. /// ThumbDistal, /// /// The tip of the thumb. /// ThumbTip, /// /// The lowest joint of the index finger. /// IndexMetacarpal, /// /// The knuckle joint of the index finger. /// IndexProximal, /// /// The middle joint of the index finger. /// IndexIntermediate, /// /// The joint nearest the tip of the index finger. /// IndexDistal, /// /// The tip of the index finger. /// IndexTip, /// /// The lowest joint of the middle finger. /// MiddleMetacarpal, /// /// The proximal joint of the middle finger. /// MiddleProximal, /// /// The middle joint of the middle finger. /// MiddleIntermediate, /// /// The joint nearest the tip of the middle finger. /// MiddleDistal, /// /// The tip of the middle finger. /// MiddleTip, /// /// The lowest joint of the ring finger. /// RingMetacarpal, /// /// The knuckle of the ring finger. /// RingProximal, /// /// The middle joint of the ring finger. /// RingIntermediate, /// /// The joint nearest the tip of the ring finger. /// RingDistal, /// /// The tip of the ring finger. /// RingTip, /// /// The lowest joint of the little finger. /// LittleMetacarpal, /// /// The knuckle joint of the little finger. /// LittleProximal, /// /// The middle joint of the little finger. /// LittleIntermediate, /// /// The joint nearest the tip of the little finger. /// LittleDistal, /// /// The tip of the little finger. /// LittleTip, } /// /// The requested hand joints' range of motion from a controller. /// /// See https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrHandJointsMotionRangeEXT for more information. public enum HandJointsMotionRange { /// /// The range of motion of a human hand, without any obstructions. /// Unobstructed = 1, /// /// The range of motion of the hand joints taking into account any physical limits imposed by the controller itself. /// /// /// This will tend to be the most accurate pose compared to the user’s actual hand pose, but might not allow a closed fist for example. /// ConformingToController = 2, } }