Oxygen Engine
Modern C++ 3D Engine using OpenGL
Loading...
Searching...
No Matches
vr.h
1#ifndef OE_EXT_VR_H
2#define OE_EXT_VR_H
3
4#include <string>
5#include <vector>
6#include <memory>
7#include <OxygenEngine/util/non_copyable.h>
8#include <OxygenEngine/core/window.h>
9#include <OxygenEngine/render/texture.h>
10
11#include <glm/mat4x4.hpp>
12
13// OpenVR Forward declarations
14namespace vr
15{
16 class IVRSystem;
17 class IVROverlay;
18
19 typedef uint64_t VROverlayHandle_t;
20 typedef uint32_t TrackedDeviceIndex_t;
21}
22
28namespace oe::ext::vr
29{
33 bool canUseVr();
34
35 class TrackedDevice;
36
42 class System
43 {
44 public:
45 static System generateForScene();
46
51
55 std::unique_ptr<TrackedDevice> getTrackedHmdDevice();
56
60 std::unique_ptr<TrackedDevice> getTrackedLeftHandDevice();
61
65 std::unique_ptr<TrackedDevice> getTrackedRightHandDevice();
66
70 std::vector<std::unique_ptr<TrackedDevice>> getAllTrackedDevices();
71
72 friend class TrackedDevice;
73
74 private:
75 ::vr::IVRSystem* _raw_system = nullptr;
76
77 System(const uint32_t& type);
78 };
79
84 {
85 public:
86 TrackedDevice(System* parent);
87
91 bool isValid();
92
103 std::string getModel();
104
108 std::string getSerialNumber();
109
114
120 bool isHmd();
121
127
128 uint64_t getFirmwareVersion();
129 int32_t getUsageTimeSinceFirstStarted();
130
131 private:
132 ::vr::TrackedDeviceIndex_t _handle = 0;
133 System* _parent = nullptr;
134
135 friend class System;
136 friend class Overlay;
137
138 //protected:
139 // TrackedDevice() {};
140 };
141
146 {
147
148 };
149
153 class Overlay : public Application
154 {
155 public:
156 Overlay(const std::string& key, const std::string& name = "");
157 ~Overlay();
158
164 void setRatio(const float& width);
165
169 void useStereoRendering(const bool enable = true);
170
175
179 void handleEvents(oe::core::EventHandler& event_handler, const glm::ivec2& window_dimensions);
180
185 void submitTexture(const std::shared_ptr<oe::render::Texture>& texture);
186
191 void bindToDevice(const std::unique_ptr<TrackedDevice>& device, const glm::mat4& matrix = glm::mat4(1.0));
192
193 private:
194 ::vr::IVROverlay* _manager = nullptr;
195 ::vr::VROverlayHandle_t _handle = 0;
196 };
197
201 class Scene : public Application
202 {
203 // TODO
204 };
205}
206#endif
207
208#ifdef OE_EXT_VR_IMPLEMENTATION
209#include <map>
210#include <stdexcept>
211#include <openvr/openvr.h>
212#include <GLFW/glfw3.h>
213
214/*static glm::mat4 convertHmd34ToMat4(const ::vr::HmdMatrix34_t& matrix)
215{
216 return glm::mat4(
217 matrix.m[0][0], matrix.m[0][1], matrix.m[0][2], matrix.m[0][3],
218 matrix.m[1][0], matrix.m[1][1], matrix.m[1][2], matrix.m[2][3],
219 matrix.m[2][0], matrix.m[2][1], matrix.m[2][2], matrix.m[2][3],
220 0, 0, 0, 1
221 );
222}*/
223
224static ::vr::HmdMatrix34_t convertMat4ToHmd34(const glm::mat4& matrix)
225{
226 return {
227 matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
228 matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
229 matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
230 };
231}
232
234{
235 return ::vr::VR_IsHmdPresent() && ::vr::VR_IsRuntimeInstalled();
236}
237
238oe::ext::vr::System::System(const uint32_t& type)
239{
240 ::vr::EVRInitError err = ::vr::VRInitError_None;
241
242 _raw_system = ::vr::VR_Init(&err, (::vr::EVRApplicationType)type);
243
244 if (err != ::vr::VRInitError_None)
245 throw std::runtime_error(::vr::VR_GetVRInitErrorAsEnglishDescription(err));
246
247 if (!::vr::VRCompositor())
248 throw std::runtime_error("Unable to initialize VR compositor!\n ");
249}
250
251oe::ext::vr::System oe::ext::vr::System::generateForScene()
252{
253 return {::vr::VRApplication_Scene};
254}
255
257{
258 return {::vr::VRApplication_Overlay};
259}
260
261std::unique_ptr<oe::ext::vr::TrackedDevice> oe::ext::vr::System::getTrackedHmdDevice()
262{
263 std::unique_ptr<oe::ext::vr::TrackedDevice> result = std::make_unique<TrackedDevice>(this);
264
265 result->_handle = ::vr::k_unTrackedDeviceIndex_Hmd;
266
267 return result;
268}
269
270std::unique_ptr<oe::ext::vr::TrackedDevice> oe::ext::vr::System::getTrackedLeftHandDevice()
271{
272 std::unique_ptr<oe::ext::vr::TrackedDevice> result;
273
274 ::vr::TrackedDeviceIndex_t id = _raw_system->GetTrackedDeviceIndexForControllerRole(::vr::TrackedControllerRole_LeftHand);
275
276 if (id != ::vr::k_unTrackedDeviceIndexInvalid && id != ::vr::k_unTrackedDeviceIndex_Hmd)
277 {
278 result = std::make_unique<TrackedDevice>(this);
279
280 result->_handle = id;
281 }
282
283 return result;
284}
285
286std::unique_ptr<oe::ext::vr::TrackedDevice> oe::ext::vr::System::getTrackedRightHandDevice()
287{
288 std::unique_ptr<oe::ext::vr::TrackedDevice> result;
289
290 ::vr::TrackedDeviceIndex_t id = _raw_system->GetTrackedDeviceIndexForControllerRole(::vr::TrackedControllerRole_RightHand);
291
292 if (id != ::vr::k_unTrackedDeviceIndexInvalid && id != ::vr::k_unTrackedDeviceIndex_Hmd)
293 {
294 result = std::make_unique<TrackedDevice>(this);
295
296 result->_handle = id;
297 }
298
299 return result;
300}
301
302std::vector<std::unique_ptr<oe::ext::vr::TrackedDevice>> oe::ext::vr::System::getAllTrackedDevices()
303{
304 std::vector<std::unique_ptr<TrackedDevice>> result;
305
306 uint32_t maxTrackedDevices = ::vr::k_unMaxTrackedDeviceCount;
307
308 for (unsigned int id = 0; id<maxTrackedDevices; ++id)
309 {
310 if (_raw_system->GetTrackedDeviceClass(id) == ::vr::TrackedDeviceClass_Invalid)
311 continue;
312
313 auto device = std::make_unique<TrackedDevice>(this);
314
315 device->_handle = id;
316
317 result.push_back(std::move(device));
318 }
319
320 return result;
321}
322
324{
325 return _parent->_raw_system->GetTrackedDeviceClass(_handle) != ::vr::TrackedDeviceClass_Invalid;
326}
327
328bool oe::ext::vr::TrackedDevice::isHmd()
329{
330 return _handle == ::vr::k_unTrackedDeviceIndex_Hmd;
331}
332
333#define GENERATE_OPENVR_GETTER(TYPE, METHOD, PROPERTY) \
334TYPE oe::ext::vr::TrackedDevice::METHOD() \
335{ \
336\
337 assert(isValid() && "Device not valid"); \
338 \
339 ::vr::ETrackedPropertyError error = ::vr::TrackedProp_Success; \
340 \
341 TYPE result = getPropertyOpenVR<TYPE>(_parent->_raw_system, _handle, ::vr::PROPERTY, &error); \
342 \
343 /* (Doesn't work : ex. for isWireless an error is thrown but the property is correctly returned)
344 if (false && error != ::vr::TrackedProp_Success) \
345 { \
346 throw std::runtime_error("Cannot fetch property: " + std::to_string(::vr::PROPERTY) + std::string(" ") + _parent->_raw_system->GetPropErrorNameFromEnum(error)); \
347 } */\
348 \
349 return result; \
350}
351
352template <typename T>
353static T getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError *error)
354{
355 throw std::runtime_error("Unknown Type!");
356}
357
358template <>
359bool getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
360{
361 return system->GetBoolTrackedDeviceProperty(handle, prop, error);
362}
363
364template <>
365float getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
366{
367 return system->GetFloatTrackedDeviceProperty(handle, prop, error);
368}
369
370template <>
371int32_t getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
372{
373 return system->GetInt32TrackedDeviceProperty(handle, prop, error);
374}
375
376template <>
377uint64_t getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
378{
379 return system->GetUint64TrackedDeviceProperty(handle, prop, error);
380}
381
382template <>
383std::string getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
384{
385 uint32_t bufferLength = system->GetStringTrackedDeviceProperty(handle, prop, NULL, 0, error);
386
387 if (bufferLength == 0)
388 return "";
389
390 char *buffer = new char[bufferLength];
391 bufferLength = system->GetStringTrackedDeviceProperty(handle, prop, buffer, bufferLength, error );
392
393 std::string result = buffer;
394
395 delete[] buffer;
396
397 return result;
398}
399
400GENERATE_OPENVR_GETTER(std::string, getModel, Prop_ModelNumber_String)
401GENERATE_OPENVR_GETTER(std::string, getSerialNumber, Prop_SerialNumber_String)
402GENERATE_OPENVR_GETTER(bool, isWireless, Prop_DeviceIsWireless_Bool)
403GENERATE_OPENVR_GETTER(bool, isCharging, Prop_DeviceIsCharging_Bool)
404GENERATE_OPENVR_GETTER(float, getBatteryCharge, Prop_DeviceBatteryPercentage_Float)
405GENERATE_OPENVR_GETTER(uint64_t, getFirmwareVersion, Prop_FirmwareVersion_Uint64)
406GENERATE_OPENVR_GETTER(int32_t, getUsageTimeSinceFirstStarted, Prop_EstimatedDeviceFirstUseTime_Int32)
407
408#undef GENERATE_OPENVR_GETTER
409
410oe::ext::vr::Overlay::Overlay(const std::string& key, const std::string& name)
411{
412 _manager = ::vr::VROverlay();
413
414 assert(_manager != nullptr);
415
416 _manager->CreateOverlay(key.c_str(), name != "" ? name.c_str() : key.c_str(), &_handle);
417 _manager->SetOverlayInputMethod(_handle, ::vr::VROverlayInputMethod_Mouse);
418
419 // Set overlay texture bounds to fit an OpenGL texture
420 ::vr::VRTextureBounds_t bounds;
421 bounds.uMin = 1;
422 bounds.uMax = 0;
423 bounds.vMin = 1;
424 bounds.vMax = 0;
425 _manager->SetOverlayTextureBounds(_handle, &bounds );
426
427 _manager->SetOverlayFlag(_handle, ::vr::VROverlayFlags_SendVRSmoothScrollEvents, true);
428
429 // Todo : Make the overlay able to read input even if dashboard is off
430 //_manager->SetOverlayFlag(_handle, ::vr::VROverlayFlags_MakeOverlaysInteractiveIfVisible, true);
431
432 _manager->ShowOverlay(_handle);
433}
434
435oe::ext::vr::Overlay::~Overlay()
436{
437 ::vr::VR_Shutdown();
438}
439
440void oe::ext::vr::Overlay::setRatio(const float& meters)
441{
442 _manager->SetOverlayWidthInMeters(_handle, meters);
443}
444
445static const std::map<uint32_t, uint32_t> OPENVR_TO_GLFW_MOUSE_BUTTONS = {
446 {::vr::VRMouseButton_Left, GLFW_MOUSE_BUTTON_LEFT},
447 {::vr::VRMouseButton_Middle, GLFW_MOUSE_BUTTON_MIDDLE},
448 {::vr::VRMouseButton_Right, GLFW_MOUSE_BUTTON_RIGHT},
449};
450
451void oe::ext::vr::Overlay::handleEvents(oe::core::EventHandler& event_handler, const glm::ivec2& window_dimensions)
452{
453 ::vr::VREvent_t vrEvent;
454
455 while (_manager->PollNextOverlayEvent(_handle, &vrEvent, sizeof(vrEvent)))
456 {
457 //oe::log << "VR Event " << vrEvent.eventType << " on " << vrEvent.trackedDeviceIndex;
458
459 switch( vrEvent.eventType )
460 {
461 case ::vr::VREvent_MouseMove:
462 {
463 // We need to flip Y because OpenVr send texture uv coordinates of point instead of position
464 // Window origin is top-left while texture origin is bottom-left
465 event_handler.setCursorPos({window_dimensions.x*vrEvent.data.mouse.x, window_dimensions.y * (1.0f-vrEvent.data.mouse.y)});
466 }
467 break;
468
469 case ::vr::VREvent_ScrollSmooth:
470 {
471 event_handler.setMouseScroll(vrEvent.data.scroll.viewportscale * glm::vec2(vrEvent.data.scroll.xdelta, vrEvent.data.scroll.ydelta));
472 }
473 break;
474
475 case ::vr::VREvent_MouseButtonDown:
476 {
477 event_handler.forceState(OPENVR_TO_GLFW_MOUSE_BUTTONS.at(vrEvent.data.mouse.button), true, true);
478 }
479 break;
480
481 case ::vr::VREvent_MouseButtonUp:
482 {
483 event_handler.forceState(OPENVR_TO_GLFW_MOUSE_BUTTONS.at(vrEvent.data.mouse.button), false, true);
484 }
485 break;
486 }
487 }
488}
489
490void oe::ext::vr::Overlay::bindToDevice(const std::unique_ptr<TrackedDevice>& device, const glm::mat4& matrix)
491{
492 ::vr::HmdMatrix34_t openvr_matrix = convertMat4ToHmd34(matrix);
493
494 _manager->SetOverlayTransformTrackedDeviceRelative(_handle, device->_handle, &openvr_matrix);
495}
496
497void oe::ext::vr::Overlay::useStereoRendering(const bool enable)
498{
499 _manager->SetOverlayFlag(_handle, ::vr::VROverlayFlags_SideBySide_Parallel, enable);
500}
501
503{
504 bool result;
505
506 _manager->GetOverlayFlag(_handle, ::vr::VROverlayFlags_SideBySide_Parallel, &result);
507
508 return result;
509}
510
511void oe::ext::vr::Overlay::submitTexture(const std::shared_ptr<oe::render::Texture>& texture)
512{
513 struct ::vr::Texture_t overlay_texture;
514 overlay_texture.eColorSpace = ::vr::ColorSpace_Auto;
515 overlay_texture.eType = ::vr::TextureType_OpenGL;
516 overlay_texture.handle = reinterpret_cast<uintptr_t*>(texture->getHandle());
517
518 _manager->SetOverlayTexture(_handle, &overlay_texture);
519}
520
521/*oe::ext::vr::System::System()
522{
523_raw_system
524}
525
526oe::ext::vr::System::~System()
527{
528
529}*/
530
531oe::ext::vr::TrackedDevice::TrackedDevice(System* parent)
532 : _parent(parent)
533{}
534#endif
535
Event handler.
Definition event_handler.h:22
void setCursorPos(const glm::vec2 &cursor_pos) noexcept
Definition event_handler.h:148
void forceState(const key_code_t &key, const int &state, const bool &isMouse=false) noexcept
Force key or mouse button status, ignoring the real status.
Common properties to all VR applications (overlay, scene, etc...)
Definition vr.h:146
VR Overlay Application.
Definition vr.h:154
void bindToDevice(const std::unique_ptr< TrackedDevice > &device, const glm::mat4 &matrix=glm::mat4(1.0))
Attach the overlay to a tracked device.
bool isUsingStereoRendering()
Check if stereo rendering is enabled.
void useStereoRendering(const bool enable=true)
Tell overlay to split texture to left/right eye.
void submitTexture(const std::shared_ptr< oe::render::Texture > &texture)
Send texture to the Overlay.
void handleEvents(oe::core::EventHandler &event_handler, const glm::ivec2 &window_dimensions)
Handle Overlay events and send them to an event_handler.
void setRatio(const float &width)
Set Overlay ratio By default the ratio is 1.0, meaning that the sent texture width will equals 1 mete...
VR Scene Application (ie. Game)
Definition vr.h:202
VR System.
Definition vr.h:43
static System generateForOverlay()
Generate a system for an overlay application.
std::vector< std::unique_ptr< TrackedDevice > > getAllTrackedDevices()
Returns all devices used on this VR system.
std::unique_ptr< TrackedDevice > getTrackedLeftHandDevice()
Get the device used as left hand.
std::unique_ptr< TrackedDevice > getTrackedRightHandDevice()
Get the device used as right hand.
std::unique_ptr< TrackedDevice > getTrackedHmdDevice()
Get the device used as HMD.
VR Device that can track data (HMD / Controller / Tracker / etc...)
Definition vr.h:84
bool isCharging()
Check if the device battery is charging.
std::string getSerialNumber()
Get the device unique serial number.
bool isValid()
Check if device is still available or disconnected / shutdown.
float getBatteryCharge()
Get if the device battery level.
std::string getModel()
Get the device model.
bool isWireless()
Check if the device can work without being connected.
Prevent class to be copied.
Definition non_copyable.h:12
Interact with Virtual Reality.
Definition vr.h:29
bool canUseVr()
Run checks to ensure there are VR capabilities.