Replace UltimateXR with HurricaneVR
This commit is contained in:
520
Assets/HurricaneVR/Framework/Scripts/Shared/HVRController.cs
Normal file
520
Assets/HurricaneVR/Framework/Scripts/Shared/HVRController.cs
Normal file
@@ -0,0 +1,520 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c59d950586f74df5b9d25caf78874ce0
|
||||
timeCreated: 1596308197
|
||||
125
Assets/HurricaneVR/Framework/Scripts/Shared/HVREnums.cs
Normal file
125
Assets/HurricaneVR/Framework/Scripts/Shared/HVREnums.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
|
||||
namespace HurricaneVR.Framework.Shared
|
||||
{
|
||||
public enum HVRHoldType
|
||||
{
|
||||
OneHand, Swap, TwoHanded, ManyHands
|
||||
}
|
||||
|
||||
public enum PoseType
|
||||
{
|
||||
HandPoser,
|
||||
PhysicPoser,
|
||||
Offset
|
||||
}
|
||||
|
||||
public enum HVRGrabTracking
|
||||
{
|
||||
ConfigurableJoint,
|
||||
FixedJoint,
|
||||
None
|
||||
}
|
||||
|
||||
public enum HVRHandSide
|
||||
{
|
||||
Left, Right
|
||||
}
|
||||
|
||||
public enum HVRSortMode
|
||||
{
|
||||
Distance, SquareMagnitude
|
||||
}
|
||||
|
||||
public enum HVRGrabTrigger
|
||||
{
|
||||
Active, Toggle, ManualRelease
|
||||
}
|
||||
|
||||
//public enum HVRGrabTriggerOverride
|
||||
//{
|
||||
// Active, Toggle, ManualRelease
|
||||
//}
|
||||
|
||||
public enum HVRGrabControls
|
||||
{
|
||||
GripOrTrigger,
|
||||
GripOnly,
|
||||
TriggerOnly,
|
||||
}
|
||||
|
||||
public enum HVRGrabDetection
|
||||
{
|
||||
Grabbable, Socket
|
||||
}
|
||||
|
||||
//the order of these cannot change, they are used in serialization
|
||||
public enum HVRButtons
|
||||
{
|
||||
Grip,
|
||||
Trigger,
|
||||
Primary,
|
||||
PrimaryTouch,
|
||||
Secondary,
|
||||
SecondaryTouch,
|
||||
Menu,
|
||||
JoystickButton,
|
||||
TrackPadButton,
|
||||
JoystickTouch,
|
||||
TriggerTouch,
|
||||
ThumbTouch,
|
||||
TriggerNearTouch,
|
||||
ThumbNearTouch,
|
||||
TrackPadLeft,
|
||||
TrackPadRight,
|
||||
TrackPadUp,
|
||||
TrackPadDown,
|
||||
TrackPadTouch
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct HVRButtonState
|
||||
{
|
||||
public bool Active;
|
||||
public bool JustActivated;
|
||||
public bool JustDeactivated;
|
||||
public float Value;
|
||||
}
|
||||
|
||||
public enum HVRLayers
|
||||
{
|
||||
Grabbable, Hand, DynamicPose, Player
|
||||
}
|
||||
|
||||
public enum HVRAxis
|
||||
{
|
||||
X, Y, Z,
|
||||
NegX, NegY, NegZ
|
||||
}
|
||||
|
||||
public enum HVRXRInputFeatures
|
||||
{
|
||||
None = 0,
|
||||
MenuButton,
|
||||
Trigger,
|
||||
Grip,
|
||||
TriggerPressed,
|
||||
GripPressed,
|
||||
PrimaryButton,
|
||||
PrimaryTouch,
|
||||
SecondaryButton,
|
||||
SecondaryTouch,
|
||||
Primary2DAxisTouch,
|
||||
Primary2DAxisClick,
|
||||
Secondary2DAxisTouch,
|
||||
Secondary2DAxisClick,
|
||||
PrimaryAxis2DUp,
|
||||
PrimaryAxis2DDown,
|
||||
PrimaryAxis2DLeft,
|
||||
PrimaryAxis2DRight,
|
||||
SecondaryAxis2DUp,
|
||||
SecondaryAxis2DDown,
|
||||
SecondaryAxis2DLeft,
|
||||
SecondaryAxis2DRight
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c24b7f0e251c41ae877baeb4f1d3fefe
|
||||
timeCreated: 1596293520
|
||||
153
Assets/HurricaneVR/Framework/Scripts/Shared/HVRFingerSettings.cs
Normal file
153
Assets/HurricaneVR/Framework/Scripts/Shared/HVRFingerSettings.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using HurricaneVR.Framework.ControllerInput;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Shared
|
||||
{
|
||||
//https://www.cloudwalkingames.com/en/scriptable-objects/fingersettings
|
||||
[CreateAssetMenu(menuName = "HurricaneVR/Finger Settings", fileName = "FingerSettings")]
|
||||
public class HVRFingerSettings : ScriptableObject
|
||||
{
|
||||
[Header("Non Knuckles SteamVR Finger Curl Overrides. Enable to use below weights when using SteamVR.")]
|
||||
public bool OverrideThumb = true;
|
||||
public bool OverrideIndex = true;
|
||||
public bool OverrideTriggerGrab = true; //trigger click controllers bend last 3 fingers on trigger pull
|
||||
|
||||
[Header("Knuckles Overrides")]
|
||||
|
||||
[Tooltip("Knuckles default thumb behaviour is mostly bent thumb on all capacitive buttons, and even joystick movement. Enable to override with the below touch weights.")]
|
||||
public bool KnucklesOverrideThumb = true;
|
||||
|
||||
[Tooltip("Knuckles Default trigger touch pulls to .83, trigger completes the finger curl to .83 to 1. Enable to override with the below touch weights")]
|
||||
public bool KnucklesOverrideIndex;
|
||||
|
||||
[Tooltip("Since OpenXR doesn't support finger tracking yet, you might want to set this to true until they implement it")]
|
||||
public bool KnucklesOverrideGripFingers;
|
||||
|
||||
[Header("Per Button Per Finger Weights")]
|
||||
|
||||
public HVRTouchWeight JoystickTouchWeight;
|
||||
public HVRTouchWeight TrackpadTouchWeight;
|
||||
public HVRTouchWeight PrimaryTouchWeight;
|
||||
public HVRTouchWeight SecondaryTouchWeight;
|
||||
public HVRTouchWeight TriggerTouchWeight;
|
||||
public HVRTouchWeight GripWeight;
|
||||
public HVRTouchWeight TriggerWeight;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
OverrideThumb = true;
|
||||
OverrideIndex = true;
|
||||
|
||||
GripWeight = new HVRTouchWeight(0f, 0f, 1f, 1f, 1f);
|
||||
TriggerWeight = new HVRTouchWeight(0f, 1f, 0f, 0f, 0f);
|
||||
PrimaryTouchWeight = SecondaryTouchWeight = TrackpadTouchWeight = new HVRTouchWeight(1f, 0f, 0f, 0f, 0f);
|
||||
JoystickTouchWeight = new HVRTouchWeight(0f, 0f, 0f, 0f, 0f);
|
||||
TriggerTouchWeight = new HVRTouchWeight(0f, .25f, 0f, 0f, 0f);
|
||||
}
|
||||
|
||||
public float ThumbTotal => JoystickTouchWeight.Thumb + TrackpadTouchWeight.Thumb + PrimaryTouchWeight.Thumb + SecondaryTouchWeight.Thumb +
|
||||
TriggerTouchWeight.Thumb + GripWeight.Thumb + TriggerWeight.Thumb;
|
||||
|
||||
public float IndexTotal => JoystickTouchWeight.Index + TrackpadTouchWeight.Index + PrimaryTouchWeight.Index + SecondaryTouchWeight.Index +
|
||||
TriggerTouchWeight.Index + GripWeight.Index + TriggerWeight.Index;
|
||||
|
||||
public float MiddleTotal => JoystickTouchWeight.Middle + TrackpadTouchWeight.Middle + PrimaryTouchWeight.Middle + SecondaryTouchWeight.Middle +
|
||||
TriggerTouchWeight.Middle + GripWeight.Middle + TriggerWeight.Middle;
|
||||
|
||||
public float RingTotal => JoystickTouchWeight.Ring + TrackpadTouchWeight.Ring + PrimaryTouchWeight.Ring + SecondaryTouchWeight.Ring +
|
||||
TriggerTouchWeight.Ring + GripWeight.Ring + TriggerWeight.Ring;
|
||||
public float PinkyTotal => JoystickTouchWeight.Pinky + TrackpadTouchWeight.Pinky + PrimaryTouchWeight.Pinky + SecondaryTouchWeight.Pinky +
|
||||
TriggerTouchWeight.Pinky + GripWeight.Pinky + TriggerWeight.Pinky;
|
||||
|
||||
public void Evaluate(float[] curls, float grip, float trigger, bool triggerTouch, bool primaryTouch, bool secondaryTouch, bool trackpadTouch, bool joystickTouch, bool knuckles, bool isOpenXR)
|
||||
{
|
||||
var joystick = joystickTouch ? 1f : 0f;
|
||||
var trackPad = trackpadTouch ? 1f : 0f;
|
||||
var primary = primaryTouch ? 1f : 0f;
|
||||
var secondary = secondaryTouch ? 1f : 0f;
|
||||
var triggerT = triggerTouch ? 1f : 0f;
|
||||
|
||||
curls[0] = JoystickTouchWeight.Thumb * joystick +
|
||||
TrackpadTouchWeight.Thumb * trackPad +
|
||||
PrimaryTouchWeight.Thumb * primary +
|
||||
SecondaryTouchWeight.Thumb * secondary +
|
||||
TriggerTouchWeight.Thumb * triggerT +
|
||||
GripWeight.Thumb * grip +
|
||||
TriggerWeight.Thumb * trigger;
|
||||
|
||||
curls[1] = JoystickTouchWeight.Index * joystick +
|
||||
TrackpadTouchWeight.Index * trackPad +
|
||||
PrimaryTouchWeight.Index * primary +
|
||||
SecondaryTouchWeight.Index * secondary +
|
||||
TriggerTouchWeight.Index * triggerT +
|
||||
GripWeight.Index * grip +
|
||||
TriggerWeight.Index * trigger;
|
||||
|
||||
//until openxr has index finger curls supplied (if ever..)
|
||||
if (!knuckles || (KnucklesOverrideGripFingers || isOpenXR))
|
||||
{
|
||||
curls[2] = JoystickTouchWeight.Middle * joystick +
|
||||
TrackpadTouchWeight.Middle * trackPad +
|
||||
PrimaryTouchWeight.Middle * primary +
|
||||
SecondaryTouchWeight.Middle * secondary +
|
||||
TriggerTouchWeight.Middle * triggerT +
|
||||
GripWeight.Middle * grip +
|
||||
TriggerWeight.Middle * trigger;
|
||||
|
||||
curls[3] = JoystickTouchWeight.Ring * joystick +
|
||||
TrackpadTouchWeight.Ring * trackPad +
|
||||
PrimaryTouchWeight.Ring * primary +
|
||||
SecondaryTouchWeight.Ring * secondary +
|
||||
TriggerTouchWeight.Ring * triggerT +
|
||||
GripWeight.Ring * grip +
|
||||
TriggerWeight.Ring * trigger;
|
||||
|
||||
curls[4] = JoystickTouchWeight.Pinky * joystick +
|
||||
TrackpadTouchWeight.Pinky * trackPad +
|
||||
PrimaryTouchWeight.Pinky * primary +
|
||||
SecondaryTouchWeight.Pinky * secondary +
|
||||
TriggerTouchWeight.Pinky * triggerT +
|
||||
GripWeight.Pinky * grip +
|
||||
TriggerWeight.Pinky * trigger;
|
||||
}
|
||||
|
||||
//if (ThumbTotal > 0) curls[0] /= ThumbTotal;
|
||||
//if (IndexTotal > 0) curls[1] /= IndexTotal;
|
||||
//if (MiddleTotal > 0) curls[2] /= MiddleTotal;
|
||||
//if (RingTotal > 0) curls[3] /= RingTotal;
|
||||
//if (PinkyTotal > 0) curls[4] /= PinkyTotal;
|
||||
|
||||
|
||||
curls[0] = Mathf.Clamp01(curls[0]);
|
||||
curls[1] = Mathf.Clamp01(curls[1]);
|
||||
curls[2] = Mathf.Clamp01(curls[2]);
|
||||
curls[3] = Mathf.Clamp01(curls[3]);
|
||||
curls[4] = Mathf.Clamp01(curls[4]);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct HVRTouchWeight
|
||||
{
|
||||
[Range(0, 1)]
|
||||
public float Thumb;
|
||||
[Range(0, 1)]
|
||||
public float Index;
|
||||
[Range(0, 1)]
|
||||
public float Middle;
|
||||
[Range(0, 1)]
|
||||
public float Ring;
|
||||
[Range(0, 1)]
|
||||
public float Pinky;
|
||||
|
||||
public HVRTouchWeight(float t, float i, float m, float r, float p)
|
||||
{
|
||||
Thumb = t;
|
||||
Index = i;
|
||||
Middle = m;
|
||||
Ring = r;
|
||||
Pinky = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b171784bbbe1f24784deef229788af8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Shared
|
||||
{
|
||||
|
||||
[CreateAssetMenu(menuName = "HurricaneVR/Grab Haptics", fileName = "GrabHaptics")]
|
||||
public class HVRGrabHaptics : ScriptableObject
|
||||
{
|
||||
public void Reset()
|
||||
{
|
||||
HandHover = new HapticData(.02f, .05f, 50f);
|
||||
|
||||
ForceHover = new HapticData(.02f, .05f, 50);
|
||||
|
||||
HandGrab = new HapticData(.04f, .5f, 50f);
|
||||
ForceGrab = new HapticData(.04f, .6f, 60f);
|
||||
|
||||
HandRelease = new HapticData(.025f, .2f, 45f);
|
||||
}
|
||||
|
||||
public HapticData HandGrab;
|
||||
public HapticData HandRelease;
|
||||
public HapticData HandHover;
|
||||
|
||||
public HapticData ForceGrab;
|
||||
public HapticData ForceHover;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HapticData
|
||||
{
|
||||
public float Duration;
|
||||
public float Amplitude;
|
||||
public float Frequency;
|
||||
|
||||
public bool Valid => Duration > .001f && Amplitude > .001f && Frequency > .001f;
|
||||
|
||||
public HapticData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public HapticData(float duration, float amplitude, float frequency)
|
||||
{
|
||||
Duration = duration;
|
||||
Amplitude = amplitude;
|
||||
Frequency = frequency;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2242baed513f2a04793dd67910504d3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Shared
|
||||
{
|
||||
[CreateAssetMenu(menuName = "HurricaneVR/Input Settings", fileName = "InputSettings")]
|
||||
public class HVRInputSettings : ScriptableObject
|
||||
{
|
||||
[Header("XR Input Settings (Oculus / WMR Plugins only) - SteamVR / OpenXR can ignore.")]
|
||||
public InputAxes JoystickAxis;
|
||||
public InputAxes TrackPadAxis;
|
||||
public HVRXRInputFeatures Primary = HVRXRInputFeatures.PrimaryButton;
|
||||
public HVRXRInputFeatures Secondary = HVRXRInputFeatures.SecondaryButton;
|
||||
public HVRXRInputFeatures Menu = HVRXRInputFeatures.MenuButton;
|
||||
public HVRXRInputFeatures PrimaryTouch = HVRXRInputFeatures.PrimaryTouch;
|
||||
public HVRXRInputFeatures SecondaryTouch = HVRXRInputFeatures.SecondaryTouch;
|
||||
public HVRXRInputFeatures JoystickButton = HVRXRInputFeatures.Primary2DAxisClick;
|
||||
public HVRXRInputFeatures TrackPadButton = HVRXRInputFeatures.Secondary2DAxisClick;
|
||||
|
||||
public HVRXRInputFeatures JoystickTouch = HVRXRInputFeatures.Primary2DAxisTouch;
|
||||
public HVRXRInputFeatures TrackPadTouch = HVRXRInputFeatures.Secondary2DAxisTouch;
|
||||
|
||||
[Header("Should Grip / Trigger use Analog checks or button presses from SteamVR / OpenXR")]
|
||||
|
||||
[Tooltip("If true grip clicked will use the grip 0-1 analog value compared to the GripThreshold, otherwise the grip pressed SteamVR / OpenXR binding")]
|
||||
public bool GripUseAnalog = true;
|
||||
|
||||
[Tooltip("If true trigger clicked check will use the trigger 0-1 analog value compared to the TriggerThreshold, otherwise the trigger pressed SteamVR / OpenXR binding will be used.")]
|
||||
public bool TriggerUseAnalog = true;
|
||||
|
||||
[Header("Grip Analog Activation")]
|
||||
|
||||
public float GripThreshold = .7f;
|
||||
|
||||
[Tooltip("If false the Threshold only will be used to determine Trigger activation / deactivation")]
|
||||
public bool GripUseReleaseThreshold;
|
||||
|
||||
[Tooltip("If lower than the Threshold, the value must fall below this value to activate. " +
|
||||
"When larger than the Threshold, the value must go above and then back below this value to deactivate.")]
|
||||
public float GripReleaseThreshold = .7f;
|
||||
|
||||
|
||||
[Tooltip("Only used when the release threshold is greater than the threshold, if true the value must drop below the Threshold before it can be considered active again" +
|
||||
"Otherwise going back over the Release Threshold will activate the button.")]
|
||||
public bool GripRequireReset;
|
||||
|
||||
[Header("Trigger Analog Activation")]
|
||||
|
||||
public float TriggerThreshold = .7f;
|
||||
|
||||
[Tooltip("If false the Threshold only will be used to determine Trigger activation / deactivation")]
|
||||
public bool TriggerUseReleaseThreshold;
|
||||
|
||||
[Tooltip("If lower than the Threshold, the value must fall below this value to activate. " +
|
||||
"When larger than the Threshold, the value must go above and then back below this value to deactivate.")]
|
||||
public float TriggerReleaseThreshold = .7f;
|
||||
|
||||
[Tooltip("Only used when the release threshold is greater than the threshold, if true the value must drop below the Threshold before it can be considered active again" +
|
||||
"Otherwise going back over the Release Threshold will activate the button.")]
|
||||
public bool TriggerRequireReset;
|
||||
|
||||
[Header("Track Pad Click Thresholds")]
|
||||
|
||||
public float Axis2DUpThreshold = .7f;
|
||||
public float Axis2DDownThreshold = .7f;
|
||||
public float Axis2DLeftThreshold = .7f;
|
||||
public float Axis2DRighThreshold = .7f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public enum InputAxes
|
||||
{
|
||||
None,
|
||||
Primary2DAxis = 1,
|
||||
Secondary2DAxis = 2,
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb2759c823f44d27b9a55f99eabacb8f
|
||||
timeCreated: 1600315200
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HurricaneVR.Framework.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute can only be applied to fields because its
|
||||
/// associated PropertyDrawer only operates on fields (either
|
||||
/// public or tagged with the [SerializeField] attribute) in
|
||||
/// the target MonoBehaviour.
|
||||
/// </summary>
|
||||
[System.AttributeUsage(System.AttributeTargets.Field)]
|
||||
public class InspectorButtonAttribute : PropertyAttribute
|
||||
{
|
||||
public static float kDefaultButtonWidth = 150;
|
||||
|
||||
public readonly string MethodName;
|
||||
|
||||
private float _buttonWidth = kDefaultButtonWidth;
|
||||
public float ButtonWidth
|
||||
{
|
||||
get { return _buttonWidth; }
|
||||
set { _buttonWidth = value; }
|
||||
}
|
||||
|
||||
public InspectorButtonAttribute(string MethodName, int buttonWidth = 150)
|
||||
{
|
||||
this.MethodName = MethodName;
|
||||
this.ButtonWidth = buttonWidth;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(InspectorButtonAttribute))]
|
||||
public class InspectorButtonPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
private MethodInfo _eventMethodInfo = null;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label)
|
||||
{
|
||||
InspectorButtonAttribute inspectorButtonAttribute = (InspectorButtonAttribute)attribute;
|
||||
Rect buttonRect = new Rect(position.x + (position.width - inspectorButtonAttribute.ButtonWidth) * 0.5f, position.y, inspectorButtonAttribute.ButtonWidth, position.height);
|
||||
if (GUI.Button(buttonRect, label.text))
|
||||
{
|
||||
System.Type eventOwnerType = prop.serializedObject.targetObject.GetType();
|
||||
string eventName = inspectorButtonAttribute.MethodName;
|
||||
|
||||
if (_eventMethodInfo == null)
|
||||
_eventMethodInfo = eventOwnerType.GetMethod(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
if (_eventMethodInfo != null)
|
||||
_eventMethodInfo.Invoke(prop.serializedObject.targetObject, null);
|
||||
else
|
||||
Debug.LogWarning(string.Format("InspectorButton: Unable to find method {0} in {1}", eventName, eventOwnerType));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6162819cbdc6b694c8f056290257a292
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f1803bf40c60f54c9740b231b61e8b9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,360 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HurricaneVR.Framework.Shared.Utilities
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
/// <summary>
|
||||
/// Circular buffer.
|
||||
///
|
||||
/// When writing to a full buffer:
|
||||
/// PushBack -> removes this[0] / Front()
|
||||
/// PushFront -> removes this[Size-1] / Back()
|
||||
///
|
||||
/// this implementation is inspired by
|
||||
/// http://www.boost.org/doc/libs/1_53_0/libs/circular_buffer/doc/circular_buffer.html
|
||||
/// because I liked their interface.
|
||||
/// </summary>
|
||||
public class CircularBuffer<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly T[] _buffer;
|
||||
|
||||
/// <summary>
|
||||
/// The _start. Index of the first element in buffer.
|
||||
/// </summary>
|
||||
private int _start;
|
||||
|
||||
/// <summary>
|
||||
/// The _end. Index after the last element in the buffer.
|
||||
/// </summary>
|
||||
private int _end;
|
||||
|
||||
/// <summary>
|
||||
/// The _size. Buffer size.
|
||||
/// </summary>
|
||||
private int _size;
|
||||
|
||||
public CircularBuffer(int capacity)
|
||||
: this(capacity, new T[] { })
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CircularBuffer{T}"/> class.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name='capacity'>
|
||||
/// Buffer capacity. Must be positive.
|
||||
/// </param>
|
||||
/// <param name='items'>
|
||||
/// Items to fill buffer with. Items length must be less than capacity.
|
||||
/// Suggestion: use Skip(x).Take(y).ToArray() to build this argument from
|
||||
/// any enumerable.
|
||||
/// </param>
|
||||
public CircularBuffer(int capacity, T[] items)
|
||||
{
|
||||
if (capacity < 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Circular buffer cannot have negative or zero capacity.", nameof(capacity));
|
||||
}
|
||||
if (items == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(items));
|
||||
}
|
||||
if (items.Length > capacity)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Too many items to fit circular buffer", nameof(items));
|
||||
}
|
||||
|
||||
_buffer = new T[capacity];
|
||||
|
||||
Array.Copy(items, _buffer, items.Length);
|
||||
_size = items.Length;
|
||||
|
||||
_start = 0;
|
||||
_end = _size == capacity ? 0 : _size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum capacity of the buffer. Elements pushed into the buffer after
|
||||
/// maximum capacity is reached (IsFull = true), will remove an element.
|
||||
/// </summary>
|
||||
public int Capacity { get { return _buffer.Length; } }
|
||||
|
||||
public bool IsFull
|
||||
{
|
||||
get
|
||||
{
|
||||
return Size == Capacity;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return Size == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current buffer size (the number of elements that the buffer has).
|
||||
/// </summary>
|
||||
public int Size { get { return _size; } }
|
||||
|
||||
/// <summary>
|
||||
/// Element at the front of the buffer - this[0].
|
||||
/// </summary>
|
||||
/// <returns>The value of the element of type T at the front of the buffer.</returns>
|
||||
public T Front()
|
||||
{
|
||||
ThrowIfEmpty();
|
||||
return _buffer[_start];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Element at the back of the buffer - this[Size - 1].
|
||||
/// </summary>
|
||||
/// <returns>The value of the element of type T at the back of the buffer.</returns>
|
||||
public T Back()
|
||||
{
|
||||
ThrowIfEmpty();
|
||||
return _buffer[(_end != 0 ? _end : Capacity) - 1];
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer is empty", index));
|
||||
}
|
||||
if (index >= _size)
|
||||
{
|
||||
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}", index, _size));
|
||||
}
|
||||
int actualIndex = InternalIndex(index);
|
||||
return _buffer[actualIndex];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer is empty", index));
|
||||
}
|
||||
if (index >= _size)
|
||||
{
|
||||
throw new IndexOutOfRangeException(string.Format("Cannot access index {0}. Buffer size is {1}", index, _size));
|
||||
}
|
||||
int actualIndex = InternalIndex(index);
|
||||
_buffer[actualIndex] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a new element to the back of the buffer. Back()/this[Size-1]
|
||||
/// will now return this element.
|
||||
///
|
||||
/// When the buffer is full, the element at Front()/this[0] will be
|
||||
/// popped to allow for this new element to fit.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to push to the back of the buffer</param>
|
||||
public void Dequeue(T item)
|
||||
{
|
||||
if (IsFull)
|
||||
{
|
||||
_buffer[_end] = item;
|
||||
Increment(ref _end);
|
||||
_start = _end;
|
||||
}
|
||||
else
|
||||
{
|
||||
_buffer[_end] = item;
|
||||
Increment(ref _end);
|
||||
++_size;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a new element to the front of the buffer. Front()/this[0]
|
||||
/// will now return this element.
|
||||
///
|
||||
/// When the buffer is full, the element at Back()/this[Size-1] will be
|
||||
/// popped to allow for this new element to fit.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to push to the front of the buffer</param>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
if (IsFull)
|
||||
{
|
||||
Decrement(ref _start);
|
||||
_end = _start;
|
||||
_buffer[_start] = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
Decrement(ref _start);
|
||||
_buffer[_start] = item;
|
||||
++_size;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element at the back of the buffer. Decreasing the
|
||||
/// Buffer size by 1.
|
||||
/// </summary>
|
||||
public void PopBack()
|
||||
{
|
||||
ThrowIfEmpty("Cannot take elements from an empty buffer.");
|
||||
Decrement(ref _end);
|
||||
_buffer[_end] = default(T);
|
||||
--_size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element at the front of the buffer. Decreasing the
|
||||
/// Buffer size by 1.
|
||||
/// </summary>
|
||||
public void PopFront()
|
||||
{
|
||||
ThrowIfEmpty("Cannot take elements from an empty buffer.");
|
||||
_buffer[_start] = default(T);
|
||||
Increment(ref _start);
|
||||
--_size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the buffer contents to an array, according to the logical
|
||||
/// contents of the buffer (i.e. independent of the internal
|
||||
/// order/contents)
|
||||
/// </summary>
|
||||
/// <returns>A new array with a copy of the buffer contents.</returns>
|
||||
public T[] ToArray()
|
||||
{
|
||||
T[] newArray = new T[Size];
|
||||
int newArrayOffset = 0;
|
||||
var segments = new ArraySegment<T>[2] { ArrayOne(), ArrayTwo() };
|
||||
foreach (ArraySegment<T> segment in segments)
|
||||
{
|
||||
Array.Copy(segment.Array, segment.Offset, newArray, newArrayOffset, segment.Count);
|
||||
newArrayOffset += segment.Count;
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
|
||||
#region IEnumerable<T> implementation
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
var segments = new ArraySegment<T>[2] { ArrayOne(), ArrayTwo() };
|
||||
foreach (ArraySegment<T> segment in segments)
|
||||
{
|
||||
for (int i = 0; i < segment.Count; i++)
|
||||
{
|
||||
yield return segment.Array[segment.Offset + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region IEnumerable implementation
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return (IEnumerator)GetEnumerator();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void ThrowIfEmpty(string message = "Cannot access an empty buffer.")
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments the provided index variable by one, wrapping
|
||||
/// around if necessary.
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
private void Increment(ref int index)
|
||||
{
|
||||
if (++index == Capacity)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrements the provided index variable by one, wrapping
|
||||
/// around if necessary.
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
private void Decrement(ref int index)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
index = Capacity;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the index in the argument to an index in <code>_buffer</code>
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The transformed index.
|
||||
/// </returns>
|
||||
/// <param name='index'>
|
||||
/// External index.
|
||||
/// </param>
|
||||
private int InternalIndex(int index)
|
||||
{
|
||||
return _start + (index < (Capacity - _start) ? index : index - Capacity);
|
||||
}
|
||||
|
||||
// doing ArrayOne and ArrayTwo methods returning ArraySegment<T> as seen here:
|
||||
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1957cccdcb0c4ef7d80a34a990065818d
|
||||
// http://www.boost.org/doc/libs/1_37_0/libs/circular_buffer/doc/circular_buffer.html#classboost_1_1circular__buffer_1f5081a54afbc2dfc1a7fb20329df7d5b
|
||||
// should help a lot with the code.
|
||||
|
||||
#region Array items easy access.
|
||||
// The array is composed by at most two non-contiguous segments,
|
||||
// the next two methods allow easy access to those.
|
||||
|
||||
private ArraySegment<T> ArrayOne()
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
return new ArraySegment<T>(new T[0]);
|
||||
}
|
||||
else if (_start < _end)
|
||||
{
|
||||
return new ArraySegment<T>(_buffer, _start, _end - _start);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ArraySegment<T>(_buffer, _start, _buffer.Length - _start);
|
||||
}
|
||||
}
|
||||
|
||||
private ArraySegment<T> ArrayTwo()
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
return new ArraySegment<T>(new T[0]);
|
||||
}
|
||||
else if (_start < _end)
|
||||
{
|
||||
return new ArraySegment<T>(_buffer, _end, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ArraySegment<T>(_buffer, 0, _end);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8a84d703a48460ba3238b4e7c981fd8
|
||||
timeCreated: 1598309908
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.HurricaneVR.Framework.Shared.Utilities
|
||||
{
|
||||
public static class CoroutineExtensions
|
||||
{
|
||||
private static readonly WaitForFixedUpdate _wffu = new WaitForFixedUpdate();
|
||||
|
||||
public static Coroutine ExecuteNextUpdate(this MonoBehaviour behaviour, Action routine)
|
||||
{
|
||||
return behaviour.StartCoroutine(ExecuteNextUpdate(routine));
|
||||
}
|
||||
|
||||
public static Coroutine ExecuteAfterSeconds(this MonoBehaviour behaviour, Action routine, float seconds)
|
||||
{
|
||||
return behaviour.StartCoroutine(ExecuteAfterSeconds(routine, seconds));
|
||||
}
|
||||
|
||||
public static Coroutine ExecuteAfterSecondsUnscaled(this MonoBehaviour behaviour, Action routine, float seconds)
|
||||
{
|
||||
return behaviour.StartCoroutine(ExecuteAfterSecondsUnscaled(routine, seconds));
|
||||
}
|
||||
|
||||
public static Coroutine ExecuteAfterFixedUpdate(this MonoBehaviour behaviour, Action routine)
|
||||
{
|
||||
return behaviour.StartCoroutine(ExecuteAfterFixedUpdate(routine));
|
||||
}
|
||||
|
||||
public static Coroutine ExecuteAfterFixedUpdates(this MonoBehaviour behaviour, Action routine, int frames)
|
||||
{
|
||||
return behaviour.StartCoroutine(ExecuteAfterFixedUpdates(routine, frames));
|
||||
}
|
||||
|
||||
private static IEnumerator ExecuteAfterSeconds(Action action, float seconds)
|
||||
{
|
||||
yield return new WaitForSeconds(seconds);
|
||||
action();
|
||||
}
|
||||
|
||||
private static IEnumerator ExecuteAfterSecondsUnscaled(Action action, float seconds)
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(seconds);
|
||||
action();
|
||||
}
|
||||
|
||||
private static IEnumerator ExecuteNextUpdate(Action action)
|
||||
{
|
||||
yield return null;
|
||||
|
||||
action();
|
||||
}
|
||||
|
||||
private static IEnumerator ExecuteAfterFixedUpdate(Action action)
|
||||
{
|
||||
yield return _wffu;
|
||||
action();
|
||||
}
|
||||
|
||||
private static IEnumerator ExecuteAfterFixedUpdates(Action action, int frames)
|
||||
{
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
yield return new WaitForFixedUpdate();
|
||||
}
|
||||
|
||||
action();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bd806c5a56644919d2f1d17e7c6b02e
|
||||
timeCreated: 1601773369
|
||||
Reference in New Issue
Block a user