// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;
using Microsoft.MixedReality.Toolkit.Windows.Input;
#if UNITY_WSA
using Unity.Profiling;
using UnityEngine.XR.WSA.Input;
#endif
namespace Microsoft.MixedReality.Toolkit.WindowsMixedReality.Input
{
///
/// A Windows Mixed Reality Controller Instance.
///
[MixedRealityController(
SupportedControllerType.WindowsMixedReality,
new[] { Handedness.Left, Handedness.Right },
"Textures/MotionController",
supportedUnityXRPipelines: SupportedUnityXRPipelines.LegacyXR)]
public class WindowsMixedRealityController : BaseWindowsMixedRealitySource, IMixedRealityHapticFeedback
{
///
/// Constructor.
///
public WindowsMixedRealityController(
TrackingState trackingState,
Handedness controllerHandedness,
IMixedRealityInputSource inputSource = null,
MixedRealityInteractionMapping[] interactions = null)
: this(trackingState, controllerHandedness, new WindowsMixedRealityControllerDefinition(controllerHandedness), inputSource, interactions)
{ }
public WindowsMixedRealityController(
TrackingState trackingState,
Handedness controllerHandedness,
IMixedRealityInputSourceDefinition definition,
IMixedRealityInputSource inputSource = null,
MixedRealityInteractionMapping[] interactions = null)
: base(trackingState, controllerHandedness, inputSource, interactions, definition)
{ }
#if UNITY_WSA
#region Update data functions
private static readonly ProfilerMarker UpdateControllerPerfMarker = new ProfilerMarker("[MRTK] WindowsMixedRealityController.UpdateController");
///
/// Update the controller data from the provided platform state.
///
/// The InteractionSourceState retrieved from the platform
public override void UpdateController(InteractionSourceState interactionSourceState)
{
if (!Enabled) { return; }
using (UpdateControllerPerfMarker.Auto())
{
base.UpdateController(interactionSourceState);
for (int i = 0; i < Interactions?.Length; i++)
{
switch (Interactions[i].InputType)
{
case DeviceInputType.None:
break;
case DeviceInputType.ThumbStick:
case DeviceInputType.ThumbStickPress:
UpdateThumbstickData(interactionSourceState, Interactions[i]);
break;
case DeviceInputType.Touchpad:
case DeviceInputType.TouchpadTouch:
case DeviceInputType.TouchpadPress:
UpdateTouchpadData(interactionSourceState, Interactions[i]);
break;
case DeviceInputType.Menu:
UpdateMenuData(interactionSourceState, Interactions[i]);
break;
}
}
}
}
private static readonly ProfilerMarker UpdateTouchpadDataPerfMarker = new ProfilerMarker("[MRTK] WindowsMixedRealityController.UpdateTouchpadData");
///
/// Update the touchpad input from the device.
///
/// The InteractionSourceState retrieved from the platform.
private void UpdateTouchpadData(InteractionSourceState interactionSourceState, MixedRealityInteractionMapping interactionMapping)
{
using (UpdateTouchpadDataPerfMarker.Auto())
{
switch (interactionMapping.InputType)
{
case DeviceInputType.TouchpadTouch:
{
// Update the interaction data source
interactionMapping.BoolData = interactionSourceState.touchpadTouched;
// If our value changed raise it.
if (interactionMapping.Changed)
{
// Raise input system event if it's enabled
if (interactionSourceState.touchpadTouched)
{
CoreServices.InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
}
else
{
CoreServices.InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
}
}
break;
}
case DeviceInputType.TouchpadPress:
{
// Update the interaction data source
interactionMapping.BoolData = interactionSourceState.touchpadPressed;
// If our value changed raise it.
if (interactionMapping.Changed)
{
// Raise input system event if it's enabled
if (interactionSourceState.touchpadPressed)
{
CoreServices.InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
}
else
{
CoreServices.InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
}
}
break;
}
case DeviceInputType.Touchpad:
{
// Update the interaction data source
interactionMapping.Vector2Data = interactionSourceState.touchpadPosition;
// If our value changed raise it.
if (interactionMapping.Changed)
{
// Raise input system event if it's enabled
CoreServices.InputSystem?.RaisePositionInputChanged(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction, interactionSourceState.touchpadPosition);
}
break;
}
}
}
}
private static readonly ProfilerMarker UpdateThumbstickDataPerfMarker = new ProfilerMarker("[MRTK] WindowsMixedRealityController.UpdateThumbstickData");
///
/// Update the thumbstick input from the device.
///
/// The InteractionSourceState retrieved from the platform.
private void UpdateThumbstickData(InteractionSourceState interactionSourceState, MixedRealityInteractionMapping interactionMapping)
{
using (UpdateThumbstickDataPerfMarker.Auto())
{
switch (interactionMapping.InputType)
{
case DeviceInputType.ThumbStickPress:
{
// Update the interaction data source
interactionMapping.BoolData = interactionSourceState.thumbstickPressed;
// If our value changed raise it.
if (interactionMapping.Changed)
{
// Raise input system event if it's enabled
if (interactionSourceState.thumbstickPressed)
{
CoreServices.InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
}
else
{
CoreServices.InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
}
}
break;
}
case DeviceInputType.ThumbStick:
{
// Update the interaction data source
interactionMapping.Vector2Data = interactionSourceState.thumbstickPosition;
// If our value changed raise it.
if (interactionMapping.Changed)
{
// Raise input system event if it's enabled
CoreServices.InputSystem?.RaisePositionInputChanged(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction, interactionSourceState.thumbstickPosition);
}
break;
}
}
}
}
private static readonly ProfilerMarker UpdateMenuDataPerfMarker = new ProfilerMarker("[MRTK] WindowsMixedRealityController.UpdateMenuData");
///
/// Update the menu button state.
///
/// The InteractionSourceState retrieved from the platform.
private void UpdateMenuData(InteractionSourceState interactionSourceState, MixedRealityInteractionMapping interactionMapping)
{
using (UpdateMenuDataPerfMarker.Auto())
{
// Update the interaction data source
interactionMapping.BoolData = interactionSourceState.menuPressed;
// If our value changed raise it.
if (interactionMapping.Changed)
{
// Raise input system event if it's enabled
if (interactionSourceState.menuPressed)
{
CoreServices.InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
}
else
{
CoreServices.InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
}
}
}
}
#endregion Update data functions
#region Controller model functions
#if WINDOWS_UWP
private WindowsMixedRealityControllerModelProvider controllerModelProvider;
///
protected override bool TryRenderControllerModel(System.Type controllerType, InputSourceType inputSourceType)
{
if (GetControllerVisualizationProfile() == null ||
!GetControllerVisualizationProfile().GetUsePlatformModelsOverride(GetType(), ControllerHandedness))
{
return base.TryRenderControllerModel(controllerType, inputSourceType);
}
else
{
TryRenderControllerModelWithModelProvider();
return true;
}
}
private async void TryRenderControllerModelWithModelProvider()
{
if (controllerModelProvider == null)
{
controllerModelProvider = new WindowsMixedRealityControllerModelProvider(ControllerHandedness);
}
UnityEngine.GameObject controllerModel = await controllerModelProvider.TryGenerateControllerModelFromPlatformSDK();
if (this != null)
{
if (controllerModel != null
&& MixedRealityControllerModelHelpers.TryAddVisualizationScript(controllerModel, GetType(), ControllerHandedness)
&& TryAddControllerModelToSceneHierarchy(controllerModel))
{
controllerModel.SetActive(true);
return;
}
UnityEngine.Debug.LogWarning("Failed to create controller model from driver; defaulting to BaseController behavior.");
base.TryRenderControllerModel(GetType(), InputSource.SourceType);
}
if (controllerModel != null)
{
// If we didn't successfully set up the model and add it to the hierarchy (which returns early), set it inactive.
controllerModel.SetActive(false);
}
}
#endif
#endregion Controller model functions
#endif // UNITY_WSA
#region Haptic feedback functions
public bool StartHapticImpulse(float intensity, float durationInSeconds = float.MaxValue) =>
#if UNITY_WSA
LastSourceStateReading.source.StartHaptics(intensity, durationInSeconds);
#else
false;
#endif // UNITY_WSA
public void StopHapticFeedback()
{
#if UNITY_WSA
LastSourceStateReading.source.StopHaptics();
#endif // UNITY_WSA
}
#endregion Haptic feedback functions
}
}