Optimize painting using single triangle brush.

Especially on the detail models, painting using the single triangle brush is noticeably faster.
This commit is contained in:
Lukáš Hejl 2024-04-02 12:24:22 +02:00 committed by Lukas Matena
parent 8cd2e8264a
commit 72f47f7963
3 changed files with 59 additions and 16 deletions

View File

@ -583,11 +583,17 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f &hit, int facet_
assert(!m_triangles[start_facet_idx].is_split()); assert(!m_triangles[start_facet_idx].is_split());
TriangleStateType start_facet_state = m_triangles[start_facet_idx].get_state(); TriangleStateType start_facet_state = m_triangles[start_facet_idx].get_state();
this->seed_fill_unselect_all_triangles();
if (propagate == BucketFillPropagate::NO) { if (propagate == BucketFillPropagate::NO) {
if (m_triangle_selected_by_seed_fill != -1)
this->seed_fill_unselect_triangle(m_triangle_selected_by_seed_fill);
m_triangles[start_facet_idx].select_by_seed_fill(); m_triangles[start_facet_idx].select_by_seed_fill();
m_triangle_selected_by_seed_fill = start_facet_idx;
return; return;
} else {
m_triangle_selected_by_seed_fill = -1;
this->seed_fill_unselect_all_triangles();
} }
const float facet_angle_limit = std::cos(Geometry::deg2rad(bucket_fill_angle)) - EPSILON; const float facet_angle_limit = std::cos(Geometry::deg2rad(bucket_fill_angle)) - EPSILON;
@ -1286,10 +1292,10 @@ void TriangleSelector::undivide_triangle(int facet_idx)
} }
} }
void TriangleSelector::remove_useless_children(int facet_idx) // Returns true when some triangle during recursive descending was removed (undivided).
{ bool TriangleSelector::remove_useless_children(int facet_idx) {
// Check that all children are leafs of the same type. If not, try to // Check that all children are leafs of the same type. If not, try to
// make them (recursive call). Remove them if sucessful. // make them (recursive call). Remove them if successful.
assert(facet_idx < int(m_triangles.size()) && m_triangles[facet_idx].valid()); assert(facet_idx < int(m_triangles.size()) && m_triangles[facet_idx].valid());
Triangle &tr = m_triangles[facet_idx]; Triangle &tr = m_triangles[facet_idx];
@ -1297,31 +1303,32 @@ void TriangleSelector::remove_useless_children(int facet_idx)
if (!tr.is_split()) { if (!tr.is_split()) {
// This is a leaf, there nothing to do. This can happen during the // This is a leaf, there nothing to do. This can happen during the
// first (non-recursive call). Shouldn't otherwise. // first (non-recursive call). Shouldn't otherwise.
return; return false;
} }
// Call this for all non-leaf children. // Call this for all non-leaf children.
bool children_removed = false;
for (int child_idx = 0; child_idx <= tr.number_of_split_sides(); ++child_idx) { for (int child_idx = 0; child_idx <= tr.number_of_split_sides(); ++child_idx) {
assert(child_idx < int(m_triangles.size()) && m_triangles[child_idx].valid()); assert(child_idx < int(m_triangles.size()) && m_triangles[child_idx].valid());
if (m_triangles[tr.children[child_idx]].is_split()) if (m_triangles[tr.children[child_idx]].is_split())
remove_useless_children(tr.children[child_idx]); children_removed |= remove_useless_children(tr.children[child_idx]);
} }
// Return if a child is not leaf or two children differ in type. // Return if a child is not leaf or two children differ in type.
TriangleStateType first_child_type = TriangleStateType::NONE; TriangleStateType first_child_type = TriangleStateType::NONE;
for (int child_idx = 0; child_idx <= tr.number_of_split_sides(); ++child_idx) { for (int child_idx = 0; child_idx <= tr.number_of_split_sides(); ++child_idx) {
if (m_triangles[tr.children[child_idx]].is_split()) if (m_triangles[tr.children[child_idx]].is_split())
return; return children_removed;
if (child_idx == 0) if (child_idx == 0)
first_child_type = m_triangles[tr.children[0]].get_state(); first_child_type = m_triangles[tr.children[0]].get_state();
else if (m_triangles[tr.children[child_idx]].get_state() != first_child_type) else if (m_triangles[tr.children[child_idx]].get_state() != first_child_type)
return; return children_removed;
} }
// If we got here, the children can be removed. // If we got here, the children can be removed.
undivide_triangle(facet_idx); undivide_triangle(facet_idx);
tr.set_state(first_child_type); tr.set_state(first_child_type);
return true;
} }
void TriangleSelector::garbage_collect() void TriangleSelector::garbage_collect()
@ -2050,7 +2057,22 @@ void TriangleSelector::seed_fill_unselect_all_triangles()
triangle.unselect_by_seed_fill(); triangle.unselect_by_seed_fill();
} }
void TriangleSelector::seed_fill_unselect_triangle(const int facet_idx) {
assert(facet_idx > 0 && facet_idx < m_triangles.size());
Triangle &triangle = m_triangles[facet_idx];
assert(!triangle.is_split());
if (!triangle.is_split())
triangle.unselect_by_seed_fill();
}
void TriangleSelector::seed_fill_apply_on_triangles(TriangleStateType new_state) { void TriangleSelector::seed_fill_apply_on_triangles(TriangleStateType new_state) {
if (m_triangle_selected_by_seed_fill != -1) {
this->seed_fill_apply_on_single_triangle(new_state, m_triangle_selected_by_seed_fill);
m_triangle_selected_by_seed_fill = -1;
return;
}
for (Triangle &triangle : m_triangles) for (Triangle &triangle : m_triangles)
if (!triangle.is_split() && triangle.is_selected_by_seed_fill()) if (!triangle.is_split() && triangle.is_selected_by_seed_fill())
triangle.set_state(new_state); triangle.set_state(new_state);
@ -2062,6 +2084,15 @@ void TriangleSelector::seed_fill_apply_on_triangles(TriangleStateType new_state)
} }
} }
void TriangleSelector::seed_fill_apply_on_single_triangle(TriangleStateType new_state, const int facet_idx) {
assert(facet_idx > 0 && facet_idx < m_triangles.size());
if (Triangle &triangle = m_triangles[facet_idx]; !triangle.is_split() && triangle.is_selected_by_seed_fill()) {
triangle.set_state(new_state);
remove_useless_children(triangle.source_triangle);
}
}
double TriangleSelector::get_triangle_area(const Triangle &triangle) const { double TriangleSelector::get_triangle_area(const Triangle &triangle) const {
const stl_vertex &v0 = m_vertices[triangle.verts_idxs[0]].v; const stl_vertex &v0 = m_vertices[triangle.verts_idxs[0]].v;
const stl_vertex &v1 = m_vertices[triangle.verts_idxs[1]].v; const stl_vertex &v1 = m_vertices[triangle.verts_idxs[1]].v;

View File

@ -401,10 +401,17 @@ public:
// For all triangles, remove the flag indicating that the triangle was selected by seed fill. // For all triangles, remove the flag indicating that the triangle was selected by seed fill.
void seed_fill_unselect_all_triangles(); void seed_fill_unselect_all_triangles();
// Remove the flag indicating that the triangle was selected by seed fill.
void seed_fill_unselect_triangle(int facet_idx);
// For all triangles selected by seed fill, set new TriangleStateType and remove flag indicating that triangle was selected by seed fill. // For all triangles selected by seed fill, set new TriangleStateType and remove flag indicating that triangle was selected by seed fill.
// The operation may merge split triangles if they are being assigned the same color. // The operation may merge split triangles if they are being assigned the same color.
void seed_fill_apply_on_triangles(TriangleStateType new_state); void seed_fill_apply_on_triangles(TriangleStateType new_state);
// For the triangle selected by seed fill, set a new TriangleStateType and remove the flag indicating that the triangle was selected by seed fill.
// The operation may merge a split triangle if it is being assigned the same color.
void seed_fill_apply_on_single_triangle(TriangleStateType new_state, const int facet_idx);
// Compute total area of the triangle. // Compute total area of the triangle.
double get_triangle_area(const Triangle &triangle) const; double get_triangle_area(const Triangle &triangle) const;
@ -495,13 +502,16 @@ protected:
std::unique_ptr<Cursor> m_cursor; std::unique_ptr<Cursor> m_cursor;
// Single triangle selected by seed fill. It is used to optimize painting using a single triangle brush.
int m_triangle_selected_by_seed_fill = -1;
// Private functions: // Private functions:
private: private:
bool select_triangle(int facet_idx, TriangleStateType type, bool triangle_splitting); bool select_triangle(int facet_idx, TriangleStateType type, bool triangle_splitting);
bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, TriangleStateType type, bool triangle_splitting); bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, TriangleStateType type, bool triangle_splitting);
void undivide_triangle(int facet_idx); void undivide_triangle(int facet_idx);
void split_triangle(int facet_idx, const Vec3i &neighbors); void split_triangle(int facet_idx, const Vec3i &neighbors);
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. bool remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const; bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const;
int push_triangle(int a, int b, int c, int source_triangle, TriangleStateType state = TriangleStateType::NONE); int push_triangle(int a, int b, int c, int source_triangle, TriangleStateType state = TriangleStateType::NONE);
void perform_split(int facet_idx, const Vec3i &neighbors, TriangleStateType old_state); void perform_split(int facet_idx, const Vec3i &neighbors, TriangleStateType old_state);

View File

@ -645,7 +645,9 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
assert(projected_mouse_position.mesh_idx == mesh_idx); assert(projected_mouse_position.mesh_idx == mesh_idx);
const Vec3f mesh_hit = projected_mouse_position.mesh_hit; const Vec3f mesh_hit = projected_mouse_position.mesh_hit;
const int facet_idx = int(projected_mouse_position.facet_idx); const int facet_idx = int(projected_mouse_position.facet_idx);
m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state); m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state);
if (m_tool_type == ToolType::SMART_FILL) { if (m_tool_type == ToolType::SMART_FILL) {
m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle, m_smart_fill_gap_area, m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle, m_smart_fill_gap_area,
(m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f), TriangleSelector::ForceReselection::YES); (m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f), TriangleSelector::ForceReselection::YES);