using System; namespace UnityEngine.XR.Hands.ProviderImplementation { /// /// Utility methods for a provider to a . /// public static partial class XRHandProviderUtility { /// /// Create a fully configurable joint with at least a pose in hand /// space, tracking state, and ID. /// /// /// Denotes whether the joint being created is on the left or right hand. /// /// /// The tracking state flags associated with this joint, representing /// which fields of the are valid. /// /// /// The of the joint. When filling out an /// element of an array of , the index must /// be converted with . /// /// /// The pose of the joint in session space, relative to the /// [XROrigin](xref:Unity.XR.CoreUtils.XROrigin). /// /// /// The radius of the joint. The default value is 0f. /// /// /// The linear velocity of the joint in hand space (relative to the /// [XROrigin](xref:Unity.XR.CoreUtils.XROrigin)). The default value is /// Vector3.zero. /// /// /// The angular velocity of the joint in hand space (relative to the /// [XROrigin](xref:Unity.XR.CoreUtils.XROrigin)). The default value is /// Vector3.zero. /// /// /// An with the given pose and other supplied /// data. /// 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, }; } /// /// Use this with your provider (if hand-tracking is enabled in your /// build settings) to have the subsystem automatically update. Users /// can subscribe to to /// respond to updates instead of waiting until a following update step. /// Makes use of UnityEngine.LowLevel.PlayerLoop to insert this /// automatic update at the beginning of the EarlyUpdate.XRUpdate /// loop. /// /// /// If the Input System backend is enabled, this will also create and /// s which update themselves from the /// callback. /// public class SubsystemUpdater { /// /// Initializes and returns an instance of . /// You should create this in your plug-in once the /// is created. /// /// /// The to update automatically. /// public SubsystemUpdater(XRHandSubsystem subsystem) => m_Subsystem = subsystem; /// /// Starts the automatic updating of the . /// You should call this from your plug-in when calling Start /// on the subsystem. /// 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); } /// /// Stops the automatic updating of the . /// You should call this from your plug-in when calling Stop /// on the subsystem. /// 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); } /// /// Destroys the automatic updating of the . /// You should call this from your plug-in when calling Destroy /// on the subsystem. /// 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 { } } } }