libgoatvr
diff example/src/main.c @ 5:e63cb28fc644
working on the linux side a bit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 18 Sep 2014 10:56:45 +0300 |
parents | |
children | b71314e80654 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/example/src/main.c Thu Sep 18 10:56:45 2014 +0300 1.3 @@ -0,0 +1,449 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <assert.h> 1.7 +#include <SDL2/SDL.h> 1.8 +#include <GL/glew.h> 1.9 +#include "vr.h" 1.10 + 1.11 +int init(void); 1.12 +void cleanup(void); 1.13 +void toggle_hmd_fullscreen(void); 1.14 +void display(void); 1.15 +void draw_scene(void); 1.16 +void draw_box(float xsz, float ysz, float zsz, float norm_sign); 1.17 +void update_rtarg(int width, int height); 1.18 +int handle_event(SDL_Event *ev); 1.19 +int key_event(int key, int state); 1.20 +void reshape(int x, int y); 1.21 +unsigned int next_pow2(unsigned int x); 1.22 +void quat_to_matrix(const float *quat, float *mat); 1.23 +unsigned int gen_chess_tex(float r0, float g0, float b0, float r1, float g1, float b1); 1.24 + 1.25 +static SDL_Window *win; 1.26 +static SDL_GLContext ctx; 1.27 +static int win_width, win_height; 1.28 + 1.29 +static unsigned int fbo, fb_tex, fb_depth; 1.30 +static int fb_width, fb_height; 1.31 +static int fb_tex_width, fb_tex_height; 1.32 + 1.33 +static unsigned int chess_tex; 1.34 + 1.35 + 1.36 +int main(int argc, char **argv) 1.37 +{ 1.38 + if(init() == -1) { 1.39 + return 1; 1.40 + } 1.41 + 1.42 + for(;;) { 1.43 + SDL_Event ev; 1.44 + while(SDL_PollEvent(&ev)) { 1.45 + if(handle_event(&ev) == -1) { 1.46 + goto done; 1.47 + } 1.48 + } 1.49 + display(); 1.50 + } 1.51 + 1.52 +done: 1.53 + cleanup(); 1.54 + return 0; 1.55 +} 1.56 + 1.57 + 1.58 +int init(void) 1.59 +{ 1.60 + int x, y; 1.61 + unsigned int flags; 1.62 + 1.63 + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); 1.64 + 1.65 + x = y = SDL_WINDOWPOS_UNDEFINED; 1.66 + flags = SDL_WINDOW_OPENGL; 1.67 + if(!(win = SDL_CreateWindow("press 'f' to move to the HMD", x, y, 1280, 800, flags))) { 1.68 + fprintf(stderr, "failed to create window\n"); 1.69 + return -1; 1.70 + } 1.71 + if(!(ctx = SDL_GL_CreateContext(win))) { 1.72 + fprintf(stderr, "failed to create OpenGL context\n"); 1.73 + return -1; 1.74 + } 1.75 + 1.76 + glewInit(); 1.77 + 1.78 + if(vr_init() == -1) { 1.79 + return -1; 1.80 + } 1.81 + 1.82 + /* resize our window to match the HMD resolution */ 1.83 + win_width = vr_get_opti(VR_OPT_DISPLAY_WIDTH); 1.84 + win_height = vr_get_opti(VR_OPT_DISPLAY_HEIGHT); 1.85 + if(!win_width || !win_height) { 1.86 + SDL_GetWindowSize(win, &win_width, &win_height); 1.87 + } else { 1.88 + SDL_SetWindowSize(win, win_width, win_height); 1.89 + SDL_SetWindowPosition(win, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); 1.90 + } 1.91 + 1.92 + /* and create a single render target texture to encompass both eyes */ 1.93 + fb_width = vr_get_opti(VR_OPT_LEYE_XRES) + vr_get_opti(VR_OPT_REYE_XRES); 1.94 + fb_height = vr_get_opti(VR_OPT_LEYE_YRES); /* assuming both are the same */ 1.95 + if(!fb_width || !fb_height) { 1.96 + fb_width = win_width; 1.97 + fb_height = win_height; 1.98 + } 1.99 + update_rtarg(fb_width, fb_height); 1.100 + 1.101 + /* set our render texture and its active area */ 1.102 + vr_output_texture(fb_tex, 0, (float)(fb_tex_height - fb_height) / (float)fb_tex_height, 1.103 + (float)fb_width / (float)fb_tex_width, 1.0); 1.104 + 1.105 + glEnable(GL_DEPTH_TEST); 1.106 + glEnable(GL_CULL_FACE); 1.107 + glEnable(GL_LIGHTING); 1.108 + glEnable(GL_LIGHT0); 1.109 + glEnable(GL_LIGHT1); 1.110 + glEnable(GL_NORMALIZE); 1.111 + 1.112 + glClearColor(0.5, 0.1, 0.1, 1); 1.113 + 1.114 + chess_tex = gen_chess_tex(1.0, 0.7, 0.4, 0.4, 0.7, 1.0); 1.115 + return 0; 1.116 +} 1.117 + 1.118 +void cleanup(void) 1.119 +{ 1.120 + vr_shutdown(); 1.121 + SDL_Quit(); 1.122 +} 1.123 + 1.124 +void toggle_hmd_fullscreen(void) 1.125 +{ 1.126 + static int fullscr, prev_x, prev_y; 1.127 + fullscr = !fullscr; 1.128 + 1.129 + if(fullscr) { 1.130 + /* going fullscreen on the rift. save current window position, and move it 1.131 + * to the rift's part of the desktop before going fullscreen 1.132 + */ 1.133 + SDL_GetWindowPosition(win, &prev_x, &prev_y); 1.134 + SDL_SetWindowPosition(win, vr_get_opti(VR_OPT_WIN_XOFFS), vr_get_opti(VR_OPT_WIN_YOFFS)); 1.135 + SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP); 1.136 + } else { 1.137 + /* return to windowed mode and move the window back to its original position */ 1.138 + SDL_SetWindowFullscreen(win, 0); 1.139 + SDL_SetWindowPosition(win, prev_x, prev_y); 1.140 + } 1.141 +} 1.142 + 1.143 +void display(void) 1.144 +{ 1.145 + int i; 1.146 + float proj_mat[16]; 1.147 + float rot_mat[16], view_mat[16]; 1.148 + 1.149 + /* start drawing onto our texture render target */ 1.150 + glBindFramebuffer(GL_FRAMEBUFFER, fbo); 1.151 + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 1.152 + 1.153 + /* for each eye ... */ 1.154 + for(i=0; i<2; i++) { 1.155 + vr_begin(i); 1.156 + 1.157 + /* -- viewport transformation -- 1.158 + * setup the viewport to draw in the left half of the framebuffer when we're 1.159 + * rendering the left eye's view (0, 0, width/2, height), and in the right half 1.160 + * of the framebuffer for the right eye's view (width/2, 0, width/2, height) 1.161 + */ 1.162 + glViewport(i == 0 ? 0 : fb_width / 2, 0, fb_width / 2, fb_height); 1.163 + 1.164 + glMatrixMode(GL_PROJECTION); 1.165 + /* -- projection transformation -- 1.166 + * we'll just have to use the projection matrix supplied by the oculus SDK for this eye 1.167 + * note that libovr matrices are the transpose of what OpenGL expects, so we have to 1.168 + * use glLoadTransposeMatrixf instead of glLoadMatrixf to load it. 1.169 + */ 1.170 + if(vr_proj_matrix(i, 0.5, 500.0, proj_mat)) { 1.171 + glLoadTransposeMatrixf(proj_mat); 1.172 + } else { 1.173 + glLoadIdentity(); 1.174 + gluPerspective(50.0, (float)fb_width / 2.0 / (float)fb_height, 0.5, 500.0); 1.175 + } 1.176 + 1.177 + /* -- view/camera transformation -- 1.178 + * we need to construct a view matrix by combining all the information provided by the oculus 1.179 + * SDK, about the position and orientation of the user's head in the world. 1.180 + */ 1.181 + glMatrixMode(GL_MODELVIEW); 1.182 + vr_view_matrix(i, view_mat); 1.183 + glLoadTransposeMatrixf(view_mat); 1.184 + /* move the camera to the eye level of the user */ 1.185 + glTranslatef(0, -vr_get_optf(VR_OPT_EYE_HEIGHT), 0); 1.186 + 1.187 + /* finally draw the scene for this eye */ 1.188 + draw_scene(); 1.189 + 1.190 + vr_end(); 1.191 + } 1.192 + 1.193 + /* after drawing both eyes into the texture render target, revert to drawing directly to the 1.194 + * display, and we call ovrHmd_EndFrame, to let the Oculus SDK draw both images properly 1.195 + * compensated for lens distortion and chromatic abberation onto the HMD screen. 1.196 + */ 1.197 + glBindFramebuffer(GL_FRAMEBUFFER, 0); 1.198 + glViewport(0, 0, win_width, win_height); 1.199 + 1.200 + vr_swap_buffers(); 1.201 + 1.202 + assert(glGetError() == GL_NO_ERROR); 1.203 +} 1.204 + 1.205 +void draw_scene(void) 1.206 +{ 1.207 + int i; 1.208 + float grey[] = {0.8, 0.8, 0.8, 1}; 1.209 + float col[] = {0, 0, 0, 1}; 1.210 + float lpos[][4] = { 1.211 + {-8, 2, 10, 1}, 1.212 + {0, 15, 0, 1} 1.213 + }; 1.214 + float lcol[][4] = { 1.215 + {0.8, 0.8, 0.8, 1}, 1.216 + {0.4, 0.3, 0.3, 1} 1.217 + }; 1.218 + 1.219 + for(i=0; i<2; i++) { 1.220 + glLightfv(GL_LIGHT0 + i, GL_POSITION, lpos[i]); 1.221 + glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lcol[i]); 1.222 + } 1.223 + 1.224 + glMatrixMode(GL_MODELVIEW); 1.225 + 1.226 + glPushMatrix(); 1.227 + glTranslatef(0, 10, 0); 1.228 + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, grey); 1.229 + glBindTexture(GL_TEXTURE_2D, chess_tex); 1.230 + glEnable(GL_TEXTURE_2D); 1.231 + draw_box(30, 20, 30, -1.0); 1.232 + glDisable(GL_TEXTURE_2D); 1.233 + glPopMatrix(); 1.234 + 1.235 + for(i=0; i<4; i++) { 1.236 + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, grey); 1.237 + glPushMatrix(); 1.238 + glTranslatef(i & 1 ? 5 : -5, 1, i & 2 ? -5 : 5); 1.239 + draw_box(0.5, 2, 0.5, 1.0); 1.240 + glPopMatrix(); 1.241 + 1.242 + col[0] = i & 1 ? 1.0 : 0.3; 1.243 + col[1] = i == 0 ? 1.0 : 0.3; 1.244 + col[2] = i & 2 ? 1.0 : 0.3; 1.245 + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col); 1.246 + 1.247 + glPushMatrix(); 1.248 + if(i & 1) { 1.249 + glTranslatef(0, 0.25, i & 2 ? 2 : -2); 1.250 + } else { 1.251 + glTranslatef(i & 2 ? 2 : -2, 0.25, 0); 1.252 + } 1.253 + draw_box(0.5, 0.5, 0.5, 1.0); 1.254 + glPopMatrix(); 1.255 + } 1.256 +} 1.257 + 1.258 +void draw_box(float xsz, float ysz, float zsz, float norm_sign) 1.259 +{ 1.260 + glMatrixMode(GL_MODELVIEW); 1.261 + glScalef(xsz * 0.5, ysz * 0.5, zsz * 0.5); 1.262 + 1.263 + if(norm_sign < 0.0) { 1.264 + glFrontFace(GL_CW); 1.265 + } 1.266 + 1.267 + glBegin(GL_QUADS); 1.268 + glNormal3f(0, 0, 1 * norm_sign); 1.269 + glTexCoord2f(0, 0); glVertex3f(-1, -1, 1); 1.270 + glTexCoord2f(1, 0); glVertex3f(1, -1, 1); 1.271 + glTexCoord2f(1, 1); glVertex3f(1, 1, 1); 1.272 + glTexCoord2f(0, 1); glVertex3f(-1, 1, 1); 1.273 + glNormal3f(1 * norm_sign, 0, 0); 1.274 + glTexCoord2f(0, 0); glVertex3f(1, -1, 1); 1.275 + glTexCoord2f(1, 0); glVertex3f(1, -1, -1); 1.276 + glTexCoord2f(1, 1); glVertex3f(1, 1, -1); 1.277 + glTexCoord2f(0, 1); glVertex3f(1, 1, 1); 1.278 + glNormal3f(0, 0, -1 * norm_sign); 1.279 + glTexCoord2f(0, 0); glVertex3f(1, -1, -1); 1.280 + glTexCoord2f(1, 0); glVertex3f(-1, -1, -1); 1.281 + glTexCoord2f(1, 1); glVertex3f(-1, 1, -1); 1.282 + glTexCoord2f(0, 1); glVertex3f(1, 1, -1); 1.283 + glNormal3f(-1 * norm_sign, 0, 0); 1.284 + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); 1.285 + glTexCoord2f(1, 0); glVertex3f(-1, -1, 1); 1.286 + glTexCoord2f(1, 1); glVertex3f(-1, 1, 1); 1.287 + glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); 1.288 + glEnd(); 1.289 + glBegin(GL_TRIANGLE_FAN); 1.290 + glNormal3f(0, 1 * norm_sign, 0); 1.291 + glTexCoord2f(0.5, 0.5); glVertex3f(0, 1, 0); 1.292 + glTexCoord2f(0, 0); glVertex3f(-1, 1, 1); 1.293 + glTexCoord2f(1, 0); glVertex3f(1, 1, 1); 1.294 + glTexCoord2f(1, 1); glVertex3f(1, 1, -1); 1.295 + glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); 1.296 + glTexCoord2f(0, 0); glVertex3f(-1, 1, 1); 1.297 + glEnd(); 1.298 + glBegin(GL_TRIANGLE_FAN); 1.299 + glNormal3f(0, -1 * norm_sign, 0); 1.300 + glTexCoord2f(0.5, 0.5); glVertex3f(0, -1, 0); 1.301 + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); 1.302 + glTexCoord2f(1, 0); glVertex3f(1, -1, -1); 1.303 + glTexCoord2f(1, 1); glVertex3f(1, -1, 1); 1.304 + glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); 1.305 + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); 1.306 + glEnd(); 1.307 + 1.308 + glFrontFace(GL_CCW); 1.309 +} 1.310 + 1.311 +/* update_rtarg creates (and/or resizes) the render target used to draw the two stero views */ 1.312 +void update_rtarg(int width, int height) 1.313 +{ 1.314 + if(!fbo) { 1.315 + /* if fbo does not exist, then nothing does... create every opengl object */ 1.316 + glGenFramebuffers(1, &fbo); 1.317 + glGenTextures(1, &fb_tex); 1.318 + glGenRenderbuffers(1, &fb_depth); 1.319 + 1.320 + glBindTexture(GL_TEXTURE_2D, fb_tex); 1.321 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 1.322 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1.323 + } 1.324 + 1.325 + glBindFramebuffer(GL_FRAMEBUFFER, fbo); 1.326 + 1.327 + /* calculate the next power of two in both dimensions and use that as a texture size */ 1.328 + fb_tex_width = next_pow2(width); 1.329 + fb_tex_height = next_pow2(height); 1.330 + 1.331 + /* create and attach the texture that will be used as a color buffer */ 1.332 + glBindTexture(GL_TEXTURE_2D, fb_tex); 1.333 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fb_tex_width, fb_tex_height, 0, 1.334 + GL_RGBA, GL_UNSIGNED_BYTE, 0); 1.335 + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex, 0); 1.336 + 1.337 + /* create and attach the renderbuffer that will serve as our z-buffer */ 1.338 + glBindRenderbuffer(GL_RENDERBUFFER, fb_depth); 1.339 + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, fb_tex_width, fb_tex_height); 1.340 + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fb_depth); 1.341 + 1.342 + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 1.343 + fprintf(stderr, "incomplete framebuffer!\n"); 1.344 + } 1.345 + 1.346 + glBindFramebuffer(GL_FRAMEBUFFER, 0); 1.347 + printf("created render target: %dx%d (texture size: %dx%d)\n", width, height, fb_tex_width, fb_tex_height); 1.348 +} 1.349 + 1.350 +int handle_event(SDL_Event *ev) 1.351 +{ 1.352 + switch(ev->type) { 1.353 + case SDL_QUIT: 1.354 + return -1; 1.355 + 1.356 + case SDL_KEYDOWN: 1.357 + case SDL_KEYUP: 1.358 + if(key_event(ev->key.keysym.sym, ev->key.state == SDL_PRESSED) == -1) { 1.359 + return -1; 1.360 + } 1.361 + break; 1.362 + 1.363 + default: 1.364 + break; 1.365 + } 1.366 + 1.367 + return 0; 1.368 +} 1.369 + 1.370 +int key_event(int key, int state) 1.371 +{ 1.372 + if(state) { 1.373 + switch(key) { 1.374 + case 27: 1.375 + return -1; 1.376 + 1.377 + case ' ': 1.378 + /* allow the user to recenter by pressing space */ 1.379 + vr_recenter(); 1.380 + break; 1.381 + 1.382 + case 'f': 1.383 + /* press f to move the window to the HMD */ 1.384 + toggle_hmd_fullscreen(); 1.385 + break; 1.386 + 1.387 + default: 1.388 + break; 1.389 + } 1.390 + } 1.391 + return 0; 1.392 +} 1.393 + 1.394 +unsigned int next_pow2(unsigned int x) 1.395 +{ 1.396 + x -= 1; 1.397 + x |= x >> 1; 1.398 + x |= x >> 2; 1.399 + x |= x >> 4; 1.400 + x |= x >> 8; 1.401 + x |= x >> 16; 1.402 + return x + 1; 1.403 +} 1.404 + 1.405 +/* convert a quaternion to a rotation matrix */ 1.406 +void quat_to_matrix(const float *quat, float *mat) 1.407 +{ 1.408 + mat[0] = 1.0 - 2.0 * quat[1] * quat[1] - 2.0 * quat[2] * quat[2]; 1.409 + mat[4] = 2.0 * quat[0] * quat[1] + 2.0 * quat[3] * quat[2]; 1.410 + mat[8] = 2.0 * quat[2] * quat[0] - 2.0 * quat[3] * quat[1]; 1.411 + mat[12] = 0.0f; 1.412 + 1.413 + mat[1] = 2.0 * quat[0] * quat[1] - 2.0 * quat[3] * quat[2]; 1.414 + mat[5] = 1.0 - 2.0 * quat[0]*quat[0] - 2.0 * quat[2]*quat[2]; 1.415 + mat[9] = 2.0 * quat[1] * quat[2] + 2.0 * quat[3] * quat[0]; 1.416 + mat[13] = 0.0f; 1.417 + 1.418 + mat[2] = 2.0 * quat[2] * quat[0] + 2.0 * quat[3] * quat[1]; 1.419 + mat[6] = 2.0 * quat[1] * quat[2] - 2.0 * quat[3] * quat[0]; 1.420 + mat[10] = 1.0 - 2.0 * quat[0]*quat[0] - 2.0 * quat[1]*quat[1]; 1.421 + mat[14] = 0.0f; 1.422 + 1.423 + mat[3] = mat[7] = mat[11] = 0.0f; 1.424 + mat[15] = 1.0f; 1.425 +} 1.426 + 1.427 +/* generate a chessboard texture with tiles colored (r0, g0, b0) and (r1, g1, b1) */ 1.428 +unsigned int gen_chess_tex(float r0, float g0, float b0, float r1, float g1, float b1) 1.429 +{ 1.430 + int i, j; 1.431 + unsigned int tex; 1.432 + unsigned char img[8 * 8 * 3]; 1.433 + unsigned char *pix = img; 1.434 + 1.435 + for(i=0; i<8; i++) { 1.436 + for(j=0; j<8; j++) { 1.437 + int black = (i & 1) == (j & 1); 1.438 + pix[0] = (black ? r0 : r1) * 255; 1.439 + pix[1] = (black ? g0 : g1) * 255; 1.440 + pix[2] = (black ? b0 : b1) * 255; 1.441 + pix += 3; 1.442 + } 1.443 + } 1.444 + 1.445 + glGenTextures(1, &tex); 1.446 + glBindTexture(GL_TEXTURE_2D, tex); 1.447 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 1.448 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 1.449 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, img); 1.450 + 1.451 + return tex; 1.452 +}