using System;
using System.Collections.Generic;
using Unity.XR.CoreUtils.Bindings.Variables;
using UnityEngine.Events;
namespace UnityEngine.XR.Hands
{
///
/// Event type that contains a reference to an XR Hand that was updated.
///
[Serializable]
public sealed class HandUpdatedEvent : UnityEvent
{
}
///
/// Extension methods to check if a flag is set in the enum.
///
public static class UpdateTypesExtensions
{
///
/// Checks if the specific flag is set in this flags enum.
///
/// The flags.
/// The flag to check for.
/// Returns if the flag is set in this flags enum.
public static bool IsSet(this XRHandTrackingEvents.UpdateTypes self, XRHandTrackingEvents.UpdateTypes flag)
{
return (self & flag) == flag;
}
}
///
/// Component that subscribes to hand tracking events from the for a particular
/// and sends UnityEvents for tracking updates.
///
[HelpURL(XRHelpURLConstants.k_XRHandTrackingEvents)]
public class XRHandTrackingEvents : MonoBehaviour
{
///
/// Flags enum that specifies which update type or types to send events for.
///
///
///
[Flags]
public enum UpdateTypes
{
///
/// No update type. Will not send events.
///
None = 0,
///
/// See
///
Dynamic = 1 << 0,
///
/// See
///
BeforeRender = 1 << 1,
}
[SerializeField]
[Tooltip("The hand to track and send events for.")]
Handedness m_Handedness = Handedness.Right;
[SerializeField]
[Tooltip("The update type to send events for when the pose and joints are updated.")]
UpdateTypes m_UpdateType = UpdateTypes.Dynamic;
[SerializeField]
UnityEvent m_PoseUpdated = new UnityEvent();
[SerializeField]
HandUpdatedEvent m_JointsUpdated = new HandUpdatedEvent();
[SerializeField]
UnityEvent m_TrackingAcquired = new UnityEvent();
[SerializeField]
UnityEvent m_TrackingLost = new UnityEvent();
[SerializeField]
UnityEvent m_TrackingChanged = new UnityEvent();
///
/// The hand to track and send events for.
///
public Handedness handedness
{
get => m_Handedness;
set => m_Handedness = value;
}
///
/// Which update type to send events for when the pose and joints are updated.
/// This can be one or multiple options from .
///
///
public UpdateTypes updateType
{
get => m_UpdateType;
set => m_UpdateType = value;
}
///
/// Whether the hand is currently being tracked, stored as a bindable variable that can be subscribed to for value changes.
///
public IReadOnlyBindableVariable bindableHandIsTracked => m_HandIsTracked;
///
/// Whether the hand is currently being tracked
///
public bool handIsTracked => m_Subsystem != null && m_Subsystem.running && m_HandIsTracked.Value;
///
/// The source of the hand tracking data.
///
public XRHandSubsystem subsystem => m_Subsystem;
///
/// The root pose of the hand.
///
public Pose rootPose => m_HandJointsUpdatedEventArgs.hand.rootPose;
///
/// Event that occurs when the joints of the hands have updated.
///
///
/// The passed to each listener is only valid while the event is invoked,
/// do not hold a reference to it.
///
public HandUpdatedEvent jointsUpdated => m_JointsUpdated;
///
/// Event that occurs when the pose of the hands has updated.
///
public UnityEvent poseUpdated => m_PoseUpdated;
///
/// Event that occurs when the hand tracking is acquired.
///
public UnityEvent trackingAcquired => m_TrackingAcquired;
///
/// Event that occurs when the hand tracking is lost.
///
public UnityEvent trackingLost => m_TrackingLost;
///
/// Event that occurs when the hand tracking is acquired (true) or lost (false).
///
public UnityEvent trackingChanged => m_TrackingChanged;
static readonly List k_SubsystemsReuse = new List();
readonly BindableVariable m_HandIsTracked = new BindableVariable();
XRHandSubsystem m_Subsystem;
readonly XRHandJointsUpdatedEventArgs m_HandJointsUpdatedEventArgs = new XRHandJointsUpdatedEventArgs();
///
/// See .
///
protected virtual void Update()
{
if (m_Subsystem != null && m_Subsystem.running)
return;
SubsystemManager.GetSubsystems(k_SubsystemsReuse);
for (var i = 0; i < k_SubsystemsReuse.Count; ++i)
{
var handSubsystem = k_SubsystemsReuse[i];
if (handSubsystem.running)
{
SetSubsystem(handSubsystem);
break;
}
}
}
///
/// See .
///
protected virtual void OnDisable()
{
UnsubscribeFromSubsystem();
}
void UnsubscribeFromSubsystem()
{
if (m_Subsystem != null)
{
m_Subsystem.trackingAcquired -= OnTrackingAcquired;
m_Subsystem.trackingLost -= OnTrackingLost;
m_Subsystem.updatedHands -= OnUpdatedHands;
m_Subsystem = null;
}
}
internal void SetSubsystem(XRHandSubsystem handSubsystem)
{
UnsubscribeFromSubsystem();
m_Subsystem = handSubsystem;
XRHand hand;
if (m_Handedness == Handedness.Left)
hand = m_Subsystem.leftHand;
else if (m_Handedness == Handedness.Right)
hand = m_Subsystem.rightHand;
else
hand = default;
m_HandJointsUpdatedEventArgs.hand = hand;
m_Subsystem.trackingAcquired += OnTrackingAcquired;
m_Subsystem.trackingLost += OnTrackingLost;
m_Subsystem.updatedHands += OnUpdatedHands;
TrackingAcquiredOrLost(hand.isTracked);
}
void OnTrackingAcquired(XRHand hand)
{
if (hand.handedness == m_Handedness)
TrackingAcquiredOrLost(true);
}
void OnTrackingLost(XRHand hand)
{
if (hand.handedness == m_Handedness)
TrackingAcquiredOrLost(false);
}
void TrackingAcquiredOrLost(bool isTracked)
{
m_HandIsTracked.Value = isTracked;
if (m_HandIsTracked.Value)
m_TrackingAcquired?.Invoke();
else
m_TrackingLost?.Invoke();
m_TrackingChanged?.Invoke(isTracked);
}
void OnUpdatedHands(XRHandSubsystem handSubsystem, XRHandSubsystem.UpdateSuccessFlags updateSuccessFlags, XRHandSubsystem.UpdateType updateEventType)
{
if (updateEventType == XRHandSubsystem.UpdateType.Dynamic && !m_UpdateType.IsSet(UpdateTypes.Dynamic)
|| updateEventType == XRHandSubsystem.UpdateType.BeforeRender && !m_UpdateType.IsSet(UpdateTypes.BeforeRender))
return;
if (m_Handedness == Handedness.Left)
{
var leftJointsUpdated = (updateSuccessFlags & XRHandSubsystem.UpdateSuccessFlags.LeftHandJoints) != XRHandSubsystem.UpdateSuccessFlags.None;
var leftRootPoseUpdated = (updateSuccessFlags & XRHandSubsystem.UpdateSuccessFlags.LeftHandRootPose) != XRHandSubsystem.UpdateSuccessFlags.None;
if (leftJointsUpdated || leftRootPoseUpdated)
{
m_HandJointsUpdatedEventArgs.hand = handSubsystem.leftHand;
m_HandJointsUpdatedEventArgs.subsystem = handSubsystem;
}
if (leftJointsUpdated)
m_JointsUpdated?.Invoke(m_HandJointsUpdatedEventArgs);
if (leftRootPoseUpdated)
m_PoseUpdated?.Invoke(m_HandJointsUpdatedEventArgs.hand.rootPose);
}
else if (m_Handedness == Handedness.Right)
{
var rightJointsUpdated = (updateSuccessFlags & XRHandSubsystem.UpdateSuccessFlags.RightHandJoints) != XRHandSubsystem.UpdateSuccessFlags.None;
var rightRootPoseUpdated = (updateSuccessFlags & XRHandSubsystem.UpdateSuccessFlags.RightHandRootPose) != XRHandSubsystem.UpdateSuccessFlags.None;
if (rightJointsUpdated || rightRootPoseUpdated)
{
m_HandJointsUpdatedEventArgs.hand = handSubsystem.rightHand;
m_HandJointsUpdatedEventArgs.subsystem = handSubsystem;
}
if (rightJointsUpdated)
m_JointsUpdated?.Invoke(m_HandJointsUpdatedEventArgs);
if (rightRootPoseUpdated)
m_PoseUpdated?.Invoke(m_HandJointsUpdatedEventArgs.hand.rootPose);
}
}
}
}