using Unity.XR.CoreUtils;
using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers;
using UnityEngine.XR.Interaction.Toolkit.Utilities;
namespace UnityEngine.XR.Interaction.Toolkit.AR.Inputs
{
///
/// Applies the pose computed from the touchscreen gestures to the Transform this component is on.
/// Similar to a Tracked Pose Driver in purpose but uses touchscreen gesture input sources to compute
/// the pose rather than directly using input actions.
///
[AddComponentMenu("XR/Input/Screen Space Ray Pose Driver", 11)]
[HelpURL(XRHelpURLConstants.k_ScreenSpaceRayPoseDriver)]
[DefaultExecutionOrder(XRInteractionUpdateOrder.k_ScreenSpaceRayPoseDriver)]
public class ScreenSpaceRayPoseDriver : MonoBehaviour
{
[SerializeField]
[Tooltip("The camera associated with the screen, and through which screen presses/touches will be interpreted.")]
Camera m_ControllerCamera;
///
/// The camera associated with the screen, and through which screen presses/touches will be interpreted.
///
public Camera controllerCamera
{
get => m_ControllerCamera;
set => m_ControllerCamera = value;
}
[SerializeField]
XRInputValueReader m_TapStartPositionInput = new XRInputValueReader("Tap Start Position");
///
/// Input to use for the screen tap start position.
///
///
public XRInputValueReader tapStartPositionInput
{
get => m_TapStartPositionInput;
set => XRInputReaderUtility.SetInputProperty(ref m_TapStartPositionInput, value, this);
}
[SerializeField]
XRInputValueReader m_DragStartPositionInput = new XRInputValueReader("Drag Start Position");
///
/// Input to use for the screen drag start position.
///
///
public XRInputValueReader dragStartPositionInput
{
get => m_DragStartPositionInput;
set => XRInputReaderUtility.SetInputProperty(ref m_DragStartPositionInput, value, this);
}
[SerializeField]
XRInputValueReader m_DragCurrentPositionInput = new XRInputValueReader("Drag Current Position");
///
/// Input to use for the screen drag current position.
///
///
public XRInputValueReader dragCurrentPositionInput
{
get => m_DragCurrentPositionInput;
set => XRInputReaderUtility.SetInputProperty(ref m_DragCurrentPositionInput, value, this);
}
[SerializeField]
[Tooltip("The input used to read the screen touch count value.")]
XRInputValueReader m_ScreenTouchCountInput = new XRInputValueReader("Screen Touch Count");
///
/// The input used to read the screen touch count value.
///
///
public XRInputValueReader screenTouchCountInput
{
get => m_ScreenTouchCountInput;
set => XRInputReaderUtility.SetInputProperty(ref m_ScreenTouchCountInput, value, this);
}
///
/// Holds captured value of the parent transform of this component to avoid doing an Object comparison to null
/// each frame when it hasn't changed reference.
///
readonly UnityObjectReferenceCache m_ParentTransformCache = new UnityObjectReferenceCache();
Vector2 m_TapStartPosition;
Vector2 m_DragStartPosition;
///
/// See .
///
protected void OnEnable()
{
if (m_ControllerCamera == null)
{
m_ControllerCamera = Camera.main;
if (m_ControllerCamera == null)
{
Debug.LogWarning($"Could not find associated {nameof(Camera)} in scene." +
$"This {nameof(ScreenSpaceRayPoseDriver)} will be disabled.", this);
enabled = false;
return;
}
}
m_TapStartPositionInput.EnableDirectActionIfModeUsed();
m_DragStartPositionInput.EnableDirectActionIfModeUsed();
m_DragCurrentPositionInput.EnableDirectActionIfModeUsed();
m_ScreenTouchCountInput.EnableDirectActionIfModeUsed();
}
///
/// See .
///
protected void OnDisable()
{
m_TapStartPositionInput.DisableDirectActionIfModeUsed();
m_DragStartPositionInput.DisableDirectActionIfModeUsed();
m_DragCurrentPositionInput.DisableDirectActionIfModeUsed();
m_ScreenTouchCountInput.DisableDirectActionIfModeUsed();
}
///
/// See .
///
protected void Update()
{
var prevTapStartPosition = m_TapStartPosition;
var tapPerformedThisFrame = m_TapStartPositionInput.TryReadValue(out m_TapStartPosition) && prevTapStartPosition != m_TapStartPosition;
var prevDragStartPosition = m_DragStartPosition;
var dragStartPerformedThisFrame = m_DragStartPositionInput.TryReadValue(out m_DragStartPosition) && prevDragStartPosition != m_DragStartPosition;
if (m_ScreenTouchCountInput.TryReadValue(out var screenTouchCount) && screenTouchCount > 1)
return;
if (dragStartPerformedThisFrame)
{
ApplyPose(m_DragStartPosition);
return;
}
if (m_DragCurrentPositionInput.TryReadValue(out var dragCurrentPosition))
{
ApplyPose(dragCurrentPosition);
return;
}
if (tapPerformedThisFrame)
{
ApplyPose(m_TapStartPosition);
return;
}
}
void ApplyPose(Vector2 screenPosition)
{
var screenToWorldPoint = m_ControllerCamera.ScreenToWorldPoint(new Vector3(screenPosition.x, screenPosition.y, m_ControllerCamera.nearClipPlane));
var directionVector = (screenToWorldPoint - m_ControllerCamera.transform.position).normalized;
var localPosition = m_ParentTransformCache.TryGet(transform.parent, out var parent)
? parent.InverseTransformPoint(screenToWorldPoint)
: screenToWorldPoint;
transform.SetLocalPose(new Pose(localPosition, Quaternion.LookRotation(directionVector)));
}
}
}