example.cpp 11 KB

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