diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ea897dd4af..d0c9d90230 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2807,6 +2807,9 @@ void GLCanvas3D::render(bool only_init) camera.apply_projection(_max_bounding_box(true, true, true,is_volumes_limit_to_expand_plate())); camera.update_frustum(); + //BBS update gizmo camera scaling (inv_zoom + perspective depth correction) + GLGizmoBase::update_camera_scaling(camera, m_selection); + m_frame_callback_list.clear(); const std::array& viewport = camera.get_viewport(); @@ -9428,8 +9431,6 @@ void GLCanvas3D::_render_volumes_for_picking() const void GLCanvas3D::_render_current_gizmo() const { - //BBS update inv_zoom - GLGizmoBase::INV_ZOOM = (float)get_active_camera().get_inv_zoom(); m_gizmos.render_current_gizmo(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 4514699330..8bec6508ce 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -5,6 +5,7 @@ #include #include +#include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_Colors.hpp" #include "slic3r/GUI/OpenGLManager.hpp" @@ -15,6 +16,7 @@ namespace Slic3r { namespace GUI { float GLGizmoBase::INV_ZOOM = 1.0f; +float GLGizmoBase::DEPTH_CORRECTION = 1.0f; const float GLGizmoBase::Grabber::SizeFactor = 0.05f; @@ -324,11 +326,51 @@ void GLGizmoBase::render_lines(const std::vector &points) wxGetApp().unbind_shader(); } +void GLGizmoBase::update_camera_scaling(const Camera& camera, const Selection& selection) +{ + INV_ZOOM = (float)camera.get_inv_zoom(); + DEPTH_CORRECTION = 1.0f; + + if (camera.get_type() != Camera::EType::Perspective) + return; + + // Left untouched on purpose: with the flag off the gizmo geometry is anchored to the object + // bounding box in world space and only the grabber cubes follow 1/zoom, so that path was never + // screen size stable to begin with. Reworking it is out of scope here. + const auto& ogl_manager = wxGetApp().get_opengl_manager(); + if (!ogl_manager || !ogl_manager->is_gizmo_keep_screen_size_enabled()) + return; + + if (selection.is_empty()) + return; + + const BoundingBoxf3& box = selection.get_bounding_box(); + if (!box.defined) + return; + + const double near_z = camera.get_near_z(); + const double gui_scale = camera.get_gui_scale(); // == near_z / orbit_distance under perspective + if (near_z <= EPSILON || gui_scale <= EPSILON) + return; + + // Scaling a world space length by 1/zoom keeps it constant on screen only under an orthographic + // projection. Under perspective the projected size also scales with orbit_distance / depth, so a + // gizmo anchored away from the orbit plane grows or shrinks on its own. Cancel that term here. + const double depth = -(camera.get_view_matrix() * box.center()).z(); // eye space depth, positive in front of the camera + if (depth <= near_z) // anchor is in front of the near plane, nothing is drawn there anyway + return; + + // Clamped on both ends: a large factor inflates the gizmo AABB which feeds back into + // Camera::apply_projection, a tiny one makes the grabber model matrix degenerate. + const double orbit_distance = near_z / gui_scale; + DEPTH_CORRECTION = (float)std::clamp(depth / orbit_distance, 0.1, 8.0); +} + float GLGizmoBase::get_grabber_size() { float grabber_size = 8.0f; if (GLGizmoBase::INV_ZOOM > 0) { - grabber_size = GLGizmoBase::Grabber::FixedGrabberSize * GLGizmoBase::Grabber::GrabberSizeFactor * GLGizmoBase::INV_ZOOM; + grabber_size = GLGizmoBase::Grabber::FixedGrabberSize * GLGizmoBase::Grabber::GrabberSizeFactor * GLGizmoBase::INV_ZOOM * GLGizmoBase::DEPTH_CORRECTION; } return grabber_size; } @@ -688,7 +730,7 @@ void GLGizmoBase::modify_radius(float& radius) const uint32_t t_height = 0; ogl_manager->get_viewport_size(t_width, t_height); radius = 0.2f * std::min(t_width, t_height); - radius *= GLGizmoBase::INV_ZOOM; + radius *= GLGizmoBase::INV_ZOOM * GLGizmoBase::DEPTH_CORRECTION; } } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index f5acee79fb..e9c75a0ba6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -32,6 +32,7 @@ namespace GUI { class ImGuiWrapper; class GLCanvas3D; +struct Camera; enum class CommonGizmosDataID; class CommonGizmosDataPool; class Selection; @@ -44,6 +45,16 @@ class GLGizmoBase static const unsigned int BASE_ID = 255 * 255 * 254; static float INV_ZOOM; + // Cancels the perspective foreshortening for screen-space-sized gizmos, stays 1.0 under an orthographic camera. + static float DEPTH_CORRECTION; + + // Refreshes INV_ZOOM and DEPTH_CORRECTION for the given camera. The selection provides the anchor the + // gizmo is drawn around; DEPTH_CORRECTION falls back to 1.0 with an orthographic camera, with nothing + // selected, or when the screen size mode is off. + // Call once per frame, after Camera::apply_projection(), which is what fills in near_z and gui_scale. + // Single anchor per frame: grabbers offset from the selection center (Scale corners, the Cut plane, + // the Text/SVG cube) keep a residual error proportional to their own depth offset. + static void update_camera_scaling(const Camera& camera, const Selection& selection); //BBS colors static std::array DEFAULT_BASE_COLOR; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index bdbfb315d4..9663dc6551 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -2872,9 +2872,11 @@ Transform3d get_screen_scalling_matrix() if (p_ogl_manager) { if (p_ogl_manager->is_gizmo_keep_screen_size_enabled()) { const auto& t_zoom = camera.get_zoom(); - screen_scalling_matrix.data()[0 * 4 + 0] = 5.0f / t_zoom; - screen_scalling_matrix.data()[1 * 4 + 1] = 5.0f / t_zoom; - screen_scalling_matrix.data()[2 * 4 + 2] = 5.0f / t_zoom; + // Same perspective correction the gizmos get, so the hints stay the same length as the gizmo arrows. + const double t_scale = 5.0 * (double)GLGizmoBase::DEPTH_CORRECTION / t_zoom; + screen_scalling_matrix.data()[0 * 4 + 0] = t_scale; + screen_scalling_matrix.data()[1 * 4 + 1] = t_scale; + screen_scalling_matrix.data()[2 * 4 + 2] = t_scale; } } return screen_scalling_matrix;