using UnityEngine;
namespace Rokid.UXR.Interaction {
/// Tools for working with Unity Poses
///
public static class PoseUtils
{
///
/// Assigns a Pose to a given transform.
///
/// The transform to which apply the pose.
/// The desired pose.
/// If the pose should be applied to the local position/rotation or world position/rotation.
public static void SetPose(this Transform transform, in Pose pose, Space space = Space.World)
{
if (space == Space.World)
{
transform.SetPositionAndRotation(pose.position, pose.rotation);
}
else
{
transform.localRotation = pose.rotation;
transform.localPosition = pose.position;
}
}
///
/// Extract the position/rotation of a given transform.
///
/// The transform from which to extract the pose.
/// If the desired position/rotation is the world or local one.
/// A Pose containing the position/rotation of the transform.
public static Pose GetPose(this Transform transform, Space space = Space.World)
{
if (space == Space.World)
{
return new Pose(transform.position, transform.rotation);
}
else
{
return new Pose(transform.localPosition, transform.localRotation);
}
}
///
/// Compose two poses, applying the first to the second one.
///
/// First pose to compose.
/// Pose to compose over the first one.
/// A Pose with the two operands applied.
public static void Multiply(in Pose a, in Pose b, ref Pose result)
{
result.position = a.position + a.rotation * b.position;
result.rotation = a.rotation * b.rotation;
}
public static Pose Multiply(in Pose a, in Pose b)
{
Pose result = new Pose();
Multiply(a, b, ref result);
return result;
}
///
/// Compose two poses, applying the provided one on top of the caller.
///
/// Pose to compose upon.
/// Pose to compose over the first one.
public static void Premultiply(this ref Pose a, in Pose b)
{
Multiply(a, b, ref a);
}
///
/// Compose two poses, applying the caller on top of the provided pose.
///
/// Pose to compose upon.
/// Pose to compose over the first one.
public static void Postmultiply(this ref Pose a, in Pose b)
{
Multiply(b, a, ref a);
}
///
/// Moves the calling pose towards a target one using interpolation
///
/// Original pose to interpolate from
/// Target pose for the interpolation.
/// Interpolation factor, normalized but will not be clamped.
public static void Lerp(this ref Pose from, in Pose to, float t)
{
Lerp(from, to, t, ref from);
}
///
/// Interpolation between two poses.
///
/// From pose.
/// To pose.
/// Interpolation factor, normalized but will not be clamped.
/// A Pose between a and b
public static void Lerp(in Pose from, in Pose to, float t, ref Pose result)
{
result.position = Vector3.LerpUnclamped(from.position, to.position, t);
result.rotation = Quaternion.SlerpUnclamped(from.rotation, to.rotation, t);
}
public static void Inverse(in Pose a, ref Pose result)
{
result.rotation = Quaternion.Inverse(a.rotation);
result.position = result.rotation * -a.position;
}
public static void Invert(this ref Pose a)
{
Inverse(a, ref a);
}
public static void CopyFrom(this ref Pose to, in Pose from)
{
to.position = from.position;
to.rotation = from.rotation;
}
///
/// Get the position/rotation difference between two transforms.
///
/// The base transform.
/// The target transform.
/// A Pose indicating the position/rotation change
public static Pose Delta(this Transform from, Transform to)
{
return Delta(from.position, from.rotation, to.position, to.rotation);
}
///
/// Get the position/rotation difference between a transform and a pose.
///
/// The base transform.
/// The target pose.
/// A Pose indicating the delta.
public static Pose Delta(this Transform from, in Pose to)
{
return Delta(from.position, from.rotation, to.position, to.rotation);
}
public static void Delta(this Transform from, in Pose to, ref Pose result)
{
Delta(from.position, from.rotation, to.position, to.rotation, ref result);
}
///
/// Get the position/rotation difference between two poses.
///
/// The base pose.
/// The target pose.
/// A Pose indicating the delta.
public static Pose Delta(in Pose from, in Pose to)
{
return Delta(from.position, from.rotation, to.position, to.rotation);
}
///
/// Get the position/rotation difference between two poses, indicated with separated positions and rotations.
///
/// The base position.
/// The base rotation.
/// The target position.
/// The target rotation.
/// A Pose indicating the delta.
private static Pose Delta(Vector3 fromPosition, Quaternion fromRotation, Vector3 toPosition, Quaternion toRotation)
{
Pose result = new Pose();
Delta(fromPosition, fromRotation, toPosition, toRotation, ref result);
return result;
}
private static void Delta(Vector3 fromPosition, Quaternion fromRotation, Vector3 toPosition, Quaternion toRotation, ref Pose result)
{
Quaternion inverseFromRot = Quaternion.Inverse(fromRotation);
result.position = inverseFromRot * (toPosition - fromPosition);
result.rotation = inverseFromRot * toRotation;
}
///
/// Get the world position/rotation of a relative position.
///
/// The transform in which the offset is local.
/// The offset from the reference.
/// A Pose in world units.
public static Pose GlobalPose(this Transform reference, in Pose offset)
{
return new Pose(
reference.position + reference.rotation * offset.position,
reference.rotation * offset.rotation);
}
///
/// Rotate a pose around an axis.
///
/// The pose to mirror.
/// The direction of the mirror.
/// The tangent of the mirror.
/// A mirrored pose.
public static Pose MirrorPoseRotation(this in Pose pose, Vector3 normal, Vector3 tangent)
{
Pose mirrorPose = pose;
Vector3 forward = pose.rotation * -Vector3.forward;
Vector3 projectedForward = Vector3.ProjectOnPlane(forward, normal);
float angleForward = Vector3.SignedAngle(projectedForward, tangent, normal);
Vector3 mirrorForward = Quaternion.AngleAxis(2 * angleForward, normal) * forward;
Vector3 up = pose.rotation * -Vector3.up;
Vector3 projectedUp = Vector3.ProjectOnPlane(up, normal);
float angleUp = Vector3.SignedAngle(projectedUp, tangent, normal);
Vector3 mirrorUp = Quaternion.AngleAxis(2 * angleUp, normal) * up;
mirrorPose.rotation = Quaternion.LookRotation(mirrorForward, mirrorUp);
return mirrorPose;
}
}
}