mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	 b9dab7540e
			
		
	
	
		b9dab7540e
		
	
	
	
	
		
			
			StatusBar class calls are commented out and replaced with notifications. SlicicingProgress notification shows progress of slicing, ProgressIndicator notification handles other progress information, like arrange objects etc.
		
			
				
	
	
		
			820 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			820 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "GLGizmoMmuSegmentation.hpp"
 | |
| 
 | |
| #include "slic3r/GUI/GLCanvas3D.hpp"
 | |
| #include "slic3r/GUI/GUI_App.hpp"
 | |
| #include "slic3r/GUI/ImGuiWrapper.hpp"
 | |
| #include "slic3r/GUI/Camera.hpp"
 | |
| #include "slic3r/GUI/Plater.hpp"
 | |
| #include "slic3r/GUI/BitmapCache.hpp"
 | |
| #include "slic3r/GUI/format.hpp"
 | |
| #include "slic3r/GUI/GUI_ObjectList.hpp"
 | |
| #include "slic3r/GUI/NotificationManager.hpp"
 | |
| #include "libslic3r/PresetBundle.hpp"
 | |
| #include "libslic3r/Model.hpp"
 | |
| 
 | |
| 
 | |
| #include <GL/glew.h>
 | |
| 
 | |
| namespace Slic3r::GUI {
 | |
| 
 | |
| static inline void show_notification_extruders_limit_exceeded()
 | |
| {
 | |
|     wxGetApp()
 | |
|         .plater()
 | |
|         ->get_notification_manager()
 | |
|         ->push_notification(NotificationType::MmSegmentationExceededExtrudersLimit, NotificationManager::NotificationLevel::RegularNotificationLevel,
 | |
|                             GUI::format(_L("Your printer has more extruders than the multi-material painting gizmo supports. For this reason, only the "
 | |
|                                            "first %1% extruders will be able to be used for painting."), GLGizmoMmuSegmentation::EXTRUDERS_LIMIT));
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::on_opening()
 | |
| {
 | |
|     if (wxGetApp().extruders_edited_cnt() > int(GLGizmoMmuSegmentation::EXTRUDERS_LIMIT))
 | |
|         show_notification_extruders_limit_exceeded();
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::on_shutdown()
 | |
| {
 | |
|     m_parent.use_slope(false);
 | |
|     m_parent.toggle_model_objects_visibility(true);
 | |
| }
 | |
| 
 | |
| std::string GLGizmoMmuSegmentation::on_get_name() const
 | |
| {
 | |
|     // FIXME Lukas H.: Discuss and change shortcut
 | |
|     return _u8L("Multimaterial painting");
 | |
| }
 | |
| 
 | |
| bool GLGizmoMmuSegmentation::on_is_selectable() const
 | |
| {
 | |
|     return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF
 | |
|             && wxGetApp().get_mode() != comSimple && wxGetApp().extruders_edited_cnt() > 1);
 | |
| }
 | |
| 
 | |
| bool GLGizmoMmuSegmentation::on_is_activable() const
 | |
| {
 | |
|     return GLGizmoPainterBase::on_is_activable() && wxGetApp().extruders_edited_cnt() > 1;
 | |
| }
 | |
| 
 | |
| static std::vector<std::array<float, 4>> get_extruders_colors()
 | |
| {
 | |
|     unsigned char                     rgb_color[3] = {};
 | |
|     std::vector<std::string>          colors       = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
 | |
|     std::vector<std::array<float, 4>> colors_out(colors.size());
 | |
|     for (const std::string &color : colors) {
 | |
|         Slic3r::GUI::BitmapCache::parse_color(color, rgb_color);
 | |
|         size_t color_idx      = &color - &colors.front();
 | |
|         colors_out[color_idx] = {float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f};
 | |
|     }
 | |
| 
 | |
|     return colors_out;
 | |
| }
 | |
| 
 | |
| static std::vector<std::string> get_extruders_names()
 | |
| {
 | |
|     size_t                   extruders_count = wxGetApp().extruders_edited_cnt();
 | |
|     std::vector<std::string> extruders_out;
 | |
|     extruders_out.reserve(extruders_count);
 | |
|     for (size_t extruder_idx = 1; extruder_idx <= extruders_count; ++extruder_idx)
 | |
|         extruders_out.emplace_back("Extruder " + std::to_string(extruder_idx));
 | |
| 
 | |
|     return extruders_out;
 | |
| }
 | |
| 
 | |
| static std::vector<int> get_extruder_id_for_volumes(const ModelObject &model_object)
 | |
| {
 | |
|     std::vector<int> extruders_idx;
 | |
|     extruders_idx.reserve(model_object.volumes.size());
 | |
|     for (const ModelVolume *model_volume : model_object.volumes) {
 | |
|         if (!model_volume->is_model_part())
 | |
|             continue;
 | |
| 
 | |
|         extruders_idx.emplace_back(model_volume->extruder_id());
 | |
|     }
 | |
| 
 | |
|     return extruders_idx;
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::init_extruders_data()
 | |
| {
 | |
|     m_original_extruders_names     = get_extruders_names();
 | |
|     m_original_extruders_colors    = get_extruders_colors();
 | |
|     m_modified_extruders_colors    = m_original_extruders_colors;
 | |
|     m_first_selected_extruder_idx  = 0;
 | |
|     m_second_selected_extruder_idx = 1;
 | |
| }
 | |
| 
 | |
| bool GLGizmoMmuSegmentation::on_init()
 | |
| {
 | |
|     // FIXME Lukas H.: Discuss and change shortcut
 | |
|     m_shortcut_key = WXK_CONTROL_N;
 | |
| 
 | |
|     m_desc["reset_direction"]      = _L("Reset direction");
 | |
|     m_desc["clipping_of_view"]     = _L("Clipping of view") + ": ";
 | |
|     m_desc["cursor_size"]          = _L("Brush size") + ": ";
 | |
|     m_desc["cursor_type"]          = _L("Brush shape");
 | |
|     m_desc["first_color_caption"]  = _L("Left mouse button") + ": ";
 | |
|     m_desc["first_color"]          = _L("First color");
 | |
|     m_desc["second_color_caption"] = _L("Right mouse button") + ": ";
 | |
|     m_desc["second_color"]         = _L("Second color");
 | |
|     m_desc["remove_caption"]       = _L("Shift + Left mouse button") + ": ";
 | |
|     m_desc["remove"]               = _L("Remove painted color");
 | |
|     m_desc["remove_all"]           = _L("Remove all painted areas");
 | |
|     m_desc["circle"]               = _L("Circle");
 | |
|     m_desc["sphere"]               = _L("Sphere");
 | |
|     m_desc["pointer"]              = _L("Pointer");
 | |
| 
 | |
|     m_desc["tool_type"]            = _L("Tool type");
 | |
|     m_desc["tool_brush"]           = _L("Brush");
 | |
|     m_desc["tool_smart_fill"]      = _L("Smart fill");
 | |
|     m_desc["tool_bucket_fill"]     = _L("Bucket fill");
 | |
| 
 | |
|     m_desc["smart_fill_angle"]     = _L("Smart fill angle");
 | |
| 
 | |
|     init_extruders_data();
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::render_painter_gizmo() const
 | |
| {
 | |
|     const Selection& selection = m_parent.get_selection();
 | |
| 
 | |
|     glsafe(::glEnable(GL_BLEND));
 | |
|     glsafe(::glEnable(GL_DEPTH_TEST));
 | |
| 
 | |
|     render_triangles(selection, false);
 | |
| 
 | |
|     m_c->object_clipper()->render_cut();
 | |
|     m_c->instances_hider()->render_cut();
 | |
|     render_cursor();
 | |
| 
 | |
|     glsafe(::glDisable(GL_BLEND));
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::set_painter_gizmo_data(const Selection &selection)
 | |
| {
 | |
|     GLGizmoPainterBase::set_painter_gizmo_data(selection);
 | |
| 
 | |
|     if (m_state != On || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF || wxGetApp().extruders_edited_cnt() <= 1)
 | |
|         return;
 | |
| 
 | |
|     ModelObject *model_object         = m_c->selection_info()->model_object();
 | |
|     if (int prev_extruders_count = int(m_original_extruders_colors.size());
 | |
|         prev_extruders_count != wxGetApp().extruders_edited_cnt() || get_extruders_colors() != m_original_extruders_colors) {
 | |
|         if (wxGetApp().extruders_edited_cnt() > int(GLGizmoMmuSegmentation::EXTRUDERS_LIMIT))
 | |
|             show_notification_extruders_limit_exceeded();
 | |
| 
 | |
|         this->init_extruders_data();
 | |
|         // Reinitialize triangle selectors because of change of extruder count need also change the size of GLIndexedVertexArray
 | |
|         if (prev_extruders_count != wxGetApp().extruders_edited_cnt())
 | |
|             this->init_model_triangle_selectors();
 | |
|     } else if (model_object != nullptr && get_extruder_id_for_volumes(*model_object) != m_original_volumes_extruder_idxs) {
 | |
|         this->init_model_triangle_selectors();
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void render_extruders_combo(const std::string                       &label,
 | |
|                                    const std::vector<std::string>          &extruders,
 | |
|                                    const std::vector<std::array<float, 4>> &extruders_colors,
 | |
|                                    size_t                                  &selection_idx)
 | |
| {
 | |
|     assert(!extruders_colors.empty());
 | |
|     assert(extruders_colors.size() == extruders_colors.size());
 | |
| 
 | |
|     auto convert_to_imu32 = [](const std::array<float, 4> &color) -> ImU32 {
 | |
|         return IM_COL32(uint8_t(color[0] * 255.f), uint8_t(color[1] * 255.f), uint8_t(color[2] * 255.f), uint8_t(color[3] * 255.f));
 | |
|     };
 | |
| 
 | |
|     size_t selection_out = selection_idx;
 | |
|     // It is necessary to use BeginGroup(). Otherwise, when using SameLine() is called, then other items will be drawn inside the combobox.
 | |
|     ImGui::BeginGroup();
 | |
|     ImVec2 combo_pos = ImGui::GetCursorScreenPos();
 | |
|     if (ImGui::BeginCombo(label.c_str(), "")) {
 | |
|         for (size_t extruder_idx = 0; extruder_idx < std::min(extruders.size(), GLGizmoMmuSegmentation::EXTRUDERS_LIMIT); ++extruder_idx) {
 | |
|             ImGui::PushID(int(extruder_idx));
 | |
|             ImVec2 start_position = ImGui::GetCursorScreenPos();
 | |
| 
 | |
|             if (ImGui::Selectable("", extruder_idx == selection_idx))
 | |
|                 selection_out = extruder_idx;
 | |
| 
 | |
|             ImGui::SameLine();
 | |
|             ImGuiStyle &style  = ImGui::GetStyle();
 | |
|             float       height = ImGui::GetTextLineHeight();
 | |
|             ImGui::GetWindowDrawList()->AddRectFilled(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), convert_to_imu32(extruders_colors[extruder_idx]));
 | |
|             ImGui::GetWindowDrawList()->AddRect(start_position, ImVec2(start_position.x + height + height / 2, start_position.y + height), IM_COL32_BLACK);
 | |
| 
 | |
|             ImGui::SetCursorScreenPos(ImVec2(start_position.x + height + height / 2 + style.FramePadding.x, start_position.y));
 | |
|             ImGui::Text("%s", extruders[extruder_idx].c_str());
 | |
|             ImGui::PopID();
 | |
|         }
 | |
| 
 | |
|         ImGui::EndCombo();
 | |
|     }
 | |
| 
 | |
|     ImVec2      backup_pos = ImGui::GetCursorScreenPos();
 | |
|     ImGuiStyle &style      = ImGui::GetStyle();
 | |
| 
 | |
|     ImGui::SetCursorScreenPos(ImVec2(combo_pos.x + style.FramePadding.x, combo_pos.y + style.FramePadding.y));
 | |
|     ImVec2 p      = ImGui::GetCursorScreenPos();
 | |
|     float  height = ImGui::GetTextLineHeight();
 | |
| 
 | |
|     ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + height + height / 2, p.y + height), convert_to_imu32(extruders_colors[selection_idx]));
 | |
|     ImGui::GetWindowDrawList()->AddRect(p, ImVec2(p.x + height + height / 2, p.y + height), IM_COL32_BLACK);
 | |
| 
 | |
|     ImGui::SetCursorScreenPos(ImVec2(p.x + height + height / 2 + style.FramePadding.x, p.y));
 | |
|     ImGui::Text("%s", extruders[selection_out].c_str());
 | |
|     ImGui::SetCursorScreenPos(backup_pos);
 | |
|     ImGui::EndGroup();
 | |
| 
 | |
|     selection_idx = selection_out;
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bottom_limit)
 | |
| {
 | |
|     if (!m_c->selection_info()->model_object())
 | |
|         return;
 | |
| 
 | |
|     const float approx_height = m_imgui->scaled(25.0f);
 | |
|                             y = std::min(y, bottom_limit - approx_height);
 | |
|     m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
 | |
| 
 | |
|     m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
 | |
| 
 | |
|     // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
 | |
|     const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
 | |
|                                                 m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
 | |
|     const float cursor_slider_left       = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
 | |
|     const float smart_fill_slider_left   = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.f);
 | |
| 
 | |
|     const float cursor_type_radio_circle  = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
 | |
|     const float cursor_type_radio_sphere  = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f);
 | |
|     const float cursor_type_radio_pointer = m_imgui->calc_text_size(m_desc["pointer"]).x + m_imgui->scaled(2.5f);
 | |
| 
 | |
|     const float button_width             = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
 | |
|     const float buttons_width            = m_imgui->scaled(0.5f);
 | |
|     const float minimal_slider_width     = m_imgui->scaled(4.f);
 | |
|     const float color_button_width       = m_imgui->calc_text_size("").x + m_imgui->scaled(1.75f);
 | |
|     const float combo_label_width        = std::max(m_imgui->calc_text_size(m_desc.at("first_color")).x,
 | |
|                                                     m_imgui->calc_text_size(m_desc.at("second_color")).x) + m_imgui->scaled(1.f);
 | |
| 
 | |
|     const float tool_type_radio_brush       = m_imgui->calc_text_size(m_desc["tool_brush"]).x + m_imgui->scaled(2.5f);
 | |
|     const float tool_type_radio_bucket_fill = m_imgui->calc_text_size(m_desc["tool_bucket_fill"]).x + m_imgui->scaled(2.5f);
 | |
|     const float tool_type_radio_smart_fill  = m_imgui->calc_text_size(m_desc["tool_smart_fill"]).x + m_imgui->scaled(2.5f);
 | |
| 
 | |
|     float caption_max    = 0.f;
 | |
|     float total_text_max = 0.;
 | |
|     for (const auto &t : std::array<std::string, 3>{"first_color", "second_color", "remove"}) {
 | |
|         caption_max    = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t + "_caption")).x);
 | |
|         total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x);
 | |
