oculus1

view src/vr.cc @ 21:ef4c9d8eeca7

added shaderless distortion method
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 02 Oct 2013 04:09:37 +0300
parents 899cf9ebffb4
children 75ab0d4ce2bb
line source
1 #include <stdio.h>
2 #include <GL/glew.h>
3 #include "vr.h"
4 #include "vr_impl.h"
5 #include "vr_sdr.h"
7 #ifndef WIN32
8 #include <alloca.h>
9 #else
10 #include <malloc.h>
11 #endif
13 #define USUB 28
14 #define VSUB 40
16 /* these are just used for the shaderless precomputed distortion method */
17 struct Mesh {
18 int prim;
19 int num_verts, num_faces;
20 unsigned int vbo;
21 unsigned int ibo;
22 };
23 struct Vertex {
24 float x, y, z;
25 float tx, ty;
26 };
29 static void init_ctx();
30 static bool init_ovr();
31 static bool init_sdr();
33 static Mesh gen_view_mesh(int usub, int vsub, float aspect, float lens_center_offset,
34 float scale, const float *dist_factors, float tex_scale_x, float tex_scale_y);
35 static void distort_texcoords(float *tc, float aspect, float lens_center_offset, float scale, const float *dist_factors);
36 static float barrel_scale(float rad, const float *k);
38 VRContext vr_ctx;
39 static unsigned int sdrprog;
41 static Mesh wrapmesh[2];
42 static bool mesh_valid;
44 bool dbg_enable;
47 extern "C" int vr_init(enum vr_init_mode mode)
48 {
49 glewInit();
51 init_ctx();
53 if(!init_ovr()) {
54 return -1;
55 }
57 if(!init_sdr()) {
58 return -1;
59 }
61 return 0;
62 }
64 extern "C" void vr_shutdown(void)
65 {
66 delete [] vr_ctx.info.display;
67 //System::Destroy();
68 }
70 static void init_ctx()
71 {
72 vr_ctx.info.width = 1280;
73 vr_ctx.info.height = 800;
74 vr_ctx.info.fov = M_PI / 2.0;
75 vr_ctx.info.aspect = (float)vr_ctx.info.width / (float)vr_ctx.info.height;
76 vr_ctx.info.ipd = 0.035;
77 vr_ctx.info.scale = 1.0;
78 }
80 static bool init_ovr()
81 {
82 LogMaskConstants log_level = LogMask_All;
83 // initialize Oculus SDK
84 const char *logenv = getenv("VR_LOGLEVEL");
85 if(logenv) {
86 switch(atoi(logenv)) {
87 case 0:
88 log_level = LogMask_None;
89 break;
90 case 1:
91 log_level = LogMask_Regular;
92 break;
93 case 2:
94 default:
95 log_level = LogMask_All;
96 break;
97 }
98 }
100 System::Init(Log::ConfigureDefaultLog(log_level));
101 if(!(vr_ctx.ovr_devman = DeviceManager::Create())) {
102 fprintf(stderr, "failed to create OVR device manager\n");
103 return false;
104 }
106 // create the display device
107 HMDInfo info;
108 if(!(vr_ctx.ovr_hmd_dev = vr_ctx.ovr_devman->EnumerateDevices<HMDDevice>().CreateDevice())) {
109 fprintf(stderr, "no oculus rift devices found\n");
110 } else {
111 if(vr_ctx.ovr_hmd_dev->GetDeviceInfo(&info)) {
112 printf("oculus device info:\n");
113 printf(" name: %s\n", info.DisplayDeviceName);
114 printf(" ipd: %f\n", info.InterpupillaryDistance);
115 printf(" distortion: %f %f %f %f\n", info.DistortionK[0],
116 info.DistortionK[1], info.DistortionK[2], info.DistortionK[3]);
117 }
119 // calculate and store viewing parameters
120 vr_ctx.info.width = info.HResolution;
121 vr_ctx.info.height = info.VResolution;
122 vr_ctx.info.aspect = (float)vr_ctx.info.width / (float)vr_ctx.info.height;
124 vr_ctx.info.ipd = info.InterpupillaryDistance;
125 for(int i=0; i<4; i++) {
126 vr_ctx.info.distort[i] = info.DistortionK[i];
127 }
129 Util::Render::StereoConfig stereohelp;
130 stereohelp.SetFullViewport(Util::Render::Viewport(0, 0, vr_ctx.info.width, vr_ctx.info.height));
131 stereohelp.SetStereoMode(Util::Render::Stereo_LeftRight_Multipass);
132 stereohelp.SetHMDInfo(info);
133 stereohelp.SetDistortionFitPointVP(-1.0, 0.0);
135 vr_ctx.info.scale = stereohelp.GetDistortionScale();
137 float vhalfsz = vr_ctx.info.scale * info.VScreenSize * 0.5;
138 vr_ctx.info.fov = 2.0 * atan(vhalfsz / info.EyeToScreenDistance);
140 vr_ctx.info.lens_center_offset = 0.5 - info.LensSeparationDistance / info.HScreenSize;
142 // calculate center of projection shift to match the lens positions
143 float center_dist_meters = info.HScreenSize * 0.25;
144 float proj_shift = center_dist_meters - info.LensSeparationDistance * 0.5;
145 vr_ctx.info.proj_center_offset = 4.0 * proj_shift / info.HScreenSize;
147 // grab the display info
148 vr_ctx.info.display = new char[strlen(info.DisplayDeviceName) + 1];
149 strcpy(vr_ctx.info.display, info.DisplayDeviceName);
151 vr_ctx.info.display_xoffs = info.DesktopX;
152 vr_ctx.info.display_yoffs = info.DesktopY;
154 printf("display: \"%s\" offset: %+d %+d\n", vr_ctx.info.display,
155 vr_ctx.info.display_xoffs, vr_ctx.info.display_yoffs);
156 }
158 // get the sensor device
159 if(vr_ctx.ovr_hmd_dev) {
160 if(!(vr_ctx.ovr_sensor_dev = vr_ctx.ovr_hmd_dev->GetSensor())) {
161 fprintf(stderr, "failed to get oculus sensor device\n");
162 }
163 } else {
164 if(!(vr_ctx.ovr_sensor_dev = vr_ctx.ovr_devman->EnumerateDevices<SensorDevice>().CreateDevice())) {
165 fprintf(stderr, "failed to get oculus sensor device\n");
166 }
167 }
169 if(vr_ctx.ovr_sensor_dev) {
170 SensorInfo sinfo;
171 if(vr_ctx.ovr_sensor_dev->GetDeviceInfo(&sinfo)) {
172 printf("oculus sensor device info:\n");
173 printf(" name: %s\n", sinfo.ProductName);
174 }
176 vr_ctx.ovr_sfusion.AttachToSensor(vr_ctx.ovr_sensor_dev);
177 }
178 return true;
179 }
181 #define EXTERNAL_SHADER
183 static bool init_sdr()
184 {
185 int status;
187 #ifdef EXTERNAL_SHADER
188 FILE *fp = fopen("sdr/sdr.glsl", "rb");
189 if(!fp) {
190 perror("failed to load sdr.glsl");
191 return false;
192 }
193 fseek(fp, 0, SEEK_END);
194 long sz = ftell(fp);
195 rewind(fp);
197 char *buf = (char*)alloca(sz + 1);
198 fread(buf, 1, sz, fp);
199 buf[sz] = 0;
200 sdr_src = buf;
202 fclose(fp);
203 #endif
205 unsigned int sdr = glCreateShader(GL_FRAGMENT_SHADER);
206 glShaderSource(sdr, 1, &sdr_src, 0);
207 glCompileShader(sdr);
208 glGetShaderiv(sdr, GL_COMPILE_STATUS, &status);
209 if(!status) {
210 fprintf(stderr, "failed to compile distortion shader\n");
212 int loglen;
213 glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &loglen);
215 if(loglen > 0) {
216 char *log = (char*)alloca(loglen);
217 glGetShaderInfoLog(sdr, loglen, &loglen, log);
218 fprintf(stderr, "%s\n", log);
219 }
221 return false;
222 }
224 sdrprog = glCreateProgram();
225 glAttachShader(sdrprog, sdr);
226 glLinkProgram(sdrprog);
227 if(!status) {
228 fprintf(stderr, "failed to link distortion shader program\n");
229 glDeleteShader(sdr);
230 return false;
231 }
233 int loc;
235 glUseProgram(sdrprog);
237 if((loc = glGetUniformLocation(sdrprog, "tex")) != -1) {
238 glUniform1i(loc, 0);
239 }
240 if((loc = glGetUniformLocation(sdrprog, "lens_center_offset")) != -1) {
241 glUniform1f(loc, 0);
242 }
243 if((loc = glGetUniformLocation(sdrprog, "scr_center")) != -1) {
244 glUniform2f(loc, 0, 0);
245 }
246 if((loc = glGetUniformLocation(sdrprog, "scale")) != -1) {
247 glUniform1f(loc, vr_ctx.info.scale);
248 }
249 if((loc = glGetUniformLocation(sdrprog, "aspect")) != -1) {
250 printf("setting aspect: %f\n", vr_ctx.info.aspect / 2.0);
251 glUniform1f(loc, vr_ctx.info.aspect / 2.0);
252 }
253 if((loc = glGetUniformLocation(sdrprog, "scale_in")) != -1) {
254 glUniform2f(loc, 1, 1);
255 }
256 if((loc = glGetUniformLocation(sdrprog, "dist_factors")) != -1) {
257 glUniform4f(loc, vr_ctx.info.distort[0], vr_ctx.info.distort[1],
258 vr_ctx.info.distort[2], vr_ctx.info.distort[3]);
259 }
261 return true;
262 }
264 extern "C" const char *vr_get_display_name(void)
265 {
266 return vr_ctx.info.display;
267 }
269 extern "C" void vr_get_display_pos(int *xptr, int *yptr)
270 {
271 *xptr = vr_ctx.info.display_xoffs;
272 *yptr = vr_ctx.info.display_yoffs;
273 }
275 extern "C" int vr_get_width(void)
276 {
277 return vr_ctx.info.width;
278 }
280 extern "C" int vr_get_height(void)
281 {
282 return vr_ctx.info.height;
283 }
285 extern "C" float vr_get_fov(void)
286 {
287 return vr_ctx.info.fov;
288 }
290 extern "C" float vr_get_aspect(void)
291 {
292 return vr_ctx.info.aspect;
293 }
295 extern "C" void vr_set_eyedist(float ipd)
296 {
297 vr_ctx.info.ipd = ipd;
298 }
300 extern "C" float vr_get_eyedist(void)
301 {
302 return vr_ctx.info.ipd;
303 }
305 extern "C" void vr_set_distort(const float *coef)
306 {
307 memcpy(vr_ctx.info.distort, coef, sizeof vr_ctx.info.distort);
308 }
310 extern "C" void vr_get_distort(float *coef)
311 {
312 memcpy(coef, vr_ctx.info.distort, sizeof vr_ctx.info.distort);
313 }
315 extern "C" void vr_set_prediction_sec(float dt)
316 {
317 vr_ctx.ovr_sfusion.SetPrediction(dt);
318 }
320 extern "C" float vr_get_prediction_sec(void)
321 {
322 return vr_ctx.ovr_sfusion.GetPredictionDelta();
323 }
325 extern "C" void vr_get_view_matrix(float *res, int eye)
326 {
327 // TODO
328 }
330 extern "C" void vr_get_proj_matrix(float *res, int eye)
331 {
332 static float eye_scale[] = {0.0, 1.0, -1.0};
334 Matrix4f proj = Matrix4f::PerspectiveRH(vr_ctx.info.fov, vr_ctx.info.aspect / 2.0, 0.3, 1000.0);
335 proj = Matrix4f::Translation(vr_ctx.info.proj_center_offset * eye_scale[eye], 0, 0) * proj;
337 memcpy(res, proj.M[0], 16 * sizeof(float));
338 }
340 extern "C" void vr_get_translation(float *offs)
341 {
342 // current oculus devkit doesn't do translation
343 offs[0] = offs[1] = offs[2] = 0.0f;
344 }
346 extern "C" void vr_get_rotation(float *quat)
347 {
348 Quatf oq = vr_ctx.ovr_sfusion.GetPredictedOrientation();
349 quat[0] = oq.x;
350 quat[1] = oq.y;
351 quat[2] = oq.z;
352 quat[3] = oq.w;
353 }
355 extern "C" void vr_get_rotation_euler(float *euler)
356 {
357 Quatf oq = vr_ctx.ovr_sfusion.GetPredictedOrientation();
358 oq.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(euler + 1, euler, euler + 2);
359 }
361 extern "C" void vr_draw_eye(int eye, unsigned int tex, float tex_scale_x, float tex_scale_y)
362 {
363 static const float rects[3][4] = {
364 {-1, -1, 1, 1},
365 {-1, -1, 0, 1},
366 {0, -1, 1, 1}
367 };
368 static const float quad_trans[3] = {0, -1, 1};
369 static const float quad_scale[3] = {1, 0.5, 0.5};
370 static const float offs_scale[3] = {0.0, -1.0, 1.0};
371 static int prev_tex_scale_x, prev_tex_scale_y;
373 glPushAttrib(GL_ENABLE_BIT);
374 glDisable(GL_DEPTH_TEST);
375 glDisable(GL_LIGHTING);
376 glDisable(GL_CULL_FACE);
378 glMatrixMode(GL_PROJECTION);
379 glPushMatrix();
380 glLoadIdentity();
382 glMatrixMode(GL_MODELVIEW);
383 glPushMatrix();
384 glLoadIdentity();
386 if(!dbg_enable) {
387 glUseProgram(sdrprog);
389 if(sdrprog) {
390 int loc;
391 if((loc = glGetUniformLocation(sdrprog, "lens_center_offset")) != -1) {
392 float offset = vr_ctx.info.lens_center_offset * offs_scale[eye];
393 glUniform1f(loc, offset);
394 }
396 if((loc = glGetUniformLocation(sdrprog, "tex_scale")) != -1) {
397 glUniform2f(loc, tex_scale_x, tex_scale_y);
398 }
399 }
401 glBindTexture(GL_TEXTURE_2D, tex);
402 glBegin(GL_QUADS);
403 glColor4f(1, 1, 1, 1);
404 glTexCoord2f(0, 0); glVertex2f(rects[eye][0], rects[eye][1]);
405 glTexCoord2f(1, 0); glVertex2f(rects[eye][2], rects[eye][1]);
406 glTexCoord2f(1, 1); glVertex2f(rects[eye][2], rects[eye][3]);
407 glTexCoord2f(0, 1); glVertex2f(rects[eye][0], rects[eye][3]);
408 glEnd();
410 glUseProgram(0);
412 } else {
414 if(!mesh_valid || tex_scale_x != prev_tex_scale_x || tex_scale_y != prev_tex_scale_y) {
415 for(int i=0; i<2; i++) {
416 int eye = i + VR_EYE_LEFT;
418 if(wrapmesh[i].vbo) {
419 glDeleteBuffers(1, &wrapmesh[i].vbo);
420 }
421 if(wrapmesh[i].ibo) {
422 glDeleteBuffers(1, &wrapmesh[i].ibo);
423 }
425 float aspect = vr_ctx.info.aspect / 2.0;
426 float offset = vr_ctx.info.lens_center_offset * offs_scale[eye];
427 wrapmesh[i] = gen_view_mesh(USUB, VSUB, aspect, offset, vr_ctx.info.scale, vr_ctx.info.distort,
428 tex_scale_x, tex_scale_y);
429 }
430 mesh_valid = true;
431 prev_tex_scale_x = tex_scale_x;
432 prev_tex_scale_y = tex_scale_y;
433 }
435 glScalef(quad_scale[eye], 1.0, 1.0);
436 glTranslatef(quad_trans[eye], 0, 0);
438 glUseProgram(0);
439 glBindTexture(GL_TEXTURE_2D, tex);
440 glEnable(GL_TEXTURE_2D);
442 glColor3f(1, 1, 1);
444 glEnableClientState(GL_VERTEX_ARRAY);
445 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
447 int meshidx = eye - VR_EYE_LEFT;
448 glBindBuffer(GL_ARRAY_BUFFER, wrapmesh[meshidx].vbo);
449 glVertexPointer(3, GL_FLOAT, sizeof(Vertex), 0);
450 glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, tx));
452 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wrapmesh[meshidx].ibo);
453 glDrawElements(GL_TRIANGLES, wrapmesh[meshidx].num_faces * 3, GL_UNSIGNED_INT, 0);
455 glBindBuffer(GL_ARRAY_BUFFER, 0);
456 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
458 glDisableClientState(GL_VERTEX_ARRAY);
459 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
460 }
462 glMatrixMode(GL_PROJECTION);
463 glPopMatrix();
464 glMatrixMode(GL_MODELVIEW);
465 glPopMatrix();
467 glPopAttrib();
468 }
470 static Mesh gen_view_mesh(int usub, int vsub, float aspect, float lens_center_offset,
471 float scale, const float *dist_factors, float tex_scale_x, float tex_scale_y)
472 {
473 int uverts = usub + 1;
474 int vverts = vsub + 1;
476 int num_verts = uverts * vverts;
477 int num_quads = usub * vsub;
478 int num_tris = num_quads * 2;
480 Vertex *varr = new Vertex[num_verts];
481 unsigned int *iarr = new unsigned int[num_tris * 3];
483 float du = 1.0 / (float)usub;
484 float dv = 1.0 / (float)vsub;
486 Vertex *vptr = varr;
487 for(int i=0; i<vverts; i++) {
488 float v = (float)i * dv;
489 float y = 2.0 * v - 1.0;
491 for(int j=0; j<uverts; j++) {
492 float u = (float)j * du;
493 float x = 2.0 * u - 1.0;
494 float tc[2] = {u, v};
496 distort_texcoords(tc, aspect, lens_center_offset, scale, dist_factors);
498 vptr->x = x;
499 vptr->y = y;
500 vptr->z = 0;
501 vptr->tx = tc[0] * tex_scale_x;
502 vptr->ty = tc[1] * tex_scale_y;
503 vptr++;
504 }
505 }
507 unsigned int *iptr = iarr;
508 for(int i=0; i<vsub; i++) {
509 for(int j=0; j<usub; j++) {
510 *iptr++ = i * uverts + j;
511 *iptr++ = (i + 1) * uverts + j;
512 *iptr++ = (i + 1) * uverts + (j + 1);
514 *iptr++ = i * uverts + j;
515 *iptr++ = (i + 1) * uverts + (j + 1);
516 *iptr++ = i * uverts + (j + 1);
517 }
518 }
520 unsigned int buf[2];
521 glGenBuffers(2, buf);
522 glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
523 glBufferData(GL_ARRAY_BUFFER, num_verts * sizeof *varr, varr, GL_STATIC_DRAW);
524 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf[1]);
525 glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_tris * 3 * sizeof *iarr, iarr, GL_STATIC_DRAW);
527 delete [] varr;
528 delete [] iarr;
530 Mesh mesh;
531 mesh.prim = GL_TRIANGLES;
532 mesh.num_verts = num_verts;
533 mesh.num_faces = num_tris;
534 mesh.vbo = buf[0];
535 mesh.ibo = buf[1];
536 return mesh;
537 }
539 static void distort_texcoords(float *tc, float aspect, float lens_center_offset, float scale, const float *dist_factors)
540 {
541 // map tc [0, 1] -> [-1, 1]
542 float ptx = tc[0] * 2.0 - 1.0;
543 float pty = tc[1] * 2.0 - 1.0;
545 ptx += lens_center_offset * 2.0;
546 pty /= aspect; // correct for aspect ratio
548 float rad = barrel_scale(ptx * ptx + pty * pty, dist_factors);
549 ptx *= rad; // scale the point by the computer distortion radius
550 pty *= rad;
552 ptx /= scale;
553 pty /= scale;
555 pty *= aspect;
556 ptx -= lens_center_offset * 2.0;
558 // map back to range [0, 1]
559 tc[0] = ptx * 0.5 + 0.5;
560 tc[1] = pty * 0.5 + 0.5;
561 }
563 static float barrel_scale(float rad, const float *k)
564 {
565 float radsq = rad * rad;
566 float radquad = radsq * radsq;
567 return k[0] + k[1] * radsq + k[2] * radquad + k[3] * radquad * radsq;
568 }