/** Get a view matrix from VRPN and adjust the view frustum * appropriately. * @param viewmatrix The location where the viewmatrix should be stored. * @param frustum The location of the view frustum that should be adjusted. */ static void viewmat_get_ivs(float viewmatrix[16], float frustum[6]) { /* Only get information from VRPN if we are DGR master, or if DGR * is being used at all. */ float pos[3]; if((dgr_is_enabled() && dgr_is_master()) || dgr_is_enabled()==0) { if(viewmat_control_mode == VIEWMAT_CONTROL_VRPN && viewmat_vrpn_obj != NULL) { /* get information from vrpn */ float orient[16]; vrpn_get(viewmat_vrpn_obj, NULL, pos, orient); } else { /* If no head tracking is available, assume the person is * standing at the origin with a normal eye height */ pos[0] = 0; pos[1] = 1.5; // normal eye height pos[2] = 0; } } /* Make sure all DGR hosts can get the position so that they * can update the frustum appropriately */ dgr_setget("!!viewMatPos", pos, sizeof(float)*3); /* Update view frustum if it was provided. */ if(frustum != NULL) { frustum[0] -= pos[0]; frustum[1] -= pos[0]; frustum[2] -= pos[1]; frustum[3] -= pos[1]; frustum[4] += pos[2]; frustum[5] += pos[2]; } /* calculate a lookat point */ float lookat[3]; float forwardVec[3] = { 0, 0, -1 }; for(int i=0; i<3; i++) lookat[i] = pos[i]+forwardVec[i]; float up[3] = {0, 1, 0}; mat4f_lookatVec_new(viewmatrix, pos, lookat, up); }
static void viewmat_get_vrpn(float viewmatrix[16], int viewportNum) { if(viewmat_vrpn_obj == NULL) return; float pos[3] = { 0,0,0 }; float rotMat[16], posMat[16]; vrpn_get(viewmat_vrpn_obj, NULL, pos, rotMat); mat4f_translate_new(posMat, -pos[0], -pos[1], -pos[2]); // position viewmat_fix_rotation(rotMat); mat4f_transpose(rotMat); /* orientation sensor rotates camera, not world */ float cyclopsViewMatrix[16]; mat4f_mult_mat4f_new(cyclopsViewMatrix, rotMat, posMat); viewmat_get_generic(viewmatrix, cyclopsViewMatrix, viewportNum); }
/** Checks if VIEWMAT_VRPN_OBJECT environment variable is set. If it is, use VRPN to control the camera position and orientation. @return Returns 1 if VRPN is set up, 0 otherwise. */ static int viewmat_init_vrpn(void) { viewmat_vrpn_obj = NULL; const char* vrpnObjString = getenv("VIEWMAT_VRPN_OBJECT"); if(vrpnObjString != NULL && strlen(vrpnObjString) > 0) { viewmat_vrpn_obj = vrpnObjString; msg(MSG_INFO, "View is following tracker object: %s\n", viewmat_vrpn_obj); /* Try to connect to VRPN server */ float vrpnPos[3]; float vrpnOrient[16]; vrpn_get(viewmat_vrpn_obj, NULL, vrpnPos, vrpnOrient); return 1; } return 0; }
/** Draw a object at the location and orientation of the tracked vrpn object */ void drawObject(const int objectIndex, float viewMat[16]) { const char *vrpnObject = global_argv[objectIndex]; const float scaleFactor = .5; float pos[4], orient[16]; vrpn_get(vrpnObject, NULL, pos, orient); float modelMat[16],translate[16],scale[16]; mat4f_scale_new(scale, scaleFactor, scaleFactor, scaleFactor); mat4f_translateVec_new(translate, pos); mat4f_mult_mat4f_many(modelMat, translate, orient, scale, NULL); 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"), 2); kuhl_errorcheck(); kuhl_geometry_draw(modelgeom); /* Draw the model */ kuhl_errorcheck(); /* Transparency of labels may not appear right because we aren't * sorting them by depth. */ float labelScale[16]; mat4f_scale_new(labelScale, 1, 1/labelAspectRatio[objectIndex-1], 1); mat4f_mult_mat4f_new(modelview, modelview, labelScale); glUniformMatrix4fv(kuhl_get_uniform("ModelView"), 1, 0, modelview); glUniform1i(kuhl_get_uniform("renderStyle"), 1); kuhl_geometry_texture(&quad, label[objectIndex-1], "tex", 1); kuhl_geometry_draw(&quad); #if 0 printf("%s is at\n", vrpnObject); vec3f_print(pos); mat4f_print(orient); #endif }
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; } }
/** Get view and projection matrices appropriate for the Oculus HMD */ static void viewmat_get_hmd_oculus(float viewmatrix[16], float projmatrix[16], int viewportID) { #ifndef MISSING_OVR /* Oculus recommends the order that we should render eyes. We * assume that smaller viewportIDs are rendered first. So, we need * to map the viewportIDs to the specific Oculus HMD eye. The * "eye" variable will be set to either ovrEye_Left (if we are * rendering the left eye) or ovrEye_Right (if we are rendering * the right eye). */ ovrEyeType eye = hmd->EyeRenderOrder[viewportID]; /* Oculus doesn't provide us with easy access to the view * frustum information. We get the projection matrix directly * from libovr. */ ovrMatrix4f ovrpersp = ovrMatrix4f_Projection(hmd->DefaultEyeFov[eye], 0.5, 500, 1); mat4f_setRow(projmatrix, &(ovrpersp.M[0][0]), 0); mat4f_setRow(projmatrix, &(ovrpersp.M[1][0]), 1); mat4f_setRow(projmatrix, &(ovrpersp.M[2][0]), 2); mat4f_setRow(projmatrix, &(ovrpersp.M[3][0]), 3); float offsetMat[16], rotMat[16], posMat[16], initPosMat[16]; mat4f_identity(offsetMat); // Viewpoint offset (IPD, etc); mat4f_identity(rotMat); // tracking system rotation mat4f_identity(posMat); // tracking system position mat4f_identity(initPosMat); // camera starting location /* Construct posMat and rotMat matrices which indicate the * position and orientation of the HMD. */ if(viewmat_vrpn_obj) // get position from VRPN { /* Get the offset for the left and right eyes from * Oculus. If you are using a separate tracking system, you * may also want to apply an offset here between the tracked * point and the eye location. */ mat4f_translate_new(offsetMat, eye_rdesc[eye].HmdToEyeViewOffset.x, // left & right IPD offset eye_rdesc[eye].HmdToEyeViewOffset.y, // vertical offset eye_rdesc[eye].HmdToEyeViewOffset.z); // forward/back offset float pos[3] = { 0,0,0 }; vrpn_get(viewmat_vrpn_obj, NULL, pos, rotMat); mat4f_translate_new(posMat, -pos[0], -pos[1], -pos[2]); // position viewmat_fix_rotation(rotMat); } else // get position from Oculus tracker { pose[eye] = ovrHmd_GetHmdPosePerEye(hmd, eye); mat4f_translate_new(posMat, // position (includes IPD offset) -pose[eye].Position.x, -pose[eye].Position.y, -pose[eye].Position.z); mat4f_rotateQuat_new(rotMat, // rotation pose[eye].Orientation.x, pose[eye].Orientation.y, pose[eye].Orientation.z, pose[eye].Orientation.w); // Starting point: // Translate the world based on the initial camera position // specified in viewmat_init(). You may choose to initialize the // camera position with y=1.5 meters to approximate a normal // standing eyeheight. float initPosVec[3]; vec3f_scalarMult_new(initPosVec, oculus_initialPos, -1.0f); mat4f_translateVec_new(initPosMat, initPosVec); // TODO: Could also get eyeheight via ovrHmd_GetFloat(hmd, OVR_KEY_EYE_HEIGHT, 1.65) } mat4f_transpose(rotMat); /* orientation sensor rotates camera, not world */ // viewmatrix = offsetMat * rotMat * posMat * initposmat mat4f_mult_mat4f_new(viewmatrix, offsetMat, rotMat); // offset is identity if we are using Oculus tracker mat4f_mult_mat4f_new(viewmatrix, viewmatrix, posMat); mat4f_mult_mat4f_new(viewmatrix, viewmatrix, initPosMat); if(0) { printf("ViewportID=%d; eye=%s\n", viewportID, eye == ovrEye_Left ? "left" : "right"); printf("Eye offset according to OVR (only used if VRPN is used): "); mat4f_print(offsetMat); printf("Rotation sensing (from OVR or VRPN): "); mat4f_print(rotMat); printf("Position tracking (from OVR or VRPN): "); mat4f_print(posMat); printf("Initial position (from set in viewmat_init()): "); mat4f_print(initPosMat); printf("Final view matrix: "); mat4f_print(viewmatrix); } #else /* We shouldn't ever get here, but we'll generate a generic view * and projection matrix just in case... */ mat4f_lookat_new(viewmatrix, 0,1.55,0, 0,1.55,-1, 0,1,0); mat4f_perspective_new(projmatrix, 50, 1, 0.5, 500); #endif }