123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- // 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 <igl/read_triangle_mesh.h>
- #include <igl/opengl2/draw_mesh.h>
- #include <igl/opengl2/draw_floor.h>
- #include <igl/pathinfo.h>
- #include <igl/list_to_matrix.h>
- #include <igl/per_face_normals.h>
- #include <igl/material_colors.h>
- #include <igl/trackball.h>
- #include <igl/snap_to_canonical_view_quat.h>
- #include <igl/write_triangle_mesh.h>
- #include <igl/REDRUM.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 <stack>
- #include <iostream>
- struct State
- {
- Eigen::MatrixXd V,N;
- Eigen::MatrixXi F;
- Eigen::VectorXd Vmax,Vmin,Vmid;
- double bbd;
- Eigen::Quaterniond rot;
- } s;
- std::stack<State> undo_stack;
- std::stack<State> 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<State>();
- }
- 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<double>(
- 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<<GREENGIN("Current baked model written to "+out_filename+".")<<endl;
- return true;
- }else
- {
- cout<<REDRUM("Current baked model failed to write to "+out_filename+".")<<endl;
- return false;
- }
- }
- void undo()
- {
- using namespace std;
- if(!undo_stack.empty())
- {
- redo_stack.push(s);
- s = undo_stack.top();
- undo_stack.pop();
- }
- }
- void redo()
- {
- using namespace std;
- if(!redo_stack.empty())
- {
- undo_stack.push(s);
- s = redo_stack.top();
- redo_stack.pop();
- }
- }
- void key(unsigned char key, int mouse_x, int mouse_y)
- {
- using namespace std;
- const int mod = glutGetModifiers();
- const bool command_down = GLUT_ACTIVE_COMMAND & mod;
- const bool shift_down = GLUT_ACTIVE_SHIFT & mod;
- switch(key)
- {
- // Ctrl-c and esc exit
- case char(3):
- case char(27):
- exit(0);
- case 'B':
- case 'b':
- push_undo();
- bake();
- init_relative();
- break;
- case 'I':
- case 'i':
- push_undo();
- s.F = s.F.rowwise().reverse().eval();
- break;
- case 'R':
- case 'r':
- push_undo();
- init_relative();
- break;
- case 'S':
- case 's':
- save();
- break;
- case 'z':
- case 'Z':
- if(command_down)
- {
- if(shift_down)
- {
- redo();
- }else
- {
- undo();
- }
- break;
- }else
- {
- push_undo();
- igl::snap_to_canonical_view_quat<double>(
- s.rot.coeffs().data(),
- 1.0,
- s.rot.coeffs().data());
- break;
- }
- default:
- cout<<"Unknown key command: '"<<key<<"' ("<<int(key)<<")"<<endl;
- }
- glutPostRedisplay();
- }
- int main(int argc, char * argv[])
- {
- using namespace std;
- using namespace Eigen;
- using namespace igl;
- if(argc < 3)
- {
- cerr<<"Usage:"<<endl<<" ./upright input.obj output.obj"<<endl;
- return 1;
- }
- // print key commands
- cout<<"[Click] and [drag] Rotate model using trackball."<<endl;
- cout<<"[Z,z] Snap rotation to canonical view."<<endl;
- cout<<"[B,b] \"Bake\" current scale, shift, and rotation "
- "into model."<<endl;
- cout<<"[⌘ Z] Undo."<<endl;
- cout<<"[⇧ ⌘ Z] Redo."<<endl;
- cout<<"[R,r] Reset rotation."<<endl;
- cout<<"[S,s] Save to output model path."<<endl;
- cout<<"[I,i] Flip orientation of faces (gold front, green "
- "back)."<<endl;
- cout<<"[^C,ESC] Exit."<<endl;
- // Read and prepare mesh
- string filename = argv[1];
- out_filename = argv[2];
- if(!read_triangle_mesh(filename,s.V,s.F))
- {
- cout<<"Failed to read from "<<filename<<"."<<endl;
- return EXIT_FAILURE;
- }
- init_relative();
- // Init glut
- glutInit(&argc,argv);
- glutInitDisplayString( "rgba depth double samples>=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;
- }
|