// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Input { /// /// Base class for services that create simulated input devices. /// public abstract class BaseInputSimulationService : BaseInputDeviceManager { /// /// Dictionary to capture all active controllers detected /// private readonly Dictionary trackedControllers = new Dictionary(); /// /// Active controllers /// private IMixedRealityController[] activeControllers = Array.Empty(); /// public override IMixedRealityController[] GetActiveControllers() => activeControllers; #region BaseInputDeviceManager Implementation /// /// 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.")] protected BaseInputSimulationService( IMixedRealityServiceRegistrar registrar, IMixedRealityInputSystem inputSystem, string name, uint priority, BaseMixedRealityProfile profile) : 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. protected BaseInputSimulationService( IMixedRealityInputSystem inputSystem, string name, uint priority, BaseMixedRealityProfile profile) : base(inputSystem, name, priority, profile) { } #endregion BaseInputDeviceManager Implementation // Register input sources for controllers based on controller data protected void UpdateControllerDevice(ControllerSimulationMode simulationMode, Handedness handedness, object controllerData) { if (controllerData != null) { if (controllerData is SimulatedHandData handData && handData.IsTracked) { SimulatedHand hand = GetOrAddControllerDevice(handedness, simulationMode) as SimulatedHand; hand.UpdateState(handData); return; } else if (controllerData is SimulatedMotionControllerData motionControllerData && motionControllerData.IsTracked) { SimulatedMotionController motionController = GetOrAddControllerDevice(handedness, simulationMode) as SimulatedMotionController; motionController.UpdateState(motionControllerData); return; } } RemoveControllerDevice(handedness); } public BaseController GetControllerDevice(Handedness handedness) { if (trackedControllers.TryGetValue(handedness, out BaseController controller)) { return controller; } return null; } protected BaseController GetOrAddControllerDevice(Handedness handedness, ControllerSimulationMode simulationMode) { var controller = GetControllerDevice(handedness); if (controller != null) { if (controller is SimulatedHand hand && hand.SimulationMode == simulationMode) { return controller; } else if (controller is SimulatedMotionController && simulationMode == ControllerSimulationMode.MotionController) { return controller; } else { // Remove and recreate controller device if simulation mode doesn't match RemoveControllerDevice(handedness); } } DebugUtilities.LogVerboseFormat("GetOrAddControllerDevice: Adding a new simulated controller of handedness {0} and simulation mode {1}", handedness, simulationMode); System.Type controllerType = null; SupportedControllerType st; IMixedRealityInputSource inputSource; switch (simulationMode) { case ControllerSimulationMode.HandGestures: st = SupportedControllerType.GGVHand; inputSource = Service?.RequestNewGenericInputSource($"Simulated GGV {handedness} Hand", RequestPointers(st, handedness), InputSourceType.Hand); controller = new SimulatedGestureHand(TrackingState.Tracked, handedness, inputSource); controllerType = typeof(SimulatedGestureHand); break; case ControllerSimulationMode.ArticulatedHand: st = SupportedControllerType.ArticulatedHand; inputSource = Service?.RequestNewGenericInputSource($"Simulated Articulated {handedness} Hand", RequestPointers(st, handedness), InputSourceType.Hand); controller = new SimulatedArticulatedHand(TrackingState.Tracked, handedness, inputSource); controllerType = typeof(SimulatedArticulatedHand); break; case ControllerSimulationMode.MotionController: st = SupportedControllerType.WindowsMixedReality; inputSource = Service?.RequestNewGenericInputSource($"Simulated {handedness} MotionController", RequestPointers(st, handedness), InputSourceType.Controller); controller = new SimulatedMotionController(TrackingState.Tracked, handedness, inputSource); controllerType = typeof(SimulatedMotionController); break; default: controller = null; break; } if (controller == null || !controller.Enabled) { // Controller failed to be setup correctly. Debug.LogError($"Failed to create {controllerType} controller"); // Return null so we don't raise the source detected. return null; } for (int i = 0; i < controller.InputSource?.Pointers?.Length; i++) { controller.InputSource.Pointers[i].Controller = controller; } Service?.RaiseSourceDetected(controller.InputSource, controller); trackedControllers.Add(handedness, controller); UpdateActiveControllers(); return controller; } protected void RemoveControllerDevice(Handedness handedness) { var controller = GetControllerDevice(handedness); if (controller != null) { DebugUtilities.LogVerboseFormat("RemoveHandDevice: Removing simulated controller of handedness", handedness); Service?.RaiseSourceLost(controller.InputSource, controller); RecyclePointers(controller.InputSource); trackedControllers.Remove(handedness); UpdateActiveControllers(); } } protected void RemoveAllControllerDevices() { foreach (var controller in trackedControllers.Values) { Service?.RaiseSourceLost(controller.InputSource, controller); RecyclePointers(controller.InputSource); } trackedControllers.Clear(); UpdateActiveControllers(); } private void UpdateActiveControllers() { activeControllers = trackedControllers.Values.ToArray(); } #region Obsolete Methods // Register input sources for hands based on hand data [Obsolete("Use UpdateControllerDevice instead.")] protected void UpdateHandDevice(ControllerSimulationMode simulationMode, Handedness handedness, SimulatedHandData handData) { UpdateControllerDevice(simulationMode, handedness, handData); } [Obsolete("Use GetControllerDevice instead.")] public SimulatedHand GetHandDevice(Handedness handedness) { return GetControllerDevice(handedness) as SimulatedHand; } [Obsolete("Use GetOrAddControllerDevice instead.")] protected SimulatedHand GetOrAddHandDevice(Handedness handedness, ControllerSimulationMode simulationMode) { return GetOrAddControllerDevice(handedness, simulationMode) as SimulatedHand; } [Obsolete("Use RemoveControllerDevice instead.")] protected void RemoveHandDevice(Handedness handedness) { RemoveControllerDevice(handedness); } [Obsolete("Use RemoveAllControllerDevices instead.")] protected void RemoveAllHandDevices() { RemoveAllControllerDevices(); } #endregion } }