Replace UltimateXR with HurricaneVR

This commit is contained in:
2024-08-08 17:01:07 +02:00
parent e8658374d6
commit fb21dbbb73
5932 changed files with 358362 additions and 2174150 deletions

View 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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c59d950586f74df5b9d25caf78874ce0
timeCreated: 1596308197

View 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
};
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c24b7f0e251c41ae877baeb4f1d3fefe
timeCreated: 1596293520

View 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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b171784bbbe1f24784deef229788af8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2242baed513f2a04793dd67910504d3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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,
};
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cb2759c823f44d27b9a55f99eabacb8f
timeCreated: 1600315200

View File

@@ -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
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6162819cbdc6b694c8f056290257a292
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8f1803bf40c60f54c9740b231b61e8b9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e8a84d703a48460ba3238b4e7c981fd8
timeCreated: 1598309908

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2bd806c5a56644919d2f1d17e7c6b02e
timeCreated: 1601773369