This commit is contained in:
enricoturri1966 2022-01-19 14:23:13 +01:00
commit 2dc63070b6
4 changed files with 309 additions and 155 deletions

View file

@ -173,6 +173,12 @@ void GLModel::set_color(int entity_id, const ColorRGBA& color)
} }
} }
ColorRGBA GLModel::get_color(size_t entity_id) const
{
if (entity_id < 0 || entity_id >= m_render_data.size()) return ColorRGBA{};
return m_render_data[entity_id].color;
}
void GLModel::reset() void GLModel::reset()
{ {
for (RenderData& data : m_render_data) { for (RenderData& data : m_render_data) {

View file

@ -76,6 +76,7 @@ namespace GUI {
// if entity_id == -1 set the color of all entities // if entity_id == -1 set the color of all entities
void set_color(int entity_id, const ColorRGBA& color); void set_color(int entity_id, const ColorRGBA& color);
ColorRGBA get_color(size_t entity_id = 0U) const;
void reset(); void reset();
void render() const; void render() const;

View file

@ -36,33 +36,72 @@ static void call_after_if_active(std::function<void()> fn, GUI_App* app = &wxGet
}); });
} }
static ModelVolume* get_model_volume(const Selection& selection, Model& model) static std::set<ObjectID> get_volume_ids(const Selection &selection)
{ {
const Selection::IndicesList& idxs = selection.get_volume_idxs(); const Selection::IndicesList &volume_ids = selection.get_volume_idxs();
// only one selected volume const ModelObjectPtrs &model_objects = selection.get_model()->objects;
if (idxs.size() != 1) std::set<ObjectID> result;
return nullptr; for (auto volume_id : volume_ids) {
const GLVolume* selected_volume = selection.get_volume(*idxs.begin()); const GLVolume *selected_volume = selection.get_volume(volume_id);
if (selected_volume == nullptr) assert(selected_volume != nullptr);
return nullptr;
const GLVolume::CompositeID& cid = selected_volume->composite_id; const GLVolume::CompositeID &cid = selected_volume->composite_id;
const ModelObjectPtrs& objs = model.objects;
if (cid.object_id < 0 || objs.size() <= static_cast<size_t>(cid.object_id)) assert(cid.object_id >= 0);
return nullptr; assert(model_objects.size() > static_cast<size_t>(cid.object_id));
const ModelObject* obj = objs[cid.object_id];
if (cid.volume_id < 0 || obj->volumes.size() <= static_cast<size_t>(cid.volume_id)) const ModelObject *obj = model_objects[cid.object_id];
return nullptr; const ModelVolume *volume = obj->volumes[cid.volume_id];
return obj->volumes[cid.volume_id]; const ObjectID & id = volume->id();
// prevent selection of volume without inidces
if (volume->mesh().its.indices.empty()) continue;
assert(result.find(id) == result.end());
result.insert(id);
}
return result;
}
// return ModelVolume from selection by object id
static ModelVolume *get_volume(const ObjectID &id, const Selection &selection) {
const Selection::IndicesList &volume_ids = selection.get_volume_idxs();
const ModelObjectPtrs &model_objects = selection.get_model()->objects;
for (auto volume_id : volume_ids) {
const GLVolume *selected_volume = selection.get_volume(volume_id);
const GLVolume::CompositeID &cid = selected_volume->composite_id;
ModelObject *obj = model_objects[cid.object_id];
ModelVolume *volume = obj->volumes[cid.volume_id];
if (id == volume->id()) return volume;
}
return nullptr;
}
static std::string create_volumes_name(const std::set<ObjectID>& ids, const Selection &selection){
assert(!ids.empty());
std::string name;
bool is_first = true;
for (const ObjectID &id : ids) {
if (is_first)
is_first = false;
else
name += " + ";
const ModelVolume *volume = get_volume(id, selection);
assert(volume != nullptr);
name += volume->name;
}
return name;
} }
GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent,
const std::string &icon_filename, const std::string &icon_filename,
unsigned int sprite_id) unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, -1) : GLGizmoBase(parent, icon_filename, -1)
, m_volume(nullptr)
, m_show_wireframe(false) , m_show_wireframe(false)
, m_move_to_center(false) , m_move_to_center(false)
, m_original_triangle_count(0)
, m_triangle_count(0)
// translation for GUI size // translation for GUI size
, tr_mesh_name(_u8L("Mesh name")) , tr_mesh_name(_u8L("Mesh name"))
, tr_triangles(_u8L("Triangles")) , tr_triangles(_u8L("Triangles"))
@ -75,15 +114,11 @@ GLGizmoSimplify::~GLGizmoSimplify()
stop_worker_thread_request(); stop_worker_thread_request();
if (m_worker.joinable()) if (m_worker.joinable())
m_worker.join(); m_worker.join();
m_glmodel.reset();
} }
bool GLGizmoSimplify::on_esc_key_down() { bool GLGizmoSimplify::on_esc_key_down() {
return false; //close();
/*if (!m_is_worker_running) return stop_worker_thread_request();
return false;
stop_worker_thread_request();
return true;*/
} }
// while opening needs GLGizmoSimplify to set window position // while opening needs GLGizmoSimplify to set window position
@ -147,8 +182,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
{ {
create_gui_cfg(); create_gui_cfg();
const Selection &selection = m_parent.get_selection(); const Selection &selection = m_parent.get_selection();
const ModelVolume *act_volume = get_model_volume(selection, wxGetApp().plater()->model()); auto act_volume_ids = get_volume_ids(selection);
if (act_volume == nullptr) { if (act_volume_ids.empty()) {
stop_worker_thread_request(); stop_worker_thread_request();
close(); close();
if (! m_parent.get_selection().is_single_volume()) { if (! m_parent.get_selection().is_single_volume()) {
@ -163,83 +198,93 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
bool is_cancelling = false; bool is_cancelling = false;
bool is_worker_running = false; bool is_worker_running = false;
bool is_result_ready = false; bool is_result_ready = false;
int progress = 0; int progress = 0;
{ {
std::lock_guard lk(m_state_mutex); std::lock_guard lk(m_state_mutex);
is_cancelling = m_state.status == State::cancelling; is_cancelling = m_state.status == State::cancelling;
is_worker_running = m_state.status == State::running; is_worker_running = m_state.status == State::running;
is_result_ready = bool(m_state.result); is_result_ready = !m_state.result.empty();
progress = m_state.progress; progress = m_state.progress;
} }
// Whether to trigger calculation after rendering is done. // Whether to trigger calculation after rendering is done.
bool start_process = false; bool start_process = false;
// Check selection of new volume (or change)
// Check selection of new volume
// Do not reselect object when processing // Do not reselect object when processing
if (act_volume != m_volume) { if (m_volume_ids != act_volume_ids) {
bool change_window_position = (m_volume == nullptr); bool change_window_position = m_volume_ids.empty();
// select different model // select different model
// close suggestion notification // close suggestion notification
auto notification_manager = wxGetApp().plater()->get_notification_manager(); auto notification_manager = wxGetApp().plater()->get_notification_manager();
notification_manager->remove_simplify_suggestion_with_id(act_volume->get_object()->id()); for (const auto &id : act_volume_ids)
notification_manager->remove_simplify_suggestion_with_id(id);
m_volume = act_volume; m_volume_ids = std::move(act_volume_ids);
init_model();
// triangle count is calculated in init model
m_original_triangle_count = m_triangle_count;
// Default value of configuration
m_configuration.decimate_ratio = 50.; // default value m_configuration.decimate_ratio = 50.; // default value
m_configuration.fix_count_by_ratio(m_volume->mesh().its.indices.size()); m_configuration.fix_count_by_ratio(m_original_triangle_count);
init_model(m_volume->mesh().its); m_configuration.use_count = false;
// Create volumes name to describe what will be simplified
std::string name = create_volumes_name(m_volume_ids, selection);
if (name.length() > m_gui_cfg->max_char_in_name)
name = name.substr(0, m_gui_cfg->max_char_in_name - 3) + "...";
m_volumes_name = name;
// Start processing. If we switched from another object, process will // Start processing. If we switched from another object, process will
// stop the background thread and it will restart itself later. // stop the background thread and it will restart itself later.
start_process = true; start_process = true;
// set window position // set window position
if (m_move_to_center && change_window_position) { if (change_window_position) {
m_move_to_center = false; ImVec2 pos;
auto parent_size = m_parent.get_canvas_size(); Size parent_size = m_parent.get_canvas_size();
ImVec2 pos(parent_size.get_width() / 2 - m_gui_cfg->window_offset_x, if (m_move_to_center) {
parent_size.get_height() / 2 - m_gui_cfg->window_offset_y); m_move_to_center = false;
ImGui::SetNextWindowPos(pos, ImGuiCond_Always); pos = ImVec2(parent_size.get_width() / 2 - m_gui_cfg->window_offset_x,
}else if (change_window_position) { parent_size.get_height() / 2 - m_gui_cfg->window_offset_y);
ImVec2 pos = ImGui::GetMousePos(); } else {
pos.x -= m_gui_cfg->window_offset_x; // keep window wisible on canvas and close to mouse click
pos.y -= m_gui_cfg->window_offset_y; pos = ImGui::GetMousePos();
// minimal top left value pos.x -= m_gui_cfg->window_offset_x;
ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding + m_parent.get_main_toolbar_height()); pos.y -= m_gui_cfg->window_offset_y;
if (pos.x < tl.x) pos.x = tl.x; // minimal top left value
if (pos.y < tl.y) pos.y = tl.y; ImVec2 tl(m_gui_cfg->window_padding,
// maximal bottom right value m_gui_cfg->window_padding + m_parent.get_main_toolbar_height());
auto parent_size = m_parent.get_canvas_size(); if (pos.x < tl.x) pos.x = tl.x;
ImVec2 br( if (pos.y < tl.y) pos.y = tl.y;
parent_size.get_width() - (2 * m_gui_cfg->window_offset_x + m_gui_cfg->window_padding), // maximal bottom right value
parent_size.get_height() - (2 * m_gui_cfg->window_offset_y + m_gui_cfg->window_padding)); ImVec2 br(parent_size.get_width() - (2 * m_gui_cfg->window_offset_x + m_gui_cfg->window_padding),
if (pos.x > br.x) pos.x = br.x; parent_size.get_height() -(2 * m_gui_cfg->window_offset_y + m_gui_cfg->window_padding));
if (pos.y > br.y) pos.y = br.y; if (pos.x > br.x) pos.x = br.x;
if (pos.y > br.y) pos.y = br.y;
}
ImGui::SetNextWindowPos(pos, ImGuiCond_Always); ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
} }
} }
bool is_multipart = (m_volume_ids.size() > 1);
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse; ImGuiWindowFlags_NoCollapse;
m_imgui->begin(on_get_name(), flag); m_imgui->begin(on_get_name(), flag);
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_mesh_name + ":"); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_mesh_name + ":");
ImGui::SameLine(m_gui_cfg->top_left_width); ImGui::SameLine(m_gui_cfg->top_left_width);
std::string name = m_volume->name; m_imgui->text(m_volumes_name);
if (name.length() > m_gui_cfg->max_char_in_name)
name = name.substr(0, m_gui_cfg->max_char_in_name - 3) + "...";
m_imgui->text(name);
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_triangles + ":"); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_triangles + ":");
ImGui::SameLine(m_gui_cfg->top_left_width); ImGui::SameLine(m_gui_cfg->top_left_width);
size_t orig_triangle_count = m_volume->mesh().its.indices.size(); m_imgui->text(std::to_string(m_original_triangle_count));
m_imgui->text(std::to_string(orig_triangle_count));
ImGui::Separator(); ImGui::Separator();
if(ImGui::RadioButton("##use_error", !m_configuration.use_count)) { if(ImGui::RadioButton("##use_error", !m_configuration.use_count) && !is_multipart) {
m_configuration.use_count = !m_configuration.use_count; m_configuration.use_count = !m_configuration.use_count;
start_process = true; start_process = true;
} }
@ -270,17 +315,21 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
} }
m_imgui->disabled_end(); // !use_count m_imgui->disabled_end(); // !use_count
if (ImGui::RadioButton("##use_count", m_configuration.use_count)) { if (ImGui::RadioButton("##use_count", m_configuration.use_count) && !is_multipart) {
m_configuration.use_count = !m_configuration.use_count; m_configuration.use_count = !m_configuration.use_count;
start_process = true; start_process = true;
} } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && is_multipart)
ImGui::SetTooltip("%s", GUI::format(_L(
"Multipart object can be simplified only by %1%. "
"If you want specify %2% process it separately."),
tr_detail_level, tr_decimate_ratio).c_str());
ImGui::SameLine(); ImGui::SameLine();
// show preview result triangle count (percent) // show preview result triangle count (percent)
if (!m_configuration.use_count) { if (!m_configuration.use_count) {
m_configuration.wanted_count = static_cast<uint32_t>(m_triangle_count); m_configuration.wanted_count = static_cast<uint32_t>(m_triangle_count);
m_configuration.decimate_ratio = m_configuration.decimate_ratio =
(1.0f - (m_configuration.wanted_count / (float) orig_triangle_count)) * 100.f; (1.0f - (m_configuration.wanted_count / (float) m_original_triangle_count)) * 100.f;
} }
m_imgui->disabled_begin(!m_configuration.use_count); m_imgui->disabled_begin(!m_configuration.use_count);
@ -295,7 +344,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
m_configuration.decimate_ratio = 0.01f; m_configuration.decimate_ratio = 0.01f;
if (m_configuration.decimate_ratio > 100.f) if (m_configuration.decimate_ratio > 100.f)
m_configuration.decimate_ratio = 100.f; m_configuration.decimate_ratio = 100.f;
m_configuration.fix_count_by_ratio(orig_triangle_count); m_configuration.fix_count_by_ratio(m_original_triangle_count);
start_process = true; start_process = true;
} }
@ -342,11 +391,13 @@ void GLGizmoSimplify::close() {
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
} }
void GLGizmoSimplify::stop_worker_thread_request() bool GLGizmoSimplify::stop_worker_thread_request()
{ {
std::lock_guard lk(m_state_mutex); std::lock_guard lk(m_state_mutex);
if (m_state.status == State::running) if (m_state.status != State::running) return false;
m_state.status = State::Status::cancelling;
m_state.status = State::Status::cancelling;
return true;
} }
@ -366,9 +417,11 @@ void GLGizmoSimplify::worker_finished()
m_worker.join(); m_worker.join();
if (GLGizmoBase::m_state == Off) if (GLGizmoBase::m_state == Off)
return; return;
if (m_state.result) const auto &result = m_state.result;
init_model(*m_state.result); if (!result.empty())
if (m_state.config != m_configuration || m_state.mv != m_volume) { update_model(result);
if (m_state.config != m_configuration || m_state.volume_ids != m_volume_ids) {
// Settings were changed, restart the worker immediately. // Settings were changed, restart the worker immediately.
process(); process();
} }
@ -377,16 +430,16 @@ void GLGizmoSimplify::worker_finished()
void GLGizmoSimplify::process() void GLGizmoSimplify::process()
{ {
if (m_volume == nullptr || m_volume->mesh().its.indices.empty()) if (m_volume_ids.empty()) return;
return;
// m_volume->mesh().its.indices.empty()
bool configs_match = false; bool configs_match = false;
bool result_valid = false; bool result_valid = false;
bool is_worker_running = false; bool is_worker_running = false;
{ {
std::lock_guard lk(m_state_mutex); std::lock_guard lk(m_state_mutex);
configs_match = (m_volume == m_state.mv && m_state.config == m_configuration); configs_match = (m_volume_ids == m_state.volume_ids && m_state.config == m_configuration);
result_valid = bool(m_state.result); result_valid = !m_state.result.empty();
is_worker_running = m_state.status == State::running; is_worker_running = m_state.status == State::running;
} }
@ -411,15 +464,20 @@ void GLGizmoSimplify::process()
// Copy configuration that will be used. // Copy configuration that will be used.
m_state.config = m_configuration; m_state.config = m_configuration;
m_state.mv = m_volume; m_state.volume_ids = m_volume_ids;
m_state.status = State::running; m_state.status = State::running;
// Create a copy of current mesh to pass to the worker thread. // Create a copy of current meshes to pass to the worker thread.
// Using unique_ptr instead of pass-by-value to avoid an extra // Using unique_ptr instead of pass-by-value to avoid an extra
// copy (which would happen when passing to std::thread). // copy (which would happen when passing to std::thread).
auto its = std::make_unique<indexed_triangle_set>(m_volume->mesh().its); const Selection& selection = m_parent.get_selection();
State::Data its;
m_worker = std::thread([this](std::unique_ptr<indexed_triangle_set> its) { for (const auto &id : m_volume_ids) {
const ModelVolume *volume = get_volume(id, selection);
its[id] = std::make_unique<indexed_triangle_set>(volume->mesh().its); // copy
}
m_worker = std::thread([this](State::Data its) {
// Checks that the UI thread did not request cancellation, throws if so. // Checks that the UI thread did not request cancellation, throws if so.
std::function<void(void)> throw_on_cancel = [this]() { std::function<void(void)> throw_on_cancel = [this]() {
@ -446,13 +504,16 @@ void GLGizmoSimplify::process()
if (! m_state.config.use_count) if (! m_state.config.use_count)
max_error = m_state.config.max_error; max_error = m_state.config.max_error;
m_state.progress = 0; m_state.progress = 0;
m_state.result.reset(); m_state.result.clear();
m_state.status = State::Status::running; m_state.status = State::Status::running;
} }
// Start the actual calculation. // Start the actual calculation.
try { try {
its_quadric_edge_collapse(*its, triangle_count, &max_error, throw_on_cancel, statusfn); for (const auto& it : its) {
float me = max_error;
its_quadric_edge_collapse(*it.second, triangle_count, &me, throw_on_cancel, statusfn);
}
} catch (SimplifyCanceledException &) { } catch (SimplifyCanceledException &) {
std::lock_guard lk(m_state_mutex); std::lock_guard lk(m_state_mutex);
m_state.status = State::idle; m_state.status = State::idle;
@ -471,25 +532,33 @@ void GLGizmoSimplify::process()
} }
void GLGizmoSimplify::apply_simplify() { void GLGizmoSimplify::apply_simplify() {
// worker must be stopped
assert(m_state.status == State::Status::idle);
// check that there is NO change of volume
assert(m_state.volume_ids == m_volume_ids);
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
int object_idx = selection.get_object_idx();
auto plater = wxGetApp().plater(); auto plater = wxGetApp().plater();
plater->take_snapshot(GUI::format(_u8L("Simplify %1%"), m_volume->name)); plater->take_snapshot(_u8L("Simplify ") + create_volumes_name(m_volume_ids, selection));
plater->clear_before_change_mesh(object_idx); plater->clear_before_change_mesh(selection.get_object_idx());
ModelVolume* mv = get_model_volume(selection, wxGetApp().model()); for (const auto &item: m_state.result) {
assert(mv == m_volume); const ObjectID &id = item.first;
const indexed_triangle_set &its = *item.second;
ModelVolume *volume = get_volume(id, selection);
assert(volume != nullptr);
ModelObject *obj = volume->get_object();
mv->set_mesh(std::move(*m_state.result)); volume->set_mesh(std::move(its));
m_state.result.reset(); volume->calculate_convex_hull();
mv->calculate_convex_hull(); volume->set_new_unique_id();
mv->set_new_unique_id(); obj->invalidate_bounding_box();
mv->get_object()->invalidate_bounding_box(); obj->ensure_on_bed(true); // allow negative z
mv->get_object()->ensure_on_bed(true); // allow negative z }
m_state.result.clear();
// fix hollowing, sla support points, modifiers, ... // fix hollowing, sla support points, modifiers, ...
int object_idx = selection.get_object_idx();
plater->changed_mesh(object_idx); plater->changed_mesh(object_idx);
// Fix warning icon in object list // Fix warning icon in object list
wxGetApp().obj_list()->update_item_error_icon(object_idx, -1); wxGetApp().obj_list()->update_item_error_icon(object_idx, -1);
@ -508,8 +577,8 @@ void GLGizmoSimplify::on_set_state()
m_parent.toggle_model_objects_visibility(true); m_parent.toggle_model_objects_visibility(true);
stop_worker_thread_request(); stop_worker_thread_request();
m_volume = nullptr; // invalidate selected model m_volume_ids.clear(); // invalidate selected model
m_glmodel.reset(); m_glmodels.clear(); // free gpu memory
} else if (GLGizmoBase::m_state == GLGizmoBase::On) { } else if (GLGizmoBase::m_state == GLGizmoBase::On) {
// when open by hyperlink it needs to show up // when open by hyperlink it needs to show up
request_rerender(); request_rerender();
@ -550,73 +619,140 @@ void GLGizmoSimplify::set_center_position() {
m_move_to_center = true; m_move_to_center = true;
} }
void GLGizmoSimplify::init_model()
void GLGizmoSimplify::init_model(const indexed_triangle_set& its)
{ {
if (its.indices.empty()) // volume ids must be set before init model
return; assert(!m_volume_ids.empty());
m_glmodel.reset();
m_glmodel.init_from(its);
m_parent.toggle_model_objects_visibility(true); // selected volume may have changed m_parent.toggle_model_objects_visibility(true); // selected volume may have changed
m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), const auto info = m_c->selection_info();
m_c->selection_info()->get_active_instance(), m_volume);
const Selection &selection = m_parent.get_selection();
if (const Selection&sel = m_parent.get_selection(); sel.get_volume_idxs().size() == 1) Model & model = *selection.get_model();
m_glmodel.set_color(-1, sel.get_volume(*sel.get_volume_idxs().begin())->color); const Selection::IndicesList &volume_ids = selection.get_volume_idxs();
m_triangle_count = its.indices.size(); const ModelObjectPtrs &model_objects = model.objects;
m_glmodels.clear();
//m_glmodels.reserve(volume_ids.size());
m_triangle_count = 0;
for (const ObjectID& id: m_volume_ids) {
const GLVolume *selected_volume;
const ModelVolume *volume = nullptr;
for (auto volume_id : volume_ids) {
selected_volume = selection.get_volume(volume_id);
const GLVolume::CompositeID &cid = selected_volume->composite_id;
ModelObject * obj = model_objects[cid.object_id];
ModelVolume * act_volume = obj->volumes[cid.volume_id];
if (id == act_volume->id()) {
volume = act_volume;
break;
}
}
assert(volume != nullptr);
const indexed_triangle_set &its = volume->mesh().its;
// set actual triangle count
m_triangle_count += its.indices.size();
assert(m_glmodels.find(id) == m_glmodels.end());
GLModel &glmodel = m_glmodels[id]; // create new glmodel
glmodel.init_from(its);
glmodel.set_color(-1,selected_volume->color);
m_parent.toggle_model_objects_visibility(false, info->model_object(),
info->get_active_instance(),
volume);
}
}
void GLGizmoSimplify::update_model(const State::Data &data)
{
// check that model exist
if (m_glmodels.empty()) return;
// check that result is for actual gl models
size_t model_count = m_glmodels.size();
if (data.size() != model_count) return;
m_triangle_count = 0;
for (const auto &item : data) {
const indexed_triangle_set &its = *item.second;
auto it = m_glmodels.find(item.first);
assert(it != m_glmodels.end());
GLModel &glmodel = it->second;
auto color = glmodel.get_color();
// when not reset it keeps old shape
glmodel.reset();
glmodel.init_from(its);
glmodel.set_color(-1, color);
m_triangle_count += its.indices.size();
}
} }
void GLGizmoSimplify::on_render() void GLGizmoSimplify::on_render()
{ {
if (! m_glmodel.is_initialized()) if (m_glmodels.empty()) return;
return;
const Selection & selection = m_parent.get_selection();
const auto& selection = m_parent.get_selection();
const auto& volume_idxs = selection.get_volume_idxs();
if (volume_idxs.empty() || volume_idxs.size() != 1) return;
const GLVolume *selected_volume = selection.get_volume(*volume_idxs.begin());
// Check that the GLVolume still belongs to the ModelObject we work on. // Check that the GLVolume still belongs to the ModelObject we work on.
if (m_volume != get_model_volume(selection, wxGetApp().model())) if (m_volume_ids != get_volume_ids(selection)) return;
return;
const Transform3d trafo_matrix = selected_volume->world_matrix(); const ModelObjectPtrs &model_objects = selection.get_model()->objects;
glsafe(::glPushMatrix()); const Selection::IndicesList &volume_idxs = selection.get_volume_idxs();
glsafe(::glMultMatrixd(trafo_matrix.data()));
auto *gouraud_shader = wxGetApp().get_shader("gouraud_light"); // no need to render nothing
glsafe(::glPushAttrib(GL_DEPTH_TEST)); if (volume_idxs.empty()) return;
glsafe(::glEnable(GL_DEPTH_TEST));
gouraud_shader->start_using();
m_glmodel.render();
gouraud_shader->stop_using();
if (m_show_wireframe) { // Iteration over selection because of world transformation matrix of object
auto* contour_shader = wxGetApp().get_shader("mm_contour"); for (auto volume_id : volume_idxs) {
contour_shader->start_using(); const GLVolume *selected_volume = selection.get_volume(volume_id);
glsafe(::glLineWidth(1.0f)); const GLVolume::CompositeID &cid = selected_volume->composite_id;
glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_LINE));
//ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); }); ModelObject *obj = model_objects[cid.object_id];
//glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); ModelVolume *volume = obj->volumes[cid.volume_id];
//glsafe(::glPolygonOffset(5.0, 5.0));
m_glmodel.render(); auto it = m_glmodels.find(volume->id());
glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)); assert(it != m_glmodels.end());
contour_shader->stop_using();
GLModel &glmodel = it->second;
const Transform3d trafo_matrix = selected_volume->world_matrix();
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(trafo_matrix.data()));
auto *gouraud_shader = wxGetApp().get_shader("gouraud_light");
glsafe(::glPushAttrib(GL_DEPTH_TEST));
glsafe(::glEnable(GL_DEPTH_TEST));
gouraud_shader->start_using();
glmodel.render();
gouraud_shader->stop_using();
if (m_show_wireframe) {
auto *contour_shader = wxGetApp().get_shader("mm_contour");
contour_shader->start_using();
glsafe(::glLineWidth(1.0f));
glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_LINE));
glmodel.render();
glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL));
contour_shader->stop_using();
}
glsafe(::glPopAttrib());
glsafe(::glPopMatrix());
} }
glsafe(::glPopAttrib());
glsafe(::glPopMatrix());
} }
CommonGizmosDataID GLGizmoSimplify::on_get_requirements() const CommonGizmosDataID GLGizmoSimplify::on_get_requirements() const
{ {
return CommonGizmosDataID( return CommonGizmosDataID(
int(CommonGizmosDataID::SelectionInfo)); int(CommonGizmosDataID::SelectionInfo));
} }
void GLGizmoSimplify::Configuration::fix_count_by_ratio(size_t triangle_count) void GLGizmoSimplify::Configuration::fix_count_by_ratio(size_t triangle_count)
{ {
if (decimate_ratio <= 0.f) if (decimate_ratio <= 0.f)

View file

@ -10,8 +10,9 @@
#include <thread> #include <thread>
namespace Slic3r { namespace Slic3r {
class ModelVolume; class ModelObject;
class Model; class Model;
class ObjectID;
namespace GUI { namespace GUI {
class NotificationManager; // for simplify suggestion class NotificationManager; // for simplify suggestion
@ -46,12 +47,11 @@ private:
void close(); void close();
void process(); void process();
void stop_worker_thread_request(); bool stop_worker_thread_request();
void worker_finished(); void worker_finished();
void create_gui_cfg(); void create_gui_cfg();
void request_rerender(bool force = false); void request_rerender(bool force = false);
void init_model(const indexed_triangle_set& its);
void set_center_position(); void set_center_position();
@ -76,10 +76,14 @@ private:
bool m_move_to_center; // opening gizmo bool m_move_to_center; // opening gizmo
const ModelVolume *m_volume; // keep pointer to actual working volume std::set<ObjectID> m_volume_ids; // keep pointers to actual working volumes
std::string m_volumes_name;
size_t m_original_triangle_count;
bool m_show_wireframe; bool m_show_wireframe;
GLModel m_glmodel; std::map<ObjectID, GLModel> m_glmodels;
size_t m_triangle_count; // triangle count of the model currently shown size_t m_triangle_count; // triangle count of the model currently shown
// Timestamp of the last rerender request. Only accessed from UI thread. // Timestamp of the last rerender request. Only accessed from UI thread.
@ -88,6 +92,8 @@ private:
// Following struct is accessed by both UI and worker thread. // Following struct is accessed by both UI and worker thread.
// Accesses protected by a mutex. // Accesses protected by a mutex.
struct State { struct State {
//using Data = std::vector<std::unique_ptr<indexed_triangle_set> >;
using Data = std::map<ObjectID, std::unique_ptr<indexed_triangle_set> >;
enum Status { enum Status {
idle, idle,
running, running,
@ -97,10 +103,15 @@ private:
Status status = idle; Status status = idle;
int progress = 0; // percent of done work int progress = 0; // percent of done work
Configuration config; // Configuration we started with. Configuration config; // Configuration we started with.
const ModelVolume* mv = nullptr; const ModelObject* mo = nullptr;
std::unique_ptr<indexed_triangle_set> result;
Data result;
std::set<ObjectID> volume_ids; // is same as result keys - store separate for faster check
}; };
void init_model(); // initialize glModels from selection
void update_model(const State::Data &data);
std::thread m_worker; std::thread m_worker;
std::mutex m_state_mutex; // guards m_state std::mutex m_state_mutex; // guards m_state
State m_state; // accessed by both threads State m_state; // accessed by both threads