main.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #include <igl/kelvinlets.h>
  2. #include <igl/opengl/glfw/Viewer.h>
  3. #include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
  4. #include <igl/opengl/glfw/imgui/ImGuiMenu.h>
  5. #include <igl/readOFF.h>
  6. #include <igl/unproject.h>
  7. #include <igl/unproject_onto_mesh.h>
  8. #include <imgui.h>
  9. #include <iostream>
  10. namespace {
  11. void ShowHelpMarker(const char* desc)
  12. {
  13. ImGui::SameLine();
  14. ImGui::TextDisabled("(?)");
  15. if (ImGui::IsItemHovered()) {
  16. ImGui::BeginTooltip();
  17. ImGui::PushTextWrapPos(450.0f);
  18. ImGui::TextUnformatted(desc);
  19. ImGui::PopTextWrapPos();
  20. ImGui::EndTooltip();
  21. }
  22. }
  23. }
  24. int main()
  25. {
  26. Eigen::MatrixXd V1, OrigV;
  27. Eigen::MatrixXi F1, OrigF;
  28. igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off", OrigV, OrigF);
  29. std::cout << "1 View original mesh\n";
  30. std::cout << "2 Switch to deformed mesh\n";
  31. V1 = OrigV;
  32. F1 = OrigF;
  33. igl::opengl::glfw::Viewer viewer;
  34. igl::opengl::glfw::imgui::ImGuiPlugin plugin;
  35. viewer.plugins.push_back(&plugin);
  36. igl::opengl::glfw::imgui::ImGuiMenu menu;
  37. plugin.widgets.push_back(&menu);
  38. auto brushRadius = 1.;
  39. auto brushType = igl::BrushType::GRAB;
  40. auto scale = 1;
  41. menu.callback_draw_custom_window = [&]() {
  42. ImGui::SetNextWindowPos(ImVec2(180.f * menu.menu_scaling(), 10),
  43. ImGuiCond_FirstUseEver);
  44. ImGui::SetNextWindowSize(ImVec2(200, 160), ImGuiCond_FirstUseEver);
  45. ImGui::Begin(
  46. "Kelvinlet Brushes", nullptr, ImGuiWindowFlags_NoSavedSettings);
  47. ImGui::InputDouble("Brush Radius", &brushRadius, 0, 0, "%.4f");
  48. ImGui::Combo("Brush type",
  49. reinterpret_cast<int*>(&brushType),
  50. "Grab\0Scale\0Twist\0Pinch\0\0");
  51. ImGui::InputInt("Falloff", &scale);
  52. ShowHelpMarker("Defines how localized the stroke is {1,2,3}");
  53. ImGui::End();
  54. };
  55. Eigen::Vector3d posStart(0, 0, 0);
  56. Eigen::Vector3d posEnd;
  57. decltype(OrigV) result;
  58. auto min_point = V1.colwise().minCoeff();
  59. auto max_point = V1.colwise().maxCoeff();
  60. // to multiply brush force proportional to size of mesh
  61. auto brush_strength = (max_point - min_point).norm();
  62. Eigen::Matrix3d twist, pinch;
  63. twist << 0, 1, -1, -1, 0, 1, 1, -1, 0; // skew-symmetric
  64. pinch << 0, 1, 1, 1, 0, 1, 1, 1, 0; // symmetric
  65. viewer.callback_key_down =
  66. [&](igl::opengl::glfw::Viewer& viewer, unsigned char key, int) {
  67. if (key == '1') {
  68. viewer.data().clear();
  69. viewer.data().set_mesh(OrigV, OrigF);
  70. viewer.core().align_camera_center(OrigV, OrigF);
  71. } else if (key == '2') {
  72. viewer.data().clear();
  73. viewer.data().set_mesh(V1, F1);
  74. viewer.core().align_camera_center(V1, F1);
  75. }
  76. return false;
  77. };
  78. viewer.callback_mouse_down =
  79. [&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool {
  80. Eigen::Vector3f bc;
  81. int fid;
  82. auto x = viewer.current_mouse_x;
  83. auto y =
  84. viewer.core().viewport(3) - static_cast<float>(viewer.current_mouse_y);
  85. if (igl::unproject_onto_mesh(Eigen::Vector2f(x, y),
  86. viewer.core().view,
  87. viewer.core().proj,
  88. viewer.core().viewport,
  89. V1,
  90. F1,
  91. fid,
  92. bc)) {
  93. posStart = igl::unproject(Eigen::Vector3f(x, y, viewer.down_mouse_z),
  94. viewer.core().view,
  95. viewer.core().proj,
  96. viewer.core().viewport)
  97. .template cast<double>();
  98. return true;
  99. }
  100. return false;
  101. };
  102. viewer.callback_mouse_move =
  103. [&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool {
  104. if (!posStart.isZero() && !posStart.hasNaN()) {
  105. posEnd = igl::unproject(
  106. Eigen::Vector3f(viewer.current_mouse_x,
  107. viewer.core().viewport[3] -
  108. static_cast<float>(viewer.current_mouse_y),
  109. viewer.down_mouse_z),
  110. viewer.core().view,
  111. viewer.core().proj,
  112. viewer.core().viewport)
  113. .template cast<double>();
  114. // exaggerate the force by a little bit
  115. Eigen::Vector3d forceVec = (posEnd - posStart) * brush_strength;
  116. int scaleFactor = forceVec.norm();
  117. if (posEnd.x() < posStart.x()) {
  118. // probably not the best way to determine direction.
  119. scaleFactor = -scaleFactor;
  120. }
  121. Eigen::Matrix3d mat;
  122. switch (brushType) {
  123. case igl::BrushType::GRAB:
  124. mat.setZero();
  125. break;
  126. case igl::BrushType::SCALE:
  127. mat = Eigen::Matrix3d::Identity() * scaleFactor;
  128. break;
  129. case igl::BrushType::TWIST:
  130. mat = twist * scaleFactor;
  131. break;
  132. case igl::BrushType::PINCH:
  133. mat = pinch * scaleFactor;
  134. break;
  135. }
  136. igl::kelvinlets(
  137. V1,
  138. posStart,
  139. forceVec,
  140. mat,
  141. igl::KelvinletParams<double>(brushRadius, scale, brushType),
  142. result);
  143. viewer.data().set_vertices(result);
  144. viewer.data().compute_normals();
  145. return true;
  146. }
  147. return false;
  148. };
  149. viewer.callback_mouse_up =
  150. [&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool {
  151. if (!posStart.isZero()) {
  152. V1 = result;
  153. posStart.setZero();
  154. return true;
  155. }
  156. return false;
  157. };
  158. viewer.data().set_mesh(V1, F1);
  159. viewer.core().align_camera_center(V1, F1);
  160. viewer.launch();
  161. }