/// <summary>
        /// Initializes the GestureTouchesUtility singleton if needed and returns a valid instance.
        /// </summary>
        /// <returns>The instance of GestureTouchesUtility.</returns>
        private static GestureTouchesUtility _GetInstance()
        {
            if (s_Instance == null)
            {
                s_Instance = new GestureTouchesUtility();
            }

            return(s_Instance);
        }
 /// <summary>
 /// Helper function for creating one finger gestures when a touch begins.
 /// </summary>
 /// <typeparam name="createGestureFunction">Function to be executed to create the
 /// gesture.</param>
 protected internal void TryCreateOneFingerGestureOnTouchBegan(
     Func <Touch, T> createGestureFunction)
 {
     for (int i = 0; i < Input.touches.Length; i++)
     {
         Touch touch = Input.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);
         }
     }
 }
        private void TryCreateGestureTwoFingerGestureOnTouchBeganForTouchIndex(
            int touchIndex,
            Func <Touch, Touch, T> createGestureFunction)
        {
            if (Input.touches[touchIndex].phase != TouchPhase.Began)
            {
                return;
            }

            Touch touch = Input.touches[touchIndex];

            if (GestureTouchesUtility.IsFingerIdRetained(touch.fingerId) ||
                GestureTouchesUtility.IsTouchOffScreenEdge(touch))
            {
                return;
            }

            for (int i = 0; i < Input.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 && Input.touches[i].phase == TouchPhase.Began)
                {
                    continue;
                }

                Touch otherTouch = Input.touches[i];
                if (GestureTouchesUtility.IsFingerIdRetained(otherTouch.fingerId) ||
                    GestureTouchesUtility.IsTouchOffScreenEdge(otherTouch))
                {
                    continue;
                }

                T gesture = createGestureFunction(touch, otherTouch);
                gesture.onStart    += OnStart;
                gesture.onFinished += OnFinished;
                m_Gestures.Add(gesture);
            }
        }
Esempio n. 4
0
        /// <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="translationMode">The translation mode, indicating the plane types allowed.
        /// </param>
        public static Placement GetBestPlacementPosition(
            Vector3 currentAnchorPosition,
            Vector2 screenPos,
            float groundingPlaneHeight,
            float hoverOffset,
            float maxTranslationDistance,
            TranslationMode translationMode)
        {
            Placement result = new Placement();

            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.
            TrackableHit hit;

            if (Frame.Raycast(
                    screenPos.x, screenPos.y, TrackableHitFlags.PlaneWithinBounds, out hit))
            {
                if (hit.Trackable is DetectedPlane)
                {
                    DetectedPlane plane = hit.Trackable as DetectedPlane;
                    if (IsPlaneTypeAllowed(translationMode, plane.PlaneType))
                    {
                        // Avoid detecting the back of existing planes.
                        if ((hit.Trackable is DetectedPlane) &&
                            Vector3.Dot(Camera.main.transform.position - hit.Pose.position,
                                        hit.Pose.rotation * Vector3.up) < 0)
                        {
                            Debug.Log("Hit at back of the current DetectedPlane");
                            return(result);
                        }

                        // Don't allow hovering for vertical or horizontal downward facing planes.
                        if (plane.PlaneType == DetectedPlaneType.Vertical ||
                            plane.PlaneType == DetectedPlaneType.HorizontalDownwardFacing)
                        {
                            // Limit the translation to maxTranslationDistance.
                            groundingPoint = LimitTranslation(
                                hit.Pose.position, currentAnchorPosition, maxTranslationDistance);

                            result.PlacementPlane              = hit;
                            result.PlacementPosition           = groundingPoint;
                            result.HoveringPosition            = groundingPoint;
                            result.UpdatedGroundingPlaneHeight = groundingPoint.y;
                            result.PlacementRotation           = hit.Pose.rotation;
                            return(result);
                        }

                        // Allow hovering for horizontal upward facing planes.
                        if (plane.PlaneType == DetectedPlaneType.HorizontalUpwardFacing)
                        {
                            // Return early if the camera is pointing upwards.
                            if (angle < 0f)
                            {
                                return(result);
                            }

                            // Limit the translation to maxTranslationDistance.
                            groundingPoint = LimitTranslation(
                                hit.Pose.position, currentAnchorPosition, maxTranslationDistance);

                            // Find the hovering position by casting from the camera onto the
                            // grounding plane and offsetting the result by the hover offset.
                            result.HoveringPosition = groundingPoint + (Vector3.up * hoverOffset);

                            // If the AR Plane is above the grounding plane, then the hit plane's
                            // position is used to replace the current groundingPlane. Otherwise,
                            // the hit is ignored because hits are only detected on lower planes by
                            // casting straight downwards in world space.
                            if (groundingPoint.y > groundingPlaneHeight)
                            {
                                result.PlacementPlane              = hit;
                                result.PlacementPosition           = groundingPoint;
                                result.UpdatedGroundingPlaneHeight = hit.Pose.position.y;
                                result.PlacementRotation           = hit.Pose.rotation;
                                return(result);
                            }
                        }
                        else
                        {
                            // Not supported plane type.
                            return(result);
                        }
                    }
                    else
                    {
                        // Plane type not allowed.
                        return(result);
                    }
                }
                else
                {
                    // Hit is not a plane.
                    return(result);
                }
            }

            // Return early if the camera is pointing upwards.
            if (angle < 0f)
            {
                return(result);
            }

            // If the grounding point is lower than the current gorunding 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.HoveringPosition = groundingPoint + (Vector3.up * hoverOffset);
            }
            else
            {
                // If we can't successfully cast onto the groundingPlane, just return early.
                return(result);
            }

            // Cast straight down onto AR planes that are lower than the current grounding plane.
            if (Frame.Raycast(
                    groundingPoint + (Vector3.up * k_DownRayOffset), Vector3.down,
                    out hit, Mathf.Infinity, TrackableHitFlags.PlaneWithinBounds))
            {
                result.PlacementPosition = hit.Pose.position;
                result.PlacementPlane    = hit;
                result.PlacementRotation = hit.Pose.rotation;
                return(result);
            }

            return(result);
        }