Tech ENABLE_SHOW_NON_MANIFOLD_EDGES - 1st installment - Calculate and show in 3D view non-manifold edges as lines

This commit is contained in:
enricoturri1966 2022-01-20 13:32:24 +01:00
parent 81edc7d752
commit 18e2cc2298
12 changed files with 166 additions and 22 deletions

View file

@ -150,6 +150,11 @@ void AppConfig::set_defaults()
if (get("order_volumes").empty())
set("order_volumes", "1");
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
if (get("non_manifold_edges").empty())
set("non_manifold_edges", "1");
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
if (get("clear_undo_redo_stack_on_new_project").empty())
set("clear_undo_redo_stack_on_new_project", "1");
}

View file

@ -64,6 +64,8 @@
#define ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
// Enable removal of old OpenGL render calls
#define ENABLE_GLBEGIN_GLEND_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
// Enable show non-manifold edges
#define ENABLE_SHOW_NON_MANIFOLD_EDGES (1 && ENABLE_2_5_0_ALPHA1)
#endif // _prusaslicer_technologies_h_

View file

@ -1239,13 +1239,30 @@ bool its_is_splittable(const indexed_triangle_set &its, const std::vector<Vec3i>
size_t its_num_open_edges(const std::vector<Vec3i> &face_neighbors)
{
size_t num_open_edges = 0;
for (Vec3i neighbors : face_neighbors)
for (const Vec3i& neighbors : face_neighbors)
for (int n : neighbors)
if (n < 0)
++ num_open_edges;
return num_open_edges;
}
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
std::vector<std::pair<int, int>> its_get_open_edges(const indexed_triangle_set& its)
{
std::vector<std::pair<int, int>> ret;
std::vector<Vec3i> face_neighbors = its_face_neighbors(its);
for (size_t i = 0; i < face_neighbors.size(); ++i) {
for (size_t j = 0; j < 3; ++j) {
if (face_neighbors[i][j] < 0) {
const Vec2i edge_indices = its_triangle_edge(its.indices[i], j);
ret.emplace_back(edge_indices[0], edge_indices[1]);
}
}
}
return ret;
}
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
size_t its_num_open_edges(const indexed_triangle_set &its)
{
return its_num_open_edges(its_face_neighbors(its));

View file

@ -227,6 +227,12 @@ bool its_is_splittable(const indexed_triangle_set &its, const std::vector<Vec3i>
size_t its_num_open_edges(const indexed_triangle_set &its);
size_t its_num_open_edges(const std::vector<Vec3i> &face_neighbors);
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
// Calculate and returns the list of unconnected face edges.
// Each edge is represented by the indices of the two endpoint vertices
std::vector<std::pair<int, int>> its_get_open_edges(const indexed_triangle_set& its);
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
// Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors.
void its_shrink_to_fit(indexed_triangle_set &its);

View file

@ -302,10 +302,10 @@ void GLVolume::SinkingContours::render()
void GLVolume::SinkingContours::update()
{
int object_idx = m_parent.object_idx();
Model& model = GUI::wxGetApp().plater()->model();
const int object_idx = m_parent.object_idx();
const Model& model = GUI::wxGetApp().plater()->model();
if (0 <= object_idx && object_idx < (int)model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) {
if (0 <= object_idx && object_idx < int(model.objects.size()) && m_parent.is_sinking() && !m_parent.is_below_printbed()) {
const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box();
if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) {
m_old_box = box;
@ -317,13 +317,16 @@ void GLVolume::SinkingContours::update()
GUI::GLModel::InitializationData init_data;
MeshSlicingParams slicing_params;
slicing_params.trafo = m_parent.world_matrix();
Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params));
for (ExPolygon &expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) {
const Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params));
for (const ExPolygon &expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) {
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Triangles;
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly);
entity.positions.reserve(entity.positions.size() + triangulation.size());
entity.normals.reserve(entity.normals.size() + triangulation.size());
entity.indices.reserve(entity.indices.size() + triangulation.size() / 3);
for (const Vec3d& v : triangulation) {
entity.positions.emplace_back(v.cast<float>() + Vec3f(0.0f, 0.0f, 0.015f)); // add a small positive z to avoid z-fighting
entity.positions.emplace_back(v.cast<float>() + 0.015f * Vec3f::UnitZ()); // add a small positive z to avoid z-fighting
entity.normals.emplace_back(Vec3f::UnitZ());
const size_t positions_count = entity.positions.size();
if (positions_count % 3 == 0) {
@ -344,6 +347,61 @@ void GLVolume::SinkingContours::update()
m_model.reset();
}
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
void GLVolume::NonManifoldEdges::render()
{
update();
glsafe(::glLineWidth(2.0f));
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(m_parent.world_matrix().data()));
m_model.set_color(-1, complementary(m_parent.render_color));
m_model.render();
glsafe(::glPopMatrix());
}
void GLVolume::NonManifoldEdges::update()
{
if (!m_update_needed)
return;
m_model.reset();
const int object_idx = m_parent.object_idx();
const Model& model = GUI::wxGetApp().plater()->model();
if (0 <= object_idx && object_idx < int(model.objects.size())) {
const ModelObject* model_object = model.objects[object_idx];
const int volume_idx = m_parent.volume_idx();
if (0 <= volume_idx && volume_idx < int(model_object->volumes.size())) {
const ModelVolume* model_volume = model_object->volumes[volume_idx];
const TriangleMesh& mesh = model_volume->mesh();
const std::vector<std::pair<int, int>> edges = its_get_open_edges(mesh.its);
if (!edges.empty()) {
GUI::GLModel::InitializationData init_data;
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Lines;
entity.positions.reserve(2 * edges.size());
entity.normals.reserve(2 * edges.size());
entity.indices.reserve(2 * edges.size());
for (const std::pair<int, int>& edge : edges) {
entity.positions.emplace_back(mesh.its.vertices[edge.first].cast<float>());
entity.positions.emplace_back(mesh.its.vertices[edge.second].cast<float>());
entity.normals.emplace_back(Vec3f::UnitZ());
entity.normals.emplace_back(Vec3f::UnitZ());
entity.indices.emplace_back(entity.positions.size() - 2);
entity.indices.emplace_back(entity.positions.size() - 1);
}
init_data.entities.emplace_back(entity);
m_model.init_from(init_data);
}
}
}
m_update_needed = false;
}
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
const ColorRGBA GLVolume::SELECTED_COLOR = ColorRGBA::GREEN();
const ColorRGBA GLVolume::HOVER_SELECT_COLOR = { 0.4f, 0.9f, 0.1f, 1.0f };
const ColorRGBA GLVolume::HOVER_DESELECT_COLOR = { 1.0f, 0.75f, 0.75f, 1.0f };
@ -363,6 +421,9 @@ const std::array<ColorRGBA, 4> GLVolume::MODEL_COLOR = { {
GLVolume::GLVolume(float r, float g, float b, float a)
: m_sla_shift_z(0.0)
, m_sinking_contours(*this)
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
, m_non_manifold_edges(*this)
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
// geometry_id == 0 -> invalid
, geometry_id(std::pair<size_t, size_t>(0, 0))
, extruder_id(0)
@ -571,6 +632,13 @@ void GLVolume::render_sinking_contours()
m_sinking_contours.render();
}
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
void GLVolume::render_non_manifold_edges()
{
m_non_manifold_edges.render();
}
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
int obj_idx,
@ -883,6 +951,14 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
}
}
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
if (m_show_non_manifold_edges && GUI::wxGetApp().app_config->get("non_manifold_edges") == "1") {
for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->render_non_manifold_edges();
}
}
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
if (disable_cullface)
glsafe(::glEnable(GL_CULL_FACE));

View file

@ -304,6 +304,25 @@ private:
SinkingContours m_sinking_contours;
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
class NonManifoldEdges
{
GLVolume& m_parent;
GUI::GLModel m_model;
bool m_update_needed{ true };
public:
NonManifoldEdges(GLVolume& volume) : m_parent(volume) {}
void render();
void set_as_dirty() { m_update_needed = true; }
private:
void update();
};
NonManifoldEdges m_non_manifold_edges;
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
public:
// Color of the triangles / quads held by this volume.
ColorRGBA color;
@ -500,6 +519,9 @@ public:
bool is_sinking() const;
bool is_below_printbed() const;
void render_sinking_contours();
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
void render_non_manifold_edges();
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const {
@ -556,7 +578,10 @@ private:
};
Slope m_slope;
bool m_show_sinking_contours = false;
bool m_show_sinking_contours{ false };
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
bool m_show_non_manifold_edges{ true };
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
public:
GLVolumePtrs volumes;
@ -629,6 +654,9 @@ public:
void set_slope_normal_z(float normal_z) { m_slope.normal_z = normal_z; }
void set_default_slope_normal_z() { m_slope.normal_z = -::cos(Geometry::deg2rad(90.0f - 45.0f)); }
void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; }
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
void set_show_non_manifold_edges(bool show) { m_show_non_manifold_edges = show; }
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
// returns true if all the volumes are completely contained in the print volume
// returns the containment state in the given out_state, if non-null