|     }
 | |
|     caption_max += m_imgui->scaled(1.f);
 | |
|     total_text_max += m_imgui->scaled(1.f);
 | |
| 
 | |
|     float sliders_width = std::max(smart_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left));
 | |
|     float window_width = minimal_slider_width + sliders_width;
 | |
|     window_width       = std::max(window_width, total_text_max);
 | |
|     window_width       = std::max(window_width, button_width);
 | |
|     window_width       = std::max(window_width, cursor_type_radio_circle + cursor_type_radio_sphere + cursor_type_radio_pointer);
 | |
|     window_width       = std::max(window_width, tool_type_radio_brush + tool_type_radio_bucket_fill + tool_type_radio_smart_fill);
 | |
|     window_width       = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f));
 | |
| 
 | |
|     auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) {
 | |
|         m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption);
 | |
|         ImGui::SameLine(caption_max);
 | |
|         m_imgui->text(text);
 | |
|     };
 | |
| 
 | |
|     for (const auto &t : std::array<std::string, 3>{"first_color", "second_color", "remove"})
 | |
|         draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t));
 | |
| 
 | |
|     m_imgui->text("");
 | |
|     ImGui::Separator();
 | |
| 
 | |
|     m_imgui->text(m_desc.at("first_color"));
 | |
