--- /dev/null
+#include <msp/gl/meshbuilder.h>
+#include <msp/gl/texture2d.h>
+#include "libovrcombiner.h"
+#include "libovrsystem.h"
+#include "libovrsystem_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 {
+
+struct LibOVRCombiner::Frustum: StereoCombiner::Frustum
+{
+ Frustum(const ovrFovPort &);
+};
+
+
+LibOVRCombiner::LibOVRCombiner(LibOVRSystem &d, GL::View &v):
+ device(d),
+ view(v),
+ 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];
+ configure_eye_frustums(Frustum(left_fov), Frustum(right_fov));
+
+ left_fov.UpTan = left_fov.DownTan = tan(fov/2.0f);
+ left_fov.LeftTan = left_fov.UpTan*render_aspect*(1-frustum_skew);
+ left_fov.RightTan = left_fov.UpTan*render_aspect*(1+frustum_skew);
+ right_fov = left_fov;
+ swap(right_fov.LeftTan, right_fov.RightTan);
+
+ 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);
+ target_width = tex_size.w;
+ target_height = tex_size.h;
+
+ 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);
+
+ device.configure_window(view.get_window());
+}
+
+void LibOVRCombiner::prepare() const
+{
+ device.begin_frame();
+}
+
+void LibOVRCombiner::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();
+
+ view.get_context().swap_buffers();
+ device.end_frame();
+}
+
+
+LibOVRCombiner::Frustum::Frustum(const ovrFovPort &fov):
+ StereoCombiner::Frustum(-fov.LeftTan, fov.RightTan, -fov.DownTan, fov.UpTan)
+{ }
+
+} // namespace VR
+} // namespace Msp