Implement a basic OpenVR driver
authorMikko Rasa <tdb@tdb.fi>
Sat, 17 Sep 2016 23:34:32 +0000 (02:34 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 17 Sep 2016 23:34:32 +0000 (02:34 +0300)
There's still work to be done but it can render to the HMD and provide
tracking.

Build
source/openvr/openvrcamera.cpp [new file with mode: 0644]
source/openvr/openvrcamera.h [new file with mode: 0644]
source/openvr/openvrcombiner.cpp [new file with mode: 0644]
source/openvr/openvrcombiner.h [new file with mode: 0644]
source/openvr/openvrdevice.cpp [new file with mode: 0644]
source/openvr/openvrdevice.h [new file with mode: 0644]

diff --git a/Build b/Build
index 33363fc3aa8f2af378918f809ccb34f7f8875558..85616de0de6e36f7815099c88abe758cc0ec6539 100644 (file)
--- a/Build
+++ b/Build
@@ -7,6 +7,7 @@ package "mspvr"
        require "mspgui";
 
        feature "libovr" "Support Oculus Rift through LibOVR";
+       feature "openvr" "Support HTC Vive and other devices through OpenVR";
 
        if_feature "libovr"
        {
@@ -19,6 +20,11 @@ package "mspvr"
                };
        };
 
