example.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. #include <igl/OpenGL_convenience.h>
  2. #include <igl/per_face_normals.h>
  3. #include <igl/read_triangle_mesh.h>
  4. #include <igl/normalize_row_lengths.h>
  5. #include <igl/draw_mesh.h>
  6. #include <igl/unproject.h>
  7. #include <igl/quat_to_mat.h>
  8. #include <igl/trackball.h>
  9. #include <igl/report_gl_error.h>
  10. #include <igl/canonical_quaternions.h>
  11. #include <igl/snap_to_canonical_view_quat.h>
  12. #include <igl/unproject_to_zero_plane.h>
  13. #include <igl/Camera.h>
  14. #include <igl/STR.h>
  15. #include <igl/draw_beach_ball.h>
  16. #include <igl/Viewport.h>
  17. #include <igl/project.h>
  18. #include <igl/EPS.h>
  19. #ifdef __APPLE__
  20. # include <OpenGL/gl.h>
  21. # include <OpenGL/glu.h>
  22. # include <GLUT/glut.h>
  23. #else
  24. # include <GL/glut.h>
  25. #endif
  26. #include <Eigen/Core>
  27. #include <vector>
  28. #include <iostream>
  29. #include <string>
  30. #include <algorithm>
  31. #define IGL_HEADER_ONLY
  32. #include <igl/draw_floor.h>
  33. #define NUM_VIEWPORTS 4
  34. class AugViewport : public igl::Viewport
  35. {
  36. public:
  37. igl::Camera camera;
  38. } viewports[NUM_VIEWPORTS];
  39. double horiz = 0.5;
  40. double vert = 0.5;
  41. bool horiz_on = false, vert_on = false;
  42. // Width and height of window
  43. int width,height;
  44. // information at mouse down
  45. igl::Camera down_camera;
  46. bool trackball_on = false;
  47. int down_mouse_x,down_mouse_y,move_x,move_y;
  48. // Position of light
  49. float light_pos[4] = {0.1,0.1,-0.9,0};
  50. // Vertex positions, normals, colors and centroid
  51. Eigen::MatrixXd V,N,C,mean;
  52. // Bounding box diagonal length
  53. double bbd;
  54. // Faces
  55. Eigen::MatrixXi F;
  56. Eigen::Vector3d ball;
  57. void init_viewports()
  58. {
  59. using namespace igl;
  60. using namespace std;
  61. for(auto & vp : viewports)
  62. {
  63. vp.camera.push_away(5.);
  64. }
  65. viewports[0].camera.dolly_zoom(0.-viewports[0].camera.m_angle);
  66. viewports[1].camera.dolly_zoom(0.-viewports[1].camera.m_angle);
  67. viewports[2].camera.dolly_zoom(0.-viewports[2].camera.m_angle);
  68. viewports[3].camera.dolly_zoom(25.-viewports[3].camera.m_angle);
  69. // Above view
  70. double XZ_PLANE_QUAT_D_FLIP[4];
  71. copy(XZ_PLANE_QUAT_D,XZ_PLANE_QUAT_D+4,XZ_PLANE_QUAT_D_FLIP);
  72. XZ_PLANE_QUAT_D_FLIP[0] *= -1.0;
  73. // Straight on
  74. copy(
  75. XZ_PLANE_QUAT_D_FLIP,
  76. XZ_PLANE_QUAT_D_FLIP+4,
  77. viewports[0].camera.m_rotation_conj.coeffs().data());
  78. // Left side view
  79. copy(
  80. XY_PLANE_QUAT_D,
  81. XY_PLANE_QUAT_D+4,
  82. viewports[1].camera.m_rotation_conj.coeffs().data());
  83. copy(
  84. CANONICAL_VIEW_QUAT_D[14],
  85. CANONICAL_VIEW_QUAT_D[14]+4,
  86. viewports[2].camera.m_rotation_conj.coeffs().data());
  87. // Straight on
  88. copy(
  89. XY_PLANE_QUAT_D,
  90. XY_PLANE_QUAT_D+4,
  91. viewports[3].camera.m_rotation_conj.coeffs().data());
  92. }
  93. const double BAR_THICKNESS = 3.0;
  94. void clamp(const double horiz, const double vert, double & eff_h, double & eff_v)
  95. {
  96. eff_h = horiz;
  97. eff_v = vert;
  98. const double MIN_H = (BAR_THICKNESS/2.0)/(double)height;
  99. const double MIN_V = (BAR_THICKNESS/2.0)/(double)width;
  100. eff_h = eff_h < MIN_H ? MIN_H : eff_h;
  101. eff_v = eff_v < MIN_V ? MIN_V : eff_v;
  102. eff_h = eff_h > (1.0-MIN_H) ? (1.0-MIN_H) : eff_h;
  103. eff_v = eff_v > (1.0-MIN_V) ? (1.0-MIN_V) : eff_v;
  104. }
  105. // Viewports are arranged like planar quadrants (CCW)
  106. // /-----.-----\
  107. // | 1 | 0 |
  108. // -------------
  109. // | 2 | 3 |
  110. // \-----.-----/
  111. void reshape_viewports()
  112. {
  113. // Make horiz and vert sane
  114. horiz = (horiz < 0 ? 0 : horiz);
  115. vert = (vert < 0 ? 0 : vert);
  116. horiz = (horiz > 1 ? 1 : horiz);
  117. vert = (vert > 1 ? 1 : vert);
  118. // Make effective horiz and vert even saner
  119. double eff_h,eff_v;
  120. clamp(horiz,vert,eff_h,eff_v);
  121. viewports[0].reshape(eff_v*width,eff_h*height,(1.-eff_v)*width,(1.-eff_h)*height);
  122. viewports[1].reshape( 0,eff_h*height, eff_v*width,(1.-eff_h)*height);
  123. viewports[2].reshape( 0, 0, eff_v*width,eff_h*height);
  124. viewports[3].reshape(eff_v*width, 0,(1.-eff_v)*width,eff_h*height);
  125. for(auto & vp : viewports)
  126. {
  127. vp.camera.m_aspect = (double)vp.width/(double)vp.height;
  128. }
  129. }
  130. void reshape(int width,int height)
  131. {
  132. using namespace std;
  133. // Save width and height
  134. ::width = width;
  135. ::height = height;
  136. reshape_viewports();
  137. }
  138. // Set up double-sided lights
  139. void lights()
  140. {
  141. using namespace std;
  142. glEnable(GL_LIGHTING);
  143. glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
  144. glEnable(GL_LIGHT0);
  145. glEnable(GL_LIGHT1);
  146. float ones[4] = {1.0,1.0,1.0,1.0};
  147. float zeros[4] = {0.0,0.0,0.0,0.0};
  148. float pos[4];
  149. copy(light_pos,light_pos+4,pos);
  150. glLightfv(GL_LIGHT0,GL_AMBIENT,zeros);
  151. glLightfv(GL_LIGHT0,GL_DIFFUSE,ones);
  152. glLightfv(GL_LIGHT0,GL_SPECULAR,zeros);
  153. glLightfv(GL_LIGHT0,GL_POSITION,pos);
  154. pos[0] *= -1;
  155. pos[1] *= -1;
  156. pos[2] *= -1;
  157. glLightfv(GL_LIGHT1,GL_AMBIENT,zeros);
  158. glLightfv(GL_LIGHT1,GL_DIFFUSE,ones);
  159. glLightfv(GL_LIGHT1,GL_SPECULAR,zeros);
  160. glLightfv(GL_LIGHT1,GL_POSITION,pos);
  161. }
  162. // Set up projection and model view of scene
  163. void push_scene(const AugViewport & vp)
  164. {
  165. using namespace igl;
  166. using namespace std;
  167. glMatrixMode(GL_PROJECTION);
  168. glPushMatrix();
  169. glLoadIdentity();
  170. auto & camera = vp.camera;
  171. glMultMatrixd(camera.projection().data());
  172. glMatrixMode(GL_MODELVIEW);
  173. glPushMatrix();
  174. glLoadIdentity();
  175. gluLookAt(
  176. camera.eye()(0), camera.eye()(1), camera.eye()(2),
  177. camera.at()(0), camera.at()(1), camera.at()(2),
  178. camera.up()(0), camera.up()(1), camera.up()(2));
  179. }
  180. void pop_scene()
  181. {
  182. glMatrixMode(GL_PROJECTION);
  183. glPopMatrix();
  184. glMatrixMode(GL_MODELVIEW);
  185. glPopMatrix();
  186. }
  187. // Scale and shift for object
  188. void push_object()
  189. {
  190. glPushMatrix();
  191. glScaled(2./bbd,2./bbd,2./bbd);
  192. glTranslated(-mean(0,0),-mean(0,1),-mean(0,2));
  193. }
  194. void pop_object()
  195. {
  196. glPopMatrix();
  197. }
  198. const float back[4] = {190.0/255.0,190.0/255.0,190.0/255.0,0};
  199. void display()
  200. {
  201. using namespace Eigen;
  202. using namespace igl;
  203. using namespace std;
  204. glClearColor(back[0],back[1],back[2],0);
  205. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  206. // All smooth points
  207. glEnable( GL_POINT_SMOOTH );
  208. // Flashlights
  209. lights();
  210. for(int vp = 0;vp<NUM_VIEWPORTS;vp++)
  211. {
  212. if(
  213. viewports[vp].width <= 0 ||
  214. viewports[vp].height <= 0)
  215. {
  216. continue;
  217. }
  218. glViewport(
  219. viewports[vp].x,
  220. viewports[vp].y,
  221. viewports[vp].width,
  222. viewports[vp].height);
  223. push_scene(viewports[vp]);
  224. glEnable(GL_DEPTH_TEST);
  225. glDepthFunc(GL_LEQUAL);
  226. glEnable(GL_NORMALIZE);
  227. glEnable(GL_COLOR_MATERIAL);
  228. glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
  229. push_object();
  230. // Draw the model
  231. glEnable(GL_LIGHTING);
  232. draw_mesh(V,F,N,C);
  233. pop_object();
  234. // Draw a nice floor
  235. glPushMatrix();
  236. glCullFace(GL_BACK);
  237. glEnable(GL_CULL_FACE);
  238. glEnable(GL_LIGHTING);
  239. glTranslated(0,-1,0);
  240. if(project(Vector3d(0,0,0))(2) - project(Vector3d(0,1,0))(2) > -FLOAT_EPS)
  241. {
  242. draw_floor_outline();
  243. }
  244. draw_floor();
  245. glPopMatrix();
  246. glDisable(GL_CULL_FACE);
  247. // Draw a ball at the mouse
  248. if(!horiz_on && !vert_on && !trackball_on)
  249. {
  250. glPushMatrix();
  251. glTranslated(ball(0),ball(1),ball(2));
  252. glScaled(0.1,0.1,0.1);
  253. draw_beach_ball();
  254. glPopMatrix();
  255. }
  256. pop_scene();
  257. }
  258. // Screen space
  259. glDisable(GL_DEPTH_TEST);
  260. glDisable(GL_LIGHTING);
  261. glViewport(0,0,width,height);
  262. glMatrixMode(GL_PROJECTION);
  263. glLoadIdentity();
  264. gluOrtho2D(0,width,0,height);
  265. glMatrixMode(GL_MODELVIEW);
  266. glLoadIdentity();
  267. // Display mouse position at cursor
  268. string str;
  269. void * font = GLUT_BITMAP_HELVETICA_18;
  270. glColor3f(1,0,0);
  271. glRasterPos2d(move_x+18,height-move_y+18);
  272. str = STR("("<<move_x<<","<<move_y<<")");
  273. for_each(str.begin(),str.end(),bind1st(ptr_fun(&glutBitmapCharacter),font));
  274. glColor3f(0,0,1);
  275. glRasterPos2d(move_x+18,height-move_y-1.5*18);
  276. for_each(str.begin(),str.end(),bind1st(ptr_fun(&glutBitmapCharacter),font));
  277. double eff_h,eff_v;
  278. clamp(horiz,vert,eff_h,eff_v);
  279. glLineWidth(BAR_THICKNESS);
  280. glColor3f(0.5,0.5,0.5);
  281. glBegin(GL_LINES);
  282. glVertex2f(0,eff_h*height);
  283. glVertex2f(width,eff_h*height);
  284. glVertex2f(eff_v*width,0);
  285. glVertex2f(eff_v*width,height);
  286. glEnd();
  287. glLineWidth(BAR_THICKNESS/3.0);
  288. glColor3f(0.8,0.8,0.8);
  289. glBegin(GL_LINES);
  290. glVertex2f(0,eff_h*height);
  291. glVertex2f(width,eff_h*height);
  292. glVertex2f(eff_v*width,0);
  293. glVertex2f(eff_v*width,height);
  294. glEnd();
  295. report_gl_error();
  296. glutSwapBuffers();
  297. }
  298. // Initialize colors to a boring green
  299. void init_C()
  300. {
  301. C.col(0).setConstant(0.4);
  302. C.col(1).setConstant(0.8);
  303. C.col(2).setConstant(0.3);
  304. }
  305. const double SNAP_DIST = 10;
  306. void mouse_move(int mouse_x, int mouse_y)
  307. {
  308. using namespace std;
  309. using namespace Eigen;
  310. using namespace igl;
  311. using namespace std;
  312. move_x = mouse_x;
  313. move_y = mouse_y;
  314. const int in_vp = find_if(viewports,viewports+NUM_VIEWPORTS,
  315. [&mouse_x,&mouse_y](const AugViewport & vp) -> bool
  316. {return vp.inside(mouse_x,height-mouse_y);})-viewports;
  317. if(in_vp < NUM_VIEWPORTS)
  318. {
  319. if(
  320. viewports[in_vp].width > 0 &&
  321. viewports[in_vp].height > 0)
  322. {
  323. glViewport(
  324. viewports[in_vp].x,
  325. viewports[in_vp].y,
  326. viewports[in_vp].width,
  327. viewports[in_vp].height);
  328. push_scene(viewports[in_vp]);
  329. Vector3d screen_ball(mouse_x,height-mouse_y,0);
  330. unproject_to_zero_plane(screen_ball,ball);
  331. pop_scene();
  332. }
  333. }
  334. if( (fabs((height-mouse_y) - horiz*height) < 2.*SNAP_DIST)
  335. && (fabs(mouse_x - vert*width) < 2.*SNAP_DIST))
  336. {
  337. glutSetCursor(GLUT_CURSOR_TOP_LEFT_CORNER);
  338. } else if( fabs((height-mouse_y) - horiz*height) < SNAP_DIST)
  339. {
  340. glutSetCursor(GLUT_CURSOR_UP_DOWN);
  341. } else if( fabs(mouse_x - vert*width) < SNAP_DIST)
  342. {
  343. glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
  344. } else
  345. {
  346. glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
  347. }
  348. glutPostRedisplay();
  349. }
  350. void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
  351. {
  352. using namespace std;
  353. using namespace Eigen;
  354. using namespace igl;
  355. switch(glutState)
  356. {
  357. case 1:
  358. // up
  359. glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
  360. trackball_on = false;
  361. break;
  362. case 0:
  363. horiz_on = vert_on = false;
  364. if( (fabs((height-mouse_y) - horiz*height) < 2.*SNAP_DIST)
  365. && (fabs(mouse_x - vert*width) < 2.*SNAP_DIST))
  366. {
  367. glutSetCursor(GLUT_CURSOR_TOP_LEFT_CORNER);
  368. horiz_on = vert_on = true;
  369. } else if( fabs((height-mouse_y) - horiz*height) < SNAP_DIST)
  370. {
  371. glutSetCursor(GLUT_CURSOR_UP_DOWN);
  372. horiz_on = true;
  373. } else if( fabs(mouse_x - vert*width) < SNAP_DIST)
  374. {
  375. glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
  376. vert_on = true;
  377. } else
  378. {
  379. down_vp = find_if(viewports,viewports+NUM_VIEWPORTS,
  380. [&mouse_x,&mouse_y](const AugViewport & vp) -> bool
  381. {return vp.inside(mouse_x,height-mouse_y);})-viewports;
  382. // down
  383. if(down_vp < NUM_VIEWPORTS)
  384. {
  385. glutSetCursor(GLUT_CURSOR_CYCLE);
  386. // collect information for trackball
  387. trackball_on = true;
  388. down_camera = viewports[down_vp].camera;
  389. down_mouse_x = mouse_x;
  390. down_mouse_y = mouse_y;
  391. }
  392. }
  393. break;
  394. }
  395. glutPostRedisplay();
  396. }
  397. void mouse_drag(int mouse_x, int mouse_y)
  398. {
  399. using namespace igl;
  400. mouse_move(mouse_x,mouse_y);
  401. if(horiz_on)
  402. {
  403. glutSetCursor(GLUT_CURSOR_UP_DOWN);
  404. horiz = (double)(height-mouse_y)/(double)height;
  405. reshape_viewports();
  406. }
  407. if(vert_on)
  408. {
  409. if(horiz_on)
  410. {
  411. glutSetCursor(GLUT_CURSOR_TOP_LEFT_CORNER);
  412. }else
  413. {
  414. glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
  415. }
  416. vert = (double)mouse_x/(double)width;
  417. reshape_viewports();
  418. }
  419. if(trackball_on)
  420. {
  421. glutSetCursor(GLUT_CURSOR_CYCLE);
  422. // Rotate according to trackball
  423. trackball<double>(
  424. viewports[down_vp].width,
  425. viewports[down_vp].height,
  426. 2.0,
  427. down_camera.m_rotation_conj.coeffs().data(),
  428. viewports[down_vp].mouse_x(down_mouse_x),
  429. viewports[down_vp].mouse_y(down_mouse_y,height),
  430. viewports[down_vp].mouse_x(mouse_x),
  431. viewports[down_vp].mouse_y(mouse_y,height),
  432. viewports[down_vp].camera.m_rotation_conj.coeffs().data());
  433. }
  434. glutPostRedisplay();
  435. }
  436. void key(unsigned char key, int mouse_x, int mouse_y)
  437. {
  438. using namespace std;
  439. switch(key)
  440. {
  441. // Ctrl-c and esc exit
  442. case char(3):
  443. case char(27):
  444. exit(0);
  445. case 'Z':
  446. {
  447. const int in_vp = find_if(viewports,viewports+NUM_VIEWPORTS,
  448. [&mouse_x,&mouse_y](const AugViewport & vp) -> bool
  449. {return vp.inside(mouse_x,height-mouse_y);})-viewports;
  450. if(in_vp < NUM_VIEWPORTS)
  451. {
  452. igl::snap_to_canonical_view_quat(
  453. viewports[in_vp].camera.m_rotation_conj.coeffs().data(),
  454. 1.0,
  455. viewports[in_vp].camera.m_rotation_conj.coeffs().data());
  456. }
  457. break;
  458. }
  459. default:
  460. cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl;
  461. }
  462. glutPostRedisplay();
  463. }
  464. int main(int argc, char * argv[])
  465. {
  466. using namespace Eigen;
  467. using namespace igl;
  468. using namespace std;
  469. // init mesh
  470. string filename = "/usr/local/igl/libigl/examples/shared/decimated-knight.obj" ;
  471. if(argc > 1)
  472. {
  473. filename = argv[1];
  474. }
  475. if(!read_triangle_mesh(filename,V,F))
  476. {
  477. return 1;
  478. }
  479. // Compute normals, centroid, colors, bounding box diagonal
  480. per_face_normals(V,F,N);
  481. normalize_row_lengths(N,N);
  482. mean = V.colwise().mean();
  483. C.resize(F.rows(),3);
  484. init_C();
  485. bbd =
  486. (V.colwise().maxCoeff() -
  487. V.colwise().minCoeff()).maxCoeff();
  488. // Init viewports
  489. init_viewports();
  490. // Init glut
  491. glutInit(&argc,argv);
  492. glutInitDisplayString( "rgba depth double samples>=8 ");
  493. glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT));
  494. glutCreateWindow("multi-viewport");
  495. glutDisplayFunc(display);
  496. glutReshapeFunc(reshape);
  497. glutKeyboardFunc(key);
  498. glutMouseFunc(mouse);
  499. glutMotionFunc(mouse_drag);
  500. glutPassiveMotionFunc(mouse_move);
  501. glutMainLoop();
  502. return 0;
  503. }