example.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. #include <igl/Camera.h>
  2. #include <igl/OpenGL_convenience.h>
  3. #include <igl/STR.h>
  4. #include <igl/barycenter.h>
  5. #include <igl/draw_floor.h>
  6. #include <igl/draw_mesh.h>
  7. #include <igl/get_seconds.h>
  8. #include <igl/jet.h>
  9. #include <igl/list_to_matrix.h>
  10. #include <igl/material_colors.h>
  11. #include <igl/matlab_format.h>
  12. #include <igl/normalize_row_lengths.h>
  13. #include <igl/pathinfo.h>
  14. #include <igl/per_face_normals.h>
  15. #include <igl/per_vertex_normals.h>
  16. #include <igl/polygon_mesh_to_triangle_mesh.h>
  17. #include <igl/quat_to_mat.h>
  18. #include <igl/readDMAT.h>
  19. #include <igl/readMESH.h>
  20. #include <igl/readOBJ.h>
  21. #include <igl/readOFF.h>
  22. #include <igl/readWRL.h>
  23. #include <igl/report_gl_error.h>
  24. #include <igl/snap_to_canonical_view_quat.h>
  25. #include <igl/snap_to_fixed_up.h>
  26. #include <igl/texture_from_tga.h>
  27. #include <igl/trackball.h>
  28. #include <igl/two_axis_valuator_fixed_up.h>
  29. #include <igl/anttweakbar/ReAntTweakBar.h>
  30. #include <igl/png/texture_from_file.h>
  31. #include <YImage.hpp>
  32. #ifdef __APPLE__
  33. # include <GLUT/glut.h>
  34. #else
  35. # include <GL/glut.h>
  36. #endif
  37. #include <Eigen/Core>
  38. #include <vector>
  39. #include <iostream>
  40. #include <algorithm>
  41. struct State
  42. {
  43. igl::Camera camera;
  44. } s;
  45. enum RotationType
  46. {
  47. ROTATION_TYPE_IGL_TRACKBALL = 0,
  48. ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1,
  49. NUM_ROTATION_TYPES = 2,
  50. } rotation_type;
  51. bool is_rotating = false;
  52. int down_x,down_y;
  53. igl::Camera down_camera;
  54. bool is_animating = false;
  55. double animation_start_time = 0;
  56. double ANIMATION_DURATION = 0.5;
  57. Eigen::Quaterniond animation_from_quat;
  58. Eigen::Quaterniond animation_to_quat;
  59. // Use vector for range-based `for`
  60. std::vector<State> undo_stack;
  61. std::vector<State> redo_stack;
  62. void push_undo()
  63. {
  64. undo_stack.push_back(s);
  65. // Clear
  66. redo_stack = std::vector<State>();
  67. }
  68. void undo()
  69. {
  70. using namespace std;
  71. if(!undo_stack.empty())
  72. {
  73. redo_stack.push_back(s);
  74. s = undo_stack.front();
  75. undo_stack.pop_back();
  76. }
  77. }
  78. void redo()
  79. {
  80. using namespace std;
  81. if(!redo_stack.empty())
  82. {
  83. undo_stack.push_back(s);
  84. s = redo_stack.front();
  85. redo_stack.pop_back();
  86. }
  87. }
  88. void TW_CALL set_rotation_type(const void * value, void * clientData)
  89. {
  90. using namespace Eigen;
  91. using namespace std;
  92. using namespace igl;
  93. const RotationType old_rotation_type = rotation_type;
  94. rotation_type = *(const RotationType *)(value);
  95. if(rotation_type == ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP &&
  96. old_rotation_type != ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP)
  97. {
  98. push_undo();
  99. animation_from_quat = s.camera.m_rotation_conj;
  100. snap_to_fixed_up(animation_from_quat,animation_to_quat);
  101. // start animation
  102. animation_start_time = get_seconds();
  103. is_animating = true;
  104. }
  105. }
  106. void TW_CALL get_rotation_type(void * value, void *clientData)
  107. {
  108. RotationType * rt = (RotationType *)(value);
  109. *rt = rotation_type;
  110. }
  111. // Width and height of window
  112. int width,height;
  113. // Position of light
  114. float light_pos[4] = {0.1,0.1,-0.9,0};
  115. // Vertex positions, normals, colors and centroid
  116. Eigen::MatrixXd V,N,TC,mid;
  117. Eigen::MatrixXi F,TF;
  118. GLuint tex_id = 0;
  119. int selected_col = 0;
  120. // Faces
  121. // Bounding box diagonal length
  122. double bbd;
  123. // Running ambient occlusion
  124. Eigen::VectorXd S;
  125. int tot_num_samples = 0;
  126. #define REBAR_NAME "temp.rbr"
  127. igl::anttweakbar::ReTwBar rebar; // Pointer to the tweak bar
  128. bool flip_y = false;
  129. bool rotate_xy = false;
  130. void reshape(int width,int height)
  131. {
  132. using namespace std;
  133. // Save width and height
  134. ::width = width;
  135. ::height = height;
  136. glMatrixMode(GL_PROJECTION);
  137. glLoadIdentity();
  138. glViewport(0,0,width,height);
  139. // Send the new window size to AntTweakBar
  140. TwWindowSize(width, height);
  141. // Set aspect for all cameras
  142. s.camera.m_aspect = (double)width/(double)height;
  143. for(auto & s : undo_stack)
  144. {
  145. s.camera.m_aspect = (double)width/(double)height;
  146. }
  147. for(auto & s : redo_stack)
  148. {
  149. s.camera.m_aspect = (double)width/(double)height;
  150. }
  151. }
  152. void push_scene()
  153. {
  154. using namespace igl;
  155. using namespace std;
  156. glMatrixMode(GL_PROJECTION);
  157. glPushMatrix();
  158. glLoadIdentity();
  159. auto & camera = s.camera;
  160. gluPerspective(camera.m_angle,camera.m_aspect,camera.m_near,camera.m_far);
  161. glMatrixMode(GL_MODELVIEW);
  162. glPushMatrix();
  163. glLoadIdentity();
  164. gluLookAt(
  165. camera.eye()(0), camera.eye()(1), camera.eye()(2),
  166. camera.at()(0), camera.at()(1), camera.at()(2),
  167. camera.up()(0), camera.up()(1), camera.up()(2));
  168. }
  169. void pop_scene()
  170. {
  171. glMatrixMode(GL_PROJECTION);
  172. glPopMatrix();
  173. glMatrixMode(GL_MODELVIEW);
  174. glPopMatrix();
  175. }
  176. void pop_object()
  177. {
  178. glPopMatrix();
  179. }
  180. // Scale and shift for object
  181. void push_object()
  182. {
  183. glPushMatrix();
  184. glScaled(2./bbd,2./bbd,2./bbd);
  185. glTranslated(-mid(0,0),-mid(0,1),-mid(0,2));
  186. }
  187. // Set up double-sided lights
  188. void lights()
  189. {
  190. using namespace std;
  191. glEnable(GL_LIGHTING);
  192. glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
  193. glEnable(GL_LIGHT0);
  194. glEnable(GL_LIGHT1);
  195. float amb[4];
  196. amb[0] = amb[1] = amb[2] = 0;
  197. amb[3] = 1.0;
  198. float diff[4] = {0.0,0.0,0.0,0.0};
  199. diff[0] = diff[1] = diff[2] = (1.0 - 0/0.4);;
  200. diff[3] = 1.0;
  201. float zeros[4] = {0.0,0.0,0.0,0.0};
  202. float pos[4];
  203. copy(light_pos,light_pos+4,pos);
  204. glLightfv(GL_LIGHT0,GL_AMBIENT,amb);
  205. glLightfv(GL_LIGHT0,GL_DIFFUSE,diff);
  206. glLightfv(GL_LIGHT0,GL_SPECULAR,zeros);
  207. glLightfv(GL_LIGHT0,GL_POSITION,pos);
  208. pos[0] *= -1;
  209. pos[1] *= -1;
  210. pos[2] *= -1;
  211. glLightfv(GL_LIGHT1,GL_AMBIENT,amb);
  212. glLightfv(GL_LIGHT1,GL_DIFFUSE,diff);
  213. glLightfv(GL_LIGHT1,GL_SPECULAR,zeros);
  214. glLightfv(GL_LIGHT1,GL_POSITION,pos);
  215. }
  216. const float back[4] = {30.0/255.0,30.0/255.0,50.0/255.0,0};
  217. void display()
  218. {
  219. using namespace Eigen;
  220. using namespace igl;
  221. using namespace std;
  222. glClearColor(back[0],back[1],back[2],0);
  223. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  224. if(is_animating)
  225. {
  226. double t = (get_seconds() - animation_start_time)/ANIMATION_DURATION;
  227. if(t > 1)
  228. {
  229. t = 1;
  230. is_animating = false;
  231. }
  232. const Quaterniond q = animation_from_quat.slerp(t,animation_to_quat).normalized();
  233. s.camera.orbit(q.conjugate());
  234. }
  235. glDisable(GL_LIGHTING);
  236. lights();
  237. push_scene();
  238. glEnable(GL_DEPTH_TEST);
  239. glDepthFunc(GL_LESS);
  240. glEnable(GL_NORMALIZE);
  241. glEnable(GL_COLOR_MATERIAL);
  242. glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
  243. push_object();
  244. // Draw the model
  245. // Set material properties
  246. glEnable(GL_COLOR_MATERIAL);
  247. glColor3f(1,1,1);
  248. glEnable(GL_TEXTURE_2D);
  249. glBindTexture(GL_TEXTURE_2D,tex_id);
  250. MatrixXd _d;
  251. MatrixXi _i;
  252. glMatrixMode(GL_TEXTURE);
  253. glPushMatrix();
  254. glLoadIdentity();
  255. if(flip_y)
  256. {
  257. glTranslated(0,1,0);
  258. glScaled(1,-1,1);
  259. }
  260. if(rotate_xy)
  261. {
  262. glRotated(90,0,0,1);
  263. glTranslated(-1,0,0);
  264. }
  265. glMatrixMode(GL_MODELVIEW);
  266. draw_mesh(V,F,N,MatrixXi(),MatrixXd(),TC,TF,MatrixXd(),0,MatrixXi(),0);
  267. glMatrixMode(GL_TEXTURE);
  268. glPopMatrix();
  269. glMatrixMode(GL_MODELVIEW);
  270. pop_object();
  271. // Draw a nice floor
  272. glPushMatrix();
  273. const double floor_offset =
  274. -2./bbd*(V.col(1).maxCoeff()-mid(1));
  275. glTranslated(0,floor_offset,0);
  276. const float GREY[4] = {0.5,0.5,0.6,1.0};
  277. const float DARK_GREY[4] = {0.2,0.2,0.3,1.0};
  278. glEnable(GL_POLYGON_OFFSET_FILL);
  279. glPolygonOffset(-1,1);
  280. glBegin(GL_QUADS);
  281. glNormal3d(0,1,0);
  282. glTexCoord2d(0,1);
  283. glVertex3d(-1,0,1);
  284. glTexCoord2d(1,1);
  285. glVertex3d(1,0,1);
  286. glTexCoord2d(1,0);
  287. glVertex3d(1,0,-1);
  288. glTexCoord2d(0,0);
  289. glVertex3d(-1,0,-1);
  290. glEnd();
  291. glDisable(GL_POLYGON_OFFSET_FILL);
  292. glDisable(GL_TEXTURE_2D);
  293. draw_floor(GREY,DARK_GREY);
  294. glPopMatrix();
  295. pop_scene();
  296. report_gl_error();
  297. TwDraw();
  298. glutSwapBuffers();
  299. if(is_animating)
  300. {
  301. glutPostRedisplay();
  302. }
  303. }
  304. void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y)
  305. {
  306. using namespace std;
  307. using namespace igl;
  308. using namespace Eigen;
  309. GLint viewport[4];
  310. glGetIntegerv(GL_VIEWPORT,viewport);
  311. if(wheel == 0 && TwMouseMotion(mouse_x, viewport[3] - mouse_y))
  312. {
  313. static double mouse_scroll_y = 0;
  314. const double delta_y = 0.125*direction;
  315. mouse_scroll_y += delta_y;
  316. TwMouseWheel(mouse_scroll_y);
  317. return;
  318. }
  319. push_undo();
  320. auto & camera = s.camera;
  321. if(wheel==0)
  322. {
  323. // factor of zoom change
  324. double s = (1.-0.01*direction);
  325. //// FOV zoom: just widen angle. This is hardly ever appropriate.
  326. //camera.m_angle *= s;
  327. //camera.m_angle = min(max(camera.m_angle,1),89);
  328. camera.push_away(s);
  329. }else
  330. {
  331. // Dolly zoom:
  332. camera.dolly_zoom((double)direction*1.0);
  333. }
  334. }
  335. void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
  336. {
  337. using namespace std;
  338. using namespace Eigen;
  339. using namespace igl;
  340. bool tw_using = TwEventMouseButtonGLUT(glutButton,glutState,mouse_x,mouse_y);
  341. switch(glutButton)
  342. {
  343. case GLUT_RIGHT_BUTTON:
  344. case GLUT_LEFT_BUTTON:
  345. {
  346. switch(glutState)
  347. {
  348. case 1:
  349. // up
  350. glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
  351. is_rotating = false;
  352. break;
  353. case 0:
  354. // down
  355. if(!tw_using)
  356. {
  357. glutSetCursor(GLUT_CURSOR_CYCLE);
  358. // collect information for trackball
  359. is_rotating = true;
  360. down_camera = s.camera;
  361. down_x = mouse_x;
  362. down_y = mouse_y;
  363. }
  364. break;
  365. }
  366. break;
  367. }
  368. // Scroll down
  369. case 3:
  370. {
  371. mouse_wheel(0,-1,mouse_x,mouse_y);
  372. break;
  373. }
  374. // Scroll up
  375. case 4:
  376. {
  377. mouse_wheel(0,1,mouse_x,mouse_y);
  378. break;
  379. }
  380. // Scroll left
  381. case 5:
  382. {
  383. mouse_wheel(1,-1,mouse_x,mouse_y);
  384. break;
  385. }
  386. // Scroll right
  387. case 6:
  388. {
  389. mouse_wheel(1,1,mouse_x,mouse_y);
  390. break;
  391. }
  392. }
  393. glutPostRedisplay();
  394. }
  395. void mouse_drag(int mouse_x, int mouse_y)
  396. {
  397. using namespace igl;
  398. using namespace Eigen;
  399. if(is_rotating)
  400. {
  401. glutSetCursor(GLUT_CURSOR_CYCLE);
  402. Quaterniond q;
  403. auto & camera = s.camera;
  404. switch(rotation_type)
  405. {
  406. case ROTATION_TYPE_IGL_TRACKBALL:
  407. {
  408. // Rotate according to trackball
  409. igl::trackball<double>(
  410. width,
  411. height,
  412. 2.0,
  413. down_camera.m_rotation_conj.coeffs().data(),
  414. down_x,
  415. down_y,
  416. mouse_x,
  417. mouse_y,
  418. q.coeffs().data());
  419. break;
  420. }
  421. case ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP:
  422. {
  423. // Rotate according to two axis valuator with fixed up vector
  424. two_axis_valuator_fixed_up(
  425. width, height,
  426. 2.0,
  427. down_camera.m_rotation_conj,
  428. down_x, down_y, mouse_x, mouse_y,
  429. q);
  430. break;
  431. }
  432. default:
  433. break;
  434. }
  435. camera.orbit(q.conjugate());
  436. }else
  437. {
  438. TwEventMouseMotionGLUT(mouse_x, mouse_y);
  439. }
  440. glutPostRedisplay();
  441. }
  442. void key(unsigned char key, int mouse_x, int mouse_y)
  443. {
  444. using namespace std;
  445. switch(key)
  446. {
  447. // ESC
  448. case char(27):
  449. rebar.save(REBAR_NAME);
  450. // ^C
  451. case char(3):
  452. exit(0);
  453. default:
  454. if(!TwEventKeyboardGLUT(key,mouse_x,mouse_y))
  455. {
  456. cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl;
  457. }
  458. }
  459. glutPostRedisplay();
  460. }
  461. int main(int argc, char * argv[])
  462. {
  463. using namespace Eigen;
  464. using namespace igl;
  465. using namespace std;
  466. // init mesh
  467. string filename = "../shared/animal.obj";
  468. string tfilename = "../shared/animal.png";
  469. if(argc < 3)
  470. {
  471. cerr<<"Usage:"<<endl<<" ./example input.obj texture.png"<<endl;
  472. cout<<endl<<"Opening default mesh..."<<endl;
  473. }else
  474. {
  475. // Read and prepare mesh
  476. filename = argv[1];
  477. tfilename = argv[2];
  478. }
  479. vector<vector<double > > vV,vN,vTC;
  480. vector<vector<int > > vF,vTF,vFN;
  481. // Convert extension to lower case
  482. if(!igl::readOBJ(filename,vV,vTC,vN,vF,vTF,vFN))
  483. {
  484. return 1;
  485. }
  486. if(vV.size() > 0)
  487. {
  488. if(!list_to_matrix(vV,V))
  489. {
  490. cerr<<"Bad V"<<endl;
  491. return 1;
  492. }
  493. polygon_mesh_to_triangle_mesh(vF,F);
  494. }
  495. if(vTC.size() > 0)
  496. {
  497. if(!list_to_matrix(vTC,TC))
  498. {
  499. cerr<<"Bad TC"<<endl;
  500. return 1;
  501. }
  502. }
  503. if(vTF.size() > 0)
  504. {
  505. if(!list_to_matrix(vTF,TF))
  506. {
  507. cerr<<"Bad TF"<<endl;
  508. return 1;
  509. }
  510. }
  511. //if(vN.size() > 0)
  512. //{
  513. // if(!list_to_matrix(vN,N))
  514. // {
  515. // return 1;
  516. // }
  517. //}else
  518. //{
  519. per_vertex_normals(V,F,N);
  520. //}
  521. // Compute normals, centroid, colors, bounding box diagonal
  522. mid = 0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff());
  523. bbd = (V.colwise().maxCoeff() - V.colwise().minCoeff()).maxCoeff();
  524. // Init glut
  525. glutInit(&argc,argv);
  526. if( !TwInit(TW_OPENGL, NULL) )
  527. {
  528. // A fatal error occured
  529. fprintf(stderr, "AntTweakBar initialization failed: %s\n", TwGetLastError());
  530. return 1;
  531. }
  532. // Create a tweak bar
  533. rebar.TwNewBar("TweakBar");
  534. rebar.TwAddVarRW("camera_rotation", TW_TYPE_QUAT4D,
  535. s.camera.m_rotation_conj.coeffs().data(), "open readonly=true");
  536. s.camera.push_away(3);
  537. s.camera.dolly_zoom(25-s.camera.m_angle);
  538. TwType RotationTypeTW = igl::anttweakbar::ReTwDefineEnumFromString("RotationType",
  539. "igl_trackball,two-a...-fixed-up");
  540. rebar.TwAddVarCB( "rotation_type", RotationTypeTW,
  541. set_rotation_type,get_rotation_type,NULL,"keyIncr=] keyDecr=[");
  542. rebar.TwAddVarRW("flip_y", TW_TYPE_BOOLCPP, &flip_y,"key=f");
  543. rebar.TwAddVarRW("rotate_xy", TW_TYPE_BOOLCPP, &rotate_xy,"key=r");
  544. rebar.load(REBAR_NAME);
  545. glutInitDisplayString( "rgba depth double samples>=8 ");
  546. glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT));
  547. glutCreateWindow("colored-mesh");
  548. glutDisplayFunc(display);
  549. glutReshapeFunc(reshape);
  550. glutKeyboardFunc(key);
  551. glutMouseFunc(mouse);
  552. glutMotionFunc(mouse_drag);
  553. glutPassiveMotionFunc(
  554. [](int x, int y)
  555. {
  556. TwEventMouseMotionGLUT(x,y);
  557. glutPostRedisplay();
  558. });
  559. static std::function<void(int)> timer_bounce;
  560. auto timer = [] (int ms) {
  561. timer_bounce(ms);
  562. };
  563. timer_bounce = [&] (int ms) {
  564. glutTimerFunc(ms, timer, ms);
  565. glutPostRedisplay();
  566. };
  567. glutTimerFunc(500, timer, 500);
  568. // Must be called after opengl context is initialized
  569. if(!igl::png::texture_from_file(tfilename,tex_id))
  570. {
  571. return 1;
  572. }
  573. glutMainLoop();
  574. return 0;
  575. }