example.cpp 11 KB

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