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
{
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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