#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 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 vec4 chromatic;\n"
- "uniform vec2 lens_center;\n"
- "uniform vec2 eye_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"
- " float r_sq = dot(texcoord, texcoord);\n"
- " float f = dot(distortion, vec4(1.0, r_sq, r_sq*r_sq, r_sq*r_sq*r_sq));\n"
- " vec2 dtc = (texcoord*f-eye_center)/(scale.xy*scale.z)+lens_center;\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"
- " {\n"
- " vec2 red_dtc = (texcoord*f*(chromatic[0]+chromatic[1]*r_sq)-eye_center)/(scale.xy*scale.z)+lens_center;\n"
- " vec2 blue_dtc = (texcoord*f*(chromatic[2]+chromatic[3]*r_sq)-eye_center)/(scale.xy*scale.z)+lens_center;\n"
- " gl_FragColor = vec4(texture2D(texture, red_dtc).r, texture2D(texture, dtc).g, texture2D(texture, blue_dtc).b, 1.0);\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(r, g, b, 1.0);\n"
"}\n";
-}
-
-namespace Msp {
-namespace VR {
-
-OculusRiftCombiner::OculusRiftCombiner():
- mesh(GL::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);
-
- chromatic[0] = 1.0f;
- chromatic[1] = 0.0f;
- chromatic[2] = 1.0f;
- chromatic[3] = 0.0f;
-
- // This will also call update_parameters
- set_distortion(1.0f, 0.22f, 0.24f);
-
- GL::MeshBuilder bld(mesh);
- bld.begin(GL::TRIANGLE_STRIP);
- bld.vertex(-1, 1);
- bld.vertex(-1, -1);
- bld.vertex(1, 1);
- bld.vertex(1, -1);
- bld.end();
-}
+ 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_view_distance(float d)
-{
- view_distance = d;
- 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_lens_separation(float s)
-{
- lens_separation = s;
- update_parameters();
+ ovrHmd_DestroyDistortionMesh(&ovr_mesh);
}
-void OculusRiftCombiner::set_eye_separation(float s)
-{
- eye_separation = s;
- update_parameters();
}
-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();
-}
+namespace Msp {
+namespace VR {
-void OculusRiftCombiner::set_red_aberration(float c0, float c1)
+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)
{
- chromatic[0] = c0;
- chromatic[1] = c1;
+ width_div = 2;
- update_parameters();
-}
+ const OculusRiftDevice::Private &dev_priv = device.get_private();
+ ovrHmd hmd = dev_priv.ovr_hmd;
-void OculusRiftCombiner::set_blue_aberration(float c2, float c3)
-{
- chromatic[2] = c2;
- chromatic[3] = c3;
+ 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;
- update_parameters();
-}
+ 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);
-void OculusRiftCombiner::set_fill_factor(float f)
-{
- fill_factor = f;
- update_parameters();
-}
+ 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;
-void OculusRiftCombiner::update_parameters()
-{
- left_shdata.uniform4("distortion", distortion);
- left_shdata.uniform4("chromatic", chromatic);
- right_shdata.uniform4("distortion", distortion);
- right_shdata.uniform4("chromatic", chromatic);
-
- // 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);
-
- /* 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);
-
- /* Determine the necessary scaling factor to avoid quality degradation in
- the center of the screen. */
- float horiz_oversize = distort((fill_factor-lens_separation)*2)/((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_shdata.uniform("scale", 2.0f, 2.5f, oversize);
- right_shdata.uniform("scale", 2.0f, 2.5f, oversize);
-
- fov = Geometry::atan(oversize*0.625f/view_distance/2)*2.0f;
- frustum_skew = lens_separation*2-1;
-}
+ create_distortion_mesh(left_mesh, hmd, ovrEye_Left, left_fov);
+ create_distortion_mesh(right_mesh, hmd, ovrEye_Right, right_fov);
-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);
-}
+ ovrSizei tex_size = ovrHmd_GetFovTextureSize(hmd, ovrEye_Left, left_fov, 1.0);
+ oversize = max(tex_size.w*2.0f/hmd->Resolution.w, tex_size.h*1.0f/hmd->Resolution.h);
-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 GL::Texture2D &left, const GL::Texture2D &right) const
{
GL::Bind bind_shprog(shprog);
+ shdata.apply();
GL::Bind bind_tex(left);
- left_shdata.apply();
- mesh.draw();
+ left_mesh.draw();
right.bind();
- right_shdata.apply();
- mesh.draw();
+ right_mesh.draw();
}
} // namespace VR