/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus 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.Grab { public static class GrabPoseHelper { public delegate Pose PoseCalculator(in Pose desiredPose, Transform relativeTo); /// /// Finds the best pose comparing the one that requires the minimum rotation /// and minimum translation. /// /// Root pose to measure from. /// The offset from the root for accurate scoring /// Nearest pose to the desired one at the hand grab pose. /// Modifiers for the score based in rotation and distance. /// The reference transform to apply the calculators to /// Delegate to calculate the nearest, by position, pose at a hand grab pose. /// Delegate to calculate the nearest, by rotation, pose at a hand grab pose. /// The score, normalized, of the best pose. public static GrabPoseScore CalculateBestPoseAtSurface(in Pose desiredPose, in Pose offset, out Pose bestPose, in PoseMeasureParameters scoringModifier, Transform relativeTo, PoseCalculator minimalTranslationPoseCalculator, PoseCalculator minimalRotationPoseCalculator) { if (scoringModifier.PositionRotationWeight == 1f) { bestPose = minimalRotationPoseCalculator(desiredPose, relativeTo); return new GrabPoseScore(desiredPose, bestPose, offset, scoringModifier); } if (scoringModifier.PositionRotationWeight == 0f) { bestPose = minimalTranslationPoseCalculator(desiredPose, relativeTo); return new GrabPoseScore(desiredPose, bestPose, offset, scoringModifier); } Pose minimalTranslationPose = minimalTranslationPoseCalculator(desiredPose, relativeTo); Pose minimalRotationPose = minimalRotationPoseCalculator(desiredPose, relativeTo); bestPose = SelectBestPose(minimalRotationPose, minimalTranslationPose, desiredPose, offset, scoringModifier, out GrabPoseScore bestScore); return bestScore; } /// /// Compares two poses to a reference and returns the most similar one /// /// First root Pose to compare with the reference. /// Second root Pose to compare with the reference. /// Reference pose to measure from. /// The offset from the roots, for accurate scoring /// Modifiers for the score based in rotation and distance. /// Out value with the score of the best pose. /// The most similar pose to reference out of the poses public static Pose SelectBestPose(in Pose poseA, in Pose poseB, in Pose reference, in Pose offset, PoseMeasureParameters scoringModifier, out GrabPoseScore bestScore) { GrabPoseScore poseAScore = new GrabPoseScore(reference, poseA, offset, scoringModifier); GrabPoseScore poseBScore = new GrabPoseScore(reference, poseB, offset, scoringModifier); if (poseAScore.IsBetterThan(poseBScore)) { bestScore = poseAScore; return poseA; } else { bestScore = poseBScore; return poseB; } } /// /// Calculates the score from a point to a set of colliders. /// When the point is outside the colliders the further from their surface means the /// lower the score. /// When the point is inside any of the colliders the score is always higher. /// /// Position to measure against the colliders /// Group of colliders to measure the score /// Output point in the surface or inside the colliders that is near the position /// A GrabPoseScore value containing the score of the position in reference to the colliders public static GrabPoseScore CollidersScore(Vector3 position, Collider[] colliders, out Vector3 hitPoint) { GrabPoseScore bestScore = GrabPoseScore.Max; GrabPoseScore score; hitPoint = position; foreach (Collider collider in colliders) { bool isPointInsideCollider = Collisions.IsPointWithinCollider(position, collider); Vector3 measuringPoint = isPointInsideCollider ? collider.bounds.center : collider.ClosestPoint(position); score = new GrabPoseScore(position, measuringPoint, isPointInsideCollider); if (score.IsBetterThan(bestScore)) { hitPoint = isPointInsideCollider ? position : measuringPoint; bestScore = score; } } return bestScore; } } }