From: Mikko Rasa Date: Thu, 15 Sep 2016 16:39:23 +0000 (+0300) Subject: Make LibOVR support optional at compile-time X-Git-Url: http://git.tdb.fi/?p=libs%2Fvr.git;a=commitdiff_plain;h=12044b218363bdd93f9fc1b3c71167d700144e45 Make LibOVR support optional at compile-time --- diff --git a/Build b/Build index 4afd3c3..06d04e0 100644 --- 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 index 0eede27..0000000 --- a/source/oculusriftcamera.cpp +++ /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 index 7abaa81..0000000 --- a/source/oculusriftcamera.h +++ /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 index 0d883fa..0000000 --- a/source/oculusriftcombiner.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include -#include -#include -#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.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(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 index 32ab9cc..0000000 --- a/source/oculusriftcombiner.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef MSP_VR_OCULUSRIFTCOMBINER_H_ -#define MSP_VR_OCULUSRIFTCOMBINER_H_ - -#include -#include -#include -#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 index f73371f..0000000 --- a/source/oculusriftdevice.cpp +++ /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 index 9eee6e3..0000000 --- a/source/oculusriftdevice.h +++ /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 index 26587ff..0000000 --- a/source/oculusriftdevice_private.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef MSP_VR_OCULUSRIFTDEVICE_PRIVATE_H_ -#define MSP_VR_OCULUSRIFTDEVICE_PRIVATE_H_ - -#include -#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 index 0000000..0eede27 --- /dev/null +++ b/source/ovr/oculusriftcamera.cpp @@ -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 index 0000000..a2b0b28 --- /dev/null +++ b/source/ovr/oculusriftcamera.h @@ -0,0 +1,27 @@ +#ifndef MSP_VR_OCULUSRIFTCAMERA_H_ +#define MSP_VR_OCULUSRIFTCAMERA_H_ + +#include + +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 index 0000000..0d883fa --- /dev/null +++ b/source/ovr/oculusriftcombiner.cpp @@ -0,0 +1,161 @@ +#include +#include +#include +#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.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(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 index 0000000..65809a3 --- /dev/null +++ b/source/ovr/oculusriftcombiner.h @@ -0,0 +1,37 @@ +#ifndef MSP_VR_OCULUSRIFTCOMBINER_H_ +#define MSP_VR_OCULUSRIFTCOMBINER_H_ + +#include +#include +#include +#include + +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 index 0000000..32558e0 --- /dev/null +++ b/source/ovr/oculusriftdevice.cpp @@ -0,0 +1,88 @@ +#include +#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 index 0000000..a5105d6 --- /dev/null +++ b/source/ovr/oculusriftdevice.h @@ -0,0 +1,44 @@ +#ifndef MSP_VR_OCULUSRIFTDEVICE_H_ +#define MSP_VR_OCULUSRIFTDEVICE_H_ + +#include +#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 index 0000000..26587ff --- /dev/null +++ b/source/ovr/oculusriftdevice_private.h @@ -0,0 +1,19 @@ +#ifndef MSP_VR_OCULUSRIFTDEVICE_PRIVATE_H_ +#define MSP_VR_OCULUSRIFTDEVICE_PRIVATE_H_ + +#include +#include "oculusriftdevice.h" + +namespace Msp { +namespace VR { + +struct OculusRiftDevice::Private +{ + ovrHmd ovr_hmd; + ovrFrameTiming frame_timing; +}; + +} // namespace VR +} // namespace Msp + +#endif