Tech ENABLE_SHOW_NON_MANIFOLD_EDGES - 1st installment - Calculate and show in 3D view non-manifold edges as lines
This commit is contained in:
parent
81edc7d752
commit
18e2cc2298
12 changed files with 166 additions and 22 deletions
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>());
|
||||
|
|
|
@ -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>());
|
||||
|
|
|
@ -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>());
|
||||
|
|
|
@ -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)"),
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Add table
Reference in a new issue