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

310 lines
13 KiB
C#

using System;
namespace UnityEngine.XR.Hands.ProviderImplementation
{
/// <summary>
/// Utility methods for a provider to a <see cref="XRHandSubsystem"/>.
/// </summary>
public static partial class XRHandProviderUtility
{
/// <summary>
/// Create a fully configurable joint with at least a pose in hand
/// space, tracking state, and ID.
/// </summary>
/// <param name="handedness">
/// Denotes whether the joint being created is on the left or right hand.
/// </param>
/// <param name="trackingState">
/// The tracking state flags associated with this joint, representing
/// which fields of the <see cref="XRHandJoint"/> are valid.
/// </param>
/// <param name="id">
/// The <see cref="XRHandJointID"/> of the joint. When filling out an
/// element of an array of <see cref="XRHandJoint"/>, the index must
/// be converted with <see cref="XRHandJointIDUtility.FromIndex"/>.
/// </param>
/// <param name="pose">
/// The pose of the joint in session space, relative to the
/// [XROrigin](xref:Unity.XR.CoreUtils.XROrigin).
/// </param>
/// <param name="radius">
/// The radius of the joint. The default value is <c>0f</c>.
/// </param>
/// <param name="linearVelocity">
/// The linear velocity of the joint in hand space (relative to the
/// [XROrigin](xref:Unity.XR.CoreUtils.XROrigin)). The default value is
/// <c>Vector3.zero</c>.
/// </param>
/// <param name="angularVelocity">
/// The angular velocity of the joint in hand space (relative to the
/// [XROrigin](xref:Unity.XR.CoreUtils.XROrigin)). The default value is
/// <c>Vector3.zero</c>.
/// </param>
/// <returns>
/// An <see cref="XRHandJoint"/> with the given pose and other supplied
/// data.
/// </returns>
public static XRHandJoint CreateJoint(
Handedness handedness,
XRHandJointTrackingState trackingState,
XRHandJointID id,
Pose pose,
float radius = 0f,
Vector3 linearVelocity = new Vector3(),
Vector3 angularVelocity = new Vector3())
{
int idAndHandedness = (int)id;
if (handedness == Handedness.Right)
idAndHandedness |= XRHandJoint.k_IsRightHandBit;
return new XRHandJoint
{
m_TrackingState = trackingState,
m_IdAndHandedness = idAndHandedness,
m_Pose = pose,
m_Radius = radius,
m_LinearVelocity = linearVelocity,
m_AngularVelocity = angularVelocity,
};
}
/// <summary>
/// Use this with your provider (if hand-tracking is enabled in your
/// build settings) to have the subsystem automatically update. Users
/// can subscribe to <see cref="XRHandSubsystem.handsUpdated"/> to
/// respond to updates instead of waiting until a following update step.
/// Makes use of <c>UnityEngine.LowLevel.PlayerLoop</c> to insert this
/// automatic update at the beginning of the <c>EarlyUpdate.XRUpdate</c>
/// loop.
/// </summary>
/// <remarks>
/// If the Input System backend is enabled, this will also create and
/// <see cref="XRHandDevice"/>s which update themselves from the
/// <see cref="XRHandSubsystem.handsUpdated"/> callback.
/// </remarks>
public class SubsystemUpdater
{
/// <summary>
/// Initializes and returns an instance of <see cref="SubsystemUpdater"/>.
/// You should create this in your plug-in once the <see cref="XRHandSubsystem"/>
/// is created.
/// </summary>
/// <param name="subsystem">
/// The <see cref="XRHandSubsystem"/> to update automatically.
/// </param>
public SubsystemUpdater(XRHandSubsystem subsystem) => m_Subsystem = subsystem;
/// <summary>
/// Starts the automatic updating of the <see cref="XRHandSubsystem"/>.
/// You should call this from your plug-in when calling <c>Start</c>
/// on the subsystem.
/// </summary>
public void Start()
{
int earlyUpdateStepIndex = -1;
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
for (int subSystemIndex = 0; subSystemIndex < playerLoop.subSystemList.Length; ++subSystemIndex)
{
if (playerLoop.subSystemList[subSystemIndex].type == typeof(UnityEngine.PlayerLoop.EarlyUpdate))
{
earlyUpdateStepIndex = subSystemIndex;
break;
}
}
if (earlyUpdateStepIndex < 0)
{
Debug.LogError("Couldn't find PlayerLoop.EarlyUpdate step - XRHandSubsystem will not automatically update!");
return;
}
int xrUpdateIndex = -1;
for (int subSystemIndex = 0; subSystemIndex < playerLoop.subSystemList[earlyUpdateStepIndex].subSystemList.Length; ++subSystemIndex)
{
if (playerLoop.subSystemList[earlyUpdateStepIndex].subSystemList[subSystemIndex].type == typeof(UnityEngine.PlayerLoop.EarlyUpdate.XRUpdate))
{
xrUpdateIndex = subSystemIndex;
break;
}
}
if (xrUpdateIndex < 0)
{
Debug.LogError("Couldn't find PlayerLoop.EarlyUpdate.XRUpdate step - XRHandSubsystem will not automatically update!");
return;
}
var systems = playerLoop.subSystemList[earlyUpdateStepIndex].subSystemList[xrUpdateIndex].subSystemList;
if (systems != null)
{
foreach (var system in systems)
{
if (system.type == typeof(XRHandSubsystemPlayerLoopRunnerUpdateSystem))
return;
}
}
var updatedSystems = new UnityEngine.LowLevel.PlayerLoopSystem[systems != null ? systems.Length + 1 : 1];
if (systems != null)
Array.Copy(systems, 0, updatedSystems, 1, systems.Length);
updatedSystems[0] = new UnityEngine.LowLevel.PlayerLoopSystem
{
type = typeof(XRHandSubsystemPlayerLoopRunnerUpdateSystem),
updateDelegate = () => OnUpdate()
};
Application.onBeforeRender += OnBeforeRender;
playerLoop.subSystemList[earlyUpdateStepIndex].subSystemList[xrUpdateIndex].subSystemList = updatedSystems;
UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop(playerLoop);
}
/// <summary>
/// Stops the automatic updating of the <see cref="XRHandSubsystem"/>.
/// You should call this from your plug-in when calling <c>Stop</c>
/// on the subsystem.
/// </summary>
public void Stop()
{
Application.onBeforeRender -= OnBeforeRender;
var playerLoop = UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop();
int earlyUpdateStepIndex = -1;
for (int subSystemIndex = 0; subSystemIndex < playerLoop.subSystemList.Length; ++subSystemIndex)
{
if (playerLoop.subSystemList[subSystemIndex].type == typeof(UnityEngine.PlayerLoop.EarlyUpdate))
{
earlyUpdateStepIndex = subSystemIndex;
break;
}
}
if (earlyUpdateStepIndex < 0)
return;
int xrUpdateIndex = -1;
for (int subSystemIndex = 0; subSystemIndex < playerLoop.subSystemList[earlyUpdateStepIndex].subSystemList.Length; ++subSystemIndex)
{
if (playerLoop.subSystemList[earlyUpdateStepIndex].subSystemList[subSystemIndex].type == typeof(UnityEngine.PlayerLoop.EarlyUpdate.XRUpdate))
{
xrUpdateIndex = subSystemIndex;
break;
}
}
if (xrUpdateIndex < 0)
return;
int indexOfOurType = -1;
var systems = playerLoop.subSystemList[earlyUpdateStepIndex].subSystemList[xrUpdateIndex].subSystemList;
if (systems == null)
return;
for (int systemIndex = 0; systemIndex < systems.Length; ++systemIndex)
{
if (systems[systemIndex].type == typeof(XRHandSubsystemPlayerLoopRunnerUpdateSystem))
{
indexOfOurType = systemIndex;
break;
}
}
if (indexOfOurType < 0)
return;
if (indexOfOurType == 0 && systems.Length == 1)
systems = null;
else if (indexOfOurType < systems.Length - 1)
Array.Copy(systems, indexOfOurType + 1, systems, indexOfOurType, systems.Length - indexOfOurType - 1);
if (systems != null)
Array.Resize(ref systems, systems.Length - 1);
playerLoop.subSystemList[earlyUpdateStepIndex].subSystemList[xrUpdateIndex].subSystemList = systems;
UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop(playerLoop);
}
/// <summary>
/// Destroys the automatic updating of the <see cref="XRHandSubsystem"/>.
/// You should call this from your plug-in when calling <c>Destroy</c>
/// on the subsystem.
/// </summary>
public void Destroy()
{
Stop();
m_Subsystem = null;
#if ENABLE_INPUT_SYSTEM && (ENABLE_VR || UNITY_GAMECORE)
if (XRHandDevice.leftHand != null)
{
InputSystem.InputSystem.RemoveDevice(XRHandDevice.leftHand);
XRHandDevice.leftHand = null;
}
if (XRHandDevice.rightHand != null)
{
InputSystem.InputSystem.RemoveDevice(XRHandDevice.rightHand);
XRHandDevice.rightHand = null;
}
#endif // ENABLE_INPUT_SYSTEM && (ENABLE_VR || UNITY_GAMECORE)
}
void OnUpdate()
{
#if UNITY_EDITOR
m_AllowUpdates = true;
#endif // UNITY_EDITOR
Update(XRHandSubsystem.UpdateType.Dynamic);
}
void OnBeforeRender()
{
Update(XRHandSubsystem.UpdateType.BeforeRender);
#if UNITY_EDITOR
m_AllowUpdates = false;
#endif // UNITY_EDITOR
}
void Update(XRHandSubsystem.UpdateType updateType)
{
#if UNITY_EDITOR
if (!m_AllowUpdates)
return;
#endif // UNITY_EDITOR
var updateSuccessFlags = m_Subsystem.TryUpdateHands(updateType);
if (updateSuccessFlags != XRHandSubsystem.UpdateSuccessFlags.None)
EnsureDevicesCreated(updateSuccessFlags, updateType);
}
// using this approach avoids initialization timing issues where
// InputSystem tears itself down at inopportune times
// (XRHandDevice.Create takes update success flags and type to
// use the same method at startup as it does during normal updates
// to avoid skipping the first frame of valid updates from the
// subsystem)
void EnsureDevicesCreated(XRHandSubsystem.UpdateSuccessFlags updateSuccessFlags, XRHandSubsystem.UpdateType updateType)
{
#if ENABLE_INPUT_SYSTEM && (ENABLE_VR || UNITY_GAMECORE)
if (XRHandDevice.leftHand == null)
XRHandDevice.leftHand = XRHandDevice.Create(m_Subsystem, Handedness.Left, updateSuccessFlags, updateType);
if (XRHandDevice.rightHand == null)
XRHandDevice.rightHand = XRHandDevice.Create(m_Subsystem, Handedness.Right, updateSuccessFlags, updateType);
#endif // ENABLE_INPUT_SYSTEM && (ENABLE_VR || UNITY_GAMECORE)
}
XRHandSubsystem m_Subsystem;
#if UNITY_EDITOR
bool m_AllowUpdates;
#endif // UNITY_EDITOR
struct XRHandSubsystemPlayerLoopRunnerUpdateSystem
{
}
}
}
}