move ImGuizmo tutorial into plugin; simplify and isolate

This commit is contained in:
Alec Jacobson
2020-09-26 15:59:28 -04:00
parent 28008528b4
commit d9864be29e
4 changed files with 131 additions and 152 deletions
-59
View File
@@ -1,59 +0,0 @@
#pragma once
////////////////////////////////////////////////////////////////////////////////
#include <imgui/imgui.h>
#include <imgui/imgui_internal.h>
#include <imguizmo/ImGuizmo.h>
#include <Eigen/Dense>
#include <vector>
////////////////////////////////////////////////////////////////////////////////
namespace igl
{
namespace opengl
{
namespace glfw
{
namespace imgui
{
void EditTransform(const float *cameraView, const float *cameraProjection, float* matrix, bool isOrthographic)
{
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
// Alec: This could be a parameter ImGuizmo::WORLD will make senze in some
// circumstances. For now, 109_ImGuizmo and my other use cases make more
// sense in LOCAL mode.
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::LOCAL);
// Maya-style key bindings
if (ImGui::IsKeyPressed('W'))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed('E'))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed('R'))
mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
ImGui::SameLine();
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(matrix, matrixTranslation, matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix);
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(cameraView, cameraProjection, mCurrentGizmoOperation, mCurrentGizmoMode, matrix, NULL, NULL);
}
} // end namespace
} // end namespace
} // end namespace
} // end namespace
@@ -0,0 +1,49 @@
#include "ImGuizmoPlugin.h"
#include <imgui/imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <imgui_fonts_droid_sans.h>
#include <GLFW/glfw3.h>
namespace igl{ namespace opengl{ namespace glfw{ namespace imgui{
IGL_INLINE void ImGuizmoPlugin::init(igl::opengl::glfw::Viewer *_viewer)
{
ImGuiMenu::init(_viewer);
}
IGL_INLINE bool ImGuizmoPlugin::pre_draw()
{
ImGuiMenu::pre_draw();
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
ImGuizmo::BeginFrame();
ImGui::PopStyleVar();
return false;
}
IGL_INLINE bool ImGuizmoPlugin::post_draw()
{
// Don't draw the Viewer's default menu: draw just the ImGuizmo
Eigen::Matrix4f view = (viewer->core().view / viewer->core().camera_zoom);
Eigen::Matrix4f proj = viewer->core().proj;
if(viewer->core().orthographic){ view(2,3) -= 1000;/* Hack depth */ }
// ImGuizmo doesn't like a lot of scaling in view, shift it to T
const float z = viewer->core().camera_base_zoom;
const Eigen::Matrix4f S =
(Eigen::Matrix4f()<< z,0,0,0, 0,z,0,0, 0,0,z,0, 0,0,0,1).finished();
view = (view * S.inverse()).eval();
const Eigen::Matrix4f T0 = T;
T = (S * T).eval();
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(
view.data(),proj.data(),operation,ImGuizmo::LOCAL,T.data(),NULL,NULL);
// invert scaling that was shifted onto T
T = (S.inverse() * T).eval();
const float diff = (T-T0).array().abs().maxCoeff();
// Only call if actually changed; otherwise, triggers on all mouse events
if( diff > 1e-7) { callback(T); }
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
return false;
}
}}}}
@@ -0,0 +1,37 @@
#ifndef IGL_OPENGL_GFLW_IMGUI_IMGUIZMOPLUGIN_H
#define IGL_OPENGL_GFLW_IMGUI_IMGUIZMOPLUGIN_H
#include "../../../igl_inline.h"
#include "ImGuiMenu.h"
#include <imgui/imgui.h>
#include <imgui/imgui_internal.h>
#include <imguizmo/ImGuizmo.h>
#include <Eigen/Dense>
namespace igl{ namespace opengl{ namespace glfw{ namespace imgui{
class ImGuizmoPlugin : public igl::opengl::glfw::imgui::ImGuiMenu
{
public:
// callback(T) called when the stored transform T changes
std::function<void(const Eigen::Matrix4f &)> callback;
// whether rotating, translating or scaling
ImGuizmo::OPERATION operation;
// stored transformation
Eigen::Matrix4f T;
// Initilize with rotate operation on an identity transform (at origin)
ImGuizmoPlugin():operation(ImGuizmo::ROTATE),T(Eigen::Matrix4f::Identity()){};
/////////////////////////////////////////////////////////////////////////////
// Boilerplate
virtual void init(igl::opengl::glfw::Viewer *_viewer) override;
virtual bool pre_draw() override;
/////////////////////////////////////////////////////////////////////////////
virtual bool post_draw() override;
};
}}}}
#ifndef IGL_STATIC_LIBRARY
# include "ImGuizmoPlugin.cpp"
#endif
#endif
+45 -93
View File
@@ -1,101 +1,53 @@
#include <igl/read_triangle_mesh.h>
#include <igl/opengl/glfw/Viewer.h>
#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
#include <igl/opengl/glfw/imgui/ImGuizmo.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <igl/opengl/glfw/imgui/ImGuizmoPlugin.h>
#include <GLFW/glfw3.h>
#include <imgui/imgui.h>
#include "tutorial_shared_path.h"
class SlicingPlugin : public igl::opengl::glfw::imgui::ImGuiMenu
{
igl::opengl::ViewerData data;
// Hard Coded Plane Mesh
const Eigen::MatrixXd OV = (Eigen::MatrixXd(4, 3) <<
-0.5, -0.5, 0.0,
-0.5, 0.5, 0.0,
0.5, 0.5, 0.0,
0.5, -0.5, 0.0).finished();
const Eigen::MatrixXi OF = (Eigen::MatrixXi(2, 3) <<
0, 2, 1,
0, 3, 2).finished();
virtual void init(igl::opengl::glfw::Viewer *_viewer) override
{
ImGuiMenu::init(_viewer);
// Load slicing plane into viewer (a square mesh)
data.set_mesh(SlicingPlugin::OV, SlicingPlugin::OF);
data.set_face_based(true);
data.set_colors(Eigen::RowVector4d(224, 86, 253, 128)/255.0);
data.show_lines = false;
}
virtual bool pre_draw() override {
ImGuiMenu::pre_draw();
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
ImGuizmo::BeginFrame();
ImGui::PopStyleVar();
return false;
}
virtual bool post_draw() override {
viewer->core().draw(data);
ImGuiMenu::post_draw();
return false;
}
virtual void draw_custom_window() override {
float menu_width = 180.f * menu_scaling();
ImGui::SetNextWindowPos(ImVec2(menu_width, 0.0f), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(1.5f * menu_width, -1.0f), ImVec2(1.5f * menu_width, -1.0f));
bool _guizmo_menu_visible = true;
ImGui::Begin(
"ImGuizmo Tools", &_guizmo_menu_visible,
ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_AlwaysAutoResize
);
draw_imguizmo_menu();
ImGui::End();
}
virtual void draw_imguizmo_menu() {
static Eigen::Matrix4f matrix = Eigen::Matrix4f::Identity();
const float z = viewer->core().camera_base_zoom;
Eigen::Affine3f rescale = Eigen::Scaling(0.5f * z)
* Eigen::Translation3f(viewer->core().camera_base_translation);
Eigen::Affine3f view = Eigen::Affine3f( viewer->core().view * 1./viewer->core().camera_zoom ) * rescale.inverse();
Eigen::Matrix4f proj = viewer->core().proj;
if(viewer->core().orthographic)
{
view(2,3) -= 50; // Adjust depth for view transform
}
igl::opengl::glfw::imgui::EditTransform(view.matrix().data(), proj.data(), matrix.data(), viewer->core().orthographic);
// Transform the slicing plane according to
// ImGuizmo tool manipulations in the viewer
Eigen::Affine3f model = Eigen::Affine3f(rescale.inverse()*matrix);
Eigen::MatrixXd V = (SlicingPlugin::OV.rowwise().homogeneous()
* model.matrix().cast<double>().transpose()).rowwise().hnormalized();
if (data.V.rows() == V.rows())
data.set_vertices(V);
}
};
#include <imgui/imgui_internal.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <imguizmo/ImGuizmo.h>
int main(int argc, char *argv[])
{
// Plot the mesh
igl::opengl::glfw::Viewer viewer;
viewer.load_mesh_from_file(argc>1?argv[1]: TUTORIAL_SHARED_PATH "/cow.off");
// Load a mesh from file
Eigen::MatrixXd V;
Eigen::MatrixXi F;
igl::read_triangle_mesh(argc>1?argv[1]: TUTORIAL_SHARED_PATH "/cow.off",V,F);
// Set up viewer
igl::opengl::glfw::Viewer vr;
vr.data().set_mesh(V,F);
// Custom menu
SlicingPlugin menu;
viewer.plugins.push_back(&menu);
viewer.launch();
igl::opengl::glfw::imgui::ImGuizmoPlugin plugin;
vr.plugins.push_back(&plugin);
// Initialize ImGuizmo at mesh centroid
plugin.T.block(0,3,3,1) =
0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff()).transpose().cast<float>();
// Update can be applied relative to this remembered initial transform
const Eigen::Matrix4f T0 = plugin.T;
// Attach callback to apply imguizmo's transform to mesh
plugin.callback = [&](const Eigen::Matrix4f & T)
{
const Eigen::Matrix4d TT = (T*T0.inverse()).cast<double>().transpose();
vr.data().set_vertices(
(V.rowwise().homogeneous()*TT).rowwise().hnormalized());
vr.data().compute_normals();
};
// Maya-style keyboard shortcuts for operation
vr.callback_key_pressed = [&](decltype(vr) &,unsigned int key, int mod)
{
switch(key)
{
case 'W': case 'w': plugin.operation = ImGuizmo::TRANSLATE; return true;
case 'E': case 'e': plugin.operation = ImGuizmo::ROTATE; return true;
case 'R': case 'r': plugin.operation = ImGuizmo::SCALE; return true;
}
return false;
};
std::cout<<R"(
W,w Switch to translate operation
E,e Switch to rotate operation
R,r Switch to scale operation
)";
vr.launch();
}