BBS Port: Mesh Subdivision (#12150)

Ported from BBS
You can now right-click a part and choose Subdivide Part to apply Loop subdivision with multiple iterations. This is useful for models with low original mesh resolution.

> [!NOTE]
> 1. Only meshes without non-manifold edges are supported.
> 2. Color attributes will be lost after subdivision. We recommend subdividing first, then painting and applying colors.

Not perfect and it can break some features but a nice to have and we can improve it.
https://github.com/user-attachments/assets/33f10e49-f6dc-44d3-8c21-9e12e1fe23dc

Best case scenario a sphere:
Each picture is a Mesh subdivision step over the other
<img width="541" height="495" alt="260202_164257_orca-slicer" src="https://github.com/user-attachments/assets/e62b3f4d-ee6b-4451-95a4-40a154d3a405" />
<img width="541" height="495" alt="260202_164302_%pn" src="https://github.com/user-attachments/assets/f7399457-be8d-45e7-b93f-f42064dadd64" />
<img width="541" height="495" alt="260202_164306_%pn" src="https://github.com/user-attachments/assets/55370035-219f-4b7f-94f4-9b31733820d6" />
<img width="541" height="495" alt="260202_164310_%pn" src="https://github.com/user-attachments/assets/3be8c904-cc6f-4efe-b4f8-f390b50d310c" />
This commit is contained in:
Ian Bassi
2026-02-11 21:42:01 -03:00
committed by GitHub
parent aa8b8620da
commit 68fdfcf0eb
11 changed files with 233 additions and 29 deletions

View File

@@ -1349,6 +1349,8 @@ void MenuFactory::create_extra_object_menu()
append_menu_item_fix_through_netfabb(&m_object_menu);
// Object Simplify
append_menu_item_simplify(&m_object_menu);
// Object Mesh Subdivision
append_menu_item_smooth_mesh(&m_object_menu);
// merge to single part
append_menu_item_merge_parts_to_single_part(&m_object_menu);
// Object Center
@@ -1400,6 +1402,8 @@ void MenuFactory::create_bbl_assemble_object_menu()
append_menu_item_fix_through_netfabb(&m_assemble_object_menu);
// Object Simplify
append_menu_item_simplify(&m_assemble_object_menu);
// Object Mesh Subdivision
append_menu_item_smooth_mesh(&m_assemble_object_menu);
m_assemble_object_menu.AppendSeparator();
}
@@ -1483,6 +1487,7 @@ void MenuFactory::create_bbl_part_menu()
append_menu_item_edit_text(menu);
append_menu_item_fix_through_netfabb(menu);
append_menu_item_simplify(menu);
append_menu_item_smooth_mesh(menu);
append_menu_item_center(menu);
append_menu_item_drop(menu);
append_menu_items_mirror(menu);
@@ -1514,6 +1519,7 @@ void MenuFactory::create_bbl_assemble_part_menu()
append_menu_item_delete(menu);
append_menu_item_simplify(menu);
append_menu_item_smooth_mesh(menu);
menu->AppendSeparator();
}
@@ -1949,6 +1955,13 @@ void MenuFactory::append_menu_item_simplify(wxMenu* menu)
[]() {return plater()->can_simplify(); }, m_parent);
}
void MenuFactory::append_menu_item_smooth_mesh(wxMenu *menu)
{
wxMenuItem *menu_item = append_menu_item(
menu, wxID_ANY, _L("Subdivision mesh") + _L("(Lost color)"), "", [](wxCommandEvent &) { obj_list()->smooth_mesh(); }, "", menu, []() { return plater()->can_smooth_mesh(); },
m_parent);
}
void MenuFactory::append_menu_item_center(wxMenu* menu)
{
append_menu_item(menu, wxID_ANY, _L("Center") , "",

View File

@@ -164,6 +164,7 @@ private:
//BBS add bbl menu item
void append_menu_item_clone(wxMenu* menu);
void append_menu_item_simplify(wxMenu* menu);
void append_menu_item_smooth_mesh(wxMenu *menu);
void append_menu_item_center(wxMenu* menu);
void append_menu_item_drop(wxMenu* menu);
void append_menu_item_per_object_process(wxMenu* menu);

View File

@@ -44,6 +44,7 @@
#endif /* __WXMSW__ */
#include "Gizmos/GLGizmoScale.hpp"
#include "libslic3r/TriangleMeshDeal.hpp"
namespace Slic3r
{
namespace GUI
@@ -5931,6 +5932,92 @@ void ObjectList::simplify()
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
}
void GUI::ObjectList::smooth_mesh()
{
wxBusyCursor cursor;
auto plater = wxGetApp().plater();
if (!plater) { return; }
plater->take_snapshot("smooth_mesh");
std::vector<int> obj_idxs, vol_idxs;
get_selection_indexes(obj_idxs, vol_idxs);
auto object_idx = obj_idxs.front();
ModelObject *obj{nullptr};
auto show_warning_dlg = [this](int cur_face_count,std::string name,bool is_part) {
int limit_face_count = 1000000;
if (cur_face_count > limit_face_count) {
auto name_str = wxString::FromUTF8(name);
auto content = wxString::Format(_L("\"%s\" will exceed 1 million faces after this subdivision, which may increase slicing time. Do you want to continue?"), name_str);
WarningDialog dlg(static_cast<wxWindow *>(wxGetApp().mainframe), (is_part ? _L("Part") : _L("Object")) + " " + content, _L("BambuStudio warning"), wxYES_NO);
if (dlg.ShowModal() == wxID_NO) {
return true;
}
return false;
}
return false;
};
auto show_smooth_mesh_error_dlg = [this](std::string name) {
auto name_str = wxString::FromUTF8(name);
auto content = wxString::Format(_L("\"%s\" part's mesh contains errors. Please repair it first."), name_str);
WarningDialog dlg(static_cast<wxWindow *>(wxGetApp().mainframe), content, _L("BambuStudio warning"), wxOK);
dlg.ShowModal();
};
bool has_show_smooth_mesh_error_dlg = false;
if (vol_idxs.empty()) {
obj = object(object_idx);
auto future_face_count = static_cast<int>(obj->facets_count()) * 4;
if (show_warning_dlg(future_face_count, obj->name,false)) {
return;
}
for (auto mv : obj->volumes) {
bool ok;
auto result_mesh = TriangleMeshDeal::smooth_triangle_mesh(mv->mesh(), ok);
if (ok) {
mv->set_mesh(result_mesh);
mv->reset_extra_facets(); // reset paint color
mv->calculate_convex_hull();
mv->invalidate_convex_hull_2d();
mv->set_new_unique_id();
} else {
if (!has_show_smooth_mesh_error_dlg) {
show_smooth_mesh_error_dlg(mv->name);
has_show_smooth_mesh_error_dlg = true;
}
}
}
obj->invalidate_bounding_box();
obj->ensure_on_bed();
plater->changed_mesh(object_idx);
} else {
obj = object(obj_idxs.front());
for (int vol_idx : vol_idxs) {
auto mv = obj->volumes[vol_idx];
auto future_face_count = static_cast<int>(mv->mesh().facets_count()) * 4;
if (show_warning_dlg(future_face_count, mv->name,true)) {
return;
}
bool ok;
auto result_mesh = TriangleMeshDeal::smooth_triangle_mesh(mv->mesh(),ok);
if (ok) {
mv->set_mesh(result_mesh);
mv->reset_extra_facets(); // reset paint color
mv->calculate_convex_hull();
mv->invalidate_convex_hull_2d();
mv->set_new_unique_id();
} else {
if (!has_show_smooth_mesh_error_dlg) {
show_smooth_mesh_error_dlg(mv->name);
has_show_smooth_mesh_error_dlg = true;
}
}
}
}
if (obj) {
obj->invalidate_bounding_box();
obj->ensure_on_bed();
plater->changed_mesh(object_idx);
}
}
void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const
{
auto obj = object(obj_idx);

View File

@@ -428,6 +428,7 @@ public:
void rename_item();
void fix_through_netfabb();
void simplify();
void smooth_mesh();
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
void copy_layers_to_clipboard();

View File

@@ -4640,6 +4640,7 @@ struct Plater::priv
bool can_layers_editing() const;
bool can_fix_through_netfabb() const;
bool can_simplify() const;
bool can_smooth_mesh() const;
bool can_set_instance_to_object() const;
bool can_mirror() const;
bool can_reload_from_disk() const;
@@ -11035,6 +11036,24 @@ bool Plater::priv::can_simplify() const
return true;
}
bool Plater::priv::can_smooth_mesh() const
{
std::vector<int> obj_idxs, vol_idxs;
sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs);
if (vol_idxs.empty()) {
for (auto obj_idx : obj_idxs)
if (model.objects[obj_idx]->get_object_stl_stats().open_edges > 0)
return false;
return true;
}
int obj_idx = obj_idxs.front();
for (auto vol_idx : vol_idxs)
if (model.objects[obj_idx]->get_object_stl_stats().open_edges > 0)
return false;
return true;
}
bool Plater::priv::can_increase_instances() const
{
if (!m_worker.is_idle()
@@ -17927,6 +17946,7 @@ bool Plater::can_decrease_instances() const { return p->can_decrease_instances()
bool Plater::can_set_instance_to_object() const { return p->can_set_instance_to_object(); }
bool Plater::can_fix_through_netfabb() const { return p->can_fix_through_netfabb(); }
bool Plater::can_simplify() const { return p->can_simplify(); }
bool Plater::can_smooth_mesh() const { return p->can_smooth_mesh(); }
bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); }
bool Plater::can_arrange() const { return p->can_arrange(); }

View File

@@ -662,6 +662,7 @@ public:
bool can_set_instance_to_object() const;
bool can_fix_through_netfabb() const;
bool can_simplify() const;
bool can_smooth_mesh() const;
bool can_split_to_objects() const;
bool can_split_to_volumes() const;
bool can_arrange() const;