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> getTrackedLeftHandDevice();
56
60 std::unique_ptr<TrackedDevice> getTrackedRightHandDevice();
61
65 std::vector<std::unique_ptr<TrackedDevice>> getAllTrackedDevices();
66
67 friend class TrackedDevice;
68
69 private:
70 ::vr::IVRSystem* _raw_system = nullptr;
71
72 System(const uint32_t& type);
73 };
74
79 {
80 public:
81 TrackedDevice(System* parent);
82
86 bool isValid();
87
98 std::string getModel();
99
103 std::string getSerialNumber();
104
109
115 bool isHmd();
116
122
123 uint64_t getFirmwareVersion();
124 int32_t getUsageTimeSinceFirstStarted();
125
126 private:
127 ::vr::TrackedDeviceIndex_t _handle = 0;
128 System* _parent = nullptr;
129
130 friend class System;
131 friend class Overlay;
132
133 //protected:
134 // TrackedDevice() {};
135 };
136
141 {
142
143 };
144
148 class Overlay : public Application
149 {
150 public:
151 Overlay(const std::string& key, const std::string& name = "");
152 ~Overlay();
153
159 void setRatio(const float& width);
160
164 void useStereoRendering(const bool enable = true);
165
170
174 void handleEvents(oe::core::EventHandler& event_handler, const glm::ivec2& window_dimensions);
175
180 void submitTexture(const std::shared_ptr<oe::render::Texture>& texture);
181
186 void bindToDevice(const std::unique_ptr<TrackedDevice>& device, const glm::mat4& matrix = glm::mat4(1.0));
187
188 private:
189 ::vr::IVROverlay* _manager = nullptr;
190 ::vr::VROverlayHandle_t _handle = 0;
191 };
192
196 class Scene : public Application
197 {
198 // TODO
199 };
200}
201#endif
202
203#ifdef OE_EXT_VR_IMPLEMENTATION
204#include <map>
205#include <stdexcept>
206#include <openvr/openvr.h>
207#include <GLFW/glfw3.h>
208
209/*static glm::mat4 convertHmd34ToMat4(const ::vr::HmdMatrix34_t& matrix)
210{
211 return glm::mat4(
212 matrix.m[0][0], matrix.m[0][1], matrix.m[0][2], matrix.m[0][3],
213 matrix.m[1][0], matrix.m[1][1], matrix.m[1][2], matrix.m[2][3],
214 matrix.m[2][0], matrix.m[2][1], matrix.m[2][2], matrix.m[2][3],
215 0, 0, 0, 1
216 );
217}*/
218
219static ::vr::HmdMatrix34_t convertMat4ToHmd34(const glm::mat4& matrix)
220{
221 return {
222 matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
223 matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
224 matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
225 };
226}
227
229{
230 return ::vr::VR_IsHmdPresent() && ::vr::VR_IsRuntimeInstalled();
231}
232
233oe::ext::vr::System::System(const uint32_t& type)
234{
235 ::vr::EVRInitError err = ::vr::VRInitError_None;
236
237 _raw_system = ::vr::VR_Init(&err, (::vr::EVRApplicationType)type);
238
239 if (err != ::vr::VRInitError_None)
240 throw std::runtime_error(::vr::VR_GetVRInitErrorAsEnglishDescription(err));
241
242 if (!::vr::VRCompositor())
243 throw std::runtime_error("Unable to initialize VR compositor!\n ");
244}
245
246oe::ext::vr::System oe::ext::vr::System::generateForScene()
247{
248 return {::vr::VRApplication_Scene};
249}
250
252{
253 return {::vr::VRApplication_Overlay};
254}
255
256std::unique_ptr<oe::ext::vr::TrackedDevice> oe::ext::vr::System::getTrackedLeftHandDevice()
257{
258 std::unique_ptr<oe::ext::vr::TrackedDevice> result;
259
260 ::vr::TrackedDeviceIndex_t id = _raw_system->GetTrackedDeviceIndexForControllerRole(::vr::TrackedControllerRole_LeftHand);
261
262 if (id != ::vr::k_unTrackedDeviceIndexInvalid && id != ::vr::k_unTrackedDeviceIndex_Hmd)
263 {
264 result = std::make_unique<TrackedDevice>(this);
265
266 result->_handle = id;
267 }
268
269 return result;
270}
271
272std::unique_ptr<oe::ext::vr::TrackedDevice> oe::ext::vr::System::getTrackedRightHandDevice()
273{
274 std::unique_ptr<oe::ext::vr::TrackedDevice> result;
275
276 ::vr::TrackedDeviceIndex_t id = _raw_system->GetTrackedDeviceIndexForControllerRole(::vr::TrackedControllerRole_RightHand);
277
278 if (id != ::vr::k_unTrackedDeviceIndexInvalid && id != ::vr::k_unTrackedDeviceIndex_Hmd)
279 {
280 result = std::make_unique<TrackedDevice>(this);
281
282 result->_handle = id;
283 }
284
285 return result;
286}
287
288std::vector<std::unique_ptr<oe::ext::vr::TrackedDevice>> oe::ext::vr::System::getAllTrackedDevices()
289{
290 std::vector<std::unique_ptr<TrackedDevice>> result;
291
292 uint32_t maxTrackedDevices = ::vr::k_unMaxTrackedDeviceCount;
293
294 for (unsigned int id = 0; id<maxTrackedDevices; ++id)
295 {
296 if (_raw_system->GetTrackedDeviceClass(id) == ::vr::TrackedDeviceClass_Invalid)
297 continue;
298
299 auto device = std::make_unique<TrackedDevice>(this);
300
301 device->_handle = id;
302
303 result.push_back(std::move(device));
304 }
305
306 return result;
307}
308
310{
311 return _parent->_raw_system->GetTrackedDeviceClass(_handle) != ::vr::TrackedDeviceClass_Invalid;
312}
313
314bool oe::ext::vr::TrackedDevice::isHmd()
315{
316 return _handle == ::vr::k_unTrackedDeviceIndex_Hmd;
317}
318
319#define GENERATE_OPENVR_GETTER(TYPE, METHOD, PROPERTY) \
320TYPE oe::ext::vr::TrackedDevice::METHOD() \
321{ \
322\
323 assert(isValid() && "Device not valid"); \
324 \
325 ::vr::ETrackedPropertyError error = ::vr::TrackedProp_Success; \
326 \
327 TYPE result = getPropertyOpenVR<TYPE>(_parent->_raw_system, _handle, ::vr::PROPERTY, &error); \
328 \
329 /* (Doesn't work : ex. for isWireless an error is thrown but the property is correctly returned)
330 if (false && error != ::vr::TrackedProp_Success) \
331 { \
332 throw std::runtime_error("Cannot fetch property: " + std::to_string(::vr::PROPERTY) + std::string(" ") + _parent->_raw_system->GetPropErrorNameFromEnum(error)); \
333 } */\
334 \
335 return result; \
336}
337
338template <typename T>
339static T getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError *error)
340{
341 throw std::runtime_error("Unknown Type!");
342}
343
344template <>
345bool getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
346{
347 return system->GetBoolTrackedDeviceProperty(handle, prop, error);
348}
349
350template <>
351float getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
352{
353 return system->GetFloatTrackedDeviceProperty(handle, prop, error);
354}
355
356template <>
357int32_t getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
358{
359 return system->GetInt32TrackedDeviceProperty(handle, prop, error);
360}
361
362template <>
363uint64_t getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
364{
365 return system->GetUint64TrackedDeviceProperty(handle, prop, error);
366}
367
368template <>
369std::string getPropertyOpenVR(::vr::IVRSystem* system, ::vr::TrackedDeviceIndex_t handle, ::vr::TrackedDeviceProperty prop, ::vr::TrackedPropertyError* error)
370{
371 uint32_t bufferLength = system->GetStringTrackedDeviceProperty(handle, prop, NULL, 0, error);
372
373 if (bufferLength == 0)
374 return "";
375
376 char *buffer = new char[bufferLength];
377 bufferLength = system->GetStringTrackedDeviceProperty(handle, prop, buffer, bufferLength, error );
378
379 std::string result = buffer;
380
381 delete[] buffer;
382
383 return result;
384}
385
386GENERATE_OPENVR_GETTER(std::string, getModel, Prop_ModelNumber_String)
387GENERATE_OPENVR_GETTER(std::string, getSerialNumber, Prop_SerialNumber_String)
388GENERATE_OPENVR_GETTER(bool, isWireless, Prop_DeviceIsWireless_Bool)
389GENERATE_OPENVR_GETTER(bool, isCharging, Prop_DeviceIsCharging_Bool)
390GENERATE_OPENVR_GETTER(float, getBatteryCharge, Prop_DeviceBatteryPercentage_Float)
391GENERATE_OPENVR_GETTER(uint64_t, getFirmwareVersion, Prop_FirmwareVersion_Uint64)
392GENERATE_OPENVR_GETTER(int32_t, getUsageTimeSinceFirstStarted, Prop_EstimatedDeviceFirstUseTime_Int32)
393
394#undef GENERATE_OPENVR_GETTER
395
396oe::ext::vr::Overlay::Overlay(const std::string& key, const std::string& name)
397{
398 _manager = ::vr::VROverlay();
399
400 assert(_manager != nullptr);
401
402 _manager->CreateOverlay(key.c_str(), name != "" ? name.c_str() : key.c_str(), &_handle);
403 _manager->SetOverlayInputMethod(_handle, ::vr::VROverlayInputMethod_Mouse);
404
405 // Set overlay texture bounds to fit an OpenGL texture
406 ::vr::VRTextureBounds_t bounds;
407 bounds.uMin = 1;
408 bounds.uMax = 0;
409 bounds.vMin = 1;
410 bounds.vMax = 0;
411 _manager->SetOverlayTextureBounds(_handle, &bounds );
412
413 _manager->SetOverlayFlag(_handle, ::vr::VROverlayFlags_SendVRSmoothScrollEvents, true);
414
415 // Todo : Make the overlay able to read input even if dashboard is off
416 //_manager->SetOverlayFlag(_handle, ::vr::VROverlayFlags_MakeOverlaysInteractiveIfVisible, true);
417
418 _manager->ShowOverlay(_handle);
419}
420
421oe::ext::vr::Overlay::~Overlay()
422{
423 ::vr::VR_Shutdown();
424}
425
426void oe::ext::vr::Overlay::setRatio(const float& meters)
427{
428 _manager->SetOverlayWidthInMeters(_handle, meters);
429}
430
431static const std::map<uint32_t, uint32_t> OPENVR_TO_GLFW_MOUSE_BUTTONS = {
432 {::vr::VRMouseButton_Left, GLFW_MOUSE_BUTTON_LEFT},
433 {::vr::VRMouseButton_Middle, GLFW_MOUSE_BUTTON_MIDDLE},
434 {::vr::VRMouseButton_Right, GLFW_MOUSE_BUTTON_RIGHT},
435};
436
437void oe::ext::vr::Overlay::handleEvents(oe::core::EventHandler& event_handler, const glm::ivec2& window_dimensions)
438{
439 ::vr::VREvent_t vrEvent;
440
441 while (_manager->PollNextOverlayEvent(_handle, &vrEvent, sizeof(vrEvent)))
442 {
443 //oe::log << "VR Event " << vrEvent.eventType << " on " << vrEvent.trackedDeviceIndex;
444
445 switch( vrEvent.eventType )
446 {
447 case ::vr::VREvent_MouseMove:
448 {
449 // We need to flip Y because OpenVr send texture uv coordinates of point instead of position
450 // Window origin is top-left while texture origin is bottom-left
451 event_handler.setCursorPos({window_dimensions.x*vrEvent.data.mouse.x, window_dimensions.y * (1.0f-vrEvent.data.mouse.y)});
452 }
453 break;
454
455 case ::vr::VREvent_ScrollSmooth:
456 {
457 event_handler.setMouseScroll(vrEvent.data.scroll.viewportscale * glm::vec2(vrEvent.data.scroll.xdelta, vrEvent.data.scroll.ydelta));
458 }
459 break;
460
461 case ::vr::VREvent_MouseButtonDown:
462 {
463 event_handler.forceState(OPENVR_TO_GLFW_MOUSE_BUTTONS.at(vrEvent.data.mouse.button), true, true);
464 }
465 break;
466
467 case ::vr::VREvent_MouseButtonUp:
468 {
469 event_handler.forceState(OPENVR_TO_GLFW_MOUSE_BUTTONS.at(vrEvent.data.mouse.button), false, true);
470 }
471 break;
472 }
473 }
474}
475
476void oe::ext::vr::Overlay::bindToDevice(const std::unique_ptr<TrackedDevice>& device, const glm::mat4& matrix)
477{
478 ::vr::HmdMatrix34_t openvr_matrix = convertMat4ToHmd34(matrix);
479
480 _manager->SetOverlayTransformTrackedDeviceRelative(_handle, device->_handle, &openvr_matrix);
481}
482
483void oe::ext::vr::Overlay::useStereoRendering(const bool enable)
484{
485 _manager->SetOverlayFlag(_handle, ::vr::VROverlayFlags_SideBySide_Parallel, enable);
486}
487
489{
490 bool result;
491
492 _manager->GetOverlayFlag(_handle, ::vr::VROverlayFlags_SideBySide_Parallel, &result);
493
494 return result;
495}
496
497void oe::ext::vr::Overlay::submitTexture(const std::shared_ptr<oe::render::Texture>& texture)
498{
499 struct ::vr::Texture_t overlay_texture;
500 overlay_texture.eColorSpace = ::vr::ColorSpace_Auto;
501 overlay_texture.eType = ::vr::TextureType_OpenGL;
502 overlay_texture.handle = reinterpret_cast<uintptr_t*>(texture->getHandle());
503
504 _manager->SetOverlayTexture(_handle, &overlay_texture);
505}
506
507/*oe::ext::vr::System::System()
508{
509_raw_system
510}
511
512oe::ext::vr::System::~System()
513{
514
515}*/
516
517oe::ext::vr::TrackedDevice::TrackedDevice(System* parent)
518 : _parent(parent)
519{
520
521}
522#endif
523
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:141
VR Overlay Application.
Definition vr.h:149
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:197
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.
VR Device that can track data (HMD / Controller / Tracker / etc...)
Definition vr.h:79
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.