|     ImGui::SameLine(combo_label_width);
 | |
|     ImGui::PushItemWidth(window_width - combo_label_width - color_button_width);
 | |
|     render_extruders_combo("##first_color_combo", m_original_extruders_names, m_original_extruders_colors, m_first_selected_extruder_idx);
 | |
|     ImGui::SameLine();
 | |
| 
 | |
|     const std::array<float, 4> &select_first_color = m_modified_extruders_colors[m_first_selected_extruder_idx];
 | |
|     ImVec4                      first_color        = ImVec4(select_first_color[0], select_first_color[1], select_first_color[2], select_first_color[3]);
 | |
|     if(ImGui::ColorEdit4("First color##color_picker", (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
 | |
|         m_modified_extruders_colors[m_first_selected_extruder_idx] = {first_color.x, first_color.y, first_color.z, first_color.w};
 | |
| 
 | |
|     m_imgui->text(m_desc.at("second_color"));
 | |
|     ImGui::SameLine(combo_label_width);
 | |
|     ImGui::PushItemWidth(window_width - combo_label_width - color_button_width);
 | |
|     render_extruders_combo("##second_color_combo", m_original_extruders_names, m_original_extruders_colors, m_second_selected_extruder_idx);
 | |
|     ImGui::SameLine();
 | |
| 
 | |
|     const std::array<float, 4> &select_second_color = m_modified_extruders_colors[m_second_selected_extruder_idx];
 | |
|     ImVec4                      second_color        = ImVec4(select_second_color[0], select_second_color[1], select_second_color[2], select_second_color[3]);
 | |
|     if(ImGui::ColorEdit4("Second color##color_picker", (float*)&second_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
 | |
|         m_modified_extruders_colors[m_second_selected_extruder_idx] = {second_color.x, second_color.y, second_color.z, second_color.w};
 | |
| 
 | |
|     const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
 | |
| 
 | |
|     ImGui::Separator();
 | |
| 
 | |
|     ImGui::AlignTextToFramePadding();
 | |
|     m_imgui->text(m_desc.at("tool_type"));
 | |
| 
 | |
|     float tool_type_offset = (window_width - tool_type_radio_brush - tool_type_radio_bucket_fill - tool_type_radio_smart_fill + m_imgui->scaled(2.f)) / 2.f;
 | |
| 
 | |
|     ImGui::NewLine();
 | |
| 
 | |
|     ImGui::AlignTextToFramePadding();
 | |
|     ImGui::SameLine(tool_type_offset + m_imgui->scaled(0.f));
 | |
|     ImGui::PushItemWidth(tool_type_radio_brush);
 | |
|     if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BRUSH)) {
 | |
|         m_tool_type = GLGizmoMmuSegmentation::ToolType::BRUSH;
 | |
|         for (auto &triangle_selector : m_triangle_selectors) {
 | |
|             triangle_selector->seed_fill_unselect_all_triangles();
 | |
|             triangle_selector->request_update_render_data();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (ImGui::IsItemHovered()) {
 | |
|         ImGui::BeginTooltip();
 | |
|         ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|         ImGui::TextUnformatted(_L("Paints facets according to the chosen painting brush.").ToUTF8().data());
 | |
|         ImGui::PopTextWrapPos();
 | |
|         ImGui::EndTooltip();
 | |
|     }
 | |
| 
 | |
|     ImGui::SameLine(tool_type_offset + tool_type_radio_brush + m_imgui->scaled(0.f));
 | |
|     ImGui::PushItemWidth(tool_type_radio_smart_fill);
 | |
|     if (m_imgui->radio_button(m_desc["tool_smart_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::SMART_FILL)) {
 | |
|         m_tool_type = GLGizmoMmuSegmentation::ToolType::SMART_FILL;
 | |
|         for (auto &triangle_selector : m_triangle_selectors) {
 | |
|             triangle_selector->seed_fill_unselect_all_triangles();
 | |
|             triangle_selector->request_update_render_data();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (ImGui::IsItemHovered()) {
 | |
|         ImGui::BeginTooltip();
 | |
|         ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|         ImGui::TextUnformatted(_L("Paints neighboring facets whose relative angle is less or equal to set angle.").ToUTF8().data());
 | |
|         ImGui::PopTextWrapPos();
 | |
|         ImGui::EndTooltip();
 | |
|     }
 | |
| 
 | |
|     ImGui::SameLine(tool_type_offset + tool_type_radio_brush + tool_type_radio_smart_fill + m_imgui->scaled(0.f));
 | |
|     ImGui::PushItemWidth(tool_type_radio_bucket_fill);
 | |
|     if (m_imgui->radio_button(m_desc["tool_bucket_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BUCKET_FILL)) {
 | |
|         m_tool_type = GLGizmoMmuSegmentation::ToolType::BUCKET_FILL;
 | |
|         for (auto &triangle_selector : m_triangle_selectors) {
 | |
|             triangle_selector->seed_fill_unselect_all_triangles();
 | |
|             triangle_selector->request_update_render_data();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (ImGui::IsItemHovered()) {
 | |
|         ImGui::BeginTooltip();
 | |
|         ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|         ImGui::TextUnformatted(_L("Paints neighboring facets that have the same color.").ToUTF8().data());
 | |
|         ImGui::PopTextWrapPos();
 | |
|         ImGui::EndTooltip();
 | |
|     }
 | |
| 
 | |
|     ImGui::Separator();
 | |
| 
 | |
|     if(m_tool_type == ToolType::BRUSH) {
 | |
|         ImGui::AlignTextToFramePadding();
 | |
|         m_imgui->text(m_desc.at("cursor_type"));
 | |
|         ImGui::NewLine();
 | |
| 
 | |
|         float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle - cursor_type_radio_pointer + m_imgui->scaled(2.f)) / 2.f;
 | |
|         ImGui::AlignTextToFramePadding();
 | |
|         ImGui::SameLine(cursor_type_offset + m_imgui->scaled(0.f));
 | |
|         ImGui::PushItemWidth(cursor_type_radio_sphere);
 | |
|         if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE))
 | |
|             m_cursor_type = TriangleSelector::CursorType::SPHERE;
 | |
| 
 | |
|         if (ImGui::IsItemHovered()) {
 | |
|             ImGui::BeginTooltip();
 | |
|             ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|             ImGui::TextUnformatted(_L("Paints all facets inside, regardless of their orientation.").ToUTF8().data());
 | |
|             ImGui::PopTextWrapPos();
 | |
|             ImGui::EndTooltip();
 | |
|         }
 | |
| 
 | |
|         ImGui::SameLine(cursor_type_offset +cursor_type_radio_sphere + m_imgui->scaled(0.f));
 | |
|         ImGui::PushItemWidth(cursor_type_radio_circle);
 | |
| 
 | |
|         if (m_imgui->radio_button(m_desc["circle"], m_cursor_type == TriangleSelector::CursorType::CIRCLE))
 | |
|             m_cursor_type = TriangleSelector::CursorType::CIRCLE;
 | |
| 
 | |
|         if (ImGui::IsItemHovered()) {
 | |
|             ImGui::BeginTooltip();
 | |
|             ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|             ImGui::TextUnformatted(_L("Ignores facets facing away from the camera.").ToUTF8().data());
 | |
|             ImGui::PopTextWrapPos();
 | |
|             ImGui::EndTooltip();
 | |
|         }
 | |
| 
 | |
|         ImGui::SameLine(cursor_type_offset + cursor_type_radio_sphere + cursor_type_radio_circle + m_imgui->scaled(0.f));
 | |
|         ImGui::PushItemWidth(cursor_type_radio_pointer);
 | |
| 
 | |
|         if (m_imgui->radio_button(m_desc["pointer"], m_cursor_type == TriangleSelector::CursorType::POINTER))
 | |
|             m_cursor_type = TriangleSelector::CursorType::POINTER;
 | |
| 
 | |
|         if (ImGui::IsItemHovered()) {
 | |
|             ImGui::BeginTooltip();
 | |
|             ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|             ImGui::TextUnformatted(_L("Paints only one facet.").ToUTF8().data());
 | |
|             ImGui::PopTextWrapPos();
 | |
|             ImGui::EndTooltip();
 | |
|         }
 | |
| 
 | |
|         m_imgui->disabled_begin(m_cursor_type != TriangleSelector::CursorType::SPHERE && m_cursor_type != TriangleSelector::CursorType::CIRCLE);
 | |
| 
 | |
|         ImGui::AlignTextToFramePadding();
 | |
|         m_imgui->text(m_desc.at("cursor_size"));
 | |
|         ImGui::SameLine(sliders_width);
 | |
|         ImGui::PushItemWidth(window_width - sliders_width);
 | |
|         m_imgui->slider_float(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
 | |
|         if (ImGui::IsItemHovered()) {
 | |
|             ImGui::BeginTooltip();
 | |
|             ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|             ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data());
 | |
|             ImGui::PopTextWrapPos();
 | |
|             ImGui::EndTooltip();
 | |
|         }
 | |
| 
 | |
|         m_imgui->checkbox(_L("Split triangles"), m_triangle_splitting_enabled);
 | |
| 
 | |
|         if (ImGui::IsItemHovered()) {
 | |
|             ImGui::BeginTooltip();
 | |
|             ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|             ImGui::TextUnformatted(_L("Split bigger facets into smaller ones while the object is painted.").ToUTF8().data());
 | |
|             ImGui::PopTextWrapPos();
 | |
|             ImGui::EndTooltip();
 | |
|         }
 | |
| 
 | |
|         m_imgui->disabled_end();
 | |
| 
 | |
|         ImGui::Separator();
 | |
|     } else if(m_tool_type == ToolType::SMART_FILL) {
 | |
|         m_imgui->text(m_desc["smart_fill_angle"] + ":");
 | |
|         ImGui::AlignTextToFramePadding();
 | |
|         std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Degree sign to use in the respective slider in MMU gizmo,"
 | |
|                                                                                 "placed after the number with no whitespace in between.");
 | |
|         ImGui::SameLine(sliders_width);
 | |
|         ImGui::PushItemWidth(window_width - sliders_width);
 | |
|         if(m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data()))
 | |
|             for (auto &triangle_selector : m_triangle_selectors) {
 | |
|                 triangle_selector->seed_fill_unselect_all_triangles();
 | |
|                 triangle_selector->request_update_render_data();
 | |
|             }
 | |
| 
 | |
|         if (ImGui::IsItemHovered()) {
 | |
|             ImGui::BeginTooltip();
 | |
|             ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|             ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data());
 | |
|             ImGui::PopTextWrapPos();
 | |
|             ImGui::EndTooltip();
 | |
|         }
 | |
| 
 | |
|         ImGui::Separator();
 | |
|     }
 | |
| 
 | |
|     if (m_c->object_clipper()->get_position() == 0.f) {
 | |
|         ImGui::AlignTextToFramePadding();
 | |
|         m_imgui->text(m_desc.at("clipping_of_view"));
 | |
|     } else {
 | |
|         if (m_imgui->button(m_desc.at("reset_direction"))) {
 | |
|             wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position(-1., false); });
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     ImGui::SameLine(sliders_width);
 | |
|     ImGui::PushItemWidth(window_width - sliders_width);
 | |
|     auto clp_dist = float(m_c->object_clipper()->get_position());
 | |
|     if (m_imgui->slider_float("  ", &clp_dist, 0.f, 1.f, "%.2f"))
 | |
|         m_c->object_clipper()->set_position(clp_dist, true);
 | |
|     if (ImGui::IsItemHovered()) {
 | |
|         ImGui::BeginTooltip();
 | |
|         ImGui::PushTextWrapPos(max_tooltip_width);
 | |
|         ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data());
 | |
|         ImGui::PopTextWrapPos();
 | |
|         ImGui::EndTooltip();
 | |
|     }
 | |
| 
 | |
|     ImGui::Separator();
 | |
|     if (m_imgui->button(m_desc.at("remove_all"))) {
 | |
|         Plater::TakeSnapshot snapshot(wxGetApp().plater(), wxString(_L("Reset selection")));
 | |
|         ModelObject *        mo  = m_c->selection_info()->model_object();
 | |
|         int                  idx = -1;
 | |
|         for (ModelVolume *mv : mo->volumes)
 | |
|             if (mv->is_model_part()) {
 | |
|                 ++idx;
 | |
|                 m_triangle_selectors[idx]->reset();
 | |
|                 m_triangle_selectors[idx]->request_update_render_data();
 | |
|             }
 | |
| 
 | |
|         update_model_object();
 | |
|         m_parent.set_as_dirty();
 | |
|     }
 | |
| 
 | |
|     m_imgui->end();
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::update_model_object() const
 | |
| {
 | |
|     bool updated = false;
 | |
|     ModelObject* mo = m_c->selection_info()->model_object();
 | |
|     int idx = -1;
 | |
|     for (ModelVolume* mv : mo->volumes) {
 | |
|         if (! mv->is_model_part())
 | |
|             continue;
 | |
|         ++idx;
 | |
|         updated |= mv->mmu_segmentation_facets.set(*m_triangle_selectors[idx].get());
 | |
|     }
 | |
| 
 | |
|     if (updated) {
 | |
|         const ModelObjectPtrs &mos = wxGetApp().model().objects;
 | |
|         wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin());
 | |
|         m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | |
|     }
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::init_model_triangle_selectors()
 | |
| {
 | |
|     const ModelObject *mo = m_c->selection_info()->model_object();
 | |
|     m_triangle_selectors.clear();
 | |
| 
 | |
|     // Don't continue when extruders colors are not initialized
 | |
|     if(m_original_extruders_colors.empty())
 | |
|         return;
 | |
| 
 | |
|     for (const ModelVolume *mv : mo->volumes) {
 | |
|         if (!mv->is_model_part())
 | |
|             continue;
 | |
| 
 | |
|         // This mesh does not account for the possible Z up SLA offset.
 | |
|         const TriangleMesh *mesh = &mv->mesh();
 | |
| 
 | |
|         int extruder_idx = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0;
 | |
|         m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorMmGui>(*mesh, m_modified_extruders_colors, m_original_extruders_colors[size_t(extruder_idx)]));
 | |
|         // Reset of TriangleSelector is done inside TriangleSelectorMmGUI's constructor, so we don't need it to perform it again in deserialize().
 | |
|         m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data(), false);
 | |
|         m_triangle_selectors.back()->request_update_render_data();
 | |
|     }
 | |
|     m_original_volumes_extruder_idxs = get_extruder_id_for_volumes(*mo);
 | |
| }
 | |
| 
 | |
| void GLGizmoMmuSegmentation::update_from_model_object()
 | |
| {
 | |
|     wxBusyCursor wait;
 | |
| 
 | |
|     // Extruder colors need to be reloaded before calling init_model_triangle_selectors to render painted triangles
 | |
|     // using colors from loaded 3MF and not from printer profile in Slicer.
 | |
|     if (int prev_extruders_count = int(m_original_extruders_colors.size());
 | |
|         prev_extruders_count != wxGetApp().extruders_edited_cnt() || get_extruders_colors() != m_original_extruders_colors)
 | |
|         this->init_extruders_data();
 | |
| 
 | |
|     this->init_model_triangle_selectors();
 | |
| }
 | |
| 
 | |
| PainterGizmoType GLGizmoMmuSegmentation::get_painter_type() const
 | |
| {
 | |
|     return PainterGizmoType::MMU_SEGMENTATION;
 | |
| }
 | |
| 
 | |
| std::array<float, 4> GLGizmoMmuSegmentation::get_cursor_sphere_left_button_color() const
 | |
| {
 | |
|     const std::array<float, 4> &color = m_modified_extruders_colors[m_first_selected_extruder_idx];
 | |
|     return {color[0], color[1], color[2], 0.25f};
 | |
| }
 | |
| 
 | |
| std::array<float, 4> GLGizmoMmuSegmentation::get_cursor_sphere_right_button_color() const
 | |
| {
 | |
|     const std::array<float, 4> &color = m_modified_extruders_colors[m_second_selected_extruder_idx];
 | |
|     return {color[0], color[1], color[2], 0.25f};
 | |
| }
 | |
| 
 | |
| static std::array<float, 4> get_seed_fill_color(const std::array<float, 4> &base_color)
 | |
| {
 | |
|     return {base_color[0] * 0.75f, base_color[1] * 0.75f, base_color[2] * 0.75f, 1.f};
 | |
| }
 | |
| 
 | |
| void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
 | |
| {
 | |
|     if (m_update_render_data)
 | |
|         update_render_data();
 | |
| 
 | |
|     auto *shader = wxGetApp().get_current_shader();
 | |
|     if (!shader)
 | |
|         return;
 | |
|     assert(shader->get_name() == "gouraud");
 | |
|     ScopeGuard guard([shader]() { if (shader) shader->set_uniform("compute_triangle_normals_in_fs", false);});
 | |
|     shader->set_uniform("compute_triangle_normals_in_fs", true);
 | |
| 
 | |
|     for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx)
 | |
|         if (m_gizmo_scene.has_VBOs(color_idx)) {
 | |
|             if (color_idx > m_colors.size()) // Seed fill VBO
 | |
|                 shader->set_uniform("uniform_color", get_seed_fill_color(color_idx == (m_colors.size() + 1) ? m_default_volume_color : m_colors[color_idx - (m_colors.size() + 1) - 1]));
 | |
|             else                             // Normal VBO
 | |
|                 shader->set_uniform("uniform_color", color_idx == 0 ? m_default_volume_color : m_colors[color_idx - 1]);
 | |
| 
 | |
|             m_gizmo_scene.render(color_idx);
 | |
|         }
 | |
| 
 | |
|     if (m_gizmo_scene.has_contour_VBO()) {
 | |
|         ScopeGuard guard_gouraud([shader]() { shader->start_using(); });
 | |
|         shader->stop_using();
 | |
| 
 | |
|         auto *contour_shader = wxGetApp().get_shader("mm_contour");
 | |
|         contour_shader->start_using();
 | |
| 
 | |
|         glsafe(::glDepthFunc(GL_LEQUAL));
 | |
|         m_gizmo_scene.render_contour();
 | |
|         glsafe(::glDepthFunc(GL_LESS));
 | |
| 
 | |
|         contour_shader->stop_using();
 | |
|     }
 | |
| 
 | |
|     m_update_render_data = false;
 | |
| }
 | |
| 
 | |
| void TriangleSelectorMmGui::update_render_data()
 | |
| {
 | |
|     m_gizmo_scene.release_geometry();
 | |
|     m_vertices.reserve(m_vertices.size() * 3);
 | |
|     for (const Vertex &vr : m_vertices) {
 | |
|         m_gizmo_scene.vertices.emplace_back(vr.v.x());
 | |
|         m_gizmo_scene.vertices.emplace_back(vr.v.y());
 | |
|         m_gizmo_scene.vertices.emplace_back(vr.v.z());
 | |
|     }
 | |
|     m_gizmo_scene.finalize_vertices();
 | |
| 
 | |
|     for (const Triangle &tr : m_triangles)
 | |
|         if (tr.valid() && !tr.is_split()) {
 | |
|             int               color = int(tr.get_state()) <= int(m_colors.size()) ? int(tr.get_state()) : 0;
 | |
|             assert(m_colors.size() + 1 + color < m_gizmo_scene.triangle_indices.size());
 | |
|             std::vector<int> &iva   = m_gizmo_scene.triangle_indices[color + tr.is_selected_by_seed_fill() * (m_colors.size() + 1)];
 | |
| 
 | |
|             if (iva.size() + 3 > iva.capacity())
 | |
|                 iva.reserve(next_highest_power_of_2(iva.size() + 3));
 | |
| 
 | |
|             iva.emplace_back(tr.verts_idxs[0]);
 | |
|             iva.emplace_back(tr.verts_idxs[1]);
 | |
|             iva.emplace_back(tr.verts_idxs[2]);
 | |
|         }
 | |
| 
 | |
|     for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx)
 | |
|         m_gizmo_scene.triangle_indices_sizes[color_idx] = m_gizmo_scene.triangle_indices[color_idx].size();
 | |
| 
 | |
|     m_gizmo_scene.finalize_triangle_indices();
 | |
| 
 | |
|     std::vector<Vec2i> contour_edges = this->get_seed_fill_contour();
 | |
|     m_gizmo_scene.contour_vertices.reserve(contour_edges.size() * 6);
 | |
|     for (const Vec2i &edge : contour_edges) {
 | |
|         m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.x());
 | |
|         m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.y());
 | |
|         m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(0)].v.z());
 | |
