//-----------------------------------------------------------------------
//
//
// 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
#if AR_FOUNDATION_PRESENT || PACKAGE_DOCS_GENERATION
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine.XR.ARFoundation;
using Unity.XR.CoreUtils;
namespace UnityEngine.XR.Interaction.Toolkit.AR
{
class MockTouch
{
public float deltaTime
{
get => ((Touch)m_Touch).deltaTime;
set => s_Fields["m_TimeDelta"].SetValue(m_Touch, value);
}
public int tapCount
{
get => ((Touch)m_Touch).tapCount;
set => s_Fields["m_TapCount"].SetValue(m_Touch, value);
}
public TouchPhase phase
{
get => ((Touch)m_Touch).phase;
set => s_Fields["m_Phase"].SetValue(m_Touch, value);
}
public Vector2 deltaPosition
{
get => ((Touch)m_Touch).deltaPosition;
set => s_Fields["m_PositionDelta"].SetValue(m_Touch, value);
}
public int fingerId
{
get => ((Touch)m_Touch).fingerId;
set => s_Fields["m_FingerId"].SetValue(m_Touch, value);
}
public Vector2 position
{
get => ((Touch)m_Touch).position;
set => s_Fields["m_Position"].SetValue(m_Touch, value);
}
public Vector2 rawPosition
{
get => ((Touch)m_Touch).rawPosition;
set => s_Fields["m_RawPosition"].SetValue(m_Touch, value);
}
static readonly Dictionary s_Fields = new Dictionary();
readonly object m_Touch = new Touch();
static MockTouch()
{
foreach (var f in typeof(Touch).GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
s_Fields.Add(f.Name, f);
}
public Touch ToTouch() => (Touch)m_Touch;
}
///
/// An adapter struct that wraps a Touch from either the Input System package or the Input Manager.
///
///
///
readonly struct CommonTouch
{
public float deltaTime => m_IsEnhancedTouch ? (float)(m_EnhancedTouch.time - m_EnhancedTouch.startTime) : m_Touch.deltaTime;
public int tapCount => m_IsEnhancedTouch ? m_EnhancedTouch.tapCount : m_Touch.tapCount;
public bool isPhaseBegan => m_IsEnhancedTouch ? m_EnhancedTouch.phase == InputSystem.TouchPhase.Began : m_Touch.phase == TouchPhase.Began;
public bool isPhaseMoved => m_IsEnhancedTouch ? m_EnhancedTouch.phase == InputSystem.TouchPhase.Moved : m_Touch.phase == TouchPhase.Moved;
public bool isPhaseStationary => m_IsEnhancedTouch ? m_EnhancedTouch.phase == InputSystem.TouchPhase.Stationary : m_Touch.phase == TouchPhase.Stationary;
public bool isPhaseEnded => m_IsEnhancedTouch ? m_EnhancedTouch.phase == InputSystem.TouchPhase.Ended : m_Touch.phase == TouchPhase.Ended;
public bool isPhaseCanceled => m_IsEnhancedTouch ? m_EnhancedTouch.phase == InputSystem.TouchPhase.Canceled : m_Touch.phase == TouchPhase.Canceled;
public Vector2 deltaPosition => m_IsEnhancedTouch ? m_EnhancedTouch.delta : m_Touch.deltaPosition;
///
/// Unique ID of the touch used to identify it across multiple frames.
/// Not to be confused with .
///
///
///
public int fingerId => m_IsEnhancedTouch ? m_EnhancedTouch.touchId : m_Touch.fingerId;
public Vector2 position => m_IsEnhancedTouch ? m_EnhancedTouch.screenPosition : m_Touch.position;
readonly Touch m_Touch;
readonly InputSystem.EnhancedTouch.Touch m_EnhancedTouch;
readonly bool m_IsEnhancedTouch;
public CommonTouch(Touch touch)
{
m_Touch = touch;
m_EnhancedTouch = default;
m_IsEnhancedTouch = false;
}
public CommonTouch(InputSystem.EnhancedTouch.Touch touch)
{
m_Touch = default;
m_EnhancedTouch = touch;
m_IsEnhancedTouch = true;
}
///
/// Gets the if constructed with .
///
/// Returns the used to construct this object. Otherwise, throws an exception.
/// Throws when this object was constructed with an Input System .
public Touch GetTouch() =>
!m_IsEnhancedTouch
? m_Touch
: throw new InvalidOperationException($"Cannot convert to {typeof(Touch).FullName} since this was sourced from the Input System package.");
///
/// Gets the if constructed with .
///
/// Returns the used to construct this object. Otherwise, throws an exception.
/// Throws when this object was constructed with an Input Manager .
public InputSystem.EnhancedTouch.Touch GetEnhancedTouch() =>
m_IsEnhancedTouch
? m_EnhancedTouch
: throw new InvalidOperationException($"Cannot convert to {typeof(InputSystem.EnhancedTouch.Touch).FullName} since this was sourced from Input Manager Input.");
}
///
/// Singleton used by Gestures and GestureRecognizers to interact with touch input.
///
/// 1. Makes it easy to find touches by fingerId.
/// 2. Allows Gestures to Lock/Release fingerIds.
/// 3. Wraps Input.Touches so that it works both in editor and on device.
/// 4. Provides helper functions for converting touch coordinates
/// and performing ray casts based on touches.
///
static class GestureTouchesUtility
{
public enum TouchInputSource
{
Legacy,
Enhanced,
Mock,
}
const float k_EdgeThresholdInches = 0.1f;
///
/// The default source of Touch input this class uses.
///
///
/// Defaults to use legacy Input Manager Touch input when the Active Input Handling mode of the Unity project
/// is set to Both for backwards compatibility with existing projects.
///
public static readonly TouchInputSource defaultTouchInputSource =
#if ENABLE_LEGACY_INPUT_MANAGER
TouchInputSource.Legacy;
#else
TouchInputSource.Enhanced;
#endif
public static TouchInputSource touchInputSource { get; set; } = defaultTouchInputSource;
///
/// The list of the status of all touches.
/// Does not return a copy in order to avoid allocation.
///
public static List touches
{
get
{
s_Touches.Clear();
switch (touchInputSource)
{
case TouchInputSource.Legacy:
for (int index = 0, touchCount = Input.touchCount; index < touchCount; ++index)
s_Touches.Add(new CommonTouch(Input.GetTouch(index)));
break;
case TouchInputSource.Enhanced:
// ReSharper disable once ForCanBeConvertedToForeach -- Would produce garbage, ReadOnlyArray does not use a struct for the enumerator
for (var index = 0; index < InputSystem.EnhancedTouch.Touch.activeTouches.Count; ++index)
{
var touch = InputSystem.EnhancedTouch.Touch.activeTouches[index];
s_Touches.Add(new CommonTouch(touch));
}
break;
case TouchInputSource.Mock:
foreach (var touch in mockTouches)
s_Touches.Add(new CommonTouch(touch.ToTouch()));
break;
}
return s_Touches;
}
}
static readonly List s_Touches = new List();
internal static readonly List mockTouches = new List();
static readonly HashSet s_RetainedFingerIds = new HashSet();
///
/// Try to find a touch for a particular finger id.
///
/// The finger id to find the touch.
/// The output touch.
/// True if a touch was found.
public static bool TryFindTouch(int fingerId, out CommonTouch touchOut)
{
foreach (var touch in touches)
{
if (touch.fingerId == fingerId)
{
touchOut = touch;
return true;
}
}
touchOut = default;
return false;
}
///
/// Converts Pixels to Inches.
///
/// The amount to convert in pixels.
/// The converted amount in inches.
public static float PixelsToInches(float pixels) => pixels / Screen.dpi;
///
/// Converts Inches to Pixels.
///
/// The amount to convert in inches.
/// The converted amount in pixels.
public static float InchesToPixels(float inches) => inches * Screen.dpi;
///
/// Used to determine if a touch is off the edge of the screen based on some slop.
/// Useful to prevent accidental touches from simply holding the device from causing
/// confusing behavior.
///
/// The touch to check.
/// True if the touch is off screen edge.
public static bool IsTouchOffScreenEdge(CommonTouch touch)
{
var slopPixels = InchesToPixels(k_EdgeThresholdInches);
var result = touch.position.x <= slopPixels;
result |= touch.position.y <= slopPixels;
result |= touch.position.x >= Screen.width - slopPixels;
result |= touch.position.y >= Screen.height - slopPixels;
return result;
}
///
/// Performs a Raycast from the camera.
///
///
/// Unity uses the 3D physics scene of the camera when performing the ray cast.
///
/// The screen position to perform the ray cast from.
/// The whose Camera is used for ray casting.
/// The fallback whose Camera is used for ray casting.
/// When this method returns, contains the result.
/// The layer mask used for limiting raycast targets.
/// The type of interaction with trigger colliders via raycast.
/// Returns if an object was hit. Otherwise, returns .
#pragma warning disable CS0618 // ARSessionOrigin is deprecated in 5.0, but kept to support older AR Foundation versions
public static bool RaycastFromCamera(Vector2 screenPos, XROrigin sessionOrigin, ARSessionOrigin arSessionOrigin, out RaycastHit result, LayerMask layerMask = default, QueryTriggerInteraction triggerInteraction = QueryTriggerInteraction.Ignore)
#pragma warning restore CS0618
{
// The ARSessionOrigin parameter will eventually be removed. This class is internal so no need for overloaded.
var camera = sessionOrigin != null
? sessionOrigin.Camera
#pragma warning disable CS0618 // ARSessionOrigin will eventually be removed, but until then use the oldest access method
: (arSessionOrigin != null ? arSessionOrigin.camera : Camera.main);
#pragma warning restore CS0618
if (camera == null)
{
result = default;
return false;
}
var ray = camera.ScreenPointToRay(screenPos);
return camera.gameObject.scene.GetPhysicsScene().Raycast(ray.origin, ray.direction, out result, Mathf.Infinity, layerMask, triggerInteraction);
}
///
/// Locks a finger Id.
///
/// The finger id to lock.
public static void LockFingerId(int fingerId)
{
s_RetainedFingerIds.Add(fingerId);
}
///
/// Releases a finger Id.
///
/// The finger id to release.
public static void ReleaseFingerId(int fingerId)
{
s_RetainedFingerIds.Remove(fingerId);
}
///
/// Returns true if the finger Id is retained.
///
/// The finger id to check.
/// Returns if the finger is retained. Otherwise, returns .
public static bool IsFingerIdRetained(int fingerId)
{
return s_RetainedFingerIds.Contains(fingerId);
}
}
}
#endif