Make the StereoCombiner information interface more flexible
[libs/vr.git] / source / oculusriftcombiner.cpp
index 9d985d9268e679584419bb2c75bb4947ce22aeef..492f5fdbcb220ee40b5f73207fb0f7ee6a94a12c 100644 (file)
 #include <cmath>
-#include "meshbuilder.h"
+#include <msp/gl/meshbuilder.h>
+#include <msp/gl/texture2d.h>
 #include "oculusriftcombiner.h"
-#include "texture2d.h"
+#include "oculusriftdevice.h"
+#include "oculusriftdevice_private.h"
 
 using namespace std;
 
+
 namespace {
 
 const char vs_source[] =
-       "uniform float offset;\n"
-       "uniform vec2 lens_center;\n"
-       "uniform vec3 scale;\n"
-       "varying vec2 texcoord;\n"
+       "varying vec2 texcoord_r;\n"
+       "varying vec2 texcoord_g;\n"
+       "varying vec2 texcoord_b;\n"
        "void main()\n"
        "{\n"
-       "       gl_Position = vec4(gl_Vertex.x*0.5+offset, gl_Vertex.yzw);\n"
-       "       texcoord = (gl_Vertex.xy*0.5+0.5-lens_center)*scale.xy;\n"
+       "       gl_Position = vec4(gl_Vertex.xy, 0.5, 1.0);\n"
+       "       texcoord_r = gl_MultiTexCoord0.xy;\n"
+       "       texcoord_g = gl_MultiTexCoord1.xy;\n"
+       "       texcoord_b = gl_MultiTexCoord2.xy;\n"
        "}\n";
 
 const char fs_source[] =
        "uniform sampler2D texture;\n"
-       "uniform vec4 distortion;\n"
-       "uniform vec2 eye_center;\n"
-       "uniform vec3 scale;\n"
-       "varying vec2 texcoord;\n"
-       "vec2 distort(vec2 coord)\n"
-       "{\n"
-       "       float r_sq = dot(coord, coord);\n"
-       "       return coord*dot(distortion, vec4(1.0, r_sq, r_sq*r_sq, r_sq*r_sq*r_sq));\n"
-       "}\n"
+       "varying vec2 texcoord_r;\n"
+       "varying vec2 texcoord_g;\n"
+       "varying vec2 texcoord_b;\n"
        "void main()\n"
        "{\n"
-       "       vec2 dtc = (distort(texcoord)-eye_center)/(scale.xy*scale.z)+0.5;\n"
-       "       if(dtc.x<0.0 || dtc.y<0.0 || dtc.x>1.0 || dtc.y>1.0)\n"
-       "               gl_FragColor = vec4(0.0);\n"
-       "       else\n"
-       "               gl_FragColor = texture2D(texture, dtc);\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(r, g, b, 1.0);\n"
        "}\n";
 
-}
-
-namespace Msp {
-namespace GL {
-
-OculusRiftCombiner::OculusRiftCombiner():
-       mesh(VERTEX2),
-       shprog(vs_source, fs_source),
-       // Default values copied from the SDK
-       view_distance(0.438f),
-       lens_separation(0.424f),
-       eye_separation(0.42735f),
-       fill_factor(0.95f)
+void create_distortion_mesh(Msp::GL::Mesh &mesh, ovrHmd hmd, ovrEyeType eye, const ovrFovPort &fov)
 {
-       width_div = 2;
-
-       left_shdata.uniform("texture", 0);
-       left_shdata.uniform("offset", -0.5f);
-       right_shdata.uniform("texture", 0);
-       right_shdata.uniform("offset", 0.5f);
-
-       // This will also call update_parameters
-       set_distortion(1.0f, 0.22f, 0.24f);
-
-       MeshBuilder bld(mesh);
-       bld.begin(TRIANGLE_STRIP);
-       bld.vertex(-1, 1);
-       bld.vertex(-1, -1);
-       bld.vertex(1, 1);
-       bld.vertex(1, -1);
-       bld.end();
-}
-
-void OculusRiftCombiner::set_view_distance(float d)
-{
-       view_distance = d;
-       update_parameters();
-}
+       ovrDistortionMesh ovr_mesh;
+       ovrHmd_CreateDistortionMesh(hmd, eye, fov, ovrDistortionCap_Chromatic, &ovr_mesh);
+
+       ovrSizei tex_size = ovrHmd_GetFovTextureSize(hmd, eye, fov, 1.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(fov, tex_size, view_rect, uv_scale_offset);
+       ovrVector2f &scale = uv_scale_offset[0];
+       ovrVector2f &offset = uv_scale_offset[1];
+
+       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*scale.x+offset.x, 1.0f-(v.TanEyeAnglesR.y*scale.y+offset.y));
+               bld.multitexcoord(1, v.TanEyeAnglesG.x*scale.x+offset.x, 1.0f-(v.TanEyeAnglesG.y*scale.y+offset.y));
+               bld.multitexcoord(2, v.TanEyeAnglesB.x*scale.x+offset.x, 1.0f-(v.TanEyeAnglesB.y*scale.y+offset.y));
+               bld.vertex(v.ScreenPosNDC.x, v.ScreenPosNDC.y);
+       }
 
-void OculusRiftCombiner::set_lens_separation(float s)
-{
-       lens_separation = s;
-       update_parameters();
-}
+       Msp::GL::Batch batch(Msp::GL::TRIANGLES);
+       batch.append(vector<unsigned>(ovr_mesh.pIndexData, ovr_mesh.pIndexData+ovr_mesh.IndexCount));
+       mesh.add_batch(batch);
 
-void OculusRiftCombiner::set_eye_separation(float s)
-{
-       eye_separation = s;
-       update_parameters();
+       ovrHmd_DestroyDistortionMesh(&ovr_mesh);
 }
 
-void OculusRiftCombiner::set_distortion(float d0, float d1, float d2, float d3)
-{
-       distortion[0] = d0;
-       distortion[1] = d1;
-       distortion[2] = d2;
-       distortion[3] = d3;
-
-       update_parameters();
 }
 
-void OculusRiftCombiner::set_fill_factor(float f)
-{
-       fill_factor = f;
-       update_parameters();
-}
+namespace Msp {
+namespace VR {
 
-void OculusRiftCombiner::update_parameters()
+OculusRiftCombiner::OculusRiftCombiner(const OculusRiftDevice &d):
+       device(d),
+       left_mesh((GL::VERTEX2, GL::TEXCOORD2,0, GL::TEXCOORD2,1, GL::TEXCOORD2,2)),
+       right_mesh((GL::VERTEX2, GL::TEXCOORD2,0, GL::TEXCOORD2,1, GL::TEXCOORD2,2)),
+       shprog(vs_source, fs_source)
 {
-       left_shdata.uniform4("distortion", distortion);
-       right_shdata.uniform4("distortion", distortion);
+       const OculusRiftDevice::Private &dev_priv = device.get_private();
+       ovrHmd hmd = dev_priv.ovr_hmd;
 
-       // Set lens center positions, in output texture coordinates
-       left_shdata.uniform("lens_center", 1.0f-lens_separation, 0.5);
-       right_shdata.uniform("lens_center", lens_separation, 0.5);
+       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;
 
-       /* Compute distance between eye and lens centers, in sampling texture
-       coordinates. */
-       float eye_offset = distort((eye_separation-lens_separation)*2);
-       left_shdata.uniform("eye_center", -eye_offset, 0.0f);
-       right_shdata.uniform("eye_center", eye_offset, 0.0f);
+       float inner = max(left_fov.RightTan, right_fov.LeftTan);
+       float outer = max(left_fov.LeftTan, right_fov.RightTan);
+       frustum_skew = (inner-outer)*2/(inner+outer);
 
-       /* Determine the necessary scaling factor to avoid quality degradation in
-       the center of the screen. */
-       float horiz_oversize = distort((fill_factor-lens_separation)*2)-eye_offset;
-       float vert_oversize = distort(1.25f*fill_factor)/(1.25f*fill_factor);
-       oversize = min(horiz_oversize, vert_oversize);
+       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;
 
-       left_shdata.uniform("scale", 2.0f, 2.5f, oversize);
-       right_shdata.uniform("scale", 2.0f, 2.5f, oversize);
+       create_distortion_mesh(left_mesh, hmd, ovrEye_Left, left_fov);
+       create_distortion_mesh(right_mesh, hmd, ovrEye_Right, right_fov);
 
-       fov = Geometry::atan(oversize*0.625f/view_distance)*2.0f;
-}
+       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;
 
-float OculusRiftCombiner::distort(float r) const
-{
-       float r_sq = r*r;
-       return r*(distortion[0]+(distortion[1]+(distortion[2]+distortion[3]*r_sq)*r_sq)*r_sq);
-}
-
-float OculusRiftCombiner::undistort(float r) const
-{
-       float x = r;
-       while(1)
-       {
-               float y = distort(x);
-               if(abs(r-y)<1e-5)
-                       return x;
+       shdata.uniform("texture", 0);
 
-               float x_sq = x*x;
-               float d = distortion[0]+(3*distortion[1]+(5*distortion[2]+7*distortion[3]*x_sq)*x_sq)*x_sq;
-               x -= (y-r)/d;
-       }
 }
 
-void OculusRiftCombiner::render(const Texture2D &left, const Texture2D &right) const
+void OculusRiftCombiner::render(const GL::Texture2D &left, const GL::Texture2D &right) const
 {
-       Bind bind_shprog(shprog);
+       GL::Bind bind_shprog(shprog);
+       shdata.apply();
 
-       Bind bind_tex(left);
-       left_shdata.apply();
-       mesh.draw();
+       GL::Bind bind_tex(left);
+       left_mesh.draw();
 
        right.bind();
-       right_shdata.apply();
-       mesh.draw();
+       right_mesh.draw();
 }
 
-} // namespace GL
+} // namespace VR
 } // namespace Msp