| 
 | |
|         m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.x());
 | |
|         m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.y());
 | |
|         m_gizmo_scene.contour_vertices.emplace_back(m_vertices[edge(1)].v.z());
 | |
|     }
 | |
| 
 | |
|     m_gizmo_scene.contour_indices.assign(m_gizmo_scene.contour_vertices.size() / 3, 0);
 | |
|     std::iota(m_gizmo_scene.contour_indices.begin(), m_gizmo_scene.contour_indices.end(), 0);
 | |
|     m_gizmo_scene.contour_indices_size = m_gizmo_scene.contour_indices.size();
 | |
| 
 | |
|     m_gizmo_scene.finalize_contour();
 | |
| }
 | |
| 
 | |
| wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const
 | |
| {
 | |
|     wxString action_name;
 | |
|     if (shift_down)
 | |
|         action_name = _L("Remove painted color");
 | |
|     else {
 | |
|         size_t extruder_id = (button_down == Button::Left ? m_first_selected_extruder_idx : m_second_selected_extruder_idx) + 1;
 | |
|         action_name        = GUI::format(_L("Painted using: Extruder %1%"), extruder_id);
 | |
|     }
 | |
|     return action_name;
 | |
| }
 | |
| 
 | |
| void GLMmSegmentationGizmo3DScene::release_geometry() {
 | |
|     if (this->vertices_VBO_id) {
 | |
|         glsafe(::glDeleteBuffers(1, &this->vertices_VBO_id));
 | |
|         this->vertices_VBO_id = 0;
 | |
|     }
 | |
|     for(auto &triangle_indices_VBO_id : triangle_indices_VBO_ids) {
 | |
|         glsafe(::glDeleteBuffers(1, &triangle_indices_VBO_id));
 | |
|         triangle_indices_VBO_id = 0;
 | |
|     }
 | |
|     if (this->contour_vertices_VBO_id) {
 | |
|         glsafe(::glDeleteBuffers(1, &this->contour_vertices_VBO_id));
 | |
|         this->contour_vertices_VBO_id = 0;
 | |
|     }
 | |
|     if (this->contour_indices_VBO_id) {
 | |
|         glsafe(::glDeleteBuffers(1, &this->contour_indices_VBO_id));
 | |
|         this->contour_indices_VBO_id = 0;
 | |
|     }
 | |
|     this->clear();
 | |
| }
 | |
