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; } } }