mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 00:36:12 +08:00
Prepare ground for IGL experiments
Test with raycasting/Marching cubes
This commit is contained in:
parent
f5ec76c230
commit
ab80ef4dd9
@ -700,6 +700,16 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu)
|
|||||||
return menu_item;
|
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* MenuFactory::append_menu_item_simplify(wxMenu* menu)
|
||||||
{
|
{
|
||||||
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Simplify model"), "",
|
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_scale_selection_to_fit_print_volume(menu);
|
||||||
|
|
||||||
append_menu_item_fix_through_netfabb(menu);
|
append_menu_item_fix_through_netfabb(menu);
|
||||||
|
append_menu_item_fix_model_mesh(menu);
|
||||||
append_menu_item_simplify(menu);
|
append_menu_item_simplify(menu);
|
||||||
append_menu_items_mirror(menu);
|
append_menu_items_mirror(menu);
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ private:
|
|||||||
wxMenuItem* append_menu_item_printable(wxMenu* menu);
|
wxMenuItem* append_menu_item_printable(wxMenu* menu);
|
||||||
void append_menu_items_osx(wxMenu* menu);
|
void append_menu_items_osx(wxMenu* menu);
|
||||||
wxMenuItem* append_menu_item_fix_through_netfabb(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);
|
wxMenuItem* append_menu_item_simplify(wxMenu* menu);
|
||||||
void append_menu_item_export_stl(wxMenu* menu);
|
void append_menu_item_export_stl(wxMenu* menu);
|
||||||
void append_menu_item_reload_from_disk(wxMenu* menu);
|
void append_menu_item_reload_from_disk(wxMenu* menu);
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <wx/numformatter.h>
|
#include <wx/numformatter.h>
|
||||||
|
|
||||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
#include "slic3r/Utils/FixModelByWin10.hpp"
|
||||||
|
#include "slic3r/Utils/FixModelMesh.hpp"
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
#include "wx/uiaction.h"
|
#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));
|
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()
|
void ObjectList::simplify()
|
||||||
{
|
{
|
||||||
auto plater = wxGetApp().plater();
|
auto plater = wxGetApp().plater();
|
||||||
|
@ -368,6 +368,7 @@ public:
|
|||||||
void split_instances();
|
void split_instances();
|
||||||
void rename_item();
|
void rename_item();
|
||||||
void fix_through_netfabb();
|
void fix_through_netfabb();
|
||||||
|
void fix_model_mesh();
|
||||||
void simplify();
|
void simplify();
|
||||||
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
||||||
|
|
||||||
|
@ -1910,6 +1910,7 @@ struct Plater::priv
|
|||||||
bool can_arrange() const;
|
bool can_arrange() const;
|
||||||
bool can_layers_editing() const;
|
bool can_layers_editing() const;
|
||||||
bool can_fix_through_netfabb() const;
|
bool can_fix_through_netfabb() const;
|
||||||
|
bool can_fix_model_mesh() const;
|
||||||
bool can_simplify() const;
|
bool can_simplify() const;
|
||||||
bool can_set_instance_to_object() const;
|
bool can_set_instance_to_object() const;
|
||||||
bool can_mirror() const;
|
bool can_mirror() const;
|
||||||
@ -4857,6 +4858,13 @@ bool Plater::priv::can_fix_through_netfabb() const
|
|||||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
#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
|
bool Plater::priv::can_simplify() const
|
||||||
{
|
{
|
||||||
// is object for simplification selected
|
// 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_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_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_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_simplify() const { return p->can_simplify(); }
|
||||||
bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
|
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_split_to_volumes() const { return p->can_split_to_volumes(); }
|
||||||
|
@ -351,6 +351,7 @@ public:
|
|||||||
bool can_decrease_instances() const;
|
bool can_decrease_instances() const;
|
||||||
bool can_set_instance_to_object() const;
|
bool can_set_instance_to_object() const;
|
||||||
bool can_fix_through_netfabb() const;
|
bool can_fix_through_netfabb() const;
|
||||||
|
bool can_fix_model_mesh() const;
|
||||||
bool can_simplify() const;
|
bool can_simplify() const;
|
||||||
bool can_split_to_objects() const;
|
bool can_split_to_objects() const;
|
||||||
bool can_split_to_volumes() const;
|
bool can_split_to_volumes() const;
|
||||||
|
154
src/slic3r/Utils/FixModelMesh.hpp
Normal file
154
src/slic3r/Utils/FixModelMesh.hpp
Normal 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_ */
|
Loading…
x
Reference in New Issue
Block a user