Make LibOVR support optional at compile-time
authorMikko Rasa <tdb@tdb.fi>
Thu, 15 Sep 2016 16:39:23 +0000 (19:39 +0300)
committerMikko Rasa <tdb@tdb.fi>
Thu, 15 Sep 2016 16:39:23 +0000 (19:39 +0300)
15 files changed:
Build
source/oculusriftcamera.cpp [deleted file]
source/oculusriftcamera.h [deleted file]
source/oculusriftcombiner.cpp [deleted file]
source/oculusriftcombiner.h [deleted file]
source/oculusriftdevice.cpp [deleted file]
source/oculusriftdevice.h [deleted file]
source/oculusriftdevice_private.h [deleted file]
source/ovr/oculusriftcamera.cpp [new file with mode: 0644]
source/ovr/oculusriftcamera.h [new file with mode: 0644]
source/ovr/oculusriftcombiner.cpp [new file with mode: 0644]
source/ovr/oculusriftcombiner.h [new file with mode: 0644]
source/ovr/oculusriftdevice.cpp [new file with mode: 0644]
source/ovr/oculusriftdevice.h [new file with mode: 0644]
source/ovr/oculusriftdevice_private.h [new file with mode: 0644]

diff --git a/Build b/Build
index 4afd3c3d4e0d6174879e2dd7a704710f96607dd3..06d04e0654424dd26e810538236f684ce2493d5c 100644 (file)
--- a/Build
+++ b/Build
@@ -4,17 +4,27 @@ package "mspvr"
        version "0.1";
 
        require "mspgl";
-       require "libovr";
-       require "libudev";
 
