#include <igl/Camera.h> #include <igl/MouseController.h> #include <igl/REDRUM.h> #include <igl/STR.h> #include <igl/barycenter.h> #include <igl/bone_parents.h> #include <igl/boundary_conditions.h> #include <igl/boundary_facets.h> #include <igl/centroid.h> #include <igl/colon.h> #include <igl/draw_beach_ball.h> #include <igl/draw_floor.h> #include <igl/draw_mesh.h> #include <igl/draw_skeleton_3d.h> #include <igl/draw_skeleton_vector_graphics.h> #include <igl/forward_kinematics.h> #include <igl/get_seconds.h> #include <igl/lbs_matrix.h> #include <igl/material_colors.h> #include <igl/next_filename.h> #include <igl/normalize_row_sums.h> #include <igl/pathinfo.h> #include <igl/per_face_normals.h> #include <igl/quat_to_mat.h> #include <igl/readDMAT.h> #include <igl/readTGF.h> #include <igl/read_triangle_mesh.h> #include <igl/remove_unreferenced.h> #include <igl/report_gl_error.h> #include <igl/snap_to_canonical_view_quat.h> #include <igl/snap_to_fixed_up.h> #include <igl/trackball.h> #include <igl/two_axis_valuator_fixed_up.h> #include <igl/volume.h> #include <igl/winding_number.h> #include <igl/writeDMAT.h> #include <igl/writeMESH.h> #include <igl/writeOBJ.h> #include <igl/writeOFF.h> #include <igl/writeTGF.h> #include <igl/anttweakbar/ReAntTweakBar.h> #include <igl/bbw/bbw.h> #include <igl/cgal/remesh_self_intersections.h> #include <igl/tetgen/mesh_with_skeleton.h> #include <igl/tetgen/tetrahedralize.h> #include <Eigen/Core> #include <Eigen/Geometry> #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut.h> #endif #ifndef GLUT_WHEEL_UP #define GLUT_WHEEL_UP 3 #endif #ifndef GLUT_WHEEL_DOWN #define GLUT_WHEEL_DOWN 4 #endif #ifndef GLUT_WHEEL_RIGHT #define GLUT_WHEEL_RIGHT 5 #endif #ifndef GLUT_WHEEL_LEFT #define GLUT_WHEEL_LEFT 6 #endif #ifndef GLUT_ACTIVE_COMMAND #define GLUT_ACTIVE_COMMAND 8 #endif #include <string> #include <vector> #include <queue> #include <stack> #include <iostream> #include <iomanip> #define VERBOSE enum SkelStyleType { SKEL_STYLE_TYPE_3D = 0, SKEL_STYLE_TYPE_VECTOR_GRAPHICS = 1, NUM_SKEL_STYLE_TYPE = 2 }skel_style; Eigen::MatrixXd V,N,W,M; Eigen::Vector3d Vmid; double bbd = 1.0; Eigen::MatrixXi F; igl::Camera camera; Eigen::MatrixXd C; Eigen::MatrixXi BE; Eigen::VectorXi P,RP; struct State { igl::MouseController mouse; Eigen::MatrixXf colors; } s; bool wireframe = false; // See README for descriptions enum RotationType { ROTATION_TYPE_IGL_TRACKBALL = 0, ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1, NUM_ROTATION_TYPES = 2, } rotation_type = ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP; std::stack<State> undo_stack; std::stack<State> redo_stack; bool is_rotating = false; bool centroid_is_visible = true; int down_x,down_y; igl::Camera down_camera; std::string output_weights_filename,output_pose_prefix; struct CameraAnimation { bool is_animating = false; double DURATION = 0.5; double start_time = 0; Eigen::Quaterniond from_quat,to_quat; } canim; typedef std::vector< Eigen::Quaterniond, Eigen::aligned_allocator<Eigen::Quaterniond> > RotationList; struct PoseAnimation { bool is_animating = false; double DURATION = 2; double start_time = 0; RotationList pose; } panim; int width,height; Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0); #define REBAR_NAME "temp.rbr" igl::anttweakbar::ReTwBar rebar; void push_undo() { undo_stack.push(s); // Clear redo_stack = std::stack<State>(); } // No-op setter, does nothing void TW_CALL no_op(const void * /*value*/, void * /*clientData*/) { } void TW_CALL set_rotation_type(const void * value, void * clientData) { using namespace Eigen; using namespace std; using namespace igl; const RotationType old_rotation_type = rotation_type; rotation_type = *(const RotationType *)(value); if(rotation_type == ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP && old_rotation_type != ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP) { push_undo(); canim.from_quat = camera.m_rotation_conj; snap_to_fixed_up(canim.from_quat,canim.to_quat); // start animation canim.start_time = get_seconds(); canim.is_animating = true; } } void TW_CALL get_rotation_type(void * value, void *clientData) { RotationType * rt = (RotationType *)(value); *rt = rotation_type; } void reshape(int width, int height) { ::width = width; ::height = height; glViewport(0,0,width,height); // Send the new window size to AntTweakBar TwWindowSize(width, height); camera.m_aspect = (double)width/(double)height; s.mouse.reshape(width,height); } void push_scene() { using namespace igl; using namespace std; glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPerspective(camera.m_angle,camera.m_aspect,camera.m_near,camera.m_far); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); gluLookAt( camera.eye()(0), camera.eye()(1), camera.eye()(2), camera.at()(0), camera.at()(1), camera.at()(2), camera.up()(0), camera.up()(1), camera.up()(2)); } void push_object() { using namespace igl; glPushMatrix(); glScaled(2./bbd,2./bbd,2./bbd); glTranslated(-Vmid(0),-Vmid(1),-Vmid(2)); } void pop_object() { glPopMatrix(); } void pop_scene() { glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } // Set up double-sided lights void lights() { using namespace std; using namespace Eigen; glEnable(GL_LIGHTING); glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); float WHITE[4] = {0.8,0.8,0.8,1.}; float GREY[4] = {0.4,0.4,0.4,1.}; float BLACK[4] = {0.,0.,0.,1.}; Vector4f pos = light_pos; glLightfv(GL_LIGHT0,GL_AMBIENT,GREY); glLightfv(GL_LIGHT0,GL_DIFFUSE,WHITE); glLightfv(GL_LIGHT0,GL_SPECULAR,BLACK); glLightfv(GL_LIGHT0,GL_POSITION,pos.data()); pos(0) *= -1; pos(1) *= -1; pos(2) *= -1; glLightfv(GL_LIGHT1,GL_AMBIENT,GREY); glLightfv(GL_LIGHT1,GL_DIFFUSE,WHITE); glLightfv(GL_LIGHT1,GL_SPECULAR,BLACK); glLightfv(GL_LIGHT1,GL_POSITION,pos.data()); } void display() { using namespace igl; using namespace std; using namespace Eigen; const float back[4] = {0.75, 0.75, 0.75,0}; glClearColor(back[0],back[1],back[2],0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if(canim.is_animating) { double t = (get_seconds() - canim.start_time)/canim.DURATION; if(t > 1) { t = 1; canim.is_animating = false; } Quaterniond q = canim.from_quat.slerp(t,canim.to_quat).normalized(); camera.orbit(q.conjugate()); } glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glEnable(GL_NORMALIZE); lights(); push_scene(); // Draw a nice floor glEnable(GL_DEPTH_TEST); glPushMatrix(); const double floor_offset = -2./bbd*(V.col(1).maxCoeff()-Vmid(1)); glTranslated(0,floor_offset,0); const float GREY[4] = {0.5,0.5,0.6,1.0}; const float DARK_GREY[4] = {0.2,0.2,0.3,1.0}; glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); draw_floor(GREY,DARK_GREY); glDisable(GL_CULL_FACE); glPopMatrix(); push_object(); const auto & draw_skeleton = [](const MatrixXd & T) { switch(skel_style) { default: case SKEL_STYLE_TYPE_3D: { draw_skeleton_3d(C,BE,T,s.colors,bbd*0.5); break; } case SKEL_STYLE_TYPE_VECTOR_GRAPHICS: draw_skeleton_vector_graphics(C,BE,T); break; } }; // Set material properties glDisable(GL_COLOR_MATERIAL); glMaterialfv(GL_FRONT, GL_AMBIENT,GOLD_AMBIENT); glMaterialfv(GL_FRONT, GL_DIFFUSE,GOLD_DIFFUSE); glMaterialfv(GL_FRONT, GL_SPECULAR,GOLD_SPECULAR); glMaterialf (GL_FRONT, GL_SHININESS, 128); glMaterialfv(GL_BACK, GL_AMBIENT,SILVER_AMBIENT); glMaterialfv(GL_BACK, GL_DIFFUSE,FAST_GREEN_DIFFUSE); glMaterialfv(GL_BACK, GL_SPECULAR,SILVER_SPECULAR); glMaterialf (GL_BACK, GL_SHININESS, 128); if(wireframe) { glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } glLineWidth(1.0); MatrixXd T; RotationList dQ; if(panim.is_animating) { double t = (get_seconds() - panim.start_time)/panim.DURATION; if(t > 1) { t = 1; panim.is_animating = false; } const auto & ease = [](const double t) { return 3.*t*t-2.*t*t*t; }; double f = (t<0.5?ease(2.*t):ease(2.-2.*t)); dQ.resize(panim.pose.size()); for(int e = 0;e<(int)panim.pose.size();e++) { dQ[e] = panim.pose[e].slerp(f,Quaterniond::Identity()).normalized(); } }else { dQ = s.mouse.rotations(); } forward_kinematics(C,BE,P,dQ,T); MatrixXd U = M*T; MatrixXd UN; per_face_normals(U,F,UN); draw_mesh(U,F,UN); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); glDisable(GL_DEPTH_TEST); draw_skeleton(T); if(centroid_is_visible) { Vector3d cen; centroid(U,F,cen); glEnable(GL_DEPTH_TEST); glPushMatrix(); glTranslated(cen(0),cen(1),cen(2)); glScaled(bbd/2.,bbd/2.,bbd/2.); glScaled(0.1,0.1,0.1); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0,-100000); draw_beach_ball(); glDisable(GL_POLYGON_OFFSET_FILL); glPopMatrix(); } // Mouse is always on top glDisable(GL_DEPTH_TEST); if(!panim.is_animating) { s.mouse.draw(); } pop_object(); pop_scene(); report_gl_error(); TwDraw(); glutSwapBuffers(); if(canim.is_animating || panim.is_animating) { glutPostRedisplay(); } } void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y) { using namespace std; using namespace igl; using namespace Eigen; GLint viewport[4]; glGetIntegerv(GL_VIEWPORT,viewport); if(wheel == 0 && TwMouseMotion(mouse_x, viewport[3] - mouse_y)) { static double mouse_scroll_y = 0; const double delta_y = 0.125*direction; mouse_scroll_y += delta_y; TwMouseWheel(mouse_scroll_y); return; } push_undo(); if(wheel==0) { // factor of zoom change double s = (1.-0.01*direction); //// FOV zoom: just widen angle. This is hardly ever appropriate. //camera.m_angle *= s; //camera.m_angle = min(max(camera.m_angle,1),89); camera.push_away(s); }else { // Dolly zoom: camera.dolly_zoom((double)direction*1.0); } glutPostRedisplay(); } void mouse(int glutButton, int glutState, int mouse_x, int mouse_y) { using namespace std; using namespace Eigen; using namespace igl; bool tw_using = TwEventMouseButtonGLUT(glutButton,glutState,mouse_x,mouse_y); const int mod = (glutButton <=2 ? glutGetModifiers() : 0); const bool option_down = mod & GLUT_ACTIVE_ALT; switch(glutButton) { case GLUT_RIGHT_BUTTON: case GLUT_LEFT_BUTTON: { push_scene(); push_object(); switch(glutState) { case 1: { // up const bool mouse_was_selecting = s.mouse.is_selecting(); is_rotating = false; s.mouse.up(mouse_x,mouse_y); glutSetCursor(GLUT_CURSOR_INHERIT); if(mouse_was_selecting) { s.mouse.set_selection_from_last_drag(C,BE,P,RP); MouseController::VectorXb S; MouseController::propogate_to_descendants_if( s.mouse.selection(),P,S); MouseController::color_if(S,MAYA_SEA_GREEN,MAYA_VIOLET,s.colors); } break; } case 0: if(!tw_using) { down_x = mouse_x; down_y = mouse_y; if(option_down || glutButton==GLUT_RIGHT_BUTTON) { glutSetCursor(GLUT_CURSOR_CYCLE); // collect information for trackball is_rotating = true; down_camera = camera; }else { push_undo(); s.mouse.down(mouse_x,mouse_y); } } break; } pop_object(); pop_scene(); break; } // Scroll down case 3: { mouse_wheel(0,-1,mouse_x,mouse_y); break; } // Scroll up case 4: { mouse_wheel(0,1,mouse_x,mouse_y); break; } // Scroll left case 5: { mouse_wheel(1,-1,mouse_x,mouse_y); break; } // Scroll right case 6: { mouse_wheel(1,1,mouse_x,mouse_y); break; } } glutPostRedisplay(); } void mouse_drag(int mouse_x, int mouse_y) { using namespace igl; using namespace std; using namespace Eigen; push_scene(); push_object(); if(is_rotating) { glutSetCursor(GLUT_CURSOR_CYCLE); Quaterniond q; switch(rotation_type) { case ROTATION_TYPE_IGL_TRACKBALL: { // Rotate according to trackball igl::trackball( width, height, 2.0, down_camera.m_rotation_conj, down_x, down_y, mouse_x, mouse_y, q); break; } case ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP: { // Rotate according to two axis valuator with fixed up vector two_axis_valuator_fixed_up( width, height, 2.0, down_camera.m_rotation_conj, down_x, down_y, mouse_x, mouse_y, q); break; } default: break; } camera.orbit(q.conjugate()); }else if(s.mouse.drag(mouse_x,mouse_y)) { } pop_object(); pop_scene(); glutPostRedisplay(); } void init_relative() { using namespace Eigen; using namespace igl; using namespace std; per_face_normals(V,F,N); const auto Vmax = V.colwise().maxCoeff(); const auto Vmin = V.colwise().minCoeff(); Vmid = 0.5*(Vmax + Vmin); bbd = (Vmax-Vmin).norm(); camera.push_away(2); } void undo() { using namespace std; if(!undo_stack.empty()) { redo_stack.push(s); s = undo_stack.top(); undo_stack.pop(); s.mouse.reshape(width,height); } } void redo() { using namespace std; if(!redo_stack.empty()) { undo_stack.push(s); s = redo_stack.top(); redo_stack.pop(); s.mouse.reshape(width,height); } } bool save_pose() { using namespace std; using namespace igl; using namespace Eigen; string output_filename; next_filename(output_pose_prefix,4,".dmat",output_filename); MatrixXd T; forward_kinematics(C,BE,P,s.mouse.rotations(),T); if(writeDMAT(output_filename,T)) { cout<<GREENGIN("Current pose written to "+output_filename+".")<<endl; return true; }else { cout<<REDRUM("Writing to "+output_filename+" failed.")<<endl; return false; } } bool save_weights() { using namespace std; using namespace igl; using namespace Eigen; if(writeDMAT(output_weights_filename,W)) { cout<<GREENGIN("Current weights written to "+ output_weights_filename+".")<<endl; return true; }else { cout<<REDRUM("Writing to "+output_weights_filename+" failed.")<<endl; return false; } } bool save_mesh() { using namespace std; using namespace igl; using namespace Eigen; MatrixXd T; forward_kinematics(C,BE,P,s.mouse.rotations(),T); MatrixXd U = M*T; string output_filename; next_filename(output_pose_prefix,4,".obj",output_filename); if(writeOBJ(output_filename,U,F)) { cout<<GREENGIN("Current mesh written to "+ output_filename+".")<<endl; return true; }else { cout<<REDRUM("Writing to "+output_filename+" failed.")<<endl; return false; } } void key(unsigned char key, int mouse_x, int mouse_y) { using namespace std; using namespace igl; using namespace Eigen; int mod = glutGetModifiers(); const bool command_down = GLUT_ACTIVE_COMMAND & mod; const bool shift_down = GLUT_ACTIVE_SHIFT & mod; switch(key) { // ESC case char(27): rebar.save(REBAR_NAME); // ^C case char(3): exit(0); case 'A': case 'a': { panim.is_animating = !panim.is_animating; panim.pose = s.mouse.rotations(); panim.start_time = get_seconds(); break; } case 'D': case 'd': { push_undo(); s.mouse.clear_selection(); break; } case 'R': { push_undo(); s.mouse.reset_selected_rotations(); break; } case 'r': { push_undo(); s.mouse.reset_rotations(); break; } case 'S': { save_mesh(); break; } case 's': { save_pose(); break; } case 'W': case 'w': { save_weights(); break; } case 'z': case 'Z': is_rotating = false; if(command_down) { if(shift_down) { redo(); }else { undo(); } break; }else { push_undo(); Quaterniond q; snap_to_canonical_view_quat(camera.m_rotation_conj,1.0,q); camera.orbit(q.conjugate()); } break; default: if(!TwEventKeyboardGLUT(key,mouse_x,mouse_y)) { cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl; } } glutPostRedisplay(); } bool clean( const Eigen::MatrixXd & V, const Eigen::MatrixXi & F, Eigen::MatrixXd & CV, Eigen::MatrixXi & CF) { using namespace igl; using namespace Eigen; using namespace std; { MatrixXi _1; VectorXi _2,IM; #ifdef VERBOSE cout<<"remesh_self_intersections"<<endl; #endif igl::cgal::remesh_self_intersections(V,F,{},CV,CF,_1,_2,IM); for_each(CF.data(),CF.data()+CF.size(),[&IM](int & a){a=IM(a);}); MatrixXd oldCV = CV; MatrixXi oldCF = CF; remove_unreferenced(oldCV,oldCF,CV,CF,IM); } MatrixXd TV; MatrixXi TT; { MatrixXi _1; // c convex hull // Y no boundary steiners // p polygon input #ifdef VERBOSE cout<<"tetrahedralize"<<endl; #endif if(igl::tetgen::tetrahedralize(CV,CF,"cYpC",TV,TT,_1) != 0) { cout<<REDRUM("CDT failed.")<<endl; return false; } } { MatrixXd BC; barycenter(TV,TT,BC); VectorXd W; #ifdef VERBOSE cout<<"winding_number"<<endl; #endif winding_number(V,F,BC,W); W = W.array().abs(); const double thresh = 0.5; const int count = (W.array()>thresh).cast<int>().sum(); MatrixXi CT(count,TT.cols()); int c = 0; for(int t = 0;t<TT.rows();t++) { if(W(t)>thresh) { CT.row(c++) = TT.row(t); } } assert(c==count); boundary_facets(CT,CF); } return true; } bool robust_weights( const Eigen::MatrixXd & V, const Eigen::MatrixXi & F, const Eigen::MatrixXd & C, const Eigen::MatrixXi & BE, Eigen::MatrixXd & W) { using namespace igl; using namespace Eigen; using namespace std; // clean mesh MatrixXd CV; MatrixXi CF; if(!clean(V,F,CV,CF)) { return false; } MatrixXd TV; MatrixXi TT; // compute tet-mesh { MatrixXi _1; #ifdef VERBOSE cout<<"mesh_with_skeleton"<<endl; #endif if(!igl::tetgen::mesh_with_skeleton(CV,CF,C,{},BE,{},10,"pq1.5Y",TV,TT,_1)) { cout<<REDRUM("tetgen failed.")<<endl; return false; } } // Finally, tetgen may have still included some insanely small tets. // Just ignore these during weight computation (and hope they don't isolate // any vertices). { const MatrixXi oldTT = TT; VectorXd vol; volume(TV,TT,vol); const double thresh = 1e-17; const int count = (vol.array()>thresh).cast<int>().sum(); TT.resize(count,oldTT.cols()); int c = 0; for(int t = 0;t<oldTT.rows();t++) { if(vol(t)>thresh) { TT.row(c++) = oldTT.row(t); } } } // compute weights VectorXi b; MatrixXd bc; if(!boundary_conditions(TV,TT,C,{},BE,{},b,bc)) { cout<<REDRUM("boundary_conditions failed.")<<endl; return false; } // compute BBW // Default bbw data and flags igl::bbw::BBWData bbw_data; bbw_data.verbosity = 1; #ifdef IGL_NO_MOSEK bbw_data.qp_solver = igl::bbw::QP_SOLVER_IGL_ACTIVE_SET; bbw_data.active_set_params.max_iter = 4; #else bbw_data.mosek_data.douparam[MSK_DPAR_INTPNT_TOL_REL_GAP]=1e-14; bbw_data.qp_solver = igl::bbw::QP_SOLVER_MOSEK; #endif // Weights matrix if(!igl::bbw::bbw(TV,TT,b,bc,bbw_data,W)) { return false; } // Normalize weights to sum to one normalize_row_sums(W,W); // keep first #V weights W.conservativeResize(V.rows(),W.cols()); return true; } int main(int argc, char * argv[]) { using namespace std; using namespace Eigen; using namespace igl; string filename = "../shared/cheburashka.off"; string skel_filename = "../shared/cheburashka.tgf"; string weights_filename = ""; output_pose_prefix = ""; switch(argc) { case 5: output_pose_prefix = argv[4]; //fall through case 4: weights_filename = argv[3]; //fall through case 3: skel_filename = argv[2]; // Read and prepare mesh filename = argv[1]; break; default: cerr<<"Usage:"<<endl<<" ./example model.obj skeleton.tgf [weights.dmat] [pose-prefix]"<<endl; cout<<endl<<"Opening default rig..."<<endl; } // print key commands cout<<"[Click] and [drag] Select a bone/Use onscreen widget to rotate bone."<<endl; cout<<"⌥ +[Click] and [drag] Rotate secene."<<endl; cout<<"⌫ Delete selected node(s) and incident bones."<<endl; cout<<"D,d Deselect all."<<endl; cout<<"R Reset selected rotation."<<endl; cout<<"r Reset all rotations."<<endl; cout<<"S Save current posed mesh."<<endl; cout<<"s Save current skeleton pose."<<endl; cout<<"W,w Save current weights."<<endl; cout<<"Z,z Snap to canonical view."<<endl; cout<<"⌘ Z Undo."<<endl; cout<<"⇧ ⌘ Z Redo."<<endl; cout<<"^C,ESC Exit (without saving)."<<endl; string dir,_1,_2,name; read_triangle_mesh(filename,V,F,dir,_1,_2,name); if(output_weights_filename.size() == 0) { output_weights_filename = dir+"/"+name+"-weights.dmat"; } if(output_pose_prefix.size() == 0) { output_pose_prefix = dir+"/"+name+"-pose-"; } { string output_filename; next_filename(output_pose_prefix,4,".dmat",output_filename); cout<<BLUEGIN("Output weights set to start with "<< output_weights_filename)<<endl; cout<<BLUEGIN("Output poses set to start with "<<output_filename)<<endl; } // Read in skeleton and precompute hierarchy readTGF(skel_filename,C,BE); // initialize mouse interface s.mouse.set_size(BE.rows()); // Rigid parts (not used) colon<int>(0,BE.rows()-1,RP); assert(RP.size() == BE.rows()); // Bone parents bone_parents(BE,P); if(weights_filename.size() == 0) { robust_weights(V,F,C,BE,W); }else { // Read in weights and precompute LBS matrix readDMAT(weights_filename,W); } lbs_matrix(V,W,M); init_relative(); // Init glut glutInit(&argc,argv); if( !TwInit(TW_OPENGL, NULL) ) { // A fatal error occured fprintf(stderr, "AntTweakBar initialization failed: %s\n", TwGetLastError()); return 1; } // Create a tweak bar rebar.TwNewBar("TweakBar"); rebar.TwAddVarRW("camera_rotation", TW_TYPE_QUAT4D, camera.m_rotation_conj.coeffs().data(), "open readonly=true"); TwType RotationTypeTW = igl::anttweakbar::ReTwDefineEnumFromString("RotationType", "igl_trackball,two-a...-fixed-up"); rebar.TwAddVarCB( "rotation_type", RotationTypeTW, set_rotation_type,get_rotation_type,NULL,"keyIncr=] keyDecr=["); rebar.TwAddVarRW("wireframe", TW_TYPE_BOOLCPP,&wireframe,"key=l"); rebar.TwAddVarRW("centroid_is_visible", TW_TYPE_BOOLCPP,¢roid_is_visible, "keyIncr=C keyDecr=c label='centroid visible?'"); TwType SkelStyleTypeTW = igl::anttweakbar::ReTwDefineEnumFromString("SkelStyleType", "3d,vector-graphics"); rebar.TwAddVarRW("style",SkelStyleTypeTW,&skel_style,""); rebar.load(REBAR_NAME); // Init antweakbar glutInitDisplayString( "rgba depth double samples>=8 "); glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)/2.0); glutCreateWindow("skeleton-poser"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(key); glutMouseFunc(mouse); glutMotionFunc(mouse_drag); glutPassiveMotionFunc((GLUTmousemotionfun)TwEventMouseMotionGLUT); glutMainLoop(); return 0; }