// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using System.Collections.Generic; using Unity.Profiling; using UnityEngine; using UInput = UnityEngine.Input; namespace Microsoft.MixedReality.Toolkit.Input.UnityInput { /// /// Manages Touch devices using unity input system. /// [MixedRealityDataProvider( typeof(IMixedRealityInputSystem), (SupportedPlatforms)(-1), // All platforms supported by Unity "Unity Touch Device Manager")] public class UnityTouchDeviceManager : BaseInputDeviceManager { /// /// Constructor. /// /// The instance that loaded the data provider. /// The instance that receives data from this provider. /// Friendly name of the service. /// Service priority. Used to determine order of instantiation. /// The service's configuration profile. [System.Obsolete("This constructor is obsolete (registrar parameter is no longer required) and will be removed in a future version of the Microsoft Mixed Reality Toolkit.")] public UnityTouchDeviceManager( IMixedRealityServiceRegistrar registrar, IMixedRealityInputSystem inputSystem, string name = null, uint priority = DefaultPriority, BaseMixedRealityProfile profile = null) : this(inputSystem, name, priority, profile) { Registrar = registrar; } /// /// Constructor. /// /// The instance that receives data from this provider. /// Friendly name of the service. /// Service priority. Used to determine order of instantiation. /// The service's configuration profile. public UnityTouchDeviceManager( IMixedRealityInputSystem inputSystem, string name = null, uint priority = DefaultPriority, BaseMixedRealityProfile profile = null) : base(inputSystem, name, priority, profile) { } private readonly Dictionary ActiveTouches = new Dictionary(); private List touchesToRemove = new List(); private static readonly ProfilerMarker UpdatePerfMarker = new ProfilerMarker("[MRTK] UnityTouchDeviceManager.Update"); /// public override void Update() { using (UpdatePerfMarker.Auto()) { if (!IsEnabled) { return; } base.Update(); // Ensure that touch up and source lost events are at least one frame apart. for (int i = 0; i < touchesToRemove.Count; i++) { IMixedRealityController controller = touchesToRemove[i]; Service?.RaiseSourceLost(controller.InputSource, controller); } touchesToRemove.Clear(); int touchCount = UInput.touchCount; for (int i = 0; i < touchCount; i++) { Touch touch = UInput.touches[i]; // Construct a ray from the current touch coordinates Ray ray = CameraCache.Main.ScreenPointToRay(touch.position); switch (touch.phase) { case TouchPhase.Began: AddTouchController(touch, ray); break; case TouchPhase.Moved: case TouchPhase.Stationary: UpdateTouchData(touch, ray); break; case TouchPhase.Ended: case TouchPhase.Canceled: RemoveTouchController(touch); break; } } } } /// public override void Disable() { base.Disable(); foreach (var controller in ActiveTouches) { if (controller.Value == null || Service == null) { continue; } foreach (var inputSource in Service.DetectedInputSources) { if (inputSource.SourceId == controller.Value.InputSource.SourceId) { Service.RaiseSourceLost(controller.Value.InputSource, controller.Value); } } } ActiveTouches.Clear(); } private static readonly ProfilerMarker AddTouchControllerPerfMarker = new ProfilerMarker("[MRTK] UnityTouchDeviceManager.AddTouchController"); private void AddTouchController(Touch touch, Ray ray) { using (AddTouchControllerPerfMarker.Auto()) { UnityTouchController controller; if (!ActiveTouches.TryGetValue(touch.fingerId, out controller)) { IMixedRealityInputSource inputSource = null; if (Service != null) { var pointers = RequestPointers(SupportedControllerType.TouchScreen, Handedness.Any); inputSource = Service.RequestNewGenericInputSource($"Touch {touch.fingerId}", pointers); } controller = new UnityTouchController(TrackingState.NotApplicable, Handedness.Any, inputSource); if (inputSource != null) { for (int i = 0; i < inputSource.Pointers.Length; i++) { inputSource.Pointers[i].Controller = controller; var touchPointer = (IMixedRealityTouchPointer)inputSource.Pointers[i]; touchPointer.TouchRay = ray; touchPointer.FingerId = touch.fingerId; } } ActiveTouches.Add(touch.fingerId, controller); } Service?.RaiseSourceDetected(controller.InputSource, controller); controller.TouchData = touch; controller.StartTouch(); } } private static readonly ProfilerMarker UpdateTouchDataPerfMarker = new ProfilerMarker("[MRTK] UnityTouchDeviceManager.UpdateTouchData"); private void UpdateTouchData(Touch touch, Ray ray) { using (UpdateTouchDataPerfMarker.Auto()) { UnityTouchController controller; if (!ActiveTouches.TryGetValue(touch.fingerId, out controller)) { return; } controller.TouchData = touch; var pointer = (IMixedRealityTouchPointer)controller.InputSource.Pointers[0]; controller.ScreenPointRay = pointer.TouchRay = ray; controller.Update(); } } private static readonly ProfilerMarker RemoveTouchControllerPerfMarker = new ProfilerMarker("[MRTK] UnityTouchDeviceManager.RemoveTouchController"); private void RemoveTouchController(Touch touch) { using (RemoveTouchControllerPerfMarker.Auto()) { UnityTouchController controller; if (!ActiveTouches.TryGetValue(touch.fingerId, out controller)) { return; } RecyclePointers(controller.InputSource); controller.TouchData = touch; controller.EndTouch(); // Schedule the source lost event. touchesToRemove.Add(controller); // Remove from the active collection ActiveTouches.Remove(touch.fingerId); } } } }