int main(int argc, char** argv) { char *modelFilename = NULL; char *modelTexturePath = NULL; int currentArgIndex = 1; // skip program name int usageError = 0; while(argc > currentArgIndex) { if(strcmp(argv[currentArgIndex], "--fit") == 0) fitToView = 1; else if(strcmp(argv[currentArgIndex], "--origin") == 0) showOrigin = 1; else if(modelFilename == NULL) { modelFilename = argv[currentArgIndex]; modelTexturePath = NULL; } else if(modelTexturePath == NULL) modelTexturePath = argv[currentArgIndex]; else { usageError = 1; } currentArgIndex++; } // If we have no model to load or if there were too many arguments. if(modelFilename == NULL || usageError) { printf("Usage:\n" "%s [--fit] [--origin] modelFile - Textures are assumed to be in the same directory as the model.\n" "- or -\n" "%s [--fit] [--origin] modelFile texturePath\n" "If the optional --fit parameter is included, the model will be scaled and translated to fit within the approximate view of the camera\n" "If the optional --origin parameter is included, a box will is drawn at the origin and unit-length lines are drawn down each axis.\n", argv[0], argv[0]); exit(EXIT_FAILURE); } /* set up our GLUT window */ glutInit(&argc, argv); glutInitWindowSize(512, 512); 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 */ #ifdef __APPLE__ glutInitDisplayMode(GLUT_3_2_CORE_PROFILE | GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE); #else glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE); glutInitContextVersion(3,2); glutInitContextProfile(GLUT_CORE_PROFILE); #endif glutCreateWindow(argv[0]); // set window title to executable name glEnable(GL_MULTISAMPLE); /* Initialize GLEW */ glewExperimental = GL_TRUE; GLenum glewError = glewInit(); if(glewError != GLEW_OK) { fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(glewError)); exit(EXIT_FAILURE); } /* When experimental features are turned on in GLEW, the first * call to glGetError() or kuhl_errorcheck() may incorrectly * report an error. So, we call glGetError() to ensure that a * later call to glGetError() will only see correct errors. For * details, see: * http://www.opengl.org/wiki/OpenGL_Loading_Library */ glGetError(); // setup callbacks glutDisplayFunc(display); glutKeyboardFunc(keyboard); /* Compile and link a GLSL program composed of a vertex shader and * a fragment shader. */ program = kuhl_create_program(GLSL_VERT_FILE, GLSL_FRAG_FILE); dgr_init(); /* Initialize DGR based on environment variables. */ projmat_init(); /* Figure out which projection matrix we should use based on environment variables */ float initCamPos[3] = {0,1.55,2}; // 1.55m is a good approx eyeheight float initCamLook[3] = {0,0,0}; // a point the camera is facing at float initCamUp[3] = {0,1,0}; // a vector indicating which direction is up viewmat_init(initCamPos, initCamLook, initCamUp); // Clear the screen while things might be loading glClearColor(.2,.2,.2,1); glClear(GL_COLOR_BUFFER_BIT); // Load the model from the file modelgeom = kuhl_load_model(modelFilename, modelTexturePath, program, bbox); if(showOrigin) origingeom = kuhl_load_model("../models/origin/origin.obj", NULL, program, NULL); init_geometryQuad(&labelQuad, program); kuhl_getfps_init(&fps_state); /* Tell GLUT to start running the main loop and to call display(), * keyboard(), etc callback methods as needed. */ glutMainLoop(); /* // An alternative approach: while(1) glutMainLoopEvent(); */ exit(EXIT_SUCCESS); }
/** Uses the VRPN library to get the position and orientation of a * tracked object. * * @param object The name of the object being tracked. * * @param hostname The IP address or hostname of the VRPN server or * tracking system computer. If hostname is set to NULL, the * ~/.vrpn-server file is consulted. * * @param pos An array to be filled in with the position information * for the tracked object. If we are unable to track the object, a * message may be printed and pos will be set to a fixed value. * * @param orient An array to be filled in with the orientation matrix * for the tracked object. The orientation matrix is in row-major * order can be used with OpenGL. If the tracking system is moving an * object around on the screen, this matrix can be used directly. If * the tracking system is moving the OpenGL camera, this matrix may * need to be inverted. If we are unable to track the object, a * message may be printed and orient will be set to the identity * matrix. * * @return 1 if we returned data from the tracker. 0 if there was * problems connecting to the tracker. */ int vrpn_get(const char *object, const char *hostname, float pos[3], float orient[16]) { /* Set to default values */ vec3f_set(pos, 10000,10000,10000); mat4f_identity(orient); #ifdef MISSING_VRPN printf("You are missing VRPN support.\n"); return 0; #else if(object == NULL || strlen(object) == 0) { msg(WARNING, "Empty or NULL object name was passed into this function.\n"); return 0; } if(hostname != NULL && strlen(hostname) == 0) { msg(WARNING, "Hostname is an empty string.\n"); return 0; } /* Construct an object@hostname string. */ std::string hostnamecpp; std::string objectcpp; if(hostname == NULL) { char *hostnameInFile = vrpn_default_host(); if(hostnameInFile) hostnamecpp = hostnameInFile; else { msg(ERROR, "Failed to find hostname of VRPN server.\n"); exit(EXIT_FAILURE); } } else hostnamecpp = hostname; objectcpp = object; std::string fullname = objectcpp + "@" + hostnamecpp; /* Check if we have a tracker object for that string in our map. */ if(nameToTracker.count(fullname)) { /* If we already have a tracker object, ask it to run the main * loop (and therefore call our handle_tracker() function if * there is new data). */ nameToTracker[fullname]->mainloop(); /* If our callback has been called, get the callback object * and get the data out of it. */ if(nameToCallbackData.count(fullname)) { vrpn_TRACKERCB t = nameToCallbackData[fullname]; float pos4[4]; for(int i=0; i<3; i++) pos4[i] = t.pos[i]; pos4[3]=1; double orientd[16]; // Convert quaternion into orientation matrix. q_to_ogl_matrix(orientd, t.quat); for(int i=0; i<16; i++) orient[i] = (float) orientd[i]; /* VICON in the MTU IVS lab is typically calibrated so that: * X = points to the right (while facing screen) * Y = points into the screen * Z = up * (left-handed coordinate system) * * PPT is typically calibrated so that: * X = the points to the wall that has two closets at both corners * Y = up * Z = points to the door * (right-handed coordinate system) * * By default, OpenGL assumes that: * X = points to the right (while facing screen in the IVS lab) * Y = up * Z = points OUT of the screen (i.e., -Z points into the screen in te IVS lab) * (right-handed coordinate system) * * Below, we convert the position and orientation * information into the OpenGL convention. */ if(strlen(hostnamecpp.c_str()) > 14 && strncmp(hostnamecpp.c_str(), "tcp://141.219.", 14) == 0) // MTU vicon tracker { float viconTransform[16] = { 1,0,0,0, // column major order! 0,0,-1,0, 0,1,0,0, 0,0,0,1 }; mat4f_mult_mat4f_new(orient, viconTransform, orient); mat4f_mult_vec4f_new(pos4, viconTransform, pos4); vec3f_copy(pos,pos4); return 1; // we successfully collected some data } else // Non-Vicon tracker { /* Don't transform other tracking systems */ // orient is already filled in vec3f_copy(pos, pos4); return 1; // we successfully collected some data } } } else { /* If this is our first time, create a tracker for the object@hostname string, register the callback handler. */ msg(INFO, "Connecting to VRPN server: %s\n", hostnamecpp.c_str()); // If we are making a TCP connection and the server isn't up, the following function call may hang for a long time vrpn_Connection *connection = vrpn_get_connection_by_name(hostnamecpp.c_str()); /* Wait for a bit to see if we can connect. Sometimes we don't immediately connect! */ for(int i=0; i<1000 && !connection->connected(); i++) { usleep(1000); // 1000 microseconds * 1000 = up to 1 second of waiting. connection->mainloop(); } /* If connection failed, exit. */ if(!connection->connected()) { delete connection; msg(ERROR, "Failed to connect to tracker: %s\n", fullname.c_str()); return 0; } vrpn_Tracker_Remote *tkr = new vrpn_Tracker_Remote(fullname.c_str(), connection); nameToTracker[fullname] = tkr; tkr->register_change_handler((void*) fullname.c_str(), handle_tracker); kuhl_getfps_init(&fps_state); kalman_initialize(&kalman, 0.1, 0.1); } return 0; #endif }
/* 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(); /* 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. This program always draws the geometry in the same order.). */ 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_eye eye = viewmat_get(viewMat, perspective, viewportID); /* Always put camera at origin (no translation, no * interpupillary distance). */ float noTranslation[4] = {0,0,0,1}; mat4f_setColumn(viewMat, noTranslation, 3); // last column /* Create a scale matrix. */ float scaleMatrix[16]; mat4f_scale_new(scaleMatrix, 20, 20, 20); // Modelview = (viewMatrix * scaleMatrix) * rotationMatrix float modelview[16]; mat4f_mult_mat4f_new(modelview, viewMat, scaleMatrix); 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(); if(texIdLeft != 0 && texIdRight != 0) // cylinder { /* Draw the cylinder with the appropriate texture */ if(eye == VIEWMAT_EYE_RIGHT) kuhl_geometry_texture(&cylinder, texIdRight, "tex", KG_WARN); else kuhl_geometry_texture(&cylinder, texIdLeft, "tex", KG_WARN); kuhl_geometry_draw(&cylinder); } else // cubemap { if(eye == VIEWMAT_EYE_RIGHT) setupCubemap(cubemapRightTex, quad, modelview); else setupCubemap(cubemapLeftTex, quad, modelview); } } // 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(); /* 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(); static int fps_state_init = 0; static kuhl_fps_state fps_state; if(fps_state_init == 0) { kuhl_getfps_init(&fps_state); fps_state_init = 1; } float fps = kuhl_getfps(&fps_state); if(fps_state.frame == 0) msg(INFO, "fps: %6.1f\n", fps); }
int main(int argc, char** argv) { /* Initialize GLUT and GLEW */ kuhl_ogl_init(&argc, argv, 512, 512, 32, GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE, 4); if(argc == 1) { msg(FATAL, "You didn't provide the name of the object(s) that you want to track."); msg(FATAL, "Usage: %s vrpnObjectName1 vrpnObjectName2 ..."); exit(EXIT_FAILURE); } global_argc = argc; global_argv = argv; // setup callbacks glutDisplayFunc(display); glutKeyboardFunc(keyboard); /* Compile and link a GLSL program composed of a vertex shader and * a fragment shader. */ program = kuhl_create_program(GLSL_VERT_FILE, GLSL_FRAG_FILE); dgr_init(); /* Initialize DGR based on environment variables. */ projmat_init(); /* Figure out which projection matrix we should use based on environment variables */ viewmat_init(initCamPos, initCamLook, initCamUp); // Clear the screen while things might be loading glClearColor(.2,.2,.2,1); glClear(GL_COLOR_BUFFER_BIT); // Load the model from the file //modelgeom = kuhl_load_model("../models/cube/cube.obj", NULL, program, NULL); modelgeom = kuhl_load_model("../models/origin/origin.obj", NULL, program, NULL); init_geometryQuad(&quad, program); label = malloc(sizeof(GLuint)*argc-1); labelAspectRatio = malloc(sizeof(float)*argc-1); float labelColor[3] = { 1,1,1 }; float labelBg[4] = { 0,0,0,.3 }; for(int i=1; i<argc; i++) { labelAspectRatio[i-1] = kuhl_make_label(argv[i], &(label[i-1]), labelColor, labelBg, 24); } kuhl_getfps_init(&fps_state); /* Tell GLUT to start running the main loop and to call display(), * keyboard(), etc callback methods as needed. */ glutMainLoop(); /* // An alternative approach: while(1) glutMainLoopEvent(); */ exit(EXIT_SUCCESS); }