example.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. // Small GLUT application to help quickly orient a model in an upright
  2. // position, scaled and shifted to fit the unit sphere.
  3. //
  4. // Demonstrates trackball mouse camera control and undo/redo stack with
  5. // immutable state object (i.e. not with reverse operations).
  6. //
  7. // Reads (V,F) from .obj,.off,.wrl files and saves (V,F) to .obj,.off. Any
  8. // normals, texture coordinates, etc. in the input file will be ignored and
  9. // lost in the output file.
  10. //
  11. #include <igl/read_triangle_mesh.h>
  12. #include <igl/opengl2/draw_mesh.h>
  13. #include <igl/opengl2/draw_floor.h>
  14. #include <igl/pathinfo.h>
  15. #include <igl/list_to_matrix.h>
  16. #include <igl/per_face_normals.h>
  17. #include <igl/material_colors.h>
  18. #include <igl/trackball.h>
  19. #include <igl/snap_to_canonical_view_quat.h>
  20. #include <igl/write_triangle_mesh.h>
  21. #include <igl/REDRUM.h>
  22. #include <Eigen/Core>
  23. #include <Eigen/Geometry>
  24. #ifdef __APPLE__
  25. #include <GLUT/glut.h>
  26. #else
  27. #include <GL/glut.h>
  28. #endif
  29. #ifndef GLUT_WHEEL_UP
  30. #define GLUT_WHEEL_UP 3
  31. #endif
  32. #ifndef GLUT_WHEEL_DOWN
  33. #define GLUT_WHEEL_DOWN 4
  34. #endif
  35. #ifndef GLUT_WHEEL_RIGHT
  36. #define GLUT_WHEEL_RIGHT 5
  37. #endif
  38. #ifndef GLUT_WHEEL_LEFT
  39. #define GLUT_WHEEL_LEFT 6
  40. #endif
  41. #ifndef GLUT_ACTIVE_COMMAND
  42. #define GLUT_ACTIVE_COMMAND 8
  43. #endif
  44. #include <string>
  45. #include <vector>
  46. #include <stack>
  47. #include <iostream>
  48. struct State
  49. {
  50. Eigen::MatrixXd V,N;
  51. Eigen::MatrixXi F;
  52. Eigen::VectorXd Vmax,Vmin,Vmid;
  53. double bbd;
  54. Eigen::Quaterniond rot;
  55. } s;
  56. std::stack<State> undo_stack;
  57. std::stack<State> redo_stack;
  58. bool trackball_on = false;
  59. int down_x,down_y;
  60. Eigen::Quaterniond down_rot;
  61. int width,height;
  62. std::string out_filename;
  63. Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0);
  64. void push_undo()
  65. {
  66. undo_stack.push(s);
  67. // Clear
  68. redo_stack = std::stack<State>();
  69. }
  70. void reshape(int width, int height)
  71. {
  72. ::width = width;
  73. ::height = height;
  74. glViewport(0,0,width,height);
  75. }
  76. void push_scene()
  77. {
  78. using namespace igl;
  79. using namespace std;
  80. const double angle = 15;
  81. glMatrixMode(GL_PROJECTION);
  82. glLoadIdentity();
  83. double zNear = 1e-2;
  84. double zFar = 100;
  85. double aspect = ((double)width)/((double)height);
  86. // Amount of scaling needed to "fix" perspective z-shift
  87. double z_fix = 1.0;
  88. // 5 is far enough to see unit "things" well
  89. const double camera_z = 2;
  90. // Test if should be using true orthographic igl::opengl2::projection
  91. if(angle == 0)
  92. {
  93. glOrtho(
  94. -0.5*camera_z*aspect,
  95. 0.5*camera_z*aspect,
  96. -0.5*camera_z,
  97. 0.5*camera_z,
  98. zNear,
  99. zFar);
  100. }else
  101. {
  102. // Make sure aspect is sane
  103. aspect = aspect < 0.01 ? 0.01 : aspect;
  104. gluPerspective(angle,aspect,zNear,zFar);
  105. z_fix = 2.*tan(angle/2./360.*2.*M_PI);
  106. }
  107. glMatrixMode(GL_MODELVIEW);
  108. glLoadIdentity();
  109. gluLookAt(0,0,camera_z,0,0,0,0,1,0);
  110. // Adjust scale to correct perspective
  111. glScaled(z_fix,z_fix,z_fix);
  112. // scale, pan
  113. }
  114. void push_object()
  115. {
  116. glPushMatrix();
  117. glMultMatrixd(Eigen::Affine3d(s.rot).matrix().data());
  118. glScaled(2./s.bbd,2./s.bbd,2./s.bbd);
  119. glTranslated(-s.Vmid(0),-s.Vmid(1),-s.Vmid(2));
  120. }
  121. void pop_object()
  122. {
  123. glPopMatrix();
  124. }
  125. void pop_scene()
  126. {
  127. glPopMatrix();
  128. }
  129. // Set up double-sided lights
  130. void lights()
  131. {
  132. using namespace std;
  133. using namespace Eigen;
  134. glEnable(GL_LIGHTING);
  135. glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
  136. glEnable(GL_LIGHT0);
  137. glEnable(GL_LIGHT1);
  138. float WHITE[4] = {0.8,0.8,0.8,1.};
  139. float GREY[4] = {0.4,0.4,0.4,1.};
  140. float BLACK[4] = {0.,0.,0.,1.};
  141. Vector4f pos = light_pos;
  142. glLightfv(GL_LIGHT0,GL_AMBIENT,GREY);
  143. glLightfv(GL_LIGHT0,GL_DIFFUSE,WHITE);
  144. glLightfv(GL_LIGHT0,GL_SPECULAR,BLACK);
  145. glLightfv(GL_LIGHT0,GL_POSITION,pos.data());
  146. pos(0) *= -1;
  147. pos(1) *= -1;
  148. pos(2) *= -1;
  149. glLightfv(GL_LIGHT1,GL_AMBIENT,GREY);
  150. glLightfv(GL_LIGHT1,GL_DIFFUSE,WHITE);
  151. glLightfv(GL_LIGHT1,GL_SPECULAR,BLACK);
  152. glLightfv(GL_LIGHT1,GL_POSITION,pos.data());
  153. }
  154. void display()
  155. {
  156. using namespace igl;
  157. using namespace std;
  158. glClearColor(1,1,1,0);
  159. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  160. glEnable(GL_DEPTH_TEST);
  161. glEnable(GL_NORMALIZE);
  162. lights();
  163. push_scene();
  164. push_object();
  165. // Set material properties
  166. glDisable(GL_COLOR_MATERIAL);
  167. glMaterialfv(GL_FRONT, GL_AMBIENT, GOLD_AMBIENT);
  168. glMaterialfv(GL_FRONT, GL_DIFFUSE, GOLD_DIFFUSE );
  169. glMaterialfv(GL_FRONT, GL_SPECULAR, GOLD_SPECULAR);
  170. glMaterialf (GL_FRONT, GL_SHININESS, 128);
  171. glMaterialfv(GL_BACK, GL_AMBIENT, SILVER_AMBIENT);
  172. glMaterialfv(GL_BACK, GL_DIFFUSE, FAST_GREEN_DIFFUSE );
  173. glMaterialfv(GL_BACK, GL_SPECULAR, SILVER_SPECULAR);
  174. glMaterialf (GL_BACK, GL_SHININESS, 128);
  175. igl::opengl2::draw_mesh(s.V,s.F,s.N);
  176. glDisable(GL_LIGHTING);
  177. glPushMatrix();
  178. glLineWidth(1.0);
  179. glColor3f(0,0,0);
  180. glTranslated( s.Vmid(0), s.Vmid(1), s.Vmid(2));
  181. glScaled(
  182. (s.Vmax(0)-s.Vmin(0)),
  183. (s.Vmax(1)-s.Vmin(1)),
  184. (s.Vmax(2)-s.Vmin(2)));
  185. glutWireCube(1.0);
  186. glPopMatrix();
  187. pop_object();
  188. glDisable(GL_LIGHTING);
  189. glPushMatrix();
  190. glLineWidth(2.0);
  191. glColor3f(1,0,0);
  192. //glTranslated(s.Vmid(0), s.Vmid(1), s.Vmid(2));
  193. glScaled(
  194. 2*(s.Vmax(0)-s.Vmin(0))/s.bbd,
  195. 2*(s.Vmax(1)-s.Vmin(1))/s.bbd,
  196. 2*(s.Vmax(2)-s.Vmin(2))/s.bbd);
  197. glutWireCube(1.0);
  198. glPopMatrix();
  199. //glPushMatrix();
  200. //glRotated(90,1,0,0);
  201. //glColor3f(0.5,0.5,0.5);
  202. //glutWireSphere(1.0,20,10);
  203. //glPopMatrix();
  204. glEnable(GL_LIGHTING);
  205. glPushMatrix();
  206. glTranslated(0,-1,0);
  207. igl::opengl2::draw_floor();
  208. glPopMatrix();
  209. pop_scene();
  210. glutSwapBuffers();
  211. }
  212. void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
  213. {
  214. using namespace std;
  215. using namespace Eigen;
  216. using namespace igl;
  217. switch(glutState)
  218. {
  219. case 1:
  220. // up
  221. glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
  222. trackball_on = false;
  223. break;
  224. case 0:
  225. push_undo();
  226. glutSetCursor(GLUT_CURSOR_CYCLE);
  227. // collect information for trackball
  228. trackball_on = true;
  229. down_rot = s.rot;
  230. down_x = mouse_x;
  231. down_y = mouse_y;
  232. break;
  233. }
  234. glutPostRedisplay();
  235. }
  236. void mouse_drag(int mouse_x, int mouse_y)
  237. {
  238. using namespace igl;
  239. if(trackball_on)
  240. {
  241. glutSetCursor(GLUT_CURSOR_CYCLE);
  242. // Rotate according to trackball
  243. trackball<double>(
  244. width,
  245. height,
  246. 2.0,
  247. down_rot.coeffs().data(),
  248. down_x,
  249. down_y,
  250. mouse_x,
  251. mouse_y,
  252. s.rot.coeffs().data());
  253. }
  254. glutPostRedisplay();
  255. }
  256. // Bake rotation and scale into V
  257. void bake()
  258. {
  259. s.V.col(0).array() -= s.Vmid(0);
  260. s.V.col(1).array() -= s.Vmid(1);
  261. s.V.col(2).array() -= s.Vmid(2);
  262. s.V *= 2./s.bbd;
  263. s.V = (s.V * s.rot.matrix().transpose()).eval();
  264. }
  265. void init_relative()
  266. {
  267. using namespace Eigen;
  268. using namespace igl;
  269. per_face_normals(s.V,s.F,s.N);
  270. s.rot = Quaterniond(1,0,0,0);
  271. s.Vmax = s.V.colwise().maxCoeff();
  272. s.Vmin = s.V.colwise().minCoeff();
  273. s.Vmid = 0.5*(s.Vmax + s.Vmin);
  274. s.bbd = (s.Vmax-s.Vmin).norm();
  275. }
  276. bool save()
  277. {
  278. using namespace std;
  279. using namespace igl;
  280. using namespace Eigen;
  281. if(write_triangle_mesh(out_filename,s.V,s.F))
  282. {
  283. cout<<GREENGIN("Current baked model written to "+out_filename+".")<<endl;
  284. return true;
  285. }else
  286. {
  287. cout<<REDRUM("Current baked model failed to write to "+out_filename+".")<<endl;
  288. return false;
  289. }
  290. }
  291. void undo()
  292. {
  293. using namespace std;
  294. if(!undo_stack.empty())
  295. {
  296. redo_stack.push(s);
  297. s = undo_stack.top();
  298. undo_stack.pop();
  299. }
  300. }
  301. void redo()
  302. {
  303. using namespace std;
  304. if(!redo_stack.empty())
  305. {
  306. undo_stack.push(s);
  307. s = redo_stack.top();
  308. redo_stack.pop();
  309. }
  310. }
  311. void key(unsigned char key, int mouse_x, int mouse_y)
  312. {
  313. using namespace std;
  314. const int mod = glutGetModifiers();
  315. const bool command_down = GLUT_ACTIVE_COMMAND & mod;
  316. const bool shift_down = GLUT_ACTIVE_SHIFT & mod;
  317. switch(key)
  318. {
  319. // Ctrl-c and esc exit
  320. case char(3):
  321. case char(27):
  322. exit(0);
  323. case 'B':
  324. case 'b':
  325. push_undo();
  326. bake();
  327. init_relative();
  328. break;
  329. case 'I':
  330. case 'i':
  331. push_undo();
  332. s.F = s.F.rowwise().reverse().eval();
  333. break;
  334. case 'R':
  335. case 'r':
  336. push_undo();
  337. init_relative();
  338. break;
  339. case 'S':
  340. case 's':
  341. save();
  342. break;
  343. case 'z':
  344. case 'Z':
  345. if(command_down)
  346. {
  347. if(shift_down)
  348. {
  349. redo();
  350. }else
  351. {
  352. undo();
  353. }
  354. break;
  355. }else
  356. {
  357. push_undo();
  358. igl::snap_to_canonical_view_quat<double>(
  359. s.rot.coeffs().data(),
  360. 1.0,
  361. s.rot.coeffs().data());
  362. break;
  363. }
  364. default:
  365. cout<<"Unknown key command: '"<<key<<"' ("<<int(key)<<")"<<endl;
  366. }
  367. glutPostRedisplay();
  368. }
  369. int main(int argc, char * argv[])
  370. {
  371. using namespace std;
  372. using namespace Eigen;
  373. using namespace igl;
  374. if(argc < 3)
  375. {
  376. cerr<<"Usage:"<<endl<<" ./upright input.obj output.obj"<<endl;
  377. return 1;
  378. }
  379. // print key commands
  380. cout<<"[Click] and [drag] Rotate model using trackball."<<endl;
  381. cout<<"[Z,z] Snap rotation to canonical view."<<endl;
  382. cout<<"[B,b] \"Bake\" current scale, shift, and rotation "
  383. "into model."<<endl;
  384. cout<<"[⌘ Z] Undo."<<endl;
  385. cout<<"[⇧ ⌘ Z] Redo."<<endl;
  386. cout<<"[R,r] Reset rotation."<<endl;
  387. cout<<"[S,s] Save to output model path."<<endl;
  388. cout<<"[I,i] Flip orientation of faces (gold front, green "
  389. "back)."<<endl;
  390. cout<<"[^C,ESC] Exit."<<endl;
  391. // Read and prepare mesh
  392. string filename = argv[1];
  393. out_filename = argv[2];
  394. if(!read_triangle_mesh(filename,s.V,s.F))
  395. {
  396. cout<<"Failed to read from "<<filename<<"."<<endl;
  397. return EXIT_FAILURE;
  398. }
  399. init_relative();
  400. // Init glut
  401. glutInit(&argc,argv);
  402. glutInitDisplayString( "rgba depth double samples>=8 ");
  403. glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT));
  404. glutCreateWindow("upright");
  405. glutDisplayFunc(display);
  406. glutReshapeFunc(reshape);
  407. glutKeyboardFunc(key);
  408. glutMouseFunc(mouse);
  409. glutMotionFunc(mouse_drag);
  410. //glutPassiveMotionFunc(mouse_move);
  411. glutMainLoop();
  412. return EXIT_SUCCESS;
  413. }