Sync upstream main

Includes nozzle-diameter guards for printers without nozzle info (#13255,
now landed upstream) and manual-calibration nozzle mismatch fix (#13882).

# Conflicts:
#	src/slic3r/GUI/GCodeViewer.cpp
#	src/slic3r/GUI/GLCanvas3D.hpp
#	src/slic3r/GUI/Tab.cpp
This commit is contained in:
Clifford Garwood committed 2026-05-29 01:49:42 -04:00
commit 9b3c1b87f4
725 files changed
+23746 -9117

No files matched your search

+191 -17
View File
@@ -14,6 +14,7 @@
#include "libslic3r/Technologies.hpp"
#include "libslic3r/Tesselate.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/AppConfig.hpp"
#include "3DScene.hpp"
#include "BackgroundSlicingProcess.hpp"
#include "GLShader.hpp"
@@ -225,7 +226,7 @@ bool GLCanvas3D::LayersEditing::is_allowed() const
float GLCanvas3D::LayersEditing::s_overlay_window_width;
void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanvas3D& canvas) {
void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(GLCanvas3D& canvas) {
if (!m_enabled)
return;
@@ -335,8 +336,23 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv
GLGizmoUtils::render_tooltip_button(&imgui, canvas, shortcuts, x, y);
ImGui::SameLine();
if (imgui.button(_L("Reset")))
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE));
imgui.disabled_begin(check_object_layers_fixed(*m_slicing_parameters, m_layer_height_profile));
if (imgui.button(_L("Reset"))) {
wxPostEvent((wxEvtHandler*) canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE));
}
imgui.disabled_end();
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
if (imgui.button(_L("Done"))) {
m_enabled = false;
GLToolbarItem* item = canvas.m_main_toolbar.get_item("layersediting");
item->set_state(GLToolbarItem::Normal);
canvas.set_as_dirty();
canvas.request_extra_frame();
}
GLCanvas3D::LayersEditing::s_overlay_window_width = ImGui::GetWindowSize().x;
imgui.end();
@@ -344,7 +360,7 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv
imgui.pop_toolbar_style();
}
void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas)
void GLCanvas3D::LayersEditing::render_overlay(GLCanvas3D& canvas)
{
render_variable_layer_height_dialog(canvas);
render_active_object_annotations(canvas);
@@ -1202,6 +1218,11 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
GLCanvas3D::~GLCanvas3D()
{
if (m_fxaa_texture_id != 0 && _set_current()) {
glsafe(::glDeleteTextures(1, &m_fxaa_texture_id));
m_fxaa_texture_id = 0;
}
reset_volumes();
m_sel_plate_toolbar.del_all_item();
@@ -2082,9 +2103,16 @@ void GLCanvas3D::render(bool only_init)
if (m_picking_enabled && m_rectangle_selection.is_dragging())
m_rectangle_selection.render(*this);
if (_is_fxaa_enabled())
_render_fxaa_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
// draw overlays
_render_overlays();
const int current_fps = m_render_stats.get_fps_and_reset_if_needed();
if (_is_fps_overlay_enabled())
_render_fps_overlay(current_fps);
if (wxGetApp().plater()->is_render_statistic_dialog_visible()) {
ImGui::ShowMetricsWindow();
@@ -2092,7 +2120,7 @@ void GLCanvas3D::render(bool only_init)
imgui.begin(std::string("Render statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
imgui.text("FPS (SwapBuffers() calls per second):");
ImGui::SameLine();
imgui.text(std::to_string(m_render_stats.get_fps_and_reset_if_needed()));
imgui.text(std::to_string(current_fps));
ImGui::Separator();
imgui.text("Compressed textures:");
ImGui::SameLine();
@@ -3202,6 +3230,22 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
wxGetApp().imgui()->reset_requires_extra_frame();
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
const int fps_cap = _get_effective_fps_cap();
if (fps_cap > 0) {
const auto now = std::chrono::steady_clock::now();
const auto min_frame_time = std::chrono::duration<double>(1.0 / static_cast<double>(fps_cap));
const auto elapsed = now - m_last_frame_start_time;
if (elapsed < min_frame_time) {
const int wait_ms = std::max(1, static_cast<int>(std::ceil(std::chrono::duration<double, std::milli>(min_frame_time - elapsed).count())));
schedule_extra_frame(wait_ms);
evt.RequestMore();
return;
}
// Pace by frame-start interval so rendering time is part of the target budget.
m_last_frame_start_time = now;
}
_refresh_if_shown_on_screen();
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
@@ -4251,7 +4295,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
bool any_gizmo_active = m_gizmos.get_current() != nullptr;
bool swap_mouse_buttons = wxGetApp().app_config->get_bool("swap_mouse_buttons");
std::map<MouseButton, MouseAction> button_mappings;
button_mappings[MouseButton::Left] = static_cast<MouseAction>(std::atoi(wxGetApp().app_config->get("left_mouse_drag_action").c_str()));
button_mappings[MouseButton::Middle] = static_cast<MouseAction>(std::atoi(wxGetApp().app_config->get("middle_mouse_drag_action").c_str()));
button_mappings[MouseButton::Right] = static_cast<MouseAction>(std::atoi(wxGetApp().app_config->get("right_mouse_drag_action").c_str()));
if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos)) {
m_mouse.drag.move_requires_threshold = false;
@@ -4466,7 +4514,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true;
}
}
else if (evt.Dragging() || is_camera_rotate(evt, swap_mouse_buttons) || is_camera_pan(evt, swap_mouse_buttons)) {
else if (evt.Dragging() || is_camera_rotate(evt, button_mappings) || is_camera_pan(evt, button_mappings)) {
m_mouse.dragging = true;
if (m_layers_editing.state != LayersEditing::Unknown && layer_editing_object_idx != -1) {
@@ -4476,10 +4524,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
// do not process the dragging if the left mouse was set down in another canvas
else if (is_camera_rotate(evt, swap_mouse_buttons)) {
else if (is_camera_rotate(evt, button_mappings)) {
// Orca: Sphere rotation for painting view
// if dragging over blank area with left button or button functions swapped then rotate
if ((any_gizmo_active || swap_mouse_buttons || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
// if dragging over blank area with left button or other button mapped to rotate, then rotate
bool middle_or_right_button_used_as_rotate = (evt.MiddleIsDown() && button_mappings[MouseButton::Middle] == MouseAction::Rotation) ||
(evt.RightIsDown() && button_mappings[MouseButton::Right] == MouseAction::Rotation);
if ((any_gizmo_active || middle_or_right_button_used_as_rotate || m_hover_volume_idxs.empty()) &&
m_mouse.is_start_position_3D_defined()) {
Camera& camera = wxGetApp().plater()->get_camera();
auto mult_pref = wxGetApp().app_config->get("camera_orbit_mult");
const double mult = mult_pref.empty() ? 1.0 : std::stod(mult_pref);
@@ -4552,7 +4603,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_camera_movement = true;
m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0);
}
else if (is_camera_pan(evt, swap_mouse_buttons)) {
else if (is_camera_pan(evt, button_mappings)) {
// if dragging with right button or if button functions swapped and dragging with left button over blank area then pan
if (m_mouse.is_start_position_2D_defined()) {
// get point in model space at Z = 0
@@ -4580,10 +4631,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
else if ((evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) ||
(m_camera_movement && !is_camera_rotate(evt, swap_mouse_buttons) && !is_camera_pan(evt, swap_mouse_buttons))) {
(m_camera_movement && !is_camera_rotate(evt, button_mappings) && !is_camera_pan(evt, button_mappings))) {
m_mouse.position = pos.cast<double>();
if (swap_mouse_buttons ? evt.RightUp() : evt.LeftUp()) {
// Check if the button that was released is mapped to rotation
if ((evt.LeftUp() && button_mappings[MouseButton::Left] == MouseAction::Rotation) ||
(evt.MiddleUp() && button_mappings[MouseButton::Middle] == MouseAction::Rotation) ||
(evt.RightUp() && button_mappings[MouseButton::Right] == MouseAction::Rotation)) {
m_rotation_center(0) = m_rotation_center(1) = m_rotation_center(2) = 0.f;
}
@@ -4784,21 +4838,42 @@ void GLCanvas3D::on_set_focus(wxFocusEvent& evt)
m_is_touchpad_navigation = wxGetApp().app_config->get_bool("camera_navigation_style");
}
bool GLCanvas3D::is_camera_rotate(const wxMouseEvent& evt, const bool buttonsSwapped) const
bool GLCanvas3D::clicked_button_matches_action(const wxMouseEvent& evt, const MouseAction action, const std::map<MouseButton, MouseAction>& mappings) const
{
MouseButton clicked = MouseButton::None;
if (evt.LeftIsDown()) {
clicked = MouseButton::Left;
}
if (evt.MiddleIsDown()) {
clicked = MouseButton::Middle;
}
if (evt.RightIsDown()) {
clicked = MouseButton::Right;
}
auto it = mappings.find(clicked);
if (it == mappings.end()) {
return false;
}
return it->second == action;
}
bool GLCanvas3D::is_camera_rotate(const wxMouseEvent& evt, const std::map<MouseButton, MouseAction>& mappings) const
{
if (m_is_touchpad_navigation) {
return evt.Moving() && evt.AltDown() && !evt.ShiftDown();
} else {
return evt.Dragging() && (buttonsSwapped ? evt.RightIsDown() : evt.LeftIsDown());
return evt.Dragging() && clicked_button_matches_action(evt, MouseAction::Rotation, mappings);
}
}
bool GLCanvas3D::is_camera_pan(const wxMouseEvent& evt, const bool buttonsSwapped) const
bool GLCanvas3D::is_camera_pan(const wxMouseEvent& evt, const std::map<MouseButton, MouseAction>& mappings) const
{
if (m_is_touchpad_navigation) {
return evt.Moving() && evt.ShiftDown() && !evt.AltDown();
} else {
return evt.Dragging() && (evt.MiddleIsDown() || (buttonsSwapped ? evt.LeftIsDown() : evt.RightIsDown()));
return evt.Dragging() && clicked_button_matches_action(evt, MouseAction::Pan, mappings);
;
}
}
@@ -7621,6 +7696,105 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
_update_volumes_hover_state();
}
bool GLCanvas3D::_is_fxaa_enabled() const
{
return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_FXAA_ENABLED);
}
int GLCanvas3D::_get_effective_fps_cap() const
{
if (wxGetApp().app_config == nullptr)
return 0;
int fps_cap = 0;
try {
fps_cap = std::stoi(wxGetApp().app_config->get(SETTING_OPENGL_FPS_CAP));
}
catch (...) {
fps_cap = 0;
}
fps_cap = std::max(0, std::min(fps_cap, 240));
return fps_cap;
}
bool GLCanvas3D::_is_fps_overlay_enabled() const
{
return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY);
}
void GLCanvas3D::_render_fps_overlay(int fps) const
{
if (fps < 0)
return;
ImGuiWrapper& imgui = *wxGetApp().imgui();
const float margin = 10.0f * get_scale();
const ImVec2 display_size = ImGui::GetIO().DisplaySize;
ImGui::SetNextWindowPos(ImVec2(display_size.x - margin, margin), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
ImGui::SetNextWindowBgAlpha(0.35f);
imgui.begin(
std::string("###fps_overlay"),
ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoInputs);
imgui.text(std::string("FPS: ") + std::to_string(fps));
imgui.end();
}
void GLCanvas3D::_render_fxaa_pass(unsigned int width, unsigned int height)
{
if (width == 0 || height == 0)
return;
GLShaderProgram* shader = wxGetApp().get_shader("fxaa");
if (shader == nullptr)
return;
if (m_fxaa_texture_id == 0) {
glsafe(::glGenTextures(1, &m_fxaa_texture_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_fxaa_texture_id));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
}
glsafe(::glBindTexture(GL_TEXTURE_2D, m_fxaa_texture_id));
if (m_fxaa_texture_size[0] != width || m_fxaa_texture_size[1] != height) {
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
m_fxaa_texture_size = { width, height };
}
glsafe(::glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height));
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_BLEND));
glsafe(::glClear(GL_COLOR_BUFFER_BIT));
shader->start_using();
shader->set_uniform("view_model_matrix", Transform3d::Identity());
shader->set_uniform("projection_matrix", Transform3d::Identity());
shader->set_uniform("uniform_texture", 0);
shader->set_uniform("inv_tex_size", Vec2f(1.0f / static_cast<float>(width), 1.0f / static_cast<float>(height)));
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_fxaa_texture_id));
m_background.render();
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
shader->stop_using();
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
}
void GLCanvas3D::_render_background()
{
bool use_error_color = false;