-       build_info
+       feature "libovr" "Support Oculus Rift through LibOVR";
+
+       if_feature "libovr"
        {
-               warning_level 1;
+               require "libovr";
+               require "libudev";
+
+               build_info
+               {
+                       warning_level 1;
+               };
        };
 
        library "mspvr"
        {
                source "source";
+               if_feature "libovr"
+               {
+                       source "source/ovr";
+               };
                install true;
                install_map
                {
diff --git a/source/oculusriftcamera.cpp b/source/oculusriftcamera.cpp
deleted file mode 100644 (file)
index 0eede27..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "oculusriftcamera.h"
-#include "oculusriftdevice.h"
-#include "oculusriftdevice_private.h"
-
-namespace Msp {
-namespace VR {
-
-OculusRiftCamera::OculusRiftCamera(const OculusRiftDevice &d, const GL::Camera &c):
-       device(d),
-       base_camera(c)
-{
-       unsigned supported = ovrTrackingCap_Orientation|ovrTrackingCap_MagYawCorrection|ovrTrackingCap_Position;
-       ovrHmd_ConfigureTracking(device.get_private().ovr_hmd, supported, 0);
-}
-
-void OculusRiftCamera::reset_tracking()
-{
-       ovrHmd_RecenterPose(device.get_private().ovr_hmd);
-}
-
-void OculusRiftCamera::update()
-{
-       double time;
-       if(device.is_timing_active())
-               time = device.get_tracking_time();
-       else
-               time = device.get_current_time();
-
-       ovrTrackingState state = ovrHmd_GetTrackingState(device.get_private().ovr_hmd, time);
-       OVR::Posef head_pose = state.HeadPose.ThePose;
-       OVR::Matrix4f tracking_matrix(head_pose.Rotation);
-       OVR::Vector3f trans = head_pose.Translation;
-       const float *m = &tracking_matrix.M[0][0];
-
-       const GL::Vector3 &base_look = base_camera.get_look_direction();
-       GL::Vector3 base_right = normalize(cross(base_look, base_camera.get_up_direction()));
-       GL::Vector3 base_up = normalize(cross(base_right, base_look));
-
-       set_position(base_camera.get_position()+trans.x*base_right+trans.y*base_up-trans.z*base_look);
-       set_up_direction(base_right*m[1]+base_up*m[5]-base_look*m[9]);
-       set_look_direction(-base_right*m[2]-base_up*m[6]+base_look*m[10]);
-       set_depth_clip(base_camera.get_near_clip(), base_camera.get_far_clip());
-       set_field_of_view(base_camera.get_field_of_view());
-       set_aspect(base_camera.get_aspect());
-}
-
-} // namespace VR
-} // namespace Msp
diff --git a/source/oculusriftcamera.h b/source/oculusriftcamera.h
deleted file mode 100644 (file)
index 7abaa81..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef MSP_VR_OCULUSRIFTCAMERA_H_
-#define MSP_VR_OCULUSRIFTCAMERA_H_
-
-#include "headtrackingcamera.h"
-
-namespace Msp {
-namespace VR {
-
-class OculusRiftDevice;
-
-class OculusRiftCamera: public HeadTrackingCamera
-{
-private:
-       const OculusRiftDevice &device;
-       const GL::Camera &base_camera;
-
-public:
-       OculusRiftCamera(const OculusRiftDevice &, const GL::Camera &);
-
-       virtual void reset_tracking();
-       virtual void update();
-};
-
-} // namespace VR
-} // namespace Msp
-
-#endif
diff --git a/source/oculusriftcombiner.cpp b/source/oculusriftcombiner.cpp
deleted file mode 100644 (file)
index 0d883fa..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-#include <cmath>
-#include <msp/gl/meshbuilder.h>
-#include <msp/gl/texture2d.h>
-#include "oculusriftcombiner.h"
-#include "oculusriftdevice.h"
-#include "oculusriftdevice_private.h"
-
-using namespace std;
-
-
-namespace {
-
-const char vs_source[] =
-       "uniform mat4 timewarp[2];\n"
-       "uniform vec2 uv_scale;\n"
-       "uniform vec2 uv_offset;\n"
-       "varying vec2 texcoord_r;\n"
-       "varying vec2 texcoord_g;\n"
-       "varying vec2 texcoord_b;\n"
-       "varying float vignette;\n"
-       "vec2 apply_timewarp(vec2 coords, float factor)\n"
-       "{\n"
-       "       vec4 coords4 = vec4(coords, 1.0, 1.0);\n"
-       "       vec4 warped = mix(timewarp[0]*coords4, timewarp[1]*coords4, factor);\n"
-       "       return (warped.xy/warped.z)*uv_scale+uv_offset;\n"
-       "}\n"
-       "void main()\n"
-       "{\n"
-       "       gl_Position = vec4(gl_Vertex.xy, 0.5, 1.0);\n"
-       "       float tw_factor = gl_MultiTexCoord3.y;\n"
-       "       texcoord_r = apply_timewarp(gl_MultiTexCoord0.xy, tw_factor);\n"
-       "       texcoord_g = apply_timewarp(gl_MultiTexCoord1.xy, tw_factor);\n"
-       "       texcoord_b = apply_timewarp(gl_MultiTexCoord2.xy, tw_factor);\n"
-       "       vignette = gl_MultiTexCoord3.x;\n"
-       "}\n";
-
-const char fs_source[] =
-       "uniform sampler2D texture;\n"
-       "varying vec2 texcoord_r;\n"
-       "varying vec2 texcoord_g;\n"
-       "varying vec2 texcoord_b;\n"
-       "varying float vignette;\n"
-       "void main()\n"
-       "{\n"
-       "       float r = texture2D(texture, texcoord_r).r;\n"
-       "       float g = texture2D(texture, texcoord_g).g;\n"
-       "       float b = texture2D(texture, texcoord_b).b;\n"
-       "       gl_FragColor = vec4(vec3(r, g, b)*vignette, 1.0);\n"
-       "}\n";
-
-void create_distortion_mesh(Msp::GL::Mesh &mesh, ovrHmd hmd, ovrEyeType eye, const ovrFovPort &fov)
-{
-       ovrDistortionMesh ovr_mesh;
-       ovrHmd_CreateDistortionMesh(hmd, eye, fov, ovrDistortionCap_Vignette|ovrDistortionCap_TimeWarp, &ovr_mesh);
-
-       Msp::GL::MeshBuilder bld(mesh);
-       for(unsigned i=0; i<ovr_mesh.VertexCount; ++i)
-       {
-               ovrDistortionVertex &v = ovr_mesh.pVertexData[i];
-               bld.multitexcoord(0, v.TanEyeAnglesR.x, v.TanEyeAnglesR.y);
-               bld.multitexcoord(1, v.TanEyeAnglesG.x, v.TanEyeAnglesG.y);
-               bld.multitexcoord(2, v.TanEyeAnglesB.x, v.TanEyeAnglesB.y);
-               bld.multitexcoord(3, v.VignetteFactor, v.TimeWarpFactor);
-               bld.vertex(v.ScreenPosNDC.x, v.ScreenPosNDC.y);
-       }
-
-       Msp::GL::Batch batch(Msp::GL::TRIANGLES);
-       batch.append(vector<unsigned>(ovr_mesh.pIndexData, ovr_mesh.pIndexData+ovr_mesh.IndexCount));
-       mesh.add_batch(batch);
-
-       ovrHmd_DestroyDistortionMesh(&ovr_mesh);
-}
-
-}
-
-namespace Msp {
-namespace VR {
-
-OculusRiftCombiner::OculusRiftCombiner(const OculusRiftDevice &d):
-       device(d),
-       left_mesh((GL::VERTEX2, GL::TEXCOORD2,0, GL::TEXCOORD2,1, GL::TEXCOORD2,2, GL::TEXCOORD2,3)),
-       right_mesh((GL::VERTEX2, GL::TEXCOORD2,0, GL::TEXCOORD2,1, GL::TEXCOORD2,2, GL::TEXCOORD2,3)),
-       shprog(vs_source, fs_source)
-{
-       ovrHmd hmd = device.get_private().ovr_hmd;
-
-       ovrFovPort left_fov = hmd->DefaultEyeFov[ovrEye_Left];
-       ovrFovPort right_fov = hmd->DefaultEyeFov[ovrEye_Right];
-       float vertical = max(max(left_fov.UpTan, left_fov.DownTan), max(right_fov.UpTan, right_fov.DownTan));
-       fov = Geometry::atan<float>(vertical)*2.0f;
-
-       float inner = max(left_fov.RightTan, right_fov.LeftTan);
-       float outer = max(left_fov.LeftTan, right_fov.RightTan);
-       frustum_skew = (inner-outer)/(inner+outer);
-
-       left_fov.UpTan = right_fov.UpTan = vertical;
-       left_fov.DownTan = right_fov.DownTan = vertical;
-       left_fov.RightTan = right_fov.LeftTan = inner;
-       left_fov.LeftTan = right_fov.RightTan = outer;
-
-       create_distortion_mesh(left_mesh, hmd, ovrEye_Left, left_fov);
-       create_distortion_mesh(right_mesh, hmd, ovrEye_Right, right_fov);
-
-       ovrSizei tex_size = ovrHmd_GetFovTextureSize(hmd, ovrEye_Left, left_fov, 1.0);
-       width_factor = tex_size.w*1.0f/hmd->Resolution.w;
-       height_factor = tex_size.h*1.0f/hmd->Resolution.h;
-       float aspect = (inner+outer)/(vertical*2);
-       aspect_factor = aspect*hmd->Resolution.h/hmd->Resolution.w;
-
-       left_shdata.uniform("texture", 0);
-       right_shdata.uniform("texture", 0);
-
-       ovrRecti view_rect;
-       view_rect.Pos.x = 0;
-       view_rect.Pos.y = 0;
-       view_rect.Size = tex_size;
-       ovrVector2f uv_scale_offset[2];
-       ovrHmd_GetRenderScaleAndOffset(left_fov, tex_size, view_rect, uv_scale_offset);
-       left_shdata.uniform("uv_scale", uv_scale_offset[0].x, -uv_scale_offset[0].y);
-       left_shdata.uniform("uv_offset", uv_scale_offset[1].x, 1-uv_scale_offset[1].y);
-       ovrHmd_GetRenderScaleAndOffset(right_fov, tex_size, view_rect, uv_scale_offset);
-       right_shdata.uniform("uv_scale", uv_scale_offset[0].x, -uv_scale_offset[0].y);
-       right_shdata.uniform("uv_offset", uv_scale_offset[1].x, 1-uv_scale_offset[1].y);
-}
-
-void OculusRiftCombiner::render(const GL::Texture2D &left, const GL::Texture2D &right) const
-{
-       GL::Bind bind_shprog(shprog);
-
-       ovrHmd hmd = device.get_private().ovr_hmd;
-
-       if(device.is_timing_active())
-       {
-               ovr_WaitTillTime(device.get_timewarp_time());
-               ovrTrackingState state = ovrHmd_GetTrackingState(hmd, device.get_tracking_time());
-
-               ovrMatrix4f matrices[2];
-               ovrHmd_GetEyeTimewarpMatrices(hmd, ovrEye_Left, state.HeadPose.ThePose, matrices);
-               left_shdata.uniform_matrix4_array("timewarp", 2, &matrices[0].M[0][0]);
-
-               ovrHmd_GetEyeTimewarpMatrices(hmd, ovrEye_Right, state.HeadPose.ThePose, matrices);
-               right_shdata.uniform_matrix4_array("timewarp", 2, &matrices[0].M[0][0]);
-       }
-       else
-       {
-               GL::Matrix matrices[2];
-               left_shdata.uniform_matrix4_array("timewarp", 2, matrices[0].data());
-               right_shdata.uniform_matrix4_array("timewarp", 2, matrices[0].data());
-       }
-
-       GL::Bind bind_tex(left);
-       left_shdata.apply();
-       left_mesh.draw();
-
-       right.bind();
-       right_shdata.apply();
-       right_mesh.draw();
-}
-
-} // namespace VR
-} // namespace Msp
diff --git a/source/oculusriftcombiner.h b/source/oculusriftcombiner.h
deleted file mode 100644 (file)
index 32ab9cc..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef MSP_VR_OCULUSRIFTCOMBINER_H_
-#define MSP_VR_OCULUSRIFTCOMBINER_H_
-
-#include <msp/gl/mesh.h>
-#include <msp/gl/program.h>
-#include <msp/gl/programdata.h>
-#include "stereocombiner.h"
-
-namespace Msp {
-namespace VR {
-
-class OculusRiftDevice;
-
-/**
-Presents a stereo view in a way suitable for an Oculus Rift HMD.  All distances
-are specified in multiples of the screen width.
-*/
-class OculusRiftCombiner: public StereoCombiner
-{
-private:
-       const OculusRiftDevice &device;
-       GL::Mesh left_mesh;
-       GL::Mesh right_mesh;
-       GL::Program shprog;
-       mutable GL::ProgramData left_shdata;
-       mutable GL::ProgramData right_shdata;
-
-public:
-       OculusRiftCombiner(const OculusRiftDevice &);
-
-       virtual void render(const GL::Texture2D &, const GL::Texture2D &) const;
-};
-
-} // namespace VR
-} // namespace Msp
-
-#endif
diff --git a/source/oculusriftdevice.cpp b/source/oculusriftdevice.cpp
deleted file mode 100644 (file)
index f73371f..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "oculusriftdevice.h"
-#include "oculusriftdevice_private.h"
-#include "stereoview.h"
-
-using namespace std;
-
-namespace Msp {
-namespace VR {
-
-unsigned OculusRiftDevice::n_instances = 0;
-
-OculusRiftDevice::OculusRiftDevice():
-       priv(new Private),
-       frame_index(0)
-{
-       if(!n_instances)
-               ovr_Initialize();
-       ++n_instances;
-
-       priv->ovr_hmd = ovrHmd_Create(0);
-       if(!priv->ovr_hmd)
-       {
-               delete priv;
-               throw runtime_error("rift hmd not found");
-       }
-}
-
-OculusRiftDevice::~OculusRiftDevice()
-{
-       ovrHmd_Destroy(priv->ovr_hmd);
-       delete priv;
-
-       --n_instances;
-       if(!n_instances)
-               ovr_Shutdown();
-}
-
-void OculusRiftDevice::configure_view(StereoView &view) const
-{
-       ovrEyeRenderDesc left_desc = ovrHmd_GetRenderDesc(priv->ovr_hmd, ovrEye_Left, priv->ovr_hmd->DefaultEyeFov[ovrEye_Left]);
-       ovrEyeRenderDesc right_desc = ovrHmd_GetRenderDesc(priv->ovr_hmd, ovrEye_Right, priv->ovr_hmd->DefaultEyeFov[ovrEye_Left]);
-       view.set_eye_spacing(left_desc.HmdToEyeViewOffset.x-right_desc.HmdToEyeViewOffset.x);
-}
-
-OculusRiftCamera *OculusRiftDevice::create_camera(const GL::Camera &bc) const
-{
-       return new OculusRiftCamera(*this, bc);
-}
-
-OculusRiftCombiner *OculusRiftDevice::create_combiner() const
-{
-       return new OculusRiftCombiner(*this);
-}
-
-void OculusRiftDevice::begin_frame()
-{
-       priv->frame_timing = ovrHmd_BeginFrameTiming(priv->ovr_hmd, ++frame_index);
-       timing_active = true;
-}
-
-void OculusRiftDevice::end_frame()
-{
-       glFinish();
-       ovrHmd_EndFrameTiming(priv->ovr_hmd);
-       timing_active = false;
-}
-
-double OculusRiftDevice::get_tracking_time() const
-{
-       if(!timing_active)
-               throw logic_error("timing not active");
-       return priv->frame_timing.ScanoutMidpointSeconds;
-}
-
-double OculusRiftDevice::get_timewarp_time() const
-{
-       if(!timing_active)
-               throw logic_error("timing not active");
-       return priv->frame_timing.TimewarpPointSeconds;
-}
-
-double OculusRiftDevice::get_current_time() const
-{
-       return ovr_GetTimeInSeconds();
-}
-
-} // namespace VR
-} // namespace Msp
diff --git a/source/oculusriftdevice.h b/source/oculusriftdevice.h
deleted file mode 100644 (file)
index 9eee6e3..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef MSP_VR_OCULUSRIFTDEVICE_H_
-#define MSP_VR_OCULUSRIFTDEVICE_H_
-
-#include "displaydevice.h"
-#include "oculusriftcamera.h"
-#include "oculusriftcombiner.h"
-
-namespace Msp {
-namespace VR {
-
-class OculusRiftDevice: public DisplayDevice
-{
-public:
-       struct Private;
-
-private:
-       Private *priv;
-       unsigned frame_index;
-       bool timing_active;
-
-       static unsigned n_instances;
-
-public:
-       OculusRiftDevice();
-       virtual ~OculusRiftDevice();
-
-       const Private &get_private() const { return *priv; }
-
-       virtual void configure_view(StereoView &) const;
-       virtual OculusRiftCamera *create_camera(const GL::Camera &) const;
-       virtual OculusRiftCombiner *create_combiner() const;
-
-       virtual void begin_frame();
-       virtual void end_frame();
-       bool is_timing_active() const { return timing_active; }
-       double get_tracking_time() const;
-       double get_timewarp_time() const;
-       double get_current_time() const;
-};
-
-} // namespace VR
-} // namespace Msp
-
-#endif
diff --git a/source/oculusriftdevice_private.h b/source/oculusriftdevice_private.h
deleted file mode 100644 (file)
index 26587ff..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef MSP_VR_OCULUSRIFTDEVICE_PRIVATE_H_
-#define MSP_VR_OCULUSRIFTDEVICE_PRIVATE_H_
-
-#include <OVR.h>
-#include "oculusriftdevice.h"
-
-namespace Msp {
-namespace VR {
-
-struct OculusRiftDevice::Private
-{
-       ovrHmd ovr_hmd;
-       ovrFrameTiming frame_timing;
-};
-
-} // namespace VR
-} // namespace Msp
-
-#endif
diff --git a/source/ovr/oculusriftcamera.cpp b/source/ovr/oculusriftcamera.cpp
new file mode 100644 (file)
index 0000000..0eede27
--- /dev/null
@@ -0,0 +1,48 @@
+#include "oculusriftcamera.h"
+#include "oculusriftdevice.h"
+#include "oculusriftdevice_private.h"
+
+namespace Msp {
+namespace VR {
+
+OculusRiftCamera::OculusRiftCamera(const OculusRiftDevice &d, const GL::Camera &c):
+       device(d),
+       base_camera(c)
+{
+       unsigned supported = ovrTrackingCap_Orientation|ovrTrackingCap_MagYawCorrection|ovrTrackingCap_Position;
+       ovrHmd_ConfigureTracking(device.get_private().ovr_hmd, supported, 0);
+}
+
+void OculusRiftCamera::reset_tracking()
+{
+       ovrHmd_RecenterPose(device.get_private().ovr_hmd);
+}
+
+void OculusRiftCamera::update()
+{
+       double time;
+       if(device.is_timing_active())
+               time = device.get_tracking_time();
+       else
+               time = device.get_current_time();
+
+       ovrTrackingState state = ovrHmd_GetTrackingState(device.get_private().ovr_hmd, time);
+       OVR::Posef head_pose = state.HeadPose.ThePose;
+       OVR::Matrix4f tracking_matrix(head_pose.Rotation);
+       OVR::Vector3f trans = head_pose.Translation;
+       const float *m = &tracking_matrix.M[0][0];
+
+       const GL::Vector3 &base_look = base_camera.get_look_direction();
+       GL::Vector3 base_right = normalize(cross(base_look, base_camera.get_up_direction()));
+       GL::Vector3 base_up = normalize(cross(base_right, base_look));
+
+       set_position(base_camera.get_position()+trans.x*base_right+trans.y*base_up-trans.z*base_look);
+       set_up_direction(base_right*m[1]+base_up*m[5]-base_look*m[9]);
+       set_look_direction(-base_right*m[2]-base_up*m[6]+base_look*m[10]);
+       set_depth_clip(base_camera.get_near_clip(), base_camera.get_far_clip());
+       set_field_of_view(base_camera.get_field_of_view());
+       set_aspect(base_camera.get_aspect());
+}
+
+} // namespace VR
+} // namespace Msp
diff --git a/source/ovr/oculusriftcamera.h b/source/ovr/oculusriftcamera.h
new file mode 100644 (file)
index 0000000..a2b0b28
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef MSP_VR_OCULUSRIFTCAMERA_H_
+#define MSP_VR_OCULUSRIFTCAMERA_H_
+
+#include <msp/vr/headtrackingcamera.h>
+
+namespace Msp {
+namespace VR {
+
+class OculusRiftDevice;
+
+class OculusRiftCamera: public HeadTrackingCamera
+{
+private:
+       const OculusRiftDevice &device;
+       const GL::Camera &base_camera;
+
+public:
+       OculusRiftCamera(const OculusRiftDevice &, const GL::Camera &);
+
+       virtual void reset_tracking();
+       virtual void update();
+};
+
+} // namespace VR
+} // namespace Msp
+
+#endif
diff --git a/source/ovr/oculusriftcombiner.cpp b/source/ovr/oculusriftcombiner.cpp
new file mode 100644 (file)
index 0000000..0d883fa
--- /dev/null
@@ -0,0 +1,161 @@
+#include <cmath>
+#include <msp/gl/meshbuilder.h>
+#include <msp/gl/texture2d.h>
+#include "oculusriftcombiner.h"
+#include "oculusriftdevice.h"
+#include "oculusriftdevice_private.h"
+
+using namespace std;
+
+
+namespace {
+
+const char vs_source[] =
+       "uniform mat4 timewarp[2];\n"
+       "uniform vec2 uv_scale;\n"
+       "uniform vec2 uv_offset;\n"
+       "varying vec2 texcoord_r;\n"
+       "varying vec2 texcoord_g;\n"
+       "varying vec2 texcoord_b;\n"
+       "varying float vignette;\n"
+       "vec2 apply_timewarp(vec2 coords, float factor)\n"
+       "{\n"
+       "       vec4 coords4 = vec4(coords, 1.0, 1.0);\n"
+       "       vec4 warped = mix(timewarp[0]*coords4, timewarp[1]*coords4, factor);\n"
+       "       return (warped.xy/warped.z)*uv_scale+uv_offset;\n"
+       "}\n"
+       "void main()\n"
+       "{\n"
+       "       gl_Position = vec4(gl_Vertex.xy, 0.5, 1.0);\n"
+       "       float tw_factor = gl_MultiTexCoord3.y;\n"
+       "       texcoord_r = apply_timewarp(gl_MultiTexCoord0.xy, tw_factor);\n"
+       "       texcoord_g = apply_timewarp(gl_MultiTexCoord1.xy, tw_factor);\n"
+       "       texcoord_b = apply_timewarp(gl_MultiTexCoord2.xy, tw_factor);\n"
+       "       vignette = gl_MultiTexCoord3.x;\n"
+       "}\n";
+
+const char fs_source[] =
+       "uniform sampler2D texture;\n"
+       "varying vec2 texcoord_r;\n"
+       "varying vec2 texcoord_g;\n"
+       "varying vec2 texcoord_b;\n"
+       "varying float vignette;\n"
+       "void main()\n"
+       "{\n"
+       "       float r = texture2D(texture, texcoord_r).r;\n"
+       "       float g = texture2D(texture, texcoord_g).g;\n"
+       "       float b = texture2D(texture, texcoord_b).b;\n"
+       "       gl_FragColor = vec4(vec3(r, g, b)*vignette, 1.0);\n"
+       "}\n";
+
+void create_distortion_mesh(Msp::GL::Mesh &mesh, ovrHmd hmd, ovrEyeType eye, const ovrFovPort &fov)
+{
+       ovrDistortionMesh ovr_mesh;
+       ovrHmd_CreateDistortionMesh(hmd, eye, fov, ovrDistortionCap_Vignette|ovrDistortionCap_TimeWarp, &ovr_mesh);
+
+       Msp::GL::MeshBuilder bld(mesh);
+       for(unsigned i=0; i<ovr_mesh.VertexCount; ++i)
+       {
+               ovrDistortionVertex &v = ovr_mesh.pVertexData[i];
+               bld.multitexcoord(0, v.TanEyeAnglesR.x, v.TanEyeAnglesR.y);
+               bld.multitexcoord(1, v.TanEyeAnglesG.x, v.TanEyeAnglesG.y);
+               bld.multitexcoord(2, v.TanEyeAnglesB.x, v.TanEyeAnglesB.y);
+               bld.multitexcoord(3, v.VignetteFactor, v.TimeWarpFactor);
+               bld.vertex(v.ScreenPosNDC.x, v.ScreenPosNDC.y);
+       }
+
+       Msp::GL::Batch batch(Msp::GL::TRIANGLES);
+       batch.append(vector<unsigned>(ovr_mesh.pIndexData, ovr_mesh.pIndexData+ovr_mesh.IndexCount));
+       mesh.add_batch(batch);
+
+       ovrHmd_DestroyDistortionMesh(&ovr_mesh);
+}
+
+}
+
+namespace Msp {
+namespace VR {
+
+OculusRiftCombiner::OculusRiftCombiner(const OculusRiftDevice &d):
+       device(d),
+       left_mesh((GL::VERTEX2, GL::TEXCOORD2,0, GL::TEXCOORD2,1, GL::TEXCOORD2,2, GL::TEXCOORD2,3)),
+       right_mesh((GL::VERTEX2, GL::TEXCOORD2,0, GL::TEXCOORD2,1, GL::TEXCOORD2,2, GL::TEXCOORD2,3)),
+       shprog(vs_source, fs_source)
+{
+       ovrHmd hmd = device.get_private().ovr_hmd;
+
+       ovrFovPort left_fov = hmd->DefaultEyeFov[ovrEye_Left];
+       ovrFovPort right_fov = hmd->DefaultEyeFov[ovrEye_Right];
+       float vertical = max(max(left_fov.UpTan, left_fov.DownTan), max(right_fov.UpTan, right_fov.DownTan));
+       fov = Geometry::atan<float>(vertical)*2.0f;
+
+       float inner = max(left_fov.RightTan, right_fov.LeftTan);
+       float outer = max(left_fov.LeftTan, right_fov.RightTan);
+       frustum_skew = (inner-outer)/(inner+outer);
+
+       left_fov.UpTan = right_fov.UpTan = vertical;
+       left_fov.DownTan = right_fov.DownTan = vertical;
+       left_fov.RightTan = right_fov.LeftTan = inner;
+       left_fov.LeftTan = right_fov.RightTan = outer;
+
+       create_distortion_mesh(left_mesh, hmd, ovrEye_Left, left_fov);
+       create_distortion_mesh(right_mesh, hmd, ovrEye_Right, right_fov);
+
+       ovrSizei tex_size = ovrHmd_GetFovTextureSize(hmd, ovrEye_Left, left_fov, 1.0);
+       width_factor = tex_size.w*1.0f/hmd->Resolution.w;
+       height_factor = tex_size.h*1.0f/hmd->Resolution.h;
+       float aspect = (inner+outer)/(vertical*2);
+       aspect_factor = aspect*hmd->Resolution.h/hmd->Resolution.w;
+
+       left_shdata.uniform("texture", 0);
+       right_shdata.uniform("texture", 0);
+
+       ovrRecti view_rect;
+       view_rect.Pos.x = 0;
+       view_rect.Pos.y = 0;
+       view_rect.Size = tex_size;
+       ovrVector2f uv_scale_offset[2];
+       ovrHmd_GetRenderScaleAndOffset(left_fov, tex_size, view_rect, uv_scale_offset);
+       left_shdata.uniform("uv_scale", uv_scale_offset[0].x, -uv_scale_offset[0].y);
+       left_shdata.uniform("uv_offset", uv_scale_offset[1].x, 1-uv_scale_offset[1].y);
+       ovrHmd_GetRenderScaleAndOffset(right_fov, tex_size, view_rect, uv_scale_offset);
+       right_shdata.uniform("uv_scale", uv_scale_offset[0].x, -uv_scale_offset[0].y);
+       right_shdata.uniform("uv_offset", uv_scale_offset[1].x, 1-uv_scale_offset[1].y);
+}
+
+void OculusRiftCombiner::render(const GL::Texture2D &left, const GL::Texture2D &right) const
+{
+       GL::Bind bind_shprog(shprog);
+
+       ovrHmd hmd = device.get_private().ovr_hmd;
+
+       if(device.is_timing_active())
+       {
+               ovr_WaitTillTime(device.get_timewarp_time());
+               ovrTrackingState state = ovrHmd_GetTrackingState(hmd, device.get_tracking_time());
+
+               ovrMatrix4f matrices[2];
+               ovrHmd_GetEyeTimewarpMatrices(hmd, ovrEye_Left, state.HeadPose.ThePose, matrices);
+               left_shdata.uniform_matrix4_array("timewarp", 2, &matrices[0].M[0][0]);
+
+               ovrHmd_GetEyeTimewarpMatrices(hmd, ovrEye_Right, state.HeadPose.ThePose, matrices);
+               right_shdata.uniform_matrix4_array("timewarp", 2, &matrices[0].M[0][0]);
+       }
+       else
+       {
+               GL::Matrix matrices[2];
+               left_shdata.uniform_matrix4_array("timewarp", 2, matrices[0].data());
+               right_shdata.uniform_matrix4_array("timewarp", 2, matrices[0].data());
+       }
+
+       GL::Bind bind_tex(left);
+       left_shdata.apply();
+       left_mesh.draw();
+
+       right.bind();
+       right_shdata.apply();
+       right_mesh.draw();
+}
+
+} // namespace VR
+} // namespace Msp
diff --git a/source/ovr/oculusriftcombiner.h b/source/ovr/oculusriftcombiner.h
new file mode 100644 (file)
index 0000000..65809a3
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef MSP_VR_OCULUSRIFTCOMBINER_H_
+#define MSP_VR_OCULUSRIFTCOMBINER_H_
+
+#include <msp/gl/mesh.h>
+#include <msp/gl/program.h>
+#include <msp/gl/programdata.h>
+#include <msp/vr/stereocombiner.h>
+
+namespace Msp {
+namespace VR {
+
+class OculusRiftDevice;
+
+/**
+Presents a stereo view in a way suitable for an Oculus Rift HMD.  All distances
+are specified in multiples of the screen width.
+*/
+class OculusRiftCombiner: public StereoCombiner
+{
+private:
+       const OculusRiftDevice &device;
+       GL::Mesh left_mesh;
+       GL::Mesh right_mesh;
+       GL::Program shprog;
+       mutable GL::ProgramData left_shdata;
+       mutable GL::ProgramData right_shdata;
+
+public:
+       OculusRiftCombiner(const OculusRiftDevice &);
+
+       virtual void render(const GL::Texture2D &, const GL::Texture2D &) const;
+};
+
+} // namespace VR
+} // namespace Msp
+
+#endif
diff --git a/source/ovr/oculusriftdevice.cpp b/source/ovr/oculusriftdevice.cpp
new file mode 100644 (file)
index 0000000..32558e0
--- /dev/null
@@ -0,0 +1,88 @@
+#include <msp/vr/stereoview.h>
+#include "oculusriftdevice.h"
+#include "oculusriftdevice_private.h"
+
+using namespace std;
+
+namespace Msp {
+namespace VR {
+
+unsigned OculusRiftDevice::n_instances = 0;
+
+OculusRiftDevice::OculusRiftDevice():
+       priv(new Private),
+       frame_index(0)
+{
+       if(!n_instances)
+               ovr_Initialize();
+       ++n_instances;
+
+       priv->ovr_hmd = ovrHmd_Create(0);
+       if(!priv->ovr_hmd)
+       {
+               delete priv;
+               throw runtime_error("rift hmd not found");
+       }
+}
+
+OculusRiftDevice::~OculusRiftDevice()
+{
+       ovrHmd_Destroy(priv->ovr_hmd);
+       delete priv;
+
+       --n_instances;
+       if(!n_instances)
+               ovr_Shutdown();
+}
+
+void OculusRiftDevice::configure_view(StereoView &view) const
+{
+       ovrEyeRenderDesc left_desc = ovrHmd_GetRenderDesc(priv->ovr_hmd, ovrEye_Left, priv->ovr_hmd->DefaultEyeFov[ovrEye_Left]);
+       ovrEyeRenderDesc right_desc = ovrHmd_GetRenderDesc(priv->ovr_hmd, ovrEye_Right, priv->ovr_hmd->DefaultEyeFov[ovrEye_Left]);
+       view.set_eye_spacing(left_desc.HmdToEyeViewOffset.x-right_desc.HmdToEyeViewOffset.x);
+}
+
+OculusRiftCamera *OculusRiftDevice::create_camera(const GL::Camera &bc) const
+{
+       return new OculusRiftCamera(*this, bc);
+}
+
+OculusRiftCombiner *OculusRiftDevice::create_combiner() const
+{
+       return new OculusRiftCombiner(*this);
+}
+
+void OculusRiftDevice::begin_frame()
+{
+       priv->frame_timing = ovrHmd_BeginFrameTiming(priv->ovr_hmd, ++frame_index);
+       timing_active = true;
+}
+
+void OculusRiftDevice::end_frame()
+{
+       glFinish();
+       ovrHmd_EndFrameTiming(priv->ovr_hmd);
+       timing_active = false;
+}
+
+double OculusRiftDevice::get_tracking_time() const
+{
+       if(!timing_active)
+               throw logic_error("timing not active");
+       return priv->frame_timing.ScanoutMidpointSeconds;
+}
+
+double OculusRiftDevice::get_timewarp_time() const
+{
+       if(!timing_active)
+               throw logic_error("timing not active");
+       return priv->frame_timing.TimewarpPointSeconds;
+}
+
+double OculusRiftDevice::get_current_time() const
+{
+       return ovr_GetTimeInSeconds();
+}
+
+} // namespace VR
+} // namespace Msp
diff --git a/source/ovr/oculusriftdevice.h b/source/ovr/oculusriftdevice.h
new file mode 100644 (file)
index 0000000..a5105d6
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef MSP_VR_OCULUSRIFTDEVICE_H_
+#define MSP_VR_OCULUSRIFTDEVICE_H_
+
+#include <msp/vr/displaydevice.h>
+#include "oculusriftcamera.h"
+#include "oculusriftcombiner.h"
+
+namespace Msp {
+namespace VR {
+
+class OculusRiftDevice: public DisplayDevice
+{
+public:
+       struct Private;
+
+private:
+       Private *priv;
+       unsigned frame_index;
+       bool timing_active;
+
+       static unsigned n_instances;
+
+public:
+       OculusRiftDevice();
+       virtual ~OculusRiftDevice();
+
+       const Private &get_private() const { return *priv; }
+
+       virtual void configure_view(StereoView &) const;
+       virtual OculusRiftCamera *create_camera(const GL::Camera &) const;
+       virtual OculusRiftCombiner *create_combiner() const;
+
+       virtual void begin_frame();
+       virtual void end_frame();
+       bool is_timing_active() const { return timing_active; }
+       double get_tracking_time() const;
+       double get_timewarp_time() const;
+       double get_current_time() const;
+};
+
+} // namespace VR
+} // namespace Msp
+
+#endif
diff --git a/source/ovr/oculusriftdevice_private.h b/source/ovr/oculusriftdevice_private.h
new file mode 100644 (file)
index 0000000..26587ff
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef MSP_VR_OCULUSRIFTDEVICE_PRIVATE_H_
+#define MSP_VR_OCULUSRIFTDEVICE_PRIVATE_H_
+
+#include <OVR.h>
+#include "oculusriftdevice.h"
+
+namespace Msp {
+namespace VR {
+
+struct OculusRiftDevice::Private
+{
+       ovrHmd ovr_hmd;
+       ovrFrameTiming frame_timing;
+};
+
+} // namespace VR
+} // namespace Msp
+
+#endif