Prepare ground for IGL experiments

Test with raycasting/Marching cubes
This commit is contained in:
PavelMikus 2022-05-17 16:35:32 +02:00
parent f5ec76c230
commit ab80ef4dd9
7 changed files with 289 additions and 0 deletions

View File

@ -700,6 +700,16 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu)
return menu_item;
}
wxMenuItem* MenuFactory::append_menu_item_fix_model_mesh(wxMenu* menu)
{
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix model mesh"), "",
[](wxCommandEvent&) { obj_list()->fix_model_mesh(); }, "", menu,
[]() {return plater()->can_fix_model_mesh(); }, m_parent);
return menu_item;
}
wxMenuItem* MenuFactory::append_menu_item_simplify(wxMenu* menu)
{
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Simplify model"), "",
@ -923,6 +933,7 @@ void MenuFactory::create_common_object_menu(wxMenu* menu)
append_menu_item_scale_selection_to_fit_print_volume(menu);
append_menu_item_fix_through_netfabb(menu);
append_menu_item_fix_model_mesh(menu);
append_menu_item_simplify(menu);
append_menu_items_mirror(menu);
}

View File

@ -92,6 +92,7 @@ private:
wxMenuItem* append_menu_item_printable(wxMenu* menu);
void append_menu_items_osx(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
wxMenuItem* append_menu_item_fix_model_mesh(wxMenu* menu);
wxMenuItem* append_menu_item_simplify(wxMenu* menu);
void append_menu_item_export_stl(wxMenu* menu);
void append_menu_item_reload_from_disk(wxMenu* menu);

View File

@ -28,6 +28,7 @@
#include <wx/numformatter.h>
#include "slic3r/Utils/FixModelByWin10.hpp"
#include "slic3r/Utils/FixModelMesh.hpp"
#ifdef __WXMSW__
#include "wx/uiaction.h"
@ -4164,6 +4165,117 @@ void ObjectList::fix_through_netfabb()
plater->get_notification_manager()->push_notification(NotificationType::NetfabbFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, boost::nowide::narrow(msg));
}
void ObjectList::fix_model_mesh()
{
// Do not fix anything when a gizmo is open. There might be issues with updates
// and what is worse, the snapshot time would refer to the internal stack.
if (!wxGetApp().plater()->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined))
return;
// model_name
std::vector<std::string> succes_models;
// model_name failing reason
std::vector<std::pair<std::string, std::string>> failed_models;
std::vector<int> obj_idxs, vol_idxs;
get_selection_indexes(obj_idxs, vol_idxs);
std::vector<std::string> model_names;
// fill names of models to repairing
if (vol_idxs.empty()) {
for (int obj_idx : obj_idxs)
model_names.push_back(object(obj_idx)->name);
}
else {
ModelObject* obj = object(obj_idxs.front());
for (int vol_idx : vol_idxs)
model_names.push_back(obj->volumes[vol_idx]->name);
}
auto plater = wxGetApp().plater();
auto fix_and_update_progress = [this, plater, model_names](const int obj_idx, const int vol_idx,
int model_idx,
wxProgressDialog& progress_dlg,
std::vector<std::string>& succes_models,
std::vector<std::pair<std::string, std::string>>& failed_models)
{
const std::string& model_name = model_names[model_idx];
wxString msg = _L("Repairing model");
if (model_names.size() == 1)
msg += ": " + from_u8(model_name) + "\n";
else {
msg += ":\n";
for (int i = 0; i < int(model_names.size()); ++i)
msg += (i == model_idx ? " > " : " ") + from_u8(model_names[i]) + "\n";
msg += "\n";
}
plater->clear_before_change_mesh(obj_idx);
std::string res;
if (!fix_by_raycasting(*(object(obj_idx)), vol_idx, progress_dlg, msg, res))
return false;
wxGetApp().plater()->changed_mesh(obj_idx);
plater->changed_mesh(obj_idx);
if (res.empty())
succes_models.push_back(model_name);
else
failed_models.push_back({ model_name, res });
update_item_error_icon(obj_idx, vol_idx);
update_info_items(obj_idx);
return true;
};
Plater::TakeSnapshot snapshot(plater, _L("Fix model mesh"));
// Open a progress dialog.
wxProgressDialog progress_dlg(_L("Fixing model mesh"), "", 100, find_toplevel_parent(plater),
wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
int model_idx{ 0 };
if (vol_idxs.empty()) {
int vol_idx{ -1 };
for (int obj_idx : obj_idxs) {
if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
break;
model_idx++;
}
}
else {
int obj_idx{ obj_idxs.front() };
for (int vol_idx : vol_idxs) {
if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
break;
model_idx++;
}
}
// Close the progress dialog
progress_dlg.Update(100, "");
// Show info notification
wxString msg;
wxString bullet_suf = "\n - ";
if (!succes_models.empty()) {
msg = _L_PLURAL("The following model was repaired successfully", "The following models were repaired successfully", succes_models.size()) + ":";
for (auto& model : succes_models)
msg += bullet_suf + from_u8(model);
msg += "\n\n";
}
if (!failed_models.empty()) {
msg += _L_PLURAL("Folowing model repair failed", "Folowing models repair failed", failed_models.size()) + ":\n";
for (auto& model : failed_models)
msg += bullet_suf + from_u8(model.first) + ": " + _(model.second);
}
if (msg.IsEmpty())
msg = _L("Repairing was canceled");
plater->get_notification_manager()->push_notification(NotificationType::NetfabbFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, boost::nowide::narrow(msg));
}
void ObjectList::simplify()
{
auto plater = wxGetApp().plater();

View File

@ -368,6 +368,7 @@ public:
void split_instances();
void rename_item();
void fix_through_netfabb();
void fix_model_mesh();
void simplify();
void update_item_error_icon(const int obj_idx, int vol_idx) const ;

View File

@ -1910,6 +1910,7 @@ struct Plater::priv
bool can_arrange() const;
bool can_layers_editing() const;
bool can_fix_through_netfabb() const;
bool can_fix_model_mesh() const;
bool can_simplify() const;
bool can_set_instance_to_object() const;
bool can_mirror() const;
@ -4857,6 +4858,13 @@ bool Plater::priv::can_fix_through_netfabb() const
#endif // FIX_THROUGH_NETFABB_ALWAYS
}
bool Plater::priv::can_fix_model_mesh() const
{
std::vector<int> obj_idxs, vol_idxs;
sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs);
return ! obj_idxs.empty() || ! vol_idxs.empty();
}
bool Plater::priv::can_simplify() const
{
// is object for simplification selected
@ -7075,6 +7083,7 @@ bool Plater::can_increase_instances() const { return p->can_increase_instances()
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_fix_model_mesh() const { return p->can_fix_model_mesh(); }
bool Plater::can_simplify() const { return p->can_simplify(); }
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(); }

View File

@ -351,6 +351,7 @@ public:
bool can_decrease_instances() const;
bool can_set_instance_to_object() const;
bool can_fix_through_netfabb() const;
bool can_fix_model_mesh() const;
bool can_simplify() const;
bool can_split_to_objects() const;
bool can_split_to_volumes() const;

View File

@ -0,0 +1,154 @@
#ifndef SRC_SLIC3R_UTILS_FIXMODELMESH_HPP_
#define SRC_SLIC3R_UTILS_FIXMODELMESH_HPP_
#include <string>
#include "libslic3r/AABBTreeIndirect.hpp"
#include "tbb/parallel_for.h"
#include "tbb/blocked_range.h"
#include "libigl/igl/copyleft/marching_cubes.h"
#include "libigl/igl/voxel_grid.h"
class wxProgressDialog;
namespace Slic3r {
namespace detail {
Vec3f sample_sphere_uniform(const Vec2f &samples) {
float term1 = 2.0f * float(PI) * samples.x();
float term2 = 2.0f * sqrt(samples.y() - samples.y() * samples.y());
return {cos(term1) * term2, sin(term1) * term2,
1.0f - 2.0f * samples.y()};
}
indexed_triangle_set fix_model_volume_mesh(const TriangleMesh &mesh) {
float thickness = 2.0f;
float resolution = 0.2f;
//prepare uniform samples of a sphere
size_t sqrt_sample_count = 8;
float step_size = 1.0f / sqrt_sample_count;
std::vector<Vec3f> precomputed_sample_directions(
sqrt_sample_count * sqrt_sample_count);
for (size_t x_idx = 0; x_idx < sqrt_sample_count; ++x_idx) {
float sample_x = x_idx * step_size + step_size / 2.0;
for (size_t y_idx = 0; y_idx < sqrt_sample_count; ++y_idx) {
size_t dir_index = x_idx * sqrt_sample_count + y_idx;
float sample_y = y_idx * step_size + step_size / 2.0;
precomputed_sample_directions[dir_index] = sample_sphere_uniform( { sample_x, sample_y });
}
}
indexed_triangle_set its = mesh.its;
float max_size = mesh.bounding_box().size().maxCoeff();
int samples = max_size / resolution;
// create grid
Eigen::MatrixXf grid_points;
Eigen::RowVector3i res;
const BoundingBoxf3 &slicer_bbox = mesh.bounding_box();
Eigen::AlignedBox<float, 3> eigen_box(slicer_bbox.min.cast<float>(), slicer_bbox.max.cast<float>());
std::cout << "building voxel grid " << std::endl;
igl::voxel_grid(eigen_box, samples, 1, grid_points, res);
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(its.vertices, its.indices);
Eigen::VectorXf grid_values;
grid_values.resize(grid_points.size());
std::cout << "g info: " << grid_points.size() << std::endl;
std::cout << "g info: " << grid_points.rows() << std::endl;
std::cout << "g info: " << grid_points.cols() << std::endl;
std::cout << "raycasting " << std::endl;
tbb::parallel_for(
tbb::blocked_range<size_t>(0, grid_points.rows()), [&](tbb::blocked_range<size_t> r) {
for (size_t index = r.begin(); index < r.end(); ++index) {
Vec3f origin = grid_points.row(index);
float &value = grid_values(index);
size_t hit_idx;
Vec3f hit_point;
bool apply_bonus = false;
float distance = sqrt(AABBTreeIndirect::squared_distance_to_indexed_triangle_set(its.vertices, its.indices, tree, origin, hit_idx, hit_point));
Vec3f face_normal = its_face_normal(its, hit_idx);
if ((hit_point - origin).dot(face_normal) > 0 && distance < thickness) {
apply_bonus = true;
}
igl::Hit hit;
size_t inside_hits = 0;
for (const Vec3f &dir : precomputed_sample_directions) {
if (AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices, tree,
Vec3d(origin.cast<double>()),
Vec3d(dir.cast<double>()), hit)) {
Vec3f face_normal = its_face_normal(its, hit.id);
if (dir.dot(face_normal) > 0) {
inside_hits += 1;
}
}
}
if (inside_hits > precomputed_sample_directions.size() / 2) {
value = -distance;
if (apply_bonus) {
value = -2.0f * distance;
}
} else {
value = distance;
}
}
}
);
std::cout << "marching cubes " << std::endl;
Eigen::MatrixXf vertices;
Eigen::MatrixXi faces;
igl::copyleft::marching_cubes(grid_values, grid_points, res.x(), res.y(), res.z(), vertices, faces);
std::cout << "vertices info: " << vertices.size() << std::endl;
std::cout << "vertices info: " << vertices.rows() << std::endl;
std::cout << "vertices info: " << vertices.cols() << std::endl;
indexed_triangle_set fixed_mesh;
fixed_mesh.vertices.resize(vertices.rows());
fixed_mesh.indices.resize(faces.rows());
for (int v = 0; v < vertices.rows(); ++v) {
fixed_mesh.vertices[v] = vertices.row(v);
}
for (int f = 0; f < faces.rows(); ++f) {
fixed_mesh.indices[f] = faces.row(f);
}
return fixed_mesh;
}
}
bool fix_by_raycasting(ModelObject &model_object, int volume_idx, wxProgressDialog &progress_dlg,
const wxString &msg_header, std::string &fix_result) {
std::vector<ModelVolume*> volumes;
if (volume_idx == -1) {
volumes = model_object.volumes;
} else {
volumes.emplace_back(model_object.volumes[volume_idx]);
}
for (ModelVolume *mv : volumes) {
auto mesh = mv->mesh();
mv->set_mesh(detail::fix_model_volume_mesh(mesh));
std::cout << "update mv " << std::endl;
mv->calculate_convex_hull();
mv->set_new_unique_id();
}
model_object.invalidate_bounding_box();
return true;
}
}
#endif /* SRC_SLIC3R_UTILS_FIXMODELMESH_HPP_ */