/*********************************************************** CSC418, SPRING 2005 dog.cpp author: Mike Pratscher based on code by: Eron Steger, J. Radulovich Main source file for assignment 2 Uses OpenGL, GLUT and GLUI libraries Instructions: Please read the assignment page to determine exactly what needs to be implemented. Then read over this file and become acquainted with its design. In particular, see lines marked 'README'. Be sure to also look over keyframe.h and vector.h. While no changes are necessary to these files, looking them over will allow you to better understand their functionality and capabilites. Add source code where it appears appropriate. In particular, see lines marked 'TODO'. You should not need to change the overall structure of the program. However it should be clear what your changes do, and you should use sufficient comments to explain your code. While the point of the assignment is to draw and animate the character, you will also be marked based on your design. ***********************************************************/ #ifdef _WIN32 #include #endif #include #include #include #include #include #include #include #include #include "keyframe.h" #include "timer.h" #include "vector.h" // *************** GLOBAL VARIABLES ************************* const float PI = 3.14159; const float SPINNER_SPEED = 0.1; // --------------- USER INTERFACE VARIABLES ----------------- // Window settings int windowID; // Glut window ID (for display) int Win[2]; // window (x,y) size GLUI* glui_joints; // Glui window with joint controls GLUI* glui_keyframe; // Glui window with keyframe controls GLUI* glui_render; // Glui window for render style char msg[256]; // String used for status message GLUI_StaticText* status; // Status message ("Status: ") // ---------------- ANIMATION VARIABLES --------------------- // Camera settings bool updateCamZPos = false; int lastX = 0; int lastY = 0; const float ZOOM_SCALE = 0.01; GLdouble camXPos = 0.0; GLdouble camYPos = 0.0; GLdouble camZPos = -7.5; const GLdouble CAMERA_FOVY = 60.0; const GLdouble NEAR_CLIP = 0.1; const GLdouble FAR_CLIP = 1000.0; // Render settings enum { WIREFRAME, SOLID, OUTLINED }; // README: the different render styles int renderStyle = WIREFRAME; // README: the selected render style // Animation settings int animate_mode = 0; // 0 = no anim, 1 = animate const float KAPPA = 1.0; // Keyframe settings Keyframe* keyframes; // list of keyframes int maxValidKeyframe = 0; // index of max valid keyframe (in keyframe list) const int KEYFRAME_MIN = 0; const int KEYFRAME_MAX = 10; // README: specifies the max number of keyframes // Time settings Timer* animationTimer; Timer* frameRateTimer; const float TIME_MIN = 0.0; const float TIME_MAX = 30.0; // README: specifies the max time of the animation const float SEC_PER_FRAME = 1.0 / 60.0; // Joint settings // README: This is the key data structure for // updating keyframes in the keyframe list and // for driving the animation. // i) When updating a keyframe, use the values // in this data structure to update the // appropriate keyframe in the keyframe list. // ii) When calculating the interpolated pose, // the resulting pose vector is placed into // this data structure. (This code is already // in place - see the animate() function) // iii) When drawing the scene, use the values in // this data structure (which are set in the // animate() function as described above) to // specify the appropriate transformations. Keyframe* joint_ui_data; // README: To change the range of a particular DOF, // simply change the appropriate min/max values below const float ROOT_TRANSLATE_X_MIN = -5.0; const float ROOT_TRANSLATE_X_MAX = 5.0; const float ROOT_TRANSLATE_Y_MIN = -5.0; const float ROOT_TRANSLATE_Y_MAX = 5.0; const float ROOT_TRANSLATE_Z_MIN = -5.0; const float ROOT_TRANSLATE_Z_MAX = 5.0; const float ROOT_ROTATE_X_MIN = 0.0; const float ROOT_ROTATE_X_MAX = 360.0; const float ROOT_ROTATE_Y_MIN = 0.0; const float ROOT_ROTATE_Y_MAX = 360.0; const float ROOT_ROTATE_Z_MIN = 0.0; const float ROOT_ROTATE_Z_MAX = 360.0; const float TAIL_PITCH_MIN = -60.0; const float TAIL_PITCH_MAX = 60.0; const float TAIL_YAW_MIN = -75.0; const float TAIL_YAW_MAX = 75.0; const float NECK_PITCH_MIN = -60.0; const float NECK_PITCH_MAX = 60.0; const float NECK_YAW_MIN = -75.0; const float NECK_YAW_MAX = 75.0; const float HEAD_PITCH_MIN = -60.0; const float HEAD_PITCH_MAX = 60.0; const float HEAD_YAW_MIN = -75.0; const float HEAD_YAW_MAX = 75.0; const float JAW_MIN = -1.0; const float JAW_MAX = 1.0; const float SHOULDER_PITCH_MIN = -45.0; const float SHOULDER_PITCH_MAX = 45.0; const float SHOULDER_YAW_MIN = -45.0; const float SHOULDER_YAW_MAX = 45.0; const float ELBOW_MIN = 0.0; const float ELBOW_MAX = 75.0; const float HIP_PITCH_MIN = -45.0; const float HIP_PITCH_MAX = 45.0; const float HIP_YAW_MIN = -45.0; const float HIP_YAW_MAX = 45.0; const float KNEE_MIN = 0.0; const float KNEE_MAX = 75.0; // *********** FUNCTION HEADER DECLARATIONS **************** // Initialization functions void initDS(); void initGlut(int argc, char** argv); void initGlui(); void initGl(); // Callbacks for handling events in glut void reshape(int w, int h); void animate(); void display(void); void mouse(int button, int state, int x, int y); void motion(int x, int y); // Functions to help draw the object void drawCube(); void drawExtrudedTri(); void drawPyramid(); // ******************** FUNCTIONS ************************ // main() function // Initializes the user interface (and any user variables) // then hands over control to the event handler, which calls // display() whenever the GL window needs to be redrawn. int main(int argc, char** argv) { // Process program arguments if(argc != 3) { printf("Usage: demo [width] [height]\n"); printf("Using 640x480 window by default...\n"); Win[0] = 640; Win[1] = 480; } else { Win[0] = atoi(argv[1]); Win[1] = atoi(argv[2]); } // Initialize data structs, glut, glui, and opengl initDS(); initGlut(argc, argv); initGlui(); initGl(); // Invoke the standard GLUT main event loop glutMainLoop(); return 0; // never reached } // Create / initialize global data structures void initDS() { keyframes = new Keyframe[KEYFRAME_MAX]; for( int i = 0; i < KEYFRAME_MAX; i++ ) keyframes[i].setID(i); animationTimer = new Timer(); frameRateTimer = new Timer(); joint_ui_data = new Keyframe(); } // Initialize glut and create a window with the specified caption void initGlut(int argc, char** argv) { // Init GLUT glutInit(&argc, argv); // Set video mode: double-buffered, color, depth-buffered glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // Create window glutInitWindowPosition (0, 0); glutInitWindowSize(Win[0],Win[1]); windowID = glutCreateWindow(argv[0]); // Setup callback functions to handle events glutReshapeFunc(reshape); // Call reshape whenever window resized glutDisplayFunc(display); // Call display whenever new frame needed glutMouseFunc(mouse); // Call mouse whenever mouse button pressed glutMotionFunc(motion); // Call motion whenever mouse moves while button pressed } // Load Keyframe button handler. Called when the "load keyframe" button is pressed void loadKeyframeButton(int) { // Get the keyframe ID from the UI int keyframeID = joint_ui_data->getID(); // Update the 'joint_ui_data' variable with the appropriate // entry from the 'keyframes' array (the list of keyframes) *joint_ui_data = keyframes[keyframeID]; // Sync the UI with the 'joint_ui_data' values glui_joints->sync_live(); glui_keyframe->sync_live(); // Let the user know the values have been loaded sprintf(msg, "Status: Keyframe %d loaded successfully", keyframeID); status->set_text(msg); } // Update Keyframe button handler. Called when the "update keyframe" button is pressed void updateKeyframeButton(int) { // Get the keyframe ID from the UI int keyframeID = joint_ui_data->getID(); // pick the maximum frame assigned to so far if (keyframeID > maxValidKeyframe) maxValidKeyframe = keyframeID; // copy the data from the current state keyframes[keyframeID].clone( *joint_ui_data ); // Let the user know the values have been updated sprintf(msg, "Status: Keyframe %d updated successfully", keyframeID); status->set_text(msg); } // Animate button handler. Called when the "animate" button is pressed. void animateButton(int) { // synchronize variables that GLUT uses glui_keyframe->sync_live(); // toggle animation mode and set idle function appropriately if( animate_mode == 0 ) { // start animation frameRateTimer->reset(); animationTimer->reset(); animate_mode = 1; GLUI_Master.set_glutIdleFunc(animate); } else { // stop animation animate_mode = 0; GLUI_Master.set_glutIdleFunc(NULL); } } // Quit button handler. Called when the "quit" button is pressed. void quitButton(int) { exit(0); } // Initialize GLUI and the user interface void initGlui() { GLUI_Panel* glui_panel; GLUI_Spinner* glui_spinner; GLUI_RadioGroup* glui_radio_group; GLUI_Master.set_glutIdleFunc(NULL); // Create GLUI window (joint controls) *************** // glui_joints = GLUI_Master.create_glui("Joint Control", 0, Win[0]+12, 0); // Create controls to specify root position and orientation glui_panel = glui_joints->add_panel("Root"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "translate x:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::ROOT_TRANSLATE_X)); glui_spinner->set_float_limits(ROOT_TRANSLATE_X_MIN, ROOT_TRANSLATE_X_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "translate y:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::ROOT_TRANSLATE_Y)); glui_spinner->set_float_limits(ROOT_TRANSLATE_Y_MIN, ROOT_TRANSLATE_Y_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "translate z:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::ROOT_TRANSLATE_Z)); glui_spinner->set_float_limits(ROOT_TRANSLATE_Z_MIN, ROOT_TRANSLATE_Z_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "rotate x:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::ROOT_ROTATE_X)); glui_spinner->set_float_limits(ROOT_ROTATE_X_MIN, ROOT_ROTATE_X_MAX, GLUI_LIMIT_WRAP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "rotate y:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::ROOT_ROTATE_Y)); glui_spinner->set_float_limits(ROOT_ROTATE_Y_MIN, ROOT_ROTATE_Y_MAX, GLUI_LIMIT_WRAP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "rotate z:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::ROOT_ROTATE_Z)); glui_spinner->set_float_limits(ROOT_ROTATE_Z_MIN, ROOT_ROTATE_Z_MAX, GLUI_LIMIT_WRAP); glui_spinner->set_speed(SPINNER_SPEED); // Create controls to specify tail rotations glui_panel = glui_joints->add_panel("Tail"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "pitch:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::TAIL_PITCH)); glui_spinner->set_float_limits(TAIL_PITCH_MIN, TAIL_PITCH_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "yaw:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::TAIL_YAW)); glui_spinner->set_float_limits(TAIL_YAW_MIN, TAIL_YAW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); // Create controls to specify neck rotations glui_panel = glui_joints->add_panel("Neck"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "pitch:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::NECK_PITCH)); glui_spinner->set_float_limits(NECK_PITCH_MIN, NECK_PITCH_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "yaw:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::NECK_YAW)); glui_spinner->set_float_limits(NECK_YAW_MIN, NECK_YAW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); // Create controls to specify head rotations glui_panel = glui_joints->add_panel("Head"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "pitch:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::HEAD_PITCH)); glui_spinner->set_float_limits(HEAD_PITCH_MIN, HEAD_PITCH_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "yaw:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::HEAD_YAW)); glui_spinner->set_float_limits(HEAD_YAW_MIN, HEAD_YAW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); // Create controls to specify jaw rotations glui_panel = glui_joints->add_panel("Jaw"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "jaw:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::JAW)); glui_spinner->set_float_limits(JAW_MIN, JAW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_joints->add_column(false); // Create controls to specify right forelimb rotations glui_panel = glui_joints->add_panel("Right forelimb"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "shoulder pitch:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::R_SHOULDER_PITCH)); glui_spinner->set_float_limits(SHOULDER_PITCH_MIN, SHOULDER_PITCH_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "shoulder yaw:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::R_SHOULDER_YAW)); glui_spinner->set_float_limits(SHOULDER_YAW_MIN, SHOULDER_YAW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "elbow:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::R_ELBOW)); glui_spinner->set_float_limits(ELBOW_MIN, ELBOW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); // Create controls to specify right hindlimb rotations glui_panel = glui_joints->add_panel("Right hindlimb"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "hip pitch:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::R_HIP_PITCH)); glui_spinner->set_float_limits(HIP_PITCH_MIN, HIP_PITCH_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "hip yaw:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::R_HIP_YAW)); glui_spinner->set_float_limits(HIP_YAW_MIN, HIP_YAW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "knee:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::R_KNEE)); glui_spinner->set_float_limits(KNEE_MIN, KNEE_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); // Create controls to specify left forelimb rotations glui_panel = glui_joints->add_panel("Left forelimb"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "shoulder pitch:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::L_SHOULDER_PITCH)); glui_spinner->set_float_limits(SHOULDER_PITCH_MIN, SHOULDER_PITCH_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "shoulder yaw:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::L_SHOULDER_YAW)); glui_spinner->set_float_limits(SHOULDER_YAW_MIN, SHOULDER_YAW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "elbow:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::L_ELBOW)); glui_spinner->set_float_limits(ELBOW_MIN, ELBOW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); // Create controls to specify left hindlimb rotations glui_panel = glui_joints->add_panel("Left hindlimb"); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "hip pitch:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::L_HIP_PITCH)); glui_spinner->set_float_limits(HIP_PITCH_MIN, HIP_PITCH_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "hip yaw:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::L_HIP_YAW)); glui_spinner->set_float_limits(HIP_YAW_MIN, HIP_YAW_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_spinner = glui_joints->add_spinner_to_panel(glui_panel, "knee:", GLUI_SPINNER_FLOAT, joint_ui_data->getDOFPtr(Keyframe::L_KNEE)); glui_spinner->set_float_limits(KNEE_MIN, KNEE_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); /////////////////////////////////////////////////////////// // TODO (Optional): // This is optional and may only be necessary if you // decide to do a model other than the dog. // Add more UI spinner elements here. Be sure to also // add the appropriate min/max range values to this // file, and to also add the appropriate enums to the // enumeration in the Keyframe class (keyframe.h). /////////////////////////////////////////////////////////// // // *************************************************** // Create GLUI window (keyframe controls) ************ // glui_keyframe = GLUI_Master.create_glui("Keyframe Control", 0, 0, Win[1]+64); // Create a control to specify the time (for setting a keyframe) glui_panel = glui_keyframe->add_panel("", GLUI_PANEL_NONE); glui_spinner = glui_keyframe->add_spinner_to_panel(glui_panel, "Time:", GLUI_SPINNER_FLOAT, joint_ui_data->getTimePtr()); glui_spinner->set_float_limits(TIME_MIN, TIME_MAX, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); // Create a control to specify a keyframe (for updating / loading a keyframe) glui_keyframe->add_column_to_panel(glui_panel, false); glui_spinner = glui_keyframe->add_spinner_to_panel(glui_panel, "Keyframe ID:", GLUI_SPINNER_INT, joint_ui_data->getIDPtr()); glui_spinner->set_int_limits(KEYFRAME_MIN, KEYFRAME_MAX-1, GLUI_LIMIT_CLAMP); glui_spinner->set_speed(SPINNER_SPEED); glui_keyframe->add_separator(); // Add buttons to load and update keyframes glui_panel = glui_keyframe->add_panel("", GLUI_PANEL_NONE); glui_keyframe->add_button_to_panel(glui_panel, "Load Keyframe", 0, loadKeyframeButton); glui_keyframe->add_column_to_panel(glui_panel, false); glui_keyframe->add_button_to_panel(glui_panel, "Update Keyframe", 0, updateKeyframeButton); // Add status line glui_panel = glui_keyframe->add_panel(""); status = glui_keyframe->add_statictext_to_panel(glui_panel, "Status: Ready"); glui_keyframe->add_separator(); // Add buttons to start / stop animation and to quit glui_panel = glui_keyframe->add_panel("", GLUI_PANEL_NONE); glui_keyframe->add_button_to_panel(glui_panel, "Start / Stop Animation", 0, animateButton); glui_keyframe->add_column_to_panel(glui_panel, false); glui_keyframe->add_button_to_panel(glui_panel, "Quit", 0, quitButton); // // *************************************************** // Create GLUI window (render controls) ************ // glui_render = GLUI_Master.create_glui("Render Control", 0, 344, Win[1]+64); // Create control to specify the render style glui_panel = glui_render->add_panel("Render Style"); glui_radio_group = glui_render->add_radiogroup_to_panel(glui_panel, &renderStyle); glui_render->add_radiobutton_to_group(glui_radio_group, "Wireframe"); glui_render->add_radiobutton_to_group(glui_radio_group, "Solid"); glui_render->add_radiobutton_to_group(glui_radio_group, "Solid w/ outlines"); // // *************************************************** // Tell GLUI windows which window is main graphics window glui_joints->set_main_gfx_window(windowID); glui_keyframe->set_main_gfx_window(windowID); glui_render->set_main_gfx_window(windowID); } // Performs most of the OpenGL intialization void initGl(void) { // glClearColor (red, green, blue, alpha) // Ignore the meaning of the 'alpha' value for now glClearColor(0.7f,0.7f,0.9f,1.0f); glEnable(GL_DEPTH_TEST); // glLineWidth(2.0); } // Calculates the interpolated joint DOF vector Vector getInterpolatedJointDOFS(float time) { // position k such that frame[k] <= time <= frame[k+1] // exit if we are past the last keyframe (holding that pose) // perform the catmull-rom interpolation // - find the position between keyframes // - retrieve the position vectors and tangents (checking for first and last // frames as special cases) // - perform the interpolation (going through increasing powers of t) // return the interpolated vector int k = 0; while ( (k < maxValidKeyframe) && (time > keyframes[k+1].getTime()) ) ++k; if ( k >= maxValidKeyframe ) return keyframes[maxValidKeyframe].getDOFVector(); float dt = (time - keyframes[k].getTime()) / (keyframes[k+1].getTime() - keyframes[k].getTime()); Vector a = keyframes[k].getDOFVector(); Vector b = keyframes[k].getDOFVector(); Vector c = keyframes[k+1].getDOFVector(); Vector d = keyframes[k+1].getDOFVector(); if (k > 0) a = keyframes[k-1].getDOFVector(); if ( (k+1) < maxValidKeyframe ) d = keyframes[k+2].getDOFVector(); Vector tk = (c-a) * KAPPA; Vector tk1 = (d-b) * KAPPA; Vector result = b; float t=dt; result += tk * t; t *= dt; result += ( b*(-3) + c*(3) - tk*(2) -tk1 ) * t; t *= dt; result += ( b*(2) + c*(-2) +tk +tk1 ) * t; // Return your Vector here return result; } // Callback idle function for animating the scene void animate() { // Only update if enough time has passed // (This locks the display to a certain frame rate rather // than updating as fast as possible. The effect is that // the animation should run at about the same rate // whether being run on a fast machine or slow machine) if( frameRateTimer->elapsed() > SEC_PER_FRAME ) { // Get the time for the current animation step float curTime = fmod(animationTimer->elapsed(), TIME_MAX); // Get the interpolated joint DOFs /////////////////////////////////////////////////////////// // README: // This statement loads the interpolated joint DOF vector // into the global 'joint_ui_data' variable. Since the // 'display' function will be called AFTER this assignment, // the 'joint_ui_data' data can be used in the 'display' // function to properly construct / display the model. // Note: Nothing should be drawn in this function! OpenGL // drawing should only happen in the display() callback. /////////////////////////////////////////////////////////// joint_ui_data->setDOFVector( getInterpolatedJointDOFS(curTime) ); // Update user interface joint_ui_data->setTime(curTime); glui_keyframe->sync_live(); // Tell glut window to update itself. This will cause the display() // callback to be called, which renders the object (once you've written // the callback). glutSetWindow(windowID); glutPostRedisplay(); // Restart the timer frameRateTimer->reset(); } } // Handles the window being resized by updating the viewport // and projection matrices void reshape(int w, int h) { // Update internal variables and OpenGL viewport Win[0] = w; Win[1] = h; glViewport(0, 0, (GLsizei)Win[0], (GLsizei)Win[1]); // Setup projection matrix for new window glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(CAMERA_FOVY, (GLdouble)Win[0]/(GLdouble)Win[1], NEAR_CLIP, FAR_CLIP); } // draw the dog's head (see generic object commentary in drawTorso) void drawHead(bool colour) { const float HEAD_X = 0.6, HEAD_Y = 0.5, HEAD_Z = 0.5; const float JAW_Y = 0.1; if (colour) glColor3f(0.5,0.5,0.5); // draw three objects: a cube for the back of the head, an extruded triangle // for the front and another (more flattened) cube for the jaw // the range of motion for the jaw should be calculated from the jaw's and // head's sizes glPushMatrix(); glPushMatrix(); glScalef( HEAD_X/2, HEAD_Y, HEAD_Z ); glTranslatef(-1,0,0); drawCube(); glPopMatrix(); glTranslatef(-1.5*HEAD_X,-HEAD_Y/2,0); glPushMatrix(); glScalef( HEAD_X/2, HEAD_Y/2, HEAD_Z ); drawExtrudedTri(); glPopMatrix(); float jawRange = (HEAD_Y/2-HEAD_Y*JAW_Y); glTranslatef(0,HEAD_Y+joint_ui_data->getDOF(Keyframe::JAW)*jawRange,0); glPushMatrix(); glScalef( HEAD_X/2, JAW_Y, HEAD_Z ); drawCube(); glPopMatrix(); glPopMatrix(); } // draw a limb (see generic object commentary in drawTorso) void drawLimb(bool colour, float pitch, float yaw, float elbow) { const float LIMB_X = 0.6, LIMB_Y = 0.2, LIMB_Z = 0.2; if (colour) glColor3f(0.3,0.3,0.3); // draw one cube for the upper joint and another for the lower joint glPushMatrix(); glRotatef(90+pitch, 0,0,1); glRotatef(yaw, 0,1,0); glPushMatrix(); glScalef( LIMB_X, LIMB_Y, LIMB_Z ); glTranslatef(-1,0,0); drawCube(); glPopMatrix(); glTranslatef(-2*LIMB_X,0,0); glRotatef(-elbow, 0,0,1); glScalef( LIMB_X, LIMB_Y, LIMB_Z ); glTranslatef(-1,0,0); if (colour) glColor3f(0.4,0.4,0.4); drawCube(); glPopMatrix(); } // draw the torso // for all objects the following method is used: // - if colour is on, then set a colour for each object // - translate to joint location // - if sliding joint: translate by spinner value // - rotate to standard orientation // - for all degrees of freedom: rotate about the basis vector by the spinner // value // - push a matrix stack // - scale by specified amount // - translate to centre of object // - render the object // - pop the matrix stack // NOTE: in some cases this is simplified where operations are unnecessary or // multiple sub-objects do not need to be drawn void drawTorso(bool colour) { const float BODY_X = 2.0, BODY_Y = 0.7, BODY_Z = 0.7; const float TAIL_X = 0.8, TAIL_Y = 0.2, TAIL_Z = 0.2; const float NECK_X = 0.3, NECK_Y = 0.3, NECK_Z = 0.3; if (colour) glColor3f(0,0,0); // draw the main body using a cube glPushMatrix(); glScalef(BODY_X, BODY_Y, BODY_Z); drawCube(); glPopMatrix(); if (colour) glColor3f(0.1,0.1,0.1); // draw the tail using a pyramid glPushMatrix(); glTranslatef(-BODY_X,BODY_Y,0); glRotatef(joint_ui_data->getDOF(Keyframe::TAIL_PITCH), 0,0,1); glRotatef(joint_ui_data->getDOF(Keyframe::TAIL_YAW), 0,1,0); glScalef(TAIL_X, TAIL_Y, TAIL_Z); glTranslatef(-1,0,0); //drawCube(); drawPyramid(); glPopMatrix(); // draw the neck using a cube, and call drawHead to complete the head if (colour) glColor3f(0.15,0.15,0.15); glPushMatrix(); glTranslatef(BODY_X,BODY_Y,0); glRotatef(-150+joint_ui_data->getDOF(Keyframe::NECK_PITCH), 0,0,1); glRotatef(joint_ui_data->getDOF(Keyframe::NECK_YAW), 0,1,0); glPushMatrix(); glScalef( NECK_X, NECK_Y, NECK_Z ); glTranslatef(-1,0,0); drawCube(); glPopMatrix(); glTranslatef(-2*NECK_X,0,0); glRotatef(joint_ui_data->getDOF(Keyframe::HEAD_PITCH), 0,0,1); glRotatef(joint_ui_data->getDOF(Keyframe::HEAD_YAW), 0,1,0); drawHead(colour); glPopMatrix(); // draw all four limbs using draw limb, they only need to be translated to // the joint locationas drawLimb will rotate through the degrees of freedom glPushMatrix(); glTranslatef(BODY_X*2/3, -BODY_Y, BODY_Z*2/3); drawLimb(colour, joint_ui_data->getDOF(Keyframe::R_SHOULDER_PITCH), joint_ui_data->getDOF(Keyframe::R_SHOULDER_YAW), joint_ui_data->getDOF(Keyframe::R_ELBOW) ); glPopMatrix(); glPushMatrix(); glTranslatef(BODY_X*2/3, -BODY_Y, -BODY_Z*2/3); drawLimb(colour, joint_ui_data->getDOF(Keyframe::L_SHOULDER_PITCH), joint_ui_data->getDOF(Keyframe::L_SHOULDER_YAW), joint_ui_data->getDOF(Keyframe::L_ELBOW) ); glPopMatrix(); glPushMatrix(); glTranslatef(-BODY_X*2/3, -BODY_Y, BODY_Z*2/3); drawLimb(colour, joint_ui_data->getDOF(Keyframe::R_HIP_PITCH), joint_ui_data->getDOF(Keyframe::R_HIP_YAW), joint_ui_data->getDOF(Keyframe::R_KNEE) ); glPopMatrix(); glPushMatrix(); glTranslatef(-BODY_X*2/3, -BODY_Y, -BODY_Z*2/3); drawLimb(colour, joint_ui_data->getDOF(Keyframe::L_HIP_PITCH), joint_ui_data->getDOF(Keyframe::L_HIP_YAW), joint_ui_data->getDOF(Keyframe::L_KNEE) ); glPopMatrix(); } // display callback // // README: This gets called by the event handler // to draw the scene, so this is where you need // to build your scene -- make your changes and // additions here. All rendering happens in this // function. For Assignment 2, updates to the // joint DOFs (joint_ui_data) happen in the // animate() function. void display(void) { // glClearColor (red, green, blue, alpha) // Ignore the meaning of the 'alpha' value for now glClearColor(0.7f,0.7f,0.9f,1.0f); // OK, now clear the screen with the background colour glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Setup the model-view transformation matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Specify camera transformation glTranslatef(camXPos, camYPos, camZPos); /////////////////////////////////////////////////////////// // TODO: // Modify this function to draw the scene. // This should include function calls that apply // the appropriate transformation matrices and render // the individual body parts. // Use the 'joint_ui_data' data structure to obtain // the joint DOFs to specify your transformations. // Sample code is provided below and demonstrates how // to access the joint DOF values. This sample code // should be replaced with your own. // Use the 'renderStyle' variable and the associated // enumeration to determine how the geometry should be // rendered. /////////////////////////////////////////////////////////// // SAMPLE CODE ********** // glPushMatrix(); // perform the global translations and rotations glTranslatef(joint_ui_data->getDOF(Keyframe::ROOT_TRANSLATE_X), joint_ui_data->getDOF(Keyframe::ROOT_TRANSLATE_Y), joint_ui_data->getDOF(Keyframe::ROOT_TRANSLATE_Z)); glRotatef(10.0 + joint_ui_data->getDOF(Keyframe::ROOT_ROTATE_X), 1.0, 0.0, 0.0); glRotatef(0.0 + joint_ui_data->getDOF(Keyframe::ROOT_ROTATE_Y), 0.0, 1.0, 0.0); glRotatef(10.0 + joint_ui_data->getDOF(Keyframe::ROOT_ROTATE_Z), 0.0, 0.0, 1.0); // determine render style and set glPolygonMode appropriately if (renderStyle == WIREFRAME) { // wireframe should use GL_LINE and draw all faces, a single colour is // used glPolygonMode(GL_FRONT, GL_LINE); glPolygonMode(GL_BACK, GL_LINE); glColor3f(1.0, 1.0, 1.0); drawTorso(false); } else if (renderStyle == SOLID) { // solid should only draw front faces, objects should be coloured glPolygonMode(GL_FRONT, GL_FILL); drawTorso(true); } else { // for outline, render as solid, then offset the lines to appear in front // of the faces and perform wireframe rendering glPolygonMode(GL_FRONT, GL_FILL); drawTorso(true); glPolygonMode(GL_FRONT, GL_LINE); glEnable(GL_POLYGON_OFFSET_LINE); glPolygonOffset(-0.01, 0.0); glColor3f(1.0, 1.0, 1.0); drawTorso(false); glDisable(GL_POLYGON_OFFSET_LINE); } // draw body part //drawCube(); glPopMatrix(); // // SAMPLE CODE ********** // Execute any GL functions that are in the queue just to be safe glFlush(); // Now, show the frame buffer that we just drew into. // (this prevents flickering). glutSwapBuffers(); } // Handles mouse button pressed / released events void mouse(int button, int state, int x, int y) { // If the RMB is pressed and dragged then zoom in / out if( button == GLUT_RIGHT_BUTTON ) { if( state == GLUT_DOWN ) { lastX = x; lastY = y; updateCamZPos = true; } else { updateCamZPos = false; } } } // Handles mouse motion events while a button is pressed void motion(int x, int y) { // If the RMB is pressed and dragged then zoom in / out if( updateCamZPos ) { // Update camera z position camZPos += (x - lastX) * ZOOM_SCALE; lastX = x; // Redraw the scene from updated camera position glutSetWindow(windowID); glutPostRedisplay(); } } // draw an extruded triangle at the current location void drawExtrudedTri() { // draw the front and back triangles glBegin(GL_TRIANGLES); glVertex3f(-1, 1, 1); glVertex3f( 1,-1, 1); glVertex3f( 1, 1, 1); glVertex3f( 1,-1,-1); glVertex3f(-1, 1,-1); glVertex3f( 1, 1,-1); glEnd(); // connect the edges using quads glBegin(GL_QUADS); glVertex3f( 1, 1, 1); glVertex3f( 1, 1,-1); glVertex3f(-1, 1,-1); glVertex3f(-1, 1, 1); glVertex3f(-1, 1, 1); glVertex3f(-1, 1,-1); glVertex3f( 1,-1,-1); glVertex3f( 1,-1, 1); glVertex3f( 1, 1, 1); glVertex3f( 1, 1,-1); glVertex3f( 1,-1,-1); glVertex3f( 1,-1, 1); glEnd(); } // draw a square based pyramid at the current location void drawPyramid() { // draw the base of the pyramid glBegin(GL_QUADS); glVertex3f( 1.0, -1.0, 1.0); glVertex3f( 1.0, -1.0, -1.0); glVertex3f( 1.0, 1.0, -1.0); glVertex3f( 1.0, 1.0, 1.0); glEnd(); // draw each face glBegin(GL_TRIANGLES); glVertex3f( 1, 1, 1); glVertex3f(-1, 0, 0); glVertex3f( 1,-1, 1); glVertex3f( 1,-1, 1); glVertex3f(-1, 0, 0); glVertex3f( 1,-1,-1); glVertex3f( 1,-1,-1); glVertex3f(-1, 0, 0); glVertex3f( 1, 1,-1); glVertex3f( 1, 1,-1); glVertex3f(-1, 0, 0); glVertex3f( 1, 1, 1); glEnd(); } // Draw a unit cube, centered at the current location // README: Helper code for drawing a cube void drawCube() { glBegin(GL_QUADS); // draw front face glVertex3f(-1.0, -1.0, 1.0); glVertex3f( 1.0, -1.0, 1.0); glVertex3f( 1.0, 1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); // draw back face glVertex3f( 1.0, -1.0, -1.0); glVertex3f(-1.0, -1.0, -1.0); glVertex3f(-1.0, 1.0, -1.0); glVertex3f( 1.0, 1.0, -1.0); // draw left face glVertex3f(-1.0, -1.0, -1.0); glVertex3f(-1.0, -1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); // draw right face glVertex3f( 1.0, -1.0, 1.0); glVertex3f( 1.0, -1.0, -1.0); glVertex3f( 1.0, 1.0, -1.0); glVertex3f( 1.0, 1.0, 1.0); // draw top glVertex3f(-1.0, 1.0, 1.0); glVertex3f( 1.0, 1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0); glVertex3f(-1.0, 1.0, -1.0); // draw bottom glVertex3f(-1.0, -1.0, -1.0); glVertex3f( 1.0, -1.0, -1.0); glVertex3f( 1.0, -1.0, 1.0); glVertex3f(-1.0, -1.0, 1.0); glEnd(); }