From f9e22513c10b3b5c120551ddea0a19a34601a6be Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 30 Mar 2022 11:48:22 +0200 Subject: [PATCH] Cut WIP: * Added a first detection if a connector position is valid * Code cleaning: Deleted unused set_range_and_pos function * Some code refactoring --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 148 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 9 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 11 -- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 1 - src/slic3r/GUI/MeshUtils.cpp | 11 ++ src/slic3r/GUI/MeshUtils.hpp | 2 + 6 files changed, 124 insertions(+), 58 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index a56218860a..5ee74cc493 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -369,18 +369,6 @@ bool GLGizmoCut3D::render_revert_button(const std::string& label_id) void GLGizmoCut3D::render_cut_plane() { - const BoundingBoxf3 box = bounding_box(); - - const float min_x = box.min.x() - Margin - m_plane_center.x(); - const float max_x = box.max.x() + Margin - m_plane_center.x(); - const float min_y = box.min.y() - Margin - m_plane_center.y(); - const float max_y = box.max.y() + Margin - m_plane_center.y(); - - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_CULL_FACE)); - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GL_SHADERS_ATTRIBUTES GLShaderProgram* shader = wxGetApp().get_shader("flat_attr"); @@ -389,6 +377,12 @@ void GLGizmoCut3D::render_cut_plane() #endif // ENABLE_GL_SHADERS_ATTRIBUTES if (shader == nullptr) return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + shader->start_using(); #if ENABLE_GL_SHADERS_ATTRIBUTES const Camera& camera = wxGetApp().plater()->get_camera(); @@ -410,14 +404,18 @@ void GLGizmoCut3D::render_cut_plane() #endif // ENABLE_GL_SHADERS_ATTRIBUTES if (!m_plane.is_initialized()) { - m_plane.reset(); - GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; init_data.reserve_vertices(4); init_data.reserve_indices(6); + const BoundingBoxf3 bb = bounding_box(); + const float min_x = bb.min.x() - Margin - m_plane_center.x(); + const float max_x = bb.max.x() + Margin - m_plane_center.x(); + const float min_y = bb.min.y() - Margin - m_plane_center.y(); + const float max_y = bb.max.y() + Margin - m_plane_center.y(); + // vertices init_data.add_vertex(Vec3f(min_x, min_y, 0.0)); init_data.add_vertex(Vec3f(max_x, min_y, 0.0)); @@ -547,14 +545,9 @@ std::string GLGizmoCut3D::on_get_name() const void GLGizmoCut3D::on_set_state() { - if (get_state() == On) { + if (get_state() == On) update_bb(); - if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { - m_selected.clear(); - m_selected.resize(selection->model_object()->cut_connectors.size(), false); - } - } m_rotation_gizmo.set_center(m_plane_center); m_rotation_gizmo.set_state(m_state); @@ -653,6 +646,13 @@ bool GLGizmoCut3D::update_bb() m_min_pos = box.min; m_bb_center = box.center(); set_center_pos(m_bb_center + m_center_offset); + + m_plane.reset(); + if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { + m_selected.clear(); + m_selected.resize(selection->model_object()->cut_connectors.size(), false); + } + return true; } return false; @@ -841,7 +841,8 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) static bool hide_clipped = true; static bool fill_cut = true; static float contour_width = 0.2f; - m_imgui->checkbox("hide_clipped", hide_clipped); + if (m_imgui->checkbox("hide_clipped", hide_clipped) && !hide_clipped) + m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_imgui->checkbox("fill_cut", fill_cut); m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); m_c->object_clipper()->set_behavior(hide_clipped, fill_cut, contour_width); @@ -860,11 +861,43 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) } } +// get volume transformation regarding to the "border". Border is related from the siae of connectors +Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) const +{ + bool is_prizm_dowel = m_connector_type == CutConnectorType::Dowel && m_connector_style == size_t(CutConnectorStyle::Prizm); + const Transform3d connector_trafo = Geometry::assemble_transform( + is_prizm_dowel ? Vec3d(0.0, 0.0, -m_connector_depth_ratio) : Vec3d::Zero(), + m_rotation_gizmo.get_rotation(), + Vec3d(0.5*m_connector_size, 0.5*m_connector_size, is_prizm_dowel ? 2 * m_connector_depth_ratio : m_connector_depth_ratio), + Vec3d::Ones()); + const Vec3d connector_bb = m_connector_mesh.transformed_bounding_box(connector_trafo).size(); + + const Vec3d bb = volume->mesh().bounding_box().size(); + + // calculate an unused border - part of the the volume, where we can't put connectors + const Vec3d border_scale(connector_bb.x() / bb.x(), connector_bb.y() / bb.y(), connector_bb.z() / bb.z()); + + const Transform3d vol_matrix = volume->get_matrix(); + const Vec3d vol_trans = vol_matrix.translation(); + // offset of the volume will be changed after scaling, so calculate the needed offset and set it to a volume_trafo + const Vec3d offset(vol_trans.x() * border_scale.x(), vol_trans.y() * border_scale.y(), vol_trans.z() * border_scale.z()); + + // scale and translate volume to suppress to put connectors too close to the border + return Geometry::assemble_transform(offset, Vec3d::Zero(), Vec3d::Ones() - border_scale, Vec3d::Ones()) * vol_matrix; +} + void GLGizmoCut3D::render_connectors(bool picking) { + m_has_invalid_connector = false; + if (m_connector_mode == CutConnectorMode::Auto) return; + const ModelObject* mo = m_c->selection_info()->model_object(); + const CutConnectors& connectors = mo->cut_connectors; + if (connectors.size() != m_selected.size()) + return; + #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GL_SHADERS_ATTRIBUTES GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat_attr") : wxGetApp().get_shader("gouraud_light_attr"); @@ -892,47 +925,59 @@ void GLGizmoCut3D::render_connectors(bool picking) #endif // ENABLE_GL_SHADERS_ATTRIBUTES ColorRGBA render_color; - const ModelObject* mo = m_c->selection_info()->model_object(); - const CutConnectors& connectors = mo->cut_connectors; - size_t cache_size = connectors.size(); - const Vec3d& instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); + const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const Vec3d& instance_offset = mi->get_offset(); const float sla_shift = m_c->selection_info()->get_sla_shift(); const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); - const Vec3d& normal = cp ? cp->get_normal() : Vec3d::Ones(); + const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; - for (size_t i = 0; i < cache_size; ++i) { + const Transform3d instance_trafo = Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift), Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones()) * mi->get_transformation().get_matrix(); + + for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; const bool& point_selected = m_selected[i]; + double height = connector.height; + // recalculate connector position to world position + Vec3d pos = connector.pos + instance_offset; + if (m_connector_type == CutConnectorType::Dowel && + m_connector_style == size_t(CutConnectorStyle::Prizm)) { + pos -= height * normal; + height *= 2; + } + pos[Z] += sla_shift; + // First decide about the color of the point. if (picking) render_color = picking_decode(BASE_ID - i - m_connectors_group_id); else { if (size_t(m_hover_id- m_connectors_group_id) == i) render_color = ColorRGBA::CYAN(); - else // neither hover nor picking - render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); - } + else { // neither hover nor picking + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + ++mesh_id; + if (!mv->is_model_part()) + continue; -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_connector_shape.set_color(render_color); -#else - const_cast(&m_connector_shape)->set_color(-1, render_color); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + const Transform3d volume_trafo = get_volume_transformation(mv); - double height = connector.height; - // recalculate connector position to world position - Vec3d pos = connector.pos + instance_offset; - if (m_connector_type == CutConnectorType::Dowel && - m_connector_style == size_t(CutConnectorStyle::Prizm)) { - pos -= height * normal; - height *= 2; + if (m_c->raycaster()->raycasters()[mesh_id]->is_valid_intersection(pos, -normal, instance_trafo * volume_trafo)) { + render_color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); + break; + } + render_color = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + } + if (!m_has_invalid_connector && render_color == ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f)) + m_has_invalid_connector = true; + } } - pos[Z] += sla_shift; #if ENABLE_GL_SHADERS_ATTRIBUTES + m_connector_shape.set_color(render_color); + const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( Vec3d(pos.x(), pos.y(), pos.z()), m_rotation_gizmo.get_rotation(), @@ -942,6 +987,8 @@ void GLGizmoCut3D::render_connectors(bool picking) shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #else + const_cast(&m_connector_shape)->set_color(-1, render_color); + glsafe(::glPushMatrix()); glsafe(::glTranslatef(pos.x(), pos.y(), pos.z())); @@ -968,6 +1015,9 @@ void GLGizmoCut3D::render_connectors(bool picking) bool GLGizmoCut3D::can_perform_cut() const { + if (m_has_invalid_connector) + return false; + BoundingBoxf3 box = bounding_box(); double dist = (m_plane_center - box.center()).norm(); if (dist > box.radius()) @@ -1053,7 +1103,10 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair Vec3f hit; Vec3f normal; bool clipping_plane_was_hit = false; - m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * mv->get_matrix(), + + const Transform3d volume_trafo = get_volume_transformation(mv); + + m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * volume_trafo, camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), nullptr, &clipping_plane_was_hit); if (clipping_plane_was_hit) { @@ -1082,7 +1135,12 @@ void GLGizmoCut3D::update_connector_shape() if (m_connector_shape.is_initialized()) m_connector_shape.reset(); - m_connector_shape.init_from(ModelObject::get_connector_mesh({ m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) })); + const indexed_triangle_set its = ModelObject::get_connector_mesh({ m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }); + m_connector_shape.init_from(its); + + m_connector_mesh.clear(); + m_connector_mesh = TriangleMesh(its); + } void GLGizmoCut3D::update_model_object() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 9fc43ec2ae..ca49d2e4ea 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -11,6 +11,7 @@ namespace Slic3r { enum class CutConnectorType : int; +class ModelVolume; namespace GUI { class Selection; @@ -30,7 +31,10 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_bb_center{ Vec3d::Zero() }; Vec3d m_center_offset{ Vec3d::Zero() }; - GLModel m_connector_shape; + GLModel m_connector_shape; + TriangleMesh m_connector_mesh; + // workaround for using of the clipping plane normal + Vec3d m_clp_normal{ Vec3d::Ones() }; #if ENABLE_LEGACY_OPENGL_REMOVAL GLModel m_plane; @@ -57,6 +61,8 @@ class GLGizmoCut3D : public GLGizmoBase bool m_selection_empty = true; bool m_wait_for_up_event = false; + bool m_has_invalid_connector{ false }; + Matrix3d m_rotation_matrix; Vec3d m_rotations{ Vec3d::Zero() }; @@ -136,6 +142,7 @@ private: void render_connect_mode_radio_button(CutConnectorMode mode); bool render_revert_button(const std::string& label); void render_connect_type_radio_button(CutConnectorType type); + Transform3d get_volume_transformation(const ModelVolume* volume) const; void render_connectors(bool picking); bool can_perform_cut() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 26003085ec..7f60892b1b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -465,17 +465,6 @@ void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) get_pool()->get_canvas()->set_as_dirty(); } -void ObjectClipper::set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos) -{ - Vec3d normal = end-origin; - double norm = normal.norm(); - pos = std::clamp(pos, 0.0001, norm); - normal.normalize(); - m_clp.reset(new ClippingPlane(normal, normal.dot(origin)+pos)); - m_clp_ratio = pos; - get_pool()->get_canvas()->set_as_dirty(); -} - void ObjectClipper::set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos) { m_clp.reset(new ClippingPlane(cpl_normal, cpl_offset)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 226e5f7b51..efd3b436e8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -260,7 +260,6 @@ public: const ClippingPlane* get_clipping_plane() const; void render_cut() const; void set_position_by_ratio(double pos, bool keep_normal); - void set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos); void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos); void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index e5cd1f79e1..ca274e4295 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -416,6 +416,17 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& } +bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const +{ + point = trafo.inverse() * point; + + std::vector hits = m_emesh.query_ray_hits(point, direction); + std::vector neg_hits = m_emesh.query_ray_hits(point, -direction); + + return !hits.empty() && !neg_hits.empty(); +} + + std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector& points, const ClippingPlane* clipping_plane) const { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index dc522eb3bf..0faf07e291 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -157,6 +157,8 @@ public: bool* was_clipping_plane_hit = nullptr // is the hit on the clipping place cross section? ) const; + bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const; + // Given a vector of points in woorld coordinates, this returns vector // of indices of points that are visible (i.e. not cut by clipping plane // or obscured by part of the mesh.