// copyright Min Zhong, cs838 proj2, April, 2000 #include "viewer.h" #include "RunButton.h" #include "RotConverter.h" #include "MotionCapturer.h" #include "vector.h" GLfloat mat_diffuse_ambient[4] = { 1, 1, 1, 1 }; GLfloat mat_specular [] = { 1,1,1,1 } ; Gl_Win::Gl_Win(int x, int y, int in_w, int in_h) : Fl_Gl_Window(x,y,in_w,in_h,"My GL Window") { w = in_w; h = in_h; b_readSuceed = false; // haven't read anything yet. cameraView = OMIN; // pick a random starting place for the camera (will be changed after motion loaded) eye[X] = 70; eye[Y] = 30; eye[Z] = 70; look[X] = 0; look[Y] = 40; look[Z] = 0; follow_theta = 30; follow_phi = 20; follow_lag = 100; fieldOfView = 45; light = true; light0_position[X] = 0; light0_position[Y] = 100; light0_position[Z] = 0; light0_position[3] = 0; // directional light b_floor = true; timeS = NULL; runBut = NULL; play_mode = CONST_SPD; endsites_draw = NULL; endsite_browser = NULL; endsite_cnt = 0; frame_cnt = 100; // make up some numbers, changed when a file read in. frame_time = 0.033333; begin = 0; end = frame_cnt - 1; key_cnt = 0; keyTimes = NULL; cap = NULL; // no cap data read in yet root = NULL; trans = NULL; euler = NULL; expmap = NULL; quatern = NULL; rot_cnt = 0; // no interp inititially b_need_reinterp = false; e_interpType = NONE; q_interpType = NONE; m_interpType = HIDE; marker_head = NULL; marker_endsite = NULL; m_rotType = EULER; // no markers showing qobj = gluNewQuadric(); // for drawing } Gl_Win::~Gl_Win() { gluDeleteQuadric(qobj); if (cap != NULL) delete(cap); if (timeS != NULL) delete(timeS); if (runBut != NULL) delete(runBut); if (endsite_browser != NULL) delete(endsite_browser); if (marker_head != NULL) del_markers(marker_head); if (marker_endsite != NULL) del_markers(marker_endsite); if (endsites_draw != NULL) free(endsites_draw); timeS = NULL; runBut = NULL; } // if cap not null, fill in appropriate data fields in Gl_Win void Gl_Win::init_with_cap() { if (cap != NULL) { root = cap->root; trans = cap->trans; euler = cap->euler; quatern = cap->quatern; expmap = cap->expmap; rot_cnt = cap->rot_cnt; frame_cnt = cap->frame_cnt; frame_time = cap->frame_time; fieldOfView = 60; // adjust camera for current view type for this file camera_omin(frame_cnt, cap->trans, look, eye); light_pos(frame_cnt, cap->trans, light0_position); // init user turning knobs key_cnt = 0; begin = 0; end = frame_cnt - 1; // adjust slider and runBut timing timeS->minimum(begin); timeS->maximum(end); timeS->range(begin, end); runBut->init_ticks(frame_time, play_mode); e_interpType = NONE; q_interpType = NONE; m_interpType = HIDE; b_need_reinterp = true; // set up keyTimes arr if (keyTimes != NULL) delete(keyTimes); keyTimes = (int*)malloc(frame_cnt * sizeof(int)); memset(keyTimes, 0, frame_cnt * sizeof(int)); // rid of old markers if (marker_head != NULL) del_markers(marker_head); if (marker_endsite != NULL) del_markers(marker_endsite); if (endsites_draw != NULL) free(endsites_draw); endsites_draw = NULL; endsite_cnt = 0; endsite_browser->clear(); // clear out old endsites m_rotType = EULER; // recursively find all end sites and make a marker list for it marker_endsite = NULL; create_endsite_marker_list(root, root, frame_cnt, marker_endsite, endsite_cnt); init_marker_draw(m_rotType, root, marker_endsite, endsite_browser, endsites_draw, endsite_cnt); } } /* draws a frame according to interpType. ref_rptr: pt to the frame of ref rot array. intrp_rptr: pt to the frame of interp rot array */ void Gl_Win::draw_frame_of_interpType(RotType rotType, InterpType interpType, double *t_ptr, double *ref_rptr, double *intrp_rptr) { double *r_ptr; r_ptr = ref_rptr; glPushMatrix(); if (interpType != NONE) { // if not enough keys no interp, need >=2 for lin, >=4 for cubic if ( key_cnt < 2 || (key_cnt < 4 && interpType == CARDINAL) || (key_cnt < 4 && interpType == Q_BERZIER) ) { fprintf(stderr, "%cNot enough keys to interpolate%c.\n", SOUND_BELL, SOUND_BELL); // bell } else { if (b_need_reinterp) { // re calc interp if necessary interp(interpType); if (m_rotType == rotType) { mdebug(DEBUG_INTP, "m_rottype = %d, rottype=%d\n", m_rotType, rotType); if (marker_head != NULL) calc_markers_pos(marker_head, m_rotType); if (marker_endsite != NULL) calc_markers_pos(marker_endsite, m_rotType); } } r_ptr = intrp_rptr; // pt to interp data } } draw_a_frame(root, rotType, t_ptr, r_ptr, true); // true=> start drawing from root glPopMatrix(); } void Gl_Win::draw() { // the draw method must be private int cur_time; // i.e. which frame we are on // clear the window, be sure to clear the Z-Buffer too glClearColor(0,0,.3f,0); // background should be blue glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glEnable( GL_DEPTH_TEST ); // compute the aspect ratio so we don't distort things double aspect = ((double) w) / ((double) h); gluPerspective(fieldOfView, aspect, 1, 1000); // set up the drawing transforms glViewport(0,0,w,h); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (root == NULL || timeS == NULL) { gluLookAt(eye[X], eye[Y], eye[Z], look[X], look[Y], look[Z], 0,1,0); drawFloor(); return; // no file loaded yet } cur_time = (int) ( timeS->value() ) % frame_cnt; // set up camera if (cameraView == FOLLOW) camera_follow(cur_time, look, eye); gluLookAt(eye[X], eye[Y], eye[Z], look[X], look[Y], look[Z], 0,1,0); if (b_floor) drawFloor(); // set light pos light_pos(frame_cnt, trans, light0_position); // apply shadow if there is a floor to cast on if (b_floor) { glShadeModel( GL_SMOOTH ) ; glEnable( GL_DEPTH_TEST ); glDisable(GL_LIGHTING); glPushMatrix(); shadowMatrix( light0_position ); glColor4f(0, 0, 0, 0); drawObjs(true, false, cur_time); glPopMatrix(); } if (light) { glEnable(GL_LIGHTING); glLightfv( GL_LIGHT0, GL_POSITION, light0_position ); // light from bulb glEnable( GL_LIGHT0 ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular ); glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS , 100.0); set_light1(eye, look); } else glDisable(GL_LIGHTING); glPushMatrix(); draw_light_bulb(light, light0_position); // bulb does not cast shadow drawObjs(false, light, cur_time); glPopMatrix(); } // real drawing block. if !shadow then draw in color void Gl_Win::drawObjs(bool shadows, bool light, int cur_time) { int frame_r_off; // frame offset into the rot array int frame_t_off; // frame offset into the trans array double *t_ptr ; double *r_ptr_ref; // ptrs to ref rot array double *r_ptr_intp; // ptrs to interpreted rot array bool b_may_draw_markers; // draw markers only if fig showing in that rotType int begin_frame; // for marker drawing begin_frame = (cur_time - 20) > begin ? cur_time - 20 : begin; // draw last 20 frames b_may_draw_markers = false; frame_t_off = 3 * cur_time; // frame offset into the trans array t_ptr = trans + frame_t_off; if (e_interpType != HIDE) { // draw fig using EULER rot in red if (!shadows) { if (!light) glColor3d(1,0,0); else { mat_diffuse_ambient[R] = 1; mat_diffuse_ambient[G] = 0; mat_diffuse_ambient[B] = 0; glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diffuse_ambient); } } // ptr to euler array frame_r_off = 3 * rot_cnt * cur_time; r_ptr_ref = cap->euler + frame_r_off; r_ptr_intp = cap->einterp + frame_r_off; // pt to interp data draw_frame_of_interpType(EULER, e_interpType, t_ptr, r_ptr_ref, r_ptr_intp); if (m_rotType == EULER) { b_may_draw_markers = true; draw_sel_markers(endsites_draw, endsite_cnt, begin_frame, cur_time); } } if (q_interpType != HIDE) { // draw fig using QUAT rot in blue if (!shadows) { if (!light) glColor3d(0,1,0); else { mat_diffuse_ambient[R] = 0; mat_diffuse_ambient[G] = 1; mat_diffuse_ambient[B] = 0; glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diffuse_ambient); } } // ptr to quatern array frame_r_off = 4 * rot_cnt * cur_time; r_ptr_ref = cap->quatern + frame_r_off; r_ptr_intp = cap->qinterp + frame_r_off; draw_frame_of_interpType(QUAT, q_interpType, t_ptr, r_ptr_ref, r_ptr_intp); if (m_rotType == QUAT) { b_may_draw_markers = true; draw_sel_markers(endsites_draw, endsite_cnt, begin_frame, cur_time); } } if (m_interpType != HIDE) { // draw fig using EXPMAP rot in blue if (!shadows) { if (!light) glColor3d(0,0,1); else { mat_diffuse_ambient[R] = 0; mat_diffuse_ambient[G] = 0; mat_diffuse_ambient[B] = 1; glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diffuse_ambient); } } // ptr to quatern array frame_r_off = 3 * rot_cnt * cur_time; r_ptr_ref = cap->expmap + frame_r_off; r_ptr_intp = cap->minterp + frame_r_off; draw_frame_of_interpType(EXPMAP, m_interpType, t_ptr, r_ptr_ref, r_ptr_intp); if (m_rotType == EXPMAP) { b_may_draw_markers = true; draw_sel_markers(endsites_draw, endsite_cnt, begin_frame, cur_time); } } if (marker_head != NULL && b_may_draw_markers == true) { // draw yellow markers if (!shadows) { if (!light) glColor3f(1, 1, 0); else { mat_diffuse_ambient[R] = 1; mat_diffuse_ambient[G] = 1; mat_diffuse_ambient[B] = 0; glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diffuse_ambient); } } draw_markers(m_rotType, marker_head, begin_frame, cur_time); } b_need_reinterp = false; // reset after done necessary reinterp } // draw the floor as a checkerboard // define two colors float floor1[3] = { .7f,.7f,.7f }; float floor2[3] = { .3f, .3f,.3f }; void Gl_Win::drawFloor() { // parameters: int xSquares = 10; int ySquares = 10; float maxX = MAX_X, maxY = MAX_Y; float minX = MIN_X, minY = MIN_Y; glDisable(GL_LIGHTING); // make the colors absolute glEnable(GL_DEPTH_TEST); // we actually need this turned on // or else it isn't opaque int x,y,v[3],i; float xp,yp,xd,yd; v[2] = 0; xd = (maxX - minX) / ((float) xSquares); yd = (maxY - minY) / ((float) ySquares); glBegin(GL_QUADS); for(x=0,xp=minX; xq_off or e_off depending on rotType Node *child; double transX, transY, transZ, rotatX, rotatY, rotatZ; double len, half_len; double voff[3], unit_voff[3], v_rot[3]; double unit_z[]= {0, 0, 1}; double a; if (root == NULL || root->type == END_SITE) return; if (isRoot) { transX = trans[X]; transY = trans[Y]; transZ = trans[Z]; } else { transX = root->offset[X]; transY = root->offset[Y]; transZ = root->offset[Z]; } glTranslated(transX, transY, transZ); if (rotType == EULER) { // rot = (Z,X,Y) r_off = root->e_off; rotatZ = rot[r_off]; rotatX = rot[r_off + 1]; rotatY = rot[r_off + 2]; glRotated(rotatZ, 0, 0, 1); glRotated(rotatX, 1, 0, 0); glRotated(rotatY, 0, 1, 0); // mdebug(DEBUG_Q, "%s e=<%2.2lf, %2.2lf, %2.2lf> \n", // rotatZ, rotatX, rotatY); } else { double aDeg, x, y, z; if (rotType == QUAT) { r_off = root->q_off; quaternToRot(rot + r_off, aDeg, x, y, z); } else {// rotType = EXPMAP r_off = root->e_off; emapToRot(rot+r_off, aDeg, x, y, z); } if (aDeg != 0) glRotated(aDeg, x, y, z); } for (i=0; ichildren_cnt; i++) { child = root->children[i]; if (child != NULL ) { voff[X] = child->offset[X]; voff[Y] = child->offset[Y]; voff[Z] = child->offset[Z]; v3_normalize(voff, unit_voff); len = sqrt(voff[X]*voff[X] + voff[Y]*voff[Y] + voff[Z]*voff[Z]); half_len = len / 2.0f; v3_crossp(unit_z, unit_voff, v_rot); v3_dotp(v_rot, unit_z, a); // dot prod to get the angle glPushMatrix(); glRotated(a * RadianToDegree, v_rot[X], v_rot[Y], v_rot[Z]); /* glRotated( -atan(x/z) * RadianToDegree, 0,1,0); //glRotated( acos(x/xz) * RadianToDegree, 0,1,0); glRotated( asin(y/xyz) * RadianToDegree , 0, 0, 1); glRotated(90, 0, 1, 0); */ gluQuadricDrawStyle(qobj, GLU_FILL); gluQuadricNormals(qobj, GLU_SMOOTH); if (child->type == END_SITE) { // make it more rounded if at ends gluCylinder(qobj, 0.2*len, 0.2*len, len, 5, 3); } else gluCylinder(qobj, 0.1*len, 0.07*len, len, 5, 1); glPopMatrix(); glBegin(GL_LINES); glVertex3d(0, 0, 0); glVertex3d(child->offset[X], child->offset[Y], child->offset[Z]); glEnd(); /* if (isRoot) { // spits out root coords of each frame mdebug(DEBUG_UI, "%s to %s trans (%2.2lf, %2.2lf, %2.2lf), len (%2.2lf, %2.2lf, %2.2lf) \n", root->name, child->name, transX, transY, transZ, child->offset[X], child->offset[Y], child->offset[Z]); } */ } } glPushMatrix(); // recurse to draw children for (i=0; ichildren_cnt; i++) { child = root->children[i]; if (child != NULL && child->type != END_SITE) { draw_a_frame(child, rotType, trans, rot, false); // isRoot=false, drawing children. glPopMatrix(); // reload parent's matrix glPushMatrix(); } } glPopMatrix(); // clean up } // handle whatever events come our way // // the mouse behavior is: // left mouse = zoom in/out // right mouse = rotate eyept. Basically, it orbits around the center // (if we're looking in 3D) // right mouse static double eyeStartX, eyeStartY, eyeStartZ; static double lookStartX, lookStartY, lookStartZ; // we'll remember where the mouse goes down for later int mouseDownX, mouseDownY; int Gl_Win::handle(int e) { int mouseDragXdist, mouseDragYdist; switch(e) { case FL_SHOW: // you must handle this, or not be seen! show(); return 1; case FL_PUSH: mouseDownX = Fl::event_x(); // remeber where the mouse went down mouseDownY = Fl::event_y(); switch(Fl::event_button()) { case 1: lookStartX = look[X]; lookStartY = look[Y]; lookStartZ = look[Z]; return 1; case 3: eyeStartX = eye[X]; eyeStartY = eye[Y]; eyeStartZ = eye[Z]; return 1; } case FL_DRAG: // if the user drags the mouse mouseDragXdist = Fl::event_x() - mouseDownX; mouseDragYdist = Fl::event_y() - mouseDownY; if ( Fl::event_key(FL_Control_L) || Fl::event_key(FL_Control_R) ) { if (cameraView == FOLLOW) // zoom in follow view adjust_follow_lag(mouseDragXdist, follow_lag); else if (cameraView == OMIN) zoom(mouseDragXdist, fieldOfView); damage(1); return 1; } switch (Fl::event_button() ) { case 1: // zoom if ( cameraView == FOLLOW ) // adj horiz angle adjust_follow_theta(mouseDragXdist, follow_theta); else if (cameraView == OMIN) orbit(mouseDragXdist, mouseDragYdist, look[X], look[Y], look[Z], eyeStartX, eyeStartY, eyeStartZ, eye[X], eye[Y], eye[Z]); damage(1); return 1; case 2: case 3: if (cameraView == FOLLOW) { adjust_follow_phi(mouseDragYdist, follow_phi); damage(1); return 1; } } //drag } // switch return 1; } /* reads in a motion cap data file. fills a motioncap obj, del old cap if exists. if suceed, set b_readSuceed and init fields in gl_win. changes the framing fl_double win's title to include the data file name */ void Gl_Win::read_input_file (Panel* wind) { Status status; char *filename = NULL; FILE *fin; char title[LINESIZE]; filename = fl_file_chooser("Enter bvh file name to load ", "*.bvh", NULL); if (filename == NULL) { fl_alert("invalid file name! File not loaded."); b_readSuceed = false; return; } if ( (fin = fopen(filename, "r")) == NULL) { fl_alert("Error opening file! File not loaded."); b_readSuceed = false; return; } // clean up if there was a capture showing if (cap != NULL) { delete(cap); cap = NULL; } cap = new MotionCapturer(); status = read_bvh_file(filename, cap); // read and parse file if (status == SUCCESS) { // change fl_double win's title sprintf(title, "GL Motion Capture: %s\n", filename); wind->label(title); b_readSuceed = true; init_with_cap(); // init necessary fields in gl_win using cap init_int_input_button(wind->beginB, FL_ALIGN_TOP, begin); init_int_input_button(wind->endB, FL_ALIGN_TOP, end); wind->damage(1); } else { b_readSuceed = false; delete(cap); } } /* if this frame not in keyTimes array already, add it in sorted increasing order, inc key_cnt */ Status Gl_Win::key_add(int frameIdx) { int i, j; if (key_cnt == frame_cnt) return FAILURE; for (i=0; i frameIdx) break; // found the right spot } for (j = key_cnt; j > i; j--) keyTimes[j] = keyTimes[j-1]; // shift bigger keys one back keyTimes[i] = frameIdx; key_cnt++; b_need_reinterp = true; return SUCCESS; } /* if nth is a valid num, store evyer nth frame in keyTimes + always key f(0) and f(frame_cnt-1) */ Status Gl_Win::key_nth( int nth) { int i; if (keyTimes == NULL || nth >= frame_cnt || nth < 1 ) return FAILURE; if (nth == 1) { for (i=0; i < frame_cnt; i++) keyTimes[i] = i; key_cnt = frame_cnt; } else { for (i=0; i frameIdx) return FAILURE; // not in array } if (i == key_cnt) return FAILURE; // not in array for (; i < key_cnt-1; i++) { keyTimes[i] = keyTimes[i+1]; } key_cnt-- ; b_need_reinterp = true; return SUCCESS; } Panel::~Panel() { delete(gl); delete(in_motion_fileB); delete(key_addB); delete(key_nthB); delete(beginB); delete(endB); }