+       if_feature "openvr"
+       {
+               require "openvr";
+       };
+
        library "mspvr"
        {
                source "source";
@@ -26,6 +32,10 @@ package "mspvr"
                {
                        source "source/ovr";
                };
+               if_feature "openvr"
+               {
+                       source "source/openvr";
+               };
                install true;
                install_map
                {
diff --git a/source/openvr/openvrcamera.cpp b/source/openvr/openvrcamera.cpp
new file mode 100644 (file)
index 0000000..6166d3a
--- /dev/null
@@ -0,0 +1,24 @@
+#include <openvr.h>
+#include "openvrcamera.h"
+#include "openvrdevice.h"
+
+namespace Msp {
+namespace VR {
+
+OpenVRCamera::OpenVRCamera(const OpenVRDevice &d, const GL::Camera &c):
+       HeadTrackingCamera(c),
+       device(d)
+{ }
+
+void OpenVRCamera::reset_tracking()
+{
+       vr::VRSystem()->ResetSeatedZeroPose();
+}
+
+void OpenVRCamera::update()
+{
+       update_from_matrix(device.get_hmd_matrix());
+}
+
+} // namespace VR
+} // namespace Msp
diff --git a/source/openvr/openvrcamera.h b/source/openvr/openvrcamera.h
new file mode 100644 (file)
index 0000000..ff86817
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef MSP_VR_OPENVRCAMERA_H_
+#define MSP_VR_OPENVRCAMERA_H_
+
+#include <msp/vr/headtrackingcamera.h>
+
+namespace Msp {
+namespace VR {
+
+class OpenVRDevice;
+
+class OpenVRCamera: public HeadTrackingCamera
+{
+private:
+       const OpenVRDevice &device;
+
+public:
+       OpenVRCamera(const OpenVRDevice &, const GL::Camera &);
+
+       virtual void reset_tracking();
+       virtual void update();
+};
+
+} // namespace VR
+} // namespace Msp
+
+#endif
diff --git a/source/openvr/openvrcombiner.cpp b/source/openvr/openvrcombiner.cpp
new file mode 100644 (file)
index 0000000..2272a1a
--- /dev/null
@@ -0,0 +1,57 @@
+#include <openvr.h>
+#include "openvrcombiner.h"
+#include "openvrdevice.h"
+
+namespace Msp {
+namespace VR {
+
+struct OpenVRCombiner::Private
+{
+       static Frustum get_projection(vr::EVREye);
+};
+
+
+OpenVRCombiner::OpenVRCombiner(OpenVRDevice &d):
+       device(d)
+{
+       vr::IVRSystem *vr_sys = vr::VRSystem();
+       uint32_t w, h;
+       vr_sys->GetRecommendedRenderTargetSize(&w, &h);
+       target_width = w;
+       target_height = h;
+
+       Frustum left_frustum = Private::get_projection(vr::Eye_Left);
+       Frustum right_frustum = Private::get_projection(vr::Eye_Right);
+       configure_eye_frustums(left_frustum, right_frustum);
+}
+
+void OpenVRCombiner::prepare() const
+{
+       device.update_pose_matrices();
+}
+
+void OpenVRCombiner::render(const GL::Texture2D &left, const GL::Texture2D &right) const
+{
+       vr::Texture_t tex;
+       tex.eType = vr::API_OpenGL;
+       tex.eColorSpace = vr::ColorSpace_Gamma;
+
+       vr::IVRCompositor *compositor = vr::VRCompositor();
+       tex.handle = reinterpret_cast<void *>(left.get_id());
+       compositor->Submit(vr::Eye_Left, &tex);
+       tex.handle = reinterpret_cast<void *>(right.get_id());
+       compositor->Submit(vr::Eye_Right, &tex);
+}
+
+
+StereoCombiner::Frustum OpenVRCombiner::Private::get_projection(vr::EVREye eye)
+{
+       Frustum frustum;
+       /* The parameter order is documented as left, right, top, bottom; however
+       the third parameter is actually negative and fourth one positive. */
+       vr::VRSystem()->GetProjectionRaw(eye, &frustum.left, &frustum.right, &frustum.bottom, &frustum.top);
+       return frustum;
+}
+
+} // namespace VR
+} // namespace Msp
diff --git a/source/openvr/openvrcombiner.h b/source/openvr/openvrcombiner.h
new file mode 100644 (file)
index 0000000..ad4a37d
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef MSP_VR_OPENVRCOMBINER_H_
+#define MSP_VR_OPENVRCOMBINER_H_
+
+#include <msp/vr/stereocombiner.h>
+
+namespace Msp {
+namespace VR {
+
+class OpenVRDevice;
+
+class OpenVRCombiner: public StereoCombiner
+{
+private:
+       struct Private;
+
+       OpenVRDevice &device;
+
+public:
+       OpenVRCombiner(OpenVRDevice &);
+
+       virtual void prepare() const;
+       virtual void render(const GL::Texture2D &, const GL::Texture2D &) const;
+};
+
+} // namespace VR
+} // namespace Msp
+
+#endif
diff --git a/source/openvr/openvrdevice.cpp b/source/openvr/openvrdevice.cpp
new file mode 100644 (file)
index 0000000..1081a04
--- /dev/null
@@ -0,0 +1,61 @@
+#include <openvr.h>
+#include "openvrdevice.h"
+
+using namespace std;
+
+namespace Msp {
+namespace VR {
+
+unsigned OpenVRDevice::n_instances = 0;
+
+OpenVRDevice::OpenVRDevice()
+{
+       if(!n_instances)
+       {
+               vr::EVRInitError init_err;
+               vr::VR_Init(&init_err, vr::VRApplication_Scene);
+               if(init_err!=vr::VRInitError_None)
+                       throw runtime_error("OpenVR initialization failed");
+       }
+       ++n_instances;
+
+       vr::IVRCompositor *compositor = vr::VRCompositor();
+       if(!compositor)
+               throw runtime_error("OpenVR compositor initialization failed");
+
+       vr::VRCompositor()->SetTrackingSpace(vr::TrackingUniverseSeated);
+}
+
+OpenVRDevice::~OpenVRDevice()
+{
+       if(!--n_instances)
+               vr::VR_Shutdown();
+}
+
+OpenVRCamera *OpenVRDevice::create_camera(const GL::Camera &bc)
+{
+       return new OpenVRCamera(*this, bc);
+}
+
+OpenVRCombiner *OpenVRDevice::create_combiner(GL::View &)
+{
+       return new OpenVRCombiner(*this);
+}
+
+void OpenVRDevice::update_pose_matrices()
+{
+       vector<vr::TrackedDevicePose_t> poses;
+       poses.resize(vr::k_unTrackedDeviceIndex_Hmd+1);
+       vr::VRCompositor()->WaitGetPoses(&poses[0], poses.size(), 0, 0);
+
+       vr::TrackedDevicePose_t &hmd_pose = poses[vr::k_unTrackedDeviceIndex_Hmd];
+       if(hmd_pose.bPoseIsValid)
+       {
+               for(unsigned i=0; i<3; ++i)
+                       for(unsigned j=0; j<4; ++j)
+                               hmd_matrix(i, j) = hmd_pose.mDeviceToAbsoluteTracking.m[i][j];
+       }
+}
+
+} // namespace VR
+} // namespace Msp
diff --git a/source/openvr/openvrdevice.h b/source/openvr/openvrdevice.h
new file mode 100644 (file)
index 0000000..a1ee0db
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef MSP_VR_OPENVRDEVICE_H_
+#define MSP_VR_OPENVRDEVICE_H_
+
+#include <msp/gl/matrix.h>
+#include <msp/vr/displaydevice.h>
+#include "openvrcamera.h"
+#include "openvrcombiner.h"
+
+namespace Msp {
+namespace VR {
+
+class OpenVRDevice: public DisplayDevice
+{
+private:
+       GL::Matrix hmd_matrix;
+
+       static unsigned n_instances;
+
+public:
+       OpenVRDevice();
+       ~OpenVRDevice();
+
+       virtual void configure_window(Graphics::Window &) const { }
+       virtual void configure_view(StereoView &) const { }
+       virtual OpenVRCamera *create_camera(const GL::Camera &);
+       virtual OpenVRCombiner *create_combiner(GL::View &);
+
+       void update_pose_matrices();
+       const GL::Matrix &get_hmd_matrix() const { return hmd_matrix; }
+};
+
+} // namespace VR
+} // namespace Msp
+
+#endif