using System; using UnityEngine; using Rokid.UXR.Utility; using Rokid.UXR.Exentesions; namespace Rokid.UXR.Interaction.ThirdParty { /// /// Use MRTK Phyisc /// Implements a move logic that will move an object based on the initial position of /// the grab point relative to the pointer and relative to the object, and subsequent /// changes to the pointer and the object's rotation /// /// Usage: /// When a manipulation starts, call Setup. /// Call Update any time to update the move logic and get a new rotation for the object. /// public class ManipulationMoveLogic { private float pointerRefDistance; private bool pointerPosIndependentOfHead = true; private Vector3 pointerLocalGrabPoint; private Vector3 objectLocalGrabPoint; private Vector3 grabToObject; private Vector3 objectLocalAttachPoint; private Vector3 attachToObject; /// /// Setup function /// public void Setup(Pose pointerCentroidPose, Vector3 grabCentroid, Pose objectPose, Vector3 objectScale) { pointerRefDistance = GetDistanceToBody(pointerCentroidPose); pointerPosIndependentOfHead = pointerRefDistance != 0; Quaternion worldToPointerRotation = Quaternion.Inverse(pointerCentroidPose.rotation); pointerLocalGrabPoint = worldToPointerRotation * (grabCentroid - pointerCentroidPose.position); attachToObject = objectPose.position - pointerCentroidPose.position; objectLocalAttachPoint = Quaternion.Inverse(objectPose.rotation) * (pointerCentroidPose.position - objectPose.position); objectLocalAttachPoint = objectLocalAttachPoint.Div(objectScale); grabToObject = objectPose.position - grabCentroid; objectLocalGrabPoint = Quaternion.Inverse(objectPose.rotation) * (grabCentroid - objectPose.position); objectLocalGrabPoint = objectLocalGrabPoint.Div(objectScale); } /// /// Update the position based on input. /// /// A Vector3 describing the desired position [Obsolete("This update function is out of date and does not properly support Near Manipulation. Use UpdateTransform instead")] public Vector3 Update(Pose pointerCentroidPose, Quaternion objectRotation, Vector3 objectScale, bool usePointerRotation) { return FarManipulationUpdate(pointerCentroidPose, objectRotation, objectScale, usePointerRotation); } /// /// Update the position based on input. /// /// A Vector3 describing the desired position public Vector3 UpdateTransform(Pose pointerCentroidPose, Transform currentTarget, bool isPointerAnchor, bool isNearManipulation) { if (isNearManipulation) { return NearManipulationUpdate(pointerCentroidPose, currentTarget); } else { return FarManipulationUpdate(pointerCentroidPose, currentTarget.rotation, currentTarget.localScale, isPointerAnchor); } } /// /// Updates the position during near manipulation /// /// A Vector3 describing the desired position during near manipulation private Vector3 NearManipulationUpdate(Pose pointerCentroidPose, Transform currentTarget) { Vector3 scaledLocalAttach = Vector3.Scale(objectLocalAttachPoint, currentTarget.localScale); Vector3 worldAttachPoint = currentTarget.rotation * scaledLocalAttach + currentTarget.position; return currentTarget.position + (pointerCentroidPose.position - worldAttachPoint); } /// /// Updates the position during far manipulation /// /// A Vector3 describing the desired position during far manipulation private Vector3 FarManipulationUpdate(Pose pointerCentroidPose, Quaternion objectRotation, Vector3 objectScale, bool isPointerAnchor) { float distanceRatio = 1.0f; if (pointerPosIndependentOfHead) { // Compute how far away the object should be based on the ratio of the current to original hand distance float currentHandDistance = GetDistanceToBody(pointerCentroidPose); distanceRatio = currentHandDistance / pointerRefDistance; } if (isPointerAnchor) { Vector3 scaledGrabToObject = Vector3.Scale(objectLocalGrabPoint, objectScale); Vector3 adjustedPointerToGrab = (pointerLocalGrabPoint * distanceRatio); adjustedPointerToGrab = pointerCentroidPose.rotation * adjustedPointerToGrab; return adjustedPointerToGrab - objectRotation * scaledGrabToObject + pointerCentroidPose.position; } else { return pointerCentroidPose.position + grabToObject + (pointerCentroidPose.rotation * pointerLocalGrabPoint) * distanceRatio; } } private float GetDistanceToBody(Pose pointerCentroidPose) { // The body is treated as a ray, parallel to the y-axis, where the start is head position. // This means that moving your hand down such that is the same distance from the body will // not cause the manipulated object to move further away from your hand. However, when you // move your hand upward, away from your head, the manipulated object will be pushed away. if (pointerCentroidPose.position.y > MainCameraCache.mainCamera.transform.position.y) { return Vector3.Distance(pointerCentroidPose.position, MainCameraCache.mainCamera.transform.position); } else { Vector2 headPosXZ = new Vector2(MainCameraCache.mainCamera.transform.position.x, MainCameraCache.mainCamera.transform.position.z); Vector2 pointerPosXZ = new Vector2(pointerCentroidPose.position.x, pointerCentroidPose.position.z); return Vector2.Distance(pointerPosXZ, headPosXZ); } } } }