diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index fc91dff2d..a87cb68fb 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1873,8 +1873,8 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b float render_color[3]; for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i) { - const sla::SupportPoint& support_point = m_editing_mode_cache[i].first; - const bool& point_selected = m_editing_mode_cache[i].second; + const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point; + const bool& point_selected = m_editing_mode_cache[i].selected; // First decide about the color of the point. if (picking) { @@ -1889,7 +1889,7 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b render_color[2] = 1.0f; } else { // neigher hover nor picking - bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].first.is_new_island; + bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].support_point.is_new_island; if (m_editing_mode) { render_color[0] = point_selected ? 1.0f : (supports_new_island ? 0.3f : 0.7f); render_color[1] = point_selected ? 0.3f : (supports_new_island ? 0.3f : 0.7f); @@ -1903,12 +1903,32 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f}; ::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive); - // Now render the sphere. Inverse matrix of the instance scaling is applied so that the - // sphere does not scale with the object. + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. ::glPushMatrix(); ::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2)); ::glMultMatrixd(instance_scaling_matrix_inverse.data()); - ::gluSphere(m_quadric, m_editing_mode_cache[i].first.head_front_radius * RenderPointScale, 64, 36); + + // Matrices set, we can render the point mark now. + // If in editing mode, we'll also render a cone pointing to the sphere. + if (m_editing_mode) { + if (m_editing_mode_cache[i].normal == Vec3f::Zero()) + update_cache_entry_normal(i); // in case the normal is not yet cached, find and cache it + + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_mode_cache[i].normal.cast()); + Eigen::AngleAxisd aa(q); + ::glRotated(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)); + + const float cone_radius = 0.25f; // mm + const float cone_height = 0.75f; + ::glPushMatrix(); + ::glTranslatef(0.f, 0.f, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale); + ::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 36, 1); + ::glTranslatef(0.f, 0.f, cone_height); + ::gluDisk(m_quadric, 0.0, cone_radius, 36, 1); + ::glPopMatrix(); + } + ::gluSphere(m_quadric, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale, 64, 36); ::glPopMatrix(); } @@ -1957,7 +1977,7 @@ void GLGizmoSlaSupports::update_mesh() m_AABB.init(m_V, m_F); } -Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) +std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) { // if the gizmo doesn't have the V, F structures for igl, calculate them first: if (m_V.size() == 0) @@ -1992,9 +2012,16 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) if (!m_AABB.intersect_ray(m_V, m_F, point1.cast(), (point2-point1).cast(), hit)) throw std::invalid_argument("unproject_on_mesh(): No intersection found."); - int fid = hit.id; - Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); - return bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)); + int fid = hit.id; // facet id + Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit + Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0))); + Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0))); + + // Calculate and return both the point and the facet normal. + return std::make_pair( + bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)), + a.cross(b) + ); } // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. @@ -2046,10 +2073,9 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous // If there is some selection, don't add new point and deselect everything instead. if (m_selection_empty) { - Vec3f new_pos; try { - new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case - m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), false)); + std::pair pos_and_normal = unproject_on_mesh(mouse_position); // don't create anything if this throws + m_editing_mode_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); m_unsaved_changes = true; } catch (...) { // not clicked on object @@ -2090,7 +2116,7 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous // Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh: for (unsigned int i=0; i() * support_point.pos; pos(2) += z_offset; GLdouble out_x, out_y, out_z; @@ -2166,7 +2192,7 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous void GLGizmoSlaSupports::delete_selected_points(bool force) { for (unsigned int idx=0; idx pos_and_normal; try { - new_pos = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1))); + pos_and_normal = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1))); } catch (...) { return; } - m_editing_mode_cache[m_hover_id].first.pos = new_pos; - m_editing_mode_cache[m_hover_id].first.is_new_island = false; + m_editing_mode_cache[m_hover_id].support_point.pos = pos_and_normal.first; + m_editing_mode_cache[m_hover_id].support_point.is_new_island = false; + m_editing_mode_cache[m_hover_id].normal = pos_and_normal.second; m_unsaved_changes = true; // Do not update immediately, wait until the mouse is released. // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); @@ -2255,6 +2282,18 @@ std::vector GLGizmoSlaSupports::get_config_options(const st } +void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const +{ + int idx = 0; + Eigen::Matrix pp = m_editing_mode_cache[i].support_point.pos; + Eigen::Matrix cc; + m_AABB.squared_distance(m_V, m_F, pp, idx, cc); + Vec3f a = (m_V.row(m_F(idx, 1)) - m_V.row(m_F(idx, 0))); + Vec3f b = (m_V.row(m_F(idx, 2)) - m_V.row(m_F(idx, 0))); + m_editing_mode_cache[i].normal = a.cross(b); +} + + #if ENABLE_IMGUI void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) @@ -2295,9 +2334,9 @@ RENDER_AGAIN: ImGui::SameLine(); if (ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f")) { // value was changed - for (auto& point_and_selection : m_editing_mode_cache) - if (point_and_selection.second) { - point_and_selection.first.head_front_radius = m_new_point_head_diameter / 2.f; + for (auto& cache_entry : m_editing_mode_cache) + if (cache_entry.selected) { + cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; m_unsaved_changes = true; } } @@ -2480,16 +2519,16 @@ void GLGizmoSlaSupports::select_point(int i) { if (i == AllPoints || i == NoPoints) { for (auto& point_and_selection : m_editing_mode_cache) - point_and_selection.second = ( i == AllPoints ); + point_and_selection.selected = ( i == AllPoints ); m_selection_empty = (i == NoPoints); if (i == AllPoints) - m_new_point_head_diameter = m_editing_mode_cache[0].first.head_front_radius * 2.f; + m_new_point_head_diameter = m_editing_mode_cache[0].support_point.head_front_radius * 2.f; } else { - m_editing_mode_cache[i].second = true; + m_editing_mode_cache[i].selected = true; m_selection_empty = false; - m_new_point_head_diameter = m_editing_mode_cache[i].first.head_front_radius * 2.f; + m_new_point_head_diameter = m_editing_mode_cache[i].support_point.head_front_radius * 2.f; } } @@ -2499,7 +2538,7 @@ void GLGizmoSlaSupports::editing_mode_discard_changes() { m_editing_mode_cache.clear(); for (const sla::SupportPoint& point : m_model_object->sla_support_points) - m_editing_mode_cache.push_back(std::make_pair(point, false)); + m_editing_mode_cache.emplace_back(point, false); m_editing_mode = false; m_unsaved_changes = false; } @@ -2513,8 +2552,8 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() if (m_unsaved_changes) { m_model_object->sla_points_status = sla::PointsStatus::UserModified; m_model_object->sla_support_points.clear(); - for (const std::pair& point_and_selection : m_editing_mode_cache) - m_model_object->sla_support_points.push_back(point_and_selection.first); + for (const CacheEntry& cache_entry : m_editing_mode_cache) + m_model_object->sla_support_points.push_back(cache_entry.support_point); // Recalculate support structures once the editing mode is left. // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); @@ -2531,7 +2570,7 @@ void GLGizmoSlaSupports::editing_mode_reload_cache() { m_editing_mode_cache.clear(); for (const sla::SupportPoint& point : m_model_object->sla_support_points) - m_editing_mode_cache.push_back(std::make_pair(point, false)); + m_editing_mode_cache.emplace_back(point, false); m_unsaved_changes = false; } diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index c0690b8bc..a872a161e 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -471,7 +471,7 @@ private: ModelObject* m_old_model_object = nullptr; int m_active_instance = -1; int m_old_instance_id = -1; - Vec3f unproject_on_mesh(const Vec2d& mouse_pos); + std::pair unproject_on_mesh(const Vec2d& mouse_pos); const float RenderPointScale = 1.f; @@ -484,6 +484,16 @@ private: Geometry::Transformation transformation; }; + class CacheEntry { + public: + CacheEntry(const sla::SupportPoint& point, bool sel, const Vec3f& norm = Vec3f::Zero()) : + support_point(point), selected(sel), normal(norm) {} + + sla::SupportPoint support_point; + bool selected; // whether the point is selected + Vec3f normal; + }; + // This holds information to decide whether recalculation is necessary: SourceDataSummary m_source_data; @@ -510,6 +520,7 @@ private: void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const; bool is_mesh_update_necessary() const; void update_mesh(); + void update_cache_entry_normal(unsigned int i) const; #if !ENABLE_IMGUI void render_tooltip_texture() const; @@ -523,7 +534,7 @@ private: float m_new_point_head_diameter; // Size of a new point. float m_minimal_point_distance = 20.f; float m_density = 100.f; - std::vector> m_editing_mode_cache; // a support point and whether it is currently selected + mutable std::vector m_editing_mode_cache; // a support point and whether it is currently selected bool m_selection_rectangle_active = false; Vec2d m_selection_rectangle_start_corner;