//----------------------------------------------------------------------- // // // Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // 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. // // //----------------------------------------------------------------------- // Modifications copyright © 2020 Unity Technologies ApS using System; using Unity.XR.CoreUtils; #if !AR_FOUNDATION_PRESENT && !PACKAGE_DOCS_GENERATION // Stub class definition used to fool version defines that this MonoScript exists (fixed in 19.3) namespace UnityEngine.XR.Interaction.Toolkit.AR { /// /// Manipulates the position of an object via a drag gesture. /// If not selected, the object will be selected when the drag gesture starts. /// [Obsolete("ARTranslationInteractable has been replaced by the ARTransformer. Use the ARTransformer instead.")] public class ARTranslationInteractable { } } #else namespace UnityEngine.XR.Interaction.Toolkit.AR { /// /// Manipulates the position of an object via a drag gesture. /// If not selected, the object will be selected when the drag gesture starts. /// [AddComponentMenu("XR/AR Translation Interactable", 22)] [RequireComponent(typeof(ARSelectionInteractable))] [HelpURL(XRHelpURLConstants.k_ARTranslationInteractable)] [Obsolete("ARTranslationInteractable has been replaced by the ARTransformer. Use the ARTransformer instead.")] public class ARTranslationInteractable : ARBaseGestureInteractable { [SerializeField] [Tooltip("Controls whether Unity constrains the object vertically, horizontally, or free to move in all axes.")] GestureTransformationUtility.GestureTranslationMode m_ObjectGestureTranslationMode; /// /// Controls whether the object will be constrained vertically, horizontally, or free to move in all axis. /// public GestureTransformationUtility.GestureTranslationMode objectGestureTranslationMode { get => m_ObjectGestureTranslationMode; set => m_ObjectGestureTranslationMode = value; } [SerializeField] [Tooltip("The maximum translation distance of this object.")] float m_MaxTranslationDistance = 10f; /// /// The maximum translation distance of this object. /// public float maxTranslationDistance { get => m_MaxTranslationDistance; set => m_MaxTranslationDistance = value; } [SerializeField] [Tooltip("The LayerMask that Unity uses during an additional ray cast when a user touch does not hit any AR trackable planes.")] LayerMask m_FallbackLayerMask; /// /// The that Unity uses during an additional ray cast /// when a user touch does not hit any AR trackable planes. /// public LayerMask fallbackLayerMask { get => m_FallbackLayerMask; set => m_FallbackLayerMask = value; } const float k_PositionSpeed = 12f; const float k_DiffThreshold = 0.0001f; bool m_IsActive; Vector3 m_DesiredLocalPosition; float m_GroundingPlaneHeight; Vector3 m_DesiredAnchorPosition; Quaternion m_DesiredRotation; GestureTransformationUtility.Placement m_LastPlacement; /// public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase) { base.ProcessInteractable(updatePhase); if (updatePhase == XRInteractionUpdateOrder.UpdatePhase.Dynamic) UpdatePosition(); } /// protected override bool CanStartManipulationForGesture(DragGesture gesture) { // If the gesture isn't targeting this item, don't start manipulating. return gesture.targetObject != null && gesture.targetObject == gameObject && transform.parent != null; } /// protected override void OnStartManipulation(DragGesture gesture) { m_GroundingPlaneHeight = transform.parent.position.y; } /// protected override void OnContinueManipulation(DragGesture gesture) { if (transform.parent == null) { Debug.LogError("Translation Interactable needs a parent object.", this); return; } m_IsActive = true; var desiredPlacement = xrOrigin != null ? GestureTransformationUtility.GetBestPlacementPosition( transform.parent.position, gesture.position, m_GroundingPlaneHeight, 0.03f, maxTranslationDistance, objectGestureTranslationMode, xrOrigin, fallbackLayerMask: m_FallbackLayerMask) #pragma warning disable 618 // Calling deprecated method to help with backwards compatibility. : GestureTransformationUtility.GetBestPlacementPosition( transform.parent.position, gesture.position, m_GroundingPlaneHeight, 0.03f, maxTranslationDistance, objectGestureTranslationMode, arSessionOrigin, fallbackLayerMask: m_FallbackLayerMask); #pragma warning restore 618 if (desiredPlacement.hasHoveringPosition && desiredPlacement.hasPlacementPosition) { // If desired position is lower than current position, don't drop it until it's finished. m_DesiredLocalPosition = transform.parent.InverseTransformPoint(desiredPlacement.hoveringPosition); m_DesiredAnchorPosition = desiredPlacement.placementPosition; m_GroundingPlaneHeight = desiredPlacement.updatedGroundingPlaneHeight; // Rotate if the plane direction has changed. if (((desiredPlacement.placementRotation * Vector3.up) - transform.up).magnitude > k_DiffThreshold) m_DesiredRotation = desiredPlacement.placementRotation; else m_DesiredRotation = transform.rotation; if (desiredPlacement.hasPlane) m_LastPlacement = desiredPlacement; } } /// protected override void OnEndManipulation(DragGesture gesture) { if (!m_LastPlacement.hasPlacementPosition) return; var oldAnchor = transform.parent.gameObject; var desiredPose = new Pose(m_DesiredAnchorPosition, m_LastPlacement.placementRotation); var desiredLocalPosition = transform.parent.InverseTransformPoint(desiredPose.position); if (desiredLocalPosition.magnitude > maxTranslationDistance) desiredLocalPosition = desiredLocalPosition.normalized * maxTranslationDistance; desiredPose.position = transform.parent.TransformPoint(desiredLocalPosition); var anchor = new GameObject("PlacementAnchor").transform; anchor.SetWorldPose(new Pose(m_LastPlacement.placementPosition, m_LastPlacement.placementRotation)); transform.parent = anchor; Destroy(oldAnchor); m_DesiredLocalPosition = Vector3.zero; // Rotate if the plane direction has changed. if (((desiredPose.rotation * Vector3.up) - transform.up).magnitude > k_DiffThreshold) m_DesiredRotation = desiredPose.rotation; else m_DesiredRotation = transform.rotation; // Make sure position is updated one last time. m_IsActive = true; } void UpdatePosition() { if (!m_IsActive) return; // Lerp position. var oldLocalPosition = transform.localPosition; var newLocalPosition = Vector3.Lerp( oldLocalPosition, m_DesiredLocalPosition, Time.deltaTime * k_PositionSpeed); var diffLength = (m_DesiredLocalPosition - newLocalPosition).magnitude; if (diffLength < k_DiffThreshold) { newLocalPosition = m_DesiredLocalPosition; m_IsActive = false; } transform.localPosition = newLocalPosition; // Lerp rotation. var oldRotation = transform.rotation; var newRotation = Quaternion.Lerp(oldRotation, m_DesiredRotation, Time.deltaTime * k_PositionSpeed); transform.rotation = newRotation; } } } #endif