/// <inheritdoc /> protected internal override bool UpdateGesture() { var foundTouches = GestureTouchesUtility.TryFindTouch(fingerId1, out var touch1); foundTouches = GestureTouchesUtility.TryFindTouch(fingerId2, out var touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } if (touch1.phase == TouchPhase.Canceled || touch2.phase == TouchPhase.Canceled) { Cancel(); return(false); } if (touch1.phase == TouchPhase.Ended || touch2.phase == TouchPhase.Ended) { Complete(); return(false); } if (touch1.phase == TouchPhase.Moved || touch2.phase == TouchPhase.Moved) { float newgap = (touch1.position - touch2.position).magnitude; gapDelta = newgap - gap; gap = newgap; return(true); } return(false); }
/// <inheritdoc /> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(fingerId1) || GestureTouchesUtility.IsFingerIdRetained(fingerId2)) { Cancel(); return(false); } var foundTouches = GestureTouchesUtility.TryFindTouch(fingerId1, out var touch1); foundTouches = GestureTouchesUtility.TryFindTouch(fingerId2, out var touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } // Check that both fingers are moving. if (touch1.deltaPosition == Vector2.zero || touch2.deltaPosition == Vector2.zero) { return(false); } var rotation = CalculateDeltaRotation( touch1.position, touch2.position, startPosition1, startPosition2); return(Mathf.Abs(rotation) >= twistRecognizer.slopRotation); }
/// <inheritdoc /> protected internal override bool UpdateGesture() { var foundTouches = GestureTouchesUtility.TryFindTouch(fingerId1, out var touch1); foundTouches = GestureTouchesUtility.TryFindTouch(fingerId2, out var touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } if (touch1.isPhaseCanceled || touch2.isPhaseCanceled) { Cancel(); return(false); } if (touch1.isPhaseEnded || touch2.isPhaseEnded) { Complete(); return(false); } if (touch1.isPhaseMoved || touch2.isPhaseMoved) { delta = ((touch1.position + touch2.position) / 2) - position; position = (touch1.position + touch2.position) / 2; return(true); } return(false); }
/// <summary> /// Updates this gesture. /// </summary> /// <returns>True if the update was successful.</returns> protected internal override bool UpdateGesture() { Touch touch; if (GestureTouchesUtility.TryFindTouch(FingerId, out touch)) { if (touch.phase == TouchPhase.Moved) { Delta = touch.position - Position; Position = touch.position; return(true); } else if (touch.phase == TouchPhase.Ended) { Complete(); } else if (touch.phase == TouchPhase.Canceled) { Cancel(); } } else { Cancel(); } return(false); }
/// <inheritdoc /> protected internal override void OnStart() { GestureTouchesUtility.LockFingerId(fingerId1); GestureTouchesUtility.LockFingerId(fingerId2); if (GestureTouchesUtility.RaycastFromCamera(startPosition1, recognizer.arSessionOrigin, out var hit1)) { var gameObject = hit1.transform.gameObject; var interactableObject = gameObject.GetComponentInParent <ARBaseGestureInteractable>(); if (interactableObject != null) { targetObject = interactableObject.gameObject; } } else if (GestureTouchesUtility.RaycastFromCamera(startPosition2, recognizer.arSessionOrigin, out var hit2)) { var gameObject = hit2.transform.gameObject; var interactableObject = gameObject.GetComponentInParent <ARBaseGestureInteractable>(); if (interactableObject != null) { targetObject = interactableObject.gameObject; } } GestureTouchesUtility.TryFindTouch(fingerId1, out var touch1); GestureTouchesUtility.TryFindTouch(fingerId2, out var touch2); position = (touch1.position + touch2.position) / 2; }
/// <summary> /// Updates this gesture. /// </summary> /// <returns>Returns <see langword="true"/> if the update was successful. Returns <see langword="false"/> otherwise.</returns> protected internal override bool UpdateGesture() { if (GestureTouchesUtility.TryFindTouch(fingerId, out var touch)) { var tapRecognizer = m_Recognizer as TapGestureRecognizer; m_ElapsedTime += touch.deltaTime; if (m_ElapsedTime > tapRecognizer.m_TimeSeconds) { Cancel(); } else if (touch.phase == TouchPhase.Moved) { var diff = (touch.position - startPosition).magnitude; var diffInches = GestureTouchesUtility.PixelsToInches(diff); if (diffInches > tapRecognizer.m_SlopInches) { Cancel(); } } else if (touch.phase == TouchPhase.Ended) { Complete(); } } else { Cancel(); } return(false); }
/// <inheritdoc /> protected internal override bool UpdateGesture() { if (GestureTouchesUtility.TryFindTouch(fingerId, out var touch)) { if (touch.isPhaseMoved) { delta = touch.position - position; position = touch.position; return(true); } if (touch.isPhaseEnded) { Complete(); } else if (touch.isPhaseCanceled) { Cancel(); } } else { Cancel(); } return(false); }
/// <summary> /// Updates this gesture. /// </summary> /// <returns>True if the update was successful.</returns> protected internal override bool UpdateGesture() { Touch touch1, touch2; bool foundTouches = GestureTouchesUtility.TryFindTouch(FingerId1, out touch1); foundTouches = GestureTouchesUtility.TryFindTouch(FingerId2, out touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } if (touch1.phase == TouchPhase.Canceled || touch2.phase == TouchPhase.Canceled) { Cancel(); return(false); } if (touch1.phase == TouchPhase.Ended || touch2.phase == TouchPhase.Ended) { Complete(); return(false); } if (touch1.phase == TouchPhase.Moved || touch2.phase == TouchPhase.Moved) { Delta = ((touch1.position + touch2.position) / 2) - Position; Position = (touch1.position + touch2.position) / 2; return(true); } return(false); }
/// <summary> /// Action to be performed when this gesture is started. /// </summary> protected internal override void OnStart() { GestureTouchesUtility.LockFingerId(FingerId1); GestureTouchesUtility.LockFingerId(FingerId2); RaycastHit hit1; RaycastHit hit2; if (GestureTouchesUtility.RaycastFromCamera(StartPosition1, out hit1)) { var gameObject = hit1.transform.gameObject; if (gameObject != null && gameObject.GetComponentInParent <ARGestureInteractor>() != null) { TargetObject = gameObject.GetComponentInParent <ARGestureInteractor>().gameObject; } } else if (GestureTouchesUtility.RaycastFromCamera(StartPosition2, out hit2)) { var gameObject = hit2.transform.gameObject; if (gameObject != null && gameObject.GetComponentInParent <ARGestureInteractor>() != null) { TargetObject = gameObject.GetComponentInParent <ARGestureInteractor>().gameObject; } } Touch touch1; GestureTouchesUtility.TryFindTouch(FingerId1, out touch1); Touch touch2; GestureTouchesUtility.TryFindTouch(FingerId2, out touch2); Position = (touch1.position + touch2.position) / 2; }
/// <summary> /// Returns true if this gesture can start. /// </summary> /// <returns>True if the gesture can start.</returns> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(FingerId1) || GestureTouchesUtility.IsFingerIdRetained(FingerId2)) { Cancel(); return(false); } Touch touch1, touch2; bool foundTouches = GestureTouchesUtility.TryFindTouch(FingerId1, out touch1); foundTouches = GestureTouchesUtility.TryFindTouch(FingerId2, out touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } // Check that at least one finger is moving. if (touch1.deltaPosition == Vector2.zero && touch2.deltaPosition == Vector2.zero) { return(false); } PinchGestureRecognizer pinchRecognizer = m_Recognizer as PinchGestureRecognizer; Vector3 firstToSecondDirection = (StartPosition1 - StartPosition2).normalized; float dot1 = Vector3.Dot(touch1.deltaPosition.normalized, -firstToSecondDirection); float dot2 = Vector3.Dot(touch2.deltaPosition.normalized, firstToSecondDirection); float dotThreshold = Mathf.Cos(pinchRecognizer.m_SlopMotionDirectionDegrees * Mathf.Deg2Rad); // Check angle of motion for the first touch. if (touch1.deltaPosition != Vector2.zero && Mathf.Abs(dot1) < dotThreshold) { return(false); } // Check angle of motion for the second touch. if (touch2.deltaPosition != Vector2.zero && Mathf.Abs(dot2) < dotThreshold) { return(false); } float startgap = (StartPosition1 - StartPosition2).magnitude; Gap = (touch1.position - touch2.position).magnitude; float separation = GestureTouchesUtility.PixelsToInches(Mathf.Abs(Gap - startgap)); if (separation < pinchRecognizer.m_SlopInches) { return(false); } return(true); }
/// <inheritdoc /> protected internal override void OnStart() { GestureTouchesUtility.LockFingerId(fingerId1); GestureTouchesUtility.LockFingerId(fingerId2); GestureTouchesUtility.TryFindTouch(fingerId1, out var touch1); GestureTouchesUtility.TryFindTouch(fingerId2, out var touch2); m_PreviousPosition1 = touch1.position; m_PreviousPosition2 = touch2.position; }
/// <summary> /// Returns true if this gesture can start. /// </summary> /// <returns>Returns <see langword="true"/> if the gesture can start. Returns <see langword="false"/> otherwise.</returns> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(fingerId)) { Cancel(); return(false); } return(true); }
/// <summary> /// Returns true if this gesture can start. /// </summary> /// <returns>True if the gesture can start.</returns> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(FingerId1) || GestureTouchesUtility.IsFingerIdRetained(FingerId2)) { Cancel(); return(false); } Touch touch1, touch2; bool foundTouches = GestureTouchesUtility.TryFindTouch(FingerId1, out touch1); foundTouches = GestureTouchesUtility.TryFindTouch(FingerId2, out touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } // Check that at least one finger is moving. if (touch1.deltaPosition == Vector2.zero && touch2.deltaPosition == Vector2.zero) { return(false); } Vector2 pos1 = touch1.position; float diff1 = (pos1 - StartPosition1).magnitude; Vector2 pos2 = touch2.position; float diff2 = (pos2 - StartPosition2).magnitude; float slopInches = (m_Recognizer as TwoFingerDragGestureRecognizer).m_SlopInches; if (GestureTouchesUtility.PixelsToInches(diff1) < slopInches || GestureTouchesUtility.PixelsToInches(diff2) < slopInches) { return(false); } TwoFingerDragGestureRecognizer recognizer = m_Recognizer as TwoFingerDragGestureRecognizer; // Check both fingers move in the same direction. float dot = Vector3.Dot(touch1.deltaPosition.normalized, touch2.deltaPosition.normalized); if (dot < Mathf.Cos(recognizer.m_AngleThresholdRadians)) { return(false); } return(true); }
void TryCreateOneFingerGestureOnTouchBegan(Func <CommonTouch, T> createGestureFunction) { foreach (var touch in GestureTouchesUtility.touches) { if (touch.isPhaseBegan && !GestureTouchesUtility.IsFingerIdRetained(touch.fingerId) && !GestureTouchesUtility.IsTouchOffScreenEdge(touch)) { var gesture = createGestureFunction(touch); AddGesture(gesture); } } }
/// <inheritdoc /> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(fingerId1) || GestureTouchesUtility.IsFingerIdRetained(fingerId2)) { Cancel(); return(false); } var foundTouches = GestureTouchesUtility.TryFindTouch(fingerId1, out var touch1); foundTouches = GestureTouchesUtility.TryFindTouch(fingerId2, out var touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } // Check that at least one finger is moving. if (touch1.deltaPosition == Vector2.zero && touch2.deltaPosition == Vector2.zero) { return(false); } Vector3 firstToSecondDirection = (startPosition1 - startPosition2).normalized; var dot1 = Vector3.Dot(touch1.deltaPosition.normalized, -firstToSecondDirection); var dot2 = Vector3.Dot(touch2.deltaPosition.normalized, firstToSecondDirection); var dotThreshold = Mathf.Cos(pinchRecognizer.slopMotionDirectionDegrees * Mathf.Deg2Rad); // Check angle of motion for the first touch. if (touch1.deltaPosition != Vector2.zero && Mathf.Abs(dot1) < dotThreshold) { return(false); } // Check angle of motion for the second touch. if (touch2.deltaPosition != Vector2.zero && Mathf.Abs(dot2) < dotThreshold) { return(false); } var startgap = (startPosition1 - startPosition2).magnitude; gap = (touch1.position - touch2.position).magnitude; var separation = GestureTouchesUtility.PixelsToInches(Mathf.Abs(gap - startgap)); return(separation >= pinchRecognizer.slopInches); }
/// <inheritdoc /> protected internal override void OnStart() { if (GestureTouchesUtility.RaycastFromCamera(startPosition, recognizer.arSessionOrigin, out var hit)) { var gameObject = hit.transform.gameObject; if (gameObject != null) { var interactableObject = gameObject.GetComponentInParent <ARBaseGestureInteractable>(); if (interactableObject != null) { targetObject = interactableObject.gameObject; } } } }
/// <summary> /// Helper function for creating one finger gestures when a touch begins. /// </summary> /// <param name="createGestureFunction">Function to be executed to create the gesture.</param> protected internal void TryCreateOneFingerGestureOnTouchBegan( Func <Touch, T> createGestureFunction) { for (int i = 0; i < GestureTouchesUtility.Touches.Length; i++) { Touch touch = GestureTouchesUtility.Touches[i]; if (touch.phase == TouchPhase.Began && !GestureTouchesUtility.IsFingerIdRetained(touch.fingerId) && !GestureTouchesUtility.IsTouchOffScreenEdge(touch)) { T gesture = createGestureFunction(touch); gesture.onStart += OnStart; gesture.onFinished += OnFinished; m_Gestures.Add(gesture); } } }
/// <summary> /// Updates this gesture. /// </summary> /// <returns>True if the update was successful.</returns> protected internal override bool UpdateGesture() { Touch touch1, touch2; bool foundTouches = GestureTouchesUtility.TryFindTouch(FingerId1, out touch1); foundTouches = GestureTouchesUtility.TryFindTouch(FingerId2, out touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } if (touch1.phase == TouchPhase.Canceled || touch2.phase == TouchPhase.Canceled) { Cancel(); return(false); } if (touch1.phase == TouchPhase.Ended || touch2.phase == TouchPhase.Ended) { Complete(); return(false); } if (touch1.phase == TouchPhase.Moved || touch2.phase == TouchPhase.Moved) { float rotation = CalculateDeltaRotation( touch1.position, touch2.position, m_PreviousPosition1, m_PreviousPosition2); DeltaRotation = rotation; m_PreviousPosition1 = touch1.position; m_PreviousPosition2 = touch2.position; return(true); } m_PreviousPosition1 = touch1.position; m_PreviousPosition2 = touch2.position; DeltaRotation = 0.0f; return(false); }
void TryCreateGestureTwoFingerGestureOnTouchBeganForTouchIndex( int touchIndex, Func <Touch, Touch, T> createGestureFunction) { if (GestureTouchesUtility.Touches[touchIndex].phase != TouchPhase.Began) { return; } var touch = GestureTouchesUtility.Touches[touchIndex]; if (GestureTouchesUtility.IsFingerIdRetained(touch.fingerId) || GestureTouchesUtility.IsTouchOffScreenEdge(touch)) { return; } for (var i = 0; i < GestureTouchesUtility.Touches.Length; i++) { if (i == touchIndex) { continue; } // Prevents the same two touches from creating two gestures if both touches began on // the same frame. if (i < touchIndex && GestureTouchesUtility.Touches[i].phase == TouchPhase.Began) { continue; } var otherTouch = GestureTouchesUtility.Touches[i]; if (GestureTouchesUtility.IsFingerIdRetained(otherTouch.fingerId) || GestureTouchesUtility.IsTouchOffScreenEdge(otherTouch)) { continue; } var gesture = createGestureFunction(touch, otherTouch); gesture.onStart += OnStart; gesture.onFinished += OnFinished; m_Gestures.Add(gesture); } }
/// <inheritdoc /> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(fingerId1) || GestureTouchesUtility.IsFingerIdRetained(fingerId2)) { Cancel(); return(false); } var foundTouches = GestureTouchesUtility.TryFindTouch(fingerId1, out var touch1); foundTouches = GestureTouchesUtility.TryFindTouch(fingerId2, out var touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } // Check that at least one finger is moving. if (touch1.deltaPosition == Vector2.zero && touch2.deltaPosition == Vector2.zero) { return(false); } var pos1 = touch1.position; var diff1 = (pos1 - startPosition1).magnitude; var pos2 = touch2.position; var diff2 = (pos2 - startPosition2).magnitude; var slopInches = dragRecognizer.slopInches; if (GestureTouchesUtility.PixelsToInches(diff1) < slopInches || GestureTouchesUtility.PixelsToInches(diff2) < slopInches) { return(false); } // Check both fingers move in the same direction. var dot = Vector3.Dot(touch1.deltaPosition.normalized, touch2.deltaPosition.normalized); return(dot >= Mathf.Cos(dragRecognizer.angleThresholdRadians)); }
/// <inheritdoc /> protected internal override void OnStart() { GestureTouchesUtility.LockFingerId(fingerId); if (GestureTouchesUtility.RaycastFromCamera(startPosition, out var hit)) { var gameObject = hit.transform.gameObject; if (gameObject != null) { var interactableObject = gameObject.GetComponentInParent <ARBaseGestureInteractable>(); if (interactableObject != null) { TargetObject = interactableObject.gameObject; } } } GestureTouchesUtility.TryFindTouch(fingerId, out var touch); position = touch.position; }
/// <summary> /// Action to be performed when this gesture is started. /// </summary> protected internal override void OnStart() { RaycastHit hit; if (GestureTouchesUtility.RaycastFromCamera(startPosition, out hit)) { var gameObject = hit.transform.gameObject; if (gameObject != null) { var interactableObject = gameObject.GetComponentInParent <ARBaseGestureInteractable>(); if (interactableObject != null) { TargetObject = interactableObject.gameObject; } else if (gameObject.layer == 9) { TargetObject = gameObject; } } } }
/// <summary> /// Returns true if this gesture can start. /// </summary> /// <returns>True if the gesture can start.</returns> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(FingerId)) { Cancel(); return(false); } if (GestureTouchesUtility.Touches.Length > 1) { for (int i = 0; i < GestureTouchesUtility.Touches.Length; i++) { Touch currentTouch = GestureTouchesUtility.Touches[i]; if (currentTouch.fingerId != FingerId && !GestureTouchesUtility.IsFingerIdRetained(currentTouch.fingerId)) { return(false); } } } Touch touch; if (GestureTouchesUtility.TryFindTouch(FingerId, out touch)) { Vector2 pos = touch.position; float diff = (pos - StartPosition).magnitude; if (GestureTouchesUtility.PixelsToInches(diff) >= (m_Recognizer as DragGestureRecognizer).m_SlopInches) { return(true); } } else { Cancel(); } return(false); }
void TryCreateGestureTwoFingerGestureOnTouchBeganForTouchIndex( int touchIndex, IReadOnlyList <CommonTouch> touches, Func <CommonTouch, CommonTouch, T> createGestureFunction) { var touch = touches[touchIndex]; if (!touch.isPhaseBegan || GestureTouchesUtility.IsFingerIdRetained(touch.fingerId) || GestureTouchesUtility.IsTouchOffScreenEdge(touch)) { return; } for (var i = 0; i < touches.Count; i++) { if (i == touchIndex) { continue; } var otherTouch = touches[i]; // Prevents the same two touches from creating two gestures if both touches began on // the same frame. if (i < touchIndex && otherTouch.isPhaseBegan) { continue; } if (GestureTouchesUtility.IsFingerIdRetained(otherTouch.fingerId) || GestureTouchesUtility.IsTouchOffScreenEdge(otherTouch)) { continue; } var gesture = createGestureFunction(touch, otherTouch); AddGesture(gesture); } }
/// <summary> /// Returns true if this gesture can start. /// </summary> /// <returns>True if the gesture can start.</returns> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(FingerId1) || GestureTouchesUtility.IsFingerIdRetained(FingerId2)) { Cancel(); return(false); } Touch touch1, touch2; bool foundTouches = GestureTouchesUtility.TryFindTouch(FingerId1, out touch1); foundTouches = GestureTouchesUtility.TryFindTouch(FingerId2, out touch2) && foundTouches; if (!foundTouches) { Cancel(); return(false); } // Check that both fingers are moving. if (touch1.deltaPosition == Vector2.zero || touch2.deltaPosition == Vector2.zero) { return(false); } TwistGestureRecognizer twistRecognizer = m_Recognizer as TwistGestureRecognizer; float rotation = CalculateDeltaRotation( touch1.position, touch2.position, StartPosition1, StartPosition2); if (Mathf.Abs(rotation) < twistRecognizer.m_SlopRotation) { return(false); } return(true); }
#pragma warning restore IDE1006 /// <inheritdoc /> protected internal override bool CanStart() { if (GestureTouchesUtility.IsFingerIdRetained(fingerId)) { Cancel(); return(false); } var touches = GestureTouchesUtility.touches; if (touches.Count > 1) { foreach (var currentTouch in touches) { if (currentTouch.fingerId != fingerId && !GestureTouchesUtility.IsFingerIdRetained(currentTouch.fingerId)) { return(false); } } } if (GestureTouchesUtility.TryFindTouch(fingerId, out var touch)) { var pos = touch.position; var diff = (pos - startPosition).magnitude; if (GestureTouchesUtility.PixelsToInches(diff) >= dragRecognizer.slopInches) { return(true); } } else { Cancel(); } return(false); }
/// <summary> /// Action to be performed when this gesture is finished. /// </summary> protected internal override void OnFinish() { GestureTouchesUtility.ReleaseFingerId(FingerId1); GestureTouchesUtility.ReleaseFingerId(FingerId2); }
/// <summary> /// Action to be performed when this gesture is started. /// </summary> protected internal override void OnStart() { GestureTouchesUtility.LockFingerId(FingerId1); GestureTouchesUtility.LockFingerId(FingerId2); }
/// <inheritdoc /> protected internal override void OnFinish() => GestureTouchesUtility.ReleaseFingerId(fingerId);
/// <summary> /// Calculates the best position to place an object in AR based on screen position. /// Could be used for tapping a location on the screen, dragging an object, or using a fixed /// cursor in the center of the screen for placing and moving objects. /// /// Objects are placed along the x/z of the grounding plane. When placed on an AR plane /// below the grounding plane, the object will drop straight down onto it in world space. /// This prevents the object from being pushed deeper into the scene when moving from a /// higher plane to a lower plane. When moving from a lower plane to a higher plane, this /// function returns a new groundingPlane to replace the old one. /// </summary> /// <returns>The best placement position.</returns> /// <param name="currentAnchorPosition">Position of the parent anchor, i.e., where the /// object is before translation starts.</param> /// <param name="screenPos">Location on the screen in pixels to place the object at.</param> /// <param name="groundingPlaneHeight">The starting height of the plane that the object is /// being placed along.</param> /// <param name="hoverOffset">How much should the object hover above the groundingPlane /// before it has been placed.</param> /// <param name="maxTranslationDistance">The maximum distance that the object can be /// translated.</param> /// <param name="gestureTranslationMode">The translation mode, indicating the plane types allowed. /// </param> public static Placement GetBestPlacementPosition( Vector3 currentAnchorPosition, Vector2 screenPos, float groundingPlaneHeight, float hoverOffset, float maxTranslationDistance, GestureTranslationMode gestureTranslationMode) { Placement result = new Placement(); if (!CheckDependentManagers()) { return(result); } result.UpdatedGroundingPlaneHeight = groundingPlaneHeight; // Get the angle between the camera and the object's down direction. float angle = Vector3.Angle(Camera.main.transform.forward, Vector3.down); angle = 90.0f - angle; float touchOffsetRatio = Mathf.Clamp01(angle / 90.0f); float screenTouchOffset = touchOffsetRatio * k_MaxScreenTouchOffset; screenPos.y += GestureTouchesUtility.InchesToPixels(screenTouchOffset); float hoverRatio = Mathf.Clamp01(angle / 45.0f); hoverOffset *= hoverRatio; float distance = (Camera.main.transform.position - currentAnchorPosition).magnitude; float distanceHoverRatio = Mathf.Clamp01(distance / k_HoverDistanceThreshold); hoverOffset *= distanceHoverRatio; // The best estimate of the point in the plane where the object will be placed: Vector3 groundingPoint; // Get the ray to cast into the scene from the perspective of the camera. if (Raycast(new Vector2(screenPos.x, screenPos.y), s_Hits, TrackableType.Planes)) { var firstHit = s_Hits[0]; var plane = s_ARPlaneManager.GetPlane(firstHit.trackableId); if (plane == null || IsPlaneTypeAllowed(gestureTranslationMode, plane.alignment)) { // Avoid detecting the back of existing planes. if (Vector3.Dot(Camera.main.transform.position - firstHit.pose.position, firstHit.pose.rotation * Vector3.up) < 0) { return(result); } // Don't allow hovering for vertical or horizontal downward facing planes. if (plane == null || (plane.alignment == PlaneAlignment.Vertical || plane.alignment == PlaneAlignment.HorizontalDown || plane.alignment == PlaneAlignment.HorizontalUp)) { groundingPoint = LimitTranslation( firstHit.pose.position, currentAnchorPosition, maxTranslationDistance); if (plane != null) { result.PlacementPlane = plane; result.HasPlane = true; } result.HasPlacementPosition = true; result.PlacementPosition = groundingPoint; result.HasHoveringPosition = true; result.HoveringPosition = groundingPoint; result.UpdatedGroundingPlaneHeight = groundingPoint.y; result.PlacementRotation = firstHit.pose.rotation; return(result); } } else { // Plane type not allowed. return(result); } } // Return early if the camera is pointing upwards. if (angle < 0f) { return(result); } // If the grounding point is lower than the current grounding plane height, or if the // raycast did not return a hit, then we extend the grounding plane to infinity, and do // a new raycast into the scene from the perspective of the camera. Ray cameraRay = Camera.main.ScreenPointToRay(screenPos); Plane groundingPlane = new Plane(Vector3.up, new Vector3(0.0f, groundingPlaneHeight, 0.0f)); // Find the hovering position by casting from the camera onto the grounding plane // and offsetting the result by the hover offset. float enter; if (groundingPlane.Raycast(cameraRay, out enter)) { groundingPoint = LimitTranslation( cameraRay.GetPoint(enter), currentAnchorPosition, maxTranslationDistance); result.HasHoveringPosition = true; result.HoveringPosition = groundingPoint + (Vector3.up * hoverOffset); } else { // If we can't successfully cast onto the groundingPlane, just return early. return(result); } return(result); }