using System;
using Unity.XR.CoreUtils;
using Unity.XR.CoreUtils.Datums;
namespace UnityEngine.XR.Interaction.Toolkit.UI.BodyUI
{
///
/// Represents the reference axis relative to the tracking anchor used to compare up and camera facing direction.
///
public enum FollowReferenceAxis
{
///
/// Represents the positive X axis.
///
Right,
///
/// Represents the positive Y axis.
///
Up,
///
/// Represents the positive Z axis.
///
Forward,
///
/// Represents the negative X axis.
///
Left,
///
/// Represents the negative Y axis.
///
Down,
///
/// Represents the negative Z axis.
///
Back,
}
///
/// Class that defines the configuration of a following behaviour for a hand or object.
/// It determines how an object should follow the hand and includes specifications about local position and rotation,
/// angle constraints, gaze snapping, and smoothing settings.
///
[Serializable]
public class FollowPreset
{
///
/// Local space anchor position for the right hand.
///
[Header("Local Space Anchor Transform")]
[Tooltip("Local space anchor position for the right hand.")]
public Vector3 rightHandLocalPosition;
///
/// Local space anchor position for the left hand.
///
[Tooltip("Local space anchor position for the left hand.")]
public Vector3 leftHandLocalPosition;
///
/// Local space anchor rotation for the right hand.
///
[Tooltip("Local space anchor rotation for the right hand.")]
public Vector3 rightHandLocalRotation;
///
/// Local space anchor rotation for the left hand.
///
[Tooltip("Local space anchor rotation for the left hand.")]
public Vector3 leftHandLocalRotation;
///
/// Reference axis equivalent used for comparisons with the user's gaze direction and the world up direction.
///
[Header("Hand anchor angle constraints")]
[Tooltip("Reference axis equivalent used for comparisons with the user's gaze direction and the world up direction.")]
public FollowReferenceAxis palmReferenceAxis = FollowReferenceAxis.Down;
///
/// Given that the default reference hand for menus is the left hand, it may be required to mirror the reference axis for the right hand.
/// This is not necessary if using the up or down axis as a reference, which is the default for hand tracking. Controllers work best with the right and left axies.
///
[Tooltip("Given that the default reference hand for menus is the left hand, it may be required to mirror the reference axis for the right hand.")]
public bool invertAxisForRightHand;
///
/// Check status of palm reference axis facing the user.
///
[Tooltip("Whether or not check if the palm reference axis is facing the user.")]
public bool requirePalmFacingUser;
///
/// Angle threshold to check if the palm reference axis is facing the user.
///
[Tooltip("The angle threshold in degrees to check if the palm reference axis is facing the user.")]
public float palmFacingUserDegreeAngleThreshold;
///
/// The dot product equivalent to the angle threshold used to check if the palm reference axis is facing the user.
///
public float palmFacingUserDotThreshold => m_PalmFacingUserDotThreshold;
float m_PalmFacingUserDotThreshold;
///
/// Check status of palm reference axis facing up.
///
[Tooltip("Whether or not check if the palm reference axis is facing up.")]
public bool requirePalmFacingUp;
///
/// Angle threshold to check if the palm reference axis is facing up.
///
[Tooltip("The angle threshold in degrees to check if the palm reference axis is facing up.")]
public float palmFacingUpDegreeAngleThreshold;
///
/// The dot product equivalent to the angle threshold used to check if the palm reference axis is facing up.
///
public float palmFacingUpDotThreshold => m_PalmFacingUpDotThreshold;
float m_PalmFacingUpDotThreshold;
///
/// Configures the snap to gaze option.
///
[Header("Snap To gaze config")]
[Tooltip("Whether to snap the following element to the gaze direction.")]
public bool snapToGaze;
///
/// The angle threshold in degrees to snap the following element to the gaze direction.
///
[Tooltip("The angle threshold in degrees to snap the following element to the gaze direction.")]
public float snapToGazeAngleThreshold;
///
/// Dot product threshold for snap to gaze.
///
public float snapToGazeDotThreshold => m_SnapToGazeDotThreshold;
float m_SnapToGazeDotThreshold;
///
/// The amount of time in seconds to wait before hiding the following element after the hand is no longer tracked.
///
[Header("Hide delay config")]
[Tooltip("The amount of time in seconds to wait before hiding the following element after the hand is no longer tracked.")]
public float hideDelaySeconds = 0.25f;
///
/// Whether to allow smoothing of the following element position and rotation.
///
[Header("Smoothing Config")]
[Tooltip("Whether to allow smoothing of the following element position and rotation.")]
public bool allowSmoothing = true;
///
/// The lower bound of smoothing to apply.
///
[Tooltip("The lower bound of smoothing to apply.")]
public float followLowerSmoothingValue = 10f;
///
/// The upper bound of smoothing to apply.
///
[Tooltip("The upper bound of smoothing to apply.")]
public float followUpperSmoothingValue = 16f;
///
/// Applies this preset to the specified tracking offsets for the left and right local positions and rotations.
/// Also recomputes dot product thresholds.
///
/// The Transform object that represents the left tracking offset.
/// The Transform object that represents the right tracking offset.
public void ApplyPreset(Transform leftTrackingOffset, Transform rightTrackingOffset)
{
leftTrackingOffset.SetLocalPose(new Pose(leftHandLocalPosition, Quaternion.Euler(leftHandLocalRotation)));
rightTrackingOffset.SetLocalPose(new Pose(rightHandLocalPosition, Quaternion.Euler(rightHandLocalRotation)));
ComputeDotProductThresholds();
}
///
/// Update the dot product thresholds based on the current angle thresholds.
///
public void ComputeDotProductThresholds()
{
m_PalmFacingUserDotThreshold = AngleToDot(palmFacingUserDegreeAngleThreshold);
m_PalmFacingUpDotThreshold = AngleToDot(palmFacingUpDegreeAngleThreshold);
m_SnapToGazeDotThreshold = AngleToDot(snapToGazeAngleThreshold);
}
static float AngleToDot(float angleDeg)
{
return Mathf.Cos(Mathf.Deg2Rad * angleDeg);
}
///
/// Gets the reference axis relative to the specified tracking root.
/// Adjusts the return value depending on whether or not this is for the user's right hand.
///
/// Tracking root Transform.
/// Whether this is for the user's right hand or not.
/// Returns a representing the reference axis relative to the specified tracking root.
public Vector3 GetReferenceAxisForTrackingAnchor(Transform trackingRoot, bool isRightHand)
{
return trackingRoot.TransformDirection(GetLocalAxis(isRightHand));
}
Vector3 GetLocalAxis(bool isRightHand)
{
Vector3 axis = Vector3.zero;
bool invert = isRightHand && invertAxisForRightHand;
switch (palmReferenceAxis)
{
case FollowReferenceAxis.Right:
axis = invert ? Vector3.left : Vector3.right;
break;
case FollowReferenceAxis.Up:
axis = invert ? Vector3.down : Vector3.up;
break;
case FollowReferenceAxis.Forward:
axis = invert ? Vector3.back : Vector3.forward;
break;
case FollowReferenceAxis.Left:
axis = invert ? Vector3.right : Vector3.left;
break;
case FollowReferenceAxis.Down:
axis = invert ? Vector3.up : Vector3.down;
break;
case FollowReferenceAxis.Back:
axis = invert ? Vector3.forward : Vector3.back;
break;
}
return axis;
}
}
///
/// Serializable container class that holds a value or container asset reference.
///
///
[Serializable]
public class FollowPresetDatumProperty : DatumProperty
{
///
public FollowPresetDatumProperty(FollowPreset value) : base(value)
{
}
///
public FollowPresetDatumProperty(FollowPresetDatum datum) : base(datum)
{
}
}
///
/// container class that holds a float affordance theme value.
///
[CreateAssetMenu(fileName = "Follow Preset Datum", menuName = "XR/Value Datums/Body UI Follow Preset Datum", order = 0)]
[HelpURL(XRHelpURLConstants.k_FollowPresetDatum)]
public class FollowPresetDatum : Datum
{
}
}