// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using System; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Input { /// /// Base Controller class to inherit from for all controllers. /// public abstract class BaseController : IMixedRealityController { /// /// Constructor. /// protected BaseController( TrackingState trackingState, Handedness controllerHandedness, IMixedRealityInputSource inputSource = null, MixedRealityInteractionMapping[] interactions = null, IMixedRealityInputSourceDefinition definition = null) { TrackingState = trackingState; ControllerHandedness = controllerHandedness; InputSource = inputSource; Interactions = interactions; Definition = definition; IsPositionAvailable = false; IsPositionApproximate = false; IsRotationAvailable = false; Type controllerType = GetType(); if (IsControllerMappingEnabled() && Interactions == null) { // We can only enable controller profiles if mappings exist. MixedRealityControllerMapping[] controllerMappings = GetControllerMappings(); // Have to test that a controller type has been registered in the profiles, // else its Unity input manager mappings will not have been set up by the inspector. bool profileFound = false; if (controllerMappings != null) { for (int i = 0; i < controllerMappings.Length; i++) { if (controllerMappings[i].ControllerType.Type == controllerType) { profileFound = true; // If it is an exact match, assign interaction mappings. if (controllerMappings[i].Handedness == ControllerHandedness && controllerMappings[i].Interactions.Length > 0) { MixedRealityInteractionMapping[] profileInteractions = controllerMappings[i].Interactions; MixedRealityInteractionMapping[] newInteractions = new MixedRealityInteractionMapping[profileInteractions.Length]; for (int j = 0; j < profileInteractions.Length; j++) { newInteractions[j] = new MixedRealityInteractionMapping(profileInteractions[j]); } AssignControllerMappings(newInteractions); break; } } } } // If no controller mappings found, try to use default interactions. if (Interactions == null || Interactions.Length < 1) { SetupDefaultInteractions(); // We still don't have controller mappings, so this may be a custom controller. if (Interactions == null || Interactions.Length < 1) { Debug.LogWarning($"No controller interaction mappings found for {controllerType}."); return; } } // If no profile was found, warn the user. Does not stop the project from running. if (!profileFound) { Debug.LogWarning($"No controller profile found for type {controllerType}; please ensure all controllers are defined in the configured MixedRealityControllerConfigurationProfile."); } } if (GetControllerVisualizationProfile() != null && GetControllerVisualizationProfile().RenderMotionControllers && InputSource != null) { TryRenderControllerModel(controllerType, InputSource.SourceType); } Enabled = true; } /// /// The default interactions for this controller. /// public virtual MixedRealityInteractionMapping[] DefaultInteractions => BuildInteractions(Definition?.GetDefaultMappings(ControllerHandedness)); /// /// The default left-handed interactions for this controller. /// public virtual MixedRealityInteractionMapping[] DefaultLeftHandedInteractions => BuildInteractions(Definition?.GetDefaultMappings(Handedness.Left)); /// /// The default right-handed interactions for this controller. /// public virtual MixedRealityInteractionMapping[] DefaultRightHandedInteractions => BuildInteractions(Definition?.GetDefaultMappings(Handedness.Right)); private MixedRealityInteractionMapping[] BuildInteractions(System.Collections.Generic.IReadOnlyList definitionInteractions) { if (definitionInteractions == null) { return null; } MixedRealityInteractionMapping[] defaultInteractions = new MixedRealityInteractionMapping[definitionInteractions.Count]; for (int i = 0; i < definitionInteractions.Count; i++) { defaultInteractions[i] = new MixedRealityInteractionMapping((uint)i, definitionInteractions[i]); } return defaultInteractions; } /// /// Represents the archetypal definition of what this controller supports and can perform. /// protected virtual IMixedRealityInputSourceDefinition Definition { get; } = null; #region IMixedRealityController Implementation /// public bool Enabled { get; set; } /// public TrackingState TrackingState { get; protected set; } /// public Handedness ControllerHandedness { get; } /// public IMixedRealityInputSource InputSource { get; } public IMixedRealityControllerVisualizer Visualizer { get; protected set; } /// public bool IsPositionAvailable { get; protected set; } /// public bool IsPositionApproximate { get; protected set; } /// public bool IsRotationAvailable { get; protected set; } /// public MixedRealityInteractionMapping[] Interactions { get; private set; } = null; public Vector3 AngularVelocity { get; protected set; } public Vector3 Velocity { get; protected set; } /// public virtual bool IsInPointingPose => true; #endregion IMixedRealityController Implementation /// /// Sets up the configuration based on the Mixed Reality Controller Mapping Profile. /// [Obsolete("This method is no longer used. Configuration now happens in the constructor. You can check this controller's Enabled property for configuration state.")] public bool SetupConfiguration(Type controllerType, InputSourceType inputSourceType = InputSourceType.Controller) { // If the constructor succeeded in finding interactions, Enabled will be true. return Enabled; } /// /// Sets up the configuration based on the Mixed Reality Controller Mapping Profile. /// /// The type this controller represents. [Obsolete("This method is no longer used. Configuration now happens in the constructor. You can check this controller's Enabled property for configuration state.")] public bool SetupConfiguration(Type controllerType) { // If the constructor succeeded in finding interactions, Enabled will be true. return Enabled; } /// /// Assign the default interactions based on controller handedness, if necessary. /// [Obsolete("The handedness parameter is no longer used. This method now reads from the controller's handedness.")] public virtual void SetupDefaultInteractions(Handedness controllerHandedness) => SetupDefaultInteractions(); /// /// Assign the default interactions based on this controller's handedness, if necessary. /// public virtual void SetupDefaultInteractions() { switch (ControllerHandedness) { case Handedness.Left: AssignControllerMappings(DefaultLeftHandedInteractions ?? DefaultInteractions); break; case Handedness.Right: AssignControllerMappings(DefaultRightHandedInteractions ?? DefaultInteractions); break; default: AssignControllerMappings(DefaultInteractions); break; } } /// /// Load the Interaction mappings for this controller from the configured Controller Mapping profile /// /// Configured mappings from a controller mapping profile public void AssignControllerMappings(MixedRealityInteractionMapping[] mappings) { Interactions = mappings; } /// /// Try to render a controller model for this controller from the visualization profile. /// /// The type of controller to load the model for. /// Whether the model represents a hand or a controller. /// True if a model was successfully loaded or model rendering is disabled. False if a model tried to load but failed. protected virtual bool TryRenderControllerModel(Type controllerType, InputSourceType inputSourceType) { MixedRealityControllerVisualizationProfile controllerVisualizationProfile = GetControllerVisualizationProfile(); bool controllerVisualizationProfilePresent = controllerVisualizationProfile != null; if (!controllerVisualizationProfilePresent || !controllerVisualizationProfile.RenderMotionControllers) { return true; } // Try to use the profile's override model first GameObject controllerModel = controllerVisualizationProfile.GetControllerModelOverride(controllerType, ControllerHandedness); // If the Controller model is still null in the end, use the global defaults. if (controllerModel == null) { if (inputSourceType == InputSourceType.Controller) { if (ControllerHandedness == Handedness.Left && controllerVisualizationProfile.GlobalLeftHandModel != null) { controllerModel = controllerVisualizationProfile.GlobalLeftHandModel; } else if (ControllerHandedness == Handedness.Right && controllerVisualizationProfile.GlobalRightHandModel != null) { controllerModel = controllerVisualizationProfile.GlobalRightHandModel; } } else if (inputSourceType == InputSourceType.Hand) { if (ControllerHandedness == Handedness.Left && controllerVisualizationProfile.GlobalLeftHandVisualizer != null) { controllerModel = controllerVisualizationProfile.GlobalLeftHandVisualizer; } else if (ControllerHandedness == Handedness.Right && controllerVisualizationProfile.GlobalRightHandVisualizer != null) { controllerModel = controllerVisualizationProfile.GlobalRightHandVisualizer; } } } if (controllerModel == null) { // no controller model available return false; } // If we've got a controller model prefab, then create it and place it in the scene. GameObject controllerObject = UnityEngine.Object.Instantiate(controllerModel); return TryAddControllerModelToSceneHierarchy(controllerObject); } protected bool TryAddControllerModelToSceneHierarchy(GameObject controllerObject) { if (controllerObject != null) { controllerObject.name = $"{ControllerHandedness}_{controllerObject.name}"; MixedRealityPlayspace.AddChild(controllerObject.transform); Visualizer = controllerObject.GetComponent(); if (Visualizer != null) { Visualizer.Controller = this; return true; } else { Debug.LogError($"{controllerObject.name} is missing a IMixedRealityControllerVisualizer component!"); return false; } } return false; } #region MRTK instance helpers protected static MixedRealityControllerVisualizationProfile GetControllerVisualizationProfile() { if (CoreServices.InputSystem?.InputSystemProfile != null) { return CoreServices.InputSystem.InputSystemProfile.ControllerVisualizationProfile; } return null; } protected static bool IsControllerMappingEnabled() { if (CoreServices.InputSystem?.InputSystemProfile != null) { return CoreServices.InputSystem.InputSystemProfile.IsControllerMappingEnabled; } return false; } protected static MixedRealityControllerMapping[] GetControllerMappings() { if (CoreServices.InputSystem?.InputSystemProfile != null && CoreServices.InputSystem.InputSystemProfile.ControllerMappingProfile != null) { // We can only enable controller profiles if mappings exist. return CoreServices.InputSystem.InputSystemProfile.ControllerMappingProfile.MixedRealityControllerMappings; } return null; } #endregion MRTK instance helpers } }