mixedreality/com.microsoft.mixedreality..../Core/Providers/InputSimulation/SimulatedControllerDataProv...

235 lines
8.9 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEngine;
/// <summary>
/// Provides per-frame data access to simulated controller data
/// </summary>
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Internal class to define current state of a controller.
/// </summary>
internal abstract class SimulatedControllerState
{
protected Handedness handedness = Handedness.None;
public Handedness Handedness => handedness;
// Show a tracked controller
public bool IsTracked = false;
// Position of the controller in viewport space
public Vector3 ViewportPosition = Vector3.zero;
// Rotation of the controller relative to the camera
public Vector3 ViewportRotation = Vector3.zero;
// Random offset to simulate tracking inaccuracy
public Vector3 JitterOffset = Vector3.zero;
protected float viewportPositionZTarget;
protected readonly float smoothScrollSpeed = 5f;
public SimulatedControllerState(Handedness _handedness)
{
handedness = _handedness;
}
public void SimulateInput(MouseDelta mouseDelta, bool useMouseRotation, float rotationSensitivity, float rotationScale, float noiseAmount)
{
if (useMouseRotation)
{
Vector3 rotationDeltaEulerAngles = Vector3.zero;
rotationDeltaEulerAngles.x += -mouseDelta.screenDelta.y * rotationSensitivity;
rotationDeltaEulerAngles.y += mouseDelta.screenDelta.x * rotationSensitivity;
rotationDeltaEulerAngles.z += mouseDelta.screenDelta.z * rotationSensitivity;
rotationDeltaEulerAngles *= rotationScale;
ViewportRotation += rotationDeltaEulerAngles;
}
else
{
ViewportPosition.x += mouseDelta.viewportDelta.x;
ViewportPosition.y += mouseDelta.viewportDelta.y;
viewportPositionZTarget += mouseDelta.viewportDelta.z;
}
JitterOffset = Random.insideUnitSphere * noiseAmount;
}
/// <summary>
/// Resets simulated controller position.
/// </summary>
/// <param name="resetTo">The position to reset controller to.</param>
public void ResetPosition(Vector3 resetTo)
{
ViewportPosition = resetTo;
viewportPositionZTarget = ViewportPosition.z;
}
/// <summary>
/// Resets simulated controller rotation.
/// </summary>
public abstract void ResetRotation();
/// <summary>
/// Update information about the controller state or position.
/// </summary>
internal void Update()
{
ViewportPosition.z = Mathf.Lerp(ViewportPosition.z, viewportPositionZTarget, smoothScrollSpeed * Time.deltaTime);
}
}
/// <summary>
/// Produces simulated data every frame that defines the position and rotation of the simulated controller.
/// </summary>
public abstract class SimulatedControllerDataProvider
{
protected MixedRealityInputSimulationProfile profile;
/// <summary>
/// If true then the left controller is always visible, regardless of simulating.
/// </summary>
public bool IsAlwaysVisibleLeft = false;
/// <summary>
/// If true then the right controller is always visible, regardless of simulating.
/// </summary>
public bool IsAlwaysVisibleRight = false;
internal SimulatedControllerState InputStateLeft;
internal SimulatedControllerState InputStateRight;
internal SimulatedControllerState InputStateGaze;
private bool isSimulatingGaze => !IsSimulatingLeft && !IsSimulatingRight && !IsAlwaysVisibleLeft && !IsAlwaysVisibleRight && !DeviceUtility.IsPresent;
/// <summary>
/// Left controller is controlled by user input.
/// </summary>
public bool IsSimulatingLeft { get; private set; } = false;
/// <summary>
/// Right controller is controlled by user input.
/// </summary>
public bool IsSimulatingRight { get; private set; } = false;
// Most recent time controller control was enabled,
protected float lastSimulationLeft = -1.0e6f;
protected float lastSimulationRight = -1.0e6f;
protected float lastSimulationGaze = -1.0e6f;
// Last timestamp when controllers were tracked
protected long lastInputTrackedTimestampLeft = 0;
protected long lastInputTrackedTimestampRight = 0;
protected long lastInputTrackedTimestampGaze = 0;
protected static readonly KeyBinding cancelRotationKey = KeyBinding.FromKey(KeyCode.Escape);
protected readonly MouseRotationProvider mouseRotation = new MouseRotationProvider();
public SimulatedControllerDataProvider(MixedRealityInputSimulationProfile _profile)
{
profile = _profile;
}
/// <summary>
/// Update controller state based on keyboard and mouse input
/// </summary>
protected virtual void SimulateUserInput(MouseDelta mouseDelta)
{
float time = Time.time;
if (KeyInputSystem.GetKeyDown(profile.ToggleLeftControllerKey))
{
IsAlwaysVisibleLeft = !IsAlwaysVisibleLeft;
}
if (KeyInputSystem.GetKeyDown(profile.ToggleRightControllerKey))
{
IsAlwaysVisibleRight = !IsAlwaysVisibleRight;
}
if (!Application.isFocused && !KeyInputSystem.SimulatingInput)
{
IsSimulatingLeft = false;
IsSimulatingRight = false;
}
else
{
if (KeyInputSystem.GetKeyDown(profile.LeftControllerManipulationKey))
{
IsSimulatingLeft = true;
if (lastSimulationLeft > 0.0f && time - lastSimulationLeft <= profile.DoublePressTime)
{
IsAlwaysVisibleLeft = !IsAlwaysVisibleLeft;
}
lastSimulationLeft = time;
}
if (KeyInputSystem.GetKeyUp(profile.LeftControllerManipulationKey))
{
IsSimulatingLeft = false;
}
if (KeyInputSystem.GetKeyDown(profile.RightControllerManipulationKey))
{
IsSimulatingRight = true;
if (lastSimulationRight > 0.0f && time - lastSimulationRight <= profile.DoublePressTime)
{
IsAlwaysVisibleRight = !IsAlwaysVisibleRight;
}
lastSimulationRight = time;
}
if (KeyInputSystem.GetKeyUp(profile.RightControllerManipulationKey))
{
IsSimulatingRight = false;
}
if (isSimulatingGaze)
{
lastSimulationGaze = time;
}
}
mouseRotation.Update(profile.ControllerRotateButton, cancelRotationKey, false);
SimulateInput(ref lastInputTrackedTimestampLeft, InputStateLeft, IsSimulatingLeft, IsAlwaysVisibleLeft, mouseDelta, mouseRotation.IsRotating);
SimulateInput(ref lastInputTrackedTimestampRight, InputStateRight, IsSimulatingRight, IsAlwaysVisibleRight, mouseDelta, mouseRotation.IsRotating);
SimulateInput(ref lastInputTrackedTimestampGaze, InputStateGaze, isSimulatingGaze, false, mouseDelta, mouseRotation.IsRotating);
}
/// Apply changes to one controller and update tracking
internal abstract void SimulateInput(
ref long lastHandTrackedTimestamp,
SimulatedControllerState state,
bool isSimulating,
bool isAlwaysVisible,
MouseDelta mouseDelta,
bool useMouseRotation);
/// <summary>
/// Reset the controller to its default state.
/// </summary>
public void ResetInput(Handedness handedness)
{
if (handedness == Handedness.Left)
{
ResetInput(InputStateLeft, IsSimulatingLeft);
}
else
{
ResetInput(InputStateRight, IsSimulatingRight);
}
}
internal virtual void ResetInput(SimulatedControllerState state, bool isSimulating)
{
if (isSimulating)
{
// Start at current mouse position
Vector3 mousePos = UnityEngine.Input.mousePosition;
state.ResetPosition(CameraCache.Main.ScreenToViewportPoint(new Vector3(mousePos.x, mousePos.y, profile.DefaultControllerDistance)));
}
else
{
state.ResetPosition(new Vector3(0.5f, 0.5f, profile.DefaultControllerDistance));
}
}
}
}