oculus2

view src/main.c @ 4:d64830551c32

hurray, it works
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 04 Sep 2014 08:31:12 +0300
parents 096b18432ba7
children cd9f1560b909
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <SDL2/SDL.h>
5 #include <GL/glew.h>
7 #ifdef WIN32
8 #define OVR_OS_WIN32
9 #endif
10 #ifdef __APPLE__
11 #define OVR_OS_MAC
12 #endif
14 #include <OVR_CAPI.h>
15 #include <OVR_CAPI_GL.h>
17 int init(void);
18 void cleanup(void);
19 void toggle_hmd_fullscreen(void);
20 void display(void);
21 void draw_scene(void);
22 void draw_box(float xsz, float ysz, float zsz, float norm_sign);
23 void update_rtarg(int width, int height);
24 int handle_event(SDL_Event *ev);
25 int key_event(int key, int state);
26 void reshape(int x, int y);
27 unsigned int next_pow2(unsigned int x);
28 void quat_to_matrix(const float *quat, float *mat);
29 unsigned int gen_chess_tex(float r0, float g0, float b0, float r1, float g1, float b1);
31 /* forward declaration to avoid including non-public headers of libovr */
32 OVR_EXPORT void ovrhmd_EnableHSWDisplaySDKRender(ovrHmd hmd, ovrBool enable);
34 static SDL_Window *win;
35 static SDL_GLContext ctx;
36 static int win_width, win_height;
38 static unsigned int fbo, fb_tex, fb_depth;
39 static int fb_width, fb_height;
40 static int fb_tex_width, fb_tex_height;
42 static ovrHmd hmd;
43 static ovrSizei eyeres[2];
44 static ovrEyeRenderDesc eye_rdesc[2];
45 static ovrGLTexture fb_ovr_tex[2];
47 static unsigned int chess_tex;
50 int main(int argc, char **argv)
51 {
52 if(init() == -1) {
53 return 1;
54 }
56 for(;;) {
57 SDL_Event ev;
58 while(SDL_PollEvent(&ev)) {
59 if(handle_event(&ev) == -1) {
60 goto done;
61 }
62 }
63 display();
64 }
66 done:
67 cleanup();
68 return 0;
69 }
72 int init(void)
73 {
74 int i, x, y;
75 unsigned int flags, dcaps;
76 union ovrGLConfig glcfg;
78 /* libovr must be initialized before we create the OpenGL context */
79 ovr_Initialize();
81 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
83 x = y = SDL_WINDOWPOS_UNDEFINED;
84 flags = SDL_WINDOW_OPENGL;
85 if(!(win = SDL_CreateWindow("press 'f' to move to the HMD", x, y, 1280, 800, flags))) {
86 fprintf(stderr, "failed to create window\n");
87 return -1;
88 }
89 if(!(ctx = SDL_GL_CreateContext(win))) {
90 fprintf(stderr, "failed to create OpenGL context\n");
91 return -1;
92 }
94 glewInit();
96 if(!(hmd = ovrHmd_Create(0))) {
97 fprintf(stderr, "failed to open Oculus HMD, falling back to virtual debug HMD\n");
98 if(!(hmd = ovrHmd_CreateDebug(ovrHmd_DK2))) {
99 fprintf(stderr, "failed to create virtual debug HMD\n");
100 return -1;
101 }
102 }
103 printf("initialized HMD: %s - %s\n", hmd->Manufacturer, hmd->ProductName);
105 SDL_SetWindowSize(win, hmd->Resolution.w, hmd->Resolution.h);
106 SDL_SetWindowPosition(win, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
107 win_width = hmd->Resolution.w;
108 win_height = hmd->Resolution.h;
110 ovrHmd_ConfigureTracking(hmd, 0xffffffff, 0);
111 eyeres[0] = ovrHmd_GetFovTextureSize(hmd, ovrEye_Left, hmd->DefaultEyeFov[0], 1.0);
112 eyeres[1] = ovrHmd_GetFovTextureSize(hmd, ovrEye_Right, hmd->DefaultEyeFov[1], 1.0);
114 fb_width = eyeres[0].w + eyeres[1].w;
115 fb_height = eyeres[0].h > eyeres[1].h ? eyeres[0].h : eyeres[1].h;
116 update_rtarg(fb_width, fb_height);
118 for(i=0; i<2; i++) {
119 fb_ovr_tex[i].OGL.Header.API = ovrRenderAPI_OpenGL;
120 fb_ovr_tex[i].OGL.Header.TextureSize.w = fb_tex_width;
121 fb_ovr_tex[i].OGL.Header.TextureSize.h = fb_tex_height;
122 fb_ovr_tex[i].OGL.Header.RenderViewport.Pos.x = i == 0 ? 0 : fb_width / 2.0;
123 fb_ovr_tex[i].OGL.Header.RenderViewport.Pos.y = fb_tex_height - fb_height;
124 fb_ovr_tex[i].OGL.Header.RenderViewport.Size.w = fb_width / 2.0;
125 fb_ovr_tex[i].OGL.Header.RenderViewport.Size.h = fb_height;
126 fb_ovr_tex[i].OGL.TexId = fb_tex;
127 }
129 memset(&glcfg, 0, sizeof glcfg);
130 glcfg.OGL.Header.API = ovrRenderAPI_OpenGL;
131 glcfg.OGL.Header.RTSize = hmd->Resolution;
132 glcfg.OGL.Header.Multisample = 1;
134 if(hmd->HmdCaps & ovrHmdCap_ExtendDesktop) {
135 printf("running in \"extended desktop\" mode\n");
136 } else {
137 #ifdef WIN32
138 void *sys_win = GetActiveWindow();
139 glcfg.OGL.Window = sys_win;
140 glcfg.OGL.DC = wglGetCurrentDC();
141 ovrHmd_AttachToWindow(hmd, sys_win, 0, 0);
142 #endif
143 printf("running in \"direct-hmd\" mode\n");
144 }
145 ovrHmd_SetEnabledCaps(hmd, ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction);
147 dcaps = ovrDistortionCap_Chromatic | ovrDistortionCap_Vignette | ovrDistortionCap_TimeWarp;
148 if(!ovrHmd_ConfigureRendering(hmd, &glcfg.Config, dcaps, hmd->DefaultEyeFov, eye_rdesc)) {
149 fprintf(stderr, "failed to configure distortion renderer\n");
150 }
152 ovrhmd_EnableHSWDisplaySDKRender(hmd, 0);
154 glEnable(GL_DEPTH_TEST);
155 glEnable(GL_CULL_FACE);
156 glEnable(GL_LIGHTING);
157 glEnable(GL_LIGHT0);
158 glEnable(GL_LIGHT1);
159 glEnable(GL_NORMALIZE);
161 glClearColor(0.1, 0.1, 0.1, 1);
163 chess_tex = gen_chess_tex(1.0, 0.7, 0.4, 0.4, 0.7, 1.0);
164 return 0;
165 }
167 void cleanup(void)
168 {
169 if(hmd) {
170 ovrHmd_Destroy(hmd);
171 }
172 ovr_Shutdown();
174 SDL_Quit();
175 }
177 void toggle_hmd_fullscreen(void)
178 {
179 static int fullscr, prev_x, prev_y;
180 fullscr = !fullscr;
182 if(fullscr) {
183 SDL_GetWindowPosition(win, &prev_x, &prev_y);
184 SDL_SetWindowPosition(win, hmd->WindowsPos.x, hmd->WindowsPos.y);
185 SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP);
186 } else {
187 SDL_SetWindowFullscreen(win, 0);
188 SDL_SetWindowPosition(win, prev_x, prev_y);
189 }
190 }
192 void display(void)
193 {
194 int i;
195 ovrMatrix4f proj;
196 ovrPosef pose[2];
197 float rot_mat[16];
199 /* the drawing starts with a call to ovrHmd_BeginFrame */
200 ovrHmd_BeginFrame(hmd, 0);
202 /* start drawing onto our texture render target */
203 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
204 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
206 /* for each eye ... */
207 for(i=0; i<2; i++) {
208 int eye = hmd->EyeRenderOrder[i];
210 /* -- viewport transformation --
211 * setup the viewport to draw in the left half of the framebuffer when we're
212 * rendering the left eye's view (0, 0, width/2, height), and in the right half
213 * of the framebuffer for the right eye's view (width/2, 0, width/2, height)
214 */
215 glViewport(eye == 0 ? 0 : fb_width / 2, 0, fb_width / 2, fb_height);
217 /* -- projection transformation --
218 * we'll just have to use the projection matrix supplied by the oculus SDK for this eye
219 * note that libovr matrices are the transpose of what OpenGL expects, so we have to
220 * use glLoadTransposeMatrixf instead of glLoadMatrixf to load it.
221 */
222 proj = ovrMatrix4f_Projection(hmd->DefaultEyeFov[eye], 0.5, 500.0, 1);
223 glMatrixMode(GL_PROJECTION);
224 glLoadTransposeMatrixf(proj.M[0]);
226 /* -- view/camera transformation --
227 * we need to construct a view matrix by combining all the information provided by the oculus
228 * SDK, about the position and orientation of the user's head in the world.
229 */
230 pose[eye] = ovrHmd_GetEyePose(hmd, eye);
231 glMatrixMode(GL_MODELVIEW);
232 glLoadIdentity();
233 glTranslatef(eye_rdesc[eye].ViewAdjust.x, eye_rdesc[eye].ViewAdjust.y, eye_rdesc[eye].ViewAdjust.z);
234 /* retrieve the orientation quaternion and convert it to a rotation matrix */
235 quat_to_matrix(&pose[eye].Orientation.x, rot_mat);
236 glMultMatrixf(rot_mat);
237 /* translate the view matrix with the positional tracking */
238 glTranslatef(-pose[eye].Position.x, -pose[eye].Position.y, -pose[eye].Position.z);
239 /* move the camera to the eye level of the user */
240 glTranslatef(0, -ovrHmd_GetFloat(hmd, OVR_KEY_EYE_HEIGHT, 1.65), 0);
242 /* finally draw the scene for this eye */
243 draw_scene();
244 }
246 /* after drawing both eyes into the texture render target, revert to drawing directly to the
247 * display, and we call ovrHmd_EndFrame, to let the Oculus SDK draw both images properly
248 * compensated for lens distortion and chromatic abberation onto the HMD screen.
249 */
250 glBindFramebuffer(GL_FRAMEBUFFER, 0);
251 glViewport(0, 0, win_width, win_height);
253 ovrHmd_EndFrame(hmd, pose, &fb_ovr_tex[0].Texture);
255 assert(glGetError() == GL_NO_ERROR);
256 }
258 void draw_scene(void)
259 {
260 int i;
261 float grey[] = {0.8, 0.8, 0.8, 1};
262 float col[] = {0, 0, 0, 1};
263 float lpos[][4] = {
264 {-8, 2, 10, 1},
265 {0, 15, 0, 1}
266 };
267 float lcol[][4] = {
268 {0.8, 0.8, 0.8, 1},
269 {0.4, 0.3, 0.3, 1}
270 };
272 for(i=0; i<2; i++) {
273 glLightfv(GL_LIGHT0 + i, GL_POSITION, lpos[i]);
274 glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lcol[i]);
275 }
277 glMatrixMode(GL_MODELVIEW);
279 glPushMatrix();
280 glTranslatef(0, 10, 0);
281 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, grey);
282 glBindTexture(GL_TEXTURE_2D, chess_tex);
283 glEnable(GL_TEXTURE_2D);
284 draw_box(30, 20, 30, -1.0);
285 glDisable(GL_TEXTURE_2D);
286 glPopMatrix();
288 for(i=0; i<4; i++) {
289 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, grey);
290 glPushMatrix();
291 glTranslatef(i & 1 ? 5 : -5, 1, i & 2 ? -5 : 5);
292 draw_box(0.5, 2, 0.5, 1.0);
293 glPopMatrix();
295 col[0] = i & 1 ? 1.0 : 0.3;
296 col[1] = i == 0 ? 1.0 : 0.3;
297 col[2] = i & 2 ? 1.0 : 0.3;
298 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
300 glPushMatrix();
301 if(i & 1) {
302 glTranslatef(0, 0.25, i & 2 ? 2 : -2);
303 } else {
304 glTranslatef(i & 2 ? 2 : -2, 0.25, 0);
305 }
306 draw_box(0.5, 0.5, 0.5, 1.0);
307 glPopMatrix();
308 }
309 }
311 void draw_box(float xsz, float ysz, float zsz, float norm_sign)
312 {
313 glMatrixMode(GL_MODELVIEW);
314 glScalef(xsz * 0.5, ysz * 0.5, zsz * 0.5);
316 if(norm_sign < 0.0) {
317 glFrontFace(GL_CW);
318 }
320 glBegin(GL_QUADS);
321 glNormal3f(0, 0, 1 * norm_sign);
322 glTexCoord2f(0, 0); glVertex3f(-1, -1, 1);
323 glTexCoord2f(1, 0); glVertex3f(1, -1, 1);
324 glTexCoord2f(1, 1); glVertex3f(1, 1, 1);
325 glTexCoord2f(0, 1); glVertex3f(-1, 1, 1);
326 glNormal3f(1 * norm_sign, 0, 0);
327 glTexCoord2f(0, 0); glVertex3f(1, -1, 1);
328 glTexCoord2f(1, 0); glVertex3f(1, -1, -1);
329 glTexCoord2f(1, 1); glVertex3f(1, 1, -1);
330 glTexCoord2f(0, 1); glVertex3f(1, 1, 1);
331 glNormal3f(0, 0, -1 * norm_sign);
332 glTexCoord2f(0, 0); glVertex3f(1, -1, -1);
333 glTexCoord2f(1, 0); glVertex3f(-1, -1, -1);
334 glTexCoord2f(1, 1); glVertex3f(-1, 1, -1);
335 glTexCoord2f(0, 1); glVertex3f(1, 1, -1);
336 glNormal3f(-1 * norm_sign, 0, 0);
337 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
338 glTexCoord2f(1, 0); glVertex3f(-1, -1, 1);
339 glTexCoord2f(1, 1); glVertex3f(-1, 1, 1);
340 glTexCoord2f(0, 1); glVertex3f(-1, 1, -1);
341 glEnd();
342 glBegin(GL_TRIANGLE_FAN);
343 glNormal3f(0, 1 * norm_sign, 0);
344 glTexCoord2f(0.5, 0.5); glVertex3f(0, 1, 0);
345 glTexCoord2f(0, 0); glVertex3f(-1, 1, 1);
346 glTexCoord2f(1, 0); glVertex3f(1, 1, 1);
347 glTexCoord2f(1, 1); glVertex3f(1, 1, -1);
348 glTexCoord2f(0, 1); glVertex3f(-1, 1, -1);
349 glTexCoord2f(0, 0); glVertex3f(-1, 1, 1);
350 glEnd();
351 glBegin(GL_TRIANGLE_FAN);
352 glNormal3f(0, -1 * norm_sign, 0);
353 glTexCoord2f(0.5, 0.5); glVertex3f(0, -1, 0);
354 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
355 glTexCoord2f(1, 0); glVertex3f(1, -1, -1);
356 glTexCoord2f(1, 1); glVertex3f(1, -1, 1);
357 glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
358 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
359 glEnd();
361 glFrontFace(GL_CCW);
362 }
364 void update_rtarg(int width, int height)
365 {
366 if(!fbo) {
367 glGenFramebuffers(1, &fbo);
368 glGenTextures(1, &fb_tex);
369 glGenRenderbuffers(1, &fb_depth);
371 glBindTexture(GL_TEXTURE_2D, fb_tex);
372 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
373 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
374 }
376 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
378 fb_tex_width = next_pow2(width);
379 fb_tex_height = next_pow2(height);
381 glBindTexture(GL_TEXTURE_2D, fb_tex);
382 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fb_tex_width, fb_tex_height, 0,
383 GL_RGBA, GL_UNSIGNED_BYTE, 0);
384 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex, 0);
386 glBindRenderbuffer(GL_RENDERBUFFER, fb_depth);
387 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, fb_tex_width, fb_tex_height);
388 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fb_depth);
390 if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
391 fprintf(stderr, "incomplete framebuffer!\n");
392 }
394 glBindFramebuffer(GL_FRAMEBUFFER, 0);
395 printf("created render target: %dx%d (texture size: %dx%d)\n", width, height, fb_tex_width, fb_tex_height);
396 }
398 int handle_event(SDL_Event *ev)
399 {
400 switch(ev->type) {
401 case SDL_QUIT:
402 return -1;
404 case SDL_KEYDOWN:
405 case SDL_KEYUP:
406 if(key_event(ev->key.keysym.sym, ev->key.state == SDL_PRESSED) == -1) {
407 return -1;
408 }
409 break;
411 default:
412 break;
413 }
415 return 0;
416 }
418 int key_event(int key, int state)
419 {
420 if(state) {
421 ovrHSWDisplayState hsw;
422 ovrHmd_GetHSWDisplayState(hmd, &hsw);
423 if(hsw.Displayed) {
424 ovrHmd_DismissHSWDisplay(hmd);
425 }
427 switch(key) {
428 case 27:
429 return -1;
431 case ' ':
432 /* allow the user to recenter by pressing space */
433 ovrHmd_RecenterPose(hmd);
434 break;
436 case 'f':
437 /* press f to move the window to the HMD */
438 toggle_hmd_fullscreen();
439 break;
441 default:
442 break;
443 }
444 }
445 return 0;
446 }
448 unsigned int next_pow2(unsigned int x)
449 {
450 x -= 1;
451 x |= x >> 1;
452 x |= x >> 2;
453 x |= x >> 4;
454 x |= x >> 8;
455 x |= x >> 16;
456 return x + 1;
457 }
459 void quat_to_matrix(const float *quat, float *mat)
460 {
461 mat[0] = 1.0 - 2.0 * quat[1] * quat[1] - 2.0 * quat[2] * quat[2];
462 mat[4] = 2.0 * quat[0] * quat[1] + 2.0 * quat[3] * quat[2];
463 mat[8] = 2.0 * quat[2] * quat[0] - 2.0 * quat[3] * quat[1];
464 mat[12] = 0.0f;
466 mat[1] = 2.0 * quat[0] * quat[1] - 2.0 * quat[3] * quat[2];
467 mat[5] = 1.0 - 2.0 * quat[0]*quat[0] - 2.0 * quat[2]*quat[2];
468 mat[9] = 2.0 * quat[1] * quat[2] + 2.0 * quat[3] * quat[0];
469 mat[13] = 0.0f;
471 mat[2] = 2.0 * quat[2] * quat[0] + 2.0 * quat[3] * quat[1];
472 mat[6] = 2.0 * quat[1] * quat[2] - 2.0 * quat[3] * quat[0];
473 mat[10] = 1.0 - 2.0 * quat[0]*quat[0] - 2.0 * quat[1]*quat[1];
474 mat[14] = 0.0f;
476 mat[3] = mat[7] = mat[11] = 0.0f;
477 mat[15] = 1.0f;
478 }
480 unsigned int gen_chess_tex(float r0, float g0, float b0, float r1, float g1, float b1)
481 {
482 int i, j;
483 unsigned int tex;
484 unsigned char img[8 * 8 * 3];
485 unsigned char *pix = img;
487 for(i=0; i<8; i++) {
488 for(j=0; j<8; j++) {
489 int black = (i & 1) == (j & 1);
490 pix[0] = (black ? r0 : r1) * 255;
491 pix[1] = (black ? g0 : g1) * 255;
492 pix[2] = (black ? b0 : b1) * 255;
493 pix += 3;
494 }
495 }
497 glGenTextures(1, &tex);
498 glBindTexture(GL_TEXTURE_2D, tex);
499 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
500 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
501 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, img);
503 return tex;