// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using UnityEngine; #if ENABLE_VR || (UNITY_GAMECORE && INPUT_SYSTEM_1_4_OR_NEWER) using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; using UnityEngine.XR.OpenXR.Input; #if UNITY_EDITOR using System.Linq; using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR.Features; #endif #endif #if UNITY_EDITOR using UnityEditor; using UnityEditor.XR.OpenXR.Features; #endif namespace Microsoft.MixedReality.OpenXR { #if UNITY_EDITOR [OpenXRFeature(UiName = featureName, BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android}, Company = "Microsoft", Desc = "Supports articulated hand tracking with 26 hand joints.", DocumentationLink = "https://aka.ms/openxr-unity", CustomRuntimeLoaderBuildTargets = null, OpenxrExtensionStrings = requestedExtensions, Required = false, Category = FeatureCategory.Feature, FeatureId = featureId, Version = "1.11.1")] #endif [RequiresNativePluginDLLs] internal class HandTrackingFeaturePlugin : OpenXRFeaturePlugin { internal const string featureId = "com.microsoft.openxr.feature.handtracking"; internal const string featureName = "Hand Tracking"; private const string requestedExtensions = "XR_EXT_hand_tracking " + "XR_EXT_hand_joints_motion_range " + "XR_EXT_hand_tracking_data_source " + "XR_MSFT_hand_tracking_mesh"; [SerializeField] private HandTrackingOptions leftHandTrackingOptions = default; [SerializeField] private HandTrackingOptions rightHandTrackingOptions = default; internal enum QuestHandTracking { v1, v2, } #if UNITY_EDITOR [EditorDrawerVisibleToBuildTarget(BuildTargetGroup.Android)] #endif [SerializeField, Tooltip("Allows for toggling specific versions of the Quest hand tracking runtime."), LabelWidth(200f), DocURL("https://developer.oculus.com/blog/presence-platforms-hand-tracking-api-gets-an-upgrade/")] private QuestHandTracking questHandTrackingMode = QuestHandTracking.v2; internal QuestHandTracking QuestHandTrackingMode => questHandTrackingMode; private HandTrackingSubsystemController m_handTrackingSubsystemController; HandTrackingFeaturePlugin() { AddSubsystemController(m_handTrackingSubsystemController = new HandTrackingSubsystemController(this)); } protected override void OnSubsystemStart() { base.OnSubsystemStart(); NativeLib.SetHandJointsMotionRange(Handedness.Left, leftHandTrackingOptions.MotionRange); NativeLib.SetHandJointsMotionRange(Handedness.Right, rightHandTrackingOptions.MotionRange); } #if ENABLE_VR || (UNITY_GAMECORE && INPUT_SYSTEM_1_4_OR_NEWER) /// protected override bool OnInstanceCreate(ulong instance) { RegisterDeviceLayout(); return base.OnInstanceCreate(instance); } [UnityEngine.Scripting.Preserve, InputControlLayout(displayName = featureName + " (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" }, isGenericTypeOfDevice = true)] public class OpenXRHandTracking : OpenXRDevice { [InputControl] public ButtonControl isTracked { get; private set; } [InputControl] public IntegerControl trackingState { get; private set; } protected override void FinishSetup() { base.FinishSetup(); isTracked = GetChildControl("isTracked"); trackingState = GetChildControl("trackingState"); } } private static void RegisterDeviceLayout() { InputSystem.RegisterLayout(typeof(OpenXRHandTracking), matches: new InputDeviceMatcher() .WithInterface(XRUtilities.InterfaceMatchAnyVersion) .WithProduct("OpenXR (Right|Left) Hand")); } private static void UnregisterDeviceLayout() { InputSystem.RemoveLayout(nameof(OpenXRHandTracking)); } #if UNITY_EDITOR /// protected override void OnEnabledChange() { base.OnEnabledChange(); CheckRegistration(); } /// /// In the editor, we need to make sure the device layout gets registered /// even if the user doesn't navigate to the project settings. /// [InitializeOnLoadMethod] private static void CheckRegistration() { // Keep the layouts registered in the editor as long as at least one of the build target // groups has the feature enabled. EditorBuildSettings.TryGetConfigObject(Constants.k_SettingsKey, out UnityEngine.Object obj); if (obj is IPackageSettings packageSettings) { if (packageSettings != null && packageSettings.GetFeatures().Any(f => f.feature.enabled)) { RegisterDeviceLayout(); } else { UnregisterDeviceLayout(); } } } #endif // UNITY_EDITOR #endif // ENABLE_VR || (UNITY_GAMECORE && INPUT_SYSTEM_1_4_OR_NEWER) } }