diff --git a/resources/images/texture_displacement_checker.svg b/resources/images/texture_displacement_checker.svg
new file mode 100644
index 0000000000..4631116379
--- /dev/null
+++ b/resources/images/texture_displacement_checker.svg
@@ -0,0 +1,165 @@
+
+
diff --git a/resources/images/texture_displacement_connected_area.svg b/resources/images/texture_displacement_connected_area.svg
new file mode 100644
index 0000000000..b57e96d951
--- /dev/null
+++ b/resources/images/texture_displacement_connected_area.svg
@@ -0,0 +1,115 @@
+
+
diff --git a/resources/images/texture_displacement_distortion.svg b/resources/images/texture_displacement_distortion.svg
new file mode 100644
index 0000000000..833a34f0cf
--- /dev/null
+++ b/resources/images/texture_displacement_distortion.svg
@@ -0,0 +1,197 @@
+
+
diff --git a/resources/images/texture_displacement_fast_preview.svg b/resources/images/texture_displacement_fast_preview.svg
new file mode 100644
index 0000000000..9e13fb68de
--- /dev/null
+++ b/resources/images/texture_displacement_fast_preview.svg
@@ -0,0 +1,132 @@
+
+
diff --git a/resources/images/texture_displacement_real_preview.svg b/resources/images/texture_displacement_real_preview.svg
new file mode 100644
index 0000000000..f3ebdaa53d
--- /dev/null
+++ b/resources/images/texture_displacement_real_preview.svg
@@ -0,0 +1,150 @@
+
+
diff --git a/resources/images/texture_displacement_wireframe.svg b/resources/images/texture_displacement_wireframe.svg
new file mode 100644
index 0000000000..20ac8c1b03
--- /dev/null
+++ b/resources/images/texture_displacement_wireframe.svg
@@ -0,0 +1,163 @@
+
+
diff --git a/resources/images/toolbar_big_brush.svg b/resources/images/toolbar_big_brush.svg
new file mode 100644
index 0000000000..20be9ec53b
--- /dev/null
+++ b/resources/images/toolbar_big_brush.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/resources/images/toolbar_face.svg b/resources/images/toolbar_face.svg
new file mode 100644
index 0000000000..6975e0f6e4
--- /dev/null
+++ b/resources/images/toolbar_face.svg
@@ -0,0 +1,97 @@
+
+
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp
index a8dcbaf7c4..426521f04d 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp
@@ -2093,6 +2093,19 @@ unsigned int GLGizmoTextureDisplacement::tool_icon_id()
return m_tool_icon.get_id();
}
+unsigned int GLGizmoTextureDisplacement::icon_id(const std::string &filename)
+{
+ auto it = m_icon_cache.find(filename);
+ if (it == m_icon_cache.end()) {
+ // First request for this icon: default-construct the texture in place and load it once (with a
+ // GL context current, since this runs from the panel render). A failed load leaves id 0 and is
+ // not retried.
+ it = m_icon_cache.try_emplace(filename).first;
+ it->second.load_from_svg_file(resources_dir() + "/images/" + filename, false, false, false, 32);
+ }
+ return it->second.get_id();
+}
+
void GLGizmoTextureDisplacement::add_texture_layer()
{
ModelVolume *mv = texture_volume();
@@ -2552,6 +2565,28 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
return changed;
};
+ // Icon toggle button shared by the selection-mode and view-mode rows: an SVG button with a teal
+ // backing and full-brightness icon when active, dimmed otherwise. Falls back to a text checkbox if
+ // its icon could not be loaded, so the control is never lost.
+ const float icon_btn_sz = m_imgui->scaled(1.5f);
+ const ImVec4 icon_active_bg(0.0f, 0.59f, 0.53f, 1.0f);
+ const auto icon_toggle = [&](int uid, unsigned int ic, bool active, const wxString &label, const wxString &tip) -> bool {
+ bool clicked;
+ ImGui::PushID(uid);
+ if (ic != 0)
+ clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic, ImVec2(icon_btn_sz, icon_btn_sz), ImVec2(0, 0),
+ ImVec2(1, 1), -1, active ? icon_active_bg : ImVec4(0, 0, 0, 0),
+ active ? ImVec4(1, 1, 1, 1) : ImVec4(0.65f, 0.65f, 0.65f, 1.f));
+ else {
+ bool v = active;
+ clicked = ImGui::Checkbox(label.ToUTF8().data(), &v);
+ }
+ ImGui::PopID();
+ if (ImGui::IsItemHovered())
+ m_imgui->tooltip(tip, m_imgui->scaled(18.f));
+ return clicked;
+ };
+
if (m_imgui->button(m_undocked ? _L("Dock panel") : _L("Undock panel")))
m_undocked = !m_undocked;
if (ImGui::IsItemHovered())
@@ -2568,17 +2603,22 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
const bool is_brush_mode = m_tool_type == ToolType::BRUSH && m_cursor_type != TriangleSelector::CursorType::POINTER;
const bool is_face_mode = m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER;
const bool is_area_mode = m_tool_type == ToolType::SMART_FILL;
- if (ImGui::RadioButton(_u8L("Brush").c_str(), is_brush_mode)) {
+ ImGui::SameLine();
+ if (icon_toggle(801, icon_id("toolbar_big_brush.svg"), is_brush_mode, _L("Brush"),
+ _L("Brush - paint over the surface by dragging"))) {
m_tool_type = ToolType::BRUSH;
m_cursor_type = TriangleSelector::CursorType::CIRCLE;
}
ImGui::SameLine();
- if (ImGui::RadioButton(_u8L("Face").c_str(), is_face_mode)) {
+ if (icon_toggle(802, icon_id("toolbar_face.svg"), is_face_mode, _L("Face"),
+ _L("Face - click individual triangles"))) {
m_tool_type = ToolType::BRUSH;
m_cursor_type = TriangleSelector::CursorType::POINTER;
}
ImGui::SameLine();
- if (ImGui::RadioButton(_u8L("Connected area").c_str(), is_area_mode)) {
+ if (icon_toggle(803, icon_id("texture_displacement_connected_area.svg"), is_area_mode, _L("Connected area"),
+ _L("Connected area - flood-fill the region reachable without crossing an edge sharper than the "
+ "angle threshold"))) {
m_tool_type = ToolType::SMART_FILL;
m_cursor_type = TriangleSelector::CursorType::POINTER;
}
@@ -2606,50 +2646,33 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
select_whole_model();
// View mode (#: "make View Mode with just icons"): Normal / Fast / Checker / Distortion behave as
- // one radio group, Wireframe as an independent toggle. All reuse the one tool icon for now, so the
- // tooltip carries the meaning. The underlying state stays m_use_bump_preview + m_uv_check_mode.
+ // one radio group, Wireframe as an independent toggle. Each has its own icon; the tooltip carries
+ // the meaning. The underlying state stays m_use_bump_preview + m_uv_check_mode.
{
- const unsigned int icon = tool_icon_id();
- const float vsz = m_imgui->scaled(1.5f);
- const ImVec4 teal(0.0f, 0.59f, 0.53f, 1.0f);
- const int cur_mode = m_use_bump_preview ? 1 :
- (m_uv_check_mode == UVCheckMode::Checker ? 2 :
- m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0);
+ const int cur_mode = m_use_bump_preview ? 1 :
+ (m_uv_check_mode == UVCheckMode::Checker ? 2 :
+ m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0);
int new_mode = cur_mode;
bool wf_toggle = false;
- // One icon button; active state shown by a teal backing and a full-brightness (vs dimmed) icon.
- // Falls back to a text toggle if the icon can't load.
- const auto icon_toggle = [&](int uid, bool active, const wxString &label, const wxString &tip) -> bool {
- bool clicked;
- ImGui::PushID(uid);
- if (icon != 0)
- clicked = m_imgui->image_button((ImTextureID) (intptr_t) icon, ImVec2(vsz, vsz), ImVec2(0, 0), ImVec2(1, 1),
- -1, active ? teal : ImVec4(0, 0, 0, 0),
- active ? ImVec4(1, 1, 1, 1) : ImVec4(0.65f, 0.65f, 0.65f, 1.f));
- else {
- bool v = active;
- clicked = ImGui::Checkbox(label.ToUTF8().data(), &v);
- }
- ImGui::PopID();
- if (ImGui::IsItemHovered())
- m_imgui->tooltip(tip, m_imgui->scaled(18.f));
- return clicked;
- };
-
m_imgui->text(_L("View"));
ImGui::SameLine();
- if (icon_toggle(701, cur_mode == 0, _L("Normal"), _L("Normal - the true displaced geometry (what Bake produces)"))) new_mode = 0;
+ if (icon_toggle(701, icon_id("texture_displacement_real_preview.svg"), cur_mode == 0, _L("Normal"),
+ _L("Normal - the true displaced geometry (what Bake produces)"))) new_mode = 0;
ImGui::SameLine();
- if (icon_toggle(702, cur_mode == 1, _L("Fast"), _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) new_mode = 1;
+ if (icon_toggle(702, icon_id("texture_displacement_fast_preview.svg"), cur_mode == 1, _L("Fast"),
+ _L("Fast - a bump-shaded approximation of the active layer only; quick to update, not exact"))) new_mode = 1;
ImGui::SameLine();
- if (icon_toggle(703, cur_mode == 2, _L("Checker"), _L("Checker - a test grid over the unwrap; squares stay square where it does not stretch"))) new_mode = 2;
+ if (icon_toggle(703, icon_id("texture_displacement_checker.svg"), cur_mode == 2, _L("Checker"),
+ _L("Checker - a test grid over the unwrap; squares stay square where it does not stretch"))) new_mode = 2;
ImGui::SameLine();
- if (icon_toggle(704, cur_mode == 3, _L("Distortion"), _L("Distortion - blue-to-red stretch heatmap over the unwrap (needs the Unwrap/LSCM projection)"))) new_mode = 3;
+ if (icon_toggle(704, icon_id("texture_displacement_distortion.svg"), cur_mode == 3, _L("Distortion"),
+ _L("Distortion - blue-to-red stretch heatmap over the unwrap (needs the Unwrap/LSCM projection)"))) new_mode = 3;
ImGui::SameLine();
ImGui::Dummy(ImVec2(m_imgui->scaled(0.6f), 0.f));
ImGui::SameLine();
- if (icon_toggle(705, m_wireframe_overlay, _L("Wireframe"), _L("Wireframe - overlay the mesh edges; independent of the view above"))) wf_toggle = true;
+ if (icon_toggle(705, icon_id("texture_displacement_wireframe.svg"), m_wireframe_overlay, _L("Wireframe"),
+ _L("Wireframe - overlay the mesh edges; independent of the view above"))) wf_toggle = true;
if (new_mode != cur_mode) {
m_use_bump_preview = (new_mode == 1);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp
index f81c6bc08a..bce96e89b9 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp
@@ -449,6 +449,12 @@ private:
GLTexture m_tool_icon;
bool m_tool_icon_tried = false;
unsigned int tool_icon_id(); // 0 if the icon could not be loaded
+
+ // The panel's icon-button rows (selection mode, view mode) each need their own small SVG. Loaded and
+ // uploaded once on first use and cached by file name (under resources/images/). Returns 0 if an icon
+ // could not be loaded, in which case the button falls back to a text control.
+ std::map m_icon_cache;
+ unsigned int icon_id(const std::string &filename);
};
} // namespace Slic3r::GUI