Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Input] Add haptic support to OpenVR and Oculus runtimes #2169

Merged
merged 21 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
dbd7a1c
added haptic feedback for openVR and oculusOVR apis
ComputerSmoke Nov 11, 2023
6437419
Wrote OpenXR haptics that don't work
ComputerSmoke Nov 12, 2023
4953ea7
Removed unused imports from OpenXRHmd
ComputerSmoke Nov 12, 2023
bef52aa
Merge branch 'stride3d:master' into master
ComputerSmoke Nov 16, 2023
0ac50ec
Merge branch 'stride3d:master' into master
ComputerSmoke Nov 19, 2023
eb433c4
Merge branch 'stride3d:master' into master
ComputerSmoke Nov 26, 2023
7a64e42
Merge branch 'stride3d:master' into master
ComputerSmoke Dec 13, 2023
c7b03fd
Merge branch 'stride3d:master' into master
ComputerSmoke Mar 2, 2024
3468fcb
Made openXR runtime throw not implemented exception when vibration ca…
ComputerSmoke Mar 2, 2024
ffe8566
Moved a property declaration to top of file
ComputerSmoke Mar 2, 2024
cfbe674
Made vibration work with start/stop vibration methods rather than jus…
ComputerSmoke Mar 4, 2024
ca37404
Fixed stop vibration in oculusTouchController
ComputerSmoke Mar 4, 2024
38913c0
Fixed Disable vibration for oculus touch controller by setting amplit…
ComputerSmoke Mar 4, 2024
1ae7f5e
fixed merge issues
ComputerSmoke Mar 4, 2024
a8d22a5
Fixed bug where calling StopVibration during Vibrate(duration) execut…
ComputerSmoke Mar 4, 2024
36a8d42
Added frequency/amplitude parameters and a property to indicate wheth…
ComputerSmoke Mar 5, 2024
cc9f293
Merge branch 'master' into FixedDuration
ComputerSmoke Mar 5, 2024
e367e11
Merge fixed duration
ComputerSmoke Mar 5, 2024
ca33507
Marked oculus runtime as limited frequency because the api ignores fr…
ComputerSmoke Mar 5, 2024
7de91cf
removed unused openXR haptics helper function that may not have been …
ComputerSmoke Mar 5, 2024
70214df
Remove invalid param in HapticsSupport summary
Eideren Mar 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
14 changes: 14 additions & 0 deletions sources/engine/Stride.VirtualReality/OculusOVR/OculusOVR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,5 +224,19 @@ 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)]
static extern void SetLeftVibration(IntPtr session, float frequency, float amplitude);
[SuppressUnmanagedCodeSecurity]
[DllImport(NativeInvoke.Library, EntryPoint = "xnOvrSetRightVibration", CallingConvention = CallingConvention.Cdecl)]
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ 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);
leftHandController = new OculusTouchController(TouchControllerHand.Left, ovrSession);
rightHandController = new OculusTouchController(TouchControllerHand.Right, ovrSession);
}

private OculusOvr.PosesProperties currentPoses;
Expand Down Expand Up @@ -162,7 +162,7 @@ public override bool CanInitialize
return initDone;
}
}

public override void Recenter()
{
OculusOvr.Recenter(ovrSession);
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

namespace Stride.VirtualReality
Expand All @@ -23,6 +24,10 @@ internal class OculusTouchController : TouchController
private uint previousButtonsState;
private Vector2 currentThumbstick;
private const float TriggerAndGripDeadzone = 0.00001f;
private IntPtr OvrSession { get; }
//Number of calls to vibrate that are currently executing
private int vibrationCount;
private readonly object vibrationLock = new();

public override Vector3 Position => currentPos;

Expand Down Expand Up @@ -137,10 +142,12 @@ public override bool ThumbResting
public override Vector2 ThumbAxis => currentThumbstick;

public override Vector2 ThumbstickAxis => currentThumbstick;
public override ControllerHaptics HapticsSupport => ControllerHaptics.LimitedFrequency;

public OculusTouchController(TouchControllerHand hand)
public OculusTouchController(TouchControllerHand hand, IntPtr OvrSession)
{
this.hand = hand;
this.OvrSession = OvrSession;
currentState = DeviceState.Invalid;
}

Expand Down Expand Up @@ -399,5 +406,32 @@ public override bool IsTouchReleased(TouchControllerButton button)
return false;
}
}

