зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1305892 - Support Oculus Touch Haptic Feedback; r=kip
MozReview-Commit-ID: 7V931IAcbbx --HG-- extra : rebase_source : 3423aa7a99b79293783cb1572f8ad3d7a257c7c7
This commit is contained in:
Родитель
3a4bd0aae6
Коммит
22ebee8da0
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "gfxVROculus.h"
|
||||
|
||||
#include "mozilla/layers/CompositorThread.h"
|
||||
#include "mozilla/dom/GamepadEventTypes.h"
|
||||
#include "mozilla/dom/GamepadBinding.h"
|
||||
|
||||
|
@ -145,7 +146,7 @@ enum class OculusRightControllerButtonType : uint16_t {
|
|||
static const uint32_t kNumOculusButton = static_cast<uint32_t>
|
||||
(OculusLeftControllerButtonType::
|
||||
NumButtonType);
|
||||
static const uint32_t kNumOculusHaptcs = 0; // TODO: Bug 1305892
|
||||
static const uint32_t kNumOculusHaptcs = 1;
|
||||
|
||||
|
||||
static bool
|
||||
|
@ -886,6 +887,8 @@ VRControllerOculus::VRControllerOculus(dom::GamepadHand aHand)
|
|||
: VRControllerHost(VRDeviceType::Oculus)
|
||||
, mIndexTrigger(0.0f)
|
||||
, mHandTrigger(0.0f)
|
||||
, mVibrateThread(nullptr)
|
||||
, mIsVibrateStopped(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(VRControllerOculus, VRControllerHost);
|
||||
|
||||
|
@ -957,6 +960,144 @@ VRControllerOculus::~VRControllerOculus()
|
|||
MOZ_COUNT_DTOR_INHERITED(VRControllerOculus, VRControllerHost);
|
||||
}
|
||||
|
||||
void
|
||||
VRControllerOculus::UpdateVibrateHaptic(ovrSession aSession,
|
||||
uint32_t aHapticIndex,
|
||||
double aIntensity,
|
||||
double aDuration,
|
||||
uint64_t aVibrateIndex,
|
||||
uint32_t aPromiseID)
|
||||
{
|
||||
// UpdateVibrateHaptic() only can be called by mVibrateThread
|
||||
MOZ_ASSERT(mVibrateThread == NS_GetCurrentThread());
|
||||
|
||||
// It has been interrupted by loss focus.
|
||||
if (mIsVibrateStopped) {
|
||||
VibrateHapticComplete(aSession, aPromiseID, true);
|
||||
return;
|
||||
}
|
||||
// Avoid the previous vibrate event to override the new one.
|
||||
if (mVibrateIndex != aVibrateIndex) {
|
||||
VibrateHapticComplete(aSession, aPromiseID, false);
|
||||
return;
|
||||
}
|
||||
|
||||
const double duration = (aIntensity == 0) ? 0 : aDuration;
|
||||
// Vibration amplitude in the [0.0, 1.0] range.
|
||||
const float amplitude = aIntensity > 1.0 ? 1.0 : aIntensity;
|
||||
// Vibration is enabled by specifying the frequency.
|
||||
// Specifying 0.0f will disable the vibration, 0.5f will vibrate at 160Hz,
|
||||
// and 1.0f will vibrate at 320Hz.
|
||||
const float frequency = (duration > 0) ? 1.0f : 0.0f;
|
||||
ovrControllerType hand;
|
||||
|
||||
switch (GetHand()) {
|
||||
case GamepadHand::Left:
|
||||
hand = ovrControllerType::ovrControllerType_LTouch;
|
||||
break;
|
||||
case GamepadHand::Right:
|
||||
hand = ovrControllerType::ovrControllerType_RTouch;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Oculus Touch only can get the response from ovr_SetControllerVibration()
|
||||
// at the presenting mode.
|
||||
ovrResult result = ovr_SetControllerVibration(aSession, hand, frequency,
|
||||
(frequency == 0.0f) ? 0.0f : amplitude);
|
||||
if (result != ovrSuccess) {
|
||||
printf_stderr("%s hand ovr_SetControllerVibration skipped.\n",
|
||||
GamepadHandValues::strings[uint32_t(GetHand())].value);
|
||||
}
|
||||
|
||||
// In Oculus dev doc, it mentions vibration lasts for a maximum of 2.5 seconds
|
||||
// at ovr_SetControllerVibration(), but we found 2.450 sec is more close to the
|
||||
// real looping use case.
|
||||
const double kVibrateRate = 2450.0;
|
||||
const double remainingTime = (duration > kVibrateRate)
|
||||
? (duration - kVibrateRate) : duration;
|
||||
|
||||
if (remainingTime) {
|
||||
MOZ_ASSERT(mVibrateThread);
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t, uint32_t>
|
||||
(this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
|
||||
aHapticIndex, aIntensity, (duration > kVibrateRate) ? remainingTime : 0, aVibrateIndex, aPromiseID);
|
||||
NS_DelayedDispatchToCurrentThread(runnable.forget(),
|
||||
(duration > kVibrateRate) ? kVibrateRate : remainingTime);
|
||||
} else {
|
||||
VibrateHapticComplete(aSession, aPromiseID, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VRControllerOculus::VibrateHapticComplete(ovrSession aSession, uint32_t aPromiseID,
|
||||
bool aStop)
|
||||
{
|
||||
if (aStop) {
|
||||
ovrControllerType hand;
|
||||
|
||||
switch (GetHand()) {
|
||||
case GamepadHand::Left:
|
||||
hand = ovrControllerType::ovrControllerType_LTouch;
|
||||
break;
|
||||
case GamepadHand::Right:
|
||||
hand = ovrControllerType::ovrControllerType_RTouch;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
ovrResult result = ovr_SetControllerVibration(aSession, hand, 0.0f, 0.0f);
|
||||
if (result != ovrSuccess) {
|
||||
printf_stderr("%s Haptics skipped.\n",
|
||||
GamepadHandValues::strings[uint32_t(GetHand())].value);
|
||||
}
|
||||
}
|
||||
|
||||
VRManager *vm = VRManager::Get();
|
||||
MOZ_ASSERT(vm);
|
||||
|
||||
CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<uint32_t>
|
||||
(vm, &VRManager::NotifyVibrateHapticCompleted, aPromiseID));
|
||||
}
|
||||
|
||||
void
|
||||
VRControllerOculus::VibrateHaptic(ovrSession aSession,
|
||||
uint32_t aHapticIndex,
|
||||
double aIntensity,
|
||||
double aDuration,
|
||||
uint32_t aPromiseID)
|
||||
{
|
||||
// Spinning up the haptics thread at the first haptics call.
|
||||
if (!mVibrateThread) {
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(mVibrateThread));
|
||||
MOZ_ASSERT(mVibrateThread);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(false, "Failed to create async thread.");
|
||||
}
|
||||
}
|
||||
++mVibrateIndex;
|
||||
mIsVibrateStopped = false;
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t, uint32_t>
|
||||
(this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
|
||||
aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromiseID);
|
||||
mVibrateThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
VRControllerOculus::StopVibrateHaptic()
|
||||
{
|
||||
mIsVibrateStopped = true;
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<VRSystemManagerOculus>
|
||||
VRSystemManagerOculus::Create()
|
||||
{
|
||||
|
@ -1262,12 +1403,31 @@ VRSystemManagerOculus::VibrateHaptic(uint32_t aControllerIdx,
|
|||
double aDuration,
|
||||
uint32_t aPromiseID)
|
||||
{
|
||||
// TODO: Bug 1305892
|
||||
// mSession is available after VRDisplay is created
|
||||
// at GetHMDs().
|
||||
if (!mSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<impl::VRControllerOculus> controller = mOculusController[aControllerIdx];
|
||||
MOZ_ASSERT(controller);
|
||||
|
||||
controller->VibrateHaptic(mSession, aHapticIndex, aIntensity, aDuration, aPromiseID);
|
||||
}
|
||||
|
||||
void
|
||||
VRSystemManagerOculus::StopVibrateHaptic(uint32_t aControllerIdx)
|
||||
{
|
||||
// mSession is available after VRDisplay is created
|
||||
// at GetHMDs().
|
||||
if (!mSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<impl::VRControllerOculus> controller = mOculusController[aControllerIdx];
|
||||
MOZ_ASSERT(controller);
|
||||
|
||||
controller->StopVibrateHaptic();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -103,15 +103,31 @@ public:
|
|||
void SetIndexTrigger(float aValue);
|
||||
float GetHandTrigger();
|
||||
void SetHandTrigger(float aValue);
|
||||
void VibrateHaptic(ovrSession aSession,
|
||||
uint32_t aHapticIndex,
|
||||
double aIntensity,
|
||||
double aDuration,
|
||||
uint32_t aPromiseID);
|
||||
void StopVibrateHaptic();
|
||||
|
||||
protected:
|
||||
virtual ~VRControllerOculus();
|
||||
|
||||
private:
|
||||
void UpdateVibrateHaptic(ovrSession aSession,
|
||||
uint32_t aHapticIndex,
|
||||
double aIntensity,
|
||||
double aDuration,
|
||||
uint64_t aVibrateIndex,
|
||||
uint32_t aPromiseID);
|
||||
void VibrateHapticComplete(ovrSession aSession, uint32_t aPromiseID, bool aStop);
|
||||
|
||||
float mAxisMove[static_cast<uint32_t>(
|
||||
OculusControllerAxisType::NumVRControllerAxisType)];
|
||||
float mIndexTrigger;
|
||||
float mHandTrigger;
|
||||
nsCOMPtr<nsIThread> mVibrateThread;
|
||||
Atomic<bool> mIsVibrateStopped;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
|
|
@ -462,11 +462,11 @@ VRControllerOpenVR::UpdateVibrateHaptic(vr::IVRSystem* aVRSystem,
|
|||
return;
|
||||
}
|
||||
|
||||
double duration = (aIntensity == 0) ? 0 : aDuration;
|
||||
const double duration = (aIntensity == 0) ? 0 : aDuration;
|
||||
// We expect OpenVR to vibrate for 5 ms, but we found it only response the
|
||||
// commend ~ 3.9 ms. For duration time longer than 3.9 ms, we separate them
|
||||
// to a loop of 3.9 ms for make users feel that is a continuous events.
|
||||
uint32_t microSec = (duration < 3.9 ? duration : 3.9) * 1000 * aIntensity;
|
||||
const uint32_t microSec = (duration < 3.9 ? duration : 3.9) * 1000 * aIntensity;
|
||||
aVRSystem->TriggerHapticPulse(GetTrackedIndex(),
|
||||
aHapticIndex, microSec);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче