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
{
}
}
}
}