Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit ec992a5

Browse files
daoshengmuMortimerGoro
authored andcommitted
Making WaveVR/OculusVR controllers support haptic feedback (#2198)
* Making WaveVR controllers support haptic feedback. * Making Oculus Quest support haptic feedback. * Making Quest thumbRest always be false.
1 parent 6ba3881 commit ec992a5

File tree

10 files changed

+207
-3
lines changed

10 files changed

+207
-3
lines changed

app/src/main/cpp/BrowserWorld.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,7 @@ BrowserWorld::Draw() {
871871
m.device->ProcessEvents();
872872
m.context->Update();
873873
m.externalVR->PullBrowserState();
874+
m.externalVR->SetHapticState(m.controllers);
874875

875876
m.CheckExitImmersive();
876877
if (m.splashAnimation) {

app/src/main/cpp/Controller.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ Controller::operator=(const Controller& aController) {
5858
numButtons = aController.numButtons;
5959
memcpy(immersiveAxes, aController.immersiveAxes, sizeof(immersiveAxes));
6060
numAxes = aController.numAxes;
61+
numHaptics = aController.numHaptics;
62+
inputFrameID = aController.inputFrameID;
63+
pulseDuration = aController.pulseDuration;
64+
pulseIntensity = aController.pulseIntensity;
6165
leftHanded = aController.leftHanded;
6266
inDeadZone = aController.inDeadZone;
6367
lastHoverEvent = aController.lastHoverEvent;
@@ -91,6 +95,10 @@ Controller::Reset() {
9195
numButtons = 0;
9296
memset(immersiveAxes, 0, sizeof(immersiveAxes));
9397
numAxes = 0;
98+
numHaptics = 0;
99+
inputFrameID = 0;
100+
pulseDuration = 0.0f;
101+
pulseIntensity = 0.0f;
94102
leftHanded = false;
95103
inDeadZone = true;
96104
lastHoverEvent = 0.0;

app/src/main/cpp/Controller.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ struct Controller {
5151
uint32_t numButtons;
5252
float immersiveAxes[kControllerMaxAxes];
5353
uint32_t numAxes;
54+
uint32_t numHaptics;
55+
float inputFrameID;
56+
float pulseDuration;
57+
float pulseIntensity;
58+
5459
bool leftHanded;
5560
bool inDeadZone;
5661
double lastHoverEvent;

app/src/main/cpp/ControllerContainer.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,45 @@ ControllerContainer::SetAxes(const int32_t aControllerIndex, const float* aData,
341341
}
342342
}
343343

344+
void
345+
ControllerContainer::SetHapticCount(const int32_t aControllerIndex, const uint32_t aNumHaptics) {
346+
if (!m.Contains(aControllerIndex)) {
347+
return;
348+
}
349+
m.list[aControllerIndex].numHaptics = aNumHaptics;
350+
}
351+
352+
uint32_t
353+
ControllerContainer::GetHapticCount(const int32_t aControllerIndex) {
354+
if (!m.Contains(aControllerIndex)) {
355+
return 0;
356+
}
357+
358+
return m.list[aControllerIndex].numHaptics;
359+
}
360+
361+
void
362+
ControllerContainer::SetHapticFeedback(const int32_t aControllerIndex, const uint64_t aInputFrameID,
363+
const float aPulseDuration, const float aPulseIntensity) {
364+
if (!m.Contains(aControllerIndex)) {
365+
return;
366+
}
367+
m.list[aControllerIndex].inputFrameID = aInputFrameID;
368+
m.list[aControllerIndex].pulseDuration = aPulseDuration;
369+
m.list[aControllerIndex].pulseIntensity = aPulseIntensity;
370+
}
371+
372+
void
373+
ControllerContainer::GetHapticFeedback(const int32_t aControllerIndex, uint64_t & aInputFrameID,
374+
float& aPulseDuration, float& aPulseIntensity) {
375+
if (!m.Contains(aControllerIndex)) {
376+
return;
377+
}
378+
aInputFrameID = m.list[aControllerIndex].inputFrameID;
379+
aPulseDuration = m.list[aControllerIndex].pulseDuration;
380+
aPulseIntensity = m.list[aControllerIndex].pulseIntensity;
381+
}
382+
344383
void
345384
ControllerContainer::SetLeftHanded(const int32_t aControllerIndex, const bool aLeftHanded) {
346385
if (!m.Contains(aControllerIndex)) {

app/src/main/cpp/ControllerContainer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ class ControllerContainer : public crow::ControllerDelegate {
4343
void SetButtonCount(const int32_t aControllerIndex, const uint32_t aNumButtons) override;
4444
void SetButtonState(const int32_t aControllerIndex, const Button aWhichButton, const int32_t aImmersiveIndex, const bool aPressed, const bool aTouched, const float aImmersiveTrigger = -1.0f) override;
4545
void SetAxes(const int32_t aControllerIndex, const float* aData, const uint32_t aLength) override;
46+
void SetHapticCount(const int32_t aControllerIndex, const uint32_t aNumHaptics) override;
47+
uint32_t GetHapticCount(const int32_t aControllerIndex) override;
48+
void SetHapticFeedback(const int32_t aControllerIndex, const uint64_t aInputFrameID, const float aPulseDuration, const float aPulseIntensity) override;
49+
void GetHapticFeedback(const int32_t aControllerIndex, uint64_t &aInputFrameID, float& aPulseDuration, float& aPulseIntensity) override;
4650
void SetLeftHanded(const int32_t aControllerIndex, const bool aLeftHanded) override;
4751
void SetTouchPosition(const int32_t aControllerIndex, const float aTouchX, const float aTouchY) override;
4852
void EndTouch(const int32_t aControllerIndex) override;

app/src/main/cpp/ControllerDelegate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class ControllerDelegate {
4242
virtual void SetButtonCount(const int32_t aControllerIndex, const uint32_t aNumButtons) = 0;
4343
virtual void SetButtonState(const int32_t aControllerIndex, const Button aWhichButton, const int32_t aImmersiveIndex, const bool aPressed, const bool aTouched, const float aImmersiveTrigger = -1.0f) = 0;
4444
virtual void SetAxes(const int32_t aControllerIndex, const float* aData, const uint32_t aLength) = 0;
45+
virtual void SetHapticCount(const int32_t aControllerIndex, const uint32_t aNumHaptics) = 0;
46+
virtual uint32_t GetHapticCount(const int32_t aControllerIndex) = 0;
47+
virtual void SetHapticFeedback(const int32_t aControllerIndex, const uint64_t aInputFrameID, const float aPulseDuration, const float aPulseIntensity) = 0;
48+
virtual void GetHapticFeedback(const int32_t aControllerIndex, uint64_t & aInputFrameID, float& aPulseDuration, float& aPulseIntensity) = 0;
4549
virtual void SetLeftHanded(const int32_t aControllerIndex, const bool aLeftHanded) = 0;
4650
virtual void SetTouchPosition(const int32_t aControllerIndex, const float aTouchX, const float aTouchY) = 0;
4751
virtual void EndTouch(const int32_t aControllerIndex) = 0;

app/src/main/cpp/ExternalVR.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ ExternalVR::PushFramePoses(const vrb::Matrix& aHeadTransform, const std::vector<
412412
for (int i = 0; i< controller.numAxes; ++i) {
413413
immersiveController.axisValue[i] = controller.immersiveAxes[i];
414414
}
415+
immersiveController.numHaptics = controller.numHaptics;
415416
immersiveController.hand = controller.leftHanded ? mozilla::gfx::ControllerHand::Left : mozilla::gfx::ControllerHand::Right;
416417

417418
const uint16_t flags = GetControllerCapabilityFlags(controller.deviceCapabilities);
@@ -489,6 +490,26 @@ ExternalVR::GetFrameResult(int32_t& aSurfaceHandle, int32_t& aTextureWidth, int3
489490
aTextureHeight = (int32_t)m.browser.layerState[0].layer_stereo_immersive.textureSize.height;
490491
}
491492

493+
void
494+
ExternalVR::SetHapticState(ControllerContainerPtr aControllerContainer) const {
495+
const uint32_t count = aControllerContainer->GetControllerCount();
496+
uint32_t i = 0, j = 0;
497+
for (i = 0; i < count; ++i) {
498+
for (j = 0; j < mozilla::gfx::kVRHapticsMaxCount; ++j) {
499+
if (m.browser.hapticState[j].controllerIndex == i && m.browser.hapticState[j].inputFrameID) {
500+
aControllerContainer->SetHapticFeedback(i, m.browser.hapticState[j].inputFrameID,
501+
m.browser.hapticState[j].pulseDuration + m.browser.hapticState[j].pulseStart,
502+
m.browser.hapticState[j].pulseIntensity);
503+
break;
504+
}
505+
}
506+
// All hapticState has already been reset to zero, so it can't be match.
507+
if (j == mozilla::gfx::kVRHapticsMaxCount) {
508+
aControllerContainer->SetHapticFeedback(i, 0, 0.0f, 0.0f);
509+
}
510+
}
511+
}
512+
492513
void
493514
ExternalVR::StopPresenting() {
494515
m.system.displayState.presentingGeneration++;

app/src/main/cpp/ExternalVR.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "vrb/MacroUtils.h"
1010
#include "Controller.h"
11+
#include "ControllerContainer.h"
1112
#include "DeviceDelegate.h"
1213
#include "Device.h"
1314
#include <memory>
@@ -59,6 +60,7 @@ class ExternalVR : public ImmersiveDisplay {
5960
int32_t& aTextureHeight,
6061
device::EyeRect& aLeftEye,
6162
device::EyeRect& aRightEye) const;
63+
void SetHapticState(ControllerContainerPtr aControllerContainer) const;
6264
void StopPresenting();
6365
void SetSourceBrowser(VRBrowserType aBrowser);
6466
ExternalVR();

app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,9 @@ struct DeviceDelegateOculusVR::State {
644644
ovrInputTrackedRemoteCapabilities capabilities = {};
645645
vrb::Matrix transform = vrb::Matrix::Identity();
646646
ovrInputStateTrackedRemote inputState = {};
647+
uint64_t inputFrameID = 0;
648+
float remainingVibrateTime = 0.0f;
649+
double lastHapticUpdateTimeStamp = 0.0f;
647650

648651
bool Is6DOF() const {
649652
return (capabilities.ControllerCapabilities & ovrControllerCaps_HasPositionTracking) &&
@@ -692,7 +695,6 @@ struct DeviceDelegateOculusVR::State {
692695
device::CPULevel minCPULevel = device::CPULevel::Normal;
693696
device::DeviceType deviceType = device::UnknownType;
694697

695-
696698
void UpdatePerspective() {
697699
float fovX = vrapi_GetSystemPropertyFloat(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X);
698700
float fovY = vrapi_GetSystemPropertyFloat(&java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_Y);
@@ -952,10 +954,13 @@ struct DeviceDelegateOculusVR::State {
952954
controller->CreateController(controllerState.index, int32_t(controllerState.hand),
953955
controllerName, beamTransform);
954956
controller->SetButtonCount(controllerState.index, 6);
957+
controller->SetHapticCount(controllerState.index, 1);
955958
} else {
956959
// Oculus Go only has one kind of controller model.
957960
controller->CreateController(controllerState.index, 0, "Oculus Go Controller");
958961
controller->SetButtonCount(controllerState.index, 2);
962+
// Oculus Go has no haptic feedback.
963+
controller->SetHapticCount(controllerState.index, 0);
959964
}
960965
controllerState.created = true;
961966
}
@@ -1037,7 +1042,6 @@ struct DeviceDelegateOculusVR::State {
10371042
const bool yTouched = (controllerState.inputState.Touches & ovrTouch_Y) != 0;
10381043
const bool menuPressed = (controllerState.inputState.Buttons & ovrButton_Enter) != 0;
10391044

1040-
10411045
controller->SetButtonState(controllerState.index, ControllerDelegate::BUTTON_X, 3, xPressed, xTouched);
10421046
controller->SetButtonState(controllerState.index, ControllerDelegate::BUTTON_Y, 4, yPressed, yTouched);
10431047

@@ -1062,7 +1066,8 @@ struct DeviceDelegateOculusVR::State {
10621066
VRB_WARN("Undefined hand type in DeviceDelegateOculusVR.");
10631067
}
10641068

1065-
const bool thumbRest = (controllerState.inputState.Touches & ovrTouch_ThumbUp) != 0;
1069+
// This is always false in Oculus Browser.
1070+
const bool thumbRest = false;
10661071
controller->SetButtonState(controllerState.index, ControllerDelegate::BUTTON_OTHERS, 5, thumbRest, thumbRest);
10671072
} else {
10681073
triggerPressed = (controllerState.inputState.Buttons & ovrButton_A) != 0;
@@ -1093,6 +1098,58 @@ struct DeviceDelegateOculusVR::State {
10931098
controller->SetButtonState(controllerState.index, ControllerDelegate::BUTTON_TOUCHPAD, 0, trackpadPressed, trackpadTouched);
10941099

10951100
controller->SetAxes(controllerState.index, axes, kNumAxes);
1101+
if (controller->GetHapticCount(controllerState.index)) {
1102+
UpdateHaptics(controllerState);
1103+
}
1104+
}
1105+
}
1106+
1107+
void UpdateHaptics(ControllerState& controllerState) {
1108+
vrb::RenderContextPtr renderContext = context.lock();
1109+
if (!renderContext) {
1110+
return;
1111+
}
1112+
if (!controller || !ovr) {
1113+
return;
1114+
}
1115+
1116+
uint64_t inputFrameID = 0;
1117+
float pulseDuration = 0.0f, pulseIntensity = 0.0f;
1118+
controller->GetHapticFeedback(controllerState.index, inputFrameID, pulseDuration, pulseIntensity);
1119+
if (inputFrameID > 0 && pulseIntensity > 0.0f && pulseDuration > 0) {
1120+
if (controllerState.inputFrameID != inputFrameID) {
1121+
// When there is a new input frame id from haptic vibration,
1122+
// that means we start a new session for a vibration.
1123+
controllerState.inputFrameID = inputFrameID;
1124+
controllerState.remainingVibrateTime = pulseDuration;
1125+
controllerState.lastHapticUpdateTimeStamp = renderContext->GetTimestamp();
1126+
} else {
1127+
// We are still running the previous vibration.
1128+
// So, it needs to reduce the delta time from the last vibration.
1129+
const double timeStamp = renderContext->GetTimestamp();
1130+
controllerState.remainingVibrateTime -= (timeStamp - controllerState.lastHapticUpdateTimeStamp);
1131+
controllerState.lastHapticUpdateTimeStamp = timeStamp;
1132+
}
1133+
1134+
if (controllerState.remainingVibrateTime > 0.0f && renderMode == device::RenderMode::Immersive) {
1135+
if (vrapi_SetHapticVibrationSimple(ovr, controllerState.deviceId, pulseIntensity > 1.0f ? 1.0f : pulseIntensity)
1136+
== ovrError_InvalidOperation) {
1137+
VRB_ERROR("vrapi_SetHapticVibrationBuffer failed.");
1138+
}
1139+
} else {
1140+
// The remaining time is zero or exiting the immersive mode, stop the vibration.
1141+
if (vrapi_SetHapticVibrationSimple(ovr, controllerState.deviceId, 0.0f) == ovrError_InvalidOperation) {
1142+
VRB_ERROR("vrapi_SetHapticVibrationBuffer failed.");
1143+
}
1144+
controllerState.remainingVibrateTime = 0.0f;
1145+
}
1146+
} else if (controllerState.remainingVibrateTime > 0.0f) {
1147+
// While the haptic feedback is terminated from the client side,
1148+
// but it still have remaining time, we need to ask for stopping vibration.
1149+
if (vrapi_SetHapticVibrationSimple(ovr, controllerState.deviceId, 0.0f) == ovrError_InvalidOperation) {
1150+
VRB_ERROR("vrapi_SetHapticVibrationBuffer failed.");
1151+
}
1152+
controllerState.remainingVibrateTime = 0.0f;
10961153
}
10971154
}
10981155

app/src/wavevr/cpp/DeviceDelegateWaveVR.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ struct DeviceDelegateWaveVR::State {
4545
int32_t gripPressedCount;
4646
vrb::Matrix transform;
4747
ElbowModel::HandEnum hand;
48+
uint64_t inputFrameID;
49+
float remainingVibrateTime;
50+
double lastHapticUpdateTimeStamp;
4851
Controller()
4952
: index(-1)
5053
, type(WVR_DeviceType_Controller_Right)
@@ -55,6 +58,9 @@ struct DeviceDelegateWaveVR::State {
5558
, gripPressedCount(0)
5659
, transform(vrb::Matrix::Identity())
5760
, hand(ElbowModel::HandEnum::Right)
61+
, inputFrameID(0)
62+
, remainingVibrateTime(0.0f)
63+
, lastHapticUpdateTimeStamp(0.0f)
5864
{}
5965
};
6066

@@ -259,6 +265,7 @@ struct DeviceDelegateWaveVR::State {
259265
}
260266
delegate->CreateController(aController.index, aController.is6DoF ? 1 : 0, aController.is6DoF ? "HTC Vive Focus Plus Controller" : "HTC Vive Focus Controller", transform);
261267
delegate->SetLeftHanded(aController.index, aController.hand == ElbowModel::HandEnum::Left);
268+
delegate->SetHapticCount(aController.index, 1);
262269
aController.created = true;
263270
aController.enabled = false;
264271
}
@@ -350,6 +357,62 @@ struct DeviceDelegateWaveVR::State {
350357
delegate->EndTouch(controller.index);
351358
}
352359
delegate->SetAxes(controller.index, immersiveAxes, kNumAxes);
360+
361+
UpdateHaptics(controller);
362+
}
363+
}
364+
365+
void UpdateHaptics(Controller& controller) {
366+
vrb::RenderContextPtr renderContext = context.lock();
367+
if (!renderContext) {
368+
return;
369+
}
370+
if (!delegate) {
371+
return;
372+
}
373+
374+
uint64_t inputFrameID = 0;
375+
float pulseDuration = 0.0f, pulseIntensity = 0.0f;
376+
delegate->GetHapticFeedback(controller.index, inputFrameID, pulseDuration, pulseIntensity);
377+
if (inputFrameID > 0 && pulseIntensity > 0.0f && pulseDuration > 0) {
378+
if (controller.inputFrameID != inputFrameID) {
379+
// When there is a new input frame id from haptic vibration,
380+
// that means we start a new session for a vibration.
381+
controller.inputFrameID = inputFrameID;
382+
controller.remainingVibrateTime = pulseDuration;
383+
controller.lastHapticUpdateTimeStamp = renderContext->GetTimestamp();
384+
} else {
385+
// We are still running the previous vibration.
386+
// So, it needs to reduce the delta time from the last vibration.
387+
const double timeStamp = renderContext->GetTimestamp();
388+
controller.remainingVibrateTime -= (timeStamp - controller.lastHapticUpdateTimeStamp);
389+
controller.lastHapticUpdateTimeStamp = timeStamp;
390+
}
391+
392+
if (controller.remainingVibrateTime > 0.0f && renderMode == device::RenderMode::Immersive) {
393+
// THe duration time unit needs to be transformed from milliseconds to microseconds.
394+
// The gamepad extensions API does not yet have independent control
395+
// of frequency and intensity. It only has vibration value (0.0 ~ 1.0).
396+
// In this WaveVR SDK, the value makes more sense to be intensity because frequency can't
397+
// < 1.0 here.
398+
int intensity = ceil(pulseIntensity * 5);
399+
intensity = intensity <= 5 ? intensity : 5;
400+
WVR_TriggerVibration(controller.type, WVR_InputId_Max, controller.remainingVibrateTime * 1000.0f,
401+
1, WVR_Intensity(intensity));
402+
} else {
403+
// The remaining time is zero or exiting the immersive mode, stop the vibration.
404+
#if !defined(__arm__) // It will crash at WaveVR SDK arm32, let's skip it.
405+
WVR_TriggerVibration(controller.type, WVR_InputId_Max, 0, 0, WVR_Intensity_Normal);
406+
#endif
407+
controller.remainingVibrateTime = 0.0f;
408+
}
409+
} else if (controller.remainingVibrateTime > 0.0f) {
410+
// While the haptic feedback is terminated from the client side,
411+
// but it still have remaining time, we need to ask for stopping vibration.
412+
#if !defined(__arm__) // It will crash at WaveVR SDK arm32, let's skip it.
413+
WVR_TriggerVibration(controller.type, WVR_InputId_Max, 0, 0, WVR_Intensity_Normal);
414+
#endif
415+
controller.remainingVibrateTime = 0.0f;
353416
}
354417
}
355418
};

0 commit comments

Comments
 (0)