From e2f2ed41720c73170c0e08f8650394d253cae973 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 5 Oct 2021 11:05:20 +0200 Subject: [PATCH 1/7] Fix for #6218 - Button tooltip in the way Use wxRichTooltip instead of wxTooltip for buttons on the bottom of a sidebar --- src/slic3r/GUI/Plater.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 90dce0a13..4726884fe 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "libslic3r/libslic3r.h" #include "libslic3r/Format/STL.hpp" @@ -611,6 +612,7 @@ struct Sidebar::priv wxButton *btn_export_gcode; wxButton *btn_reslice; + wxString btn_reslice_tip; ScalableButton *btn_send_gcode; //ScalableButton *btn_eject_device; ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected) @@ -622,6 +624,7 @@ struct Sidebar::priv ~priv(); void show_preset_comboboxes(); + void show_rich_tip(const wxString& tooltip, wxButton* btn); }; Sidebar::priv::~priv() @@ -650,6 +653,17 @@ void Sidebar::priv::show_preset_comboboxes() scrolled->Refresh(); } +void Sidebar::priv::show_rich_tip(const wxString& tooltip, wxButton* btn) +{ + if (tooltip.IsEmpty()) + return; + wxRichToolTip tip(tooltip, ""); + tip.SetIcon(wxICON_NONE); + tip.SetTitleFont(wxGetApp().normal_font()); + tip.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + tip.SetTimeout(1200); + tip.ShowFor(btn); +} // Sidebar / public @@ -784,7 +798,11 @@ Sidebar::Sidebar(Plater *parent) #endif //__APPLE__ ScalableBitmap bmp = ScalableBitmap(this, icon_name, bmp_px_cnt); *btn = new ScalableButton(this, wxID_ANY, bmp, "", wxBU_EXACTFIT); - (*btn)->SetToolTip(tooltip); + + (*btn)->Bind(wxEVT_ENTER_WINDOW, [tooltip, btn, this](wxMouseEvent& event) { + p->show_rich_tip(tooltip, *btn); + event.Skip(); + }); (*btn)->Hide(); }; @@ -843,6 +861,11 @@ Sidebar::Sidebar(Plater *parent) p->plater->reslice(); p->plater->select_view_3D("Preview"); }); + + p->btn_reslice->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& event) { + p->show_rich_tip(p->btn_reslice_tip, p->btn_reslice); + event.Skip(); + }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); // p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); @@ -970,7 +993,7 @@ void Sidebar::update_reslice_btn_tooltip() const wxString tooltip = wxString("Slice") + " [" + GUI::shortkey_ctrl_prefix() + "R]"; if (m_mode != comSimple) tooltip += wxString("\n") + _L("Hold Shift to Slice & Export G-code"); - p->btn_reslice->SetToolTip(tooltip); + p->btn_reslice_tip = tooltip; } void Sidebar::msw_rescale() From 9a24b08e2890481f25941ba5e9a8a59fcb315d63 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Oct 2021 11:15:03 +0200 Subject: [PATCH 2/7] Fixed toolpaths colors when loading a 3mf containing color changes and background process is active --- src/slic3r/GUI/GUI_Preview.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index cfe4d6561..a67694541 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -962,10 +962,9 @@ void Preview::load_print_as_fff(bool keep_z_range) if (0 <= type && type < static_cast(GCodeViewer::EViewType::Count)) { m_choice_view_type->SetSelection(type); m_canvas->set_gcode_view_preview_type(static_cast(type)); - if (wxGetApp().is_gcode_viewer()) { + if (wxGetApp().is_gcode_viewer()) m_keep_current_preview_type = true; - refresh_print(); - } + refresh_print(); } } } From 23cff74efbc5ab08e972c109f5c2fc14e7f7dc13 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 5 Oct 2021 12:26:45 +0200 Subject: [PATCH 3/7] Hint notification: selected filament tag check. --- resources/data/hints.ini | 1 + src/slic3r/GUI/HintNotification.cpp | 41 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index e7b1bb681..a79a8228a 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -48,6 +48,7 @@ # enabled_tags = ... # disabled_tags = ... # supported tags are: simple; advanced; expert; FFF; MMU; SLA; Windows; Linux; OSX; +# and all filament types: PLA; PET; ABS; ASA; FLEX; HIPS; EDGE; NGEN; NYLON; PVA; PC; PP; PEI; PEEK; PEKK; POM; PSU; PVDF; SCAFF; # Tags are case sensitive. # FFF is affirmative for both one or more extruder printers. # Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;) diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 1963e7915..4f298eabf 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -5,10 +5,14 @@ #include "GUI_ObjectList.hpp" #include "GLCanvas3D.hpp" #include "MainFrame.hpp" +#include "Tab.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Config.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Preset.hpp" +#include "libslic3r/Config.hpp" +#include "libslic3r/PrintConfig.hpp" #include #include @@ -159,6 +163,33 @@ TagCheckResult tag_check_system(const std::string& tag) return TagCheckNotCompatible; } +TagCheckResult tag_check_material(const std::string& tag) +{ + if (const GUI::Tab* tab = wxGetApp().get_tab(Preset::Type::TYPE_FILAMENT)) { + // search PrintConfig filament_type to find if allowed tag + if (wxGetApp().app_config->get("filament_type").find(tag)) { + const Preset& preset = tab->m_presets->get_edited_preset(); + const auto* opt = preset.config.opt("filament_type"); + if (opt->values[0] == tag) + return TagCheckAffirmative; + return TagCheckNegative; + } + return TagCheckNotCompatible; + } + /* TODO: SLA materials + else if (const GUI::Tab* tab = wxGetApp().get_tab(Preset::Type::TYPE_SLA_MATERIAL)) { + //if (wxGetApp().app_config->get("material_type").find(tag)) { + const Preset& preset = tab->m_presets->get_edited_preset(); + const auto* opt = preset.config.opt("material_type"); + if (opt->values[0] == tag) + return TagCheckAffirmative; + return TagCheckNegative; + //} + return TagCheckNotCompatible; + }*/ + return TagCheckNotCompatible; +} + // return true if NOT in disabled mode. bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags) { @@ -189,6 +220,11 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag if (result == TagCheckResult::TagCheckAffirmative) continue; result = tag_check_system(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + result = tag_check_material(tag); if (result == TagCheckResult::TagCheckNegative) return false; if (result == TagCheckResult::TagCheckAffirmative) @@ -225,6 +261,11 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag if (result == TagCheckResult::TagCheckAffirmative) return false; result = tag_check_system(tag); + if (result == TagCheckResult::TagCheckAffirmative) + return false; + if (result == TagCheckResult::TagCheckNegative) + continue; + result = tag_check_material(tag); if (result == TagCheckResult::TagCheckAffirmative) return false; if (result == TagCheckResult::TagCheckNegative) From 3e07d2e8533dfa0619a3149b9b61616de6ed8715 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 5 Oct 2021 13:21:19 +0200 Subject: [PATCH 4/7] Changed conditions for the warning icon. If mesh isn't manifold then always show the red "exclamation" icon It mesh is manifold but was repaired then show the gray/white "exclamation" icon --- src/slic3r/GUI/GUI_ObjectList.cpp | 57 ++++++++++++++++++------------- src/slic3r/GUI/GUI_ObjectList.hpp | 4 +-- src/slic3r/GUI/Plater.cpp | 36 ++++++------------- 3 files changed, 47 insertions(+), 50 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 1413af553..b83f5e0a6 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -382,46 +382,57 @@ int ObjectList::get_mesh_errors_count(const int obj_idx, const int vol_idx /*= - static std::string get_warning_icon_name(const TriangleMeshStats& stats) { - return stats.repaired() ? (stats.manifold() ? "exclamation_manifold" : "exclamation") : ""; + return stats.manifold() ? (stats.repaired() ? "exclamation_manifold" : "") : "exclamation"; } -std::pair ObjectList::get_mesh_errors(const int obj_idx, const int vol_idx /*= -1*/, bool from_plater /*= false*/) const +std::pair ObjectList::get_mesh_errors(const int obj_idx, const int vol_idx /*= -1*/, wxString* sidebar_info /*= nullptr*/) const { - const int errors = get_mesh_errors_count(obj_idx, vol_idx); - - if (errors == 0) - return { {}, {} }; // hide tooltip - - // Create tooltip string, if there are errors - wxString tooltip = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors) + ":\n"; - const TriangleMeshStats& stats = vol_idx == -1 ? (*m_objects)[obj_idx]->get_object_stl_stats() : (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats(); - if (stats.degenerate_facets > 0) - tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n"; - if (stats.edges_fixed > 0) - tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n"; - if (stats.facets_removed > 0) - tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n"; - if (stats.facets_reversed > 0) - tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n"; - if (stats.backwards_edges > 0) - tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n"; + if (!stats.repaired() && stats.manifold()) { + if (sidebar_info) + *sidebar_info = _L("No errors detected"); + return { {}, {} }; // hide tooltip + } + wxString tooltip, auto_repaired_info, remaining_info; + + // Create tooltip string, if there are errors + if (stats.repaired()) { + const int errors = get_mesh_errors_count(obj_idx, vol_idx); + auto_repaired_info = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors); + tooltip += auto_repaired_info +":\n"; + + if (stats.degenerate_facets > 0) + tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n"; + if (stats.edges_fixed > 0) + tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n"; + if (stats.facets_removed > 0) + tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n"; + if (stats.facets_reversed > 0) + tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n"; + if (stats.backwards_edges > 0) + tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n"; + } if (!stats.manifold()) { + remaining_info = format_wxstr(_L_PLURAL("Remaining %1$d open edge", "Remaining %1$d open edges", stats.open_edges), stats.open_edges); + tooltip += _L("Remaning errors") + ":\n"; tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d open edge", "%1$d open edges", stats.open_edges), stats.open_edges) + "\n"; } - if (is_windows10() && !from_plater) + if (sidebar_info) + *sidebar_info = stats.manifold() ? auto_repaired_info : (remaining_info + (stats.repaired() ? ("\n" + auto_repaired_info) : "")); + + if (is_windows10() && !sidebar_info) tooltip += "\n" + _L("Right button click the icon to fix STL through Netfabb"); return { tooltip, get_warning_icon_name(stats) }; } -std::pair ObjectList::get_mesh_errors(bool from_plater /*= false*/) +std::pair ObjectList::get_mesh_errors(wxString* sidebar_info /*= nullptr*/) { if (!GetSelection()) return { "", "" }; @@ -429,7 +440,7 @@ std::pair ObjectList::get_mesh_errors(bool from_plater /* int obj_idx, vol_idx; get_selected_item_indexes(obj_idx, vol_idx); - return get_mesh_errors(obj_idx, vol_idx, from_plater); + return get_mesh_errors(obj_idx, vol_idx, sidebar_info); } void ObjectList::set_tooltip_for_item(const wxPoint& pt) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index f168f6ff1..54e3f5d45 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -217,8 +217,8 @@ public: // Return value is a pair , used for the tooltip and related warning icon // Function without parameters is for a call from Manipulation panel, // when we don't know parameters of selected item - std::pair get_mesh_errors(const int obj_idx, const int vol_idx = -1, bool from_plater = false) const; - std::pair get_mesh_errors(bool from_plater = false); + std::pair get_mesh_errors(const int obj_idx, const int vol_idx = -1, wxString* sidebar_info = nullptr) const; + std::pair get_mesh_errors(wxString* sidebar_info = nullptr); void set_tooltip_for_item(const wxPoint& pt); void selection_changed(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4726884fe..72cdd3db6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -174,13 +174,10 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : label_materials = init_info_label(&info_materials, _L("Materials")); Add(grid_sizer, 0, wxEXPAND); - auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _L("Manifold") + ":"); - info_manifold_text->SetFont(wxGetApp().small_font()); info_manifold = new wxStaticText(parent, wxID_ANY, ""); info_manifold->SetFont(wxGetApp().small_font()); manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(m_warning_icon_name)); auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); - sizer_manifold->Add(info_manifold_text, 0); sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4); @@ -202,10 +199,10 @@ void ObjectInfo::msw_rescale() void ObjectInfo::update_warning_icon(const std::string& warning_icon_name) { - if (warning_icon_name.empty()) - return; - m_warning_icon_name = warning_icon_name; - manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name)); + if (showing_manifold_warning_icon = !warning_icon_name.empty()) { + m_warning_icon_name = warning_icon_name; + manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name)); + } } enum SlicedInfoIdx @@ -1167,24 +1164,13 @@ void Sidebar::show_info_sizer() p->object_info->info_facets->SetLabel(format_wxstr(_L_PLURAL("%1% (%2$d shell)", "%1% (%2$d shells)", stats.number_of_parts), static_cast(model_object->facets_count()), stats.number_of_parts)); - if (stats.repaired()) { - int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + stats.facets_reversed + stats.backwards_edges; - p->object_info->info_manifold->SetLabel(format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors)); - - auto mesh_errors = obj_list()->get_mesh_errors(true); - wxString tooltip = mesh_errors.first; - - p->object_info->update_warning_icon(mesh_errors.second); - p->object_info->showing_manifold_warning_icon = true; - p->object_info->info_manifold->SetToolTip(tooltip); - p->object_info->manifold_warning_icon->SetToolTip(tooltip); - } - else { - p->object_info->info_manifold->SetLabel(_L("Yes")); - p->object_info->showing_manifold_warning_icon = false; - p->object_info->info_manifold->SetToolTip(""); - p->object_info->manifold_warning_icon->SetToolTip(""); - } + wxString info_manifold_label; + auto mesh_errors = obj_list()->get_mesh_errors(&info_manifold_label); + wxString tooltip = mesh_errors.first; + p->object_info->update_warning_icon(mesh_errors.second); + p->object_info->info_manifold->SetLabel(info_manifold_label); + p->object_info->info_manifold->SetToolTip(tooltip); + p->object_info->manifold_warning_icon->SetToolTip(tooltip); p->object_info->show_sizer(true); From 29a5f48f436f367ad4022a5f06c3a6a150c7f0ba Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 5 Oct 2021 13:31:09 +0200 Subject: [PATCH 5/7] Minor performance optimization for convex intersection algo --- src/libslic3r/Geometry.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index d1b78a02c..420ab473f 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1630,12 +1630,13 @@ void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn) // Set current caliper direction to be the lower edge angle from X axis int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir()); Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia; + Idx *initial = current; bool visitor_continue = true; - size_t a_start = ia.idx(), b_start = ib.idx(); - bool a_finished = false, b_finished = false; + size_t start = initial->idx(); + bool finished = false; - while (visitor_continue && !(a_finished && b_finished)) { + while (visitor_continue && !finished) { Point current_dir_a = current == &ia ? current->dir() : -current->dir(); visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a); @@ -1655,8 +1656,7 @@ void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn) std::swap(current, other); } - if (ia.idx() == a_start) a_finished = true; - if (ib.idx() == b_start) b_finished = true; + if (initial->idx() == start) finished = true; } } @@ -1693,8 +1693,8 @@ bool intersects(const Polygon &A, const Polygon &B) BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}}; BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}}; - if (!bbA.overlap(bbB)) - return false; +// if (!bbA.overlap(bbB)) +// return false; // Establish starting antipodals as extreme vertex pairs in X or Y direction // which reside on different polygons. If no such pair is found, the two From 80844ca3374718af3c6bde05363d732221aeaf53 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 5 Oct 2021 13:36:14 +0200 Subject: [PATCH 6/7] RichTooltips for the buttons on a sidebar: Fixed wrong positioning of the tooltip when PrusaSlicer is on secondary display Show a right triangle tip in the bottom right corner of the tooltip. --- src/slic3r/GUI/Plater.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 72cdd3db6..472c434a0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -656,6 +656,7 @@ void Sidebar::priv::show_rich_tip(const wxString& tooltip, wxButton* btn) return; wxRichToolTip tip(tooltip, ""); tip.SetIcon(wxICON_NONE); + tip.SetTipKind(wxTipKind_BottomRight); tip.SetTitleFont(wxGetApp().normal_font()); tip.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); tip.SetTimeout(1200); From 37219fe4f35c129e800b2c6b8b4e90ec70b9fa0a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Oct 2021 14:49:18 +0200 Subject: [PATCH 7/7] Fixed crash when turning on shells visibility in preview and the application was started with background processing active --- src/slic3r/GUI/GCodeViewer.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 4a3849af8..fd2fe9cbc 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -3203,6 +3203,14 @@ void GCodeViewer::render_shells() if (shader == nullptr) return; + // when the background processing is enabled, it may happen that the shells data have been loaded + // before opengl has been initialized for the preview canvas. + // when this happens, the volumes' data have not been sent to gpu yet. + for (GLVolume* v : m_shells.volumes.volumes) { + if (!v->indexed_vertex_array.has_VBOs()) + v->finalize_geometry(true); + } + // glsafe(::glDepthMask(GL_FALSE)); shader->start_using();