VR4Medical/ICI/Library/PackageCache/com.unity.xr.hands@b137b9cef9d8/Runtime/OpenXR/HandTracking.cs
2025-07-29 13:45:50 +03:00

344 lines
13 KiB
C#

#if UNITY_OPENXR_PACKAGE || PACKAGE_DOCS_GENERATION
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine.XR.Hands;
using UnityEngine.XR.Hands.ProviderImplementation;
using UnityEngine.XR.Management;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.Management;
#endif
#if UNITY_OPENXR_PACKAGE_1_2 && !UNITY_OPENXR_PACKAGE_1_6
using UnityEngine.XR.OpenXR.Features.OculusQuestSupport;
#endif
namespace UnityEngine.XR.Hands.OpenXR
{
/// <summary>
/// This <see cref="OpenXRInteractionFeature"/> enables the use of
/// hand-tracking data in OpenXR through the <see cref="XRHandSubsystem"/>.
/// It enables <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hand_tracking">
/// XR_EXT_hand_tracking</see> in the underlying runtime. To retrieve hand
/// data, use the <see cref="XRHandSubsystem"/> retrieved from
/// <see cref="HandTracking.subsystem"/>.
/// </summary>
/// <remarks>
/// For this extension to be available, you must install the
/// <see href="https://docs.unity3d.com/Packages/com.unity.xr.hands@latest/manual/index.html">
/// XR Hands package</see>.
/// </remarks>
#if UNITY_EDITOR
[UnityEditor.XR.OpenXR.Features.OpenXRFeature(UiName = "Hand Tracking Subsystem",
BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android },
Company = "Unity",
Desc = "Creates and manages an XRHandSubsystem.",
DocumentationLink = XRHelpURLConstants.k_CurrentManualDocsBaseUrl + "features/handtracking.html",
Version = "0.0.1",
OpenxrExtensionStrings = extensionString,
Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Feature,
FeatureId = featureId,
Priority = -100)]
#endif
public class HandTracking : OpenXRFeature
{
/// <summary>
/// The feature ID string. This is used to give the feature a well known
/// ID for reference.
/// </summary>
public const string featureId = "com.unity.openxr.feature.input.handtracking";
/// <summary>
/// The OpenXR Extension string. OpenXR uses this to check if this
/// extension is available or enabled. See
/// <see href="https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hand_tracking">hand interaction extension</see>
/// documentation for more information on this OpenXR extension.
/// </summary>
public const string extensionString = "XR_EXT_hand_tracking";
/// <summary>
/// The <see cref="XRHandSubsystem"/> that retrieves hand data from its
/// provider. Will only be valid when this feature is enabled and
/// running. To subscribe to updates, use <see cref="XRHandSubsystem.handsUpdated"/>.
/// </summary>
public static XRHandSubsystem subsystem => s_Subsystem;
/// <summary>
/// Event-args struct passed to <see cref="subsystemCreated"/> when
/// the subsystem is created.
/// </summary>
public struct SubsystemCreatedEventArgs
{
/// <summary>
/// The subsystem that was just created.
/// </summary>
public XRHandSubsystem subsystem { get; internal set; }
}
/// <summary>
/// Event-args struct passed to <see cref="destroyingSubsystem"/> when
/// the subsystem is about to be destroyed.
/// </summary>
public struct DestroyingSubsystemEventArgs
{
/// <summary>
/// The subsystem about to be destroyed.
/// </summary>
public XRHandSubsystem subsystem { get; internal set; }
}
/// <summary>
/// Called when this feature creates an <see cref="XRHandSubsystem"/>.
/// </summary>
public static Action<SubsystemCreatedEventArgs> subsystemCreated;
/// <summary>
/// Called just before this feature destroys an <see cref="XRHandSubsystem"/>.
/// </summary>
public static Action<DestroyingSubsystemEventArgs> destroyingSubsystem;
/// <summary>
/// Whether an <see cref="XRHandSubsystem"/> should be created when the
/// session is. Defaults to <see langword="true"/>.
/// </summary>
/// <remarks>
/// If you wish to set this to <see langword="false"/> in time to stop
/// automatic creation, do so in a <c>static</c> method decorated with
/// <c>[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]</c>.
/// When you wish to initialize the subsystem, you can then call
/// <see cref="EnsureSubsystemInitialized"/>.
/// </remarks>
public static bool automaticallyInitializeSubsystem { get; set; } = true;
/// <summary>
/// Ensures an <see cref="XRHandSubsystem"/> is created.
/// </summary>
/// <remarks>
/// Will be automatically called at start-up if
/// <see cref="automaticallyInitializeSubsystem"/> is <see langword="true"/>,
/// which it is by default. If called later, will automatically start
/// the subsystem if the session is running.
/// </remarks>
public static void EnsureSubsystemInitialized()
{
if (s_Subsystem != null)
return;
var descriptors = new List<XRHandSubsystemDescriptor>();
s_This.CreateSubsystem<XRHandSubsystemDescriptor, XRHandSubsystem>(descriptors, OpenXRHandProvider.id);
s_Subsystem = XRGeneralSettings.Instance?.Manager?.activeLoader?.GetLoadedSubsystem<XRHandSubsystem>();
if (s_Subsystem == null)
{
Debug.LogError($"Failed to find descriptor '{OpenXRHandProvider.id}' - HandTracking OpenXR feature will not do anything!");
return;
}
s_This.m_Updater = new XRHandProviderUtility.SubsystemUpdater(s_Subsystem);
if (s_This.m_ShouldBeRunning)
{
s_Subsystem.Start();
s_This.m_Updater.Start();
}
if (subsystemCreated != null)
subsystemCreated.Invoke(new SubsystemCreatedEventArgs {subsystem = s_Subsystem});
}
/// <summary>See <see cref="OpenXRFeature.OnSystemChange(ulong)"/>.</summary>
protected override void OnSystemChange(ulong xrSystem)
{
base.OnSystemChange(xrSystem);
NativeApi.OnSystemChange(xrSystem);
}
/// <summary>See <see cref="OpenXRFeature.OnInstanceCreate(ulong)"/>.</summary>
protected override bool OnInstanceCreate(ulong xrInstance)
{
s_This = this;
if (!base.OnInstanceCreate(xrInstance))
return false;
return NativeApi.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
}
/// <summary>See <see cref="OpenXRFeature.OnAppSpaceChange(ulong)"/>.</summary>
protected override void OnAppSpaceChange(ulong xrSpace)
{
base.OnAppSpaceChange(xrSpace);
NativeApi.OnAppSpaceChange(xrSpace);
}
/// <summary>
/// Called after xrCreateSession.
/// </summary>
/// <remarks>
/// Creates an <see cref="XRHandSubsystem"/> with the OpenXR provider.
/// </remarks>
protected override void OnSessionCreate(ulong xrSession)
{
base.OnSessionCreate(xrSession);
NativeApi.OnSessionCreate(xrSession);
if (automaticallyInitializeSubsystem)
EnsureSubsystemInitialized();
}
/// <summary>See <see cref="OpenXRFeature.OnAppSpaceChange(ulong)"/>.</summary>
protected override void OnSessionDestroy(ulong xrSpace)
{
base.OnSessionDestroy(xrSpace);
NativeApi.OnSessionDestroy(xrSpace);
}
/// <summary>See <see cref="OpenXRFeature.OnInstanceDestroy(ulong)"/>.</summary>
protected override void OnInstanceDestroy(ulong xrInstance)
{
base.OnInstanceDestroy(xrInstance);
NativeApi.OnInstanceDestroy(xrInstance);
}
/// <summary>See <see cref="OpenXRFeature.OnInstanceLossPending(ulong)"/>.</summary>
protected override void OnInstanceLossPending(ulong xrInstance)
{
base.OnInstanceLossPending(xrInstance);
NativeApi.OnInstanceLossPending(xrInstance);
}
/// <summary>
/// Called after the OpenXR loader has started its subsystems.
/// </summary>
/// <remarks>
/// Starts the <see cref="XRHandSubsystem"/> and automatic updating for
/// it. To subscribe to updates, use <see cref="XRHandSubsystem.handsUpdated"/>.
/// </remarks>
protected override void OnSubsystemStart()
{
m_ShouldBeRunning = true;
if (s_Subsystem == null)
return;
s_Subsystem.Start();
m_Updater.Start();
}
/// <summary>
/// Called before the OpenXR loader stops its subsystems.
/// </summary>
/// <remarks>
/// Stops the <see cref="XRHandSubsystem"/> and automatic updating for it.
/// </remarks>
protected override void OnSubsystemStop()
{
m_ShouldBeRunning = false;
m_Updater?.Stop();
s_Subsystem?.Stop();
}
/// <summary>
/// Called before the OpenXR loader destroys its subsystems.
/// </summary>
/// <remarks>
/// Destroys the <see cref="XRHandSubsystem"/>.
/// </remarks>
protected override void OnSubsystemDestroy()
{
m_Updater?.Destroy();
m_Updater = null;
if (destroyingSubsystem != null)
destroyingSubsystem.Invoke(new DestroyingSubsystemEventArgs {subsystem = s_Subsystem});
s_Subsystem?.Destroy();
s_Subsystem = null;
}
/// <see cref="OpenXRFeature.HookGetInstanceProcAddr(IntPtr)"/>
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
=> NativeApi.Intercept_xrGetInstanceProcAddr(func);
#if UNITY_EDITOR
protected override void GetValidationChecks(List<ValidationRule> results, BuildTargetGroup targetGroup)
{
#if UNITY_OPENXR_PACKAGE_1_2 && !UNITY_OPENXR_PACKAGE_1_6
results.Add(new ValidationRule(this)
{
message = "Hand-tracking does not work on a Quest device at runtime without a version of the OpenXR package at version 1.6.0 or newer.",
checkPredicate = () =>
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
if (null == settings)
return false;
var questFeature = settings.GetFeature<OculusQuestFeature>();
return questFeature == null || !questFeature.enabled;
},
error = true
});
#endif // UNITY_OPENXR_PACKAGE_1_6
}
#endif // UNITY_EDITOR
internal const string k_LibraryName = "UnityOpenXRHands";
static class NativeApi
{
[DllImport(k_LibraryName, EntryPoint = "UnityOpenXRHands_OnSystemChange")]
static internal extern void OnSystemChange(ulong xrSystem);
[DllImport(k_LibraryName, EntryPoint = "UnityOpenXRHands_OnInstanceCreate")]
static internal extern bool OnInstanceCreate(ulong xrInstance, IntPtr xrGetInstanceProcAddr);
[DllImport(k_LibraryName, EntryPoint = "UnityOpenXRHands_OnAppSpaceChange")]
static internal extern void OnAppSpaceChange(ulong xrSpace);
[DllImport(k_LibraryName, EntryPoint = "UnityOpenXRHands_OnSessionCreate")]
static internal extern void OnSessionCreate(ulong xrSession);
[DllImport(k_LibraryName, EntryPoint = "UnityOpenXRHands_OnSessionDestroy")]
static internal extern void OnSessionDestroy(ulong xrSession);
[DllImport(k_LibraryName, EntryPoint = "UnityOpenXRHands_OnInstanceDestroy")]
static internal extern void OnInstanceDestroy(ulong xrInstance);
[DllImport(k_LibraryName, EntryPoint = "UnityOpenXRHands_OnInstanceLossPending")]
static internal extern void OnInstanceLossPending(ulong xrInstance);
[DllImport(k_LibraryName, EntryPoint = "UnityOpenXRHands_intercept_xrGetInstanceProcAddr")]
static internal extern IntPtr Intercept_xrGetInstanceProcAddr(IntPtr func);
}
#if UNITY_EDITOR
internal static bool OpenXRLoaderEnabledForEditorPlayMode()
{
var settings = XRGeneralSettings.Instance?.AssignedSettings ?? (XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTargetGroup.Standalone)?.AssignedSettings);
if (!settings)
return false;
foreach (var activeLoader in settings.activeLoaders)
{
if (activeLoader is OpenXRLoader)
return true;
}
return false;
}
#endif
XRHandProviderUtility.SubsystemUpdater m_Updater;
bool m_ShouldBeRunning;
static XRHandSubsystem s_Subsystem;
static HandTracking s_This;
}
}
#endif // UNITY_OPENXR_PACKAGE || PACKAGE_DOCS_GENERATION