Files

520 lines
18 KiB
C#

using System.Collections.Generic;
using HurricaneVR.Framework.ControllerInput;
using HurricaneVR.Framework.Core;
using HurricaneVR.Framework.Shared.Utilities;
using UnityEngine;
using UnityEngine.XR;
namespace HurricaneVR.Framework.Shared
{
public abstract class HVRController : MonoBehaviour
{
public HVRHandSide Side { get; set; }
public HVRButtonState GripButtonState;
public HVRButtonState TriggerButtonState;
public HVRButtonState PrimaryButtonState;
public HVRButtonState SecondaryButtonState;
public HVRButtonState MenuButtonState;
public HVRButtonState PrimaryTouchButtonState;
public HVRButtonState SecondaryTouchButtonState;
public HVRButtonState JoystickButtonState;
public HVRButtonState TrackpadButtonState;
public HVRButtonState JoystickTouchState;
public HVRButtonState TrackPadTouchState;
public HVRButtonState TriggerTouchState;
public HVRButtonState ThumbTouchState;
public HVRButtonState TriggerNearTouchState;
public HVRButtonState ThumbNearTouchState;
public HVRButtonState TrackPadUp;
public HVRButtonState TrackPadLeft;
public HVRButtonState TrackPadRight;
public HVRButtonState TrackPadDown;
public Vector2 JoystickAxis;
public Vector2 TrackpadAxis;
public bool PrimaryButton;
public bool SecondaryButton;
public bool JoystickClicked;
public bool TrackPadClicked;
public bool MenuButton;
public bool PrimaryTouch;
public bool SecondaryTouch;
public float Grip;
public float GripForce;
public float Trigger;
public bool ThumbTouch;
public bool TriggerTouch;
public bool ThumbNearTouch;
public bool TriggerNearTouch;
/// <summary>
/// Supplied by SteamVR, OpenXR, or OVR input bindings
/// </summary>
public bool GripButton;
/// <summary>
/// Suppplied by SteamVR, OpenXR, or OVR input bindings
/// </summary>
public bool TriggerButton;
public bool JoystickTouch;
public bool TrackPadTouch;
public float[] FingerCurls;
private float[] openxrFingerCurls;
public float ThumbCurl;
public float IndexCurl;
public float MiddleCurl;
public float RingCurl;
public float PinkyCurl;
public Vector3 Velocity;
public Vector3 AngularVelocity;
public bool IsActive { get; set; }
public XRNode XRNode;
private InputDevice _device;
public InputDevice Device
{
get
{
if (_device.isValid)
return _device;
_device = InputDevices.GetDeviceAtXRNode(XRNode);
return _device;
}
}
public Vector2 ThumbstickDeadZone { get; set; }
public HVRInputSettings InputMap { get; set; }
public HVRFingerSettings FingerSettings { get; set; }
public bool Knuckles => ControllerType == HVRControllerType.Knuckles;
public bool WMR => ControllerType == HVRControllerType.WMR;
public bool Vive => ControllerType == HVRControllerType.Vive;
private static readonly Dictionary<HVRButtons, HVRButtonState> _leftButtonStates = new Dictionary<HVRButtons, HVRButtonState>();
private static readonly Dictionary<HVRButtons, HVRButtonState> _rightButtonStates = new Dictionary<HVRButtons, HVRButtonState>();
public static float[] LeftFingerCurls = new float[5];
public static float[] RightFingerCurls = new float[5];
public HVRControllerType ControllerType { get; set; }
public float AngularVelocityMagnitude;
public float VelocityMagnitude;
public readonly CircularBuffer<float> RecentVelocities = new CircularBuffer<float>(200);
protected virtual void Awake()
{
FingerCurls = new float[5];
openxrFingerCurls = new float[5];
}
protected virtual void Start()
{
ResetTrackedVelocities();
}
private void Update()
{
UpdateInput();
CorrectDeadzone();
CheckButtonState(HVRButtons.Grip, ref GripButtonState);
CheckButtonState(HVRButtons.Trigger, ref TriggerButtonState);
CheckButtonState(HVRButtons.JoystickButton, ref JoystickButtonState);
CheckButtonState(HVRButtons.TrackPadButton, ref TrackpadButtonState);
CheckButtonState(HVRButtons.Primary, ref PrimaryButtonState);
CheckButtonState(HVRButtons.Secondary, ref SecondaryButtonState);
CheckButtonState(HVRButtons.Menu, ref MenuButtonState);
CheckButtonState(HVRButtons.PrimaryTouch, ref PrimaryTouchButtonState);
CheckButtonState(HVRButtons.SecondaryTouch, ref SecondaryTouchButtonState);
CheckButtonState(HVRButtons.JoystickTouch, ref JoystickTouchState);
CheckButtonState(HVRButtons.TrackPadTouch, ref TrackPadTouchState);
CheckButtonState(HVRButtons.TriggerTouch, ref TriggerTouchState);
CheckButtonState(HVRButtons.ThumbTouch, ref ThumbTouchState);
CheckButtonState(HVRButtons.TriggerNearTouch, ref TriggerNearTouchState);
CheckButtonState(HVRButtons.ThumbNearTouch, ref ThumbNearTouchState);
CheckButtonState(HVRButtons.TrackPadUp, ref TrackPadUp);
CheckButtonState(HVRButtons.TrackPadLeft, ref TrackPadLeft);
CheckButtonState(HVRButtons.TrackPadRight, ref TrackPadRight);
CheckButtonState(HVRButtons.TrackPadDown, ref TrackPadDown);
RecentVelocities.Enqueue(Velocity.magnitude);
Device.TryGetFeatureValue(CommonUsages.deviceVelocity, out Velocity);
Device.TryGetFeatureValue(CommonUsages.deviceAngularVelocity, out AngularVelocity);
AngularVelocityMagnitude = AngularVelocity.magnitude;
VelocityMagnitude = Velocity.magnitude;
UpdateFingerCurls();
var curls = LeftFingerCurls;
if (XRNode == XRNode.RightHand)
{
curls = RightFingerCurls;
}
for (int i = 0; i < 5; i++)
{
curls[i] = FingerCurls[i];
}
AfterInputUpdate();
}
protected virtual void AfterInputUpdate()
{
}
protected virtual void UpdateFingerCurls()
{
if (FingerSettings)
{
FingerSettings.Evaluate(
FingerCurls,
Grip,
Trigger,
TriggerTouchState.Active,
PrimaryTouchButtonState.Active,
SecondaryTouchButtonState.Active,
TrackPadTouchState.Active,
JoystickTouchState.Active,
Knuckles,
HVRInputManager.Instance.IsOpenXR);
}
#if USING_XRHANDS
if (Knuckles && HVRInputManager.Instance.IsOpenXR)
{
HVROpenXRFingerCurls.Update();
if (HVROpenXRFingerCurls.TryGetCurls(Side, openxrFingerCurls))
{
FingerCurls[2] = openxrFingerCurls[2];
FingerCurls[3] = openxrFingerCurls[3];
FingerCurls[4] = openxrFingerCurls[4];
}
}
#endif
}
protected void ResetTrackedVelocities()
{
for (var i = 0; i < 200; i++)
{
RecentVelocities.Enqueue(0f);
}
}
public float GetAverageVelocity(float seconds)
{
var frames = seconds / Time.fixedDeltaTime;
var sum = 0f;
for (var i = 0; i < frames; i++)
{
sum += RecentVelocities[i];
}
if (frames == 0f) return 0f;
return sum / frames;
}
private void CorrectDeadzone()
{
if (Mathf.Abs(JoystickAxis.x) < ThumbstickDeadZone.x) JoystickAxis.x = 0f;
if (Mathf.Abs(JoystickAxis.y) < ThumbstickDeadZone.y) JoystickAxis.y = 0f;
}
protected abstract void UpdateInput();
protected virtual void CheckButtonState(HVRButtons button, ref HVRButtonState buttonState)
{
ResetButton(ref buttonState);
switch (button)
{
case HVRButtons.Grip:
buttonState.Value = Grip;
SetButtonState(button, ref buttonState, GetIsGripPressed());
break;
case HVRButtons.Trigger:
buttonState.Value = Trigger;
SetButtonState(button, ref buttonState, GetIsTriggerPressed());
break;
case HVRButtons.Primary:
SetButtonState(button, ref buttonState, PrimaryButton);
break;
case HVRButtons.PrimaryTouch:
SetButtonState(button, ref buttonState, PrimaryTouch);
break;
case HVRButtons.Secondary:
SetButtonState(button, ref buttonState, SecondaryButton);
break;
case HVRButtons.SecondaryTouch:
SetButtonState(button, ref buttonState, SecondaryTouch);
break;
case HVRButtons.Menu:
SetButtonState(button, ref buttonState, MenuButton);
break;
case HVRButtons.JoystickButton:
SetButtonState(button, ref buttonState, JoystickClicked);
break;
case HVRButtons.TrackPadButton:
SetButtonState(button, ref buttonState, TrackPadClicked);
break;
case HVRButtons.JoystickTouch:
SetButtonState(button, ref buttonState, JoystickTouch);
break;
case HVRButtons.TrackPadTouch:
SetButtonState(button, ref buttonState, TrackPadTouch);
break;
case HVRButtons.TriggerTouch:
SetButtonState(button, ref buttonState, TriggerTouch);
break;
case HVRButtons.ThumbTouch:
SetButtonState(button, ref buttonState, ThumbTouch);
break;
case HVRButtons.TriggerNearTouch:
SetButtonState(button, ref buttonState, TriggerNearTouch);
break;
case HVRButtons.ThumbNearTouch:
SetButtonState(button, ref buttonState, ThumbNearTouch);
break;
case HVRButtons.TrackPadLeft:
SetButtonState(button, ref buttonState, TrackPadClicked && TrackpadAxis.x <= -InputMap.Axis2DLeftThreshold);
break;
case HVRButtons.TrackPadRight:
SetButtonState(button, ref buttonState, TrackPadClicked && TrackpadAxis.x >= InputMap.Axis2DRighThreshold);
break;
case HVRButtons.TrackPadUp:
SetButtonState(button, ref buttonState, TrackPadClicked && TrackpadAxis.y >= InputMap.Axis2DUpThreshold);
break;
case HVRButtons.TrackPadDown:
SetButtonState(button, ref buttonState, TrackPadClicked && TrackpadAxis.y <= -InputMap.Axis2DDownThreshold);
break;
}
}
private bool _triggerLowerReset;
private bool _triggerUpperReset;
private bool _gripLowerReset;
private bool _gripUpperReset;
protected virtual bool GetIsTriggerPressed()
{
if (InputMap.TriggerUseAnalog)
{
if (InputMap.TriggerUseReleaseThreshold)
{
if (InputMap.TriggerThreshold > InputMap.TriggerReleaseThreshold)
{
if (TriggerButtonState.Active)
return Trigger >= InputMap.TriggerReleaseThreshold;
return Trigger >= InputMap.TriggerThreshold;
}
if (Trigger < InputMap.TriggerThreshold)
_triggerLowerReset = true;
if (TriggerButtonState.Active)
{
if (Trigger > InputMap.TriggerReleaseThreshold)
_triggerUpperReset = true;
if (Trigger < InputMap.TriggerReleaseThreshold && _triggerUpperReset)
{
_triggerUpperReset = false;
return false;
}
if (Trigger < InputMap.TriggerThreshold)
return false;
return true;
}
if (Trigger > InputMap.TriggerReleaseThreshold && !_triggerUpperReset && !InputMap.TriggerRequireReset)
return true;
if (Trigger > InputMap.TriggerThreshold && _triggerLowerReset)
{
_triggerLowerReset = false;
return true;
}
return false;
}
return Trigger >= InputMap.TriggerThreshold;
}
return TriggerButton;
}
protected virtual bool GetIsGripPressed()
{
if (InputMap.GripUseAnalog)
{
if (InputMap.GripUseReleaseThreshold)
{
if (InputMap.GripThreshold > InputMap.GripReleaseThreshold)
{
if (GripButtonState.Active)
return Grip >= InputMap.GripReleaseThreshold;
return Grip >= InputMap.GripThreshold;
}
if (Grip < InputMap.GripThreshold)
_gripLowerReset = true;
if (GripButtonState.Active)
{
if (Grip > InputMap.GripReleaseThreshold)
_gripUpperReset = true;
if (Grip < InputMap.GripReleaseThreshold && _gripUpperReset)
{
_gripUpperReset = false;
return false;
}
if (Grip < InputMap.GripThreshold)
return false;
return true;
}
if (Grip > InputMap.GripReleaseThreshold && !_gripUpperReset && !InputMap.GripRequireReset)
return true;
if (Grip > InputMap.GripThreshold && _gripLowerReset)
{
_gripLowerReset = false;
return true;
}
return false;
}
return Grip >= InputMap.GripThreshold;
}
return GripButton;
}
protected void SetButtonState(HVRButtons button, ref HVRButtonState buttonState, bool pressed)
{
if (pressed)
{
if (!buttonState.Active)
{
buttonState.JustActivated = true;
buttonState.Active = true;
}
}
else
{
if (buttonState.Active)
{
buttonState.Active = false;
buttonState.JustDeactivated = true;
}
}
SetButtonState(Side, button, buttonState);
}
protected void ResetButton(ref HVRButtonState buttonState)
{
buttonState.JustDeactivated = false;
buttonState.JustActivated = false;
buttonState.Value = 0f;
}
public static void SetButtonState(HVRHandSide side, HVRButtons button, HVRButtonState state)
{
var map = side == HVRHandSide.Right ? _rightButtonStates : _leftButtonStates;
map[button] = state;
}
public static HVRButtonState GetButtonState(HVRHandSide side, HVRButtons button)
{
var map = side == HVRHandSide.Right ? _rightButtonStates : _leftButtonStates;
map.TryGetValue(button, out var state);
return state;
}
public virtual void Vibrate(HapticData haptics)
{
if (HVRSettings.Instance.DisableHaptics) return;
if (haptics != null && haptics.Valid)
{
Vibrate(haptics.Amplitude, haptics.Duration, haptics.Frequency);
}
}
/// <summary>
/// <para>Sends a haptic impulse to the controller.</para>
/// </summary>
/// <param name="amplitude">The normalized (0.0 to 1.0) amplitude value of the haptic impulse to play on the device.</param>
/// <param name="duration">The duration in seconds that the haptic impulse will play. Only supported on Oculus.</param>
public virtual void Vibrate(float amplitude, float duration = 1f, float frequency = 1f)
{
if (HVRSettings.Instance.DisableHaptics) return;
if (Device.isValid && Device.TryGetHapticCapabilities(out var hapticCapabilities) && hapticCapabilities.supportsImpulse)
{
amplitude = Mathf.Clamp(amplitude, 0f, 1f);
Device.SendHapticImpulse(0, amplitude, duration);
}
}
}
public enum InputSDK
{
XRInput,
Oculus,
SteamVR,
InputSystem,
None
}
public enum HVRControllerType
{
None,
Oculus,
WMR,
Vive,
Knuckles,
Cosmos,
ReverbG2,
Pico
}
}