void clampPaddles() { float frustum[6]; projmat_get_frustum(frustum, -1, -1); // left screen boundary if(paddleA.xpos < frustum[0]+paddleA.width/2) paddleA.xpos = frustum[0]+paddleA.width/2; if(paddleB.xpos < frustum[0]+paddleB.width/2) paddleB.xpos = frustum[0]+paddleB.width/2; // right screen boundary if(paddleA.xpos > frustum[1]-paddleA.width/2) paddleA.xpos = frustum[1]-paddleA.width/2; if(paddleB.xpos > frustum[1]-paddleB.width/2) paddleB.xpos = frustum[1]-paddleB.width/2; }
/* Called by GLUT whenever the window needs to be redrawn. This * function should not be called directly by the programmer. Instead, * we can call glutPostRedisplay() to request that GLUT call display() * at some point. */ void display() { /* If we are using DGR, send or receive data to keep multiple * processes/computers synchronized. */ dgr_update(); glClearColor(.2,.2,.2,0); // set clear color to grey // Clear the screen to black, clear the depth buffer glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); // turn on depth testing kuhl_errorcheck(); /* 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. */ for(int viewportID=0; viewportID<viewmat_num_viewports(); 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]); /* Get the frustum information which will be later used to generate a perspective projection matrix. */ float f[6]; // left, right, top, bottom, near>0, far>0 projmat_get_frustum(f, viewport[2], viewport[3]); /* Get the view or camera matrix; update the frustum values if needed. */ float viewMat[16]; viewmat_get(viewMat, f, viewportID); /* Create a 4x4 perspective projection matrix from the frustum values. */ float perspective[16]; mat4f_frustum_new(perspective,f[0], f[1], f[2], f[3], f[4], f[5]); /* Calculate an angle to rotate the * object. glutGet(GLUT_ELAPSED_TIME) is the number of * milliseconds since glutInit() was called. */ int count = glutGet(GLUT_ELAPSED_TIME) % 10000; // get a counter that repeats every 10 seconds float angle = count / 10000.0 * 360; // rotate 360 degrees every 10 seconds /* Make sure all computers/processes use the same angle */ dgr_setget("angle", &angle, sizeof(GLfloat)); /* Create a 4x4 rotation matrix based on the angle we computed. */ float rotateMat[16]; mat4f_rotateAxis_new(rotateMat, angle, 0,1,0); /* Create a scale matrix. */ float scaleMatrix[16]; mat4f_scale_new(scaleMatrix, 3, 3, 3); // Modelview = (viewMatrix * scaleMatrix) * rotationMatrix float modelview[16]; mat4f_mult_mat4f_new(modelview, viewMat, scaleMatrix); mat4f_mult_mat4f_new(modelview, modelview, rotateMat); kuhl_errorcheck(); 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 /* Send the modelview matrix to the vertex program. */ glUniformMatrix4fv(kuhl_get_uniform("ModelView"), 1, // number of 4x4 float matrices 0, // transpose modelview); // value kuhl_errorcheck(); /* Draw the geometry using the matrices that we sent to the * vertex programs immediately above */ kuhl_geometry_draw(&triangle); kuhl_geometry_draw(&quad); glUseProgram(0); // stop using a GLSL program. } // finish viewport loop /* Check for errors. If there are errors, consider adding more * calls to kuhl_errorcheck() in your code. */ kuhl_errorcheck(); /* Display the buffer we just drew (necessary for double buffering). */ glutSwapBuffers(); /* 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(); }
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(); }
void display() { /* If we are using DGR, send or receive data to keep multiple * processes/computers synchronized. */ dgr_update(); /* Syncronize the DGR objects */ dgr_setget("paddleA", &paddleA, sizeof(Paddle)); dgr_setget("paddleB", &paddleB, sizeof(Paddle)); dgr_setget("ball", &ball, sizeof(Ball)); dgr_setget("planet", planet, sizeof(float)*3); dgr_setget("state", &gameState, sizeof(int)); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); glEnable(GL_NORMALIZE); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); glEnable(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glClearColor(0,0,0,0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glColor3f(1,1,1); glMatrixMode(GL_PROJECTION); glLoadIdentity(); float frustum[6]; projmat_get_frustum(frustum, -1, -1); glOrtho(frustum[0], frustum[1], frustum[2], frustum[3], frustum[4], frustum[5]); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Pick a depth that is between the near and far planes. float depth = -(frustum[4] + frustum[5])/2.0; // Move the light source GLfloat position[] = { 1.0f, -1.0f, depth+5.5f, 1.0f }; glLightfv(GL_LIGHT0, GL_POSITION, position); // Draw the background stars float masterfrust[6]; projmat_get_master_frustum(masterfrust); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, texIdStars); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Draw the background quad with the scrolling star texture float tickmod = ticks / 200.0f; glBegin(GL_QUADS); glTexCoord2f(tickmod+1.0f, -tickmod); glVertex3f(masterfrust[1], masterfrust[3], depth-3.0); glTexCoord2f(tickmod, -tickmod); glVertex3f(masterfrust[0], masterfrust[3], depth-3.0); glTexCoord2f(tickmod, 1.0f-tickmod); glVertex3f(masterfrust[0], masterfrust[2], depth-3.0); glTexCoord2f(tickmod+1.0f, 1.0f-tickmod); glVertex3f(masterfrust[1], masterfrust[2], depth-3.0); glEnd(); //Draw the earth glMatrixMode(GL_MODELVIEW); glPushMatrix(); glBindTexture(GL_TEXTURE_2D, texIdEarth); glTranslatef(planet[0], planet[1], depth-3.0); glRotatef(25.0f, 0.0f, 0.0f, 1.0f); glRotatef(-90, 1.0f, 0.0f, 0.0f); glRotatef(ticks, 0.0f, 0.0f, 1.0f); ticks += .005; if(ticks > 360.0f)ticks = 0.0f; gluSphere(earth, planet[2]*1.65f, 200, 200); glPopMatrix(); //Draw the clouds glEnable(GL_BLEND); glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR); glPushMatrix(); glBindTexture(GL_TEXTURE_2D, texIdClouds); glLoadIdentity(); glTranslatef(planet[0], planet[1], depth-3.0); glRotatef(25.0f, 0.0f, 0.0f, 1.0f); glRotatef(-90, 1.0f, 0.0f, 0.0f); glRotatef(ticks, 1.0f, 0.0f, 1.0f); gluSphere(clouds, planet[2]*1.652f, 200, 200); glPopMatrix(); // Reset somethings for the rest of the scene glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // top player (player 1) paddle drawPaddle(paddleA, depth+5.0f); // bottom player (player 2) paddle drawPaddle(paddleB, depth+5.0f); glDisable(GL_BLEND); // ball glEnable(GL_LIGHTING); glColor3fv(ball.color); glPushMatrix(); glTranslatef(ball.xpos, ball.ypos, depth+4.0f); glutSolidSphere(ball.radius, 100, 100); glPopMatrix(); /* If DGR is enabled, only do this in the master*/ if(dgr_is_enabled() == 0 || dgr_is_master()) { // Run the game code game(); } glFlush(); glutSwapBuffers(); glutPostRedisplay(); // call display() repeatedly }
int main( int argc, char* argv[] ) { /* Initialize glut */ glutInit(&argc, argv); //initialize the toolkit glEnable(GL_POINT_SMOOTH); glutSetOption(GLUT_MULTISAMPLE, 4); // set msaa samples; default to 4 /* Ask GLUT to for a double buffered, full color window that includes a depth buffer */ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE); //set display mode glutInitWindowSize(768, 512); //set window size glutInitWindowPosition(0, 0); //set window position on screen glutCreateWindow(argv[0]); //open the screen window glEnable(GL_MULTISAMPLE); /* Initialize glew */ int glew_err = glewInit(); if(glew_err != GLEW_OK) fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(glew_err)); /* Initialize call backs */ glutDisplayFunc(display); glutKeyboardFunc(keyboard); /* Initialize DGR */ dgr_init(); /* Initialize DGR based on environment variables. */ projmat_init(); /* Figure out which projection matrix we should use based on environment variables */ float frustum[6]; // left, right, bottom, top, near, far // 0 1 2 3 4 5 projmat_get_frustum(frustum, -1, -1); ball.xpos = (frustum[0] + frustum[1])/2.0; ball.ypos = (frustum[2] + frustum[3])/2.0; ball.speed = ball.minSpeed = (frustum[3]-frustum[2]) / 178.462f; paddleA.xpos = ball.xpos; paddleA.ypos = frustum[3]-(frustum[3]-frustum[2])/20.0; paddleA.width = (frustum[1]-frustum[0])/10.0; paddleA.increment = paddleA.width / 3.0; paddleA.thickness = (frustum[3]-frustum[2])/25.0; paddleB.xpos = paddleA.xpos; paddleB.ypos = frustum[2]+(frustum[3]-frustum[2])/20.0; paddleB.width = paddleA.width; paddleB.increment = paddleA.increment; paddleB.thickness = -paddleA.thickness; msg(INFO, "Initial ball position %f %f\n", ball.xpos, ball.ypos); msg(INFO, "Initial Ball speed: %f\n", frustum[3]-frustum[2], ball.speed); msg(INFO, "Initial paddle A position %f %f\n", paddleA.xpos, paddleA.ypos); msg(INFO, "Initial paddle B position %f %f\n", paddleB.xpos, paddleB.ypos); ball.radius = (frustum[1]-frustum[0])/50.0; planet[0] = ((frustum[0] + frustum[1])/2.0f) - ((frustum[1] - frustum[0])/2.4f); planet[1] = ((frustum[2] + frustum[3])/2.0f) - ((frustum[1] - frustum[0])*1.7f); planet[2] = (frustum[1] - frustum[0]); earth = gluNewQuadric(); clouds = gluNewQuadric(); gluQuadricDrawStyle(earth, GLU_FILL); gluQuadricTexture(earth, GL_TRUE); gluQuadricNormals(earth, GLU_SMOOTH); gluQuadricDrawStyle(clouds, GLU_FILL); gluQuadricTexture(clouds, GL_TRUE); gluQuadricNormals(clouds, GLU_SMOOTH); kuhl_read_texture_file(EARTH, &texIdEarth); kuhl_read_texture_file(CLOUDS, &texIdClouds); kuhl_read_texture_file(STARS, &texIdStars); glutMainLoop(); }
void game() { float frustum[6]; projmat_get_frustum(frustum, -1, -1); if(USE_VRPN) { vrpn_get(TRACKED_OBJ_A, NULL, vrpnPos, vrpnOrient); paddleA.xpos = vrpnPos[0]; if(vrpnPos[1] <= .5) paddleA.ready = true; vrpn_get(TRACKED_OBJ_B, NULL, vrpnPos, vrpnOrient); paddleB.xpos = vrpnPos[0]; if(vrpnPos[1] <= .5) paddleB.ready = true; } //Preform the action based on the game state switch(gameState) { //This state indicates that atleast one player is not ready case GS_WAITING: //When both players are ready, shift to the ready state if(paddleA.ready && paddleB.ready) { startTime = time(NULL); gameState = GS_READY; } else { // Reset the ball to it's starting state ball.xpos = (frustum[0]+frustum[1])/2.0; ball.ypos = (frustum[2]+frustum[3])/2.0; ball.xdir = 0; ball.ydir = 0; ball.color[0] = ball.baseColor[0]; ball.color[1] = ball.baseColor[1]; ball.color[2] = ball.baseColor[2]; } break; //This state indicates that both players are ready to play case GS_READY: //We should wait in this state for 2 seconds if(time(NULL)-startTime >= 2) { //Start the ball moving either up or down. srand48(startTime); ball.ydir = 1; if(drand48() < .5) ball.ydir = -1; gameState = GS_PLAYING; } break; //This state indicates that the game is currently being played case GS_PLAYING: // Move the ball ball.xpos += ball.xdir * ball.speed; ball.ypos += ball.ydir * ball.speed; //Make sure the ball has not slowed down too much if(ball.speed < ball.minSpeed) { ball.speed = ball.minSpeed; } bool isBounce = false; //Handle the sides of the play area if(ball.xpos-ball.radius < frustum[0]) // left wall { ball.xpos = frustum[0]+ball.radius; ball.xdir = -ball.xdir; isBounce = true; } if(ball.xpos+ball.radius > frustum[1]) // right wall { ball.xpos = frustum[1]-ball.radius; ball.xdir = -ball.xdir; isBounce = true; } // Handle the Top and the bottom of the play area if(ball.ypos > frustum[3] || ball.ypos < frustum[2]) // top orr bottom wall { gameState = GS_SCORED; break; } // check for player 1 (top) paddle hit if(ball.ypos > paddleA.ypos-ball.radius && ball.ydir > 0) { // if we hit paddle if(ball.xpos+ball.radius*.9 > paddleA.xpos-paddleA.width/2 && ball.xpos-ball.radius*.9 < paddleA.xpos+paddleA.width/2) { ball.ypos = paddleA.ypos-ball.radius; ball.ydir = -ball.ydir; isBounce = true; ball.bounceCount++; } } // check for player 2 (bottom) paddle hit if(ball.ypos < paddleB.ypos+ball.radius && ball.ydir < 0) { // if we hit paddle if(ball.xpos+ball.radius*.9 > paddleB.xpos-paddleB.width/2 && ball.xpos-ball.radius*.9 < paddleB.xpos+paddleB.width/2) { ball.ypos = paddleB.ypos+ball.radius; ball.ydir = -ball.ydir; isBounce = true; ball.bounceCount++; } } // speedup the ball periodically if(ball.bounceCount == ball.speedUp) { ball.bounceCount = 0; ball.speed = ball.speed / .7; // speed up ball.speedUp++; ball.color[0] = ball.fastColor[0]; ball.color[1] = ball.fastColor[1]; ball.color[2] = ball.fastColor[2]; } else // If a speedup didn't happen, make the ball more green { float step = (float)ball.bounceCount / ((float)ball.speedUp-1); ball.color[0] = ball.baseColor[0] + ((ball.fastColor[0] - ball.baseColor[0]) * step); ball.color[1] = ball.baseColor[1] + ((ball.fastColor[1] - ball.baseColor[1]) * step); ball.color[2] = ball.baseColor[2] + ((ball.fastColor[2] - ball.baseColor[2]) * step); } // add noise to bounces so they don't bounce perfectly. if(isBounce) { // add more noise as game speeds up. int scale = ball.speedUp; if(scale > 3) scale = 3; double newXdir; double newYdir; do { newXdir = ball.xdir + (drand48()-.5) / 8.0 * scale; newYdir = ball.ydir + (drand48()-.5) / 8.0 * scale; // normalize direction vector float dirLength = sqrtf(newXdir*newXdir + newYdir*newYdir); newXdir /= dirLength; newYdir /= dirLength; // Keep trying new values until we find something that // isn't moving too much left/right. Also, force bounces // to keep the ball bouncing in the same direction // vertically. } while(fabs(newYdir) < .2 || ball.ydir * newYdir < 0); ball.xdir = newXdir; ball.ydir = newYdir; } break; //This state indicates that one player just scored case GS_SCORED: //Reset the bounce count, then figure out who scored ball.bounceCount = 0; bool paddleAScored = (ball.ypos < frustum[2]); //Change the paddle widths based on who scored paddleA.width += paddleA.increment * (paddleAScored ? 1 :-1); paddleB.width += paddleB.increment * (paddleAScored ? -1 :1); if(paddleA.width < 0.001 || paddleB.width < 0.001)//Check if some one lost the game { msg(WARNING, "%s Player wins!\n", (paddleAScored ? "Red" : "Blue")); //Reset the paddles for the next game paddleA.width = paddleB.width = (frustum[1]-frustum[0])/10.0; //Reset the ball for the next game ball.speed = ball.minSpeed = (frustum[3]-frustum[2]) / 178.462f; ball.speedUp = ball.baseSpeedUp; } else // Only lost the point, not the game; { ball.speed *= .7; // slow down ball.speedUp--; } //Set the players to not ready and transition to the waiting state paddleA.ready = paddleB.ready = false; gameState = GS_WAITING; break; } }
void display() { dgr_update(); dgr_setget("style", &renderStyle, sizeof(int)); // Clear the screen to black, clear the depth buffer glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 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); /* 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. */ for(int viewportID=0; viewportID<viewmat_num_viewports(); 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]); /* Get the frustum information which will be later used to generate a perspective projection matrix. */ float f[6]; // left, right, top, bottom, near>0, far>0 projmat_get_frustum(f, viewport[2], viewport[3]); /* Get the view or camera matrix; update the frustum values if needed. */ float viewMat[16]; viewmat_get(viewMat, f, viewportID); glUseProgram(program); /* Communicate matricies to OpenGL */ float perspective[16]; mat4f_frustum_new(perspective,f[0], f[1], f[2], f[3], f[4], f[5]); glUniformMatrix4fv(kuhl_get_uniform("Projection"), 1, // count 0, // transpose perspective); // value float modelMat[16]; get_model_matrix(modelMat); // modelview = view * model float modelview[16]; mat4f_mult_mat4f_new(modelview, viewMat, modelMat); glUniformMatrix4fv(kuhl_get_uniform("ModelView"), 1, // count 0, // transpose modelview); // value glUniform1i(kuhl_get_uniform("renderStyle"), renderStyle); // Copy far plane value into vertex program so we can render depth buffer. glUniform1f(kuhl_get_uniform("farPlane"), f[5]); kuhl_errorcheck(); kuhl_draw_model_file_ogl3(modelFilename, modelTexturePath, program); kuhl_errorcheck(); glUseProgram(0); // stop using a GLSL program. } // finish viewport loop int time = glutGet(GLUT_ELAPSED_TIME); float fps = kuhl_getfps(time); if(time % 1000 == 0) printf("Frames per second: %0.1f\n", fps); /* Check for errors. If there are errors, consider adding more * calls to kuhl_errorcheck() in your code. */ kuhl_errorcheck(); glFlush(); glFinish(); /* Display the buffer we just drew (necessary for double buffering). */ glutSwapBuffers(); // 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(); }
void display(void) { kuhl_limitfps(100); dgr_update(); // Make sure slaves get updates ASAP dgr_setget("currentTex", ¤tTexture, sizeof(int)); /* If the texture has changed since we were previously in display() */ if(alreadyDisplayedTexture != currentTexture) { // Load the new texture loadTexture(currentTexture); // Keep a record of which texture we are currently displaying // so we can detect when DGR changes currentTexture on a // slave. alreadyDisplayedTexture = currentTexture; } /* The view frustum is an orthographic frustum for this * application. The size of the frustum doesn't matter much, but * the aspect ratio of the frustum should match the aspect ratio * of the screen/window. */ float frustum[6], masterFrustum[6]; /* The following two methods will get the master view frustum and * the current process view frustum. If we are running in a * standalone version, these two frustums will be the same. */ projmat_get_master_frustum(masterFrustum); projmat_get_frustum(frustum, -1, -1); // frustum of this process (master or slave) /* Set this view frustum for this process. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(frustum[0],frustum[1], frustum[2],frustum[3], -1, 1); glMatrixMode(GL_MODELVIEW); // Middle of master frustum in the vertical direction. It divides the top // tiles from the bottom tiles. float masterFrustumMid = (masterFrustum[2]+masterFrustum[3])/2; // Dimensions of the master view frustum float masterFrustumWidth = masterFrustum[1]-masterFrustum[0]; float masterFrustumHeight = masterFrustum[3]-masterFrustum[2]; // The width of the quad (in frustum units). Since the image will // be stretched to fit the screen vertically, and our units are in // frustum units, the width of the tile is the height of the // frustum times the aspect ratio divided by the number of tiles // in the horizontal direction. float quadWidth = aspectRatio * masterFrustumHeight; float tileWidth = quadWidth/numTiles; // TODO: Maybe just scale the image vertically if the image almost fits in the screen horizontally? int msSincePictureDisplayed = glutGet(GLUT_ELAPSED_TIME)-lastAdvance; int scrollStatus = 0; // 0=don't need to scroll or done scrolling, 1=currently scrolling if(masterFrustumWidth < quadWidth)// do we need to scroll on this image { // Do we still need to scroll? if(scrollAmount < quadWidth-masterFrustumWidth) scrollStatus = 1; if(scrollStatus == 1) { // Wait a few seconds before scrolling. It takes a while // for all slaves on IVS to get the images. if(msSincePictureDisplayed > 5000) scrollAmount = ((msSincePictureDisplayed-5000) / (SCROLL_SPEED*1000.0))*masterFrustumWidth; else scrollAmount = 0; // If we calculated the scroll amount to be the largest // scrollAmount we'll need if(scrollAmount > quadWidth-masterFrustumWidth) { // dwell at the end of the image even if autoadvance is on. int now = glutGet(GLUT_ELAPSED_TIME); // make sure we still have a few seconds before advancing if(SLIDESHOW_WAIT*1000-(now-lastAdvance) < 3000) { // Go back and set lastAdvance time so we have // some time to dwell here. lastAdvance = now-SLIDESHOW_WAIT*1000+3000; } } } } dgr_setget("scrollAmount", &scrollAmount, sizeof(float)); /* If autoadvance is set and we are not scrolling (or done * scrolling) figure out if it is now time to advance to the next * image. */ if(autoAdvance == 1 && scrollStatus != 1) { // printf("time since last advance %d\n", glutGet(GLUT_ELAPSED_TIME)-lastAdvance); if(glutGet(GLUT_ELAPSED_TIME)-lastAdvance > SLIDESHOW_WAIT*1000) // time to show new image: { currentTexture = getNextTexture(); loadTexture(currentTexture); return; } } glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glColor3f(1,1,1); // color of quad // Draw the top and bottom quad for each of the tiles going across the screen horizontally. */ for(GLuint i=0; i<numTiles*2; i=i+2) { float tileLeft = (i/2 )*tileWidth + masterFrustum[0]; float tileRight = (i/2+1)*tileWidth + masterFrustum[0]; // Draw bottom tile glBindTexture(GL_TEXTURE_2D, texNames[i]); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2d(tileLeft -scrollAmount, masterFrustum[2]); // lower left glTexCoord2f(1.0, 0.0); glVertex2d(tileRight-scrollAmount, masterFrustum[2]); // lower right glTexCoord2f(1.0, 1.0); glVertex2d(tileRight-scrollAmount, masterFrustumMid); // upper right glTexCoord2f(0.0, 1.0); glVertex2d(tileLeft -scrollAmount, masterFrustumMid); // upper left glEnd(); // Draw top tile glBindTexture(GL_TEXTURE_2D, texNames[i+1]); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2d(tileLeft -scrollAmount, masterFrustumMid); // lower left glTexCoord2f(1.0, 0.0); glVertex2d(tileRight-scrollAmount, masterFrustumMid); // lower right glTexCoord2f(1.0, 1.0); glVertex2d(tileRight-scrollAmount, masterFrustum[3]); // upper right glTexCoord2f(0.0, 1.0); glVertex2d(tileLeft -scrollAmount, masterFrustum[3]); // upper left glEnd(); } glDisable(GL_TEXTURE_2D); /* Draw filename label on top of a quad. */ glColor4f(0,0,0,.3); glBegin(GL_QUADS); glVertex2d(-1,-1); glVertex2d(-.5,-1); glVertex2d(-.5,-.96); glVertex2d(-1,-.96); glEnd(); glColor4f(1,1,1,.9); glRasterPos2f(-.98,-.98); void *font = GLUT_BITMAP_TIMES_ROMAN_24; char *str = globalargv[currentTexture]; for(GLuint i=0; i<strlen(str); i++) glutBitmapCharacter(font, str[i]); /* Flush and swap the OpenGL buffers. */ glFlush(); glutSwapBuffers(); glutPostRedisplay(); }
/** Get a 4x4 view matrix. Some types of systems also need to update * the frustum based on where the virtual camera is. For example, on * the IVS display wall, the frustum is adjusted dynamically based on * where a person is relative to the screens. * * @param viewmatrix A 4x4 view matrix for viewmat to fill in. * * @param projmatrix A 4x4 projection matrix for viewmat to fill in. * * @param viewportID If there is only one viewport, set this to * 0. This value must be smaller than the value reported by * viewmat_num_viewports(). In an HMD, typically viewportID=0 is the * left eye and viewportID=1 is the right eye. However, some Oculus * HMDs will result in this being swapped. To definitively know which * eye this view matrix corresponds to, examine the return value of * this function. * * @return A viewmat_eye enum which indicates if this view matrix is * for the left, right, middle, or unknown eye. * */ viewmat_eye viewmat_get(float viewmatrix[16], float projmatrix[16], int viewportID) { viewmat_eye eye = viewmat_viewport_to_eye(viewportID); int viewport[4]; // x,y of lower left corner, width, height viewmat_get_viewport(viewport, viewportID); /* Get the view or camera matrix; update the frustum values if needed. */ float f[6]; // left, right, bottom, top, near>0, far>0 projmat_get_frustum(f, viewport[2], viewport[3], viewportID); /* If we are running in IVS mode and using the tracking systems, * all computers need to update their frustum differently. The * master process will be controlled by VRPN, and all slaves will * have their controllers set to "none". Here, we detect for this * situation and make sure all processes work correctly. */ if(viewmat_display_mode == VIEWMAT_IVS && viewmat_control_mode == VIEWMAT_CONTROL_VRPN) { // Will update view matrix and frustum information viewmat_get_ivs(viewmatrix, f); mat4f_frustum_new(projmatrix, f[0], f[1], f[2], f[3], f[4], f[5]); } else { switch(viewmat_control_mode) { case VIEWMAT_CONTROL_MOUSE: // mouse movement viewmat_get_mouse(viewmatrix, viewportID); mat4f_frustum_new(projmatrix, f[0], f[1], f[2], f[3], f[4], f[5]); break; case VIEWMAT_CONTROL_NONE: // Get the view matrix from the mouse movement code...but // we haven't registered mouse movement callback // functions, so mouse movement won't work. viewmat_get_mouse(viewmatrix, viewportID); mat4f_frustum_new(projmatrix, f[0], f[1], f[2], f[3], f[4], f[5]); break; case VIEWMAT_CONTROL_ORIENT: viewmat_get_orient_sensor(viewmatrix, viewportID); mat4f_frustum_new(projmatrix, f[0], f[1], f[2], f[3], f[4], f[5]); break; case VIEWMAT_CONTROL_OCULUS: viewmat_get_hmd_oculus(viewmatrix, projmatrix, viewportID); // previous function sets projmatrix for us... break; case VIEWMAT_CONTROL_VRPN: viewmat_get_vrpn(viewmatrix, viewportID); mat4f_frustum_new(projmatrix, f[0], f[1], f[2], f[3], f[4], f[5]); break; default: msg(MSG_FATAL, "Unknown viewmat control mode: %d\n", viewmat_control_mode); exit(EXIT_FAILURE); } } /* Send the view matrix to DGR. At some point in the future, we * may have multiple computers using different view matrices. For * now, even in IVS mode, all processes will use the same view * matrix (IVS uses different view frustums per process). */ char dgrkey[128]; snprintf(dgrkey, 128, "!!viewmat%d", viewportID); dgr_setget(dgrkey, viewmatrix, sizeof(float)*16); /* Sanity checks */ viewmat_validate_ipd(viewmatrix, viewportID); return eye; }
/* Called by GLUT whenever the window needs to be redrawn. This * function should not be called directly by the programmer. Instead, * we can call glutPostRedisplay() to request that GLUT call display() * at some point. */ void display() { /* If we are using DGR, send or receive data to keep multiple * processes/computers synchronized. */ dgr_update(); 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_limitfps(60); update(); kuhl_geometry_draw(modelgeom); /* Draw the model */ kuhl_errorcheck(); glUseProgram(0); // stop using a GLSL program. } // finish viewport loop viewmat_end_frame(); /* 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(); }