// Small GLUT application to help quickly orient a model in an upright // position, scaled and shifted to fit the unit sphere. // // Demonstrates trackball mouse camera control and undo/redo stack with // immutable state object (i.e. not with reverse operations). // // Reads (V,F) from .obj,.off,.wrl files and saves (V,F) to .obj,.off. Any // normals, texture coordinates, etc. in the input file will be ignored and // lost in the output file. // #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #else #include #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 #include #include #include struct State { Eigen::MatrixXd V,N; Eigen::MatrixXi F; Eigen::VectorXd Vmax,Vmin,Vmid; double bbd; Eigen::Quaterniond rot; } s; std::stack undo_stack; std::stack redo_stack; bool trackball_on = false; int down_x,down_y; Eigen::Quaterniond down_rot; int width,height; std::string out_filename; Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0); void push_undo() { undo_stack.push(s); // Clear redo_stack = std::stack(); } void reshape(int width, int height) { ::width = width; ::height = height; glViewport(0,0,width,height); } void push_scene() { using namespace igl; using namespace std; const double angle = 15; glMatrixMode(GL_PROJECTION); glLoadIdentity(); double zNear = 1e-2; double zFar = 100; double aspect = ((double)width)/((double)height); // Amount of scaling needed to "fix" perspective z-shift double z_fix = 1.0; // 5 is far enough to see unit "things" well const double camera_z = 2; // Test if should be using true orthographic igl::opengl2::projection if(angle == 0) { glOrtho( -0.5*camera_z*aspect, 0.5*camera_z*aspect, -0.5*camera_z, 0.5*camera_z, zNear, zFar); }else { // Make sure aspect is sane aspect = aspect < 0.01 ? 0.01 : aspect; gluPerspective(angle,aspect,zNear,zFar); z_fix = 2.*tan(angle/2./360.*2.*M_PI); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0,0,camera_z,0,0,0,0,1,0); // Adjust scale to correct perspective glScaled(z_fix,z_fix,z_fix); // scale, pan } void push_object() { glPushMatrix(); glMultMatrixd(Eigen::Affine3d(s.rot).matrix().data()); glScaled(2./s.bbd,2./s.bbd,2./s.bbd); glTranslated(-s.Vmid(0),-s.Vmid(1),-s.Vmid(2)); } void pop_object() { glPopMatrix(); } void pop_scene() { 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; glClearColor(1,1,1,0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); lights(); push_scene(); push_object(); // 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); igl::opengl2::draw_mesh(s.V,s.F,s.N); glDisable(GL_LIGHTING); glPushMatrix(); glLineWidth(1.0); glColor3f(0,0,0); glTranslated( s.Vmid(0), s.Vmid(1), s.Vmid(2)); glScaled( (s.Vmax(0)-s.Vmin(0)), (s.Vmax(1)-s.Vmin(1)), (s.Vmax(2)-s.Vmin(2))); glutWireCube(1.0); glPopMatrix(); pop_object(); glDisable(GL_LIGHTING); glPushMatrix(); glLineWidth(2.0); glColor3f(1,0,0); //glTranslated(s.Vmid(0), s.Vmid(1), s.Vmid(2)); glScaled( 2*(s.Vmax(0)-s.Vmin(0))/s.bbd, 2*(s.Vmax(1)-s.Vmin(1))/s.bbd, 2*(s.Vmax(2)-s.Vmin(2))/s.bbd); glutWireCube(1.0); glPopMatrix(); //glPushMatrix(); //glRotated(90,1,0,0); //glColor3f(0.5,0.5,0.5); //glutWireSphere(1.0,20,10); //glPopMatrix(); glEnable(GL_LIGHTING); glPushMatrix(); glTranslated(0,-1,0); igl::opengl2::draw_floor(); glPopMatrix(); pop_scene(); glutSwapBuffers(); } void mouse(int glutButton, int glutState, int mouse_x, int mouse_y) { using namespace std; using namespace Eigen; using namespace igl; switch(glutState) { case 1: // up glutSetCursor(GLUT_CURSOR_LEFT_ARROW); trackball_on = false; break; case 0: push_undo(); glutSetCursor(GLUT_CURSOR_CYCLE); // collect information for trackball trackball_on = true; down_rot = s.rot; down_x = mouse_x; down_y = mouse_y; break; } glutPostRedisplay(); } void mouse_drag(int mouse_x, int mouse_y) { using namespace igl; if(trackball_on) { glutSetCursor(GLUT_CURSOR_CYCLE); // Rotate according to trackball trackball( width, height, 2.0, down_rot.coeffs().data(), down_x, down_y, mouse_x, mouse_y, s.rot.coeffs().data()); } glutPostRedisplay(); } // Bake rotation and scale into V void bake() { s.V.col(0).array() -= s.Vmid(0); s.V.col(1).array() -= s.Vmid(1); s.V.col(2).array() -= s.Vmid(2); s.V *= 2./s.bbd; s.V = (s.V * s.rot.matrix().transpose()).eval(); } void init_relative() { using namespace Eigen; using namespace igl; per_face_normals(s.V,s.F,s.N); s.rot = Quaterniond(1,0,0,0); s.Vmax = s.V.colwise().maxCoeff(); s.Vmin = s.V.colwise().minCoeff(); s.Vmid = 0.5*(s.Vmax + s.Vmin); s.bbd = (s.Vmax-s.Vmin).norm(); } bool save() { using namespace std; using namespace igl; using namespace Eigen; if(write_triangle_mesh(out_filename,s.V,s.F)) { cout<( s.rot.coeffs().data(), 1.0, s.rot.coeffs().data()); break; } default: cout<<"Unknown key command: '"<=8 "); glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)); glutCreateWindow("upright"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(key); glutMouseFunc(mouse); glutMotionFunc(mouse_drag); //glutPassiveMotionFunc(mouse_move); glutMainLoop(); return EXIT_SUCCESS; }