QoL: 3D navigator (#4181)

* Initial integration of ImGuizmo

* Fix mouse capture

* Fix frame update

* Update face color

* Show current camera rotation

* Fix coord mapping

* Update camera rotation from 3d navigator

* Use orthographic

* Render axis

* Make the axis color lighter if at back

* Show axis label

* Fix linux build

* Move to separate method

* Refine

* Add option to show/hide 3d navigator

* Add license info

* Handle dpi scaling

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
Noisyfox 2024-02-27 21:57:31 +08:00 committed by GitHub
parent 4991a3d312
commit bf4d59a676
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 3695 additions and 1 deletions

View file

@ -573,7 +573,7 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
encoding_check(libslic3r_gui)
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto)
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto)
#target_link_libraries(libslic3r_gui libslic3r cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi libcurl OpenSSL::SSL OpenSSL::Crypto ${wxWidgets_LIBRARIES} glfw)
if (MSVC)

View file

@ -100,6 +100,7 @@ void CopyrightsDialog::fill_entries()
{ "GLFW", "", "https://www.glfw.org" },
{ "GNU gettext", "", "https://www.gnu.org/software/gettext" },
{ "ImGUI", "", "https://github.com/ocornut/imgui" },
{ "ImGuizmo", "", "https://github.com/CedricGuillemet/ImGuizmo" },
{ "Libigl", "", "https://libigl.github.io" },
{ "libnest2d", "", "https://github.com/tamasmeszaros/libnest2d" },
{ "lib_fts", "", "https://www.forrestthewoods.com" },

View file

@ -419,6 +419,15 @@ void Camera::rotate_local_around_target(const Vec3d& rotation_rad)
}
}
void Camera::set_rotation(const Transform3d& rotation)
{
const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
m_view_rotation = Eigen::Quaterniond(rotation.matrix().template block<3, 3>(0, 0));
m_view_rotation.normalize();
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
update_zenit();
}
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box)
{
std::pair<double, double> ret;

View file

@ -146,6 +146,8 @@ public:
// rotate the camera around three axes parallel to the camera local axes and passing through m_target
void rotate_local_around_target(const Vec3d& rotation_rad);
void set_rotation(const Transform3d& rotation);
// returns true if the camera z axis (forward) is pointing in the negative direction of the world z axis
bool is_looking_downward() const { return get_dir_forward().dot(Vec3d::UnitZ()) < 0.0; }

View file

@ -85,6 +85,8 @@
#endif
#include <imgui/imgui_internal.h>
#include <imguizmo/ImGuizmo.h>
static constexpr const float TRACKBALLSIZE = 0.8f;
static Slic3r::ColorRGBA DEFAULT_BG_LIGHT_COLOR = { 0.906f, 0.906f, 0.906f, 1.0f };
@ -5535,6 +5537,70 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo
return settings_changed;
}
static float identityMatrix[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
static const float cameraProjection[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
void GLCanvas3D::_render_3d_navigator()
{
if (!wxGetApp().show_3d_navigator()) {
return;
}
ImGuizmo::BeginFrame();
ImGuizmo::AllowAxisFlip(false);
auto& style = ImGuizmo::GetStyle();
style.Colors[ImGuizmo::COLOR::DIRECTION_X] = ImGuiWrapper::to_ImVec4(ColorRGBA::Y());
style.Colors[ImGuizmo::COLOR::DIRECTION_Y] = ImGuiWrapper::to_ImVec4(ColorRGBA::Z());
style.Colors[ImGuizmo::COLOR::DIRECTION_Z] = ImGuiWrapper::to_ImVec4(ColorRGBA::X());
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_X], "y");
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Y], "z");
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Z], "x");
float sc = get_scale();
#ifdef WIN32
const int dpi = get_dpi_for_window(wxGetApp().GetTopWindow());
sc *= (float) dpi / (float) DPI_DEFAULT;
#endif // WIN32
const ImGuiIO& io = ImGui::GetIO();
const float viewManipulateLeft = 0;
const float viewManipulateTop = io.DisplaySize.y;
const float camDistance = 8.f;
ImGuizmo::SetID(0);
Camera& camera = wxGetApp().plater()->get_camera();
Transform3d m = Transform3d::Identity();
m.matrix().block(0, 0, 3, 3) = camera.get_view_rotation().toRotationMatrix();
// Rotate along X and Z axis for 90 degrees to have Y-up
const auto coord_mapping_transform = Geometry::rotation_transform(Vec3d(0.5 * PI, 0, 0.5 * PI));
m = m * coord_mapping_transform;
float cameraView[16];
for (unsigned int c = 0; c < 4; ++c) {
for (unsigned int r = 0; r < 4; ++r) {
cameraView[c * 4 + r] = m(r, c);
}
}
const float size = 128 * sc;
const bool dirty = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD,
identityMatrix, camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size),
ImVec2(size, size), 0x10101010);
if (dirty) {
for (unsigned int c = 0; c < 4; ++c) {
for (unsigned int r = 0; r < 4; ++r) {
m(r, c) = cameraView[c * 4 + r];
}
}
// Rotate back
m = m * (coord_mapping_transform.inverse());
camera.set_rotation(m);
request_extra_frame();
}
}
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
static void debug_output_thumbnail(const ThumbnailData& thumbnail_data)
@ -7324,6 +7390,8 @@ void GLCanvas3D::_render_overlays()
}*/
}
m_labels.render(sorted_instances);
_render_3d_navigator();
}
void GLCanvas3D::_render_style_editor()

View file

@ -1181,6 +1181,7 @@ private:
//BBS: GUI refactor: adjust main toolbar position
bool _render_orient_menu(float left, float right, float bottom, float top);
bool _render_arrange_menu(float left, float right, float bottom, float top);
void _render_3d_navigator();
// render thumbnail using the default framebuffer
void render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<ColorRGBA>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type);

View file

@ -329,6 +329,9 @@ private:
bool show_gcode_window() const { return m_show_gcode_window; }
void toggle_show_gcode_window();
bool show_3d_navigator() const { return app_config->get_bool("show_3d_navigator"); }
void toggle_show_3d_navigator() const { app_config->set_bool("show_3d_navigator", !show_3d_navigator()); }
wxString get_inf_dialog_contect () {return m_info_dialog_content;};
std::vector<std::string> split_str(std::string src, std::string separator);

View file

@ -2567,6 +2567,15 @@ void MainFrame::init_menubar_as_editor()
this, [this]() { return m_tabpanel->GetSelection() == tpPreview; },
[this]() { return wxGetApp().show_gcode_window(); }, this);
append_menu_check_item(
viewMenu, wxID_ANY, _L("Show 3D Navigator"), _L("Show 3D navigator in Prepare and Preview scene"),
[this](wxCommandEvent&) {
wxGetApp().toggle_show_3d_navigator();
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
},
this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; },
[this]() { return wxGetApp().show_3d_navigator(); }, this);
append_menu_item(
viewMenu, wxID_ANY, _L("Reset Window Layout"), _L("Reset to default window layout"),
[this](wxCommandEvent&) { m_plater->reset_window_layout(); }, "", this,