From dbd7a1c9cbb66444e27dec7b7c99c58d00fbc828 Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sat, 11 Nov 2023 16:20:43 -0600 Subject: [PATCH 01/13] added haptic feedback for openVR and oculusOVR apis --- .../OculusOVR/OculusOVR.cpp | 17 ++++++++ .../OculusOVR/OculusOVR.cs | 7 ++++ .../OculusOVR/OculusOvrHmd.cs | 7 +++- .../OculusOVR/OculusTouchController.cs | 11 +++++- .../OculusOVR/OculusVibrator.cs | 39 +++++++++++++++++++ .../OculusOVR/OculusVibratorLeft.cs | 25 ++++++++++++ .../OculusOVR/OculusVibratorRight.cs | 24 ++++++++++++ .../OpenVR/OpenVrTouchController.cs | 12 ++++++ .../Stride.VirtualReality/OpenXR/OpenXRHmd.cs | 11 ++++++ .../OpenXR/OpenXrTouchController.cs | 6 +++ .../Stride.VirtualReality/TouchController.cs | 8 ++++ .../WindowsMixedRealityTouchController.cs | 5 +++ 12 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 sources/engine/Stride.VirtualReality/OculusOVR/OculusVibrator.cs create mode 100644 sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorLeft.cs create mode 100644 sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorRight.cs diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cpp b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cpp index 0eeab8f97e..9d7d040057 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cpp +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cpp @@ -463,6 +463,15 @@ extern "C" { { ovr_GetAudioDeviceOutGuidStr(deviceString); } + + DLL_EXPORT_API void xnOvrSetLeftVibration(xnOvrSession* session, float frequency, float amplitude) + { + ovr_SetControllerVibration(session->Session, ovrControllerType_LTouch, frequency, amplitude); + } + DLL_EXPORT_API void xnOvrSetRightVibration(xnOvrSession* session, float frequency, float amplitude) + { + ovr_SetControllerVibration(session->Session, ovrControllerType_RTouch, frequency, amplitude); + } } #else @@ -587,6 +596,14 @@ extern "C" { DLL_EXPORT_API void xnOvrSetQuadLayerParams(void* layer, float* position, float* orientation, float* size, npBool headLocked) { } + + DLL_EXPORT_API void xnOvrSetLeftVibration(void* session, float frequency, float amplitude) + { + } + DLL_EXPORT_API void xnOvrSetRightVibration(void* session, float frequency, float amplitude) + { + } + } #endif diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs index eb91746092..59ef985050 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs @@ -224,5 +224,12 @@ public static string GetAudioDeviceFullName() GetAudioDeviceID(deviceName); return $"\\\\?\\SWD#MMDEVAPI#{deviceName}#{{e6327cad-dcec-4949-ae8a-991e976a79d2}}"; //this should not change the guid is related to audio device type } + + [SuppressUnmanagedCodeSecurity] + [DllImport(NativeInvoke.Library, EntryPoint = "xnOvrSetLeftVibration", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetLeftVibration(IntPtr session, float frequency, float amplitude); + [SuppressUnmanagedCodeSecurity] + [DllImport(NativeInvoke.Library, EntryPoint = "xnOvrSetRightVibration", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetRightVibration(IntPtr session, float frequency, float amplitude); } } diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOvrHmd.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOvrHmd.cs index 281c84b485..82a0b3ac6b 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOvrHmd.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOvrHmd.cs @@ -9,6 +9,7 @@ using Stride.Core.Mathematics; using Stride.Games; using Stride.Graphics; +using Stride.VirtualReality.OculusOVR; using CommandList = Stride.Graphics.CommandList; namespace Stride.VirtualReality @@ -83,8 +84,10 @@ public override void Enable(GraphicsDevice device, GraphicsDeviceManager graphic ActualRenderFrameSize = new Size2(textures[0].Width, textures[0].Height); - leftHandController = new OculusTouchController(TouchControllerHand.Left); - rightHandController = new OculusTouchController(TouchControllerHand.Right); + var leftVibrator = new OculusVibratorLeft(ovrSession); + leftHandController = new OculusTouchController(TouchControllerHand.Left, leftVibrator); + var rightVibrator = new OculusVibratorRight(ovrSession); + rightHandController = new OculusTouchController(TouchControllerHand.Right, rightVibrator); } private OculusOvr.PosesProperties currentPoses; diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs index 3d263e2338..358fd224ba 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs @@ -2,6 +2,8 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; using Stride.Core.Mathematics; +using System.Threading.Tasks; +using Stride.VirtualReality.OculusOVR; namespace Stride.VirtualReality { @@ -23,6 +25,7 @@ internal class OculusTouchController : TouchController private uint previousButtonsState; private Vector2 currentThumbstick; private const float TriggerAndGripDeadzone = 0.00001f; + private readonly OculusVibrator vibrator; public override Vector3 Position => currentPos; @@ -138,9 +141,10 @@ public override bool ThumbResting public override Vector2 ThumbstickAxis => currentThumbstick; - public OculusTouchController(TouchControllerHand hand) + public OculusTouchController(TouchControllerHand hand, OculusVibrator vibrator) { this.hand = hand; + this.vibrator = vibrator; currentState = DeviceState.Invalid; } @@ -399,5 +403,10 @@ public override bool IsTouchReleased(TouchControllerButton button) return false; } } + + public override async Task Vibrate(int duration) + { + await vibrator.Vibrate(duration); + } } } diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibrator.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibrator.cs new file mode 100644 index 0000000000..ac15d869d5 --- /dev/null +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibrator.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Silk.NET.OpenXR; + +namespace Stride.VirtualReality.OculusOVR +{ + internal abstract class OculusVibrator + { + protected int concurrentCallCount; + /// + /// Vibrate for a number of milliseconds + /// + /// The number of milliseconds to vibrate for + /// + public async Task Vibrate(int duration) + { + concurrentCallCount++; + while(duration > 2000) + { + SetOvrVibration(true); + duration -= 2000; + await Task.Delay(2000); + } + SetOvrVibration(true); + await Task.Delay(duration); + concurrentCallCount--; + if(concurrentCallCount == 0) + SetOvrVibration(false); + } + /// + /// Enable or disable vibration. Should be called periodically, vibration automatically disables after 2.5 seconds. + /// + /// true to start vibration, false to stop vibration + protected abstract void SetOvrVibration(bool vibrationEnabled); + } +} diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorLeft.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorLeft.cs new file mode 100644 index 0000000000..a7d8c01193 --- /dev/null +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorLeft.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Stride.VirtualReality.OculusOVR +{ + internal class OculusVibratorLeft : OculusVibrator + { + protected readonly IntPtr ovrSession; + + public OculusVibratorLeft(IntPtr ovrSession) + { + this.ovrSession = ovrSession; + } + protected override void SetOvrVibration(bool vibrationEnabled) + { + if (vibrationEnabled) + OculusOvr.SetLeftVibration(ovrSession, 1, 1); + else + OculusOvr.SetLeftVibration(ovrSession, 0, 0); + } + } +} diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorRight.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorRight.cs new file mode 100644 index 0000000000..4521479702 --- /dev/null +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorRight.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Stride.VirtualReality.OculusOVR +{ + internal class OculusVibratorRight : OculusVibrator + { + protected readonly IntPtr ovrSession; + public OculusVibratorRight(IntPtr ovrSession) + { + this.ovrSession = ovrSession; + } + protected override void SetOvrVibration(bool vibrationEnabled) + { + if (vibrationEnabled) + OculusOvr.SetRightVibration(ovrSession, 1, 1); + else + OculusOvr.SetRightVibration(ovrSession, 0, 0); + } + } +} diff --git a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs index 978018bbb6..f60ad16118 100644 --- a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs @@ -5,6 +5,7 @@ using System; using Stride.Core.Mathematics; using Stride.Games; +using System.Threading.Tasks; namespace Stride.VirtualReality { @@ -123,6 +124,17 @@ public override bool IsTouchReleased(TouchControllerButton button) return controller?.GetTouchUp(ToOpenVrButton(button)) ?? false; } + public override async Task Vibrate(int duration) + { + while(duration > 50) + { + Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex+1, 0, 1000 * 50); + duration -= 50; + await Task.Delay(50); + } + Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex+1, 0, (ushort)duration); + } + public override Vector3 Position => currentPos; public override Quaternion Rotation => currentRot; diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs index d345d6b8c9..0537da7d8a 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using Silk.NET.Core.Native; using System.Runtime.CompilerServices; +using System.Threading.Tasks; namespace Stride.VirtualReality { @@ -1050,6 +1051,16 @@ public override unsafe void Update(GameTime gameTime) leftHand.Update(gameTime); rightHand.Update(gameTime); } + /*public async Task VibrateLeft(int duration) + { + HapticActionInfo haptic_action_info = new HapticActionInfo() + { + Type = StructureType.HapticActionInfo, + Next = null, + + }; + Xr.ApplyHapticFeedback(globalSession, ) + }*/ public override void Dispose() { diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs index e72c5486d0..f0fac0da9e 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs @@ -4,6 +4,7 @@ using Silk.NET.OpenXR; using Stride.Core.Mathematics; using Stride.Games; +using System.Threading.Tasks; namespace Stride.VirtualReality { @@ -166,5 +167,10 @@ public override unsafe void Update(GameTime time) currentRot.W = handLocation.Pose.Orientation.W; } } + + public override async Task Vibrate(int duration) + { + throw new NotImplementedException(); + } } } diff --git a/sources/engine/Stride.VirtualReality/TouchController.cs b/sources/engine/Stride.VirtualReality/TouchController.cs index 649221887c..de0160a29b 100644 --- a/sources/engine/Stride.VirtualReality/TouchController.cs +++ b/sources/engine/Stride.VirtualReality/TouchController.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Threading.Tasks; using Stride.Core.Mathematics; using Stride.Games; @@ -80,6 +81,13 @@ public virtual void Update(GameTime time) /// public abstract bool IsTouchReleased(TouchControllerButton button); + /// + /// Vibrate the controller for a fixed duration. + /// + /// Vibration duration, in milliseconds + /// + public abstract Task Vibrate(int duration); + public virtual void Dispose() { } diff --git a/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs b/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs index 2eccdb45fc..bb35a2e075 100644 --- a/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs +++ b/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs @@ -156,6 +156,11 @@ private void SetSpatialInteractionSourceLocation(SpatialInteractionSourceLocatio currentLinearVelocity = location.Velocity?.ToVector3() ?? currentLinearVelocity; currentAngularVelocity = location.AngularVelocity?.ToVector3() ?? currentAngularVelocity; } + + public override async Task Vibrate(int duration) + { + throw new NotImplementedException(); + } } } From 643741977caf946b472a70853f88ce2c6f951714 Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sun, 12 Nov 2023 12:21:26 -0600 Subject: [PATCH 02/13] Wrote OpenXR haptics that don't work --- .../OpenVR/OpenVrTouchController.cs | 2 +- .../Stride.VirtualReality/OpenXR/OpenXRHmd.cs | 12 ++------ .../OpenXR/OpenXRInput.cs | 4 +++ .../OpenXR/OpenXrTouchController.cs | 28 ++++++++++++++++++- .../WindowsMixedRealityTouchController.cs | 1 + 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs index f60ad16118..0319b31153 100644 --- a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs @@ -132,7 +132,7 @@ public override async Task Vibrate(int duration) duration -= 50; await Task.Delay(50); } - Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex+1, 0, (ushort)duration); + Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex+1, 0, (ushort)(1000 * duration)); } public override Vector3 Position => currentPos; diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs index 0537da7d8a..2ab6bc437d 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs @@ -10,6 +10,8 @@ using Silk.NET.Core.Native; using System.Runtime.CompilerServices; using System.Threading.Tasks; +using static Stride.VirtualReality.OpenXRInput; +using System.Text; namespace Stride.VirtualReality { @@ -1051,16 +1053,6 @@ public override unsafe void Update(GameTime gameTime) leftHand.Update(gameTime); rightHand.Update(gameTime); } - /*public async Task VibrateLeft(int duration) - { - HapticActionInfo haptic_action_info = new HapticActionInfo() - { - Type = StructureType.HapticActionInfo, - Next = null, - - }; - Xr.ApplyHapticFeedback(globalSession, ) - }*/ public override void Dispose() { diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs index c5b1041ce4..007ea69e2a 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs @@ -139,6 +139,10 @@ public static Silk.NET.OpenXR.Action GetAction(TouchControllerHand hand, TouchCo throw new ArgumentException("Don't know button: " + button); } } + public static Silk.NET.OpenXR.Action GetHapticAction(TouchControllerHand hand) + { + return MappedActions[(int)hand, (int)HAND_PATHS.HapticOut]; + } public static bool GetActionBool(TouchControllerHand hand, TouchControllerButton button, out bool wasChangedSinceLast, bool fallback = false) { diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs index f0fac0da9e..a44e2a6d58 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs @@ -170,7 +170,33 @@ public override unsafe void Update(GameTime time) public override async Task Vibrate(int duration) { - throw new NotImplementedException(); + SendVibrate(duration); + await Task.Delay(duration); + } + //TODO: This does not make controllers vibrate + private unsafe void SendVibrate(int duration) + { + var action = OpenXRInput.GetHapticAction(myHand); + HapticActionInfo hapticActionInfo = new HapticActionInfo() + { + Type = StructureType.HapticActionInfo, + Next = null, + Action = action, + }; + HapticVibration vibration = new HapticVibration() + { + Type = StructureType.HapticVibration, + Next = null, + Duration = duration * 1000, + Amplitude = 1, + Frequency = 10 + }; + HapticBaseHeader hapticBaseHeader = new HapticBaseHeader() + { + Type = StructureType.HapticVibration, + Next = &vibration + }; + baseHMD.Xr.ApplyHapticFeedback(baseHMD.globalSession, &hapticActionInfo, &vibration); } } } diff --git a/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs b/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs index bb35a2e075..4ca38a842c 100644 --- a/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs +++ b/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs @@ -157,6 +157,7 @@ private void SetSpatialInteractionSourceLocation(SpatialInteractionSourceLocatio currentAngularVelocity = location.AngularVelocity?.ToVector3() ?? currentAngularVelocity; } + //TODO: implement this public override async Task Vibrate(int duration) { throw new NotImplementedException(); From 4953ea706ecfc6dea8509a2f35316b3e9982b733 Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sun, 12 Nov 2023 12:23:31 -0600 Subject: [PATCH 03/13] Removed unused imports from OpenXRHmd --- sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs index 2ab6bc437d..d345d6b8c9 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs @@ -9,9 +9,6 @@ using System.Diagnostics; using Silk.NET.Core.Native; using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using static Stride.VirtualReality.OpenXRInput; -using System.Text; namespace Stride.VirtualReality { From 3468fcb32108bf6525dddf03a511592bb30bad85 Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sat, 2 Mar 2024 12:15:18 -0600 Subject: [PATCH 04/13] Made openXR runtime throw not implemented exception when vibration called, and cleaned up the oculus runtime vibration code --- .../OculusOVR/OculusOVR.cs | 11 +++++- .../OculusOVR/OculusOvrHmd.cs | 9 ++--- .../OculusOVR/OculusTouchController.cs | 35 +++++++++++++---- .../OculusOVR/OculusVibrator.cs | 39 ------------------- .../OculusOVR/OculusVibratorLeft.cs | 25 ------------ .../OculusOVR/OculusVibratorRight.cs | 24 ------------ .../OpenVR/OpenVrTouchController.cs | 17 ++++---- .../OpenXR/OpenXrTouchController.cs | 35 ++--------------- .../Stride.VirtualReality/TouchController.cs | 6 +-- 9 files changed, 55 insertions(+), 146 deletions(-) delete mode 100644 sources/engine/Stride.VirtualReality/OculusOVR/OculusVibrator.cs delete mode 100644 sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorLeft.cs delete mode 100644 sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorRight.cs diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs index 59ef985050..9c5b24fac2 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs @@ -227,9 +227,16 @@ public static string GetAudioDeviceFullName() [SuppressUnmanagedCodeSecurity] [DllImport(NativeInvoke.Library, EntryPoint = "xnOvrSetLeftVibration", CallingConvention = CallingConvention.Cdecl)] - public static extern void SetLeftVibration(IntPtr session, float frequency, float amplitude); + static extern void SetLeftVibration(IntPtr session, float frequency, float amplitude); [SuppressUnmanagedCodeSecurity] [DllImport(NativeInvoke.Library, EntryPoint = "xnOvrSetRightVibration", CallingConvention = CallingConvention.Cdecl)] - public static extern void SetRightVibration(IntPtr session, float frequency, float amplitude); + static extern void SetRightVibration(IntPtr session, float frequency, float amplitude); + public static void SetVibration(IntPtr session, TouchControllerHand hand, float frequency, float amplitude) + { + if (hand == TouchControllerHand.Left) + SetLeftVibration(session, frequency, amplitude); + else + SetRightVibration(session, frequency, amplitude); + } } } diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOvrHmd.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOvrHmd.cs index 82a0b3ac6b..a9f12c4518 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusOvrHmd.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusOvrHmd.cs @@ -9,7 +9,6 @@ using Stride.Core.Mathematics; using Stride.Games; using Stride.Graphics; -using Stride.VirtualReality.OculusOVR; using CommandList = Stride.Graphics.CommandList; namespace Stride.VirtualReality @@ -84,10 +83,8 @@ public override void Enable(GraphicsDevice device, GraphicsDeviceManager graphic ActualRenderFrameSize = new Size2(textures[0].Width, textures[0].Height); - var leftVibrator = new OculusVibratorLeft(ovrSession); - leftHandController = new OculusTouchController(TouchControllerHand.Left, leftVibrator); - var rightVibrator = new OculusVibratorRight(ovrSession); - rightHandController = new OculusTouchController(TouchControllerHand.Right, rightVibrator); + leftHandController = new OculusTouchController(TouchControllerHand.Left, ovrSession); + rightHandController = new OculusTouchController(TouchControllerHand.Right, ovrSession); } private OculusOvr.PosesProperties currentPoses; @@ -165,7 +162,7 @@ public override bool CanInitialize return initDone; } } - + public override void Recenter() { OculusOvr.Recenter(ovrSession); diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs index 358fd224ba..d2bae5b3a3 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs @@ -1,9 +1,8 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; -using Stride.Core.Mathematics; using System.Threading.Tasks; -using Stride.VirtualReality.OculusOVR; +using Stride.Core.Mathematics; namespace Stride.VirtualReality { @@ -25,7 +24,7 @@ internal class OculusTouchController : TouchController private uint previousButtonsState; private Vector2 currentThumbstick; private const float TriggerAndGripDeadzone = 0.00001f; - private readonly OculusVibrator vibrator; + IntPtr OvrSession { get; } public override Vector3 Position => currentPos; @@ -141,10 +140,10 @@ public override bool ThumbResting public override Vector2 ThumbstickAxis => currentThumbstick; - public OculusTouchController(TouchControllerHand hand, OculusVibrator vibrator) + public OculusTouchController(TouchControllerHand hand, IntPtr OvrSession) { this.hand = hand; - this.vibrator = vibrator; + this.OvrSession = OvrSession; currentState = DeviceState.Invalid; } @@ -404,9 +403,31 @@ public override bool IsTouchReleased(TouchControllerButton button) } } - public override async Task Vibrate(int duration) + protected int ConcurrentVibratingCallCount { get; set; } + /// + /// Vibrate for a number of milliseconds + /// + /// The number of milliseconds to vibrate for + /// + public override async Task Vibrate(int durationMs) { - await vibrator.Vibrate(duration); + void SetOvrVibration(bool enable) + { + float frequency = enable ? 1 : 0; + OculusOvr.SetVibration(OvrSession, hand, frequency, 1); + } + ConcurrentVibratingCallCount++; + while (durationMs > 2000) + { + SetOvrVibration(true); + durationMs -= 2000; + await Task.Delay(2000); + } + SetOvrVibration(true); + await Task.Delay(durationMs); + ConcurrentVibratingCallCount--; + if (ConcurrentVibratingCallCount == 0) + SetOvrVibration(false); } } } diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibrator.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibrator.cs deleted file mode 100644 index ac15d869d5..0000000000 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibrator.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Silk.NET.OpenXR; - -namespace Stride.VirtualReality.OculusOVR -{ - internal abstract class OculusVibrator - { - protected int concurrentCallCount; - /// - /// Vibrate for a number of milliseconds - /// - /// The number of milliseconds to vibrate for - /// - public async Task Vibrate(int duration) - { - concurrentCallCount++; - while(duration > 2000) - { - SetOvrVibration(true); - duration -= 2000; - await Task.Delay(2000); - } - SetOvrVibration(true); - await Task.Delay(duration); - concurrentCallCount--; - if(concurrentCallCount == 0) - SetOvrVibration(false); - } - /// - /// Enable or disable vibration. Should be called periodically, vibration automatically disables after 2.5 seconds. - /// - /// true to start vibration, false to stop vibration - protected abstract void SetOvrVibration(bool vibrationEnabled); - } -} diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorLeft.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorLeft.cs deleted file mode 100644 index a7d8c01193..0000000000 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorLeft.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Stride.VirtualReality.OculusOVR -{ - internal class OculusVibratorLeft : OculusVibrator - { - protected readonly IntPtr ovrSession; - - public OculusVibratorLeft(IntPtr ovrSession) - { - this.ovrSession = ovrSession; - } - protected override void SetOvrVibration(bool vibrationEnabled) - { - if (vibrationEnabled) - OculusOvr.SetLeftVibration(ovrSession, 1, 1); - else - OculusOvr.SetLeftVibration(ovrSession, 0, 0); - } - } -} diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorRight.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorRight.cs deleted file mode 100644 index 4521479702..0000000000 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusVibratorRight.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Stride.VirtualReality.OculusOVR -{ - internal class OculusVibratorRight : OculusVibrator - { - protected readonly IntPtr ovrSession; - public OculusVibratorRight(IntPtr ovrSession) - { - this.ovrSession = ovrSession; - } - protected override void SetOvrVibration(bool vibrationEnabled) - { - if (vibrationEnabled) - OculusOvr.SetRightVibration(ovrSession, 1, 1); - else - OculusOvr.SetRightVibration(ovrSession, 0, 0); - } - } -} diff --git a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs index 0319b31153..30bf845e17 100644 --- a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs @@ -2,10 +2,9 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. #if STRIDE_GRAPHICS_API_DIRECT3D11 -using System; +using System.Threading.Tasks; using Stride.Core.Mathematics; using Stride.Games; -using System.Threading.Tasks; namespace Stride.VirtualReality { @@ -82,7 +81,7 @@ private OpenVR.Controller.ButtonId ToOpenVrButton(TouchControllerButton button) switch (button) { case TouchControllerButton.Thumbstick: - return OpenVR.Controller.ButtonId.ButtonSteamVrTouchpad; + return OpenVR.Controller.ButtonId.ButtonSteamVrTouchpad; case TouchControllerButton.Trigger: return OpenVR.Controller.ButtonId.ButtonSteamVrTrigger; case TouchControllerButton.Grip: @@ -124,15 +123,15 @@ public override bool IsTouchReleased(TouchControllerButton button) return controller?.GetTouchUp(ToOpenVrButton(button)) ?? false; } - public override async Task Vibrate(int duration) + public override async Task Vibrate(int durationMs) { - while(duration > 50) + while (durationMs > 45) { - Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex+1, 0, 1000 * 50); - duration -= 50; - await Task.Delay(50); + Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, 1000 * 50); + durationMs -= 45; + await Task.Delay(45); } - Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex+1, 0, (ushort)(1000 * duration)); + Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, (ushort)(1000 * durationMs)); } public override Vector3 Position => currentPos; diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs index a99ead263d..f158f75bf3 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs @@ -1,12 +1,10 @@ #nullable enable using System; -using System.Collections.Generic; -using System.Text; +using System.Threading.Tasks; using Silk.NET.OpenXR; using Stride.Core.Mathematics; using Stride.Games; -using System.Threading.Tasks; namespace Stride.VirtualReality { @@ -171,36 +169,11 @@ public override unsafe void Update(GameTime time) currentRot.W = handLocation.Pose.Orientation.W; } } - + + //TODO: Make controller vibrate for duration public override async Task Vibrate(int duration) { - SendVibrate(duration); - await Task.Delay(duration); - } - //TODO: This does not make controllers vibrate - private unsafe void SendVibrate(int duration) - { - var action = OpenXRInput.GetHapticAction(myHand); - HapticActionInfo hapticActionInfo = new HapticActionInfo() - { - Type = StructureType.HapticActionInfo, - Next = null, - Action = action, - }; - HapticVibration vibration = new HapticVibration() - { - Type = StructureType.HapticVibration, - Next = null, - Duration = duration * 1000, - Amplitude = 1, - Frequency = 10 - }; - HapticBaseHeader hapticBaseHeader = new HapticBaseHeader() - { - Type = StructureType.HapticVibration, - Next = &vibration - }; - baseHMD.Xr.ApplyHapticFeedback(baseHMD.globalSession, &hapticActionInfo, &vibration); + throw new NotImplementedException("Controller vibration is not implemented for OpenXr runtime."); } } } diff --git a/sources/engine/Stride.VirtualReality/TouchController.cs b/sources/engine/Stride.VirtualReality/TouchController.cs index de0160a29b..b037b9383d 100644 --- a/sources/engine/Stride.VirtualReality/TouchController.cs +++ b/sources/engine/Stride.VirtualReality/TouchController.cs @@ -20,7 +20,7 @@ public abstract class TouchController : IDisposable public abstract DeviceState State { get; } public virtual void Update(GameTime time) - { + { } public abstract float Trigger { get; } @@ -86,10 +86,10 @@ public virtual void Update(GameTime time) /// /// Vibration duration, in milliseconds /// - public abstract Task Vibrate(int duration); + public abstract Task Vibrate(int durationMs); public virtual void Dispose() - { + { } } } From ffe8566d8c21562e89def4ea0320bace90fb568a Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sat, 2 Mar 2024 12:17:11 -0600 Subject: [PATCH 05/13] Moved a property declaration to top of file --- .../Stride.VirtualReality/OculusOVR/OculusTouchController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs index d2bae5b3a3..fc74b2d7b3 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs @@ -25,6 +25,8 @@ internal class OculusTouchController : TouchController private Vector2 currentThumbstick; private const float TriggerAndGripDeadzone = 0.00001f; IntPtr OvrSession { get; } + //Number of calls to vibrate that are currently executing + protected int ConcurrentVibratingCallCount { get; set; } public override Vector3 Position => currentPos; @@ -403,7 +405,6 @@ public override bool IsTouchReleased(TouchControllerButton button) } } - protected int ConcurrentVibratingCallCount { get; set; } /// /// Vibrate for a number of milliseconds /// From cfbe6743be7f90f0bca43dacdba6ad38b6d0b1ac Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sun, 3 Mar 2024 20:05:45 -0600 Subject: [PATCH 06/13] Made vibration work with start/stop vibration methods rather than just vibration for duration method --- .../OculusOVR/OculusTouchController.cs | 34 +++----------- .../OpenVR/OpenVrTouchController.cs | 17 ++++--- .../OpenXR/OpenXrTouchController.cs | 10 +++-- .../Stride.VirtualReality/TouchController.cs | 45 +++++++++++++++++-- .../WindowsMixedRealityTouchController.cs | 13 ++++-- 5 files changed, 73 insertions(+), 46 deletions(-) diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs index fc74b2d7b3..d76a316e0a 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs @@ -25,8 +25,6 @@ internal class OculusTouchController : TouchController private Vector2 currentThumbstick; private const float TriggerAndGripDeadzone = 0.00001f; IntPtr OvrSession { get; } - //Number of calls to vibrate that are currently executing - protected int ConcurrentVibratingCallCount { get; set; } public override Vector3 Position => currentPos; @@ -141,7 +139,6 @@ public override bool ThumbResting public override Vector2 ThumbAxis => currentThumbstick; public override Vector2 ThumbstickAxis => currentThumbstick; - public OculusTouchController(TouchControllerHand hand, IntPtr OvrSession) { this.hand = hand; @@ -404,31 +401,14 @@ public override bool IsTouchReleased(TouchControllerButton button) return false; } } - - /// - /// Vibrate for a number of milliseconds - /// - /// The number of milliseconds to vibrate for - /// - public override async Task Vibrate(int durationMs) + protected override async Task EnableVibration() { - void SetOvrVibration(bool enable) - { - float frequency = enable ? 1 : 0; - OculusOvr.SetVibration(OvrSession, hand, frequency, 1); - } - ConcurrentVibratingCallCount++; - while (durationMs > 2000) - { - SetOvrVibration(true); - durationMs -= 2000; - await Task.Delay(2000); - } - SetOvrVibration(true); - await Task.Delay(durationMs); - ConcurrentVibratingCallCount--; - if (ConcurrentVibratingCallCount == 0) - SetOvrVibration(false); + OculusOvr.SetVibration(OvrSession, hand, 1, 1); + await Task.Delay(2400); + } + protected override async Task DisableVibration() + { + OculusOvr.SetVibration(OvrSession, hand, 0, 1); } } } diff --git a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs index 30bf845e17..e685b1fb28 100644 --- a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs @@ -122,16 +122,15 @@ public override bool IsTouchReleased(TouchControllerButton button) { return controller?.GetTouchUp(ToOpenVrButton(button)) ?? false; } - - public override async Task Vibrate(int durationMs) + protected override async Task EnableVibration() { - while (durationMs > 45) - { - Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, 1000 * 50); - durationMs -= 45; - await Task.Delay(45); - } - Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, (ushort)(1000 * durationMs)); + Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, 1000 * 5); + await Task.Delay(5); + } + protected override async Task DisableVibration() + { + //Vibration cannot be stopped, but does not continue for more than 5 milliseconds. + await Task.Delay(5); } public override Vector3 Position => currentPos; diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs index f158f75bf3..43068fab10 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs @@ -169,9 +169,13 @@ public override unsafe void Update(GameTime time) currentRot.W = handLocation.Pose.Orientation.W; } } - - //TODO: Make controller vibrate for duration - public override async Task Vibrate(int duration) + //TODO: Make controller vibrate + protected override async Task EnableVibration() + { + throw new NotImplementedException("Controller vibration is not implemented for OpenXr runtime."); + } + //TODO: Make controller stop vibrating + protected override async Task DisableVibration() { throw new NotImplementedException("Controller vibration is not implemented for OpenXr runtime."); } diff --git a/sources/engine/Stride.VirtualReality/TouchController.cs b/sources/engine/Stride.VirtualReality/TouchController.cs index b037b9383d..1876347f0a 100644 --- a/sources/engine/Stride.VirtualReality/TouchController.cs +++ b/sources/engine/Stride.VirtualReality/TouchController.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Threading; using System.Threading.Tasks; using Stride.Core.Mathematics; using Stride.Games; @@ -38,6 +39,8 @@ public virtual void Update(GameTime time) public abstract Vector2 ThumbAxis { get; } public abstract Vector2 ThumbstickAxis { get; } + //Number of concurrent calls to Vibrate(duration) so that the longest will complete. + int _vibrationCounter; /// /// Returns true if in this frame the button switched to pressed state @@ -82,12 +85,48 @@ public virtual void Update(GameTime time) public abstract bool IsTouchReleased(TouchControllerButton button); /// - /// Vibrate the controller for a fixed duration. + /// Vibrate the controller for a fixed duration. Stops vibration at end of duration if no other Vibrate calls are currently running. /// - /// Vibration duration, in milliseconds + /// Vibration duration, in milliseconds /// - public abstract Task Vibrate(int durationMs); + public async Task Vibrate(int durationMs) + { + Vibrate(); + await Task.Delay(durationMs); + Interlocked.Decrement(ref _vibrationCounter); + if (_vibrationCounter == 0) + await StopVibration(); + } + /// + /// Vibrate the controller until StopVibration() is called + /// + /// + public async void Vibrate() + { + Interlocked.Increment(ref _vibrationCounter); + while (_vibrationCounter > 0) + await EnableVibration(); + } + /// + /// Stop the controller's vibration + /// + /// A Task which completes when controller vibration is stopped + public async Task StopVibration() + { + _vibrationCounter = 0; + await DisableVibration(); + } + /// + /// Enable controller vibration for a period of time + /// + /// A Task which completes when the vibration times out + protected abstract Task EnableVibration(); + /// + /// Disable controller vibration + /// + /// A Task which completes when the controller vibration has stopped. + protected abstract Task DisableVibration(); public virtual void Dispose() { } diff --git a/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs b/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs index 4ca38a842c..8175a1f3e1 100644 --- a/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs +++ b/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs @@ -156,11 +156,16 @@ private void SetSpatialInteractionSourceLocation(SpatialInteractionSourceLocatio currentLinearVelocity = location.Velocity?.ToVector3() ?? currentLinearVelocity; currentAngularVelocity = location.AngularVelocity?.ToVector3() ?? currentAngularVelocity; } - - //TODO: implement this - public override async Task Vibrate(int duration) + + //TODO: Make controller vibrate + protected override async Task EnableVibration() + { + throw new NotImplementedException("Controller vibration is not implemented for windows mixed reality runtime."); + } + //TODO: Make controller stop vibrating + protected override async Task DisableVibration() { - throw new NotImplementedException(); + throw new NotImplementedException("Controller vibration is not implemented for windows mixed reality runtime."); } } } From ca37404a22b83ae10d7fe912cad9f4f750e5e945 Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sun, 3 Mar 2024 20:33:45 -0600 Subject: [PATCH 07/13] Fixed stop vibration in oculusTouchController --- .../Stride.VirtualReality/OculusOVR/OculusTouchController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs index fc74b2d7b3..a3d76864d1 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs @@ -415,7 +415,8 @@ public override async Task Vibrate(int durationMs) void SetOvrVibration(bool enable) { float frequency = enable ? 1 : 0; - OculusOvr.SetVibration(OvrSession, hand, frequency, 1); + float amplitude = enable ? 1 : 0; + OculusOvr.SetVibration(OvrSession, hand, frequency, amplitude); } ConcurrentVibratingCallCount++; while (durationMs > 2000) From 38913c0feab92c5e23e2f4bb0a5721ea8c1a324a Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sun, 3 Mar 2024 20:37:35 -0600 Subject: [PATCH 08/13] Fixed Disable vibration for oculus touch controller by setting amplitude to 0 --- .../Stride.VirtualReality/OculusOVR/OculusTouchController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs index d76a316e0a..51d5c8c36b 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs @@ -408,7 +408,7 @@ protected override async Task EnableVibration() } protected override async Task DisableVibration() { - OculusOvr.SetVibration(OvrSession, hand, 0, 1); + OculusOvr.SetVibration(OvrSession, hand, 0, 0); } } } From a8d22a565c4e2df41b2ca119bf747a3aaf9b8e53 Mon Sep 17 00:00:00 2001 From: computersmoke Date: Sun, 3 Mar 2024 20:43:03 -0600 Subject: [PATCH 09/13] Fixed bug where calling StopVibration during Vibrate(duration) execution could give unexpected _vibrationCounter value --- sources/engine/Stride.VirtualReality/TouchController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/engine/Stride.VirtualReality/TouchController.cs b/sources/engine/Stride.VirtualReality/TouchController.cs index 1876347f0a..1aabf65bf1 100644 --- a/sources/engine/Stride.VirtualReality/TouchController.cs +++ b/sources/engine/Stride.VirtualReality/TouchController.cs @@ -94,7 +94,7 @@ public async Task Vibrate(int durationMs) Vibrate(); await Task.Delay(durationMs); Interlocked.Decrement(ref _vibrationCounter); - if (_vibrationCounter == 0) + if (_vibrationCounter <= 0) await StopVibration(); } /// From 36a8d42cb849d73fa1e94c697f47a4d16618070a Mon Sep 17 00:00:00 2001 From: computersmoke Date: Mon, 4 Mar 2024 18:11:45 -0600 Subject: [PATCH 10/13] Added frequency/amplitude parameters and a property to indicate whether they are supported --- .../OculusOVR/OculusTouchController.cs | 37 ++++++++++--------- .../OpenVR/OpenVrTouchController.cs | 13 ++++--- .../OpenXR/OpenXrTouchController.cs | 7 +--- .../Stride.VirtualReality/TouchController.cs | 29 +++++++++++++-- .../WindowsMixedRealityTouchController.cs | 7 ++-- 5 files changed, 59 insertions(+), 34 deletions(-) diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs index fc74b2d7b3..8886406ab9 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs @@ -24,9 +24,10 @@ internal class OculusTouchController : TouchController private uint previousButtonsState; private Vector2 currentThumbstick; private const float TriggerAndGripDeadzone = 0.00001f; - IntPtr OvrSession { get; } + private IntPtr OvrSession { get; } //Number of calls to vibrate that are currently executing - protected int ConcurrentVibratingCallCount { get; set; } + private int vibrationCount; + private readonly object vibrationLock = new(); public override Vector3 Position => currentPos; @@ -141,6 +142,7 @@ public override bool ThumbResting public override Vector2 ThumbAxis => currentThumbstick; public override Vector2 ThumbstickAxis => currentThumbstick; + public override ControllerHaptics HapticsSupport => ControllerHaptics.Full; public OculusTouchController(TouchControllerHand hand, IntPtr OvrSession) { @@ -405,30 +407,31 @@ public override bool IsTouchReleased(TouchControllerButton button) } } - /// - /// Vibrate for a number of milliseconds - /// - /// The number of milliseconds to vibrate for - /// - public override async Task Vibrate(int durationMs) + public override async Task Vibrate(int durationMs, float frequency, float amplitude) { void SetOvrVibration(bool enable) { - float frequency = enable ? 1 : 0; - OculusOvr.SetVibration(OvrSession, hand, frequency, 1); + float freqParam = enable ? frequency : 0; + float ampParam = enable ? amplitude : 0; + OculusOvr.SetVibration(OvrSession, hand, freqParam, ampParam); } - ConcurrentVibratingCallCount++; - while (durationMs > 2000) + lock (vibrationLock) + { + vibrationCount++; + } + while (durationMs > 2400) { SetOvrVibration(true); - durationMs -= 2000; - await Task.Delay(2000); + durationMs -= 2400; + await Task.Delay(2400); } SetOvrVibration(true); await Task.Delay(durationMs); - ConcurrentVibratingCallCount--; - if (ConcurrentVibratingCallCount == 0) - SetOvrVibration(false); + lock (vibrationLock) + { + if (--vibrationCount == 0) + SetOvrVibration(false); + } } } } diff --git a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs index 30bf845e17..0a6af90fda 100644 --- a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs @@ -75,6 +75,7 @@ public override void Update(GameTime gameTime) public override Vector2 ThumbAxis => controller?.GetAxis() ?? Vector2.Zero; public override Vector2 ThumbstickAxis => controller?.GetAxis() ?? Vector2.Zero; + public override ControllerHaptics HapticsSupport => ControllerHaptics.Limited; private OpenVR.Controller.ButtonId ToOpenVrButton(TouchControllerButton button) { @@ -123,13 +124,15 @@ public override bool IsTouchReleased(TouchControllerButton button) return controller?.GetTouchUp(ToOpenVrButton(button)) ?? false; } - public override async Task Vibrate(int durationMs) + public override async Task Vibrate(int durationMs, float frequency, float amplitude) { - while (durationMs > 45) + if (frequency <= 0 || amplitude <= 0) + return; + while (durationMs > 60) { - Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, 1000 * 50); - durationMs -= 45; - await Task.Delay(45); + Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, 1000 * 60); + durationMs -= 60; + await Task.Delay(60); } Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, (ushort)(1000 * durationMs)); } diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs index f158f75bf3..f27531f3a7 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXrTouchController.cs @@ -1,6 +1,5 @@ #nullable enable -using System; using System.Threading.Tasks; using Silk.NET.OpenXR; using Stride.Core.Mathematics; @@ -71,6 +70,7 @@ public OpenXrTouchController(OpenXRHmd hmd, OpenXRInput input, TouchControllerHa public override Vector2 ThumbstickAxis => GetAxis((int)TouchControllerButton.Thumbstick); public override Vector2 ThumbAxis => GetAxis((int)TouchControllerButton.Touchpad); + public override ControllerHaptics HapticsSupport => ControllerHaptics.None; public Vector2 GetAxis(int index) { @@ -171,9 +171,6 @@ public override unsafe void Update(GameTime time) } //TODO: Make controller vibrate for duration - public override async Task Vibrate(int duration) - { - throw new NotImplementedException("Controller vibration is not implemented for OpenXr runtime."); - } + public override async Task Vibrate(int duration, float frequency, float amplitude) { } } } diff --git a/sources/engine/Stride.VirtualReality/TouchController.cs b/sources/engine/Stride.VirtualReality/TouchController.cs index b037b9383d..506c82df48 100644 --- a/sources/engine/Stride.VirtualReality/TouchController.cs +++ b/sources/engine/Stride.VirtualReality/TouchController.cs @@ -39,6 +39,24 @@ public virtual void Update(GameTime time) public abstract Vector2 ThumbstickAxis { get; } + public enum ControllerHaptics + { + None, + Limited, + LimitedFrequency, + LimitedAmplitude, + Full + } + /// + /// Degree to which this touch controller type supports haptics. + /// None: no haptics support, controller does not vibrate. + /// Limited: cannot vibrate at any specific frequency or amplitude. Does not vibrate if corresponding parameter is not positive. + /// Full: vibrate method respects both frequency and vibration parameters + /// + /// + /// + public abstract ControllerHaptics HapticsSupport { get; } + /// /// Returns true if in this frame the button switched to pressed state /// @@ -82,11 +100,16 @@ public virtual void Update(GameTime time) public abstract bool IsTouchReleased(TouchControllerButton button); /// - /// Vibrate the controller for a fixed duration. + /// Vibrate the controller for a fixed duration. Do so at specified frequency/amplitude if supported by runtime. + /// Oculus runtime supports vibrating at frequency 0.0, 0.5, or 1.0, and amplitude in range [0.0, 1.0] + /// openVR supports vibrating, but does not support frequency or amplitude + /// openXR and WindowsMixedReality currently do not support vibration. /// - /// Vibration duration, in milliseconds + /// Vibration duration, in milliseconds + /// Frequency of vibration in range [0.0, 1.0] + /// Amplitude of vibration in range [0.0, 1.0] /// - public abstract Task Vibrate(int durationMs); + public abstract Task Vibrate(int durationMs, float frequency = 1, float amplitude = 1); public virtual void Dispose() { diff --git a/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs b/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs index 4ca38a842c..37ba6f13a8 100644 --- a/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs +++ b/sources/engine/Stride.VirtualReality/WindowsMixedReality/WindowsMixedRealityTouchController.cs @@ -68,6 +68,8 @@ internal WindowsMixedRealityTouchController(TouchControllerHand hand, SpatialInt public override bool IsTouchedDown(TouchControllerButton button) => !IsButtonTouched(button, previousState) ? IsButtonTouched(button, currentState) : false; public override bool IsTouchReleased(TouchControllerButton button) => IsButtonTouched(button, previousState) ? !IsButtonTouched(button, currentState) : false; + + public override ControllerHaptics HapticsSupport => ControllerHaptics.None; public void Update(PerceptionTimestamp timeStamp, SpatialCoordinateSystem coordinateSystem) { @@ -158,10 +160,7 @@ private void SetSpatialInteractionSourceLocation(SpatialInteractionSourceLocatio } //TODO: implement this - public override async Task Vibrate(int duration) - { - throw new NotImplementedException(); - } + public override async Task Vibrate(int duration, float frequency, float amplitude) { } } } From ca33507a694c127df94c6d848f9618a57b51bb4e Mon Sep 17 00:00:00 2001 From: computersmoke Date: Mon, 4 Mar 2024 18:25:34 -0600 Subject: [PATCH 11/13] Marked oculus runtime as limited frequency because the api ignores frequency (contrary to its documentation) --- .../Stride.VirtualReality/OculusOVR/OculusTouchController.cs | 2 +- .../Stride.VirtualReality/OpenVR/OpenVrTouchController.cs | 2 -- sources/engine/Stride.VirtualReality/TouchController.cs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs index 8886406ab9..c27f04b383 100644 --- a/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OculusOVR/OculusTouchController.cs @@ -142,7 +142,7 @@ public override bool ThumbResting public override Vector2 ThumbAxis => currentThumbstick; public override Vector2 ThumbstickAxis => currentThumbstick; - public override ControllerHaptics HapticsSupport => ControllerHaptics.Full; + public override ControllerHaptics HapticsSupport => ControllerHaptics.LimitedFrequency; public OculusTouchController(TouchControllerHand hand, IntPtr OvrSession) { diff --git a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs index 0a6af90fda..23d412b4b1 100644 --- a/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs +++ b/sources/engine/Stride.VirtualReality/OpenVR/OpenVrTouchController.cs @@ -126,8 +126,6 @@ public override bool IsTouchReleased(TouchControllerButton button) public override async Task Vibrate(int durationMs, float frequency, float amplitude) { - if (frequency <= 0 || amplitude <= 0) - return; while (durationMs > 60) { Valve.VR.OpenVR.System.TriggerHapticPulse((uint)controllerIndex + 1, 0, 1000 * 60); diff --git a/sources/engine/Stride.VirtualReality/TouchController.cs b/sources/engine/Stride.VirtualReality/TouchController.cs index 506c82df48..b3d441b0f0 100644 --- a/sources/engine/Stride.VirtualReality/TouchController.cs +++ b/sources/engine/Stride.VirtualReality/TouchController.cs @@ -50,7 +50,7 @@ public enum ControllerHaptics /// /// Degree to which this touch controller type supports haptics. /// None: no haptics support, controller does not vibrate. - /// Limited: cannot vibrate at any specific frequency or amplitude. Does not vibrate if corresponding parameter is not positive. + /// Limited: cannot vibrate at any specific frequency or amplitude. Corresponding parameter is ignored. /// Full: vibrate method respects both frequency and vibration parameters /// /// From 7de91cf624feca3652d6d66238b486e87eae94e0 Mon Sep 17 00:00:00 2001 From: computersmoke Date: Mon, 4 Mar 2024 18:33:59 -0600 Subject: [PATCH 12/13] removed unused openXR haptics helper function that may not have been correct --- .../Stride.VirtualReality/OpenXR/OpenXRInput.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs index 4fe8c04721..420752ebd8 100644 --- a/sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs +++ b/sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Text; using Silk.NET.Core.Native; using Silk.NET.OpenXR; @@ -150,10 +149,6 @@ private static Silk.NET.OpenXR.Action GetAction(TouchControllerHand hand, TouchC throw new ArgumentException("Don't know button: " + button); } } - public static Silk.NET.OpenXR.Action GetHapticAction(TouchControllerHand hand) - { - return MappedActions[(int)hand, (int)HAND_PATHS.HapticOut]; - } public bool GetActionBool(TouchControllerHand hand, TouchControllerButton button, out bool wasChangedSinceLast, bool fallback = false) { @@ -222,9 +217,9 @@ public float GetActionFloat(TouchControllerHand hand, TouchControllerButton butt private static unsafe void Initialize(OpenXRHmd hmd) { // make actions - for (int i=0; i bindings = new List(); // for each hand... - for (int hand=0; hand<2; hand++) + for (int hand = 0; hand < 2; hand++) { // for each path we want to bind... - for (int path=0; path possiblePaths = PathPriorities[path]; - for (int pathattempt=0; pathattempt Date: Tue, 5 Mar 2024 11:19:27 +0100 Subject: [PATCH 13/13] Remove invalid param in HapticsSupport summary --- sources/engine/Stride.VirtualReality/TouchController.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/sources/engine/Stride.VirtualReality/TouchController.cs b/sources/engine/Stride.VirtualReality/TouchController.cs index b3d441b0f0..ca7257041c 100644 --- a/sources/engine/Stride.VirtualReality/TouchController.cs +++ b/sources/engine/Stride.VirtualReality/TouchController.cs @@ -53,8 +53,6 @@ public enum ControllerHaptics /// Limited: cannot vibrate at any specific frequency or amplitude. Corresponding parameter is ignored. /// Full: vibrate method respects both frequency and vibration parameters /// - /// - /// public abstract ControllerHaptics HapticsSupport { get; } ///