You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
5.4 KiB
176 lines
5.4 KiB
#include <igl/kelvinlets.h>
|
|
#include <igl/opengl/glfw/Viewer.h>
|
|
#include <igl/opengl/glfw/imgui/ImGuiPlugin.h>
|
|
#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
|
|
#include <igl/readOFF.h>
|
|
#include <igl/unproject.h>
|
|
#include <igl/unproject_onto_mesh.h>
|
|
#include <imgui.h>
|
|
#include <iostream>
|
|
|
|
namespace {
|
|
void ShowHelpMarker(const char* desc)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(?)");
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::BeginTooltip();
|
|
ImGui::PushTextWrapPos(450.0f);
|
|
ImGui::TextUnformatted(desc);
|
|
ImGui::PopTextWrapPos();
|
|
ImGui::EndTooltip();
|
|
}
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
Eigen::MatrixXd V1, OrigV;
|
|
Eigen::MatrixXi F1, OrigF;
|
|
|
|
igl::readOFF(TUTORIAL_SHARED_PATH "/bumpy.off", OrigV, OrigF);
|
|
std::cout << "1 View original mesh\n";
|
|
std::cout << "2 Switch to deformed mesh\n";
|
|
V1 = OrigV;
|
|
F1 = OrigF;
|
|
|
|
igl::opengl::glfw::Viewer viewer;
|
|
igl::opengl::glfw::imgui::ImGuiPlugin plugin;
|
|
viewer.plugins.push_back(&plugin);
|
|
igl::opengl::glfw::imgui::ImGuiMenu menu;
|
|
plugin.widgets.push_back(&menu);
|
|
|
|
auto brushRadius = 1.;
|
|
auto brushType = igl::BrushType::GRAB;
|
|
auto scale = 1;
|
|
menu.callback_draw_custom_window = [&]() {
|
|
ImGui::SetNextWindowPos(ImVec2(180.f * menu.menu_scaling(), 10),
|
|
ImGuiCond_FirstUseEver);
|
|
ImGui::SetNextWindowSize(ImVec2(200, 160), ImGuiCond_FirstUseEver);
|
|
ImGui::Begin(
|
|
"Kelvinlet Brushes", nullptr, ImGuiWindowFlags_NoSavedSettings);
|
|
|
|
ImGui::InputDouble("Brush Radius", &brushRadius, 0, 0, "%.4f");
|
|
ImGui::Combo("Brush type",
|
|
reinterpret_cast<int*>(&brushType),
|
|
"Grab\0Scale\0Twist\0Pinch\0\0");
|
|
ImGui::InputInt("Falloff", &scale);
|
|
ShowHelpMarker("Defines how localized the stroke is {1,2,3}");
|
|
ImGui::End();
|
|
};
|
|
|
|
Eigen::Vector3d posStart(0, 0, 0);
|
|
Eigen::Vector3d posEnd;
|
|
decltype(OrigV) result;
|
|
auto min_point = V1.colwise().minCoeff();
|
|
auto max_point = V1.colwise().maxCoeff();
|
|
// to multiply brush force proportional to size of mesh
|
|
auto brush_strength = (max_point - min_point).norm();
|
|
Eigen::Matrix3d twist, pinch;
|
|
twist << 0, 1, -1, -1, 0, 1, 1, -1, 0; // skew-symmetric
|
|
pinch << 0, 1, 1, 1, 0, 1, 1, 1, 0; // symmetric
|
|
|
|
viewer.callback_key_down =
|
|
[&](igl::opengl::glfw::Viewer& viewer, unsigned char key, int) {
|
|
if (key == '1') {
|
|
viewer.data().clear();
|
|
viewer.data().set_mesh(OrigV, OrigF);
|
|
viewer.core().align_camera_center(OrigV, OrigF);
|
|
} else if (key == '2') {
|
|
viewer.data().clear();
|
|
viewer.data().set_mesh(V1, F1);
|
|
viewer.core().align_camera_center(V1, F1);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
viewer.callback_mouse_down =
|
|
[&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool {
|
|
Eigen::Vector3f bc;
|
|
int fid;
|
|
auto x = viewer.current_mouse_x;
|
|
auto y =
|
|
viewer.core().viewport(3) - static_cast<float>(viewer.current_mouse_y);
|
|
if (igl::unproject_onto_mesh(Eigen::Vector2f(x, y),
|
|
viewer.core().view,
|
|
viewer.core().proj,
|
|
viewer.core().viewport,
|
|
V1,
|
|
F1,
|
|
fid,
|
|
bc)) {
|
|
posStart = igl::unproject(Eigen::Vector3f(x, y, viewer.down_mouse_z),
|
|
viewer.core().view,
|
|
viewer.core().proj,
|
|
viewer.core().viewport)
|
|
.template cast<double>();
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
viewer.callback_mouse_move =
|
|
[&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool {
|
|
if (!posStart.isZero() && !posStart.hasNaN()) {
|
|
posEnd = igl::unproject(
|
|
Eigen::Vector3f(viewer.current_mouse_x,
|
|
viewer.core().viewport[3] -
|
|
static_cast<float>(viewer.current_mouse_y),
|
|
viewer.down_mouse_z),
|
|
viewer.core().view,
|
|
viewer.core().proj,
|
|
viewer.core().viewport)
|
|
.template cast<double>();
|
|
|
|
// exaggerate the force by a little bit
|
|
Eigen::Vector3d forceVec = (posEnd - posStart) * brush_strength;
|
|
|
|
int scaleFactor = forceVec.norm();
|
|
if (posEnd.x() < posStart.x()) {
|
|
// probably not the best way to determine direction.
|
|
scaleFactor = -scaleFactor;
|
|
}
|
|
Eigen::Matrix3d mat;
|
|
switch (brushType) {
|
|
case igl::BrushType::GRAB:
|
|
mat.setZero();
|
|
break;
|
|
case igl::BrushType::SCALE:
|
|
mat = Eigen::Matrix3d::Identity() * scaleFactor;
|
|
break;
|
|
case igl::BrushType::TWIST:
|
|
mat = twist * scaleFactor;
|
|
break;
|
|
case igl::BrushType::PINCH:
|
|
mat = pinch * scaleFactor;
|
|
break;
|
|
}
|
|
|
|
igl::kelvinlets(
|
|
V1,
|
|
posStart,
|
|
forceVec,
|
|
mat,
|
|
igl::KelvinletParams<double>(brushRadius, scale, brushType),
|
|
result);
|
|
viewer.data().set_vertices(result);
|
|
viewer.data().compute_normals();
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
viewer.callback_mouse_up =
|
|
[&](igl::opengl::glfw::Viewer& viewer, int, int) -> bool {
|
|
if (!posStart.isZero()) {
|
|
V1 = result;
|
|
posStart.setZero();
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
viewer.data().set_mesh(V1, F1);
|
|
viewer.core().align_camera_center(V1, F1);
|
|
viewer.launch();
|
|
}
|
|
|