/************************************************************************************ Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at https://developer.oculus.com/licenses/oculussdk/ Unless required by applicable law or agreed to in writing, the Utilities SDK distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ************************************************************************************/ using UnityEngine; namespace Oculus.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 RelativeOffset(this Transform to, Transform from) { return RelativeOffset(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 offset. public static Pose RelativeOffset(this Transform to, in Pose from) { return RelativeOffset(from.position, from.rotation, to.position, to.rotation); } public static void RelativeOffset(this Transform to, in Pose from, ref Pose result) { RelativeOffset(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 offset. public static Pose RelativeOffset(in Pose from, in Pose to) { return RelativeOffset(from.position, from.rotation, to.position, to.rotation); } /// /// Get the position/rotation difference between two poses. /// /// The base pose. /// The target pose. /// >A Pose indicating the offset. public static void RelativeOffset(in Pose from, in Pose to, ref Pose result) { RelativeOffset(from.position, from.rotation, to.position, to.rotation, ref result); } /// /// 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 offset. public static Pose RelativeOffset(Vector3 fromPosition, Quaternion fromRotation, Vector3 toPosition, Quaternion toRotation) { Pose result = new Pose(); RelativeOffset(fromPosition, fromRotation, toPosition, toRotation, ref result); return result; } public static void RelativeOffset(Vector3 fromPosition, Quaternion fromRotation, Vector3 toPosition, Quaternion toRotation, ref Pose result) { Quaternion inverseTo = Quaternion.Inverse(toRotation); result.position = inverseTo * (fromPosition - toPosition); result.rotation = inverseTo * fromRotation; } /// /// 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); } /// /// Indicates how similar two poses are. /// /// First pose to compare. /// Second pose to compare. /// The max distance in which the poses can be similar. /// 0 indicates no similitude, 1 for equal poses public static float Similarity(in Pose from, in Pose to, HandPosing.PoseMeasureParameters scoringModifier) { float rotationDifference = RotationalSimilarity(from.rotation, to.rotation); float positionDifference = PositionalSimilarity(from.position, to.position, scoringModifier.MaxDistance); return positionDifference * (1f - scoringModifier.PositionRotationWeight) + rotationDifference * (scoringModifier.PositionRotationWeight); } /// /// Get how similar two positions are. /// It uses a maximum value to normalize the output /// /// The first position. /// The second position. /// The Maximum distance used to normalise the output /// 0 when the input positions are further than maxDistance, 1 for equal positions. public static float PositionalSimilarity(in Vector3 from, in Vector3 to, float maxDistance) { float distance = Vector3.Distance(from, to); if (distance == 0) { return 1f; } return 1f - Mathf.Clamp01(distance / maxDistance); } /// /// Get how similar two rotations are. /// Since the Quaternion.Dot is bugged in unity. We compare the /// dot products of the forward and up vectors of the rotations. /// /// The first rotation. /// The second rotation. /// 0 for opposite rotations, 1 for equal rotations. public static float RotationalSimilarity(in Quaternion from, in Quaternion to) { float forwardDifference = Vector3.Dot(from * Vector3.forward, to * Vector3.forward) * 0.5f + 0.5f; float upDifference = Vector3.Dot(from * Vector3.up, to * Vector3.up) * 0.5f + 0.5f; return forwardDifference * upDifference; } /// /// 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; } } }