/** Sets up viewmat to only have one viewport. This can be called * every frame since resizing the window will change the size of the * viewport! */ static void viewmat_one_viewport(void) { /* One viewport fills the entire screen */ int windowWidth, windowHeight; viewmat_window_size(&windowWidth, &windowHeight); viewports_size = 1; viewports[0][0] = 0; viewports[0][1] = 0; viewports[0][2] = windowWidth; viewports[0][3] = windowHeight; }
/** Calculates a view frustum based on the current projmat * settings. * * @param result The location for view frustum values to be stored. * * @param viewportWidth The width of the viewport this frustum is * for. If viewportWidth is -1, it is assumed that the frustum will * fill the entire window. This option is useful for HMD rendering * where there are two viewports for a single window. The viewport * dimensions are necessary to calculate an appropriate aspect ratio * for the frustum. * * @param viewportHeight The height of the viewport this frustum is * for. If viewportHeight is -1, it is assumed that he frustum will * fill the entire window. This option is useful for HMD rendering * where there are two viewports for a single window. The viewport * dimensions are necessary to calculate an appropriate aspect ratio * for the frustum. **/ void projmat_get_frustum(float result[6], int viewportWidth, int viewportHeight) { if(projmat_mode == -1 || projmat_mode == 0) { int windowWidth, windowHeight; viewmat_window_size(&windowWidth, &windowHeight); if(viewportWidth < 0) viewportWidth = windowWidth; if(viewportHeight < 0) viewportHeight = windowHeight; float aspect = viewportWidth/(float)viewportHeight; float nearPlane = 0.1; float farPlane = 200; float vfov = 65; if(projmat_mode == 0) vfov = projmat_vfov; float fovyRad = vfov * M_PI/180.0f; float height = nearPlane * tanf(fovyRad/2.0f); float width = height * aspect; result[0] = -width; result[1] = width; result[2] = -height; result[3] = height; result[4] = nearPlane; result[5] = farPlane; // Save the frustum in proj_master_frustum and proj_frustum to // reduce any confusion. Since there are no slaves (which is // implied by not giving a frustum or specifying a simple // vertical field of view), then the master frustum matches // this frustum. for(int i=0; i<6; i++) { projmat_master_frustum[i] = result[i]; projmat_frustum[i] = result[i]; } return; } if(projmat_mode == 1) // we were given a view frustum { for(int i=0; i<6; i++) result[i] = projmat_frustum[i]; return; } }
/** Sets up viewmat to split the screen vertically into two * viewports. This can be called every frame since resizing the window * will change the size of the viewport! */ static void viewmat_two_viewports(void) { /* Two viewports, one for each eye */ int windowWidth, windowHeight; viewmat_window_size(&windowWidth, &windowHeight); /* TODO: Figure out if it makes sense to make this configurable at runtime. */ viewports_size = 2; viewports[0][0] = 0; viewports[0][1] = 0; viewports[0][2] = windowWidth/2; viewports[0][3] = windowHeight; viewports[1][0] = windowWidth/2; viewports[1][1] = 0; viewports[1][2] = windowWidth/2; viewports[1][3] = windowHeight; }
static void viewmat_anaglyph_viewports(void) { /* One viewport fills the entire screen */ int windowWidth, windowHeight; viewmat_window_size(&windowWidth, &windowHeight); viewports_size = 2; /* Our anaglyph rendering uses parallel cameras. Changing the * offset will change the distance at which objects are perceived * to be at the depth of your screen. For example, a star that is * infinitely far away form the parallel cameras would project * onto the same pixel in each camera. However, if we displayed * those two images without an offset, your eyes would have to * verge to the depth of the screen to fuse the star---causing * convergence cues to indicate that the star is at the depth of * the screen. Offsetting the image horizontally by the * inter-pupil eye distances can resolve this problem. Offsetting * too should be avoided because it causes divergence. * * Anaglyph images may not look good because some light will get * to the incorrect eye due to imperfect filters. Also, if you * move the camera very close to an object, may be difficult to * fuse the object. (Objects close to your eyes in the real world * are hard to fuse too!). * * The offset parameter below is in pixels. Depending on the size * the pixels on your screen, you may need to adjust this value. */ int offset = 20; for(int i=0; i<2; i++) { if(i == 0) viewports[i][0] = -offset/2; else viewports[i][0] = offset/2; viewports[i][1] = 0; viewports[i][2] = windowWidth; viewports[i][3] = windowHeight; } }
void display() { /* If we are using DGR, send or receive data to keep multiple * processes/computers synchronized. */ dgr_update(); /* Get current frames per second calculations. */ float fps = kuhl_getfps(&fps_state); if(dgr_is_enabled() == 0 || dgr_is_master()) { // If DGR is being used, only display dgr counter if we are // the master process. // Check if FPS value was just updated by kuhl_getfps() if(fps_state.frame == 0) { char label[1024]; snprintf(label, 1024, "FPS: %0.1f", fps); /* Delete old label if it exists */ if(fpsLabel != 0) glDeleteTextures(1, &fpsLabel); /* Make a new label */ float labelColor[3] = { 1,1,1 }; float labelBg[4] = { 0,0,0,.3 }; /* Change the last parameter (point size) to adjust the * size of the texture that the text is rendered in to. */ fpsLabelAspectRatio = kuhl_make_label(label, &fpsLabel, labelColor, labelBg, 24); if(fpsLabel != 0) kuhl_geometry_texture(&labelQuad, fpsLabel, "tex", 1); } } /* Ensure the slaves use the same render style as the master * process. */ dgr_setget("style", &renderStyle, sizeof(int)); /* Render the scene once for each viewport. Frequently one * viewport will fill the entire screen. However, this loop will * run twice for HMDs (once for the left eye and once for the * right. */ viewmat_begin_frame(); for(int viewportID=0; viewportID<viewmat_num_viewports(); viewportID++) { viewmat_begin_eye(viewportID); /* Where is the viewport that we are drawing onto and what is its size? */ int viewport[4]; // x,y of lower left corner, width, height viewmat_get_viewport(viewport, viewportID); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); /* Clear the current viewport. Without glScissor(), glClear() * clears the entire screen. We could call glClear() before * this viewport loop---but on order for all variations of * this code to work (Oculus support, etc), we can only draw * after viewmat_begin_eye(). */ glScissor(viewport[0], viewport[1], viewport[2], viewport[3]); glEnable(GL_SCISSOR_TEST); glClearColor(.2,.2,.2,0); // set clear color to grey glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); glEnable(GL_DEPTH_TEST); // turn on depth testing kuhl_errorcheck(); /* Turn on blending (note, if you are using transparent textures, the transparency may not look correct unless you draw further items before closer items.). */ glEnable(GL_BLEND); glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); /* Get the view or camera matrix; update the frustum values if needed. */ float viewMat[16], perspective[16]; viewmat_get(viewMat, perspective, viewportID); glUseProgram(program); kuhl_errorcheck(); /* Send the perspective projection matrix to the vertex program. */ glUniformMatrix4fv(kuhl_get_uniform("Projection"), 1, // number of 4x4 float matrices 0, // transpose perspective); // value float modelMat[16]; get_model_matrix(modelMat); float modelview[16]; mat4f_mult_mat4f_new(modelview, viewMat, modelMat); // modelview = view * model /* Send the modelview matrix to the vertex program. */ glUniformMatrix4fv(kuhl_get_uniform("ModelView"), 1, // number of 4x4 float matrices 0, // transpose modelview); // value glUniform1i(kuhl_get_uniform("renderStyle"), renderStyle); // Copy far plane value into vertex program so we can render depth buffer. float f[6]; // left, right, bottom, top, near>0, far>0 projmat_get_frustum(f, viewport[2], viewport[3]); glUniform1f(kuhl_get_uniform("farPlane"), f[5]); kuhl_errorcheck(); kuhl_geometry_draw(modelgeom); /* Draw the model */ kuhl_errorcheck(); if(showOrigin) { /* Save current line width */ GLfloat origLineWidth; glGetFloatv(GL_LINE_WIDTH, &origLineWidth); glLineWidth(4); // make lines thick /* Object coordinate system origin */ kuhl_geometry_draw(origingeom); /* Draw the origin marker */ /* World coordinate origin */ mat4f_copy(modelview, viewMat); glUniformMatrix4fv(kuhl_get_uniform("ModelView"), 1, // number of 4x4 float matrices 0, // transpose modelview); // value kuhl_geometry_draw(origingeom); /* Draw the origin marker */ /* Restore line width */ glLineWidth(origLineWidth); } if(dgr_is_enabled() == 0 || dgr_is_master()) { /* The shape of the frames per second quad depends on the * aspect ratio of the label texture and the aspect ratio of * the window (because we are placing the quad in normalized * device coordinates). */ int windowWidth, windowHeight; viewmat_window_size(&windowWidth, &windowHeight); float windowAspect = windowWidth / (float)windowHeight; float stretchLabel[16]; mat4f_scale_new(stretchLabel, 1/8.0 * fpsLabelAspectRatio / windowAspect, 1/8.0, 1); /* Position label in the upper left corner of the screen */ float transLabel[16]; mat4f_translate_new(transLabel, -.9, .8, 0); mat4f_mult_mat4f_new(modelview, transLabel, stretchLabel); glUniformMatrix4fv(kuhl_get_uniform("ModelView"), 1, 0, modelview); /* Make sure we don't use a projection matrix */ float identity[16]; mat4f_identity(identity); glUniformMatrix4fv(kuhl_get_uniform("Projection"), 1, 0, identity); /* Don't use depth testing and make sure we use the texture * rendering style */ glDisable(GL_DEPTH_TEST); glUniform1i(kuhl_get_uniform("renderStyle"), 1); kuhl_geometry_draw(&labelQuad); /* Draw the quad */ glEnable(GL_DEPTH_TEST); kuhl_errorcheck(); } glUseProgram(0); // stop using a GLSL program. } // finish viewport loop viewmat_end_frame(); /* Update the model for the next frame based on the time. We * convert the time to seconds and then use mod to cause the * animation to repeat. */ int time = glutGet(GLUT_ELAPSED_TIME); dgr_setget("time", &time, sizeof(int)); kuhl_update_model(modelgeom, 0, ((time%10000)/1000.0)); /* Check for errors. If there are errors, consider adding more * calls to kuhl_errorcheck() in your code. */ kuhl_errorcheck(); //kuhl_video_record("videoout", 30); /* Ask GLUT to call display() again. We shouldn't call display() * ourselves recursively because it will not leave time for GLUT * to call other callback functions for when a key is pressed, the * window is resized, etc. */ glutPostRedisplay(); }