example.cpp 11 KB

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