#include "viewer.h" #include "Utils.h" #include "RunButton.h" #include "RotConverter.h" #include "MotionCapturer.h" #include "vector.h" #include "IK.h" GLfloat mat_diffuse_ambient[4] = { 1, 1, 1, 1 }; GLfloat mat_specular [] = { 1,1,1,1 } ; extern int intersect_plane( double plane[4], // ax+by+cz+d=0 int winx, int winy, double pt[3] ) ; extern Status read_bvh_file(FILE *file, MotionCapturer *motionCapturer, FileType type); 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; cameraView = OMIN; // pick a random starting place for the camera (will be changed after motion loaded) v3_assign(eye, 100, 70, 100); v3_assign(look, 0, 15, 0); follow_theta = 30; follow_phi = 20; follow_lag = 100; fieldOfView = 45; light = true; light_pos[X] = 15; light_pos[Y] = 100; light_pos[Z] = 15; light_pos[W] = 1; b_floor = true; in_bvh_name[0] = 0; // null the name to be safe timeS = NULL; runBut = NULL; play_mode = CONST_SPD; endsites_draw = NULL; endsite_browser = NULL; endsite_cnt = 0; cur_time = 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; cur_tptr = NULL; cur_qptr = NULL; rot_cnt = 0; // no interp inititially b_need_reinterp = false; e_interpType = HIDE; q_interpType = NONE; m_interpType = HIDE; marker_head = NULL; marker_endsite = NULL; m_rotType = EULER; // no markers showing qobj = gluNewQuadric(); // for drawing // ik stuff b_ik = false; selNode = NULL; pinNode = NULL; gb_goal[0] = gb_goal[1] = gb_goal[2] = 0; gb_goal[3] = 1; ik_spring_fac = 0; ik_damp = 0.5; ik_sens = 0.025; } 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); } // 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; cur_tptr = cap->trans; cur_qptr = cap->quatern; // no interp init rot_cnt = cap->rot_cnt; frame_cnt = cap->frame_cnt; frame_time = cap->frame_time; fieldOfView = 45; // init user turning knobs key_cnt = 0; begin = 0; end = frame_cnt - 1; cur_time = (int) ( timeS->value() ) % frame_cnt; // adjust slider and runBut timing timeS->minimum(begin); timeS->maximum(end); timeS->range(begin, end); runBut->init_ticks(frame_time, play_mode); e_interpType = HIDE; 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 = QUAT; // 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); // ik stuff b_ik = false; selNode = NULL; pinNode = cap->endsites[0]; // make left foot pinned for now make_skeleton(cap->root); init_flex(cap); } } /* 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); // 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, 0, 0); // background should be black 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 (cap == 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 } // 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(); // apply shadow if there is a floor to cast on if (b_floor) { glShadeModel( GL_SMOOTH ) ; glDisable( GL_DEPTH_TEST ); glDisable(GL_LIGHTING); glPushMatrix(); shadowMatrix( light_pos ); glColor4f(0, 0, 0, 0); drawObjs(true, false, cur_time); glPopMatrix(); } if (light) set_light(light_pos); else glDisable(GL_LIGHTING); glPushMatrix(); draw_light_bulb(light, light_pos); drawObjs(false, light, cur_time); glPopMatrix(); 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.6,0.6); else { mat_diffuse_ambient[R] = 1; mat_diffuse_ambient[G] = 0.6f; mat_diffuse_ambient[B] = 0.6f; 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 if (runBut->value() == 0 ) { // when not in play mode, draw IK target if (shadows) glColor3d(0.2, 0.1, 0); // funky yellow shade else { if (!light) glColor3d(1, 0.2, 1); else { mat_diffuse_ambient[R] = 1; mat_diffuse_ambient[G] = 0.2f; mat_diffuse_ambient[B] = 1; glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_diffuse_ambient); } } glPushMatrix(); glTranslated(gb_goal[X], gb_goal[Y], gb_goal[Z]); gluSphere(qobj, 3, 5, 5); // highlight the sel node //printf("draw gb goal = %2.2lf, %2.2lf, %2.2lf, sel=%s\n", // gb_goal[X], gb_goal[Y], gb_goal[Z], selNode->name); glPopMatrix(); } } // draw the floor as a checkerboard // define two colors float floor1[3] = { .7f, .7f, 0.8f }; float floor2[3] = { .2f, .1f, .3f }; void Gl_Win::drawFloor() { // parameters: int xSquares = MAX_X / 10; int ySquares = MAX_Y / 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 rotatX, rotatY, rotatZ; double len; double voff[3], v_rot[3]; double unit_z[]= {0, 0, 1}; double a; if (root == NULL || root->type == END_SITE) return; if (root->type == ROOT) glTranslated(trans[X], trans[Y], trans[Z]); else glTranslated(root->offset[X], root->offset[Y], root->offset[Z]); 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); } 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 ) { glLoadName(child->pickname); // for 3d picking v3_assign(voff, child->offset); len = v3_magnitude(voff); v3_crossp(unit_z, voff, v_rot); a = acos( v3_cos_bw2v(voff, unit_z) ); // dot prod to get the angle glPushMatrix(); { // draw bone if (!(voff[X]==0 && voff[Y]==0)) // if not already aligned to z, aligh it glRotated(a * RadianToDegree, v_rot[X], v_rot[Y], v_rot[Z]); 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(); glPushMatrix(); draw_a_frame(child, rotType, trans, rot); // recurse drawing children. glPopMatrix(); // reload parent's matrix } } } // 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; GLint viewport[4]; double plane[4]; GLdouble mvmatrix[16], projmatrix[16]; int mx, my; int Gl_Win::handle(int e) { static double pushWorldCoord[3]; // mouse was pushed at these coordinates 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(); mx = mouseDownX; my = mouseDownY; lookStartX = look[X]; lookStartY = look[Y]; lookStartZ = look[Z]; eyeStartX = eye[X]; eyeStartY = eye[Y]; eyeStartZ = eye[Z]; selNode = clickInNode(mouseDownX, mouseDownY); if (selNode != NULL) { node_lc_to_gbCoord(selNode, selNode->gb_curPos); v3_assign(gb_goal, selNode->gb_curPos); printf("%s selected, <%2.2lf, %2.2lf, %2.2lf>\n", selNode->name, gb_goal[X], gb_goal[Y], gb_goal[Z]); } v3_sub(eye, look, plane); plane[W] = - (v3_dotp(plane, gb_goal)); if ( intersect_plane(plane, mouseDownX, mouseDownY, pushWorldCoord ) != 0 ) { printf( "Error: intersection isn't valid - coplanar or parallel" ); selNode = NULL; } damage(1); return 1; case FL_DRAG: // if the user drags the mouse int x = Fl::event_x(); int y = Fl::event_y(); mouseDragXdist = x - mouseDownX; mouseDragYdist = y - mouseDownY; // zoom if ( Fl::event_key(FL_Shift_L) || Fl::event_key(FL_Shift_R) ) { if (cameraView == FOLLOW) adjust_follow_lag(mouseDragXdist, follow_lag); else if (cameraView == OMIN) zoom(mouseDragXdist, fieldOfView); damage(1); return 1; } if (Fl::event_button() == 2 || Fl::event_button() == 3) { if ( cameraView == FOLLOW ) // adj horiz angle adjust_follow_theta(mouseDragXdist, follow_theta); else if (cameraView == OMIN) // adj horiz and vert angle orbit(mouseDragXdist, mouseDragYdist, look[X], look[Y], look[Z], eyeStartX, eyeStartY, eyeStartZ, eye[X], eye[Y], eye[Z]); damage(1); return 1; } // adj vert angl in follow if (Fl::event_key(FL_Alt_L) || Fl::event_key(FL_Alt_R) ) { if (cameraView == FOLLOW) { adjust_follow_phi(mouseDragYdist, follow_phi); damage(1); } return 1; } #ifdef easy // do ik if (selNode != NULL) { Point4D win_coord = {0, 0, 0, 1}; double temp_y, temp_z; glGetIntegerv(GL_VIEWPORT, viewport); glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix); glGetDoublev(GL_PROJECTION_MATRIX, projmatrix); // take cur goal's 3d coord as reference gluProject(gb_goal[X], gb_goal[Y], gb_goal[Z], mvmatrix, projmatrix, viewport, &win_coord[X], &win_coord[Y], &win_coord[Z]); gluUnProject(Fl::event_x(), viewport[3]-Fl::event_y(), win_coord[Z], mvmatrix, projmatrix, viewport, &(gb_goal[X]), &temp_y, &temp_z); if (Fl::event_button() == 1) { //LMB horiz mvmt gb_goal[Z] = temp_z; // change z, leave y } else if (Fl::event_key(FL_Control_L) || Fl::event_key(FL_Control_R) ){// ctrl+LMB vertical mvmt gb_goal[Y] = temp_y; // change y, leave z } #endif //#else //#ifdef plane if (selNode != NULL) { double diff[3]; if ( intersect_plane( plane, x, y, diff ) == 0 ) { v3_sub(diff, pushWorldCoord, diff); v3_add(diff, gb_goal, gb_goal); do_ik(gb_goal); v3_assign(pushWorldCoord, gb_goal); // update push pos } } //#endif damage(1); } // switch e return 1; } /* reads in a motion cap data file. fills a motioncap obj, del old cap if exists. if suceed, init fields in gl_win, and changes the framing Panel's related UI fields */ Status Gl_Win::read_input_file (char *filename, Panel* wind, FileType type) { Status status; char title[LINESIZE]; char s_val[10]; FILE *fin = NULL; if (filename == NULL) { fl_alert("invalid file name! File not loaded."); return FAILURE; } else if ( (fin = fopen(filename, "r")) == NULL) { fl_alert("Error opening file! File not loaded."); return FAILURE; } MotionCapturer *tmp_cap = new MotionCapturer(); status = read_bvh_file(fin, tmp_cap, type); // read and parse file if (status != SUCCESS) { delete(tmp_cap); return FAILURE; } // upon successful read: clean up if there was a capture showing if (cap != NULL) delete(cap); cap = tmp_cap; strcpy(in_bvh_name, filename); init_with_cap(); // init necessary fields in gl_win using cap // change fl_double win upon success read sprintf(title, "GL Motion Capture: %s\n", filename); wind->label(title); init_int_input_button(wind->beginB, FL_ALIGN_TOP, begin); init_int_input_button(wind->endB, FL_ALIGN_TOP, end); _itoa(0, s_val, 10); wind->key_nthB->value(s_val); if (type == BVH) wind->euler_browser->show(1); else if (type == QBVH) wind->euler_browser->hide(); wind->euler_browser->value(HIDE); wind->quat_browser->value(NONE); wind->emap_browser->value(HIDE); return SUCCESS; } /* 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; } void Panel::del_all() { delete(gl); delete(key_addB); delete(key_nthB); delete(key_rmvB); delete(key_printB); delete(key_clearB); delete(cameraB); delete(play_modeB); delete(beginB); delete(endB); delete(lightB); delete(floorB); delete(euler_browser); delete(quat_browser); delete(emap_browser); delete(marker_type_browser); } Panel::~Panel() { del_all(); }