example.cpp 13 KB

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