diff --git a/resources/shaders/mm_contour.fs b/resources/shaders/mm_contour.fs new file mode 100644 index 000000000..14c18dcf1 --- /dev/null +++ b/resources/shaders/mm_contour.fs @@ -0,0 +1,6 @@ +#version 110 + +void main() +{ + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/resources/shaders/mm_contour.vs b/resources/shaders/mm_contour.vs new file mode 100644 index 000000000..d0d3ee42a --- /dev/null +++ b/resources/shaders/mm_contour.vs @@ -0,0 +1,6 @@ +#version 110 + +void main() +{ + gl_Position = ftransform(); +} diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 9e16c0317..485a7fcf2 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -295,11 +295,48 @@ void TriangleSelector::append_touching_subtriangles(int itriangle, int vertexi, process_subtriangle(touching.second, Partition::Second); } -void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate) +// It appends all edges that are touching the edge (vertexi, vertexj) of the triangle and are not selected by seed fill +// It doesn't append the edges that are touching the triangle only by part of the edge that means the triangles are from lower depth. +void TriangleSelector::append_touching_edges(int itriangle, int vertexi, int vertexj, std::vector &touching_edges_out) const +{ + if (itriangle == -1) + return; + + auto process_subtriangle = [this, &itriangle, &vertexi, &vertexj, &touching_edges_out](const int subtriangle_idx, Partition partition) -> void { + assert(subtriangle_idx != -1); + if (!m_triangles[subtriangle_idx].is_split()) { + if (!m_triangles[subtriangle_idx].is_selected_by_seed_fill()) { + int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj); + if (partition == Partition::First && midpoint != -1) { + touching_edges_out.emplace_back(vertexi, midpoint); + } else if (partition == Partition::First && midpoint == -1) { + touching_edges_out.emplace_back(vertexi, vertexj); + } else { + assert(midpoint != -1 && partition == Partition::Second); + touching_edges_out.emplace_back(midpoint, vertexj); + } + } + } else if (int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj); midpoint != -1) + append_touching_edges(subtriangle_idx, partition == Partition::First ? vertexi : midpoint, partition == Partition::First ? midpoint : vertexj, + touching_edges_out); + else + append_touching_edges(subtriangle_idx, vertexi, vertexj, touching_edges_out); + }; + + std::pair touching = this->triangle_subtriangles(itriangle, vertexi, vertexj); + if (touching.first != -1) + process_subtriangle(touching.first, Partition::First); + + if (touching.second != -1) + process_subtriangle(touching.second, Partition::Second); +} + +void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate, bool force_reselection) { int start_facet_idx = select_unsplit_triangle(hit, facet_start); + assert(start_facet_idx != -1); // Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill. - if (start_facet_idx == -1 || m_triangles[start_facet_idx].is_selected_by_seed_fill()) + if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection)) return; assert(!m_triangles[start_facet_idx].is_split()); @@ -1358,6 +1395,48 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const } } +std::vector TriangleSelector::get_seed_fill_contour() const { + std::vector edges_out; + for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) { + const Vec3i neighbors = root_neighbors(*m_mesh, facet_idx); + assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors)); + this->get_seed_fill_contour_recursive(facet_idx, neighbors, neighbors, edges_out); + } + + return edges_out; +} + +void TriangleSelector::get_seed_fill_contour_recursive(const int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector &edges_out) const { + assert(facet_idx != -1 && facet_idx < int(m_triangles.size())); + assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors)); + const Triangle *tr = &m_triangles[facet_idx]; + if (!tr->valid()) + return; + + if (tr->is_split()) { + int num_of_children = tr->number_of_split_sides() + 1; + if (num_of_children != 1) { + for (int i = 0; i < num_of_children; ++i) { + assert(i < int(tr->children.size())); + assert(tr->children[i] < int(m_triangles.size())); + // Recursion, deep first search over the children of this triangle. + // All children of this triangle were created by splitting a single source triangle of the original mesh. + this->get_seed_fill_contour_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i), this->child_neighbors_propagated(*tr, neighbors_propagated, i), edges_out); + } + } + } else if (tr->is_selected_by_seed_fill()) { + Vec3i vertices = {m_triangles[facet_idx].verts_idxs[0], m_triangles[facet_idx].verts_idxs[1], m_triangles[facet_idx].verts_idxs[2]}; + append_touching_edges(neighbors(0), vertices(1), vertices(0), edges_out); + append_touching_edges(neighbors(1), vertices(2), vertices(1), edges_out); + append_touching_edges(neighbors(2), vertices(0), vertices(2), edges_out); + + // It appends the edges that are touching the triangle only by part of the edge that means the triangles are from lower depth. + for (int idx = 0; idx < 3; ++idx) + if (int neighbor_tr_idx = neighbors_propagated(idx); neighbor_tr_idx != -1 && !m_triangles[neighbor_tr_idx].is_split() && !m_triangles[neighbor_tr_idx].is_selected_by_seed_fill()) + edges_out.emplace_back(vertices(idx), vertices(next_idx_modulo(idx, 3))); + } +} + std::pair>, std::vector> TriangleSelector::serialize() const { // Each original triangle of the mesh is assigned a number encoding its state diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 39c1ad22e..2ab053123 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -53,8 +53,9 @@ public: bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle void bucket_fill_select_triangles(const Vec3f &hit, // point where to start - int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to - bool propagate); // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. + int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to + bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. + bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle bool has_facets(EnforcerBlockerType state) const; static bool has_facets(const std::pair>, std::vector> &data, EnforcerBlockerType test_state); @@ -63,6 +64,8 @@ public: indexed_triangle_set get_facets(EnforcerBlockerType state) const; // Get facets at a given state. Triangulate T-joints. indexed_triangle_set get_facets_strict(EnforcerBlockerType state) const; + // Get edges around the selected area by seed fill. + std::vector get_seed_fill_contour() const; // Set facet of the mesh to a given state. Only works for original triangles. void set_facet(int facet_idx, EnforcerBlockerType state); @@ -222,6 +225,7 @@ private: std::pair triangle_subtriangles(int itriangle, int vertexi, int vertexj) const; void append_touching_subtriangles(int itriangle, int vertexi, int vertexj, std::vector &touching_subtriangles_out) const; + void append_touching_edges(int itriangle, int vertexi, int vertexj, std::vector &touching_edges_out) const; #ifndef NDEBUG bool verify_triangle_neighbors(const Triangle& tr, const Vec3i& neighbors) const; @@ -235,6 +239,8 @@ private: std::vector &out_triangles) const; void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector &out_triangles) const; + void get_seed_fill_contour_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector &edges_out) const; + int m_free_triangles_head { -1 }; int m_free_vertices_head { -1 }; }; diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 788fe90c0..33eec63e8 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -61,6 +61,8 @@ std::pair GLShadersManager::init() ); // used to render variable layers heights in 3d editor valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" }); + // used to render highlight contour around selected triangles inside the multi-material gizmo + valid &= append_shader("mm_contour", { "mm_contour.vs", "mm_contour.fs" }); return { valid, error }; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index c99281359..3d8be9eff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -336,8 +336,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott } } - - if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(max_tooltip_width); @@ -467,7 +465,12 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott "placed after the number with no whitespace in between."); ImGui::SameLine(sliders_width); ImGui::PushItemWidth(window_width - sliders_width); - m_imgui->slider_float("##seed_fill_angle", &m_seed_fill_angle, SeedFillAngleMin, SeedFillAngleMax, format_str.data()); + if(m_imgui->slider_float("##seed_fill_angle", &m_seed_fill_angle, SeedFillAngleMin, SeedFillAngleMax, 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); @@ -594,10 +597,13 @@ std::array GLGizmoMmuSegmentation::get_cursor_sphere_right_button_colo return {color[0], color[1], color[2], 0.25f}; } +static std::array get_seed_fill_color(const std::array &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) { - static constexpr std::array seed_fill_color{0.f, 1.f, 0.44f, 1.f}; - if (m_update_render_data) update_render_data(); @@ -610,12 +616,24 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui) for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx) if (m_gizmo_scene.has_VBOs(color_idx)) { - shader->set_uniform("uniform_color", color_idx == 0 ? m_default_volume_color : - color_idx == (m_gizmo_scene.triangle_indices.size() - 1) ? seed_fill_color : - m_colors[color_idx - 1]); + 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(); + m_gizmo_scene.render_contour(); + contour_shader->stop_using(); + } + m_update_render_data = false; } @@ -632,10 +650,10 @@ void TriangleSelectorMmGui::update_render_data() for (const Triangle &tr : m_triangles) if (tr.valid() && !tr.is_split()) { - int color = int(tr.get_state()); - std::vector &iva = tr.is_selected_by_seed_fill() ? m_gizmo_scene.triangle_indices.back() : - color < int(m_gizmo_scene.triangle_indices.size() - 1) ? m_gizmo_scene.triangle_indices[color] : - m_gizmo_scene.triangle_indices.front(); + 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 &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)); @@ -648,6 +666,24 @@ void TriangleSelectorMmGui::update_render_data() m_gizmo_scene.triangle_indices_sizes[color_idx] = m_gizmo_scene.triangle_indices[color_idx].size(); m_gizmo_scene.finalize_triangle_indices(); + + std::vector 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 @@ -671,6 +707,14 @@ void GLMmSegmentationGizmo3DScene::release_geometry() { 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(); } @@ -681,6 +725,10 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const assert(this->vertices_VBO_id != 0); assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 0); + ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); }); + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + glsafe(::glPolygonOffset(5.0, 5.0)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id)); glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float)))); @@ -698,13 +746,36 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const 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() * 4, this->vertices.data(), GL_STATIC_DRAW)); + 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(); } @@ -719,19 +790,32 @@ void GLMmSegmentationGizmo3DScene::finalize_triangle_indices() 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() * 4, this->triangle_indices[buffer_idx].data(), - GL_STATIC_DRAW)); + 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_geometry() +void GLMmSegmentationGizmo3DScene::finalize_contour() { - assert(this->vertices_VBO_id == 0); - assert(this->triangle_indices.size() == this->triangle_indices_VBO_ids.size()); - finalize_vertices(); - finalize_triangle_indices(); + 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 diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index b1b19bfac..afd5854a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -25,9 +25,8 @@ public: return this->triangle_indices_VBO_ids[triangle_indices_idx] != 0; } - // Finalize the initialization of the geometry and indices, upload the geometry and indices to OpenGL VBO objects - // and possibly releasing it if it has been loaded into the VBOs. - void finalize_geometry(); + [[nodiscard]] inline bool has_contour_VBO() const { return this->contour_indices_VBO_id != 0; } + // Release the geometry data, release OpenGL VBOs. void release_geometry(); // Finalize the initialization of the geometry, upload the geometry to OpenGL VBO objects @@ -36,6 +35,9 @@ public: // Finalize the initialization of the indices, upload the indices to OpenGL VBO objects // and possibly releasing it if it has been loaded into the VBOs. void finalize_triangle_indices(); + // Finalize the initialization of the contour geometry and the indices, upload both to OpenGL VBO objects + // and possibly releasing it if it has been loaded into the VBOs. + void finalize_contour(); void clear() { @@ -45,28 +47,41 @@ public: for (size_t &triangle_indices_size : this->triangle_indices_sizes) triangle_indices_size = 0; + + this->contour_vertices.clear(); + this->contour_indices.clear(); + this->contour_indices_size = 0; } void render(size_t triangle_indices_idx) const; + void render_contour() const; + std::vector vertices; std::vector> triangle_indices; + std::vector contour_vertices; + std::vector contour_indices; + // When the triangle indices are loaded into the graphics card as Vertex Buffer Objects, // the above mentioned std::vectors are cleared and the following variables keep their original length. std::vector triangle_indices_sizes; + size_t contour_indices_size{0}; // IDs of the Vertex Array Objects, into which the geometry has been loaded. // Zero if the VBOs are not sent to GPU yet. unsigned int vertices_VBO_id{0}; std::vector triangle_indices_VBO_ids; + + unsigned int contour_vertices_VBO_id{0}; + unsigned int contour_indices_VBO_id{0}; }; class TriangleSelectorMmGui : public TriangleSelectorGUI { public: - // Plus 2 in the initialization of m_gizmo_scene is because the first position is allocated for non-painted triangles, and the last position is allocated for seed fill. + // Plus 1 in the initialization of m_gizmo_scene is because the first position is allocated for non-painted triangles, and the indices above colors.size() are allocated for seed fill. explicit TriangleSelectorMmGui(const TriangleMesh &mesh, const std::vector> &colors, const std::array &default_volume_color) - : TriangleSelectorGUI(mesh), m_colors(colors), m_default_volume_color(default_volume_color), m_gizmo_scene(colors.size() + 2) {} + : TriangleSelectorGUI(mesh), m_colors(colors), m_default_volume_color(default_volume_color), m_gizmo_scene(2 * (colors.size() + 1)) {} ~TriangleSelectorMmGui() override = default; // Render current selection. Transformation matrices are supposed diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 2e8c5a4a7..ac9d7adcf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -324,11 +324,11 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous new_state = action == SLAGizmoEventType::LeftDown ? this->get_left_button_state_type() : this->get_right_button_state_type(); } - const Camera& camera = wxGetApp().plater()->get_camera(); - const Selection& selection = m_parent.get_selection(); - const ModelObject* mo = m_c->selection_info()->model_object(); - const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; - const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); + const Camera &camera = wxGetApp().plater()->get_camera(); + const Selection &selection = m_parent.get_selection(); + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; + const Transform3d &instance_trafo = mi->get_transformation().get_matrix(); // List of mouse positions that will be used as seeds for painting. std::vector mouse_positions{mouse_position}; @@ -387,6 +387,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous assert(m_rr.mesh_id < int(m_triangle_selectors.size())); if (m_tool_type == ToolType::SEED_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) { m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); + if (m_tool_type == ToolType::SEED_FILL) + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle, true); + else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false, true); + else if (m_tool_type == ToolType::BUCKET_FILL) + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), true, true); + m_seed_fill_last_mesh_id = -1; } else if (m_tool_type == ToolType::BRUSH) m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type,