From fd1f9d65fb28901a98c7ba4ea388916344a5c896 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 11 Mar 2019 10:35:23 +0100 Subject: [PATCH 01/11] First steps on SLA clipping plane --- src/slic3r/GUI/GLCanvas3D.cpp | 75 +++++++++++----- src/slic3r/GUI/GLCanvas3D.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 90 ++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 1 + 4 files changed, 150 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cc1794216..6fdad8795 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1317,6 +1317,22 @@ bool GLCanvas3D::Gizmos::gizmo_event(SLAGizmoEventType action, const Vec2d& mous return false; } + + +std::pair GLCanvas3D::Gizmos::get_sla_clipping_plane() const +{ + if (!m_enabled) + return std::make_pair(0.f, 0.f); + + GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); + if (it != m_gizmos.end()) + return reinterpret_cast(it->second)->get_sla_clipping_plane(); + + return std::make_pair(0.f, 0.f);; +} + + + void GLCanvas3D::Gizmos::render_current_gizmo(const Selection& selection) const { if (!m_enabled) @@ -2566,7 +2582,27 @@ void GLCanvas3D::render() if (early_bed_render) _render_bed(theta); +//////////////////////// + //Eigen::Matrix stashed_projection_matrix; + //::glGetDoublev(GL_PROJECTION_MATRIX, stashed_projection_matrix.data()); + const Size& cnv_size = get_canvas_size(); + if (m_gizmos.get_current_type() == Gizmos::SlaSupports) { + std::pair clipping_limits = m_gizmos.get_sla_clipping_plane(); + set_ortho_projection((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height(), clipping_limits.first, clipping_limits.second); + } + +//////////////////// _render_objects(); +////////////////////////////////// + if (m_gizmos.get_current_type() == Gizmos::SlaSupports) { + //::glMatrixMode(GL_PROJECTION); + //::glLoadIdentity(); + //::glMultMatrixd(stashed_projection_matrix.data()); + //::glMatrixMode(GL_MODELVIEW); + _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); + } +////////////////////////////////// + _render_sla_slices(); _render_selection(); @@ -4416,29 +4452,13 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) _set_current(); ::glViewport(0, 0, w, h); - ::glMatrixMode(GL_PROJECTION); - ::glLoadIdentity(); - - const BoundingBoxf3& bbox = _max_bounding_box(); - switch (m_camera.type) { case Camera::Ortho: { - float w2 = w; - float h2 = h; - float two_zoom = 2.0f * get_camera_zoom(); - if (two_zoom != 0.0f) - { - float inv_two_zoom = 1.0f / two_zoom; - w2 *= inv_two_zoom; - h2 *= inv_two_zoom; - } - // FIXME: calculate a tighter value for depth will improve z-fighting - float depth = 5.0f * (float)bbox.max_size(); - ::glOrtho(-w2, w2, -h2, h2, -depth, depth); - + float depth = 5.0f * (float)(_max_bounding_box().max_size()); + set_ortho_projection(w, h, -depth, depth); break; } // case Camera::Perspective: @@ -4470,8 +4490,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) } } - ::glMatrixMode(GL_MODELVIEW); - m_dirty = false; } @@ -4685,6 +4703,23 @@ void GLCanvas3D::_render_axes() const m_bed.render_axes(); } + +void GLCanvas3D::set_ortho_projection(float w, float h, float near, float far) const +{ + float two_zoom = 2.0f * get_camera_zoom(); + if (two_zoom != 0.0f) + { + float inv_two_zoom = 1.0f / two_zoom; + w *= inv_two_zoom; + h *= inv_two_zoom; + } + ::glMatrixMode(GL_PROJECTION); + ::glLoadIdentity(); + ::glOrtho(-w, w, -h, h, near, far); + ::glMatrixMode(GL_MODELVIEW); +} + + void GLCanvas3D::_render_objects() const { if (m_volumes.empty()) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 7e414d52a..acaae8dbd 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -448,6 +448,7 @@ private: void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false); + std::pair get_sla_clipping_plane() const; void render_current_gizmo(const Selection& selection) const; void render_current_gizmo_for_picking_pass(const Selection& selection) const; @@ -774,6 +775,9 @@ private: // Returns the view ray line, in world coordinate, at the given mouse position. Linef3 mouse_ray(const Point& mouse_pos); + // Sets current projection matrix to ortho, accounting for current camera zoom. + void set_ortho_projection(float w, float h, float near, float far) const; + void _start_timer(); void _stop_timer(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 2264c541e..e2478bc0c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -56,6 +56,10 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S if (model_object && selection.is_from_single_instance()) { + // Cache the bb - it's needed for dealing with the clipping plane quite often + // It could be done inside update_mesh but one has to account for scaling of the instance. + m_active_instance_bb = m_model_object->instance_bounding_box(m_active_instance); + if (is_mesh_update_necessary()) { update_mesh(); editing_mode_reload_cache(); @@ -232,6 +236,21 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) ::glPopMatrix(); } + + +bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera, float z_shift) const +{ + if (m_clipping_plane_distance == 0.f) + return false; + + Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point; + transformed_point(2) += z_shift; + return direction_to_camera.dot(m_active_instance_bb.center()) + m_active_instance_bb.radius() + - m_clipping_plane_distance * 2*m_active_instance_bb.radius() < direction_to_camera.dot(transformed_point); +} + + + bool GLGizmoSlaSupports::is_mesh_update_necessary() const { return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) @@ -559,6 +578,69 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const + +std::pair GLGizmoSlaSupports::get_sla_clipping_plane() const +{ + if (!m_model_object) + return std::make_pair(0.f, 0.f);; + + Eigen::Matrix modelview_matrix; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); + + // clipping space origin transformed to world coords: + Vec3d clipping_origin = (modelview_matrix.inverse() * Eigen::Matrix{0, 0, 0, 1}).block<3,1>(0,0); + + // we'll recover current look direction from the modelview matrix (in world coords): + Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); + float dist = direction_to_camera.dot(clipping_origin) - direction_to_camera.dot(m_active_instance_bb.center()); + + return std::make_pair((dist - m_active_instance_bb.radius()) + m_clipping_plane_distance * 2*m_active_instance_bb.radius(), dist + 5.f*m_active_instance_bb.radius()); +} + + +/* +void GLGizmoSlaSupports::find_intersections(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& idxs) const +{ + if (aabb->is_leaf()) { // this is a facet + // corner.dot(normal) - offset + unsigned int facet_idx = aabb->m_primitive; + + Vec3f a = m_V.row(m_F(facet_idx, 0)); + Vec3f b = m_V.row(m_F(facet_idx, 1)); + Vec3f c = m_V.row(m_F(facet_idx, 2)); + } + else { // not a leaf + using CornerType = Eigen::AlignedBox::CornerType; + bool sign = std::signbit(offset - normal.dot(aabb->m_box.corner(CornerType(0)))); + for (unsigned int i=1; i<8; ++i) + if (std::signbit(offset - normal.dot(aabb->m_box.corner(CornerType(i)))) != sign) { + find_intersections(aabb->m_left, normal, offset, idxs); + find_intersections(aabb->m_right, normal, offset, idxs); + } + } +} + +void GLGizmoSlaSupports::make_line_segments() const +{ + TriangleMeshSlicer tms(&m_model_object->volumes.front()->mesh); + Vec3f normal(0.f, 1.f, 1.f); + double d = 0.; + + std::vector lines; + find_intersections(&m_AABB, normal, d, lines); + ExPolygons expolys; + tms.make_expolygons_simple(lines, &expolys); + + SVG svg("slice_loops.svg", get_extents(expolys)); + svg.draw(expolys); + //for (const IntersectionLine &l : lines[i]) + // svg.draw(l, "red", 0); + //svg.draw_outline(expolygons, "black", "blue", 0); + svg.Close(); +} +*/ + + void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) { if (!m_model_object) @@ -676,6 +758,13 @@ RENDER_AGAIN: (m_model_object->sla_points_status == sla::PointsStatus::Generating ? "Generation in progress..." : "UNKNOWN STATUS")))); } + + // Following is rendered in both editing and non-editing mode: + m_imgui->text("Clipping of view: "); + ImGui::SameLine(); + ImGui::PushItemWidth(150.0f); + bool value_changed = ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f"); + m_imgui->end(); if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode @@ -761,6 +850,7 @@ void GLGizmoSlaSupports::on_set_state() m_parent.toggle_model_objects_visibility(true); m_editing_mode = false; // so it is not active next time the gizmo opens m_editing_mode_cache.clear(); + m_clipping_plane_distance = 0.f; } m_old_state = m_state; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index bb3cf06ce..a5f77a21b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -22,6 +22,7 @@ private: ModelObject* m_model_object = nullptr; ModelObject* m_old_model_object = nullptr; int m_active_instance = -1; + BoundingBoxf3 m_active_instance_bb; // to cache the bb std::pair unproject_on_mesh(const Vec2d& mouse_pos); const float RenderPointScale = 1.f; From bc9164e40ca11f8b42e4421b3207f58ac044b1ee Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 20 Mar 2019 08:48:42 +0100 Subject: [PATCH 02/11] SLA gizmo now respects the clipping plane when rendering points and raycasting mouse onto mesh --- src/slic3r/GUI/GLCanvas3D.cpp | 50 ++++++++++++-------- src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 47 +++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 1 + 4 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6fdad8795..6f55e4059 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2582,27 +2582,7 @@ void GLCanvas3D::render() if (early_bed_render) _render_bed(theta); -//////////////////////// - //Eigen::Matrix stashed_projection_matrix; - //::glGetDoublev(GL_PROJECTION_MATRIX, stashed_projection_matrix.data()); - const Size& cnv_size = get_canvas_size(); - if (m_gizmos.get_current_type() == Gizmos::SlaSupports) { - std::pair clipping_limits = m_gizmos.get_sla_clipping_plane(); - set_ortho_projection((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height(), clipping_limits.first, clipping_limits.second); - } - -//////////////////// _render_objects(); -////////////////////////////////// - if (m_gizmos.get_current_type() == Gizmos::SlaSupports) { - //::glMatrixMode(GL_PROJECTION); - //::glLoadIdentity(); - //::glMultMatrixd(stashed_projection_matrix.data()); - //::glMatrixMode(GL_MODELVIEW); - _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); - } -////////////////////////////////// - _render_sla_slices(); _render_selection(); @@ -4720,6 +4700,28 @@ void GLCanvas3D::set_ortho_projection(float w, float h, float near, float far) c } +void GLCanvas3D::set_sla_clipping(bool enable) const +{ + if (m_gizmos.get_current_type() != Gizmos::SlaSupports) + return; + + if (enable) { + ::glMatrixMode(GL_PROJECTION); + ::glPushMatrix(); + ::glMatrixMode(GL_MODELVIEW); + const Size& cnv_size = get_canvas_size(); + std::pair clipping_limits = m_gizmos.get_sla_clipping_plane(); + set_ortho_projection((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height(), clipping_limits.first, clipping_limits.second); + } + else { + ::glMatrixMode(GL_PROJECTION); + ::glPopMatrix(); + ::glMatrixMode(GL_MODELVIEW); + ::glClear(GL_DEPTH_BUFFER_BIT); + } +} + + void GLCanvas3D::_render_objects() const { if (m_volumes.empty()) @@ -4728,6 +4730,8 @@ void GLCanvas3D::_render_objects() const ::glEnable(GL_LIGHTING); ::glEnable(GL_DEPTH_TEST); + set_sla_clipping(true); + if (m_use_VBOs) { if (m_picking_enabled) @@ -4789,6 +4793,8 @@ void GLCanvas3D::_render_objects() const } } + set_sla_clipping(false); + ::glDisable(GL_LIGHTING); } @@ -4831,6 +4837,8 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const if (!fake_colors) ::glEnable(GL_LIGHTING); + set_sla_clipping(true); + // do not cull backfaces to show broken geometry, if any ::glDisable(GL_CULL_FACE); @@ -4869,6 +4877,8 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glEnable(GL_CULL_FACE); + set_sla_clipping(false); + if (!fake_colors) ::glDisable(GL_LIGHTING); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index acaae8dbd..44c43a1e3 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -778,6 +778,9 @@ private: // Sets current projection matrix to ortho, accounting for current camera zoom. void set_ortho_projection(float w, float h, float near, float far) const; + // Set/unset near clipping plane according to SLA gizmo requirements. + void set_sla_clipping(bool enable) const; + void _start_timer(); void _stop_timer(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index e2478bc0c..07eec27e2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -157,6 +157,11 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); + // we'll recover current look direction from the modelview matrix (in world coords): + Eigen::Matrix modelview_matrix; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); + Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); + ::glPushMatrix(); ::glTranslated(0.0, 0.0, z_shift); ::glMultMatrixd(instance_matrix.data()); @@ -167,6 +172,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point; const bool& point_selected = m_editing_mode_cache[i].selected; + if (is_point_clipped(support_point.pos.cast(), direction_to_camera, z_shift)) + continue; + // First decide about the color of the point. if (picking) { std::array color = picking_color_component(i); @@ -285,6 +293,8 @@ void GLGizmoSlaSupports::update_mesh() m_AABB.init(m_V, m_F); } +// Unprojects the mouse position on the mesh and return the hit point and normal of the facet. +// The function throws if no intersection if found. 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: @@ -303,12 +313,15 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse ::gluUnProject(mouse_pos(0), viewport(3)-mouse_pos(1), 0.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point1(0), &point1(1), &point1(2)); ::gluUnProject(mouse_pos(0), viewport(3)-mouse_pos(1), 1.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point2(0), &point2(1), &point2(2)); - igl::Hit hit; + std::vector hits; const Selection& selection = m_parent.get_selection(); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); double z_offset = volume->get_sla_shift_z(); + // we'll recover current look direction from the modelview matrix (in world coords): + Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); + point1(2) -= z_offset; point2(2) -= z_offset; @@ -317,17 +330,37 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse point1 = inv * point1; point2 = inv * point2; - if (!m_AABB.intersect_ray(m_V, m_F, point1.cast(), (point2-point1).cast(), hit)) + if (!m_AABB.intersect_ray(m_V, m_F, point1.cast(), (point2-point1).cast(), hits)) throw std::invalid_argument("unproject_on_mesh(): No intersection found."); - 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))); + std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); + + // Now let's iterate through the points and find the first that is not clipped: + unsigned int i=0; + Vec3f bc; + Vec3f a; + Vec3f b; + Vec3f result; + for (i=0; i(), direction_to_camera, z_offset)) + break; + } + + if (i==hits.size() || (hits.size()-i) % 2 != 0) { + // All hits are either clipped, or there is an odd number of unclipped + // hits - meaning the nearest must be from inside the mesh. + throw std::invalid_argument("unproject_on_mesh(): No intersection found."); + } // 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)), + result, a.cross(b) ); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index a5f77a21b..872e3541f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -86,6 +86,7 @@ private: int m_canvas_height; std::vector get_config_options(const std::vector& keys) const; + bool is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera, float z_shift) const; // Methods that do the model_object and editing cache synchronization, // editing mode selection, etc: From 273fcf68a134b0c7526663049fad2f5636b67488 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 25 Mar 2019 10:47:23 +0100 Subject: [PATCH 03/11] SLA gizmo now uses glClipPlane instead of touching projection matrix Messing with the projection matrix invalidates the z-buffer This currently only works in OpenGL legacy mode --- src/slic3r/GUI/GLCanvas3D.cpp | 36 +++++++++----------- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 25 ++++++-------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 3 +- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6f55e4059..3e4824cde 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1319,16 +1319,16 @@ bool GLCanvas3D::Gizmos::gizmo_event(SLAGizmoEventType action, const Vec2d& mous -std::pair GLCanvas3D::Gizmos::get_sla_clipping_plane() const +GLCanvas3D::ClippingPlane GLCanvas3D::Gizmos::get_sla_clipping_plane() const { if (!m_enabled) - return std::make_pair(0.f, 0.f); + return ClippingPlane(); GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) return reinterpret_cast(it->second)->get_sla_clipping_plane(); - return std::make_pair(0.f, 0.f);; + return ClippingPlane(); } @@ -4706,19 +4706,17 @@ void GLCanvas3D::set_sla_clipping(bool enable) const return; if (enable) { - ::glMatrixMode(GL_PROJECTION); - ::glPushMatrix(); - ::glMatrixMode(GL_MODELVIEW); - const Size& cnv_size = get_canvas_size(); - std::pair clipping_limits = m_gizmos.get_sla_clipping_plane(); - set_ortho_projection((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height(), clipping_limits.first, clipping_limits.second); - } - else { - ::glMatrixMode(GL_PROJECTION); - ::glPopMatrix(); - ::glMatrixMode(GL_MODELVIEW); - ::glClear(GL_DEPTH_BUFFER_BIT); + ClippingPlane gizmo_clipping_plane; + try { + gizmo_clipping_plane = m_gizmos.get_sla_clipping_plane(); + } + catch (...) { return; } + + ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)gizmo_clipping_plane.get_data()); + ::glEnable(GL_CLIP_PLANE0); } + else + ::glDisable(GL_CLIP_PLANE0); } @@ -4774,10 +4772,10 @@ void GLCanvas3D::_render_objects() const { if (m_use_clipping_planes) { - ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_clipping_planes[0].get_data()); - ::glEnable(GL_CLIP_PLANE0); - ::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[1].get_data()); + ::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[0].get_data()); ::glEnable(GL_CLIP_PLANE1); + ::glClipPlane(GL_CLIP_PLANE2, (GLdouble*)m_clipping_planes[1].get_data()); + ::glEnable(GL_CLIP_PLANE2); } // do not cull backfaces to show broken geometry, if any @@ -4788,8 +4786,8 @@ void GLCanvas3D::_render_objects() const if (m_use_clipping_planes) { - ::glDisable(GL_CLIP_PLANE0); ::glDisable(GL_CLIP_PLANE1); + ::glDisable(GL_CLIP_PLANE2); } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 44c43a1e3..b09f3746f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -448,7 +448,7 @@ private: void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false); - std::pair get_sla_clipping_plane() const; + ClippingPlane get_sla_clipping_plane() const; void render_current_gizmo(const Selection& selection) const; void render_current_gizmo_for_picking_pass(const Selection& selection) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 07eec27e2..65be22b44 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -612,47 +612,42 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const -std::pair GLGizmoSlaSupports::get_sla_clipping_plane() const +GLCanvas3D::ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const { if (!m_model_object) - return std::make_pair(0.f, 0.f);; + throw std::invalid_argument("GLGizmoSlaSupports::get_sla_clipping_plane() has no model object pointer."); Eigen::Matrix modelview_matrix; ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); - // clipping space origin transformed to world coords: - Vec3d clipping_origin = (modelview_matrix.inverse() * Eigen::Matrix{0, 0, 0, 1}).block<3,1>(0,0); - // we'll recover current look direction from the modelview matrix (in world coords): Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); - float dist = direction_to_camera.dot(clipping_origin) - direction_to_camera.dot(m_active_instance_bb.center()); + float dist = direction_to_camera.dot(m_active_instance_bb.center()); - return std::make_pair((dist - m_active_instance_bb.radius()) + m_clipping_plane_distance * 2*m_active_instance_bb.radius(), dist + 5.f*m_active_instance_bb.radius()); + return GLCanvas3D::ClippingPlane(-direction_to_camera.normalized(),(dist - (-m_active_instance_bb.radius()) - m_clipping_plane_distance * 2*m_active_instance_bb.radius())); } /* -void GLGizmoSlaSupports::find_intersections(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& idxs) const +void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& idxs) const { if (aabb->is_leaf()) { // this is a facet // corner.dot(normal) - offset - unsigned int facet_idx = aabb->m_primitive; - - Vec3f a = m_V.row(m_F(facet_idx, 0)); - Vec3f b = m_V.row(m_F(facet_idx, 1)); - Vec3f c = m_V.row(m_F(facet_idx, 2)); + idxs.push_back(aabb->m_primitive); } else { // not a leaf using CornerType = Eigen::AlignedBox::CornerType; bool sign = std::signbit(offset - normal.dot(aabb->m_box.corner(CornerType(0)))); for (unsigned int i=1; i<8; ++i) if (std::signbit(offset - normal.dot(aabb->m_box.corner(CornerType(i)))) != sign) { - find_intersections(aabb->m_left, normal, offset, idxs); - find_intersections(aabb->m_right, normal, offset, idxs); + find_intersecting_facets(aabb->m_left, normal, offset, idxs); + find_intersecting_facets(aabb->m_right, normal, offset, idxs); } } } + + void GLGizmoSlaSupports::make_line_segments() const { TriangleMeshSlicer tms(&m_model_object->volumes.front()->mesh); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 872e3541f..f61709d61 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -52,7 +52,7 @@ public: void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down); void delete_selected_points(bool force = false); - std::pair get_sla_clipping_plane() const; + GLCanvas3D::ClippingPlane get_sla_clipping_plane() const; private: bool on_init(); @@ -87,6 +87,7 @@ private: std::vector get_config_options(const std::vector& keys) const; bool is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera, float z_shift) const; + void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const; // Methods that do the model_object and editing cache synchronization, // editing mode selection, etc: From 9b7857aaab416a18cf67d273e46308e21d3295a2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 25 Mar 2019 12:01:02 +0100 Subject: [PATCH 04/11] SLA gizmo clipping plane logic moved to fragment shader This means the clipping now works again with both legacy and modern OpenGL --- resources/shaders/gouraud.fs | 10 +++- resources/shaders/gouraud.vs | 4 +- src/slic3r/GUI/3DScene.cpp | 4 ++ src/slic3r/GUI/3DScene.hpp | 4 ++ src/slic3r/GUI/GLCanvas3D.cpp | 49 ++++++++------------ src/slic3r/GUI/GLCanvas3D.hpp | 6 +-- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 +- 7 files changed, 41 insertions(+), 38 deletions(-) diff --git a/resources/shaders/gouraud.fs b/resources/shaders/gouraud.fs index f6116eec7..5b4ef0480 100644 --- a/resources/shaders/gouraud.fs +++ b/resources/shaders/gouraud.fs @@ -8,16 +8,22 @@ varying vec2 intensity; varying vec3 delta_box_min; varying vec3 delta_box_max; -varying float world_z; +varying vec3 world_pos; uniform vec4 uniform_color; // x = min z, y = max z; uniform vec2 z_range; +// clipping plane (general orientation): +uniform vec4 clipping_plane; + void main() { - if ((world_z < z_range.x) || (z_range.y < world_z)) + if ((world_pos.z < z_range.x) || (z_range.y < world_pos.z)) + discard; + + if (world_pos.x*clipping_plane.x + world_pos.y*clipping_plane.y + world_pos.z*clipping_plane.z + clipping_plane.w < 0.f ) discard; // if the fragment is outside the print volume -> use darker color diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs index 84ae51391..a226cf312 100644 --- a/resources/shaders/gouraud.vs +++ b/resources/shaders/gouraud.vs @@ -34,7 +34,7 @@ varying vec2 intensity; varying vec3 delta_box_min; varying vec3 delta_box_max; -varying float world_z; +varying vec3 world_pos; void main() { @@ -69,5 +69,5 @@ void main() } gl_Position = ftransform(); - world_z = vec3(print_box.volume_world_matrix * gl_Vertex).z; + world_pos = vec3(print_box.volume_world_matrix * gl_Vertex); } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 7ba61bdb6..57c227e07 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -785,6 +785,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; GLint z_range_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "z_range") : -1; + GLint clipping_plane_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "clipping_plane") : -1; GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1; GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1; GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; @@ -799,6 +800,9 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool if (z_range_id != -1) glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range)); + if (clipping_plane_id != -1) + glsafe(::glUniform4fv(clipping_plane_id, 1, (const GLfloat*)clipping_plane)); + GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func); for (GLVolumeWithZ& volume : to_render) { volume.first->set_render_color(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 5cc301a39..0ed8d2e6b 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -427,6 +427,9 @@ private: // z range for clipping in shaders float z_range[2]; + // plane coeffs for clipping in shaders + float clipping_plane[4]; + public: GLVolumePtrs volumes; @@ -485,6 +488,7 @@ public: } void set_z_range(float min_z, float max_z) { z_range[0] = min_z; z_range[1] = max_z; } + void set_clipping_plane(const double* coeffs) { clipping_plane[0] = coeffs[0]; clipping_plane[1] = coeffs[1]; clipping_plane[2] = coeffs[2]; clipping_plane[3] = coeffs[3]; } // 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 diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3e4824cde..4214abc60 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1321,14 +1321,14 @@ bool GLCanvas3D::Gizmos::gizmo_event(SLAGizmoEventType action, const Vec2d& mous GLCanvas3D::ClippingPlane GLCanvas3D::Gizmos::get_sla_clipping_plane() const { - if (!m_enabled) - return ClippingPlane(); + if (!m_enabled || m_current != SlaSupports) + return ClippingPlane::ClipsNothing(); GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) return reinterpret_cast(it->second)->get_sla_clipping_plane(); - return ClippingPlane(); + return ClippingPlane::ClipsNothing(); } @@ -4602,7 +4602,12 @@ void GLCanvas3D::_picking_pass() const ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); + ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data()); + ::glEnable(GL_CLIP_PLANE0); _render_volumes(true); + ::glDisable(GL_CLIP_PLANE0); + m_gizmos.render_current_gizmo_for_picking_pass(m_selection); if (m_multisample_allowed) @@ -4700,25 +4705,6 @@ void GLCanvas3D::set_ortho_projection(float w, float h, float near, float far) c } -void GLCanvas3D::set_sla_clipping(bool enable) const -{ - if (m_gizmos.get_current_type() != Gizmos::SlaSupports) - return; - - if (enable) { - ClippingPlane gizmo_clipping_plane; - try { - gizmo_clipping_plane = m_gizmos.get_sla_clipping_plane(); - } - catch (...) { return; } - - ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)gizmo_clipping_plane.get_data()); - ::glEnable(GL_CLIP_PLANE0); - } - else - ::glDisable(GL_CLIP_PLANE0); -} - void GLCanvas3D::_render_objects() const { @@ -4728,7 +4714,7 @@ void GLCanvas3D::_render_objects() const ::glEnable(GL_LIGHTING); ::glEnable(GL_DEPTH_TEST); - set_sla_clipping(true); + m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); if (m_use_VBOs) { @@ -4750,6 +4736,8 @@ void GLCanvas3D::_render_objects() const else m_volumes.set_z_range(-FLT_MAX, FLT_MAX); + m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); + m_shader.start_using(); if (m_picking_enabled && m_layers_editing.is_enabled() && m_layers_editing.last_object_id != -1) { int object_id = m_layers_editing.last_object_id; @@ -4770,6 +4758,9 @@ void GLCanvas3D::_render_objects() const } else { + ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data()); + ::glEnable(GL_CLIP_PLANE0); + if (m_use_clipping_planes) { ::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[0].get_data()); @@ -4777,6 +4768,7 @@ void GLCanvas3D::_render_objects() const ::glClipPlane(GL_CLIP_PLANE2, (GLdouble*)m_clipping_planes[1].get_data()); ::glEnable(GL_CLIP_PLANE2); } + // do not cull backfaces to show broken geometry, if any m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, [this](const GLVolume& volume) { @@ -4784,15 +4776,16 @@ void GLCanvas3D::_render_objects() const }); m_volumes.render_legacy(GLVolumeCollection::Transparent, false); + ::glDisable(GL_CLIP_PLANE0); + if (m_use_clipping_planes) { ::glDisable(GL_CLIP_PLANE1); ::glDisable(GL_CLIP_PLANE2); } } - - set_sla_clipping(false); - + + m_camera_clipping_plane = ClippingPlane::ClipsNothing(); ::glDisable(GL_LIGHTING); } @@ -4835,8 +4828,6 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const if (!fake_colors) ::glEnable(GL_LIGHTING); - set_sla_clipping(true); - // do not cull backfaces to show broken geometry, if any ::glDisable(GL_CULL_FACE); @@ -4875,8 +4866,6 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glEnable(GL_CULL_FACE); - set_sla_clipping(false); - if (!fake_colors) ::glDisable(GL_LIGHTING); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b09f3746f..0245b8d6c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -354,6 +354,8 @@ public: m_data[3] = offset; } + static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); } + const double* get_data() const { return m_data; } }; @@ -561,6 +563,7 @@ private: mutable Gizmos m_gizmos; mutable GLToolbar m_toolbar; ClippingPlane m_clipping_planes[2]; + mutable ClippingPlane m_camera_clipping_plane; bool m_use_clipping_planes; mutable SlaCap m_sla_caps[2]; std::string m_sidebar_field; @@ -778,9 +781,6 @@ private: // Sets current projection matrix to ortho, accounting for current camera zoom. void set_ortho_projection(float w, float h, float near, float far) const; - // Set/unset near clipping plane according to SLA gizmo requirements. - void set_sla_clipping(bool enable) const; - void _start_timer(); void _stop_timer(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 65be22b44..dce7f64dc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -615,7 +615,7 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const GLCanvas3D::ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const { if (!m_model_object) - throw std::invalid_argument("GLGizmoSlaSupports::get_sla_clipping_plane() has no model object pointer."); + return GLCanvas3D::ClippingPlane::ClipsNothing(); Eigen::Matrix modelview_matrix; ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); From bbda1896f9d4f78146af57d8587d4533db00716c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Mar 2019 07:44:02 +0100 Subject: [PATCH 05/11] The gizmo is now able to triangulate and show the cut, the triangulated cut is cached --- src/libslic3r/TriangleMesh.cpp | 53 ++++++++++-- src/libslic3r/TriangleMesh.hpp | 5 ++ src/slic3r/GUI/GLCanvas3D.cpp | 38 +++++---- src/slic3r/GUI/GLCanvas3D.hpp | 3 - src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 90 ++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 9 +- 6 files changed, 150 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index bfba364af..44c638a40 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -693,6 +693,16 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type } } + + +void TriangleMeshSlicer::set_up_direction(const Vec3f& up) +{ + m_quaternion.setFromTwoVectors(up, Vec3f::UnitZ()); + m_use_quaternion = true; +} + + + void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; @@ -795,7 +805,14 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const { - const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; + const stl_facet &facet_orig = this->mesh->stl.facet_start[facet_idx]; + stl_facet facet = facet_orig; + + if (m_use_quaternion) { + facet.vertex[0] = m_quaternion * facet.vertex[0]; + facet.vertex[1] = m_quaternion * facet.vertex[1]; + facet.vertex[2] = m_quaternion * facet.vertex[2]; + } // find facet extents const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2))); @@ -860,26 +877,42 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( IntersectionPoint points[3]; size_t num_points = 0; size_t point_on_layer = size_t(-1); - + // Reorder vertices so that the first one is the one with lowest Z. // This is needed to get all intersection lines in a consistent order // (external on the right of the line) const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0); + + // These are used only if the cut plane is inclined: + stl_vertex rotated_a; + stl_vertex rotated_b; + for (int j = i; j - i < 3; ++j) { // loop through facet edges int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)]; int a_id = vertices[j % 3]; int b_id = vertices[(j+1) % 3]; - const stl_vertex *a = &this->v_scaled_shared[a_id]; - const stl_vertex *b = &this->v_scaled_shared[b_id]; + + const stl_vertex *a; + const stl_vertex *b; + if (m_use_quaternion) { + rotated_a = m_quaternion * this->v_scaled_shared[a_id]; + rotated_b = m_quaternion * this->v_scaled_shared[b_id]; + a = &rotated_a; + b = &rotated_b; + } + else { + a = &this->v_scaled_shared[a_id]; + b = &this->v_scaled_shared[b_id]; + } // Is edge or face aligned with the cutting plane? if (a->z() == slice_z && b->z() == slice_z) { // Edge is horizontal and belongs to the current layer. - const stl_vertex &v0 = this->v_scaled_shared[vertices[0]]; - const stl_vertex &v1 = this->v_scaled_shared[vertices[1]]; - const stl_vertex &v2 = this->v_scaled_shared[vertices[2]]; - const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal; + const stl_vertex &v0 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[0]]) : this->v_scaled_shared[vertices[0]]; + const stl_vertex &v1 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[1]]) : this->v_scaled_shared[vertices[1]]; + const stl_vertex &v2 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[2]]) : this->v_scaled_shared[vertices[2]]; + const stl_normal &normal = m_use_quaternion ? stl_vertex(m_quaternion * this->mesh->stl.facet_start[facet_idx].normal) : this->mesh->stl.facet_start[facet_idx].normal; // We may ignore this edge for slicing purposes, but we may still use it for object cutting. FacetSliceType result = Slicing; if (min_z == max_z) { @@ -995,7 +1028,9 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( if (i == line_out->a_id || i == line_out->b_id) i = vertices[2]; assert(i != line_out->a_id && i != line_out->b_id); - line_out->edge_type = (this->v_scaled_shared[i].z() < slice_z) ? feTop : feBottom; + line_out->edge_type = ((m_use_quaternion ? + m_quaternion * this->v_scaled_shared[i].z() + : this->v_scaled_shared[i].z()) < slice_z) ? feTop : feBottom; } #endif return Slicing; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index d389500c6..a0020255d 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -171,6 +171,7 @@ public: FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx, const float min_z, const float max_z, IntersectionLine *line_out) const; void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; + void set_up_direction(const Vec3f& up); private: const TriangleMesh *mesh; @@ -178,6 +179,10 @@ private: std::vector facets_edges; // Scaled copy of this->mesh->stl.v_shared std::vector v_scaled_shared; + // Quaternion that will be used to rotate every facet before the slicing + Eigen::Quaternion m_quaternion; + // Whether or not the above quaterion should be used + bool m_use_quaternion = false; void _slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; void make_loops(std::vector &lines, Polygons* loops) const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4214abc60..717ebcdda 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4432,13 +4432,29 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) _set_current(); ::glViewport(0, 0, w, h); + ::glMatrixMode(GL_PROJECTION); + ::glLoadIdentity(); + + const BoundingBoxf3& bbox = _max_bounding_box(); + switch (m_camera.type) { case Camera::Ortho: { + float w2 = w; + float h2 = h; + float two_zoom = 2.0f * get_camera_zoom(); + if (two_zoom != 0.0f) + { + float inv_two_zoom = 1.0f / two_zoom; + w2 *= inv_two_zoom; + h2 *= inv_two_zoom; + } + // FIXME: calculate a tighter value for depth will improve z-fighting - float depth = 5.0f * (float)(_max_bounding_box().max_size()); - set_ortho_projection(w, h, -depth, depth); + float depth = 5.0f * (float)bbox.max_size(); + ::glOrtho(-w2, w2, -h2, h2, -depth, depth); + break; } // case Camera::Perspective: @@ -4470,6 +4486,8 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) } } + ::glMatrixMode(GL_MODELVIEW); + m_dirty = false; } @@ -4689,22 +4707,6 @@ void GLCanvas3D::_render_axes() const } -void GLCanvas3D::set_ortho_projection(float w, float h, float near, float far) const -{ - float two_zoom = 2.0f * get_camera_zoom(); - if (two_zoom != 0.0f) - { - float inv_two_zoom = 1.0f / two_zoom; - w *= inv_two_zoom; - h *= inv_two_zoom; - } - ::glMatrixMode(GL_PROJECTION); - ::glLoadIdentity(); - ::glOrtho(-w, w, -h, h, near, far); - ::glMatrixMode(GL_MODELVIEW); -} - - void GLCanvas3D::_render_objects() const { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 0245b8d6c..656b6e229 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -778,9 +778,6 @@ private: // Returns the view ray line, in world coordinate, at the given mouse position. Linef3 mouse_ray(const Point& mouse_pos); - // Sets current projection matrix to ortho, accounting for current camera zoom. - void set_ortho_projection(float w, float h, float near, float far) const; - void _start_timer(); void _stop_timer(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index dce7f64dc..78719c776 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -9,6 +9,7 @@ #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/Tesselate.hpp" namespace Slic3r { @@ -91,12 +92,71 @@ void GLGizmoSlaSupports::on_render(const Selection& selection) const ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); - render_points(selection, false); + // we'll recover current look direction from the modelview matrix (in world coords): + Eigen::Matrix modelview_matrix; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); + Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); + + if (m_quadric != nullptr && selection.is_from_single_instance()) + render_points(selection, direction_to_camera, false); + render_selection_rectangle(); + render_clipping_plane(selection, direction_to_camera); ::glDisable(GL_BLEND); } + + +void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const Vec3d& direction_to_camera) const +{ + if (m_clipping_plane_distance == 0.f) + return; + + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + double z_shift = vol->get_sla_shift_z(); + Transform3f instance_matrix = vol->get_instance_transformation().get_matrix().cast(); + Transform3f instance_matrix_no_translation_no_scaling = vol->get_instance_transformation().get_matrix(true,false,true).cast(); + Transform3f instance_matrix_no_translation = vol->get_instance_transformation().get_matrix(true).cast(); + Vec3f scaling = vol->get_instance_scaling_factor().cast(); + + Vec3f up = instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera.cast().normalized(); + up = Vec3f(up(0)*scaling(0), up(1)*scaling(1), up(2)*scaling(2)); + float height = m_active_instance_bb.radius() - m_clipping_plane_distance * 2*m_active_instance_bb.radius(); + float height_mesh = height; + + if (m_clipping_plane_distance != m_old_clipping_plane_distance + || m_old_direction_to_camera != direction_to_camera) { + + std::vector list_of_expolys; + m_tms->set_up_direction(up); + m_tms->slice(std::vector{height_mesh}, 0.f, &list_of_expolys, [](){}); + m_triangles = triangulate_expolygons_2f(list_of_expolys[0]); + + m_old_direction_to_camera = direction_to_camera; + m_old_clipping_plane_distance = m_clipping_plane_distance; + } + + ::glPushMatrix(); + ::glTranslated(0.0, 0.0, z_shift); + ::glMultMatrixf(instance_matrix.data()); + Eigen::Quaternionf q; + q.setFromTwoVectors(Vec3f::UnitZ(), up); + Eigen::AngleAxisf aa(q); + ::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)); + ::glTranslatef(0.f, 0.f, -0.001f); // to make sure the cut is safely beyond the near clipping plane + + ::glBegin(GL_TRIANGLES); + ::glColor3f(1.0f, 0.37f, 0.0f); + for (const Vec2f& point : m_triangles) + ::glVertex3f(point(0), point(1), height_mesh); + ::glEnd(); + + ::glPopMatrix(); +} + + + void GLGizmoSlaSupports::render_selection_rectangle() const { if (!m_selection_rectangle_active) @@ -141,14 +201,16 @@ void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const { ::glEnable(GL_DEPTH_TEST); - render_points(selection, true); + // we'll recover current look direction from the modelview matrix (in world coords): + Eigen::Matrix modelview_matrix; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); + Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); + + render_points(selection, direction_to_camera, true); } -void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const +void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d& direction_to_camera, bool picking) const { - if (m_quadric == nullptr || !selection.is_from_single_instance()) - return; - if (!picking) ::glEnable(GL_LIGHTING); @@ -157,11 +219,6 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); - // we'll recover current look direction from the modelview matrix (in world coords): - Eigen::Matrix modelview_matrix; - ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); - Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); - ::glPushMatrix(); ::glTranslated(0.0, 0.0, z_shift); ::glMultMatrixd(instance_matrix.data()); @@ -263,9 +320,6 @@ bool GLGizmoSlaSupports::is_mesh_update_necessary() const { return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) && ((m_model_object != m_old_model_object) || m_V.size()==0); - - //if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix)) - // return false; } void GLGizmoSlaSupports::update_mesh() @@ -273,10 +327,9 @@ void GLGizmoSlaSupports::update_mesh() wxBusyCursor wait; Eigen::MatrixXf& V = m_V; Eigen::MatrixXi& F = m_F; - // Composite mesh of all instances in the world coordinate system. // This mesh does not account for the possible Z up SLA offset. - TriangleMesh mesh = m_model_object->raw_mesh(); - const stl_file& stl = mesh.stl; + m_mesh = m_model_object->raw_mesh(); + const stl_file& stl = m_mesh.stl; V.resize(3 * stl.stats.number_of_facets, 3); F.resize(stl.stats.number_of_facets, 3); for (unsigned int i=0; i(); m_AABB.init(m_V, m_F); + + m_tms.reset(new TriangleMeshSlicer); + m_tms->init(&m_mesh, [](){}); } // Unprojects the mouse position on the mesh and return the hit point and normal of the facet. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index f61709d61..3a5e903c1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -31,6 +31,8 @@ private: Eigen::MatrixXf m_V; // vertices Eigen::MatrixXi m_F; // facets indices igl::AABB m_AABB; + TriangleMesh m_mesh; + mutable std::vector m_triangles; class CacheEntry { public: @@ -61,7 +63,8 @@ private: virtual void on_render_for_picking(const Selection& selection) const; void render_selection_rectangle() const; - void render_points(const Selection& selection, bool picking = false) const; + void render_points(const Selection& selection, const Vec3d& direction_to_camera, bool picking = false) const; + void render_clipping_plane(const Selection& selection, const Vec3d& direction_to_camera) const; bool is_mesh_update_necessary() const; void update_mesh(); void update_cache_entry_normal(unsigned int i) const; @@ -74,6 +77,8 @@ private: float m_density = 100.f; mutable std::vector m_editing_mode_cache; // a support point and whether it is currently selected float m_clipping_plane_distance = 0.f; + mutable float m_old_clipping_plane_distance = 0.f; + mutable Vec3d m_old_direction_to_camera; bool m_selection_rectangle_active = false; Vec2d m_selection_rectangle_start_corner; @@ -85,6 +90,8 @@ private: int m_canvas_width; int m_canvas_height; + mutable std::unique_ptr m_tms; + std::vector get_config_options(const std::vector& keys) const; bool is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera, float z_shift) const; void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const; From 7531f2d5e769ea1dae162559b769ab8fcd01e047 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 3 Apr 2019 14:23:04 +0200 Subject: [PATCH 06/11] Selection rectangle now respects the clipping plane position --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 41 +++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 78719c776..f727572cb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -490,11 +490,14 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // bounding box created from the rectangle corners - will take care of order of the corners BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast()), Point(m_selection_rectangle_end_corner.cast())}); - const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true); + const Transform3d& instance_matrix_no_translation_no_scaling = volume->get_instance_transformation().get_matrix(true,false,true); + // we'll recover current look direction from the modelview matrix (in world coords)... Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]); // ...and transform it to model coords. - direction_to_camera = (instance_matrix_no_translation.inverse().cast() * direction_to_camera).normalized().eval(); + Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast() * direction_to_camera).normalized().eval(); + Vec3f scaling = volume->get_instance_scaling_factor().cast(); + direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2)); // 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(), direction_to_camera.cast(), z_offset)) { bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: std::vector hits; // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. - if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits)) + if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) { + std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); + if (hits.front().t < 0.001f) + hits.erase(hits.begin()); + + if (m_clipping_plane_distance != 0.f) { + // If the closest hit facet normal points in the same direction as the ray, + // we are looking through the mesh and should therefore discard the point: + int fid = hits.front().id; // facet id + 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))); + if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f) + is_obscured = true; + + // Eradicate all hits that are on clipped surfaces: + for (unsigned int j=0; j(), direction_to_camera.cast(), z_offset)) { + hits.erase(hits.begin()+j); + --j; + } + } + } + // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction. // Also, the threshold is in mesh coordinates, not in actual dimensions. - if (hits.size() > 1 || hits.front().t > 0.001f) + if (!hits.empty()) is_obscured = true; + } if (!is_obscured) select_point(i); From 09cf1b9b0070644bf48eb6ef06bfa1f3f59b3995 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 5 Apr 2019 16:38:54 +0200 Subject: [PATCH 07/11] Allowed general object transformation and SLA z-shift (clipping plane) --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 47 +++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 5 ++- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index f727572cb..82b08a5e5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -59,7 +59,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S { // Cache the bb - it's needed for dealing with the clipping plane quite often // It could be done inside update_mesh but one has to account for scaling of the instance. - m_active_instance_bb = m_model_object->instance_bounding_box(m_active_instance); + m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); if (is_mesh_update_necessary()) { update_mesh(); @@ -96,6 +96,7 @@ void GLGizmoSlaSupports::on_render(const Selection& selection) const Eigen::Matrix modelview_matrix; ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); if (m_quadric != nullptr && selection.is_from_single_instance()) render_points(selection, direction_to_camera, false); @@ -114,16 +115,13 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const return; const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); - double z_shift = vol->get_sla_shift_z(); Transform3f instance_matrix = vol->get_instance_transformation().get_matrix().cast(); Transform3f instance_matrix_no_translation_no_scaling = vol->get_instance_transformation().get_matrix(true,false,true).cast(); - Transform3f instance_matrix_no_translation = vol->get_instance_transformation().get_matrix(true).cast(); Vec3f scaling = vol->get_instance_scaling_factor().cast(); - Vec3f up = instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera.cast().normalized(); - up = Vec3f(up(0)*scaling(0), up(1)*scaling(1), up(2)*scaling(2)); - float height = m_active_instance_bb.radius() - m_clipping_plane_distance * 2*m_active_instance_bb.radius(); - float height_mesh = height; + Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera.cast(); + Vec3f up = Vec3f(up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2)); + float height_mesh = (m_active_instance_bb_radius - m_clipping_plane_distance * 2*m_active_instance_bb_radius) * (up_noscale.norm()/up.norm()); if (m_clipping_plane_distance != m_old_clipping_plane_distance || m_old_direction_to_camera != direction_to_camera) { @@ -138,7 +136,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const } ::glPushMatrix(); - ::glTranslated(0.0, 0.0, z_shift); + ::glTranslated(0.0, 0.0, m_z_shift); ::glMultMatrixf(instance_matrix.data()); Eigen::Quaternionf q; q.setFromTwoVectors(Vec3f::UnitZ(), up); @@ -215,12 +213,11 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d& ::glEnable(GL_LIGHTING); const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); - double z_shift = vol->get_sla_shift_z(); const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); ::glPushMatrix(); - ::glTranslated(0.0, 0.0, z_shift); + ::glTranslated(0.0, 0.0, m_z_shift); ::glMultMatrixd(instance_matrix.data()); float render_color[3]; @@ -229,7 +226,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d& const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point; const bool& point_selected = m_editing_mode_cache[i].selected; - if (is_point_clipped(support_point.pos.cast(), direction_to_camera, z_shift)) + if (is_point_clipped(support_point.pos.cast(), direction_to_camera)) continue; // First decide about the color of the point. @@ -303,15 +300,15 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d& -bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera, float z_shift) const +bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera) const { if (m_clipping_plane_distance == 0.f) return false; Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point; - transformed_point(2) += z_shift; - return direction_to_camera.dot(m_active_instance_bb.center()) + m_active_instance_bb.radius() - - m_clipping_plane_distance * 2*m_active_instance_bb.radius() < direction_to_camera.dot(transformed_point); + transformed_point(2) += m_z_shift; + return direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift)) + m_active_instance_bb_radius + - m_clipping_plane_distance * 2*m_active_instance_bb_radius < direction_to_camera.dot(transformed_point); } @@ -373,13 +370,12 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse const Selection& selection = m_parent.get_selection(); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - double z_offset = volume->get_sla_shift_z(); // we'll recover current look direction from the modelview matrix (in world coords): Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); - point1(2) -= z_offset; - point2(2) -= z_offset; + point1(2) -= m_z_shift; + point2(2) -= m_z_shift; Transform3d inv = volume->get_instance_transformation().get_matrix().inverse(); @@ -404,7 +400,7 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0))); b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0))); result = 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)); - if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast(), direction_to_camera, z_offset)) + if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast(), direction_to_camera)) break; } @@ -485,7 +481,6 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const Selection& selection = m_parent.get_selection(); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - double z_offset = volume->get_sla_shift_z(); // bounding box created from the rectangle corners - will take care of order of the corners BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast()), Point(m_selection_rectangle_end_corner.cast())}); @@ -503,20 +498,18 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous for (unsigned int i=0; i() * support_point.pos; - pos(2) += z_offset; + pos(2) += m_z_shift; GLdouble out_x, out_y, out_z; ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); out_y = m_canvas_height - out_y; - if (rectangle.contains(Point(out_x, out_y)) && !is_point_clipped(support_point.pos.cast(), direction_to_camera.cast(), z_offset)) { + if (rectangle.contains(Point(out_x, out_y)) && !is_point_clipped(support_point.pos.cast(), direction_to_camera.cast())) { bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: std::vector hits; // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) { std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); - if (hits.front().t < 0.001f) - hits.erase(hits.begin()); if (m_clipping_plane_distance != 0.f) { // If the closest hit facet normal points in the same direction as the ray, @@ -534,7 +527,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit Vec3f hit_pos = 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)); - if (is_point_clipped(hit_pos.cast(), direction_to_camera.cast(), z_offset)) { + if (is_point_clipped(hit_pos.cast(), direction_to_camera.cast())) { hits.erase(hits.begin()+j); --j; } @@ -709,9 +702,9 @@ GLCanvas3D::ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const // we'll recover current look direction from the modelview matrix (in world coords): Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); - float dist = direction_to_camera.dot(m_active_instance_bb.center()); + float dist = direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift)); - return GLCanvas3D::ClippingPlane(-direction_to_camera.normalized(),(dist - (-m_active_instance_bb.radius()) - m_clipping_plane_distance * 2*m_active_instance_bb.radius())); + return GLCanvas3D::ClippingPlane(-direction_to_camera.normalized(),(dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius)); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 3a5e903c1..d6df55f93 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -22,7 +22,8 @@ private: ModelObject* m_model_object = nullptr; ModelObject* m_old_model_object = nullptr; int m_active_instance = -1; - BoundingBoxf3 m_active_instance_bb; // to cache the bb + float m_active_instance_bb_radius; // to cache the bb + mutable float m_z_shift = 0.f; std::pair unproject_on_mesh(const Vec2d& mouse_pos); const float RenderPointScale = 1.f; @@ -93,7 +94,7 @@ private: mutable std::unique_ptr m_tms; std::vector get_config_options(const std::vector& keys) const; - bool is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera, float z_shift) const; + bool is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera) const; void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const; // Methods that do the model_object and editing cache synchronization, From 3a09f66e513c363198d34714747a6e799ee9a318 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 8 Apr 2019 16:05:18 +0200 Subject: [PATCH 08/11] Fixed a literal in vertex shader that refused to compile on OSX --- resources/shaders/gouraud.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/shaders/gouraud.fs b/resources/shaders/gouraud.fs index 5b4ef0480..b2bc915ab 100644 --- a/resources/shaders/gouraud.fs +++ b/resources/shaders/gouraud.fs @@ -23,7 +23,7 @@ void main() if ((world_pos.z < z_range.x) || (z_range.y < world_pos.z)) discard; - if (world_pos.x*clipping_plane.x + world_pos.y*clipping_plane.y + world_pos.z*clipping_plane.z + clipping_plane.w < 0.f ) + if (world_pos.x*clipping_plane.x + world_pos.y*clipping_plane.y + world_pos.z*clipping_plane.z + clipping_plane.w < 0.0 ) discard; // if the fragment is outside the print volume -> use darker color From 4a210aeecf3a720a12d3e249c6e2da5cea533085 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 11 Apr 2019 15:44:32 +0200 Subject: [PATCH 09/11] Vojtech's improvements in the SLA preview cutting dialog. --- src/admesh/stl.h | 14 +++++- src/admesh/stlinit.cpp | 37 ++++++++------- src/libslic3r/Model.cpp | 20 ++++---- src/libslic3r/TriangleMesh.cpp | 16 ++----- src/libslic3r/TriangleMesh.hpp | 5 +- src/slic3r/GUI/GLCanvas3D.cpp | 11 +++-- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 48 ++++++++++++-------- 7 files changed, 86 insertions(+), 65 deletions(-) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 2c436b426..d682b2434 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -43,11 +43,21 @@ typedef Eigen::Matrix stl_normal; static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); -typedef struct { +struct stl_facet { stl_normal normal; stl_vertex vertex[3]; char extra[2]; -} stl_facet; + + stl_facet rotated(const Eigen::Quaternion &rot) { + stl_facet out; + out.normal = rot * this->normal; + out.vertex[0] = rot * this->vertex[0]; + out.vertex[1] = rot * this->vertex[1]; + out.vertex[2] = rot * this->vertex[2]; + return out; + } +}; + #define SIZEOF_STL_FACET 50 static_assert(offsetof(stl_facet, normal) == 0, "stl_facet.normal has correct offset"); diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index e2939b8af..911f4f5e8 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -41,10 +41,12 @@ stl_open(stl_file *stl, const char *file) { stl_count_facets(stl, file); stl_allocate(stl); stl_read(stl, 0, true); - if (!stl->error) fclose(stl->fp); + if (stl->fp != nullptr) { + fclose(stl->fp); + stl->fp = nullptr; + } } - void stl_initialize(stl_file *stl) { memset(stl, 0, sizeof(stl_file)); @@ -118,7 +120,7 @@ stl_count_facets(stl_file *stl, const char *file) { } /* Read the int following the header. This should contain # of facets */ - bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp); + bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp) != 0; #ifndef BOOST_LITTLE_ENDIAN // Convert from little endian to big endian. stl_internal_reverse_quads((char*)&header_num_facets, 4); @@ -257,7 +259,6 @@ stl_reallocate(stl_file *stl) { time running this for the stl and therefore we should reset our max and min stats. */ void stl_read(stl_file *stl, int first_facet, bool first) { stl_facet facet; - int i; if (stl->error) return; @@ -268,7 +269,7 @@ void stl_read(stl_file *stl, int first_facet, bool first) { } char normal_buf[3][32]; - for(i = first_facet; i < stl->stats.number_of_facets; i++) { + for(uint32_t i = first_facet; i < stl->stats.number_of_facets; i++) { if(stl->stats.type == binary) /* Read a single facet from a binary .STL file */ { @@ -366,17 +367,19 @@ void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first) } } -void -stl_close(stl_file *stl) { - if (stl->error) return; +void stl_close(stl_file *stl) +{ + assert(stl->fp == nullptr); + assert(stl->heads == nullptr); + assert(stl->tail == nullptr); - if(stl->neighbors_start != NULL) - free(stl->neighbors_start); - if(stl->facet_start != NULL) - free(stl->facet_start); - if(stl->v_indices != NULL) - free(stl->v_indices); - if(stl->v_shared != NULL) - free(stl->v_shared); + if (stl->facet_start != NULL) + free(stl->facet_start); + if (stl->neighbors_start != NULL) + free(stl->neighbors_start); + if (stl->v_indices != NULL) + free(stl->v_indices); + if (stl->v_shared != NULL) + free(stl->v_shared); + memset(stl, 0, sizeof(stl_file)); } - diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index e634dd138..ac4cd73ef 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -897,13 +897,13 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const throw std::invalid_argument("Can't call raw_bounding_box() with no instances"); #endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT - TriangleMesh vol_mesh(v->mesh); #if ENABLE_GENERIC_SUBPARTS_PLACEMENT - vol_mesh.transform(inst_matrix * v->get_matrix()); - bb.merge(vol_mesh.bounding_box()); + bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix())); #else - vol_mesh.transform(v->get_matrix()); - bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true)); + // unmaintaned + assert(false); + // vol_mesh.transform(v->get_matrix()); + // bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true)); #endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT } return bb; @@ -920,13 +920,13 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ { if (v->is_model_part()) { - TriangleMesh mesh(v->mesh); #if ENABLE_GENERIC_SUBPARTS_PLACEMENT - mesh.transform(inst_matrix * v->get_matrix()); - bb.merge(mesh.bounding_box()); + bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix())); #else - mesh.transform(v->get_matrix()); - bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate)); + // not maintained + assert(false); + //mesh.transform(v->get_matrix()); + //bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate)); #endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT } } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 44c638a40..2d603661d 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -805,14 +805,7 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const { - const stl_facet &facet_orig = this->mesh->stl.facet_start[facet_idx]; - stl_facet facet = facet_orig; - - if (m_use_quaternion) { - facet.vertex[0] = m_quaternion * facet.vertex[0]; - facet.vertex[1] = m_quaternion * facet.vertex[1]; - facet.vertex[2] = m_quaternion * facet.vertex[2]; - } + const stl_facet &facet = m_use_quaternion ? this->mesh->stl.facet_start[facet_idx].rotated(m_quaternion) : this->mesh->stl.facet_start[facet_idx]; // find facet extents const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2))); @@ -884,7 +877,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0); - // These are used only if the cut plane is inclined: + // These are used only if the cut plane is tilted: stl_vertex rotated_a; stl_vertex rotated_b; @@ -909,10 +902,11 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( // Is edge or face aligned with the cutting plane? if (a->z() == slice_z && b->z() == slice_z) { // Edge is horizontal and belongs to the current layer. + // The following rotation of the three vertices may not be efficient, but this branch happens rarely. const stl_vertex &v0 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[0]]) : this->v_scaled_shared[vertices[0]]; const stl_vertex &v1 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[1]]) : this->v_scaled_shared[vertices[1]]; const stl_vertex &v2 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[2]]) : this->v_scaled_shared[vertices[2]]; - const stl_normal &normal = m_use_quaternion ? stl_vertex(m_quaternion * this->mesh->stl.facet_start[facet_idx].normal) : this->mesh->stl.facet_start[facet_idx].normal; + const stl_normal &normal = facet.normal; // We may ignore this edge for slicing purposes, but we may still use it for object cutting. FacetSliceType result = Slicing; if (min_z == max_z) { @@ -1029,7 +1023,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( i = vertices[2]; assert(i != line_out->a_id && i != line_out->b_id); line_out->edge_type = ((m_use_quaternion ? - m_quaternion * this->v_scaled_shared[i].z() + (m_quaternion * this->v_scaled_shared[i]).z() : this->v_scaled_shared[i].z()) < slice_z) ? feTop : feBottom; } #endif diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index a0020255d..4bf5ce039 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -25,9 +25,10 @@ public: TriangleMesh(const Pointf3s &points, const std::vector &facets); TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; } TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); } - ~TriangleMesh() { stl_close(&this->stl); } + ~TriangleMesh() { clear(); } TriangleMesh& operator=(const TriangleMesh &other); TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; } + void clear() { stl_close(&this->stl); this->repaired = false; } void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); } void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); } void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); } @@ -182,7 +183,7 @@ private: // Quaternion that will be used to rotate every facet before the slicing Eigen::Quaternion m_quaternion; // Whether or not the above quaterion should be used - bool m_use_quaternion = false; + bool m_use_quaternion = false; void _slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; void make_loops(std::vector &lines, Polygons* loops) const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 96a767c4e..d5da8ac1b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3494,10 +3494,13 @@ void GLCanvas3D::_picking_pass() const glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); - ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data()); - ::glEnable(GL_CLIP_PLANE0); + if (! m_use_VBOs) { + ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data()); + ::glEnable(GL_CLIP_PLANE0); + } _render_volumes(true); - ::glDisable(GL_CLIP_PLANE0); + if (! m_use_VBOs) + ::glDisable(GL_CLIP_PLANE0); m_gizmos.render_current_gizmo_for_picking_pass(m_selection); @@ -3661,7 +3664,7 @@ void GLCanvas3D::_render_objects() const } m_camera_clipping_plane = ClippingPlane::ClipsNothing(); - ::glDisable(GL_LIGHTING); + glsafe(::glDisable(GL_LIGHTING)); } void GLCanvas3D::_render_selection() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 21d1bb3a7..087ec1b01 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -58,6 +58,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S { // Cache the bb - it's needed for dealing with the clipping plane quite often // It could be done inside update_mesh but one has to account for scaling of the instance. + //FIXME calling ModelObject::instance_bounding_box() is expensive! m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); if (is_mesh_update_necessary()) { @@ -123,6 +124,11 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const || m_old_direction_to_camera != direction_to_camera) { std::vector list_of_expolys; + if (! m_tms) { + m_tms.reset(new TriangleMeshSlicer); + m_tms->init(const_cast(&m_mesh), [](){}); + } + m_tms->set_up_direction(up); m_tms->slice(std::vector{height_mesh}, 0.f, &list_of_expolys, [](){}); m_triangles = triangulate_expolygons_2f(list_of_expolys[0]); @@ -131,22 +137,23 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const m_old_clipping_plane_distance = m_clipping_plane_distance; } - ::glPushMatrix(); - ::glTranslated(0.0, 0.0, m_z_shift); - ::glMultMatrixf(instance_matrix.data()); - Eigen::Quaternionf q; - q.setFromTwoVectors(Vec3f::UnitZ(), up); - Eigen::AngleAxisf aa(q); - ::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)); - ::glTranslatef(0.f, 0.f, -0.001f); // to make sure the cut is safely beyond the near clipping plane - - ::glBegin(GL_TRIANGLES); - ::glColor3f(1.0f, 0.37f, 0.0f); - for (const Vec2f& point : m_triangles) - ::glVertex3f(point(0), point(1), height_mesh); - ::glEnd(); - - ::glPopMatrix(); + if (! m_triangles.empty()) { + ::glPushMatrix(); + ::glTranslated(0.0, 0.0, m_z_shift); + ::glMultMatrixf(instance_matrix.data()); + Eigen::Quaternionf q; + q.setFromTwoVectors(Vec3f::UnitZ(), up); + Eigen::AngleAxisf aa(q); + ::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)); + ::glTranslatef(0.f, 0.f, -0.001f); // to make sure the cut is safely beyond the near clipping plane + ::glColor3f(1.0f, 0.37f, 0.0f); + ::glBegin(GL_TRIANGLES); + ::glColor3f(1.0f, 0.37f, 0.0f); + for (const Vec2f& point : m_triangles) + ::glVertex3f(point(0), point(1), height_mesh); + ::glEnd(); + ::glPopMatrix(); + } } @@ -344,9 +351,6 @@ void GLGizmoSlaSupports::update_mesh() m_AABB = igl::AABB(); m_AABB.init(m_V, m_F); - - m_tms.reset(new TriangleMeshSlicer); - m_tms->init(&m_mesh, [](){}); } // Unprojects the mouse position on the mesh and return the hit point and normal of the facet. @@ -969,6 +973,12 @@ void GLGizmoSlaSupports::on_set_state() m_editing_mode = false; // so it is not active next time the gizmo opens m_editing_mode_cache.clear(); m_clipping_plane_distance = 0.f; + // Release copy of the mesh, triangle slicer and the AABB spatial search structure. + m_mesh.clear(); + m_AABB.deinit(); + m_V = Eigen::MatrixXf(); + m_F = Eigen::MatrixXi(); + m_tms.reset(nullptr); }); } m_old_state = m_state; From d8a3308f8a3f6f93780ae217280130605f3b3711 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 11 Apr 2019 15:45:14 +0200 Subject: [PATCH 10/11] Setting the SLA Pad wall height to zero and adding a warning to not set it to non zero, as it may be difficult to tear the object off the vat foil. --- src/libslic3r/PrintConfig.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a978a3175..66641a7f5 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2528,14 +2528,17 @@ void PrintConfigDef::init_sla_params() def = this->add("pad_wall_height", coFloat); def->label = L("Pad wall height"); - def->tooltip = L("Defines the cavity depth. Set to zero to disable the cavity."); + def->tooltip = L("Defines the pad cavity depth. Set to zero to disable the cavity. " + "Be careful when enabling this feature, as some resins may " + "produce an extreme suction effect inside the cavity, " + "which makes pealing the print off the vat foil difficult."); def->category = L("Pad"); // def->tooltip = L(""); def->sidetext = L("mm"); def->min = 0; def->max = 30; - def->mode = comSimple; - def->default_value = new ConfigOptionFloat(5.0); + def->mode = comExpert; + def->default_value = new ConfigOptionFloat(0.); def = this->add("pad_max_merge_distance", coFloat); def->label = L("Max merge distance"); From 59758fea550cd595e5343527201de3a49deacbc0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 11 Apr 2019 17:07:41 +0200 Subject: [PATCH 11/11] Material correction XY merged --- src/slic3r/GUI/Tab.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c735a40ee..30e3bfe89 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3224,7 +3224,8 @@ void TabSLAMaterial::build() optgroup = page->new_optgroup(_(L("Corrections"))); optgroup->label_width = 19 * m_em_unit;//190; std::vector corrections = {"material_correction"}; - std::vector axes{ "X", "Y", "Z" }; +// std::vector axes{ "X", "Y", "Z" }; + std::vector axes{ "XY", "Z" }; for (auto& opt_key : corrections) { auto line = Line{ m_config->def()->get(opt_key)->full_label, "" }; int id = 0;