using System;
using Unity.Burst;
using Unity.Mathematics;
namespace UnityEngine.XR.Hands.Gestures
{
///
/// Defines hand orientation relative to the user and a target Transform.
///
[Serializable]
public class XRHandRelativeOrientation
{
///
/// A condition that can be used to check the orientation of a hand relative to the user's head
/// or the tracking origin.
///
#if BURST_PRESENT
[BurstCompile]
#endif
[Serializable]
public class UserCondition
{
///
/// The that is used to compare to the direction relative to the target.
///
public XRHandAxis handAxis
{
get => m_HandAxis;
set => m_HandAxis = value;
}
///
/// The used to evaluate the condition.
///
public XRHandAlignmentCondition alignmentCondition
{
get => m_AlignmentCondition;
set => m_AlignmentCondition = value;
}
///
/// The to compare to the for this condition.
///
public XRHandUserRelativeDirection referenceDirection
{
get => m_ReferenceDirection;
set => m_ReferenceDirection = value;
}
///
/// If the is ,
/// this is the maximum angle between the and the reference direction for the condition to be met.
///
/// If the is ,
/// this is the maximum angle between the and the opposite of the reference direction for the condition to be met.
///
/// If the is ,
/// this is the maximum angle difference from 90 degrees (perpendicular) between the and the reference direction for the condition to be met.
///
public float angleTolerance
{
get => m_AngleTolerance;
set => m_AngleTolerance = value;
}
///
/// If enabled, the y position will be ignored when referencing a direction relative to another position.
///
public bool ignorePositionY
{
get => m_IgnorePositionY;
set => m_IgnorePositionY = value;
}
internal bool CheckCondition(
in float3 rootPosition, in quaternion rootRotation,
in RigidTransform originTransform, in RigidTransform headTransform,
float handednessMultiplier)
{
return CheckConditionBursted(
m_HandAxis, m_AlignmentCondition, m_ReferenceDirection,
m_AngleTolerance,
rootPosition, rootRotation,
originTransform, headTransform,
m_IgnorePositionY,
handednessMultiplier);
}
#if BURST_PRESENT
[BurstCompile]
#endif
static bool CheckConditionBursted(
XRHandAxis handAxis,
XRHandAlignmentCondition alignmentCondition,
XRHandUserRelativeDirection referenceDirection,
float tolerance,
in float3 rootPosition, in quaternion rootRotation,
in RigidTransform originTransform,
in RigidTransform headTransform,
bool ignorePositionY, float handednessMultiplier)
{
XRHandOrientationUtility.GetHandAxisDirection(
out var calculatedHandAxisDirection,
handAxis,
rootRotation,
handednessMultiplier);
GetReferenceDirection(
out var calculatedReferenceDirection,
rootPosition,
referenceDirection,
originTransform,
headTransform,
handednessMultiplier);
return XRHandOrientationUtility.CheckDirectionAlignment(
alignmentCondition,
tolerance,
ignorePositionY,
calculatedHandAxisDirection,
calculatedReferenceDirection);
}
#if BURST_PRESENT
[BurstCompile]
#endif
static void GetReferenceDirection(
out float3 result,
in float3 handPosition,
XRHandUserRelativeDirection direction,
in RigidTransform handTrackingOrigin,
in RigidTransform headTransform,
float handednessMultiplier)
{
float3 worldDirection;
switch (direction)
{
case XRHandUserRelativeDirection.OriginUp:
worldDirection = math.rotate(handTrackingOrigin, new float3(0f, 1f, 0f));
break;
case XRHandUserRelativeDirection.HandToHead:
worldDirection = headTransform.pos - math.transform(handTrackingOrigin, handPosition);
break;
case XRHandUserRelativeDirection.NoseDirection:
worldDirection = math.rotate(headTransform, new float3(0f, 0f, 1f));
break;
case XRHandUserRelativeDirection.ChinDirection:
worldDirection = math.rotate(headTransform, new float3(0f, -1f, 0f));
break;
case XRHandUserRelativeDirection.EarDirection:
worldDirection = math.rotate(headTransform, new float3(-handednessMultiplier, 0f, 0f));
break;
default:
worldDirection = new float3(0f, 1f, 0f);
break;
}
result = math.mul(math.inverse(handTrackingOrigin).rot, worldDirection);
}
[SerializeField]
[Tooltip("The axis of the hand using the OpenXR spec axis mapping for hands.")]
XRHandAxis m_HandAxis = XRHandAxis.PalmDirection;
[SerializeField]
[Tooltip("The method used to compare two directions.")]
XRHandAlignmentCondition m_AlignmentCondition = XRHandAlignmentCondition.AlignsWith;
[SerializeField]
[Tooltip("The axis to compare the hand's axis to.")]
XRHandUserRelativeDirection m_ReferenceDirection = XRHandUserRelativeDirection.OriginUp;
[SerializeField]
[Tooltip("The condition will be true if the angle difference between the hand axis and the reference direction (in degrees) is less than this value.")]
[Range(XRHandOrientationUtility.k_MinimumAngleTolerance, XRHandOrientationUtility.k_MaximumAngleTolerance)]
float m_AngleTolerance;
[SerializeField]
[Tooltip("If enabled, the y position will be ignored when comparing to a direction relative to another position.")]
bool m_IgnorePositionY;
}
///
/// A condition that can be used to check the orientation of a hand
/// relative to some other target. The target is another transform
/// referenced by the property.
///
///
/// To check the orientation relative to the user's head or origin, use
/// to avoid the need to set a specific
/// target transform per condition.
///
#if BURST_PRESENT
[BurstCompile]
#endif
[Serializable]
public class TargetCondition
{
///
/// The that is used to compare to the direction relative to the target.
///
public XRHandAxis handAxis
{
get => m_HandAxis;
set => m_HandAxis = value;
}
///
/// The used to evaluate the condition.
///
public XRHandAlignmentCondition alignmentCondition
{
get => m_AlignmentCondition;
set => m_AlignmentCondition = value;
}
///
/// The to compare to the for this condition.
///
public XRHandTargetRelativeDirection referenceDirection
{
get => m_ReferenceDirection;
set => m_ReferenceDirection = value;
}
///
/// If the is ,
/// this is the maximum angle between the and the reference direction for the condition to be met.
///
/// If the is ,
/// this is the maximum angle between the and the opposite of the reference direction for the condition to be met.
///
/// If the is ,
/// this is the maximum angle difference from 90 degrees (perpendicular) between the and the reference direction for the condition to be met.
///
public float angleTolerance
{
get => m_AngleTolerance;
set => m_AngleTolerance = value;
}
///
/// If enabled, the Y position will be ignored when referencing a direction relative to another position.
///
public bool ignorePositionY
{
get => m_IgnorePositionY;
set => m_IgnorePositionY = value;
}
internal bool CheckCondition(
in float3 rootPosition, in quaternion rootRotation,
in RigidTransform originTransform,
in RigidTransform targetTransform,
float handednessMultiplier)
{
return CheckConditionBursted(
m_HandAxis, m_AlignmentCondition, m_ReferenceDirection,
m_AngleTolerance, m_IgnorePositionY,
rootPosition, rootRotation,
originTransform, targetTransform,
handednessMultiplier);
}
#if BURST_PRESENT
[BurstCompile]
#endif
static bool CheckConditionBursted(
XRHandAxis xrHandAxis,
XRHandAlignmentCondition condition,
XRHandTargetRelativeDirection direction,
float threshold, bool ignorePositionY,
in float3 rootPosition, in quaternion rootRotation,
in RigidTransform originTransform, in RigidTransform targetTransform,
float handednessMultiplier)
{
XRHandOrientationUtility.GetHandAxisDirection(
out var currentHandAxisDirection,
xrHandAxis,
rootRotation,
handednessMultiplier);
GetReferenceDirection(
out var currentReferenceDirection,
rootPosition,
direction,
originTransform,
targetTransform);
return XRHandOrientationUtility.CheckDirectionAlignment(
condition,
threshold,
ignorePositionY,
currentHandAxisDirection,
currentReferenceDirection);
}
#if BURST_PRESENT
[BurstCompile]
#endif
static void GetReferenceDirection(
out float3 result,
in float3 rootPosition,
in XRHandTargetRelativeDirection direction,
in RigidTransform handTrackingOrigin,
in RigidTransform targetTransform)
{
float3 worldDirection;
switch (direction)
{
case XRHandTargetRelativeDirection.HandToTarget:
worldDirection = targetTransform.pos - math.transform(handTrackingOrigin, rootPosition);
break;
case XRHandTargetRelativeDirection.TargetForward:
worldDirection = math.rotate(targetTransform, new float3(0f, 0f, 1f));
break;
case XRHandTargetRelativeDirection.TargetRight:
worldDirection = math.rotate(targetTransform, new float3(1f, 0f, 0f));
break;
case XRHandTargetRelativeDirection.TargetUp:
worldDirection = math.rotate(targetTransform, new float3(0f, 1f, 0f));
break;
default:
worldDirection = new float3(0f, 1f, 0f);
break;
}
result = math.mul(math.inverse(handTrackingOrigin).rot, worldDirection);
}
[SerializeField]
[Tooltip("The axis of the hand using the OpenXR spec axis mapping for hands.")]
XRHandAxis m_HandAxis = XRHandAxis.PalmDirection;
[SerializeField]
[Tooltip("The method used to compare two directions.")]
XRHandAlignmentCondition m_AlignmentCondition = XRHandAlignmentCondition.AlignsWith;
[SerializeField]
[Tooltip("The axis to compare the hand's axis to.")]
XRHandTargetRelativeDirection m_ReferenceDirection = XRHandTargetRelativeDirection.HandToTarget;
[SerializeField]
[Tooltip("The condition will be true if the angle difference between the hand axis and the reference direction (in degrees) is less than this value.")]
[Range(XRHandOrientationUtility.k_MinimumAngleTolerance, XRHandOrientationUtility.k_MaximumAngleTolerance)]
float m_AngleTolerance;
[SerializeField]
[Tooltip("If enabled, the y position will be ignored when comparing to a direction relative to another position.")]
bool m_IgnorePositionY;
}
///
/// The list of conditions to check for the hand's orientation, relative to the user.
///
public UserCondition[] userConditions
{
get => m_UserConditions;
set => m_UserConditions = value;
}
[SerializeField]
[Tooltip("A list of conditions to user to check a hand's orientation, relative to the user.")]
UserCondition[] m_UserConditions;
///
/// The list of conditions to check for the hand's orientation, relative to a target Transform.
///
///
/// All of these conditions use .
///
public TargetCondition[] targetConditions
{
get => m_TargetConditions;
set => m_TargetConditions = value;
}
[SerializeField]
[Tooltip("A list of the conditions to use to check a hand's orientation, relative to a target Transform.")]
TargetCondition[] m_TargetConditions;
///
/// The target transform that the are relative to.
///
///
/// This is not used for .
///
public Transform targetTransform
{
get => m_TargetTransform;
set => m_TargetTransform = value;
}
Transform m_TargetTransform;
///
/// Checks the hand orientation conditions to determine whether the hand is in the correct orientation.
///
/// The root pose of the hand.
/// The handedness of the hand.
/// Whether all the conditions are met.
public bool CheckConditions(Pose rootPose, Handedness handedness)
{
if (!XRHandOrientationUtility.TryGetOriginTransform(out var originTransform) ||
!XRHandOrientationUtility.TryGetHeadTransform(out var headTransform))
return false;
var rootPosePosition = (float3)rootPose.position;
var rootPoseRotation = (quaternion)rootPose.rotation;
var originRigidTransform = new RigidTransform(originTransform.rotation, originTransform.position);
var handednessMultiplier = handedness == Handedness.Right ? -1f : 1f;
for (var conditionIndex = 0; conditionIndex < m_UserConditions.Length; ++conditionIndex)
{
if (!m_UserConditions[conditionIndex].CheckCondition(
rootPosePosition, rootPoseRotation,
originRigidTransform,
new RigidTransform(headTransform.rotation, headTransform.position),
handednessMultiplier))
return false;
}
if (m_TargetTransform == null)
return true;
var targetRigidTransform = new RigidTransform(m_TargetTransform.rotation, m_TargetTransform.position);
for (var conditionIndex = 0; conditionIndex < m_TargetConditions.Length; ++conditionIndex)
{
if (!m_TargetConditions[conditionIndex].CheckCondition(
rootPosePosition, rootPoseRotation,
originRigidTransform, targetRigidTransform,
handednessMultiplier))
return false;
}
return true;
}
}
}