| 
 | |
| void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const
 | |
| {
 | |
|     assert(triangle_indices_idx < this->triangle_indices_VBO_ids.size());
 | |
|     assert(this->triangle_indices_sizes.size() == this->triangle_indices_VBO_ids.size());
 | |
|     assert(this->vertices_VBO_id != 0);
 | |
|     assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 0);
 | |
| 
 | |
|     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id));
 | |
|     glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float))));
 | |
| 
 | |
|     glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
 | |
| 
 | |
|     // Render using the Vertex Buffer Objects.
 | |
|     if (this->triangle_indices_sizes[triangle_indices_idx] > 0) {
 | |
|         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_ids[triangle_indices_idx]));
 | |
|         glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_sizes[triangle_indices_idx]), GL_UNSIGNED_INT, nullptr));
 | |
|         glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | |
|     }
 | |
| 
 | |
|     glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
 | |
| 
 | |
|     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | |
| }
 | |
| 
 | |
| void GLMmSegmentationGizmo3DScene::render_contour() const
 | |
| {
 | |
|     assert(this->contour_vertices_VBO_id != 0);
 | |
|     assert(this->contour_indices_VBO_id != 0);
 | |
| 
 | |
|     glsafe(::glLineWidth(4.0f));
 | |
| 
 | |
|     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_vertices_VBO_id));
 | |