View file

@ -5198,6 +5198,9 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances());
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
m_volumes.set_show_non_manifold_edges(!m_gizmos.is_hiding_instances() && m_gizmos.get_current_type() != GLGizmosManager::Simplify);
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
if (shader != nullptr) {

View file

@ -115,8 +115,8 @@ void GLGizmoCut::on_render()
m_plane.reset();
GLModel::InitializationData init_data;
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Triangles;
GLModel::InitializationData::Entity entity;
entity.type = GLModel::PrimitiveType::Triangles;
entity.positions.reserve(4);
entity.positions.emplace_back(Vec3f(min_x, min_y, plane_center.z()));
entity.positions.emplace_back(Vec3f(max_x, min_y, plane_center.z()));
@ -170,8 +170,8 @@ void GLGizmoCut::on_render()
m_grabber_connection.reset();
GLModel::InitializationData init_data;
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Lines;
GLModel::InitializationData::Entity entity;
entity.type = GLModel::PrimitiveType::Lines;
entity.positions.reserve(2);
entity.positions.emplace_back(plane_center.cast<float>());
entity.positions.emplace_back(m_grabbers[0].center.cast<float>());

View file

@ -114,8 +114,8 @@ void GLGizmoMove3D::on_render()
m_grabber_connections[id].model.reset();
GLModel::InitializationData init_data;
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Lines;
GLModel::InitializationData::Entity entity;
entity.type = GLModel::PrimitiveType::Lines;
entity.positions.reserve(2);
entity.positions.emplace_back(center.cast<float>());
entity.positions.emplace_back(m_grabbers[id].center.cast<float>());

View file

@ -402,8 +402,8 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int
m_grabber_connections[id].model.reset();
GLModel::InitializationData init_data;
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Lines;
GLModel::InitializationData::Entity entity;
entity.type = GLModel::PrimitiveType::Lines;
entity.positions.reserve(2);
entity.positions.emplace_back(m_grabbers[id_1].center.cast<float>());
entity.positions.emplace_back(m_grabbers[id_2].center.cast<float>());

View file

@ -410,6 +410,13 @@ void PreferencesDialog::build()
"If disabled, you can reorder Model Parts, Negative Volumes and Modifiers. But one of the model parts have to be on the first place."),
app_config->get("order_volumes") == "1");
#if ENABLE_SHOW_NON_MANIFOLD_EDGES
append_bool_option(m_optgroup_gui, "non_manifold_edges",
L("Show non-manifold edges"),
L("If enabled, shows non-manifold edges."),
app_config->get("non_manifold_edges") == "1");
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
#ifdef _MSW_DARK_MODE
append_bool_option(m_optgroup_gui, "tabs_as_menu",
L("Set settings tabs as menu items (experimental)"),

View file

@ -1901,8 +1901,8 @@ void Selection::render_bounding_box(const BoundingBoxf3 & box, float* color) con
const Vec3f size = 0.2f * box.size().cast<float>();
GLModel::InitializationData init_data;
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Lines;
GLModel::InitializationData::Entity entity;
entity.type = GLModel::PrimitiveType::Lines;
entity.positions.reserve(48);
entity.positions.emplace_back(Vec3f(b_min.x(), b_min.y(), b_min.z()));
@ -2234,8 +2234,8 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
m_planes.models[0].reset();
GLModel::InitializationData init_data;
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Triangles;
GLModel::InitializationData::Entity entity;
entity.type = GLModel::PrimitiveType::Triangles;
entity.positions.reserve(4);
entity.positions.emplace_back(Vec3f(p1.x(), p1.y(), z1));
entity.positions.emplace_back(Vec3f(p2.x(), p1.y(), z1));
@ -2264,8 +2264,8 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
m_planes.models[1].reset();
GLModel::InitializationData init_data;
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Triangles;
GLModel::InitializationData::Entity entity;
entity.type = GLModel::PrimitiveType::Triangles;
entity.positions.reserve(4);
entity.positions.emplace_back(Vec3f(p1.x(), p1.y(), z2));
entity.positions.emplace_back(Vec3f(p2.x(), p1.y(), z2));