example.cpp 14 KB

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