example.cpp 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177
  1. #include <igl/Camera.h>
  2. #include <igl/REDRUM.h>
  3. #include <igl/REDRUM.h>
  4. #include <igl/adjacency_list.h>
  5. #include <igl/adjacency_matrix.h>
  6. #include <igl/boundary_conditions.h>
  7. #include <igl/centroid.h>
  8. #include <igl/colon.h>
  9. #include <igl/draw_floor.h>
  10. #include <igl/draw_mesh.h>
  11. #include <igl/draw_skeleton_3d.h>
  12. #include <igl/draw_skeleton_vector_graphics.h>
  13. #include <igl/file_exists.h>
  14. #include <igl/forward_kinematics.h>
  15. #include <igl/get_seconds.h>
  16. #include <igl/lbs_matrix.h>
  17. #include <igl/list_to_matrix.h>
  18. #include <igl/material_colors.h>
  19. #include <igl/matlab_format.h>
  20. #include <igl/normalize_row_sums.h>
  21. #include <igl/pathinfo.h>
  22. #include <igl/per_face_normals.h>
  23. #include <igl/project.h>
  24. #include <igl/quat_to_mat.h>
  25. #include <igl/readTGF.h>
  26. #include <igl/read_triangle_mesh.h>
  27. #include <igl/remove_unreferenced.h>
  28. #include <igl/report_gl_error.h>
  29. #include <igl/right_axis.h>
  30. #include <igl/slice.h>
  31. #include <igl/snap_to_canonical_view_quat.h>
  32. #include <igl/snap_to_fixed_up.h>
  33. #include <igl/sort_triangles.h>
  34. #include <igl/trackball.h>
  35. #include <igl/two_axis_valuator_fixed_up.h>
  36. #include <igl/unique.h>
  37. #include <igl/unproject.h>
  38. #include <igl/writeOBJ.h>
  39. #include <igl/writeOFF.h>
  40. #include <igl/writeTGF.h>
  41. #include <igl/anttweakbar/ReAntTweakBar.h>
  42. #include <igl/embree/EmbreeIntersector.h>
  43. #include <igl/embree/unproject_in_mesh.h>
  44. #include <Eigen/Core>
  45. #include <Eigen/Geometry>
  46. #ifdef __APPLE__
  47. #include <GLUT/glut.h>
  48. #else
  49. #include <GL/glut.h>
  50. #endif
  51. #ifndef GLUT_WHEEL_UP
  52. #define GLUT_WHEEL_UP 3
  53. #endif
  54. #ifndef GLUT_WHEEL_DOWN
  55. #define GLUT_WHEEL_DOWN 4
  56. #endif
  57. #ifndef GLUT_WHEEL_RIGHT
  58. #define GLUT_WHEEL_RIGHT 5
  59. #endif
  60. #ifndef GLUT_WHEEL_LEFT
  61. #define GLUT_WHEEL_LEFT 6
  62. #endif
  63. #ifndef GLUT_ACTIVE_COMMAND
  64. #define GLUT_ACTIVE_COMMAND 8
  65. #endif
  66. #include <string>
  67. #include <vector>
  68. #include <queue>
  69. #include <stack>
  70. #include <iostream>
  71. enum SkelStyleType
  72. {
  73. SKEL_STYLE_TYPE_3D = 0,
  74. SKEL_STYLE_TYPE_VECTOR_GRAPHICS = 1,
  75. NUM_SKEL_STYLE_TYPE = 2
  76. }skel_style;
  77. Eigen::MatrixXd V,N,sorted_N;
  78. Eigen::Vector3d Vmid,Vcen;
  79. double bbd = 1.0;
  80. Eigen::MatrixXi F,sorted_F;
  81. Eigen::VectorXi P;
  82. igl::Camera camera;
  83. struct State
  84. {
  85. Eigen::MatrixXd C;
  86. Eigen::MatrixXi BE;
  87. Eigen::VectorXi sel;
  88. } s;
  89. bool wireframe = false;
  90. bool skeleton_on_top = false;
  91. double alpha = 0.5;
  92. // See README for descriptions
  93. enum RotationType
  94. {
  95. ROTATION_TYPE_IGL_TRACKBALL = 0,
  96. ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1,
  97. NUM_ROTATION_TYPES = 2,
  98. } rotation_type = ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP;
  99. std::stack<State> undo_stack;
  100. std::stack<State> redo_stack;
  101. bool is_rotating = false;
  102. bool is_dragging = false;
  103. bool new_leaf_on_drag = false;
  104. bool new_root_on_drag = false;
  105. int down_x,down_y;
  106. Eigen::MatrixXd down_C;
  107. igl::Camera down_camera;
  108. std::string output_filename;
  109. bool is_animating = false;
  110. double animation_start_time = 0;
  111. double ANIMATION_DURATION = 0.5;
  112. Eigen::Quaterniond animation_from_quat;
  113. Eigen::Quaterniond animation_to_quat;
  114. int width,height;
  115. Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0);
  116. #define REBAR_NAME "temp.rbr"
  117. igl::anttweakbar::ReTwBar rebar;
  118. igl::embree::EmbreeIntersector ei;
  119. void push_undo()
  120. {
  121. undo_stack.push(s);
  122. // Clear
  123. redo_stack = std::stack<State>();
  124. }
  125. // No-op setter, does nothing
  126. void TW_CALL no_op(const void * /*value*/, void * /*clientData*/)
  127. {
  128. }
  129. void TW_CALL set_rotation_type(const void * value, void * clientData)
  130. {
  131. using namespace Eigen;
  132. using namespace std;
  133. using namespace igl;
  134. const RotationType old_rotation_type = rotation_type;
  135. rotation_type = *(const RotationType *)(value);
  136. if(rotation_type == ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP &&
  137. old_rotation_type != ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP)
  138. {
  139. push_undo();
  140. animation_from_quat = camera.m_rotation_conj;
  141. snap_to_fixed_up(animation_from_quat,animation_to_quat);
  142. // start animation
  143. animation_start_time = get_seconds();
  144. is_animating = true;
  145. }
  146. }
  147. void TW_CALL get_rotation_type(void * value, void *clientData)
  148. {
  149. RotationType * rt = (RotationType *)(value);
  150. *rt = rotation_type;
  151. }
  152. void reshape(int width, int height)
  153. {
  154. ::width = width;
  155. ::height = height;
  156. glViewport(0,0,width,height);
  157. // Send the new window size to AntTweakBar
  158. TwWindowSize(width, height);
  159. camera.m_aspect = (double)width/(double)height;
  160. }
  161. void push_scene()
  162. {
  163. using namespace igl;
  164. using namespace std;
  165. glMatrixMode(GL_PROJECTION);
  166. glPushMatrix();
  167. glLoadIdentity();
  168. gluPerspective(camera.m_angle,camera.m_aspect,camera.m_near,camera.m_far);
  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 push_object()
  178. {
  179. using namespace igl;
  180. glPushMatrix();
  181. glScaled(2./bbd,2./bbd,2./bbd);
  182. glTranslated(-Vmid(0),-Vmid(1),-Vmid(2));
  183. }
  184. void pop_object()
  185. {
  186. glPopMatrix();
  187. }
  188. void pop_scene()
  189. {
  190. glMatrixMode(GL_PROJECTION);
  191. glPopMatrix();
  192. glMatrixMode(GL_MODELVIEW);
  193. glPopMatrix();
  194. }
  195. // Set up double-sided lights
  196. void lights()
  197. {
  198. using namespace std;
  199. using namespace Eigen;
  200. glEnable(GL_LIGHTING);
  201. glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
  202. glEnable(GL_LIGHT0);
  203. glEnable(GL_LIGHT1);
  204. float WHITE[4] = {0.8,0.8,0.8,1.};
  205. float GREY[4] = {0.4,0.4,0.4,1.};
  206. float BLACK[4] = {0.,0.,0.,1.};
  207. Vector4f pos = light_pos;
  208. glLightfv(GL_LIGHT0,GL_AMBIENT,GREY);
  209. glLightfv(GL_LIGHT0,GL_DIFFUSE,WHITE);
  210. glLightfv(GL_LIGHT0,GL_SPECULAR,BLACK);
  211. glLightfv(GL_LIGHT0,GL_POSITION,pos.data());
  212. pos(0) *= -1;
  213. pos(1) *= -1;
  214. pos(2) *= -1;
  215. glLightfv(GL_LIGHT1,GL_AMBIENT,GREY);
  216. glLightfv(GL_LIGHT1,GL_DIFFUSE,WHITE);
  217. glLightfv(GL_LIGHT1,GL_SPECULAR,BLACK);
  218. glLightfv(GL_LIGHT1,GL_POSITION,pos.data());
  219. }
  220. void sort()
  221. {
  222. using namespace std;
  223. using namespace Eigen;
  224. using namespace igl;
  225. push_scene();
  226. push_object();
  227. VectorXi I;
  228. sort_triangles(V,F,sorted_F,I);
  229. slice(N,I,1,sorted_N);
  230. pop_object();
  231. pop_scene();
  232. }
  233. void display()
  234. {
  235. using namespace igl;
  236. using namespace std;
  237. using namespace Eigen;
  238. const float back[4] = {0.75, 0.75, 0.75,0};
  239. glClearColor(back[0],back[1],back[2],0);
  240. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  241. static bool first = true;
  242. if(first)
  243. {
  244. sort();
  245. first = false;
  246. }
  247. if(is_animating)
  248. {
  249. double t = (get_seconds() - animation_start_time)/ANIMATION_DURATION;
  250. if(t > 1)
  251. {
  252. t = 1;
  253. is_animating = false;
  254. }
  255. Quaterniond q = animation_from_quat.slerp(t,animation_to_quat).normalized();
  256. camera.orbit(q.conjugate());
  257. }
  258. glEnable(GL_DEPTH_TEST);
  259. glDepthFunc(GL_LEQUAL);
  260. glEnable(GL_NORMALIZE);
  261. glEnable(GL_BLEND);
  262. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  263. lights();
  264. push_scene();
  265. // Draw a nice floor
  266. glEnable(GL_DEPTH_TEST);
  267. glPushMatrix();
  268. const double floor_offset =
  269. -2./bbd*(V.col(1).maxCoeff()-Vmid(1));
  270. glTranslated(0,floor_offset,0);
  271. const float GREY[4] = {0.5,0.5,0.6,1.0};
  272. const float DARK_GREY[4] = {0.2,0.2,0.3,1.0};
  273. glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
  274. glEnable(GL_CULL_FACE);
  275. glCullFace(GL_BACK);
  276. draw_floor(GREY,DARK_GREY);
  277. glDisable(GL_CULL_FACE);
  278. glPopMatrix();
  279. push_object();
  280. const auto & draw_skeleton = []()
  281. {
  282. switch(skel_style)
  283. {
  284. default:
  285. case SKEL_STYLE_TYPE_3D:
  286. {
  287. MatrixXf colors = MAYA_VIOLET.transpose().replicate(s.BE.rows(),1);
  288. for(int si=0;si<s.sel.size();si++)
  289. {
  290. for(int b=0;b<s.BE.rows();b++)
  291. {
  292. if(s.BE(b,0) == s.sel(si) || s.BE(b,1) == s.sel(si))
  293. {
  294. colors.row(b) = MAYA_SEA_GREEN;
  295. }
  296. }
  297. }
  298. draw_skeleton_3d(s.C,s.BE,MatrixXd(),colors,bbd*0.5);
  299. break;
  300. }
  301. case SKEL_STYLE_TYPE_VECTOR_GRAPHICS:
  302. draw_skeleton_vector_graphics(s.C,s.BE);
  303. break;
  304. }
  305. };
  306. if(!skeleton_on_top)
  307. {
  308. draw_skeleton();
  309. }
  310. // Set material properties
  311. glDisable(GL_COLOR_MATERIAL);
  312. glMaterialfv(GL_FRONT, GL_AMBIENT,
  313. Vector4f(GOLD_AMBIENT[0],GOLD_AMBIENT[1],GOLD_AMBIENT[2],alpha).data());
  314. glMaterialfv(GL_FRONT, GL_DIFFUSE,
  315. Vector4f(GOLD_DIFFUSE[0],GOLD_DIFFUSE[1],GOLD_DIFFUSE[2],alpha).data());
  316. glMaterialfv(GL_FRONT, GL_SPECULAR,
  317. Vector4f(GOLD_SPECULAR[0],GOLD_SPECULAR[1],GOLD_SPECULAR[2],alpha).data());
  318. glMaterialf (GL_FRONT, GL_SHININESS, 128);
  319. glMaterialfv(GL_BACK, GL_AMBIENT,
  320. Vector4f(SILVER_AMBIENT[0],SILVER_AMBIENT[1],SILVER_AMBIENT[2],alpha).data());
  321. glMaterialfv(GL_BACK, GL_DIFFUSE,
  322. Vector4f(FAST_GREEN_DIFFUSE[0],FAST_GREEN_DIFFUSE[1],FAST_GREEN_DIFFUSE[2],alpha).data());
  323. glMaterialfv(GL_BACK, GL_SPECULAR,
  324. Vector4f(SILVER_SPECULAR[0],SILVER_SPECULAR[1],SILVER_SPECULAR[2],alpha).data());
  325. glMaterialf (GL_BACK, GL_SHININESS, 128);
  326. if(wireframe)
  327. {
  328. glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
  329. }
  330. glLineWidth(1.0);
  331. draw_mesh(V,sorted_F,sorted_N);
  332. glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
  333. if(skeleton_on_top)
  334. {
  335. glDisable(GL_DEPTH_TEST);
  336. draw_skeleton();
  337. }
  338. pop_object();
  339. pop_scene();
  340. report_gl_error();
  341. TwDraw();
  342. glutSwapBuffers();
  343. if(is_animating)
  344. {
  345. glutPostRedisplay();
  346. }
  347. }
  348. void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y)
  349. {
  350. using namespace std;
  351. using namespace igl;
  352. using namespace Eigen;
  353. GLint viewport[4];
  354. glGetIntegerv(GL_VIEWPORT,viewport);
  355. if(wheel == 0 && TwMouseMotion(mouse_x, viewport[3] - mouse_y))
  356. {
  357. static double mouse_scroll_y = 0;
  358. const double delta_y = 0.125*direction;
  359. mouse_scroll_y += delta_y;
  360. TwMouseWheel(mouse_scroll_y);
  361. return;
  362. }
  363. push_undo();
  364. if(wheel==0)
  365. {
  366. // factor of zoom change
  367. double s = (1.-0.01*direction);
  368. //// FOV zoom: just widen angle. This is hardly ever appropriate.
  369. //camera.m_angle *= s;
  370. //camera.m_angle = min(max(camera.m_angle,1),89);
  371. camera.push_away(s);
  372. }else
  373. {
  374. // Dolly zoom:
  375. camera.dolly_zoom((double)direction*1.0);
  376. }
  377. glutPostRedisplay();
  378. }
  379. Eigen::VectorXi selection(const std::vector<bool> & mask)
  380. {
  381. const int count = std::count(mask.begin(),mask.end(),true);
  382. Eigen::VectorXi sel(count);
  383. int s = 0;
  384. for(int c = 0;c<(int)mask.size();c++)
  385. {
  386. if(mask[c])
  387. {
  388. sel(s) = c;
  389. s++;
  390. }
  391. }
  392. return sel;
  393. }
  394. std::vector<bool> selection_mask(const Eigen::VectorXi & sel, const int n)
  395. {
  396. std::vector<bool> mask(n,false);
  397. for(int si = 0;si<sel.size();si++)
  398. {
  399. const int i = sel(si);
  400. mask[i] = true;
  401. }
  402. return mask;
  403. }
  404. bool ss_select(
  405. const double mouse_x,
  406. const double mouse_y,
  407. const Eigen::MatrixXd & C,
  408. const bool accum,
  409. Eigen::VectorXi & sel)
  410. {
  411. using namespace igl;
  412. using namespace Eigen;
  413. using namespace std;
  414. //// zap old list
  415. //if(!accum)
  416. //{
  417. // sel.resize(0,1);
  418. //}
  419. vector<bool> old_mask = selection_mask(s.sel,s.C.rows());
  420. vector<bool> mask(old_mask.size(),false);
  421. double min_dist = 1e25;
  422. bool sel_changed = false;
  423. bool found = false;
  424. for(int c = 0;c<C.rows();c++)
  425. {
  426. const RowVector3d & Cc = C.row(c);
  427. const auto Pc = project(Cc);
  428. const double SELECTION_DIST = 18;//pixels
  429. const double dist = (Pc.head(2)-RowVector2d(mouse_x,height-mouse_y)).norm();
  430. if(dist < SELECTION_DIST && (accum || dist < min_dist))
  431. {
  432. mask[c] = true;
  433. min_dist = dist;
  434. found = true;
  435. sel_changed |= mask[c] != old_mask[c];
  436. }
  437. }
  438. for(int c = 0;c<C.rows();c++)
  439. {
  440. if(accum)
  441. {
  442. mask[c] = mask[c] ^ old_mask[c];
  443. }else
  444. {
  445. if(!sel_changed)
  446. {
  447. mask[c] = mask[c] || old_mask[c];
  448. }
  449. }
  450. }
  451. sel = selection(mask);
  452. return found;
  453. }
  454. void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
  455. {
  456. using namespace std;
  457. using namespace Eigen;
  458. using namespace igl;
  459. bool tw_using = TwEventMouseButtonGLUT(glutButton,glutState,mouse_x,mouse_y);
  460. const int mod = (glutButton <=2 ? glutGetModifiers() : 0);
  461. const bool option_down = mod & GLUT_ACTIVE_ALT;
  462. const bool shift_down = mod & GLUT_ACTIVE_SHIFT;
  463. const bool command_down = GLUT_ACTIVE_COMMAND & mod;
  464. switch(glutButton)
  465. {
  466. case GLUT_RIGHT_BUTTON:
  467. case GLUT_LEFT_BUTTON:
  468. {
  469. switch(glutState)
  470. {
  471. case 1:
  472. // up
  473. glutSetCursor(GLUT_CURSOR_INHERIT);
  474. if(is_rotating)
  475. {
  476. sort();
  477. }
  478. is_rotating = false;
  479. is_dragging = false;
  480. break;
  481. case 0:
  482. new_leaf_on_drag = false;
  483. new_root_on_drag = false;
  484. if(!tw_using)
  485. {
  486. down_x = mouse_x;
  487. down_y = mouse_y;
  488. if(option_down)
  489. {
  490. glutSetCursor(GLUT_CURSOR_CYCLE);
  491. // collect information for trackball
  492. is_rotating = true;
  493. down_camera = camera;
  494. }else
  495. {
  496. push_undo();
  497. push_scene();
  498. push_object();
  499. // Zap selection
  500. if(shift_down)
  501. {
  502. s.sel.resize(0,1);
  503. }
  504. if(ss_select(mouse_x,mouse_y,s.C,
  505. command_down && !shift_down,
  506. s.sel))
  507. {
  508. if(shift_down)
  509. {
  510. new_leaf_on_drag = true;
  511. }
  512. }else
  513. {
  514. new_root_on_drag = true;
  515. }
  516. is_dragging = !command_down;
  517. down_C = s.C;
  518. pop_object();
  519. pop_scene();
  520. }
  521. }
  522. break;
  523. }
  524. break;
  525. }
  526. // Scroll down
  527. case 3:
  528. {
  529. mouse_wheel(0,-1,mouse_x,mouse_y);
  530. break;
  531. }
  532. // Scroll up
  533. case 4:
  534. {
  535. mouse_wheel(0,1,mouse_x,mouse_y);
  536. break;
  537. }
  538. // Scroll left
  539. case 5:
  540. {
  541. mouse_wheel(1,-1,mouse_x,mouse_y);
  542. break;
  543. }
  544. // Scroll right
  545. case 6:
  546. {
  547. mouse_wheel(1,1,mouse_x,mouse_y);
  548. break;
  549. }
  550. }
  551. glutPostRedisplay();
  552. }
  553. void mouse_drag(int mouse_x, int mouse_y)
  554. {
  555. using namespace igl;
  556. using namespace std;
  557. using namespace Eigen;
  558. if(is_rotating)
  559. {
  560. glutSetCursor(GLUT_CURSOR_CYCLE);
  561. Quaterniond q;
  562. switch(rotation_type)
  563. {
  564. case ROTATION_TYPE_IGL_TRACKBALL:
  565. {
  566. // Rotate according to trackball
  567. igl::trackball<double>(
  568. width,
  569. height,
  570. 2.0,
  571. down_camera.m_rotation_conj.coeffs().data(),
  572. down_x,
  573. down_y,
  574. mouse_x,
  575. mouse_y,
  576. q.coeffs().data());
  577. break;
  578. }
  579. case ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP:
  580. {
  581. // Rotate according to two axis valuator with fixed up vector
  582. two_axis_valuator_fixed_up(
  583. width, height,
  584. 2.0,
  585. down_camera.m_rotation_conj,
  586. down_x, down_y, mouse_x, mouse_y,
  587. q);
  588. break;
  589. }
  590. default:
  591. break;
  592. }
  593. camera.orbit(q.conjugate());
  594. }
  595. if(is_dragging)
  596. {
  597. push_scene();
  598. push_object();
  599. if(new_leaf_on_drag)
  600. {
  601. assert(s.C.size() >= 1);
  602. // one new node
  603. s.C.conservativeResize(s.C.rows()+1,3);
  604. const int nc = s.C.rows();
  605. assert(s.sel.size() >= 1);
  606. s.C.row(nc-1) = s.C.row(s.sel(0));
  607. // one new bone
  608. s.BE.conservativeResize(s.BE.rows()+1,2);
  609. s.BE.row(s.BE.rows()-1) = RowVector2i(s.sel(0),nc-1);
  610. // select just last node
  611. s.sel.resize(1,1);
  612. s.sel(0) = nc-1;
  613. // reset down_C
  614. down_C = s.C;
  615. new_leaf_on_drag = false;
  616. }
  617. if(new_root_on_drag)
  618. {
  619. // two new nodes
  620. s.C.conservativeResize(s.C.rows()+2,3);
  621. const int nc = s.C.rows();
  622. Vector3d obj;
  623. int nhits = igl::embree::unproject_in_mesh(mouse_x,height-mouse_y,ei,obj);
  624. if(nhits == 0)
  625. {
  626. Vector3d pV_mid = project(Vcen);
  627. obj = unproject(Vector3d(mouse_x,height-mouse_y,pV_mid(2)));
  628. }
  629. s.C.row(nc-2) = obj;
  630. s.C.row(nc-1) = obj;
  631. // select last node
  632. s.sel.resize(1,1);
  633. s.sel(0) = nc-1;
  634. // one new bone
  635. s.BE.conservativeResize(s.BE.rows()+1,2);
  636. s.BE.row(s.BE.rows()-1) = RowVector2i(nc-2,nc-1);
  637. // reset down_C
  638. down_C = s.C;
  639. new_root_on_drag = false;
  640. }
  641. double z = 0;
  642. Vector3d obj,win;
  643. int nhits = igl::embree::unproject_in_mesh(mouse_x,height-mouse_y,ei,obj);
  644. project(obj,win);
  645. z = win(2);
  646. for(int si = 0;si<s.sel.size();si++)
  647. {
  648. const int c = s.sel(si);
  649. Vector3d pc = project((RowVector3d) down_C.row(c));
  650. pc(0) += mouse_x-down_x;
  651. pc(1) += (height-mouse_y)-(height-down_y);
  652. if(nhits > 0)
  653. {
  654. pc(2) = z;
  655. }
  656. s.C.row(c) = unproject(pc);
  657. }
  658. pop_object();
  659. pop_scene();
  660. }
  661. glutPostRedisplay();
  662. }
  663. void init_relative()
  664. {
  665. using namespace Eigen;
  666. using namespace igl;
  667. using namespace std;
  668. per_face_normals(V,F,N);
  669. const auto Vmax = V.colwise().maxCoeff();
  670. const auto Vmin = V.colwise().minCoeff();
  671. Vmid = 0.5*(Vmax + Vmin);
  672. centroid(V,F,Vcen);
  673. bbd = (Vmax-Vmin).norm();
  674. cout<<"bbd: "<<bbd<<endl;
  675. camera.push_away(2);
  676. }
  677. void undo()
  678. {
  679. using namespace std;
  680. if(!undo_stack.empty())
  681. {
  682. redo_stack.push(s);
  683. s = undo_stack.top();
  684. undo_stack.pop();
  685. }
  686. }
  687. void redo()
  688. {
  689. using namespace std;
  690. if(!redo_stack.empty())
  691. {
  692. undo_stack.push(s);
  693. s = redo_stack.top();
  694. redo_stack.pop();
  695. }
  696. }
  697. void symmetrize()
  698. {
  699. using namespace std;
  700. using namespace igl;
  701. using namespace Eigen;
  702. if(s.sel.size() == 0)
  703. {
  704. cout<<YELLOWGIN("Make a selection first.")<<endl;
  705. return;
  706. }
  707. push_undo();
  708. push_scene();
  709. push_object();
  710. Vector3d right;
  711. right_axis(right.data(),right.data()+1,right.data()+2);
  712. right.normalize();
  713. MatrixXd RC(s.C.rows(),s.C.cols());
  714. MatrixXd old_C = s.C;
  715. for(int c = 0;c<s.C.rows();c++)
  716. {
  717. const Vector3d Cc = s.C.row(c);
  718. const auto A = Cc-Vcen;
  719. const auto A1 = A.dot(right) * right;
  720. const auto A2 = A-A1;
  721. RC.row(c) = Vcen + A2 - A1;
  722. }
  723. vector<bool> mask = selection_mask(s.sel,s.C.rows());
  724. // stupid O(n²) matching
  725. for(int c = 0;c<s.C.rows();c++)
  726. {
  727. // not selected
  728. if(!mask[c])
  729. {
  730. continue;
  731. }
  732. const Vector3d Cc = s.C.row(c);
  733. int min_r = -1;
  734. double min_dist = 1e25;
  735. double max_dist = 0.1*bbd;
  736. for(int r= 0;r<RC.rows();r++)
  737. {
  738. const Vector3d RCr = RC.row(r);
  739. const double dist = (Cc-RCr).norm();
  740. if(
  741. dist<min_dist && // closest
  742. dist<max_dist && // not too far away
  743. (c==r || (Cc-Vcen).dot(right)*(RCr-Vcen).dot(right) > 0) // on same side
  744. )
  745. {
  746. min_dist = dist;
  747. min_r = r;
  748. }
  749. }
  750. if(min_r>=0)
  751. {
  752. if(mask[min_r])
  753. {
  754. s.C.row(c) = 0.5*(Cc.transpose()+RC.row(min_r));
  755. }else
  756. {
  757. s.C.row(c) = RC.row(min_r);
  758. }
  759. }
  760. }
  761. pop_object();
  762. pop_scene();
  763. }
  764. bool save()
  765. {
  766. using namespace std;
  767. using namespace igl;
  768. if(writeTGF(output_filename,s.C,s.BE))
  769. {
  770. cout<<GREENGIN("Current skeleton written to "+output_filename+".")<<endl;
  771. return true;
  772. }else
  773. {
  774. cout<<REDRUM("Writing to "+output_filename+" failed.")<<endl;
  775. return false;
  776. }
  777. }
  778. void key(unsigned char key, int mouse_x, int mouse_y)
  779. {
  780. using namespace std;
  781. using namespace igl;
  782. using namespace Eigen;
  783. int mod = glutGetModifiers();
  784. const bool command_down = GLUT_ACTIVE_COMMAND & mod;
  785. const bool shift_down = GLUT_ACTIVE_SHIFT & mod;
  786. switch(key)
  787. {
  788. // ESC
  789. case char(27):
  790. rebar.save(REBAR_NAME);
  791. // ^C
  792. case char(3):
  793. exit(0);
  794. case char(127):
  795. {
  796. push_undo();
  797. // delete
  798. MatrixXi new_BE(s.BE.rows(),s.BE.cols());
  799. int count = 0;
  800. for(int b=0;b<s.BE.rows();b++)
  801. {
  802. bool selected = false;
  803. for(int si=0;si<s.sel.size();si++)
  804. {
  805. if(s.BE(b,0) == s.sel(si) || s.BE(b,1) == s.sel(si))
  806. {
  807. selected = true;
  808. break;
  809. }
  810. }
  811. if(!selected)
  812. {
  813. new_BE.row(count) = s.BE.row(b);
  814. count++;
  815. }
  816. }
  817. new_BE.conservativeResize(count,new_BE.cols());
  818. const auto old_C = s.C;
  819. VectorXi I;
  820. remove_unreferenced(old_C,new_BE,s.C,s.BE,I);
  821. s.sel.resize(0,1);
  822. break;
  823. }
  824. case 'A':
  825. case 'a':
  826. {
  827. push_undo();
  828. s.sel = colon<int>(0,s.C.rows()-1);
  829. break;
  830. }
  831. case 'C':
  832. case 'c':
  833. {
  834. push_undo();
  835. // snap close vertices
  836. SparseMatrix<double> A;
  837. adjacency_matrix(s.BE,A);
  838. VectorXi J = colon<int>(0,s.C.rows()-1);
  839. // stupid O(n²) version
  840. for(int c = 0;c<s.C.rows();c++)
  841. {
  842. for(int d = c+1;d<s.C.rows();d++)
  843. {
  844. if(
  845. A.coeff(c,d) == 0 && // no edge
  846. (s.C.row(c)-s.C.row(d)).norm() < 0.02*bbd //close
  847. )
  848. {
  849. // c < d
  850. J(d) = c;
  851. }
  852. }
  853. }
  854. for(int e = 0;e<s.BE.rows();e++)
  855. {
  856. s.BE(e,0) = J(s.BE(e,0));
  857. s.BE(e,1) = J(s.BE(e,1));
  858. }
  859. const auto old_BE = s.BE;
  860. const auto old_C = s.C;
  861. VectorXi I;
  862. remove_unreferenced(old_C,old_BE,s.C,s.BE,I);
  863. for(int i = 0;i<s.sel.size();i++)
  864. {
  865. s.sel(i) = J(s.sel(i));
  866. }
  867. VectorXi _;
  868. igl::unique(s.sel,s.sel,_,_);
  869. break;
  870. }
  871. case 'D':
  872. case 'd':
  873. {
  874. push_undo();
  875. s.sel.resize(0,1);
  876. break;
  877. }
  878. case 'P':
  879. case 'p':
  880. {
  881. // add bone to parents (should really only be one)
  882. push_undo();
  883. vector<int> new_sel;
  884. const int old_nbe = s.BE.rows();
  885. for(int si=0;si<s.sel.size();si++)
  886. {
  887. for(int b=0;b<old_nbe;b++)
  888. {
  889. if(s.BE(b,1) == s.sel(si))
  890. {
  891. // one new node
  892. s.C.conservativeResize(s.C.rows()+1,3);
  893. const int nc = s.C.rows();
  894. s.C.row(nc-1) = 0.5*(s.C.row(s.BE(b,1)) + s.C.row(s.BE(b,0)));
  895. // one new bone
  896. s.BE.conservativeResize(s.BE.rows()+1,2);
  897. s.BE.row(s.BE.rows()-1) = RowVector2i(nc-1,s.BE(b,1));
  898. s.BE(b,1) = nc-1;
  899. // select last node
  900. new_sel.push_back(nc-1);
  901. }
  902. }
  903. }
  904. list_to_matrix(new_sel,s.sel);
  905. break;
  906. }
  907. case 'R':
  908. case 'r':
  909. {
  910. // re-root try at first selected
  911. if(s.sel.size() > 0)
  912. {
  913. push_undo();
  914. // only use first
  915. s.sel.conservativeResize(1,1);
  916. // Ideally this should only effect the connected component of s.sel(0)
  917. const auto & C = s.C;
  918. auto & BE = s.BE;
  919. vector<bool> seen(C.rows(),false);
  920. // adjacency list
  921. vector<vector< int> > A;
  922. adjacency_list(BE,A,false);
  923. int e = 0;
  924. queue<int> Q;
  925. Q.push(s.sel(0));
  926. seen[s.sel(0)] = true;
  927. while(!Q.empty())
  928. {
  929. const int c = Q.front();
  930. Q.pop();
  931. for(const auto & d : A[c])
  932. {
  933. if(!seen[d])
  934. {
  935. BE(e,0) = c;
  936. BE(e,1) = d;
  937. e++;
  938. Q.push(d);
  939. seen[d] = true;
  940. }
  941. }
  942. }
  943. // only keep tree
  944. BE.conservativeResize(e,BE.cols());
  945. }
  946. break;
  947. }
  948. case 'S':
  949. case 's':
  950. {
  951. save();
  952. break;
  953. }
  954. case 'U':
  955. case 'u':
  956. {
  957. push_scene();
  958. push_object();
  959. for(int c = 0;c<s.C.rows();c++)
  960. {
  961. Vector3d P = project((Vector3d)s.C.row(c));
  962. Vector3d obj;
  963. int nhits = igl::embree::unproject_in_mesh(P(0),P(1),ei,obj);
  964. if(nhits > 0)
  965. {
  966. s.C.row(c) = obj;
  967. }
  968. }
  969. pop_object();
  970. pop_scene();
  971. break;
  972. }
  973. case 'Y':
  974. case 'y':
  975. {
  976. symmetrize();
  977. break;
  978. }
  979. case 'z':
  980. case 'Z':
  981. is_rotating = false;
  982. is_dragging = false;
  983. if(command_down)
  984. {
  985. if(shift_down)
  986. {
  987. redo();
  988. }else
  989. {
  990. undo();
  991. }
  992. break;
  993. }else
  994. {
  995. push_undo();
  996. Quaterniond q;
  997. snap_to_canonical_view_quat(camera.m_rotation_conj,1.0,q);
  998. camera.orbit(q.conjugate());
  999. }
  1000. break;
  1001. default:
  1002. if(!TwEventKeyboardGLUT(key,mouse_x,mouse_y))
  1003. {
  1004. cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl;
  1005. }
  1006. }
  1007. glutPostRedisplay();
  1008. }
  1009. int main(int argc, char * argv[])
  1010. {
  1011. using namespace std;
  1012. using namespace Eigen;
  1013. using namespace igl;
  1014. string filename = "../shared/decimated-knight.obj";
  1015. string skel_filename = "";
  1016. output_filename = "";
  1017. switch(argc)
  1018. {
  1019. case 4:
  1020. output_filename = argv[3];
  1021. //fall through
  1022. case 3:
  1023. skel_filename = argv[2];
  1024. if(output_filename.size() == 0)
  1025. {
  1026. output_filename = skel_filename;
  1027. }
  1028. //fall through
  1029. case 2:
  1030. // Read and prepare mesh
  1031. filename = argv[1];
  1032. break;
  1033. default:
  1034. cerr<<"Usage:"<<endl<<" ./example input.obj [input/output.tgf]"<<endl;
  1035. cout<<endl<<"Opening default mesh..."<<endl;
  1036. }
  1037. // print key commands
  1038. cout<<"[Click] and [drag] Create bone (or select node) and reposition."<<endl;
  1039. cout<<"⇧ +[Click] and [drag] Select node (or create one) and _pull out_ new bone."<<endl;
  1040. cout<<"⌥ +[Click] and [drag] Rotate secene."<<endl;
  1041. cout<<"⌫ Delete selected node(s) and incident bones."<<endl;
  1042. cout<<"A,a Select all."<<endl;
  1043. cout<<"D,d Deselect all."<<endl;
  1044. cout<<"C,c Snap close nodes."<<endl;
  1045. cout<<"P,p Split \"parent\" bone(s) of selection by creating new node(s)."<<endl;
  1046. cout<<"R,r Breadth first search at selection to redirect skeleton into tree."<<endl;
  1047. cout<<"S,s Save current skeleton to output .tgf file."<<endl;
  1048. cout<<"U,u Project then unproject inside mesh (as if dragging each by ε)."<<endl;
  1049. cout<<"Y,Y Symmetrize selection over plane through object centroid and right vector."<<endl;
  1050. cout<<"Z,z Snap to canonical view."<<endl;
  1051. cout<<"⌘ Z Undo."<<endl;
  1052. cout<<"⇧ ⌘ Z Redo."<<endl;
  1053. cout<<"^C,ESC Exit (without saving)."<<endl;
  1054. string dir,_1,_2,name;
  1055. read_triangle_mesh(filename,V,F,dir,_1,_2,name);
  1056. if(output_filename.size() == 0)
  1057. {
  1058. output_filename = dir+"/"+name+".tgf";
  1059. }
  1060. if(file_exists(output_filename.c_str()))
  1061. {
  1062. cout<<YELLOWGIN("Output set to overwrite "<<output_filename)<<endl;
  1063. }else
  1064. {
  1065. cout<<BLUEGIN("Output set to "<<output_filename)<<endl;
  1066. }
  1067. if(skel_filename.length() > 0)
  1068. {
  1069. readTGF(skel_filename,s.C,s.BE);
  1070. }
  1071. init_relative();
  1072. ei.init(V.cast<float>(),F.cast<int>());
  1073. // Init glut
  1074. glutInit(&argc,argv);
  1075. if( !TwInit(TW_OPENGL, NULL) )
  1076. {
  1077. // A fatal error occured
  1078. fprintf(stderr, "AntTweakBar initialization failed: %s\n", TwGetLastError());
  1079. return 1;
  1080. }
  1081. // Create a tweak bar
  1082. rebar.TwNewBar("TweakBar");
  1083. rebar.TwAddVarRW("camera_rotation", TW_TYPE_QUAT4D,
  1084. camera.m_rotation_conj.coeffs().data(), "open readonly=true");
  1085. TwType RotationTypeTW = igl::anttweakbar::ReTwDefineEnumFromString("RotationType",
  1086. "igl_trackball,two-a...-fixed-up");
  1087. rebar.TwAddVarCB( "rotation_type", RotationTypeTW,
  1088. set_rotation_type,get_rotation_type,NULL,"keyIncr=] keyDecr=[");
  1089. rebar.TwAddVarRW("skeleton_on_top", TW_TYPE_BOOLCPP,&skeleton_on_top,"key=O");
  1090. rebar.TwAddVarRW("wireframe", TW_TYPE_BOOLCPP,&wireframe,"key=l");
  1091. TwType SkelStyleTypeTW = igl::anttweakbar::ReTwDefineEnumFromString("SkelStyleType",
  1092. "3d,vector-graphics");
  1093. rebar.TwAddVarRW("style",SkelStyleTypeTW,&skel_style,"");
  1094. rebar.TwAddVarRW("alpha",TW_TYPE_DOUBLE,&alpha,
  1095. "keyIncr=} keyDecr={ min=0 max=1 step=0.1");
  1096. rebar.load(REBAR_NAME);
  1097. // Init antweakbar
  1098. glutInitDisplayString( "rgba depth double samples>=8 ");
  1099. glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)/2.0);
  1100. glutCreateWindow("skeleton-builder");
  1101. glutDisplayFunc(display);
  1102. glutReshapeFunc(reshape);
  1103. glutKeyboardFunc(key);
  1104. glutMouseFunc(mouse);
  1105. glutMotionFunc(mouse_drag);
  1106. glutPassiveMotionFunc((GLUTmousemotionfun)TwEventMouseMotionGLUT);
  1107. glutMainLoop();
  1108. return 0;
  1109. }