example.cpp 12 KB

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