# HG changeset patch # User John Tsiombikas # Date 1409808672 -10800 # Node ID d64830551c3278bdb31320bbea54b6ef00b67b32 # Parent 096b18432ba7be6c04b2f80c582b91321a11a09d hurray, it works diff -r 096b18432ba7 -r d64830551c32 src/main.c --- a/src/main.c Tue Aug 26 18:37:24 2014 +0300 +++ b/src/main.c Thu Sep 04 08:31:12 2014 +0300 @@ -16,6 +16,7 @@ int init(void); void cleanup(void); +void toggle_hmd_fullscreen(void); void display(void); void draw_scene(void); void draw_box(float xsz, float ysz, float zsz, float norm_sign); @@ -24,7 +25,10 @@ int key_event(int key, int state); void reshape(int x, int y); unsigned int next_pow2(unsigned int x); +void quat_to_matrix(const float *quat, float *mat); +unsigned int gen_chess_tex(float r0, float g0, float b0, float r1, float g1, float b1); +/* forward declaration to avoid including non-public headers of libovr */ OVR_EXPORT void ovrhmd_EnableHSWDisplaySDKRender(ovrHmd hmd, ovrBool enable); static SDL_Window *win; @@ -40,8 +44,10 @@ static ovrEyeRenderDesc eye_rdesc[2]; static ovrGLTexture fb_ovr_tex[2]; +static unsigned int chess_tex; -int main(void) + +int main(int argc, char **argv) { if(init() == -1) { return 1; @@ -49,13 +55,11 @@ for(;;) { SDL_Event ev; - while(SDL_PollEvent(&ev)) { if(handle_event(&ev) == -1) { goto done; } } - display(); } @@ -71,14 +75,14 @@ unsigned int flags, dcaps; union ovrGLConfig glcfg; - // this must be called before any OpenGL init according to the docs + /* libovr must be initialized before we create the OpenGL context */ ovr_Initialize(); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); x = y = SDL_WINDOWPOS_UNDEFINED; flags = SDL_WINDOW_OPENGL; - if(!(win = SDL_CreateWindow("simple oculus example", x, y, 1280, 800, flags))) { + if(!(win = SDL_CreateWindow("press 'f' to move to the HMD", x, y, 1280, 800, flags))) { fprintf(stderr, "failed to create window\n"); return -1; } @@ -99,13 +103,14 @@ printf("initialized HMD: %s - %s\n", hmd->Manufacturer, hmd->ProductName); SDL_SetWindowSize(win, hmd->Resolution.w, hmd->Resolution.h); + SDL_SetWindowPosition(win, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + win_width = hmd->Resolution.w; + win_height = hmd->Resolution.h; ovrHmd_ConfigureTracking(hmd, 0xffffffff, 0); eyeres[0] = ovrHmd_GetFovTextureSize(hmd, ovrEye_Left, hmd->DefaultEyeFov[0], 1.0); eyeres[1] = ovrHmd_GetFovTextureSize(hmd, ovrEye_Right, hmd->DefaultEyeFov[1], 1.0); - SDL_GetWindowSize(win, &win_width, &win_height); - fb_width = eyeres[0].w + eyeres[1].w; fb_height = eyeres[0].h > eyeres[1].h ? eyeres[0].h : eyeres[1].h; update_rtarg(fb_width, fb_height); @@ -139,7 +144,7 @@ } ovrHmd_SetEnabledCaps(hmd, ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction); - dcaps = ovrDistortionCap_Chromatic | ovrDistortionCap_Vignette;// | ovrDistortionCap_TimeWarp; + dcaps = ovrDistortionCap_Chromatic | ovrDistortionCap_Vignette | ovrDistortionCap_TimeWarp; if(!ovrHmd_ConfigureRendering(hmd, &glcfg.Config, dcaps, hmd->DefaultEyeFov, eye_rdesc)) { fprintf(stderr, "failed to configure distortion renderer\n"); } @@ -150,9 +155,12 @@ glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_NORMALIZE); - glClearColor(0.05, 0.05, 0.05, 1); + glClearColor(0.1, 0.1, 0.1, 1); + chess_tex = gen_chess_tex(1.0, 0.7, 0.4, 0.4, 0.7, 1.0); return 0; } @@ -166,77 +174,79 @@ SDL_Quit(); } +void toggle_hmd_fullscreen(void) +{ + static int fullscr, prev_x, prev_y; + fullscr = !fullscr; + + if(fullscr) { + SDL_GetWindowPosition(win, &prev_x, &prev_y); + SDL_SetWindowPosition(win, hmd->WindowsPos.x, hmd->WindowsPos.y); + SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP); + } else { + SDL_SetWindowFullscreen(win, 0); + SDL_SetWindowPosition(win, prev_x, prev_y); + } +} + void display(void) { int i; ovrMatrix4f proj; ovrPosef pose[2]; + float rot_mat[16]; + /* the drawing starts with a call to ovrHmd_BeginFrame */ ovrHmd_BeginFrame(hmd, 0); + /* start drawing onto our texture render target */ glBindFramebuffer(GL_FRAMEBUFFER, fbo); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glViewport(0, 0, fb_width, fb_height); - - /*glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glPushAttrib(GL_ENABLE_BIT); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - - glBegin(GL_QUADS); - glColor3f(1, 0, 0); - glVertex2f(-1, -1); - glVertex2f(0, -1); - glVertex2f(0, 1); - glVertex2f(-1, 1); - - glColor3f(1, 0, 1); - glVertex2f(-0.6, -0.1); - glVertex2f(-0.4, -0.1); - glVertex2f(-0.4, 0.1); - glVertex2f(-0.6, 0.1); - - glColor3f(0, 1, 0); - glVertex2f(0, -1); - glVertex2f(1, -1); - glVertex2f(1, 1); - glVertex2f(0, 1); - - glColor3f(0, 1, 1); - glVertex2f(0.4, -0.1); - glVertex2f(0.6, -0.1); - glVertex2f(0.6, 0.1); - glVertex2f(0.4, 0.1); - glEnd(); - - glPopAttrib();*/ - /* for each eye ... */ for(i=0; i<2; i++) { int eye = hmd->EyeRenderOrder[i]; - /* vport0(0, 0, width/2, height), vport1(width/2, 0, width/2, height) */ + /* -- viewport transformation -- + * setup the viewport to draw in the left half of the framebuffer when we're + * rendering the left eye's view (0, 0, width/2, height), and in the right half + * of the framebuffer for the right eye's view (width/2, 0, width/2, height) + */ glViewport(eye == 0 ? 0 : fb_width / 2, 0, fb_width / 2, fb_height); + /* -- projection transformation -- + * we'll just have to use the projection matrix supplied by the oculus SDK for this eye + * note that libovr matrices are the transpose of what OpenGL expects, so we have to + * use glLoadTransposeMatrixf instead of glLoadMatrixf to load it. + */ proj = ovrMatrix4f_Projection(hmd->DefaultEyeFov[eye], 0.5, 500.0, 1); glMatrixMode(GL_PROJECTION); - //glLoadMatrixf(proj.M[0]); - glLoadIdentity(); - gluPerspective(50.0, (float)fb_width / 2.0 / (float)fb_height, 0.5, 500.0); + glLoadTransposeMatrixf(proj.M[0]); + /* -- view/camera transformation -- + * we need to construct a view matrix by combining all the information provided by the oculus + * SDK, about the position and orientation of the user's head in the world. + */ pose[eye] = ovrHmd_GetEyePose(hmd, eye); glMatrixMode(GL_MODELVIEW); - /* TODO: get HMD orientation data and use it */ - //glTranslatef(0, -ovrHmd_GetFloat(hmd, OVR_KEY_EYE_HEIGHT, 1.65), 0); + glLoadIdentity(); + glTranslatef(eye_rdesc[eye].ViewAdjust.x, eye_rdesc[eye].ViewAdjust.y, eye_rdesc[eye].ViewAdjust.z); + /* retrieve the orientation quaternion and convert it to a rotation matrix */ + quat_to_matrix(&pose[eye].Orientation.x, rot_mat); + glMultMatrixf(rot_mat); + /* translate the view matrix with the positional tracking */ + glTranslatef(-pose[eye].Position.x, -pose[eye].Position.y, -pose[eye].Position.z); + /* move the camera to the eye level of the user */ + glTranslatef(0, -ovrHmd_GetFloat(hmd, OVR_KEY_EYE_HEIGHT, 1.65), 0); + /* finally draw the scene for this eye */ draw_scene(); } + /* after drawing both eyes into the texture render target, revert to drawing directly to the + * display, and we call ovrHmd_EndFrame, to let the Oculus SDK draw both images properly + * compensated for lens distortion and chromatic abberation onto the HMD screen. + */ glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, win_width, win_height); @@ -248,25 +258,52 @@ void draw_scene(void) { int i; - float lpos[] = {0, 5, 0, 1}; - glLightfv(GL_LIGHT0, GL_POSITION, lpos); + float grey[] = {0.8, 0.8, 0.8, 1}; + float col[] = {0, 0, 0, 1}; + float lpos[][4] = { + {-8, 2, 10, 1}, + {0, 15, 0, 1} + }; + float lcol[][4] = { + {0.8, 0.8, 0.8, 1}, + {0.4, 0.3, 0.3, 1} + }; + + for(i=0; i<2; i++) { + glLightfv(GL_LIGHT0 + i, GL_POSITION, lpos[i]); + glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lcol[i]); + } + + glMatrixMode(GL_MODELVIEW); glPushMatrix(); - glTranslatef(0, 0, -8); - draw_box(2, 2, 2, 1.0); + glTranslatef(0, 10, 0); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, grey); + glBindTexture(GL_TEXTURE_2D, chess_tex); + glEnable(GL_TEXTURE_2D); + draw_box(30, 20, 30, -1.0); + glDisable(GL_TEXTURE_2D); glPopMatrix(); - glTranslatef(0, 5, 0); - draw_box(20, 10, 20, -1.0); + for(i=0; i<4; i++) { + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, grey); + glPushMatrix(); + glTranslatef(i & 1 ? 5 : -5, 1, i & 2 ? -5 : 5); + draw_box(0.5, 2, 0.5, 1.0); + glPopMatrix(); - for(i=0; i<4; i++) { + col[0] = i & 1 ? 1.0 : 0.3; + col[1] = i == 0 ? 1.0 : 0.3; + col[2] = i & 2 ? 1.0 : 0.3; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col); + glPushMatrix(); if(i & 1) { - glTranslatef(0, 0, i & 2 ? 7.5 : -7.5); + glTranslatef(0, 0.25, i & 2 ? 2 : -2); } else { - glTranslatef(i & 2 ? 7.5 : -7.5, 0, 0); + glTranslatef(i & 2 ? 2 : -2, 0.25, 0); } - draw_box(3, 0, 3, 1.0); + draw_box(0.5, 0.5, 0.5, 1.0); glPopMatrix(); } } @@ -301,16 +338,24 @@ glTexCoord2f(1, 0); glVertex3f(-1, -1, 1); glTexCoord2f(1, 1); glVertex3f(-1, 1, 1); glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); + glEnd(); + glBegin(GL_TRIANGLE_FAN); glNormal3f(0, 1 * norm_sign, 0); + glTexCoord2f(0.5, 0.5); glVertex3f(0, 1, 0); glTexCoord2f(0, 0); glVertex3f(-1, 1, 1); glTexCoord2f(1, 0); glVertex3f(1, 1, 1); glTexCoord2f(1, 1); glVertex3f(1, 1, -1); glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); + glTexCoord2f(0, 0); glVertex3f(-1, 1, 1); + glEnd(); + glBegin(GL_TRIANGLE_FAN); glNormal3f(0, -1 * norm_sign, 0); + glTexCoord2f(0.5, 0.5); glVertex3f(0, -1, 0); glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); glTexCoord2f(1, 0); glVertex3f(1, -1, -1); glTexCoord2f(1, 1); glVertex3f(1, -1, 1); glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); glEnd(); glFrontFace(GL_CCW); @@ -353,6 +398,9 @@ int handle_event(SDL_Event *ev) { switch(ev->type) { + case SDL_QUIT: + return -1; + case SDL_KEYDOWN: case SDL_KEYUP: if(key_event(ev->key.keysym.sym, ev->key.state == SDL_PRESSED) == -1) { @@ -370,24 +418,87 @@ int key_event(int key, int state) { if(state) { + ovrHSWDisplayState hsw; + ovrHmd_GetHSWDisplayState(hmd, &hsw); + if(hsw.Displayed) { + ovrHmd_DismissHSWDisplay(hmd); + } + switch(key) { case 27: return -1; + case ' ': + /* allow the user to recenter by pressing space */ + ovrHmd_RecenterPose(hmd); + break; + + case 'f': + /* press f to move the window to the HMD */ + toggle_hmd_fullscreen(); + break; + default: break; } } return 0; } - -unsigned int next_pow2(unsigned int x) -{ - x -= 1; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - return x + 1; + +unsigned int next_pow2(unsigned int x) +{ + x -= 1; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; +} + +void quat_to_matrix(const float *quat, float *mat) +{ + mat[0] = 1.0 - 2.0 * quat[1] * quat[1] - 2.0 * quat[2] * quat[2]; + mat[4] = 2.0 * quat[0] * quat[1] + 2.0 * quat[3] * quat[2]; + mat[8] = 2.0 * quat[2] * quat[0] - 2.0 * quat[3] * quat[1]; + mat[12] = 0.0f; + + mat[1] = 2.0 * quat[0] * quat[1] - 2.0 * quat[3] * quat[2]; + mat[5] = 1.0 - 2.0 * quat[0]*quat[0] - 2.0 * quat[2]*quat[2]; + mat[9] = 2.0 * quat[1] * quat[2] + 2.0 * quat[3] * quat[0]; + mat[13] = 0.0f; + + mat[2] = 2.0 * quat[2] * quat[0] + 2.0 * quat[3] * quat[1]; + mat[6] = 2.0 * quat[1] * quat[2] - 2.0 * quat[3] * quat[0]; + mat[10] = 1.0 - 2.0 * quat[0]*quat[0] - 2.0 * quat[1]*quat[1]; + mat[14] = 0.0f; + + mat[3] = mat[7] = mat[11] = 0.0f; + mat[15] = 1.0f; +} + +unsigned int gen_chess_tex(float r0, float g0, float b0, float r1, float g1, float b1) +{ + int i, j; + unsigned int tex; + unsigned char img[8 * 8 * 3]; + unsigned char *pix = img; + + for(i=0; i<8; i++) { + for(j=0; j<8; j++) { + int black = (i & 1) == (j & 1); + pix[0] = (black ? r0 : r1) * 255; + pix[1] = (black ? g0 : g1) * 255; + pix[2] = (black ? b0 : b1) * 255; + pix += 3; + } + } + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, img); + + return tex; } \ No newline at end of file