|     glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), nullptr));
 | |
| 
 | |
|     glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
 | |
| 
 | |
|     if (this->contour_indices_size > 0) {
 | |
|         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->contour_indices_VBO_id));
 | |
|         glsafe(::glDrawElements(GL_LINES, GLsizei(this->contour_indices_size), GL_UNSIGNED_INT, nullptr));
 | |
|         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | |
|     }
 | |
| 
 | |
|     glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
 | |
| 
 | |
|     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | |
| }
 | |
| 
 | |
| void GLMmSegmentationGizmo3DScene::finalize_vertices()
 | |
| {
 | |
|     assert(this->vertices_VBO_id == 0);
 | |
|     if (!this->vertices.empty()) {
 | |
|         glsafe(::glGenBuffers(1, &this->vertices_VBO_id));
 | |
|         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id));
 | |
|         glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(float), this->vertices.data(), GL_STATIC_DRAW));
 | |
|         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | |
|         this->vertices.clear();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void GLMmSegmentationGizmo3DScene::finalize_triangle_indices()
 | |
| {
 | |
|     assert(std::all_of(triangle_indices_VBO_ids.cbegin(), triangle_indices_VBO_ids.cend(), [](const auto &ti_VBO_id) { return ti_VBO_id == 0; }));
 | |
| 
 | |
|     assert(this->triangle_indices.size() == this->triangle_indices_VBO_ids.size());
 | |
|     for (size_t buffer_idx = 0; buffer_idx < this->triangle_indices.size(); ++buffer_idx)
 | |
|         if (!this->triangle_indices[buffer_idx].empty()) {
 | |
|             glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_ids[buffer_idx]));
 | |
