oculus1

view src/main.cc @ 29:9a973ef0e2a3

fixed the performance issue under MacOSX by replacing glutSolidTeapot (which uses glEvalMesh) with my own teapot generator.
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 27 Oct 2013 06:31:18 +0200
parents cedf581048c7
children
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include "opengl.h"
6 #include "vr.h"
7 #include "camera.h"
8 #include "sdr.h"
9 #include "teapot.h"
11 #ifdef __APPLE__
12 #include <OpenGL/OpenGL.h>
13 #include <OpenGL/CGLCurrent.h>
14 #include <ApplicationServices/ApplicationServices.h>
15 #endif
17 static bool init();
18 static void cleanup();
19 static void disp();
20 static void disp_vr();
21 static void draw_scene();
22 static void draw_teapot(float size);
23 static void draw_squares();
24 static void draw_grid(float size, float spacing);
25 static void toggle_mouselook();
26 static void toggle_fullscreen();
27 static void idle();
28 static void reshape(int x, int y);
29 static void keyb(unsigned char key, int x, int y);
30 static void keyup(unsigned char key, int x, int y);
31 static void mouse(int bn, int st, int x, int y);
32 static void motion(int x, int y);
33 static void passive(int x, int y);
34 static void sball_rotate(int rx, int ry, int rz);
35 static bool parse_args(int argc, char **argv);
36 static void printfps();
37 static int next_pow2(int x);
39 static VRFpsCamera cam;
40 static int width, height;
41 static bool use_vr = false;
43 static bool keystate[256];
45 static int rtarg_width, rtarg_height, rtarg_tex_width, rtarg_tex_height;
46 static float tex_scale_x, tex_scale_y;
47 static unsigned int fbo, tex[2], zbuf;
49 static unsigned int teapot_sdr;
51 static bool fullscreen_pending;
54 int main(int argc, char **argv)
55 {
56 glutInitWindowSize(1280, 800);
57 glutInit(&argc, argv);
59 if(!parse_args(argc, argv)) {
60 return 1;
61 }
63 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
64 glutCreateWindow("oculus vr test 01");
66 width = glutGet(GLUT_WINDOW_WIDTH);
67 height = glutGet(GLUT_WINDOW_HEIGHT);
69 glutDisplayFunc(use_vr ? disp_vr : disp);
70 glutIdleFunc(idle);
71 glutReshapeFunc(reshape);
72 glutKeyboardFunc(keyb);
73 glutKeyboardUpFunc(keyup);
74 glutMouseFunc(mouse);
75 glutMotionFunc(motion);
76 glutSpaceballRotateFunc(sball_rotate);
78 if(!init()) {
79 return 1;
80 }
81 atexit(cleanup);
83 glutMainLoop();
84 return 0;
85 }
87 static bool init()
88 {
89 glewInit(); // this must be first
91 glEnable(GL_DEPTH_TEST);
92 glEnable(GL_LIGHTING);
93 glEnable(GL_CULL_FACE);
95 glEnable(GL_LIGHT0);
96 glEnable(GL_LIGHTING);
97 glEnable(GL_NORMALIZE);
99 // y = height of neck
100 cam.input_move(0, 1.65, 0);
102 if(use_vr) {
103 if(vr_init(VR_INIT_OCULUS) == -1) {
104 return false;
105 }
107 // reshape to the size of the VR display
108 int xsz = vr_get_width();
109 int ysz = vr_get_height();
111 glutReshapeWindow(xsz, ysz);
113 rtarg_width = (xsz + xsz / 2) / 2;
114 rtarg_height = ysz + ysz / 2;
116 rtarg_tex_width = next_pow2(rtarg_width);
117 rtarg_tex_height = next_pow2(rtarg_height);
118 tex_scale_x = (float)rtarg_width / (float)rtarg_tex_width;
119 tex_scale_y = (float)rtarg_height / (float)rtarg_tex_height;
121 printf("render target: %dx%d (%dx%d)\n", rtarg_width, rtarg_height, rtarg_tex_width, rtarg_tex_height);
123 // create render targets for each eye
124 GLenum wrap_mode = GL_CLAMP_TO_EDGE;
125 if(!GLEW_SGIS_texture_edge_clamp) {
126 wrap_mode = GL_CLAMP;
127 }
129 glGenTextures(2, tex);
130 for(int i=0; i<2; i++) {
131 glBindTexture(GL_TEXTURE_2D, tex[i]);
132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
133 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode);
136 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, rtarg_tex_width, rtarg_tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
137 }
139 // create the depth render buffer
140 glGenRenderbuffersEXT(1, &zbuf);
141 glBindRenderbufferEXT(GL_RENDERBUFFER, zbuf);
142 glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, rtarg_tex_width, rtarg_tex_height);
144 // create the FBO
145 glGenFramebuffersEXT(1, &fbo);
146 glBindFramebufferEXT(GL_FRAMEBUFFER, fbo);
147 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[0], 0);
148 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, zbuf);
149 }
151 teapot_sdr = create_program_load("sdr/phong.v.glsl", "sdr/phong.p.glsl");
153 // enable vsync
154 #ifdef __APPLE__
155 int swapint = 1;
156 CGLContextObj ctx = CGLGetCurrentContext();
157 CGLSetParameter(ctx, kCGLCPSwapInterval, &swapint);
158 #endif
160 #ifdef __APPLE__
161 // this is required for glutWarpPointer to work correctly, otherwise after every
162 // warp, mac os x freezes event processing for 1/4 second.
163 CGSetLocalEventsSuppressionInterval(0.0);
164 #endif
165 return true;
166 }
168 static void cleanup()
169 {
170 glDeleteTextures(2, tex);
171 glDeleteRenderbuffersEXT(1, &zbuf);
172 glDeleteFramebuffersEXT(1, &fbo);
173 vr_shutdown();
174 }
176 static void handle_input(float dt)
177 {
178 Vector3 inpv;
179 float offs = dt * 2.0;
181 if(keystate['w'] || keystate['W']) {
182 inpv.z -= offs;
183 }
184 if(keystate['s'] || keystate['S']) {
185 inpv.z += offs;
186 }
187 if(keystate['d'] || keystate['D']) {
188 inpv.x += offs;
189 }
190 if(keystate['a'] || keystate['A']) {
191 inpv.x -= offs;
192 }
194 cam.input_move(inpv.x, inpv.y, inpv.z);
195 }
197 // display function used in regular mode
198 static void disp()
199 {
200 static long prev_msec;
201 long msec = glutGet(GLUT_ELAPSED_TIME);
202 float dt = (msec - prev_msec) / 1000.0;
203 prev_msec = msec;
205 handle_input(dt);
207 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
209 glMatrixMode(GL_PROJECTION);
210 glLoadIdentity();
211 gluPerspective(45, (float)width / (float)height, 0.25, 500.0);
213 glMatrixMode(GL_MODELVIEW);
214 glLoadIdentity();
215 cam.use_inverse();
216 draw_scene();
218 glutSwapBuffers();
219 assert(glGetError() == GL_NO_ERROR);
221 printfps();
222 }
224 // display function used in VR mode
225 static void disp_vr()
226 {
227 static long prev_msec;
228 long msec = glutGet(GLUT_ELAPSED_TIME);
229 float dt = (msec - prev_msec) / 1000.0;
230 prev_msec = msec;
232 handle_input(dt);
233 cam.track_vr();
235 float proj_matrix[16];
237 float eye_dist = vr_get_eyedist();
239 glViewport(0, 0, rtarg_width, rtarg_height);
241 glClearColor(0.1, 0.1, 0.1, 1.0);
243 // draw left view
244 glBindFramebufferEXT(GL_FRAMEBUFFER, fbo);
245 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[0], 0);
247 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
249 glMatrixMode(GL_PROJECTION);
250 vr_get_proj_matrix(proj_matrix, VR_EYE_LEFT);
251 glLoadTransposeMatrixf(proj_matrix);
253 glMatrixMode(GL_MODELVIEW);
254 glLoadIdentity();
255 glTranslatef(eye_dist / 2.0, 0, 0);
256 cam.use_inverse();
257 draw_scene();
260 // draw right view
261 glBindFramebufferEXT(GL_FRAMEBUFFER, fbo);
262 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[1], 0);
264 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
266 glMatrixMode(GL_PROJECTION);
267 vr_get_proj_matrix(proj_matrix, VR_EYE_RIGHT);
268 glLoadTransposeMatrixf(proj_matrix);
270 glMatrixMode(GL_MODELVIEW);
271 glLoadIdentity();
272 glTranslatef(-eye_dist / 2.0, 0, 0);
273 cam.use_inverse();
274 draw_scene();
276 // return to the regular window framebuffer
277 glBindFramebuffer(GL_FRAMEBUFFER, 0);
278 glViewport(0, 0, width, height);
280 glClearColor(0, 0, 0, 0);
281 glClear(GL_COLOR_BUFFER_BIT);
283 vr_draw_eye(VR_EYE_LEFT, tex[0], tex_scale_x, tex_scale_y);
284 vr_draw_eye(VR_EYE_RIGHT, tex[1], tex_scale_x, tex_scale_y);
286 glutSwapBuffers();
287 assert(glGetError() == GL_NO_ERROR);
289 glFinish();
291 printfps();
292 }
294 static void draw_scene()
295 {
296 float lpos[] = {0, 60, 0, 1};
297 glLightfv(GL_LIGHT0, GL_POSITION, lpos);
299 draw_grid(50.0, 2.5);
301 static const Vector2 teapos[] = {
302 Vector2(-8, -8), Vector2(8, -8), Vector2(8, 8), Vector2(-8, 8)
303 };
304 static const float teasize[] = { 1.0, 2.0, 1.7, 1.4 };
305 static const float teacolor[][4] = {
306 {1.0, 0.4, 0.2, 1.0}, {0.2, 0.35, 1.0, 1.0}, {1.0, 0.9, 0.3, 1.0}, {0.3, 1.0, 0.4, 1.0}
307 };
308 static const float spec[] = {0.8, 0.8, 0.8, 1.0};
310 glUseProgram(teapot_sdr);
312 for(int i=0; i<4; i++) {
313 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, teacolor[i]);
314 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
315 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 70.0);
317 glPushMatrix();
318 glTranslatef(teapos[i].x, 0, teapos[i].y);
319 draw_teapot(teasize[i]);
320 glPopMatrix();
321 }
322 glUseProgram(0);
324 draw_squares();
325 }
327 static void draw_teapot(float size)
328 {
329 static int tealist;
331 if(!tealist) {
332 tealist = glGenLists(1);
333 glNewList(tealist, GL_COMPILE);
334 //glutSolidTeapot(1.0);
335 bezier_teapot(1.0);
336 glEndList();
337 }
339 glMatrixMode(GL_MODELVIEW);
340 glPushMatrix();
341 glScalef(size, size, size);
342 glTranslatef(0, 0.73, 0);
344 glFrontFace(GL_CW);
345 glCallList(tealist);
346 //bezier_teapot(1.0);
347 glFrontFace(GL_CCW);
349 glPopMatrix();
350 }
352 static void draw_squares()
353 {
354 static const int num_sq = 8;
356 glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
357 glDisable(GL_LIGHTING);
359 glMatrixMode(GL_MODELVIEW);
360 glPushMatrix();
361 glTranslatef(0, 1, 0);
364 glLineWidth(2.0);
365 glColor3f(1.0, 0.7, 0.2);
367 float zdist = 2.0;
368 for(int i=0; i<num_sq; i++) {
369 glBegin(GL_LINE_LOOP);
370 glVertex3f(-1, -1, -zdist);
371 glVertex3f(1, -1, -zdist);
372 glVertex3f(1, 1, -zdist);
373 glVertex3f(-1, 1, -zdist);
374 glEnd();
376 zdist += 2.0;
377 }
379 glPopMatrix();
380 glPopAttrib();
381 }
383 static void draw_grid(float size, float spacing)
384 {
385 int num_lines = size / spacing;
386 float dist = size / 2.0;
388 glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
389 glDisable(GL_LIGHTING);
391 glLineWidth(1.0);
393 glBegin(GL_LINES);
394 glColor3f(0.4, 0.4, 0.4);
396 float x = -dist;
397 for(int i=0; i<=num_lines; i++) {
398 if(i != num_lines / 2) {
399 glVertex3f(-dist, 0, x);
400 glVertex3f(dist, 0, x);
401 glVertex3f(x, 0, -dist);
402 glVertex3f(x, 0, dist);
403 }
404 x += spacing;
405 }
406 glEnd();
408 glLineWidth(2.0);
410 glBegin(GL_LINES);
411 glColor3f(1.0, 0, 0);
412 glVertex3f(-dist, 0, 0);
413 glVertex3f(dist, 0, 0);
414 glColor3f(0, 1.0, 0);
415 glVertex3f(0, 0, -dist);
416 glVertex3f(0, 0, dist);
417 glEnd();
419 glPopAttrib();
420 }
422 static bool mouselook;
424 static void toggle_mouselook()
425 {
426 mouselook = !mouselook;
427 if(mouselook) {
428 glutPassiveMotionFunc(passive);
429 glutSetCursor(GLUT_CURSOR_NONE);
430 glutWarpPointer(width / 2, height / 2);
431 } else {
432 glutPassiveMotionFunc(0);
433 glutSetCursor(GLUT_CURSOR_INHERIT);
434 }
435 }
437 static bool fullscreen;
438 static void toggle_fullscreen()
439 {
440 static int prev_x, prev_y;
441 static int prev_xsz, prev_ysz;
443 fullscreen = !fullscreen;
445 if(fullscreen) {
446 prev_x = glutGet(GLUT_WINDOW_X);
447 prev_y = glutGet(GLUT_WINDOW_Y);
448 prev_xsz = width;
449 prev_ysz = height;
451 if(use_vr) {
452 // go fullscreen to the correct monitor
453 int x, y;
454 vr_get_display_pos(&x, &y);
455 glutPositionWindow(x, y);
457 // also warp the mouse and enable mouselook
458 glutWarpPointer(x + width / 2, y + height / 2);
460 /* Ok this next line needs some explanation:
461 * glutPositionWindow, doesn't necessarilly get executed directly.
462 * GLUT might defer it for the next round of event processing, just
463 * so that it may coalesce multiple positioning requests.
464 * However that means that if we just called glutFullScreen right
465 * here, the window manager would have no idea that the window will
466 * move to another monitor, thus making it the size of the monitor
467 * it occupied before the move.
468 * So I'm setting a flag here, and execute the glutFullScreen call
469 * at the next idle invocation. (would display be a safer place?).
470 */
471 fullscreen_pending = true;
472 } else {
473 glutFullScreen();
474 toggle_mouselook();
475 }
476 } else {
477 glutReshapeWindow(prev_xsz, prev_ysz);
478 glutPositionWindow(prev_x, prev_y);
480 if(mouselook) {
481 toggle_mouselook();
482 }
483 }
484 glutPostRedisplay();
485 }
488 static void idle()
489 {
490 if(fullscreen_pending) {
491 glutFullScreen();
493 if(!mouselook) {
494 toggle_mouselook();
495 }
496 fullscreen_pending = false;
497 }
498 glutPostRedisplay();
499 }
502 static void reshape(int x, int y)
503 {
504 width = x;
505 height = y;
506 }
508 static void keyb(unsigned char key, int x, int y)
509 {
510 switch(key) {
511 case 27:
512 exit(0);
514 case 'm':
515 toggle_mouselook();
516 break;
518 case 'f':
519 toggle_fullscreen();
520 break;
521 }
523 keystate[key] = true;
524 glutPostRedisplay();
525 }
527 static void keyup(unsigned char key, int x, int y)
528 {
529 keystate[key] = false;
530 glutPostRedisplay();
531 }
533 static bool bnstate[32];
534 static int prev_x, prev_y;
536 static void mouse(int bn, int st, int x, int y)
537 {
538 prev_x = x;
539 prev_y = y;
540 bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN;
541 }
543 static void motion(int x, int y)
544 {
545 if(mouselook) {
546 // just call passive, it does what we need
547 passive(x, y);
548 }
549 }
551 static void passive(int x, int y)
552 {
553 // no need to test mouselook; this callback is only set when mouselook is enabled
554 int center_x = width / 2;
555 int center_y = height / 2;
557 int dx = x - center_x;
558 int dy = y - center_y;
560 if(use_vr && fullscreen) {
561 dy = 0;
562 }
564 if(!dx && !dy) {
565 return;
566 }
568 float dtheta_deg = dy * 0.1;
569 float dphi_deg = dx * 0.1;
571 cam.input_rotate(DEG_TO_RAD(dtheta_deg), DEG_TO_RAD(dphi_deg), 0);
573 glutWarpPointer(center_x, center_y);
574 glutPostRedisplay();
575 }
577 static void sball_rotate(int rx, int ry, int rz)
578 {
579 }
581 static bool parse_args(int argc, char **argv)
582 {
583 for(int i=1; i<argc; i++) {
584 if(argv[i][0] == '-') {
585 if(strcmp(argv[i], "-vr") == 0) {
586 use_vr = true;
587 } else if(strcmp(argv[i], "-novr") == 0) {
588 use_vr = false;
589 } else {
590 fprintf(stderr, "invalid option: %s\n", argv[i]);
591 return false;
592 }
593 } else {
594 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
595 return false;
596 }
597 }
598 return true;
599 }
601 static void printfps()
602 {
603 static unsigned int last_upd, frames;
604 unsigned int msec = glutGet(GLUT_ELAPSED_TIME);
605 unsigned int dmsec = msec - last_upd;
607 frames++;
609 if(dmsec > 2000) {
610 float dt = (float)dmsec / 1000.0f;
612 printf("fps: %.2f \r", (float)frames / dt);
613 fflush(stdout);
615 frames = 0;
616 last_upd = msec;
617 }
618 }
620 static int next_pow2(int x)
621 {
622 x--;
623 x = (x >> 1) | x;
624 x = (x >> 2) | x;
625 x = (x >> 4) | x;
626 x = (x >> 8) | x;
627 x = (x >> 16) | x;
628 return x + 1;
629 }