using System;
using Unity.Collections;
using UnityEngine.SubsystemsImplementation;
using UnityEngine.XR.Hands.Gestures;
namespace UnityEngine.XR.Hands.ProviderImplementation
{
///
/// Methods to implement by the implementing provider for an .
///
public abstract class XRHandSubsystemProvider
: SubsystemProvider
{
///
/// Gets the layout of hand joints for this provider, by having the
/// provider mark each index corresponding to a
/// get marked as if the provider attempts to track
/// that joint.
///
///
/// Called once on creation so that before the subsystem is even started,
/// the user can immediately create a valid hierarchical structure as
/// soon as they get a reference to the subsystem without even needing to
/// start it. This is called before any call to
/// .
///
///
/// Each index corresponds to a . For each
/// joint that the provider will attempt to track, mark that spot as
/// by calling .ToIndex() on that ID.
///
public abstract void GetHandLayout(NativeArray handJointsInLayout);
///
/// Gets the on the current
/// device for the given .
///
///
/// Called once for each finger on creation so that the subsystem will
/// always have valid configurations to base detection math off of. If
/// the provider does not override this, defaults will be reported -
/// this means that if the device is more constrained in reporting joint
/// data than the defaults, gestures and poses may not be detected correctly.
/// Called after for each finger, but before
/// the subsystem is returned during a call to
/// .
///
///
/// Which finger to get the for.
///
///
/// A populated representing
/// range of motion for the given .
///
///
/// Will throw an exception if is not a named
/// value of .
///
public virtual XRFingerShapeConfiguration GetFingerShapeConfiguration(XRHandFingerID fingerID)
{
switch (fingerID)
{
case XRHandFingerID.Thumb:
return FingerConfigDefaults.k_Thumb;
case XRHandFingerID.Index:
return FingerConfigDefaults.k_Index;
case XRHandFingerID.Middle:
return FingerConfigDefaults.k_Middle;
case XRHandFingerID.Ring:
return FingerConfigDefaults.k_Ring;
case XRHandFingerID.Little:
return FingerConfigDefaults.k_Little;
default:
throw new ArgumentException("Invalid XRHandFingerID!");
}
}
///
/// Attempts to retrieve current hand-tracking data from the provider.
///
///
/// Informs the provider which kind of timing the update is being
/// requested under.
///
///
/// Update this and include XRHandSubsystem.UpdateSuccesFlags.LeftHandRootPose
/// in the return value to update the left hand's root pose.
///
///
/// Array of hand joints to fill out for the left hand. These are
/// initialized with a copy of the current joint data for the left hand,
/// so if the last known tracking data for a particular joint is still
/// fine, you don't need to fill out that data again. If you update
/// these, include XRHandSubsystem.UpdateSuccesFlags.LeftHandJoints
/// in the return value to have the changes reflected in the subsystem.
///
///
/// Update this and include XRHandSubsystem.UpdateSuccesFlags.RightHandRootPose
/// in the return value to update the right hand's root pose.
///
///
/// Array of hand joints to fill out for the right hand. These are
/// initialized with a copy of the current joint data for the right hand,
/// so if the last known tracking data for a particular joint is still
/// fine, you don't need to fill out that data again. If you update
/// these, include XRHandSubsystem.UpdateSuccesFlags.RightHandJoints
/// in the return value to have the changes reflected in the subsystem.
///
///
/// Returns to describe which tracking
/// data was successfully updated.
///
public abstract XRHandSubsystem.UpdateSuccessFlags TryUpdateHands(
XRHandSubsystem.UpdateType updateType,
ref Pose leftHandRootPose,
NativeArray leftHandJoints,
ref Pose rightHandRootPose,
NativeArray rightHandJoints);
///
/// Whether the provider is currently able to surface data from any of ,
/// , , ,
/// , , or .
///
public virtual bool canSurfaceCommonPoseData => false;
///
/// Gets the aim pose. Will only be called if
/// is enabled.
///
///
/// Which hand to retrieve data for.
///
///
/// The pose to update the aim pose to, if available. Will not be used if
/// is returned.
///
///
/// Returns if successful and the aim pose was
/// filled out, returns otherwise.
///
public virtual bool TryGetAimPose(Handedness handedness, out Pose aimPose)
{
aimPose = Pose.identity;
return false;
}
///
/// Gets the aim activate value. Will only be called if
/// is enabled.
///
///
/// Which hand to retrieve data for.
///
///
/// The aim activate value, if available. Will not be used if
/// is returned.
///
///
/// Returns if successful and the aim activate value was
/// filled out, returns otherwise.
///
public virtual bool TryGetAimActivateValue(Handedness handedness, out float aimActivateValue)
{
aimActivateValue = 0f;
return false;
}
///
/// Gets the grasp value. Will only be called if
/// is enabled.
///
///
/// Which hand to retrieve data for.
///
///
/// The grasp value, if available. Will not be used if
/// is returned.
///
///
/// Returns if successful and the grasp value was
/// filled out, returns otherwise.
///
public virtual bool TryGetGraspValue(Handedness handedness, out float graspValue)
{
graspValue = 0f;
return false;
}
///
/// Gets the grip pose. Will only be called if
/// is enabled.
///
///
/// Which hand to retrieve data for.
///
///
/// The pose to update the aim pose to, if available. Will not be used if
/// is returned.
///
///
/// Returns if successful and the grip pose was
/// filled out, returns otherwise.
///
public virtual bool TryGetGripPose(Handedness handedness, out Pose gripPose)
{
gripPose = Pose.identity;
return false;
}
///
/// Gets the pinch pose. Will only be called if
/// is enabled.
///
///
/// Which hand to retrieve data for.
///
///
/// The pose to update the pinch pose to, if available. Will not be used if
/// is returned.
///
///
/// Returns if successful and the pinch pose was
/// filled out, returns otherwise.
///
public virtual bool TryGetPinchPose(Handedness handedness, out Pose pinchPose)
{
pinchPose = Pose.identity;
return false;
}
///
/// Gets the pinch value. Will only be called if
/// is enabled.
///
///
/// Which hand to retrieve data for.
///
///
/// The pinch value, if available. Will not be used if
/// is returned.
///
///
/// Returns if successful and the grasp value was
/// filled out, returns otherwise.
///
public virtual bool TryGetPinchValue(Handedness handedness, out float pinchValue)
{
pinchValue = 0f;
return false;
}
///
/// Gets the poke pose. Will only be called if
/// is enabled.
///
///
/// Which hand to retrieve data for.
///
///
/// The pose to update the poke pose to, if available. Will not be used if
/// is returned.
///
///
/// Returns if successful and the poke pose was
/// filled out, returns otherwise.
///
public virtual bool TryGetPokePose(Handedness handedness, out Pose pokePose)
{
pokePose = Pose.identity;
return false;
}
// these defaults were captured using a Meta Quest 2
static class FingerConfigDefaults
{
static internal readonly XRFingerShapeConfiguration k_Thumb = new XRFingerShapeConfiguration
{
minimumFullCurlDegrees1 = 132f,
maximumFullCurlDegrees1 = 162f,
minimumFullCurlDegrees2 = 129f,
maximumFullCurlDegrees2 = 180f,
minimumFullCurlDegrees3 = -1f,
maximumFullCurlDegrees3 = -1f,
minimumBaseCurlDegrees = 2f,
maximumBaseCurlDegrees = 60f,
minimumTipCurlDegrees1 = 119f,
maximumTipCurlDegrees1 = 169f,
minimumTipCurlDegrees2 = 129f,
maximumTipCurlDegrees2 = 180f,
minimumPinchDistance = -1f,
maximumPinchDistance = -1f,
minimumSpreadDegrees = 3f,
maximumSpreadDegrees = 57f,
};
static internal readonly XRFingerShapeConfiguration k_Index = new XRFingerShapeConfiguration
{
minimumFullCurlDegrees1 = 102f,
maximumFullCurlDegrees1 = 180f,
minimumFullCurlDegrees2 = 90f,
maximumFullCurlDegrees2 = 174f,
minimumFullCurlDegrees3 = 120f,
maximumFullCurlDegrees3 = 180f,
minimumBaseCurlDegrees = 102f,
maximumBaseCurlDegrees = 180f,
minimumTipCurlDegrees1 = 90f,
maximumTipCurlDegrees1 = 174f,
minimumTipCurlDegrees2 = 120f,
maximumTipCurlDegrees2 = 180f,
minimumPinchDistance = 0.01f,
maximumPinchDistance = 0.045f,
minimumSpreadDegrees = 3f,
maximumSpreadDegrees = 18f,
};
static internal readonly XRFingerShapeConfiguration k_Middle = new XRFingerShapeConfiguration
{
minimumFullCurlDegrees1 = 92f,
maximumFullCurlDegrees1 = 180f,
minimumFullCurlDegrees2 = 90f,
maximumFullCurlDegrees2 = 174f,
minimumFullCurlDegrees3 = 116f,
maximumFullCurlDegrees3 = 180f,
minimumBaseCurlDegrees = 92f,
maximumBaseCurlDegrees = 180f,
minimumTipCurlDegrees1 = 90f,
maximumTipCurlDegrees1 = 174f,
minimumTipCurlDegrees2 = 116f,
maximumTipCurlDegrees2 = 180f,
minimumPinchDistance = 0.01f,
maximumPinchDistance = 0.045f,
minimumSpreadDegrees = 3f,
maximumSpreadDegrees = 20f,
};
static internal readonly XRFingerShapeConfiguration k_Ring = new XRFingerShapeConfiguration
{
minimumFullCurlDegrees1 = 90f,
maximumFullCurlDegrees1 = 180f,
minimumFullCurlDegrees2 = 90f,
maximumFullCurlDegrees2 = 174f,
minimumFullCurlDegrees3 = 112f,
maximumFullCurlDegrees3 = 180f,
minimumBaseCurlDegrees = 95f,
maximumBaseCurlDegrees = 180f,
minimumTipCurlDegrees1 = 90f,
maximumTipCurlDegrees1 = 174f,
minimumTipCurlDegrees2 = 112f,
maximumTipCurlDegrees2 = 180f,
minimumPinchDistance = 0.01f,
maximumPinchDistance = 0.045f,
minimumSpreadDegrees = 3f,
maximumSpreadDegrees = 20f,
};
static internal readonly XRFingerShapeConfiguration k_Little = new XRFingerShapeConfiguration
{
minimumFullCurlDegrees1 = 90f,
maximumFullCurlDegrees1 = 180f,
minimumFullCurlDegrees2 = 90f,
maximumFullCurlDegrees2 = 164f,
minimumFullCurlDegrees3 = 116f,
maximumFullCurlDegrees3 = 180f,
minimumBaseCurlDegrees = 95f,
maximumBaseCurlDegrees = 180f,
minimumTipCurlDegrees1 = 90f,
maximumTipCurlDegrees1 = 164f,
minimumTipCurlDegrees2 = 116f,
maximumTipCurlDegrees2 = 180f,
minimumPinchDistance = 0.01f,
maximumPinchDistance = 0.045f,
minimumSpreadDegrees = -1f,
maximumSpreadDegrees = -1f,
};
}
}
}