|             glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_ids[buffer_idx]));
 | |
|             glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices[buffer_idx].size() * sizeof(int), this->triangle_indices[buffer_idx].data(), GL_STATIC_DRAW));
 | |
|             glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | |
|             this->triangle_indices[buffer_idx].clear();
 | |
|         }
 | |
| }
 | |
| 
 | |
| void GLMmSegmentationGizmo3DScene::finalize_contour()
 | |
| {
 | |
|     assert(this->contour_vertices_VBO_id == 0);
 | |
|     assert(this->contour_indices_VBO_id == 0);
 | |
| 
 | |
|     if (!this->contour_vertices.empty()) {
 | |
|         glsafe(::glGenBuffers(1, &this->contour_vertices_VBO_id));
 | |
|         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_vertices_VBO_id));
 | |
|         glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_vertices.size() * sizeof(float), this->contour_vertices.data(), GL_STATIC_DRAW));
 | |
|         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | |
|         this->contour_vertices.clear();
 | |
|     }
 | |
| 
 | |
|     if (!this->contour_indices.empty()) {
 | |
|         glsafe(::glGenBuffers(1, &this->contour_indices_VBO_id));
 | |
|         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->contour_indices_VBO_id));
 | |
|         glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW));
 | |
|         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | |
|         this->contour_indices.clear();
 | |
|     }
 | |
| }
 | |
| 
 | |
| } // namespace Slic3r
 |