294 lines
13 KiB
C#
294 lines
13 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using System;
|
|
using Unity.Profiling;
|
|
using Microsoft.MixedReality.Toolkit.Utilities;
|
|
using Microsoft.MixedReality.Toolkit.Input;
|
|
|
|
#if HP_CONTROLLER_ENABLED
|
|
using Microsoft.MixedReality.Input;
|
|
using MotionControllerHandedness = Microsoft.MixedReality.Input.Handedness;
|
|
using Handedness = Microsoft.MixedReality.Toolkit.Utilities.Handedness;
|
|
#endif
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.WindowsMixedReality
|
|
{
|
|
#if HP_CONTROLLER_ENABLED
|
|
/// <summary>
|
|
/// Class for accessing the data provided by the HP Motion Controller's API
|
|
/// </summary>
|
|
public class MotionControllerState
|
|
{
|
|
public MotionControllerState(MotionController mc)
|
|
{
|
|
this.MotionController = mc;
|
|
}
|
|
public void Update(DateTime currentTime)
|
|
{
|
|
this.CurrentReading = MotionController.TryGetReadingAtTime(currentTime);
|
|
}
|
|
public MotionController MotionController { get; private set; }
|
|
public MotionControllerReading CurrentReading { get; private set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Class for handling updating a controller via data provided by the HP Motion Controller's API
|
|
/// </summary>
|
|
public class HPMotionControllerInputHandler
|
|
{
|
|
private IMixedRealityInputSource InputSource;
|
|
private Handedness ControllerHandedness;
|
|
private MixedRealityInteractionMapping[] Interactions;
|
|
|
|
public HPMotionControllerInputHandler(Handedness controllerHandedness, IMixedRealityInputSource inputSource = null, MixedRealityInteractionMapping[] interactions = null)
|
|
{
|
|
ControllerHandedness = controllerHandedness;
|
|
InputSource = inputSource;
|
|
Interactions = interactions;
|
|
}
|
|
|
|
private static readonly ProfilerMarker UpdateControllerPerfMarker = new ProfilerMarker("[MRTK] HPMotionControllerInputHandler.UpdateController");
|
|
|
|
/// <summary>
|
|
/// Update the controller data from .
|
|
/// </summary>
|
|
public virtual void UpdateController(MotionControllerState controllerState)
|
|
{
|
|
controllerState.Update(DateTime.Now);
|
|
|
|
using (UpdateControllerPerfMarker.Auto())
|
|
{
|
|
for (int i = 0; i < Interactions?.Length; i++)
|
|
{
|
|
switch (Interactions[i].AxisType)
|
|
{
|
|
case AxisType.None:
|
|
break;
|
|
case AxisType.Digital:
|
|
UpdateButtonData(Interactions[i], controllerState);
|
|
break;
|
|
case AxisType.SingleAxis:
|
|
UpdateSingleAxisData(Interactions[i], controllerState);
|
|
break;
|
|
case AxisType.DualAxis:
|
|
UpdateDualAxisData(Interactions[i], controllerState);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static readonly ProfilerMarker UpdateButtonDataPerfMarker = new ProfilerMarker("[MRTK] HPMotionControllerInputHandler.UpdateButtonData");
|
|
|
|
/// <summary>
|
|
/// Update an interaction bool data type from a bool input
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Raises an Input System "Input Down" event when the key is down, and raises an "Input Up" when it is released (e.g. a Button)
|
|
/// </remarks>
|
|
internal virtual void UpdateButtonData(MixedRealityInteractionMapping interactionMapping, MotionControllerState controllerState)
|
|
{
|
|
using (UpdateButtonDataPerfMarker.Auto())
|
|
{
|
|
// Handedness must be left or right in order to differentiate between buttons for the left and right hand.
|
|
MotionControllerHandedness controllerHandedness = controllerState.MotionController.Handedness;
|
|
|
|
Debug.Assert(controllerHandedness != MotionControllerHandedness.Unknown);
|
|
Debug.Assert(interactionMapping.AxisType == AxisType.Digital);
|
|
|
|
if (interactionMapping.InputType == DeviceInputType.TriggerTouch)
|
|
{
|
|
var triggerData = controllerState.CurrentReading.GetPressedValue(ControllerInput.Trigger);
|
|
interactionMapping.BoolData = !Mathf.Approximately(triggerData, 0.0f);
|
|
}
|
|
else if (interactionMapping.InputType == DeviceInputType.GripTouch)
|
|
{
|
|
var gripData = controllerState.CurrentReading.GetPressedValue(ControllerInput.Grasp);
|
|
interactionMapping.BoolData = !Mathf.Approximately(gripData, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
ControllerInput button;
|
|
|
|
// Update the interaction data source
|
|
// Interactions handled mirror the GenericXRSDKController to maintain parity. ThumbstickTouch and Touchpad are left out
|
|
// due to having no ControllerInput equivalents
|
|
switch (interactionMapping.InputType)
|
|
{
|
|
case DeviceInputType.Select:
|
|
case DeviceInputType.TriggerNearTouch:
|
|
case DeviceInputType.TriggerPress:
|
|
button = ControllerInput.Trigger;
|
|
break;
|
|
case DeviceInputType.GripNearTouch:
|
|
case DeviceInputType.GripPress:
|
|
button = ControllerInput.Grasp;
|
|
break;
|
|
case DeviceInputType.ButtonPress:
|
|
case DeviceInputType.PrimaryButtonPress:
|
|
button = controllerHandedness == MotionControllerHandedness.Left ? ControllerInput.X_Button : ControllerInput.A_Button;
|
|
break;
|
|
case DeviceInputType.SecondaryButtonPress:
|
|
button = controllerHandedness == MotionControllerHandedness.Left ? ControllerInput.Y_Button : ControllerInput.B_Button;
|
|
break;
|
|
case DeviceInputType.Menu:
|
|
button = ControllerInput.Menu;
|
|
break;
|
|
case DeviceInputType.ThumbStickPress:
|
|
button = ControllerInput.Thumbstick;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
|
|
var buttonData = controllerState.CurrentReading.GetPressedValue(button);
|
|
interactionMapping.BoolData = Mathf.Approximately(buttonData, 1.0f);
|
|
}
|
|
|
|
// If our value changed raise it.
|
|
if (interactionMapping.Changed)
|
|
{
|
|
// Raise input system event if it's enabled
|
|
if (interactionMapping.BoolData)
|
|
{
|
|
CoreServices.InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
|
|
}
|
|
else
|
|
{
|
|
CoreServices.InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static readonly ProfilerMarker UpdateSingleAxisDataPerfMarker = new ProfilerMarker("[MRTK] HPMotionControllerInputHandler.UpdateSingleAxisData");
|
|
|
|
/// <summary>
|
|
/// Update an interaction float data type from a SingleAxis (float) input
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Raises a FloatInputChanged event when the float data changes
|
|
/// </remarks>
|
|
internal virtual void UpdateSingleAxisData(MixedRealityInteractionMapping interactionMapping, MotionControllerState controllerState)
|
|
{
|
|
using (UpdateSingleAxisDataPerfMarker.Auto())
|
|
{
|
|
Debug.Assert(interactionMapping.AxisType == AxisType.SingleAxis);
|
|
// First handle updating the bool values, since those events are only raised once the trigger/gripped is pressed
|
|
switch (interactionMapping.InputType)
|
|
{
|
|
case DeviceInputType.TriggerPress:
|
|
var triggerData = controllerState.CurrentReading.GetPressedValue(ControllerInput.Trigger);
|
|
interactionMapping.BoolData = triggerData.Equals(1);
|
|
break;
|
|
case DeviceInputType.GripPress:
|
|
var gripData = controllerState.CurrentReading.GetPressedValue(ControllerInput.Grasp);
|
|
interactionMapping.BoolData = gripData.Equals(1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// If our value changed raise it.
|
|
if (interactionMapping.Changed)
|
|
{
|
|
// Raise bool input system event if it's available
|
|
if (interactionMapping.BoolData)
|
|
{
|
|
CoreServices.InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
|
|
}
|
|
else
|
|
{
|
|
CoreServices.InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
|
|
}
|
|
}
|
|
|
|
// Next handle updating the float values
|
|
switch (interactionMapping.InputType)
|
|
{
|
|
case DeviceInputType.Trigger:
|
|
var triggerData = controllerState.CurrentReading.GetPressedValue(ControllerInput.Trigger);
|
|
interactionMapping.FloatData = triggerData;
|
|
break;
|
|
case DeviceInputType.Grip:
|
|
var gripData = controllerState.CurrentReading.GetPressedValue(ControllerInput.Grasp);
|
|
interactionMapping.FloatData = gripData;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// If our value changed raise it.
|
|
if (interactionMapping.Changed)
|
|
{
|
|
// Raise float input system event if it's enabled
|
|
CoreServices.InputSystem?.RaiseFloatInputChanged(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction, interactionMapping.FloatData);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static readonly ProfilerMarker UpdateDualAxisDataPerfMarker = new ProfilerMarker("[MRTK] HPMotionControllerInputHandler.UpdateDualAxisData");
|
|
|
|
/// <summary>
|
|
/// Update the touchpad / thumbstick input from the device
|
|
/// </summary>
|
|
internal virtual void UpdateDualAxisData(MixedRealityInteractionMapping interactionMapping, MotionControllerState controllerState)
|
|
{
|
|
using (UpdateDualAxisDataPerfMarker.Auto())
|
|
{
|
|
Debug.Assert(interactionMapping.AxisType == AxisType.DualAxis);
|
|
|
|
// Only process the reading if the input mapping is for the thumbstick
|
|
if (interactionMapping.InputType != DeviceInputType.ThumbStick)
|
|
return;
|
|
|
|
System.Numerics.Vector2 controllerAxisData = controllerState.CurrentReading.GetXYValue(ControllerInput.Thumbstick);
|
|
float xAxisData = AdjustForDeadzone(2.0f * (controllerAxisData.X - 0.5f));
|
|
float yAxisData = AdjustForDeadzone(2.0f * (controllerAxisData.Y - 0.5f));
|
|
Vector2 axisData = new Vector2(xAxisData, yAxisData);
|
|
|
|
// Update the interaction data source
|
|
interactionMapping.Vector2Data = axisData;
|
|
|
|
// If our value changed raise it.
|
|
if (interactionMapping.Changed)
|
|
{
|
|
// Raise input system event if it's enabled
|
|
CoreServices.InputSystem?.RaisePositionInputChanged(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction, interactionMapping.Vector2Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
private const float INNER_DEADZONE = 0.25f;
|
|
private const float OUTER_DEADZONE = 0.1f;
|
|
/// <summary>
|
|
/// Returns a float which snaps to 0, 1.0f, or -1.0f if the input parameter falls within certain deadzones
|
|
/// </summary>
|
|
/// <param name="f">float between -1.0f and 1.0f</param>
|
|
/// <returns>A float adjusted to snap to certain values if the initial value fell within certain deadzones</returns>
|
|
private float AdjustForDeadzone(float f)
|
|
{
|
|
if (Mathf.Abs(f) < INNER_DEADZONE)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
else if (Mathf.Abs(f) > 1.0f-OUTER_DEADZONE)
|
|
{
|
|
return 1.0f * Mathf.Sign(f);
|
|
}
|
|
else
|
|
{
|
|
return f;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|