Reworked visualization of selected triangles by seed fill and bucket fill inside the multi-material painting gizmo. Now is drawn boundary around a selected area, and also all triangles inside the selected area are drawn with a darker color.
This commit is contained in:
parent
e5ce748b10
commit
18cff61bd2
8 changed files with 239 additions and 34 deletions
6
resources/shaders/mm_contour.fs
Normal file
6
resources/shaders/mm_contour.fs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#version 110
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
}
|
6
resources/shaders/mm_contour.vs
Normal file
6
resources/shaders/mm_contour.vs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#version 110
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = ftransform();
|
||||||
|
}
|
|
@ -295,11 +295,48 @@ void TriangleSelector::append_touching_subtriangles(int itriangle, int vertexi,
|
||||||
process_subtriangle(touching.second, Partition::Second);
|
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<Vec2i> &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<int, int> 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);
|
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.
|
// 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;
|
return;
|
||||||
|
|
||||||
assert(!m_triangles[start_facet_idx].is_split());
|
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<Vec2i> TriangleSelector::get_seed_fill_contour() const {
|
||||||
|
std::vector<Vec2i> 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<Vec2i> &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<std::pair<int, int>>, std::vector<bool>> TriangleSelector::serialize() const
|
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector::serialize() const
|
||||||
{
|
{
|
||||||
// Each original triangle of the mesh is assigned a number encoding its state
|
// Each original triangle of the mesh is assigned a number encoding its state
|
||||||
|
|
|
@ -54,7 +54,8 @@ public:
|
||||||
|
|
||||||
void bucket_fill_select_triangles(const Vec3f &hit, // point where to start
|
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
|
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 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;
|
bool has_facets(EnforcerBlockerType state) const;
|
||||||
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, EnforcerBlockerType test_state);
|
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, EnforcerBlockerType test_state);
|
||||||
|
@ -63,6 +64,8 @@ public:
|
||||||
indexed_triangle_set get_facets(EnforcerBlockerType state) const;
|
indexed_triangle_set get_facets(EnforcerBlockerType state) const;
|
||||||
// Get facets at a given state. Triangulate T-joints.
|
// Get facets at a given state. Triangulate T-joints.
|
||||||
indexed_triangle_set get_facets_strict(EnforcerBlockerType state) const;
|
indexed_triangle_set get_facets_strict(EnforcerBlockerType state) const;
|
||||||
|
// Get edges around the selected area by seed fill.
|
||||||
|
std::vector<Vec2i> get_seed_fill_contour() const;
|
||||||
|
|
||||||
// Set facet of the mesh to a given state. Only works for original triangles.
|
// Set facet of the mesh to a given state. Only works for original triangles.
|
||||||
void set_facet(int facet_idx, EnforcerBlockerType state);
|
void set_facet(int facet_idx, EnforcerBlockerType state);
|
||||||
|
@ -222,6 +225,7 @@ private:
|
||||||
std::pair<int, int> triangle_subtriangles(int itriangle, int vertexi, int vertexj) const;
|
std::pair<int, int> triangle_subtriangles(int itriangle, int vertexi, int vertexj) const;
|
||||||
|
|
||||||
void append_touching_subtriangles(int itriangle, int vertexi, int vertexj, std::vector<int> &touching_subtriangles_out) const;
|
void append_touching_subtriangles(int itriangle, int vertexi, int vertexj, std::vector<int> &touching_subtriangles_out) const;
|
||||||
|
void append_touching_edges(int itriangle, int vertexi, int vertexj, std::vector<Vec2i> &touching_edges_out) const;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
bool verify_triangle_neighbors(const Triangle& tr, const Vec3i& neighbors) const;
|
bool verify_triangle_neighbors(const Triangle& tr, const Vec3i& neighbors) const;
|
||||||
|
@ -235,6 +239,8 @@ private:
|
||||||
std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||||
void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||||
|
|
||||||
|
void get_seed_fill_contour_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec2i> &edges_out) const;
|
||||||
|
|
||||||
int m_free_triangles_head { -1 };
|
int m_free_triangles_head { -1 };
|
||||||
int m_free_vertices_head { -1 };
|
int m_free_vertices_head { -1 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,6 +61,8 @@ std::pair<bool, std::string> GLShadersManager::init()
|
||||||
);
|
);
|
||||||
// used to render variable layers heights in 3d editor
|
// used to render variable layers heights in 3d editor
|
||||||
valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" });
|
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 };
|
return { valid, error };
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,8 +336,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::BeginTooltip();
|
ImGui::BeginTooltip();
|
||||||
ImGui::PushTextWrapPos(max_tooltip_width);
|
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.");
|
"placed after the number with no whitespace in between.");
|
||||||
ImGui::SameLine(sliders_width);
|
ImGui::SameLine(sliders_width);
|
||||||
ImGui::PushItemWidth(window_width - 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()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::BeginTooltip();
|
ImGui::BeginTooltip();
|
||||||
ImGui::PushTextWrapPos(max_tooltip_width);
|
ImGui::PushTextWrapPos(max_tooltip_width);
|
||||||
|
@ -594,10 +597,13 @@ std::array<float, 4> GLGizmoMmuSegmentation::get_cursor_sphere_right_button_colo
|
||||||
return {color[0], color[1], color[2], 0.25f};
|
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)
|
void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
|
||||||
{
|
{
|
||||||
static constexpr std::array<float, 4> seed_fill_color{0.f, 1.f, 0.44f, 1.f};
|
|
||||||
|
|
||||||
if (m_update_render_data)
|
if (m_update_render_data)
|
||||||
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)
|
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 (m_gizmo_scene.has_VBOs(color_idx)) {
|
||||||
shader->set_uniform("uniform_color", color_idx == 0 ? m_default_volume_color :
|
if (color_idx > m_colors.size()) // Seed fill VBO
|
||||||
color_idx == (m_gizmo_scene.triangle_indices.size() - 1) ? seed_fill_color :
|
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]));
|
||||||
m_colors[color_idx - 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);
|
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;
|
m_update_render_data = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,10 +650,10 @@ void TriangleSelectorMmGui::update_render_data()
|
||||||
|
|
||||||
for (const Triangle &tr : m_triangles)
|
for (const Triangle &tr : m_triangles)
|
||||||
if (tr.valid() && !tr.is_split()) {
|
if (tr.valid() && !tr.is_split()) {
|
||||||
int color = int(tr.get_state());
|
int color = int(tr.get_state()) <= int(m_colors.size()) ? int(tr.get_state()) : 0;
|
||||||
std::vector<int> &iva = tr.is_selected_by_seed_fill() ? m_gizmo_scene.triangle_indices.back() :
|
assert(m_colors.size() + 1 + color < m_gizmo_scene.triangle_indices.size());
|
||||||
color < int(m_gizmo_scene.triangle_indices.size() - 1) ? m_gizmo_scene.triangle_indices[color] :
|
std::vector<int> &iva = m_gizmo_scene.triangle_indices[color + tr.is_selected_by_seed_fill() * (m_colors.size() + 1)];
|
||||||
m_gizmo_scene.triangle_indices.front();
|
|
||||||
if (iva.size() + 3 > iva.capacity())
|
if (iva.size() + 3 > iva.capacity())
|
||||||
iva.reserve(next_highest_power_of_2(iva.size() + 3));
|
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.triangle_indices_sizes[color_idx] = m_gizmo_scene.triangle_indices[color_idx].size();
|
||||||
|
|
||||||
m_gizmo_scene.finalize_triangle_indices();
|
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 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));
|
glsafe(::glDeleteBuffers(1, &triangle_indices_VBO_id));
|
||||||
triangle_indices_VBO_id = 0;
|
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();
|
this->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,6 +725,10 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const
|
||||||
assert(this->vertices_VBO_id != 0);
|
assert(this->vertices_VBO_id != 0);
|
||||||
assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 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(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id));
|
||||||
glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float))));
|
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));
|
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()
|
void GLMmSegmentationGizmo3DScene::finalize_vertices()
|
||||||
{
|
{
|
||||||
assert(this->vertices_VBO_id == 0);
|
assert(this->vertices_VBO_id == 0);
|
||||||
if (!this->vertices.empty()) {
|
if (!this->vertices.empty()) {
|
||||||
glsafe(::glGenBuffers(1, &this->vertices_VBO_id));
|
glsafe(::glGenBuffers(1, &this->vertices_VBO_id));
|
||||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 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));
|
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||||
this->vertices.clear();
|
this->vertices.clear();
|
||||||
}
|
}
|
||||||
|
@ -719,19 +790,32 @@ void GLMmSegmentationGizmo3DScene::finalize_triangle_indices()
|
||||||
if (!this->triangle_indices[buffer_idx].empty()) {
|
if (!this->triangle_indices[buffer_idx].empty()) {
|
||||||
glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_ids[buffer_idx]));
|
glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_ids[buffer_idx]));
|
||||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 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(),
|
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices[buffer_idx].size() * sizeof(int), this->triangle_indices[buffer_idx].data(), GL_STATIC_DRAW));
|
||||||
GL_STATIC_DRAW));
|
|
||||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||||
this->triangle_indices[buffer_idx].clear();
|
this->triangle_indices[buffer_idx].clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLMmSegmentationGizmo3DScene::finalize_geometry()
|
void GLMmSegmentationGizmo3DScene::finalize_contour()
|
||||||
{
|
{
|
||||||
assert(this->vertices_VBO_id == 0);
|
assert(this->contour_vertices_VBO_id == 0);
|
||||||
assert(this->triangle_indices.size() == this->triangle_indices_VBO_ids.size());
|
assert(this->contour_indices_VBO_id == 0);
|
||||||
finalize_vertices();
|
|
||||||
finalize_triangle_indices();
|
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
|
} // namespace Slic3r
|
||||||
|
|
|
@ -25,9 +25,8 @@ public:
|
||||||
return this->triangle_indices_VBO_ids[triangle_indices_idx] != 0;
|
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
|
[[nodiscard]] inline bool has_contour_VBO() const { return this->contour_indices_VBO_id != 0; }
|
||||||
// and possibly releasing it if it has been loaded into the VBOs.
|
|
||||||
void finalize_geometry();
|
|
||||||
// Release the geometry data, release OpenGL VBOs.
|
// Release the geometry data, release OpenGL VBOs.
|
||||||
void release_geometry();
|
void release_geometry();
|
||||||
// Finalize the initialization of the geometry, upload the geometry to OpenGL VBO objects
|
// 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
|
// 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.
|
// and possibly releasing it if it has been loaded into the VBOs.
|
||||||
void finalize_triangle_indices();
|
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()
|
void clear()
|
||||||
{
|
{
|
||||||
|
@ -45,28 +47,41 @@ public:
|
||||||
|
|
||||||
for (size_t &triangle_indices_size : this->triangle_indices_sizes)
|
for (size_t &triangle_indices_size : this->triangle_indices_sizes)
|
||||||
triangle_indices_size = 0;
|
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(size_t triangle_indices_idx) const;
|
||||||
|
|
||||||
|
void render_contour() const;
|
||||||
|
|
||||||
std::vector<float> vertices;
|
std::vector<float> vertices;
|
||||||
std::vector<std::vector<int>> triangle_indices;
|
std::vector<std::vector<int>> triangle_indices;
|
||||||
|
|
||||||
|
std::vector<float> contour_vertices;
|
||||||
|
std::vector<int> contour_indices;
|
||||||
|
|
||||||
// When the triangle indices are loaded into the graphics card as Vertex Buffer Objects,
|
// 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.
|
// the above mentioned std::vectors are cleared and the following variables keep their original length.
|
||||||
std::vector<size_t> triangle_indices_sizes;
|
std::vector<size_t> triangle_indices_sizes;
|
||||||
|
size_t contour_indices_size{0};
|
||||||
|
|
||||||
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
|
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
|
||||||
// Zero if the VBOs are not sent to GPU yet.
|
// Zero if the VBOs are not sent to GPU yet.
|
||||||
unsigned int vertices_VBO_id{0};
|
unsigned int vertices_VBO_id{0};
|
||||||
std::vector<unsigned int> triangle_indices_VBO_ids;
|
std::vector<unsigned int> triangle_indices_VBO_ids;
|
||||||
|
|
||||||
|
unsigned int contour_vertices_VBO_id{0};
|
||||||
|
unsigned int contour_indices_VBO_id{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
class TriangleSelectorMmGui : public TriangleSelectorGUI {
|
class TriangleSelectorMmGui : public TriangleSelectorGUI {
|
||||||
public:
|
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<std::array<float, 4>> &colors, const std::array<float, 4> &default_volume_color)
|
explicit TriangleSelectorMmGui(const TriangleMesh &mesh, const std::vector<std::array<float, 4>> &colors, const std::array<float, 4> &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;
|
~TriangleSelectorMmGui() override = default;
|
||||||
|
|
||||||
// Render current selection. Transformation matrices are supposed
|
// Render current selection. Transformation matrices are supposed
|
||||||
|
|
|
@ -387,6 +387,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
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)) {
|
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);
|
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;
|
m_seed_fill_last_mesh_id = -1;
|
||||||
} else if (m_tool_type == ToolType::BRUSH)
|
} 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,
|
m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type,
|
||||||
|
|
Loading…
Reference in a new issue