Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include <math.h>
7 :
8 : #include "prlink.h"
9 : #include "prenv.h"
10 : #include "gfxPrefs.h"
11 : #include "nsString.h"
12 : #include "mozilla/Preferences.h"
13 :
14 : #include "mozilla/gfx/Quaternion.h"
15 :
16 : #ifdef XP_WIN
17 : #include "CompositorD3D11.h"
18 : #include "TextureD3D11.h"
19 : #endif // XP_WIN
20 :
21 : #include "gfxVROpenVR.h"
22 : #include "VRManager.h"
23 :
24 : #include "nsServiceManagerUtils.h"
25 : #include "nsIScreenManager.h"
26 :
27 : #include "mozilla/layers/CompositorThread.h"
28 : #include "mozilla/dom/GamepadEventTypes.h"
29 : #include "mozilla/dom/GamepadBinding.h"
30 :
31 : #ifndef M_PI
32 : # define M_PI 3.14159265358979323846
33 : #endif
34 :
35 : using namespace mozilla;
36 : using namespace mozilla::gfx;
37 : using namespace mozilla::gfx::impl;
38 : using namespace mozilla::layers;
39 : using namespace mozilla::dom;
40 :
41 : #define BTN_MASK_FROM_ID(_id) \
42 : ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
43 :
44 : static const uint32_t kNumOpenVRHaptcs = 1;
45 :
46 0 : VRDisplayOpenVR::VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
47 : ::vr::IVRChaperone *aVRChaperone,
48 0 : ::vr::IVRCompositor *aVRCompositor)
49 : : VRDisplayHost(VRDeviceType::OpenVR)
50 : , mVRSystem(aVRSystem)
51 : , mVRChaperone(aVRChaperone)
52 : , mVRCompositor(aVRCompositor)
53 0 : , mIsPresenting(false)
54 : {
55 0 : MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayHost);
56 :
57 0 : mDisplayInfo.mDisplayName.AssignLiteral("OpenVR HMD");
58 0 : mDisplayInfo.mIsConnected = true;
59 0 : mDisplayInfo.mIsMounted = false;
60 0 : mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
61 0 : VRDisplayCapabilityFlags::Cap_Orientation |
62 0 : VRDisplayCapabilityFlags::Cap_Position |
63 0 : VRDisplayCapabilityFlags::Cap_External |
64 0 : VRDisplayCapabilityFlags::Cap_Present |
65 0 : VRDisplayCapabilityFlags::Cap_StageParameters;
66 :
67 : ::vr::ETrackedPropertyError err;
68 0 : bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, &err);
69 0 : if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
70 0 : mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
71 : }
72 :
73 0 : mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
74 :
75 : uint32_t w, h;
76 0 : mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
77 0 : mDisplayInfo.mEyeResolution.width = w;
78 0 : mDisplayInfo.mEyeResolution.height = h;
79 :
80 : // SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
81 0 : for (uint32_t eye = 0; eye < 2; ++eye) {
82 : // get l/r/t/b clip plane coordinates
83 : float l, r, t, b;
84 0 : mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &l, &r, &t, &b);
85 0 : mDisplayInfo.mEyeFOV[eye].SetFromTanRadians(-t, r, b, -l);
86 :
87 0 : ::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
88 :
89 0 : mDisplayInfo.mEyeTranslation[eye].x = eyeToHead.m[0][3];
90 0 : mDisplayInfo.mEyeTranslation[eye].y = eyeToHead.m[1][3];
91 0 : mDisplayInfo.mEyeTranslation[eye].z = eyeToHead.m[2][3];
92 : }
93 :
94 0 : UpdateStageParameters();
95 0 : }
96 :
97 0 : VRDisplayOpenVR::~VRDisplayOpenVR()
98 : {
99 0 : Destroy();
100 0 : MOZ_COUNT_DTOR_INHERITED(VRDisplayOpenVR, VRDisplayHost);
101 0 : }
102 :
103 : void
104 0 : VRDisplayOpenVR::Destroy()
105 : {
106 0 : StopPresentation();
107 0 : ::vr::VR_Shutdown();
108 0 : }
109 :
110 : void
111 0 : VRDisplayOpenVR::UpdateStageParameters()
112 : {
113 0 : float sizeX = 0.0f;
114 0 : float sizeZ = 0.0f;
115 0 : if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
116 0 : ::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
117 0 : mDisplayInfo.mStageSize.width = sizeX;
118 0 : mDisplayInfo.mStageSize.height = sizeZ;
119 :
120 0 : mDisplayInfo.mSittingToStandingTransform._11 = t.m[0][0];
121 0 : mDisplayInfo.mSittingToStandingTransform._12 = t.m[1][0];
122 0 : mDisplayInfo.mSittingToStandingTransform._13 = t.m[2][0];
123 0 : mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
124 :
125 0 : mDisplayInfo.mSittingToStandingTransform._21 = t.m[0][1];
126 0 : mDisplayInfo.mSittingToStandingTransform._22 = t.m[1][1];
127 0 : mDisplayInfo.mSittingToStandingTransform._23 = t.m[2][1];
128 0 : mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
129 :
130 0 : mDisplayInfo.mSittingToStandingTransform._31 = t.m[0][2];
131 0 : mDisplayInfo.mSittingToStandingTransform._32 = t.m[1][2];
132 0 : mDisplayInfo.mSittingToStandingTransform._33 = t.m[2][2];
133 0 : mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
134 :
135 0 : mDisplayInfo.mSittingToStandingTransform._41 = t.m[0][3];
136 0 : mDisplayInfo.mSittingToStandingTransform._42 = t.m[1][3];
137 0 : mDisplayInfo.mSittingToStandingTransform._43 = t.m[2][3];
138 0 : mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
139 : } else {
140 : // If we fail, fall back to reasonable defaults.
141 : // 1m x 1m space, 0.75m high in seated position
142 :
143 0 : mDisplayInfo.mStageSize.width = 1.0f;
144 0 : mDisplayInfo.mStageSize.height = 1.0f;
145 :
146 0 : mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
147 0 : mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
148 0 : mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
149 0 : mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
150 :
151 0 : mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
152 0 : mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
153 0 : mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
154 0 : mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
155 :
156 0 : mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
157 0 : mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
158 0 : mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
159 0 : mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
160 :
161 0 : mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
162 0 : mDisplayInfo.mSittingToStandingTransform._42 = 0.75f;
163 0 : mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
164 0 : mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
165 : }
166 0 : }
167 :
168 : void
169 0 : VRDisplayOpenVR::ZeroSensor()
170 : {
171 0 : mVRSystem->ResetSeatedZeroPose();
172 0 : UpdateStageParameters();
173 0 : }
174 :
175 : void
176 0 : VRDisplayOpenVR::PollEvents()
177 : {
178 : ::vr::VREvent_t event;
179 0 : while (mVRSystem->PollNextEvent(&event, sizeof(event))) {
180 0 : switch (event.eventType) {
181 : case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
182 0 : mDisplayInfo.mIsMounted = true;
183 0 : break;
184 : case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
185 0 : mDisplayInfo.mIsMounted = false;
186 0 : break;
187 : case ::vr::EVREventType::VREvent_DriverRequestedQuit:
188 : case ::vr::EVREventType::VREvent_Quit:
189 : case ::vr::EVREventType::VREvent_ProcessQuit:
190 : case ::vr::EVREventType::VREvent_QuitAcknowledged:
191 : case ::vr::EVREventType::VREvent_QuitAborted_UserPrompt:
192 0 : mDisplayInfo.mIsConnected = false;
193 0 : break;
194 : default:
195 : // ignore
196 0 : break;
197 : }
198 : }
199 0 : }
200 :
201 : VRHMDSensorState
202 0 : VRDisplayOpenVR::GetSensorState()
203 : {
204 0 : PollEvents();
205 :
206 : ::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
207 : // Note: We *must* call WaitGetPoses in order for any rendering to happen at all
208 0 : mVRCompositor->WaitGetPoses(poses, ::vr::k_unMaxTrackedDeviceCount, nullptr, 0);
209 :
210 0 : VRHMDSensorState result;
211 :
212 : ::vr::Compositor_FrameTiming timing;
213 0 : timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
214 0 : if (mVRCompositor->GetFrameTiming(&timing)) {
215 0 : result.timestamp = timing.m_flSystemTimeInSeconds;
216 : } else {
217 : // This should not happen, but log it just in case
218 0 : NS_WARNING("OpenVR - IVRCompositor::GetFrameTiming failed");
219 : }
220 :
221 0 : if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
222 0 : poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
223 0 : poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
224 : {
225 0 : const ::vr::TrackedDevicePose_t& pose = poses[::vr::k_unTrackedDeviceIndex_Hmd];
226 :
227 0 : gfx::Matrix4x4 m;
228 : // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
229 : // because of its arrangement, we can copy the 12 elements in and
230 : // then transpose them to the right place. We do this so we can
231 : // pull out a Quaternion.
232 0 : memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(float) * 12);
233 0 : m.Transpose();
234 :
235 0 : gfx::Quaternion rot;
236 0 : rot.SetFromRotationMatrix(m);
237 0 : rot.Invert();
238 :
239 0 : result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
240 0 : result.orientation[0] = rot.x;
241 0 : result.orientation[1] = rot.y;
242 0 : result.orientation[2] = rot.z;
243 0 : result.orientation[3] = rot.w;
244 0 : result.angularVelocity[0] = pose.vAngularVelocity.v[0];
245 0 : result.angularVelocity[1] = pose.vAngularVelocity.v[1];
246 0 : result.angularVelocity[2] = pose.vAngularVelocity.v[2];
247 :
248 0 : result.flags |= VRDisplayCapabilityFlags::Cap_Position;
249 0 : result.position[0] = m._41;
250 0 : result.position[1] = m._42;
251 0 : result.position[2] = m._43;
252 0 : result.linearVelocity[0] = pose.vVelocity.v[0];
253 0 : result.linearVelocity[1] = pose.vVelocity.v[1];
254 0 : result.linearVelocity[2] = pose.vVelocity.v[2];
255 : }
256 :
257 0 : result.inputFrameID = mDisplayInfo.mFrameId;
258 0 : return result;
259 : }
260 :
261 : void
262 0 : VRDisplayOpenVR::StartPresentation()
263 : {
264 0 : if (mIsPresenting) {
265 0 : return;
266 : }
267 0 : mIsPresenting = true;
268 : }
269 :
270 : void
271 0 : VRDisplayOpenVR::StopPresentation()
272 : {
273 0 : if (!mIsPresenting) {
274 0 : return;
275 : }
276 :
277 0 : mVRCompositor->ClearLastSubmittedFrame();
278 :
279 0 : mIsPresenting = false;
280 : }
281 :
282 :
283 : #if defined(XP_WIN)
284 :
285 : bool
286 : VRDisplayOpenVR::SubmitFrame(TextureSourceD3D11* aSource,
287 : const IntSize& aSize,
288 : const gfx::Rect& aLeftEyeRect,
289 : const gfx::Rect& aRightEyeRect)
290 : {
291 : if (!mIsPresenting) {
292 : return false;
293 : }
294 :
295 : ::vr::Texture_t tex;
296 : tex.handle = (void *)aSource->GetD3D11Texture();
297 : tex.eType = ::vr::ETextureType::TextureType_DirectX;
298 : tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
299 :
300 : ::vr::VRTextureBounds_t bounds;
301 : bounds.uMin = aLeftEyeRect.x;
302 : bounds.vMin = 1.0 - aLeftEyeRect.y;
303 : bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
304 : bounds.vMax = 1.0 - aLeftEyeRect.y - aLeftEyeRect.height;
305 :
306 : ::vr::EVRCompositorError err;
307 : err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
308 : if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
309 : printf_stderr("OpenVR Compositor Submit() failed.\n");
310 : }
311 :
312 : bounds.uMin = aRightEyeRect.x;
313 : bounds.vMin = 1.0 - aRightEyeRect.y;
314 : bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
315 : bounds.vMax = 1.0 - aRightEyeRect.y - aRightEyeRect.height;
316 :
317 : err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
318 : if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
319 : printf_stderr("OpenVR Compositor Submit() failed.\n");
320 : }
321 :
322 : mVRCompositor->PostPresentHandoff();
323 :
324 : return true;
325 : }
326 :
327 : #endif
328 :
329 : void
330 0 : VRDisplayOpenVR::NotifyVSync()
331 : {
332 : // We update mIsConneced once per frame.
333 0 : mDisplayInfo.mIsConnected = ::vr::VR_IsHmdPresent();
334 :
335 : // Make sure we respond to OpenVR events even when not presenting
336 0 : PollEvents();
337 :
338 0 : VRDisplayHost::NotifyVSync();
339 0 : }
340 :
341 0 : VRControllerOpenVR::VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aNumButtons,
342 0 : uint32_t aNumAxes, ::vr::ETrackedDeviceClass aDeviceType)
343 : : VRControllerHost(VRDeviceType::OpenVR)
344 : , mTrigger(0)
345 : , mAxisMove(aNumAxes)
346 : , mVibrateThread(nullptr)
347 0 : , mIsVibrateStopped(false)
348 : {
349 0 : MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
350 :
351 0 : switch (aDeviceType) {
352 : case ::vr::TrackedDeviceClass_Controller:
353 0 : mControllerInfo.mControllerName.AssignLiteral("OpenVR Gamepad");
354 0 : break;
355 : case ::vr::TrackedDeviceClass_GenericTracker:
356 0 : mControllerInfo.mControllerName.AssignLiteral("OpenVR Tracker");
357 0 : break;
358 : default:
359 0 : MOZ_ASSERT(false);
360 : break;
361 : }
362 :
363 0 : mAxisMove.SetLengthAndRetainStorage(aNumAxes);
364 0 : mControllerInfo.mMappingType = GamepadMappingType::_empty;
365 0 : mControllerInfo.mHand = aHand;
366 0 : mControllerInfo.mNumButtons = aNumButtons;
367 0 : mControllerInfo.mNumAxes = aNumAxes;
368 0 : mControllerInfo.mNumHaptics = kNumOpenVRHaptcs;
369 0 : }
370 :
371 0 : VRControllerOpenVR::~VRControllerOpenVR()
372 : {
373 0 : if (mVibrateThread) {
374 0 : mVibrateThread->Shutdown();
375 0 : mVibrateThread = nullptr;
376 : }
377 :
378 0 : MOZ_COUNT_DTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
379 0 : }
380 :
381 : void
382 0 : VRControllerOpenVR::SetTrackedIndex(uint32_t aTrackedIndex)
383 : {
384 0 : mTrackedIndex = aTrackedIndex;
385 0 : }
386 :
387 : uint32_t
388 0 : VRControllerOpenVR::GetTrackedIndex()
389 : {
390 0 : return mTrackedIndex;
391 : }
392 :
393 : float
394 0 : VRControllerOpenVR::GetAxisMove(uint32_t aAxis)
395 : {
396 0 : return mAxisMove[aAxis];
397 : }
398 :
399 : void
400 0 : VRControllerOpenVR::SetAxisMove(uint32_t aAxis, float aValue)
401 : {
402 0 : mAxisMove[aAxis] = aValue;
403 0 : }
404 :
405 : void
406 0 : VRControllerOpenVR::SetTrigger(float aValue)
407 : {
408 0 : mTrigger = aValue;
409 0 : }
410 :
411 : float
412 0 : VRControllerOpenVR::GetTrigger()
413 : {
414 0 : return mTrigger;
415 : }
416 :
417 : void
418 0 : VRControllerOpenVR::SetHand(dom::GamepadHand aHand)
419 : {
420 0 : mControllerInfo.mHand = aHand;
421 0 : }
422 :
423 : void
424 0 : VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
425 : uint32_t aHapticIndex,
426 : double aIntensity,
427 : double aDuration,
428 : uint64_t aVibrateIndex,
429 : uint32_t aPromiseID)
430 : {
431 : // UpdateVibrateHaptic() only can be called by mVibrateThread
432 0 : MOZ_ASSERT(mVibrateThread == NS_GetCurrentThread());
433 :
434 : // It has been interrupted by loss focus.
435 0 : if (mIsVibrateStopped) {
436 0 : VibrateHapticComplete(aPromiseID);
437 0 : return;
438 : }
439 : // Avoid the previous vibrate event to override the new one.
440 0 : if (mVibrateIndex != aVibrateIndex) {
441 0 : VibrateHapticComplete(aPromiseID);
442 0 : return;
443 : }
444 :
445 0 : const double duration = (aIntensity == 0) ? 0 : aDuration;
446 : // We expect OpenVR to vibrate for 5 ms, but we found it only response the
447 : // commend ~ 3.9 ms. For duration time longer than 3.9 ms, we separate them
448 : // to a loop of 3.9 ms for make users feel that is a continuous events.
449 0 : const uint32_t microSec = (duration < 3.9 ? duration : 3.9) * 1000 * aIntensity;
450 0 : aVRSystem->TriggerHapticPulse(GetTrackedIndex(),
451 0 : aHapticIndex, microSec);
452 :
453 : // In OpenVR spec, it mentions TriggerHapticPulse() may not trigger another haptic pulse
454 : // on this controller and axis combination for 5ms.
455 0 : const double kVibrateRate = 5.0;
456 0 : if (duration >= kVibrateRate) {
457 0 : MOZ_ASSERT(mVibrateThread);
458 :
459 : RefPtr<Runnable> runnable =
460 : NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t, uint32_t>
461 0 : ("VRControllerOpenVR::UpdateVibrateHaptic",
462 : this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
463 0 : aHapticIndex, aIntensity, duration - kVibrateRate, aVibrateIndex, aPromiseID);
464 0 : NS_DelayedDispatchToCurrentThread(runnable.forget(), kVibrateRate);
465 : } else {
466 : // The pulse has completed
467 0 : VibrateHapticComplete(aPromiseID);
468 : }
469 : }
470 :
471 : void
472 0 : VRControllerOpenVR::VibrateHapticComplete(uint32_t aPromiseID)
473 : {
474 0 : VRManager *vm = VRManager::Get();
475 :
476 0 : CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<uint32_t>
477 0 : ("VRManager::NotifyVibrateHapticCompleted",
478 0 : vm, &VRManager::NotifyVibrateHapticCompleted, aPromiseID));
479 0 : }
480 :
481 : void
482 0 : VRControllerOpenVR::VibrateHaptic(::vr::IVRSystem* aVRSystem,
483 : uint32_t aHapticIndex,
484 : double aIntensity,
485 : double aDuration,
486 : uint32_t aPromiseID)
487 : {
488 : // Spinning up the haptics thread at the first haptics call.
489 0 : if (!mVibrateThread) {
490 0 : nsresult rv = NS_NewThread(getter_AddRefs(mVibrateThread));
491 0 : MOZ_ASSERT(mVibrateThread);
492 :
493 0 : if (NS_FAILED(rv)) {
494 0 : MOZ_ASSERT(false, "Failed to create async thread.");
495 : }
496 : }
497 0 : ++mVibrateIndex;
498 0 : mIsVibrateStopped = false;
499 :
500 : RefPtr<Runnable> runnable =
501 : NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t, uint32_t>
502 0 : ("VRControllerOpenVR::UpdateVibrateHaptic",
503 : this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
504 0 : aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromiseID);
505 0 : mVibrateThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
506 0 : }
507 :
508 : void
509 0 : VRControllerOpenVR::StopVibrateHaptic()
510 : {
511 0 : mIsVibrateStopped = true;
512 0 : }
513 :
514 0 : VRSystemManagerOpenVR::VRSystemManagerOpenVR()
515 0 : : mVRSystem(nullptr)
516 : {
517 0 : }
518 :
519 : /*static*/ already_AddRefed<VRSystemManagerOpenVR>
520 3 : VRSystemManagerOpenVR::Create()
521 : {
522 3 : MOZ_ASSERT(NS_IsMainThread());
523 :
524 3 : if (!gfxPrefs::VREnabled() || !gfxPrefs::VROpenVREnabled()) {
525 3 : return nullptr;
526 : }
527 :
528 0 : if (!::vr::VR_IsRuntimeInstalled()) {
529 0 : return nullptr;
530 : }
531 :
532 0 : RefPtr<VRSystemManagerOpenVR> manager = new VRSystemManagerOpenVR();
533 0 : return manager.forget();
534 : }
535 :
536 : void
537 0 : VRSystemManagerOpenVR::Destroy()
538 : {
539 0 : Shutdown();
540 0 : }
541 :
542 : void
543 0 : VRSystemManagerOpenVR::Shutdown()
544 : {
545 0 : if (mOpenVRHMD) {
546 0 : mOpenVRHMD = nullptr;
547 : }
548 0 : RemoveControllers();
549 0 : mVRSystem = nullptr;
550 0 : }
551 :
552 : bool
553 0 : VRSystemManagerOpenVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
554 : {
555 0 : if (!::vr::VR_IsHmdPresent() ||
556 0 : (mOpenVRHMD && !mOpenVRHMD->GetIsConnected())) {
557 : // OpenVR runtime could be quit accidentally,
558 : // and we make it re-initialize.
559 0 : mOpenVRHMD = nullptr;
560 0 : mVRSystem = nullptr;
561 0 : } else if (mOpenVRHMD == nullptr) {
562 : ::vr::HmdError err;
563 :
564 0 : ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
565 0 : if (err) {
566 0 : return false;
567 : }
568 :
569 0 : ::vr::IVRSystem *system = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err);
570 0 : if (err || !system) {
571 0 : ::vr::VR_Shutdown();
572 0 : return false;
573 : }
574 0 : ::vr::IVRChaperone *chaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err);
575 0 : if (err || !chaperone) {
576 0 : ::vr::VR_Shutdown();
577 0 : return false;
578 : }
579 0 : ::vr::IVRCompositor *compositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err);
580 0 : if (err || !compositor) {
581 0 : ::vr::VR_Shutdown();
582 0 : return false;
583 : }
584 :
585 0 : mVRSystem = system;
586 0 : mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor);
587 : }
588 :
589 0 : if (mOpenVRHMD) {
590 0 : aHMDResult.AppendElement(mOpenVRHMD);
591 0 : return true;
592 : }
593 0 : return false;
594 : }
595 :
596 : bool
597 0 : VRSystemManagerOpenVR::GetIsPresenting()
598 : {
599 0 : if (mOpenVRHMD) {
600 0 : VRDisplayInfo displayInfo(mOpenVRHMD->GetDisplayInfo());
601 0 : return displayInfo.GetPresentingGroups() != kVRGroupNone;
602 : }
603 :
604 0 : return false;
605 : }
606 :
607 : void
608 0 : VRSystemManagerOpenVR::HandleInput()
609 : {
610 : // mVRSystem is available after VRDisplay is created
611 : // at GetHMDs().
612 0 : if (!mVRSystem) {
613 0 : return;
614 : }
615 :
616 0 : RefPtr<impl::VRControllerOpenVR> controller;
617 : ::vr::VRControllerState_t state;
618 : ::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
619 0 : mVRSystem->GetDeviceToAbsoluteTrackingPose(::vr::TrackingUniverseSeated, 0.0f,
620 0 : poses, ::vr::k_unMaxTrackedDeviceCount);
621 : // Process OpenVR controller state
622 0 : for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
623 0 : uint32_t axisIdx = 0;
624 0 : uint32_t buttonIdx = 0;
625 0 : controller = mOpenVRController[i];
626 0 : const uint32_t trackedIndex = controller->GetTrackedIndex();
627 :
628 0 : MOZ_ASSERT(mVRSystem->GetTrackedDeviceClass(trackedIndex)
629 : == ::vr::TrackedDeviceClass_Controller ||
630 : mVRSystem->GetTrackedDeviceClass(trackedIndex)
631 : == ::vr::TrackedDeviceClass_GenericTracker);
632 :
633 : // Sometimes, OpenVR controllers are not located by HMD at the initial time.
634 : // That makes us have to update the hand info at runtime although switching controllers
635 : // to the other hand does not have new changes at the current OpenVR SDK. But, it makes sense
636 : // to detect hand changing at runtime.
637 0 : const ::vr::ETrackedControllerRole role = mVRSystem->
638 : GetControllerRoleForTrackedDeviceIndex(
639 0 : trackedIndex);
640 0 : const dom::GamepadHand hand = GetGamepadHandFromControllerRole(role);
641 0 : if (hand != controller->GetHand()) {
642 0 : controller->SetHand(hand);
643 0 : NewHandChangeEvent(i, hand);
644 : }
645 :
646 0 : if (mVRSystem->GetControllerState(trackedIndex, &state, sizeof(state))) {
647 0 : for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
648 0 : const uint32_t axisType = mVRSystem->GetInt32TrackedDeviceProperty(
649 : trackedIndex,
650 : static_cast<::vr::TrackedDeviceProperty>(
651 0 : ::vr::Prop_Axis0Type_Int32 + j));
652 0 : switch (axisType) {
653 : case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
654 : case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
655 0 : HandleAxisMove(i, axisIdx,
656 0 : state.rAxis[j].x);
657 0 : ++axisIdx;
658 0 : HandleAxisMove(i, axisIdx,
659 0 : state.rAxis[j].y);
660 0 : ++axisIdx;
661 0 : HandleButtonPress(i, buttonIdx,
662 : ::vr::ButtonMaskFromId(
663 0 : static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j)),
664 0 : state.ulButtonPressed, state.ulButtonTouched);
665 0 : ++buttonIdx;
666 0 : break;
667 : case vr::EVRControllerAxisType::k_eControllerAxis_Trigger:
668 0 : HandleTriggerPress(i, buttonIdx, state.rAxis[j].x);
669 0 : ++buttonIdx;
670 0 : break;
671 : }
672 : }
673 0 : MOZ_ASSERT(axisIdx ==
674 : controller->GetControllerInfo().GetNumAxes());
675 :
676 0 : const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty(
677 0 : trackedIndex, ::vr::Prop_SupportedButtons_Uint64);
678 0 : if (supportedButtons &
679 0 : BTN_MASK_FROM_ID(k_EButton_A)) {
680 0 : HandleButtonPress(i, buttonIdx,
681 : BTN_MASK_FROM_ID(k_EButton_A),
682 0 : state.ulButtonPressed, state.ulButtonTouched);
683 0 : ++buttonIdx;
684 : }
685 0 : if (supportedButtons &
686 0 : BTN_MASK_FROM_ID(k_EButton_Grip)) {
687 0 : HandleButtonPress(i, buttonIdx,
688 : BTN_MASK_FROM_ID(k_EButton_Grip),
689 0 : state.ulButtonPressed, state.ulButtonTouched);
690 0 : ++buttonIdx;
691 : }
692 0 : if (supportedButtons &
693 0 : BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
694 0 : HandleButtonPress(i, buttonIdx,
695 : BTN_MASK_FROM_ID(k_EButton_ApplicationMenu),
696 0 : state.ulButtonPressed, state.ulButtonTouched);
697 0 : ++buttonIdx;
698 : }
699 0 : if (supportedButtons &
700 0 : BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
701 0 : HandleButtonPress(i, buttonIdx,
702 : BTN_MASK_FROM_ID(k_EButton_DPad_Left),
703 0 : state.ulButtonPressed, state.ulButtonTouched);
704 0 : ++buttonIdx;
705 : }
706 0 : if (supportedButtons &
707 0 : BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
708 0 : HandleButtonPress(i, buttonIdx,
709 : BTN_MASK_FROM_ID(k_EButton_DPad_Up),
710 0 : state.ulButtonPressed, state.ulButtonTouched);
711 0 : ++buttonIdx;
712 : }
713 0 : if (supportedButtons &
714 0 : BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
715 0 : HandleButtonPress(i, buttonIdx,
716 : BTN_MASK_FROM_ID(k_EButton_DPad_Right),
717 0 : state.ulButtonPressed, state.ulButtonTouched);
718 0 : ++buttonIdx;
719 : }
720 0 : if (supportedButtons &
721 0 : BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
722 0 : HandleButtonPress(i, buttonIdx,
723 : BTN_MASK_FROM_ID(k_EButton_DPad_Down),
724 0 : state.ulButtonPressed, state.ulButtonTouched);
725 0 : ++buttonIdx;
726 : }
727 0 : MOZ_ASSERT(buttonIdx ==
728 : controller->GetControllerInfo().GetNumButtons());
729 0 : controller->SetButtonPressed(state.ulButtonPressed);
730 0 : controller->SetButtonTouched(state.ulButtonTouched);
731 :
732 : // Start to process pose
733 0 : const ::vr::TrackedDevicePose_t& pose = poses[trackedIndex];
734 0 : GamepadPoseState poseState;
735 :
736 0 : if (pose.bDeviceIsConnected) {
737 0 : poseState.flags |= (GamepadCapabilityFlags::Cap_Orientation |
738 0 : GamepadCapabilityFlags::Cap_Position);
739 : }
740 :
741 0 : if (pose.bPoseIsValid &&
742 0 : pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
743 0 : gfx::Matrix4x4 m;
744 :
745 : // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
746 : // because of its arrangement, we can copy the 12 elements in and
747 : // then transpose them to the right place. We do this so we can
748 : // pull out a Quaternion.
749 0 : memcpy(&m.components, &pose.mDeviceToAbsoluteTracking, sizeof(float) * 12);
750 0 : m.Transpose();
751 :
752 0 : gfx::Quaternion rot;
753 0 : rot.SetFromRotationMatrix(m);
754 0 : rot.Invert();
755 :
756 0 : poseState.orientation[0] = rot.x;
757 0 : poseState.orientation[1] = rot.y;
758 0 : poseState.orientation[2] = rot.z;
759 0 : poseState.orientation[3] = rot.w;
760 0 : poseState.angularVelocity[0] = pose.vAngularVelocity.v[0];
761 0 : poseState.angularVelocity[1] = pose.vAngularVelocity.v[1];
762 0 : poseState.angularVelocity[2] = pose.vAngularVelocity.v[2];
763 0 : poseState.isOrientationValid = true;
764 :
765 0 : poseState.position[0] = m._41;
766 0 : poseState.position[1] = m._42;
767 0 : poseState.position[2] = m._43;
768 0 : poseState.linearVelocity[0] = pose.vVelocity.v[0];
769 0 : poseState.linearVelocity[1] = pose.vVelocity.v[1];
770 0 : poseState.linearVelocity[2] = pose.vVelocity.v[2];
771 0 : poseState.isPositionValid = true;
772 : }
773 0 : HandlePoseTracking(i, poseState, controller);
774 : }
775 : }
776 : }
777 :
778 : void
779 0 : VRSystemManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx,
780 : uint32_t aButton,
781 : uint64_t aButtonMask,
782 : uint64_t aButtonPressed,
783 : uint64_t aButtonTouched)
784 : {
785 0 : RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
786 0 : MOZ_ASSERT(controller);
787 0 : const uint64_t pressedDiff = (controller->GetButtonPressed() ^ aButtonPressed);
788 0 : const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
789 :
790 0 : if (!pressedDiff && !touchedDiff) {
791 0 : return;
792 : }
793 :
794 0 : if (pressedDiff & aButtonMask ||
795 0 : touchedDiff & aButtonMask) {
796 : // diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or
797 : // touched event, otherwise it is an old event and needs to notify
798 : // the button has been released.
799 0 : NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed,
800 0 : aButtonMask & aButtonTouched,
801 0 : (aButtonMask & aButtonPressed) ? 1.0L : 0.0L);
802 : }
803 : }
804 :
805 : void
806 0 : VRSystemManagerOpenVR::HandleTriggerPress(uint32_t aControllerIdx,
807 : uint32_t aButton,
808 : float aValue)
809 : {
810 0 : RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
811 0 : MOZ_ASSERT(controller);
812 0 : const float oldValue = controller->GetTrigger();
813 : // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
814 : // We prefer to let developers to set their own threshold for the adjustment.
815 : // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
816 : // we just check the button value is larger than the threshold value or not.
817 0 : const float threshold = gfxPrefs::VRControllerTriggerThreshold();
818 :
819 : // Avoid sending duplicated events in IPC channels.
820 0 : if (oldValue != aValue) {
821 0 : NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
822 0 : aValue > threshold, aValue);
823 0 : controller->SetTrigger(aValue);
824 : }
825 0 : }
826 :
827 : void
828 0 : VRSystemManagerOpenVR::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
829 : float aValue)
830 : {
831 0 : RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
832 0 : MOZ_ASSERT(controller);
833 :
834 0 : if (controller->GetAxisMove(aAxis) != aValue) {
835 0 : NewAxisMove(aControllerIdx, aAxis, aValue);
836 0 : controller->SetAxisMove(aAxis, aValue);
837 : }
838 0 : }
839 :
840 : void
841 0 : VRSystemManagerOpenVR::HandlePoseTracking(uint32_t aControllerIdx,
842 : const GamepadPoseState& aPose,
843 : VRControllerHost* aController)
844 : {
845 0 : MOZ_ASSERT(aController);
846 0 : if (aPose != aController->GetPose()) {
847 0 : aController->SetPose(aPose);
848 0 : NewPoseState(aControllerIdx, aPose);
849 : }
850 0 : }
851 :
852 : dom::GamepadHand
853 0 : VRSystemManagerOpenVR::GetGamepadHandFromControllerRole(
854 : ::vr::ETrackedControllerRole aRole)
855 : {
856 : dom::GamepadHand hand;
857 :
858 0 : switch(aRole) {
859 : case ::vr::ETrackedControllerRole::TrackedControllerRole_Invalid:
860 0 : hand = dom::GamepadHand::_empty;
861 0 : break;
862 : case ::vr::ETrackedControllerRole::TrackedControllerRole_LeftHand:
863 0 : hand = dom::GamepadHand::Left;
864 0 : break;
865 : case ::vr::ETrackedControllerRole::TrackedControllerRole_RightHand:
866 0 : hand = dom::GamepadHand::Right;
867 0 : break;
868 : default:
869 0 : MOZ_ASSERT(false);
870 : break;
871 : }
872 :
873 0 : return hand;
874 : }
875 :
876 : void
877 0 : VRSystemManagerOpenVR::VibrateHaptic(uint32_t aControllerIdx,
878 : uint32_t aHapticIndex,
879 : double aIntensity,
880 : double aDuration,
881 : uint32_t aPromiseID)
882 : {
883 : // mVRSystem is available after VRDisplay is created
884 : // at GetHMDs().
885 0 : if (!mVRSystem) {
886 0 : return;
887 : }
888 :
889 0 : RefPtr<impl::VRControllerOpenVR> controller = mOpenVRController[aControllerIdx];
890 0 : MOZ_ASSERT(controller);
891 :
892 0 : controller->VibrateHaptic(mVRSystem, aHapticIndex, aIntensity, aDuration, aPromiseID);
893 : }
894 :
895 : void
896 0 : VRSystemManagerOpenVR::StopVibrateHaptic(uint32_t aControllerIdx)
897 : {
898 : // mVRSystem is available after VRDisplay is created
899 : // at GetHMDs().
900 0 : if (!mVRSystem) {
901 0 : return;
902 : }
903 :
904 0 : RefPtr<impl::VRControllerOpenVR> controller = mOpenVRController[aControllerIdx];
905 0 : MOZ_ASSERT(controller);
906 :
907 0 : controller->StopVibrateHaptic();
908 : }
909 :
910 : void
911 0 : VRSystemManagerOpenVR::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
912 : {
913 0 : aControllerResult.Clear();
914 0 : for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
915 0 : aControllerResult.AppendElement(mOpenVRController[i]);
916 : }
917 0 : }
918 :
919 : void
920 0 : VRSystemManagerOpenVR::ScanForControllers()
921 : {
922 : // mVRSystem is available after VRDisplay is created
923 : // at GetHMDs().
924 0 : if (!mVRSystem) {
925 0 : return;
926 : }
927 :
928 : ::vr::TrackedDeviceIndex_t trackedIndexArray[::vr::k_unMaxTrackedDeviceCount];
929 0 : uint32_t newControllerCount = 0;
930 : // Basically, we would have HMDs in the tracked devices,
931 : // but we are just interested in the controllers.
932 0 : for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
933 0 : trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) {
934 :
935 0 : if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
936 0 : continue;
937 : }
938 :
939 0 : const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
940 0 : GetTrackedDeviceClass(trackedDevice);
941 0 : if (deviceType != ::vr::TrackedDeviceClass_Controller
942 0 : && deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
943 0 : continue;
944 : }
945 :
946 0 : trackedIndexArray[newControllerCount] = trackedDevice;
947 0 : ++newControllerCount;
948 : }
949 :
950 0 : if (newControllerCount != mControllerCount) {
951 0 : RemoveControllers();
952 :
953 : // Re-adding controllers to VRControllerManager.
954 0 : for (::vr::TrackedDeviceIndex_t i = 0; i < newControllerCount; ++i) {
955 0 : const ::vr::TrackedDeviceIndex_t trackedDevice = trackedIndexArray[i];
956 0 : const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
957 0 : GetTrackedDeviceClass(trackedDevice);
958 0 : const ::vr::ETrackedControllerRole role = mVRSystem->
959 : GetControllerRoleForTrackedDeviceIndex(
960 0 : trackedDevice);
961 0 : const GamepadHand hand = GetGamepadHandFromControllerRole(role);
962 0 : uint32_t numButtons = 0;
963 0 : uint32_t numAxes = 0;
964 :
965 : // Scan the axes that the controllers support
966 0 : for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
967 0 : const uint32_t supportAxis = mVRSystem->GetInt32TrackedDeviceProperty(trackedDevice,
968 : static_cast<vr::TrackedDeviceProperty>(
969 0 : ::vr::Prop_Axis0Type_Int32 + j));
970 0 : switch (supportAxis) {
971 : case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
972 : case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
973 0 : numAxes += 2; // It has x and y axes.
974 0 : ++numButtons;
975 0 : break;
976 : case ::vr::k_eControllerAxis_Trigger:
977 0 : ++numButtons;
978 0 : break;
979 : }
980 : }
981 :
982 : // Scan the buttons that the controllers support
983 0 : const uint64_t supportButtons = mVRSystem->GetUint64TrackedDeviceProperty(
984 0 : trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
985 0 : if (supportButtons &
986 0 : BTN_MASK_FROM_ID(k_EButton_A)) {
987 0 : ++numButtons;
988 : }
989 0 : if (supportButtons &
990 0 : BTN_MASK_FROM_ID(k_EButton_Grip)) {
991 0 : ++numButtons;
992 : }
993 0 : if (supportButtons &
994 0 : BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
995 0 : ++numButtons;
996 : }
997 0 : if (supportButtons &
998 0 : BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
999 0 : ++numButtons;
1000 : }
1001 0 : if (supportButtons &
1002 0 : BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
1003 0 : ++numButtons;
1004 : }
1005 0 : if (supportButtons &
1006 0 : BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
1007 0 : ++numButtons;
1008 : }
1009 0 : if (supportButtons &
1010 0 : BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
1011 0 : ++numButtons;
1012 : }
1013 :
1014 : RefPtr<VRControllerOpenVR> openVRController =
1015 0 : new VRControllerOpenVR(hand, numButtons, numAxes, deviceType);
1016 0 : openVRController->SetTrackedIndex(trackedDevice);
1017 0 : mOpenVRController.AppendElement(openVRController);
1018 :
1019 : // Not already present, add it.
1020 0 : AddGamepad(openVRController->GetControllerInfo());
1021 0 : ++mControllerCount;
1022 : }
1023 : }
1024 : }
1025 :
1026 : void
1027 0 : VRSystemManagerOpenVR::RemoveControllers()
1028 : {
1029 : // The controller count is changed, removing the existing gamepads first.
1030 0 : for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
1031 0 : RemoveGamepad(i);
1032 : }
1033 0 : mOpenVRController.Clear();
1034 0 : mControllerCount = 0;
1035 0 : }
1036 :
|