public override async Task Vibrate(int durationMs, float frequency, float amplitude)
{
void SetOvrVibration(bool enable)
{
float freqParam = enable ? frequency : 0;
float ampParam = enable ? amplitude : 0;
OculusOvr.SetVibration(OvrSession, hand, freqParam, ampParam);
}
lock (vibrationLock)
{
vibrationCount++;
}
while (durationMs > 2400)
{
SetOvrVibration(true);
durationMs -= 2400;
await Task.Delay(2400);
}
SetOvrVibration(true);
await Task.Delay(durationMs);
lock (vibrationLock)
{
if (--vibrationCount == 0)
SetOvrVibration(false);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// 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;

Expand Down Expand Up @@ -75,13 +75,14 @@ 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)
{
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:
Expand Down Expand Up @@ -123,6 +124,17 @@ public override bool IsTouchReleased(TouchControllerButton button)
return controller?.GetTouchUp(ToOpenVrButton(button)) ?? false;
}

public override async Task Vibrate(int durationMs, float frequency, float amplitude)
{
while (durationMs > 60)
{
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));
}

public override Vector3 Position => currentPos;

public override Quaternion Rotation => currentRot;
Expand Down
13 changes: 6 additions & 7 deletions sources/engine/Stride.VirtualReality/OpenXR/OpenXRInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

using System;
using System.Collections.Generic;
using System.Text;
using Silk.NET.Core.Native;
using Silk.NET.OpenXR;

Expand Down Expand Up @@ -218,9 +217,9 @@ public float GetActionFloat(TouchControllerHand hand, TouchControllerButton butt
private static unsafe void Initialize(OpenXRHmd hmd)
{
// make actions
for (int i=0; i<HAND_PATH_COUNT; i++)
for (int i = 0; i < HAND_PATH_COUNT; i++)
{
for (int j=0; j<2; j++)
for (int j = 0; j < 2; j++)
{
ActionCreateInfo action_info = new ActionCreateInfo()
{
Expand All @@ -240,21 +239,21 @@ private static unsafe void Initialize(OpenXRHmd hmd)
}

// probe bindings for all profiles
for (int i=0; i<InteractionProfiles.Length; i++)
for (int i = 0; i < InteractionProfiles.Length; i++)
{
ulong profile = 0;
hmd.Xr.StringToPath(hmd.Instance, InteractionProfiles[i], ref profile);

List<ActionSuggestedBinding> bindings = new List<ActionSuggestedBinding>();
// 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<HAND_PATH_COUNT; path++)
for (int path = 0; path < HAND_PATH_COUNT; path++)
{
// list all possible paths that might be valid and pick the first one
List<string> possiblePaths = PathPriorities[path];
for (int pathattempt=0; pathattempt<possiblePaths.Count; pathattempt++)
for (int pathattempt = 0; pathattempt < possiblePaths.Count; pathattempt++)
{
// get the hand at the start, then put in the attempt
string final_path = hand == (int)TouchControllerHand.Left ? "/user/hand/left" : "/user/hand/right";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#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;
Expand Down Expand Up @@ -72,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)
{
Expand Down Expand Up @@ -170,5 +169,8 @@ 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, float frequency, float amplitude) { }
}
}
33 changes: 31 additions & 2 deletions sources/engine/Stride.VirtualReality/TouchController.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -19,7 +20,7 @@ public abstract class TouchController : IDisposable
public abstract DeviceState State { get; }

public virtual void Update(GameTime time)
{
{
}

public abstract float Trigger { get; }
Expand All @@ -38,6 +39,22 @@ public virtual void Update(GameTime time)

public abstract Vector2 ThumbstickAxis { get; }

public enum ControllerHaptics
{
None,
Limited,
LimitedFrequency,
LimitedAmplitude,
Full
}
/// <summary>
/// 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. Corresponding parameter is ignored.
/// Full: vibrate method respects both frequency and vibration parameters
/// </summary>
public abstract ControllerHaptics HapticsSupport { get; }

/// <summary>
/// Returns true if in this frame the button switched to pressed state
/// </summary>
Expand Down Expand Up @@ -80,8 +97,20 @@ public virtual void Update(GameTime time)
/// <returns></returns>
public abstract bool IsTouchReleased(TouchControllerButton button);

/// <summary>
/// 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.
/// </summary>
/// <param name="durationMs">Vibration duration, in milliseconds</param>
/// <param name="frequency">Frequency of vibration in range [0.0, 1.0]</param>
/// <param name="amplitude">Amplitude of vibration in range [0.0, 1.0]</param>
/// <returns></returns>
public abstract Task Vibrate(int durationMs, float frequency = 1, float amplitude = 1);

public virtual void Dispose()
{
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -156,6 +158,9 @@ private void SetSpatialInteractionSourceLocation(SpatialInteractionSourceLocatio
currentLinearVelocity = location.Velocity?.ToVector3() ?? currentLinearVelocity;
currentAngularVelocity = location.AngularVelocity?.ToVector3() ?? currentAngularVelocity;
}

//TODO: implement this
public override async Task Vibrate(int duration, float frequency, float amplitude) { }
}
}

Expand Down