openvr_test1
diff src/app.cc @ 0:806d30b46748
OpenVR test initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 13 Apr 2016 09:39:37 +0300 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/app.cc Wed Apr 13 09:39:37 2016 +0300 1.3 @@ -0,0 +1,439 @@ 1.4 +#include <stdio.h> 1.5 +#include <assert.h> 1.6 +#include <GL/glew.h> 1.7 +#include <openvr.h> 1.8 +#include <gmath/gmath.h> 1.9 +#include "app.h" 1.10 + 1.11 +using namespace vr; // OpenVR namespace 1.12 + 1.13 +static void draw_scene(); 1.14 +static void draw_box(float xsz, float ysz, float zsz, float norm_sign); 1.15 +static Mat4 openvr_matrix4(const HmdMatrix44_t &mat); 1.16 +static Mat4 openvr_matrix(const HmdMatrix34_t &mat); 1.17 +static VRTextureBounds_t openvr_tex_bounds(float umin, float vmin, float umax, float vmax); 1.18 +static void update_rtarg(int width, int height); 1.19 +static unsigned int next_pow2(unsigned int x); 1.20 +static unsigned int gen_chess_tex(float r0, float g0, float b0, float r1, float g1, float b1); 1.21 + 1.22 +static int win_width, win_height; 1.23 + 1.24 +static unsigned int fbo, fb_tex, fb_depth; 1.25 +static int fb_width, fb_height; 1.26 +static int fb_tex_width, fb_tex_height; 1.27 + 1.28 +static unsigned int chess_tex; 1.29 + 1.30 +// openvr stuff 1.31 +static IVRSystem *vrsys; 1.32 +static IVRCompositor *vrcomp; 1.33 +static bool vrdev_state[k_unMaxTrackedDeviceCount]; 1.34 +static TrackedDevicePose_t vrdev_pose[k_unMaxTrackedDeviceCount]; 1.35 +static Mat4 eye_matrix[2]; 1.36 +static Mat4 proj_matrix[2]; 1.37 +static Texture_t vrtex = {0, API_OpenGL, ColorSpace_Linear}; 1.38 +static VRTextureBounds_t vrtex_bounds[2]; 1.39 + 1.40 +bool app_init() 1.41 +{ 1.42 + glewInit(); 1.43 + 1.44 + glEnable(GL_DEPTH_TEST); 1.45 + glEnable(GL_CULL_FACE); 1.46 + glEnable(GL_LIGHTING); 1.47 + glEnable(GL_LIGHT0); 1.48 + glEnable(GL_LIGHT1); 1.49 + glEnable(GL_NORMALIZE); 1.50 + 1.51 + glClearColor(0.1, 0.1, 0.1, 1); 1.52 + 1.53 + chess_tex = gen_chess_tex(1.0, 0.7, 0.4, 0.4, 0.7, 1.0); 1.54 + 1.55 + // Initialize OpenVR 1.56 + EVRInitError vrerr; 1.57 + if(!(vrsys = VR_Init(&vrerr, VRApplication_Scene))) { 1.58 + fprintf(stderr, "failed to initialize OpenVR: %s\n", 1.59 + VR_GetVRInitErrorAsEnglishDescription(vrerr)); 1.60 + return false; 1.61 + } 1.62 + if(!(vrcomp = VRCompositor())) { 1.63 + fprintf(stderr, "failed to initialize OpenVR compositor\n"); 1.64 + return false; 1.65 + } 1.66 + 1.67 + uint32_t xsz, ysz; 1.68 + vrsys->GetRecommendedRenderTargetSize(&xsz, &ysz); 1.69 + fb_width = xsz * 2; 1.70 + fb_height = ysz; 1.71 + update_rtarg(fb_width, fb_height); // create render target 1.72 + 1.73 + for(int i=0; i<2; i++) { 1.74 + EVREye eye = i == 0 ? Eye_Left : Eye_Right; 1.75 + 1.76 + // projection matrix for each eye 1.77 + proj_matrix[i] = openvr_matrix4(vrsys->GetProjectionMatrix(eye, 0.5, 500.0, API_OpenGL)); 1.78 + // eye (relative to head) matrix 1.79 + eye_matrix[i] = openvr_matrix(vrsys->GetEyeToHeadTransform(eye)); 1.80 + } 1.81 + 1.82 + // XXX this doesn't seem to work correctly for some reason 1.83 + //vrcomp->ShowMirrorWindow(); 1.84 + 1.85 + assert(glGetError() == GL_NO_ERROR); 1.86 + return true; 1.87 +} 1.88 + 1.89 +void app_shutdown() 1.90 +{ 1.91 + // if we call VR_Shutdown while a frame is pending, we'll crash 1.92 + vrcomp->ClearLastSubmittedFrame(); 1.93 + VR_Shutdown(); 1.94 +} 1.95 + 1.96 +static void update() 1.97 +{ 1.98 + // process OpenVR events (TODO: I think there are more events to handle) 1.99 + VREvent_t ev; 1.100 + while(vrsys->PollNextEvent(&ev, sizeof ev)) { 1.101 + switch(ev.eventType) { 1.102 + case VREvent_TrackedDeviceActivated: 1.103 + printf("Device %u activated\n", ev.trackedDeviceIndex); 1.104 + break; 1.105 + 1.106 + case VREvent_TrackedDeviceDeactivated: 1.107 + printf("Device %u lost\n", ev.trackedDeviceIndex); 1.108 + break; 1.109 + 1.110 + case VREvent_TrackedDeviceUpdated: 1.111 + printf("Device %u updated(?)\n", ev.trackedDeviceIndex); 1.112 + break; 1.113 + } 1.114 + } 1.115 + 1.116 + // TODO implement controllers 1.117 + for(int i=0; i<k_unMaxTrackedDeviceCount; i++) { 1.118 + VRControllerState_t st; 1.119 + if(vrsys->GetControllerState(i, &st)) { 1.120 + // XXX ? 1.121 + vrdev_state[i] = st.ulButtonPressed == 0; 1.122 + } 1.123 + } 1.124 + 1.125 + // this will probably block at some point... investigate further 1.126 + vrcomp->WaitGetPoses(vrdev_pose, k_unMaxTrackedDeviceCount, 0, 0); 1.127 +} 1.128 + 1.129 +void app_draw() 1.130 +{ 1.131 + update(); 1.132 + 1.133 + glBindFramebuffer(GL_FRAMEBUFFER, fbo); 1.134 + glClearColor(0, 0, 0, 1); 1.135 + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 1.136 + 1.137 + for(int i=0; i<2; i++) { 1.138 + EVREye eye = i == 0 ? Eye_Left : Eye_Right; 1.139 + 1.140 + glViewport(i == 0 ? 0 : fb_width / 2, 0, fb_width / 2, fb_height); 1.141 + 1.142 + glMatrixMode(GL_PROJECTION); 1.143 + glLoadMatrixf(proj_matrix[i][0]); 1.144 + 1.145 + Mat4 hmd_mat = openvr_matrix(vrdev_pose[k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking); 1.146 + Mat4 view_mat = inverse(eye_matrix[i] * hmd_mat); 1.147 + 1.148 + glMatrixMode(GL_MODELVIEW); 1.149 + glLoadMatrixf(view_mat[0]); 1.150 + 1.151 + draw_scene(); 1.152 + vrcomp->Submit(eye, &vrtex, vrtex_bounds + i); 1.153 + } 1.154 + /* this is supposed to tell the compositor to get on with showing the frame without waiting for 1.155 + * the next WaitGetPoses call. 1.156 + */ 1.157 + vrcomp->PostPresentHandoff(); 1.158 + 1.159 + glUseProgram(0); 1.160 + 1.161 + // render window output 1.162 + glBindFramebuffer(GL_FRAMEBUFFER, 0); 1.163 + glViewport(0, 0, win_width, win_height); 1.164 + 1.165 + glMatrixMode(GL_PROJECTION); 1.166 + glLoadIdentity(); 1.167 + glMatrixMode(GL_MODELVIEW); 1.168 + glLoadIdentity(); 1.169 + 1.170 + glPushAttrib(GL_ENABLE_BIT); 1.171 + glBindTexture(GL_TEXTURE_2D, fb_tex); 1.172 + glEnable(GL_TEXTURE_2D); 1.173 + glDisable(GL_LIGHTING); 1.174 + glDisable(GL_DEPTH_TEST); 1.175 + 1.176 + glBegin(GL_QUADS); 1.177 + float umax = (float)fb_width / fb_tex_width; 1.178 + float vmax = (float)fb_height / fb_tex_height; 1.179 + glTexCoord2f(0, 0); glVertex2f(-1, -1); 1.180 + glTexCoord2f(umax, 0); glVertex2f(1, -1); 1.181 + glTexCoord2f(umax, vmax); glVertex2f(1, 1); 1.182 + glTexCoord2f(0, vmax); glVertex2f(-1, 1); 1.183 + glEnd(); 1.184 + 1.185 + glPopAttrib(); 1.186 + 1.187 + app_swap_buffers(); 1.188 +} 1.189 + 1.190 +void app_reshape(int x, int y) 1.191 +{ 1.192 + win_width = x; 1.193 + win_height = y; 1.194 + glViewport(0, 0, x, y); 1.195 + 1.196 + glMatrixMode(GL_PROJECTION); 1.197 + glLoadIdentity(); 1.198 + gluPerspective(50, (float)x / (float)y, 0.5, 500.0); 1.199 +} 1.200 + 1.201 +static void draw_scene(void) 1.202 +{ 1.203 + float grey[] = {0.8, 0.8, 0.8, 1}; 1.204 + float col[] = {0, 0, 0, 1}; 1.205 + float lpos[][4] = { 1.206 + {-8, 2, 10, 1}, 1.207 + {0, 15, 0, 1} 1.208 + }; 1.209 + float lcol[][4] = { 1.210 + {0.8, 0.8, 0.8, 1}, 1.211 + {0.4, 0.3, 0.3, 1} 1.212 + }; 1.213 + 1.214 + for(int i=0; i<2; i++) { 1.215 + glLightfv(GL_LIGHT0 + i, GL_POSITION, lpos[i]); 1.216 + glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lcol[i]); 1.217 + } 1.218 + 1.219 + glMatrixMode(GL_MODELVIEW); 1.220 + 1.221 + glPushMatrix(); 1.222 + glTranslatef(0, 10, 0); 1.223 + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, grey); 1.224 + glBindTexture(GL_TEXTURE_2D, chess_tex); 1.225 + glEnable(GL_TEXTURE_2D); 1.226 + draw_box(30, 20, 30, -1.0); 1.227 + glDisable(GL_TEXTURE_2D); 1.228 + glPopMatrix(); 1.229 + 1.230 + for(int i=0; i<4; i++) { 1.231 + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, grey); 1.232 + glPushMatrix(); 1.233 + glTranslatef(i & 1 ? 5 : -5, 1, i & 2 ? -5 : 5); 1.234 + draw_box(0.5, 2, 0.5, 1.0); 1.235 + glPopMatrix(); 1.236 + 1.237 + col[0] = i & 1 ? 1.0 : 0.3; 1.238 + col[1] = i == 0 ? 1.0 : 0.3; 1.239 + col[2] = i & 2 ? 1.0 : 0.3; 1.240 + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col); 1.241 + 1.242 + glPushMatrix(); 1.243 + if(i & 1) { 1.244 + glTranslatef(0, 0.25, i & 2 ? 2 : -2); 1.245 + } else { 1.246 + glTranslatef(i & 2 ? 2 : -2, 0.25, 0); 1.247 + } 1.248 + draw_box(0.5, 0.5, 0.5, 1.0); 1.249 + glPopMatrix(); 1.250 + } 1.251 + 1.252 + col[0] = 1; 1.253 + col[1] = 1; 1.254 + col[2] = 0.4; 1.255 + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col); 1.256 + draw_box(0.05, 1.2, 6, 1.0); 1.257 + draw_box(6, 1.2, 0.05, 1.0); 1.258 +} 1.259 + 1.260 +static void draw_box(float xsz, float ysz, float zsz, float norm_sign) 1.261 +{ 1.262 + glMatrixMode(GL_MODELVIEW); 1.263 + glPushMatrix(); 1.264 + glScalef(xsz * 0.5, ysz * 0.5, zsz * 0.5); 1.265 + 1.266 + if(norm_sign < 0.0) { 1.267 + glFrontFace(GL_CW); 1.268 + } 1.269 + 1.270 + glBegin(GL_QUADS); 1.271 + glNormal3f(0, 0, 1 * norm_sign); 1.272 + glTexCoord2f(0, 0); glVertex3f(-1, -1, 1); 1.273 + glTexCoord2f(1, 0); glVertex3f(1, -1, 1); 1.274 + glTexCoord2f(1, 1); glVertex3f(1, 1, 1); 1.275 + glTexCoord2f(0, 1); glVertex3f(-1, 1, 1); 1.276 + glNormal3f(1 * norm_sign, 0, 0); 1.277 + glTexCoord2f(0, 0); glVertex3f(1, -1, 1); 1.278 + glTexCoord2f(1, 0); glVertex3f(1, -1, -1); 1.279 + glTexCoord2f(1, 1); glVertex3f(1, 1, -1); 1.280 + glTexCoord2f(0, 1); glVertex3f(1, 1, 1); 1.281 + glNormal3f(0, 0, -1 * norm_sign); 1.282 + glTexCoord2f(0, 0); glVertex3f(1, -1, -1); 1.283 + glTexCoord2f(1, 0); glVertex3f(-1, -1, -1); 1.284 + glTexCoord2f(1, 1); glVertex3f(-1, 1, -1); 1.285 + glTexCoord2f(0, 1); glVertex3f(1, 1, -1); 1.286 + glNormal3f(-1 * norm_sign, 0, 0); 1.287 + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); 1.288 + glTexCoord2f(1, 0); glVertex3f(-1, -1, 1); 1.289 + glTexCoord2f(1, 1); glVertex3f(-1, 1, 1); 1.290 + glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); 1.291 + glEnd(); 1.292 + glBegin(GL_TRIANGLE_FAN); 1.293 + glNormal3f(0, 1 * norm_sign, 0); 1.294 + glTexCoord2f(0.5, 0.5); glVertex3f(0, 1, 0); 1.295 + glTexCoord2f(0, 0); glVertex3f(-1, 1, 1); 1.296 + glTexCoord2f(1, 0); glVertex3f(1, 1, 1); 1.297 + glTexCoord2f(1, 1); glVertex3f(1, 1, -1); 1.298 + glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); 1.299 + glTexCoord2f(0, 0); glVertex3f(-1, 1, 1); 1.300 + glEnd(); 1.301 + glBegin(GL_TRIANGLE_FAN); 1.302 + glNormal3f(0, -1 * norm_sign, 0); 1.303 + glTexCoord2f(0.5, 0.5); glVertex3f(0, -1, 0); 1.304 + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); 1.305 + glTexCoord2f(1, 0); glVertex3f(1, -1, -1); 1.306 + glTexCoord2f(1, 1); glVertex3f(1, -1, 1); 1.307 + glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); 1.308 + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); 1.309 + glEnd(); 1.310 + 1.311 + glFrontFace(GL_CCW); 1.312 + glPopMatrix(); 1.313 +} 1.314 + 1.315 +static Mat4 openvr_matrix4(const HmdMatrix44_t &mat) 1.316 +{ 1.317 + return Mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0], 1.318 + mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1], 1.319 + mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2], 1.320 + mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]); 1.321 +} 1.322 + 1.323 +static Mat4 openvr_matrix(const HmdMatrix34_t &mat) 1.324 +{ 1.325 + return Mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], 0, 1.326 + mat.m[0][1], mat.m[1][1], mat.m[2][1], 0, 1.327 + mat.m[0][2], mat.m[1][2], mat.m[2][2], 0, 1.328 + mat.m[0][3], mat.m[1][3], mat.m[2][3], 1); 1.329 +} 1.330 + 1.331 +static VRTextureBounds_t openvr_tex_bounds(float umin, float vmin, float umax, float vmax) 1.332 +{ 1.333 + VRTextureBounds_t res; 1.334 + res.uMin = umin; 1.335 + res.uMax = umax; 1.336 + res.vMin = vmin; 1.337 + res.vMax = vmax; 1.338 + return res; 1.339 +} 1.340 + 1.341 +/* update_rtarg creates (and/or resizes) the render target used to draw the two stero views */ 1.342 +static void update_rtarg(int width, int height) 1.343 +{ 1.344 + if(!fbo) { 1.345 + /* if fbo does not exist, then nothing does... create every opengl object */ 1.346 + glGenFramebuffers(1, &fbo); 1.347 + glGenTextures(1, &fb_tex); 1.348 + glGenRenderbuffers(1, &fb_depth); 1.349 + 1.350 + glBindTexture(GL_TEXTURE_2D, fb_tex); 1.351 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 1.352 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1.353 + } 1.354 + 1.355 + glBindFramebuffer(GL_FRAMEBUFFER, fbo); 1.356 + 1.357 + /* calculate the next power of two in both dimensions and use that as a texture size */ 1.358 + fb_tex_width = next_pow2(width); 1.359 + fb_tex_height = next_pow2(height); 1.360 + 1.361 + /* create and attach the texture that will be used as a color buffer */ 1.362 + glBindTexture(GL_TEXTURE_2D, fb_tex); 1.363 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fb_tex_width, fb_tex_height, 0, 1.364 + GL_RGBA, GL_UNSIGNED_BYTE, 0); 1.365 + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex, 0); 1.366 + 1.367 + /* create and attach the renderbuffer that will serve as our z-buffer */ 1.368 + glBindRenderbuffer(GL_RENDERBUFFER, fb_depth); 1.369 + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, fb_tex_width, fb_tex_height); 1.370 + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fb_depth); 1.371 + 1.372 + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 1.373 + fprintf(stderr, "incomplete framebuffer!\n"); 1.374 + } 1.375 + 1.376 + glBindFramebuffer(GL_FRAMEBUFFER, 0); 1.377 + printf("created render target: %dx%d (texture size: %dx%d)\n", width, height, fb_tex_width, fb_tex_height); 1.378 + 1.379 + /* update the OpenVR texture descriptors */ 1.380 + vrtex.handle = (void*)fb_tex; 1.381 + float umax = (float)fb_width / fb_tex_width; 1.382 + float vmax = (float)fb_height / fb_tex_height; 1.383 + vrtex_bounds[0] = openvr_tex_bounds(0, 1.0 - vmax, 0.5 * umax, 1.0); 1.384 + vrtex_bounds[1] = openvr_tex_bounds(0.5 * umax, 1.0 - vmax, umax, 1.0); 1.385 +} 1.386 + 1.387 + 1.388 +void app_keyboard(int key, bool pressed) 1.389 +{ 1.390 + if(pressed) { 1.391 + switch(key) { 1.392 + case 27: 1.393 + app_quit(); 1.394 + break; 1.395 + 1.396 + case ' ': 1.397 + case 'r': 1.398 + vrsys->ResetSeatedZeroPose(); 1.399 + break; 1.400 + 1.401 + default: 1.402 + break; 1.403 + } 1.404 + } 1.405 +} 1.406 + 1.407 +static unsigned int next_pow2(unsigned int x) 1.408 +{ 1.409 + x -= 1; 1.410 + x |= x >> 1; 1.411 + x |= x >> 2; 1.412 + x |= x >> 4; 1.413 + x |= x >> 8; 1.414 + x |= x >> 16; 1.415 + return x + 1; 1.416 +} 1.417 + 1.418 +/* generate a chessboard texture with tiles colored (r0, g0, b0) and (r1, g1, b1) */ 1.419 +unsigned int gen_chess_tex(float r0, float g0, float b0, float r1, float g1, float b1) 1.420 +{ 1.421 + unsigned int tex; 1.422 + unsigned char img[8 * 8 * 3]; 1.423 + unsigned char *pix = img; 1.424 + 1.425 + for(int i=0; i<8; i++) { 1.426 + for(int j=0; j<8; j++) { 1.427 + int black = (i & 1) == (j & 1); 1.428 + pix[0] = (black ? r0 : r1) * 255; 1.429 + pix[1] = (black ? g0 : g1) * 255; 1.430 + pix[2] = (black ? b0 : b1) * 255; 1.431 + pix += 3; 1.432 + } 1.433 + } 1.434 + 1.435 + glGenTextures(1, &tex); 1.436 + glBindTexture(GL_TEXTURE_2D, tex); 1.437 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 1.438 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 1.439 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, img); 1.440 + 1.441 + return tex; 1.442 +}