From e2ccc6ec0c8add5da57a038df550cc27103cbdc3 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 14 May 2020 13:42:46 +0200
Subject: [PATCH 1/4] FDM supports gizmo: trying to increase performance during
 painting

---
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index b85fe57f0..0fea328bb 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -393,13 +393,13 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
             for (const std::pair<Vec3f, size_t>& hit_and_facet : hit_positions_and_facet_ids[mesh_id]) {
                 some_mesh_was_hit = true;
                 const TriangleMesh* mesh = &mv->mesh();
-                std::vector<NeighborData>& neighbors = m_neighbors[mesh_id];
+                const std::vector<NeighborData>& neighbors = m_neighbors[mesh_id];
 
                 // Calculate direction from camera to the hit (in mesh coords):
                 Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast<float>() - hit_and_facet.first).normalized();
 
                 // A lambda to calculate distance from the centerline:
-                auto squared_distance_from_line = [&hit_and_facet, &dir](const Vec3f point) -> float {
+                auto squared_distance_from_line = [&hit_and_facet, &dir](const Vec3f& point) -> float {
                     Vec3f diff = hit_and_facet.first - point;
                     return (diff - diff.dot(dir) * dir).squaredNorm();
                 };
@@ -461,17 +461,10 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
         {
             if (m_button_down == Button::None)
                 m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right);
-            // Force rendering. In case the user is dragging, the queue can be
-            // flooded by wxEVT_MOVING event and rendering would be skipped.
-            m_parent.render();
             return true;
         }
-        if (action == SLAGizmoEventType::Dragging && m_button_down != Button::None) {
-            // Same as above. We don't want the cursor to freeze when we
-            // leave the mesh while painting.
-            m_parent.render();
+        if (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)
             return true;
-        }
     }
 
     if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp)

From e312f3be4374f4684110b0897191f2c0339e9cc4 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 15 May 2020 14:16:56 +0200
Subject: [PATCH 2/4] FDM supports gizmo: caching triangle normals in order to
 increase performance

---
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp |  6 ++++--
 src/slic3r/GUI/MeshUtils.cpp                 | 15 +++++----------
 src/slic3r/GUI/MeshUtils.hpp                 |  9 +++++++--
 3 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 0fea328bb..6843bab46 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -506,8 +506,10 @@ void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv,
             if (status != type)
                 continue;
             for (int i=0; i<3; ++i)
-                iva.push_geometry(mesh->its.vertices[mesh->its.indices[facet_idx](i)].cast<double>(),
-                                  MeshRaycaster::get_triangle_normal(mesh->its, facet_idx).cast<double>());
+                iva.push_geometry(
+                    mesh->its.vertices[mesh->its.indices[facet_idx](i)].cast<double>(),
+                    m_c->raycaster()->raycasters()[mesh_id]->get_triangle_normal(facet_idx).cast<double>()
+                );
             iva.push_triangle(3*triangle_cnt, 3*triangle_cnt+1, 3*triangle_cnt+2);
             ++triangle_cnt;
         }
diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp
index 6c4f5e49d..18526d8ce 100644
--- a/src/slic3r/GUI/MeshUtils.cpp
+++ b/src/slic3r/GUI/MeshUtils.cpp
@@ -95,11 +95,9 @@ void MeshClipper::recalculate_triangles()
 }
 
 
-Vec3f MeshRaycaster::get_triangle_normal(const indexed_triangle_set& its, size_t facet_idx)
+Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const
 {
-    Vec3f a(its.vertices[its.indices[facet_idx](1)] - its.vertices[its.indices[facet_idx](0)]);
-    Vec3f b(its.vertices[its.indices[facet_idx](2)] - its.vertices[its.indices[facet_idx](0)]);
-    return Vec3f(a.cross(b)).normalized();
+    return m_normals[facet_idx];
 }
 
 void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
@@ -218,12 +216,9 @@ Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
     int idx = 0;
     Vec3d closest_point;
     m_emesh.squared_distance(point.cast<double>(), idx, closest_point);
-    if (normal) {
-        auto indices = m_emesh.F().row(idx);
-        Vec3d a(m_emesh.V().row(indices(1)) - m_emesh.V().row(indices(0)));
-        Vec3d b(m_emesh.V().row(indices(2)) - m_emesh.V().row(indices(0)));
-        *normal = Vec3f(a.cross(b).cast<float>());
-    }
+    if (normal)
+        *normal = m_normals[idx];
+
     return closest_point.cast<float>();
 }
 
diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp
index 92f444f55..8e321730c 100644
--- a/src/slic3r/GUI/MeshUtils.hpp
+++ b/src/slic3r/GUI/MeshUtils.hpp
@@ -108,7 +108,11 @@ public:
     // The pointer can be invalidated after constructor returns.
     MeshRaycaster(const TriangleMesh& mesh)
         : m_emesh(mesh)
-    {}
+    {
+        m_normals.reserve(mesh.stl.facet_start.size());
+        for (const stl_facet& facet : mesh.stl.facet_start)
+            m_normals.push_back(facet.normal);
+    }
 
     void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
                              Vec3d& point, Vec3d& direction) const;
@@ -140,10 +144,11 @@ public:
 
     Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
 
-    static Vec3f get_triangle_normal(const indexed_triangle_set& its, size_t facet_idx);
+    Vec3f get_triangle_normal(size_t facet_idx) const;
 
 private:
     sla::EigenMesh3D m_emesh;
+    std::vector<stl_normal> m_normals;
 };
 
     

From ed55ebba550bb1f35ad43eab0cde5c40e5d6f486 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 15 May 2020 16:23:19 +0200
Subject: [PATCH 3/4] FDM supports gizmo: Further attempts to increase
 performance when painting

---
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 128 +++++++++++++------
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp |  11 +-
 2 files changed, 96 insertions(+), 43 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 6843bab46..f7b55f0ab 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -15,6 +15,8 @@
 namespace Slic3r {
 namespace GUI {
 
+static constexpr size_t MaxVertexBuffers = 50;
+
 GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
     : GLGizmoBase(parent, icon_filename, sprite_id)
     , m_quadric(nullptr)
@@ -123,10 +125,9 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
 
         // Now render both enforcers and blockers.
         for (int i=0; i<2; ++i) {
-            if (m_ivas[mesh_id][i].has_VBOs()) {
-                glsafe(::glColor4f(i ? 1.f : 0.2f, 0.2f, i ? 0.2f : 1.0f, 0.5f));
-                m_ivas[mesh_id][i].render();
-            }
+            glsafe(::glColor4f(i ? 1.f : 0.2f, 0.2f, i ? 0.2f : 1.0f, 0.5f));
+            for (const GLIndexedVertexArray& iva : m_ivas[mesh_id][i])
+                iva.render();
         }
         glsafe(::glPopMatrix());
     }
@@ -205,8 +206,14 @@ void GLGizmoFdmSupports::update_from_model_object()
             ++num_of_volumes;
     m_selected_facets.resize(num_of_volumes);
     m_neighbors.resize(num_of_volumes);
+
     m_ivas.clear();
     m_ivas.resize(num_of_volumes);
+    for (size_t i=0; i<num_of_volumes; ++i) {
+        m_ivas[i][0].reserve(MaxVertexBuffers);
+        m_ivas[i][1].reserve(MaxVertexBuffers);
+    }
+
 
     int volume_id = -1;
     for (const ModelVolume* mv : mo->volumes) {
@@ -226,7 +233,8 @@ void GLGizmoFdmSupports::update_from_model_object()
             for (int i : list)
                 m_selected_facets[volume_id][i] = type;
         }
-        update_vertex_buffers(mv, volume_id, true, true);
+        update_vertex_buffers(mv, volume_id, FacetSupportType::ENFORCER);
+        update_vertex_buffers(mv, volume_id, FacetSupportType::BLOCKER);
 
         m_neighbors[volume_id].resize(3 * mesh->its.indices.size());
 
@@ -389,6 +397,8 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
             const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.;
             const float limit = pow(m_cursor_radius/avg_scaling , 2.f);
 
+            std::vector<size_t> new_facets;
+
             // For all hits on this mesh...
             for (const std::pair<Vec3f, size_t>& hit_and_facet : hit_positions_and_facet_ids[mesh_id]) {
                 some_mesh_was_hit = true;
@@ -438,23 +448,43 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
                     ++facet_idx;
                 }
 
-                // Now just select all facets that passed.
+                //std::vector<size_t> new_facets;
+                //new_facets.clear();
+                new_facets.reserve(facets_to_select.size());
+
+                // Now just select all facets that passed and remember which
+                // ones have really changed state.
                 for (size_t next_facet : facets_to_select) {
                     FacetSupportType& facet = m_selected_facets[mesh_id][next_facet];
 
-                    if (facet != new_state && facet != FacetSupportType::NONE) {
-                        // this triangle is currently in the other VBA.
-                        // Both VBAs need to be refreshed.
-                        update_both = true;
+                    if (facet != new_state) {
+                        if (facet != FacetSupportType::NONE) {
+                            // this triangle is currently in the other VBA.
+                            // Both VBAs need to be refreshed.
+                            update_both = true;
+                        }
+                        facet = new_state;
+                        new_facets.push_back(next_facet);
                     }
-                    facet = new_state;
                 }
             }
 
-            update_vertex_buffers(mv, mesh_id,
-                                  new_state == FacetSupportType::ENFORCER || update_both,
-                                  new_state == FacetSupportType::BLOCKER || update_both
-                                  );
+            if (! new_facets.empty()) {
+                if (new_state != FacetSupportType::NONE) {
+                    // append triangles into the respective VBA
+                    update_vertex_buffers(mv, mesh_id, new_state, &new_facets);
+                    if (update_both) {
+                        auto other = new_state == FacetSupportType::ENFORCER
+                                ? FacetSupportType::BLOCKER
+                                : FacetSupportType::ENFORCER;
+                        update_vertex_buffers(mv, mesh_id, other); // regenerate the other VBA
+                    }
+                }
+                else {
+                    update_vertex_buffers(mv, mesh_id, FacetSupportType::ENFORCER);
+                    update_vertex_buffers(mv, mesh_id, FacetSupportType::BLOCKER);
+                }
+            }
         }
 
         if (some_mesh_was_hit)
@@ -488,34 +518,54 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
 
 void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv,
                                                int mesh_id,
-                                               bool update_enforcers,
-                                               bool update_blockers)
+                                               FacetSupportType type,
+                                               const std::vector<size_t>* new_facets)
 {
     const TriangleMesh* mesh = &mv->mesh();
 
-    for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) {
-        if ((type == FacetSupportType::ENFORCER && ! update_enforcers)
-         || (type == FacetSupportType::BLOCKER && ! update_blockers))
-            continue;
+    std::vector<GLIndexedVertexArray>& ivas = m_ivas[mesh_id][type == FacetSupportType::ENFORCER ? 0 : 1];
 
-        GLIndexedVertexArray& iva = m_ivas[mesh_id][type==FacetSupportType::ENFORCER ? 0 : 1];
-        iva.release_geometry();
-        size_t triangle_cnt=0;
+    // lambda to push facet into vertex buffer
+    auto push_facet = [this, &mesh, &mesh_id](size_t idx, GLIndexedVertexArray& iva) {
+        for (int i=0; i<3; ++i)
+            iva.push_geometry(
+                mesh->its.vertices[mesh->its.indices[idx](i)].cast<double>(),
+                m_c->raycaster()->raycasters()[mesh_id]->get_triangle_normal(idx).cast<double>()
+            );
+        size_t num = iva.triangle_indices_size;
+        iva.push_triangle(num, num+1, num+2);
+    };
+
+
+    if (ivas.size() == MaxVertexBuffers || ! new_facets) {
+        // If there are too many or they should be regenerated, make one large
+        // GLVertexBufferArray.
+        ivas.clear(); // destructors release geometry
+        ivas.push_back(GLIndexedVertexArray());
+
+        bool pushed = false;
         for (size_t facet_idx=0; facet_idx<m_selected_facets[mesh_id].size(); ++facet_idx) {
-            FacetSupportType status = m_selected_facets[mesh_id][facet_idx];
-            if (status != type)
-                continue;
-            for (int i=0; i<3; ++i)
-                iva.push_geometry(
-                    mesh->its.vertices[mesh->its.indices[facet_idx](i)].cast<double>(),
-                    m_c->raycaster()->raycasters()[mesh_id]->get_triangle_normal(facet_idx).cast<double>()
-                );
-            iva.push_triangle(3*triangle_cnt, 3*triangle_cnt+1, 3*triangle_cnt+2);
-            ++triangle_cnt;
+            if (m_selected_facets[mesh_id][facet_idx] == type) {
+                push_facet(facet_idx, ivas.back());
+                pushed = true;
+            }
         }
-        if (! m_selected_facets[mesh_id].empty())
-            iva.finalize_geometry(true);
+        if (pushed)
+            ivas.back().finalize_geometry(true);
+        else
+            ivas.pop_back();
+    } else {
+        // we are only appending - let's make new vertex array and let the old ones live
+        ivas.push_back(GLIndexedVertexArray());
+        for (size_t facet_idx : *new_facets)
+            push_facet(facet_idx, ivas.back());
+
+        if (! new_facets->empty())
+            ivas.back().finalize_geometry(true);
+        else
+            ivas.pop_back();
     }
+
 }
 
 
@@ -548,7 +598,8 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwr
                         ? FacetSupportType::BLOCKER
                         : FacetSupportType::ENFORCER;
         }
-        update_vertex_buffers(mv, mesh_id, true, true);
+        update_vertex_buffers(mv, mesh_id, FacetSupportType::ENFORCER);
+        update_vertex_buffers(mv, mesh_id, FacetSupportType::BLOCKER);
     }
 
     Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle")
@@ -618,7 +669,8 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
                 if (mv->is_model_part()) {
                     m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE);
                     mv->m_supported_facets.clear();
-                    update_vertex_buffers(mv, idx, true, true);
+                    update_vertex_buffers(mv, idx, FacetSupportType::ENFORCER);
+                    update_vertex_buffers(mv, idx, FacetSupportType::BLOCKER);
                     m_parent.set_as_dirty();
                 }
             }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
index bed6d00a0..14b28909f 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
@@ -33,14 +33,15 @@ private:
     // individual facets (one of the enum values above).
     std::vector<std::vector<FacetSupportType>> m_selected_facets;
 
-    // Store two vertex buffer arrays (for enforcers/blockers)
-    // for each model-part volume.
-    std::vector<std::array<GLIndexedVertexArray, 2>> m_ivas;
+    // Vertex buffer arrays for each model-part volume. There is a vector of
+    // arrays so that adding triangles can be done without regenerating all
+    // other triangles. Enforcers and blockers are of course separate.
+    std::vector<std::array<std::vector<GLIndexedVertexArray>, 2>> m_ivas;
 
     void update_vertex_buffers(const ModelVolume* mv,
                                int mesh_id,
-                               bool update_enforcers,
-                               bool update_blockers);
+                               FacetSupportType type, // enforcers / blockers
+                               const std::vector<size_t>* new_facets = nullptr); // nullptr -> regenerate all
 
 public:
     GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);

From 1a38d39f82818be6e71551fafe379fb4765dea01 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 15 May 2020 18:31:55 +0200
Subject: [PATCH 4/4] FDM supports gizmo: slight code refactoring

---
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 161 +++++++++----------
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp |   3 +-
 2 files changed, 80 insertions(+), 84 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index f7b55f0ab..41361eaa9 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -233,8 +233,8 @@ void GLGizmoFdmSupports::update_from_model_object()
             for (int i : list)
                 m_selected_facets[volume_id][i] = type;
         }
-        update_vertex_buffers(mv, volume_id, FacetSupportType::ENFORCER);
-        update_vertex_buffers(mv, volume_id, FacetSupportType::BLOCKER);
+        update_vertex_buffers(mesh, volume_id, FacetSupportType::ENFORCER);
+        update_vertex_buffers(mesh, volume_id, FacetSupportType::BLOCKER);
 
         m_neighbors[volume_id].resize(3 * mesh->its.indices.size());
 
@@ -333,7 +333,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
         Vec3f closest_hit = Vec3f::Zero();
         double closest_hit_squared_distance = std::numeric_limits<double>::max();
         size_t closest_facet = 0;
-        size_t closest_hit_mesh_id = size_t(-1);
+        int closest_hit_mesh_id = -1;
 
         // Transformations of individual meshes
         std::vector<Transform3d> trafo_matrices;
@@ -376,17 +376,22 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
         }
         // We now know where the ray hit, let's save it and cast another ray
         if (closest_hit_mesh_id != size_t(-1)) // only if there is at least one hit
-            hit_positions_and_facet_ids[closest_hit_mesh_id].emplace_back(closest_hit, closest_facet);
+            some_mesh_was_hit = true;
 
+        if (some_mesh_was_hit) {
+            // Now propagate the hits
+            mesh_id = -1;
+            const TriangleMesh* mesh = nullptr;
+            for (const ModelVolume* mv : mo->volumes) {
+                if (! mv->is_model_part())
+                    continue;
+                ++mesh_id;
+                if (mesh_id == closest_hit_mesh_id) {
+                    mesh = &mv->mesh();
+                    break;
+                }
+            }
 
-        // Now propagate the hits
-        mesh_id = -1;
-        for (const ModelVolume* mv : mo->volumes) {
-
-            if (! mv->is_model_part())
-                continue;
-
-            ++mesh_id;
             bool update_both = false;
 
             const Transform3d& trafo_matrix = trafo_matrices[mesh_id];
@@ -397,98 +402,90 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
             const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.;
             const float limit = pow(m_cursor_radius/avg_scaling , 2.f);
 
-            std::vector<size_t> new_facets;
+            const std::pair<Vec3f, size_t>& hit_and_facet = { closest_hit, closest_facet };
 
-            // For all hits on this mesh...
-            for (const std::pair<Vec3f, size_t>& hit_and_facet : hit_positions_and_facet_ids[mesh_id]) {
-                some_mesh_was_hit = true;
-                const TriangleMesh* mesh = &mv->mesh();
-                const std::vector<NeighborData>& neighbors = m_neighbors[mesh_id];
+            const std::vector<NeighborData>& neighbors = m_neighbors[mesh_id];
 
-                // Calculate direction from camera to the hit (in mesh coords):
-                Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast<float>() - hit_and_facet.first).normalized();
+            // Calculate direction from camera to the hit (in mesh coords):
+            Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast<float>() - hit_and_facet.first).normalized();
 
-                // A lambda to calculate distance from the centerline:
-                auto squared_distance_from_line = [&hit_and_facet, &dir](const Vec3f& point) -> float {
-                    Vec3f diff = hit_and_facet.first - point;
-                    return (diff - diff.dot(dir) * dir).squaredNorm();
-                };
+            // A lambda to calculate distance from the centerline:
+            auto squared_distance_from_line = [&hit_and_facet, &dir](const Vec3f& point) -> float {
+                Vec3f diff = hit_and_facet.first - point;
+                return (diff - diff.dot(dir) * dir).squaredNorm();
+            };
 
-                // A lambda to determine whether this facet is potentionally visible (still can be obscured)
-                auto faces_camera = [&dir](const ModelVolume* mv, const size_t& facet) -> bool {
-                    return (mv->mesh().stl.facet_start[facet].normal.dot(dir) > 0.);
-                };
-                // Now start with the facet the pointer points to and check all adjacent facets. neighbors vector stores
-                // pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be
-                // quickly found by finding a vertex in the list and read the respective facet ids.
-                std::vector<size_t> facets_to_select{hit_and_facet.second};
-                NeighborData vertex = std::make_pair(0, 0);
-                std::vector<bool> visited(m_selected_facets[mesh_id].size(), false); // keep track of facets we already processed
-                size_t facet_idx = 0; // index into facets_to_select
-                auto it = neighbors.end();
-                while (facet_idx < facets_to_select.size()) {
-                    size_t facet = facets_to_select[facet_idx];
-                    if (! visited[facet]) {
-                        // check all three vertices and in case they're close enough, find the remaining facets
-                        // and add them to the list to be proccessed later
-                        for (size_t i=0; i<3; ++i) {
-                            vertex.first = mesh->its.indices[facet](i); // vertex index
-                            float dist = squared_distance_from_line(mesh->its.vertices[vertex.first]);
-                            if (dist < limit) {
-                                it = std::lower_bound(neighbors.begin(), neighbors.end(), vertex);
-                                while (it != neighbors.end() && it->first == vertex.first) {
-                                    if (it->second != facet && faces_camera(mv, it->second))
-                                        facets_to_select.push_back(it->second);
-                                    ++it;
-                                }
+            // A lambda to determine whether this facet is potentionally visible (still can be obscured)
+            auto faces_camera = [&dir, &mesh](const size_t& facet) -> bool {
+                return (mesh->stl.facet_start[facet].normal.dot(dir) > 0.);
+            };
+            // Now start with the facet the pointer points to and check all adjacent facets. neighbors vector stores
+            // pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be
+            // quickly found by finding a vertex in the list and read the respective facet ids.
+            std::vector<size_t> facets_to_select{hit_and_facet.second};
+            NeighborData vertex = std::make_pair(0, 0);
+            std::vector<bool> visited(m_selected_facets[mesh_id].size(), false); // keep track of facets we already processed
+            size_t facet_idx = 0; // index into facets_to_select
+            auto it = neighbors.end();
+            while (facet_idx < facets_to_select.size()) {
+                size_t facet = facets_to_select[facet_idx];
+                if (! visited[facet]) {
+                    // check all three vertices and in case they're close enough, find the remaining facets
+                    // and add them to the list to be proccessed later
+                    for (size_t i=0; i<3; ++i) {
+                        vertex.first = mesh->its.indices[facet](i); // vertex index
+                        float dist = squared_distance_from_line(mesh->its.vertices[vertex.first]);
+                        if (dist < limit) {
+                            it = std::lower_bound(neighbors.begin(), neighbors.end(), vertex);
+                            while (it != neighbors.end() && it->first == vertex.first) {
+                                if (it->second != facet && faces_camera(it->second))
+                                    facets_to_select.push_back(it->second);
+                                ++it;
                             }
                         }
-                        visited[facet] = true;
                     }
-                    ++facet_idx;
+                    visited[facet] = true;
                 }
+                ++facet_idx;
+            }
 
-                //std::vector<size_t> new_facets;
-                //new_facets.clear();
-                new_facets.reserve(facets_to_select.size());
+            std::vector<size_t> new_facets;
+            new_facets.reserve(facets_to_select.size());
 
-                // Now just select all facets that passed and remember which
-                // ones have really changed state.
-                for (size_t next_facet : facets_to_select) {
-                    FacetSupportType& facet = m_selected_facets[mesh_id][next_facet];
+            // Now just select all facets that passed and remember which
+            // ones have really changed state.
+            for (size_t next_facet : facets_to_select) {
+                FacetSupportType& facet = m_selected_facets[mesh_id][next_facet];
 
-                    if (facet != new_state) {
-                        if (facet != FacetSupportType::NONE) {
-                            // this triangle is currently in the other VBA.
-                            // Both VBAs need to be refreshed.
-                            update_both = true;
-                        }
-                        facet = new_state;
-                        new_facets.push_back(next_facet);
+                if (facet != new_state) {
+                    if (facet != FacetSupportType::NONE) {
+                        // this triangle is currently in the other VBA.
+                        // Both VBAs need to be refreshed.
+                        update_both = true;
                     }
+                    facet = new_state;
+                    new_facets.push_back(next_facet);
                 }
             }
 
             if (! new_facets.empty()) {
                 if (new_state != FacetSupportType::NONE) {
                     // append triangles into the respective VBA
-                    update_vertex_buffers(mv, mesh_id, new_state, &new_facets);
+                    update_vertex_buffers(mesh, mesh_id, new_state, &new_facets);
                     if (update_both) {
                         auto other = new_state == FacetSupportType::ENFORCER
                                 ? FacetSupportType::BLOCKER
                                 : FacetSupportType::ENFORCER;
-                        update_vertex_buffers(mv, mesh_id, other); // regenerate the other VBA
+                        update_vertex_buffers(mesh, mesh_id, other); // regenerate the other VBA
                     }
                 }
                 else {
-                    update_vertex_buffers(mv, mesh_id, FacetSupportType::ENFORCER);
-                    update_vertex_buffers(mv, mesh_id, FacetSupportType::BLOCKER);
+                    update_vertex_buffers(mesh, mesh_id, FacetSupportType::ENFORCER);
+                    update_vertex_buffers(mesh, mesh_id, FacetSupportType::BLOCKER);
                 }
             }
-        }
 
-        if (some_mesh_was_hit)
-        {
+
             if (m_button_down == Button::None)
                 m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right);
             return true;
@@ -516,13 +513,11 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
 }
 
 
-void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv,
+void GLGizmoFdmSupports::update_vertex_buffers(const TriangleMesh* mesh,
                                                int mesh_id,
                                                FacetSupportType type,
                                                const std::vector<size_t>* new_facets)
 {
-    const TriangleMesh* mesh = &mv->mesh();
-
     std::vector<GLIndexedVertexArray>& ivas = m_ivas[mesh_id][type == FacetSupportType::ENFORCER ? 0 : 1];
 
     // lambda to push facet into vertex buffer
@@ -598,8 +593,8 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool overwr
                         ? FacetSupportType::BLOCKER
                         : FacetSupportType::ENFORCER;
         }
-        update_vertex_buffers(mv, mesh_id, FacetSupportType::ENFORCER);
-        update_vertex_buffers(mv, mesh_id, FacetSupportType::BLOCKER);
+        update_vertex_buffers(&mv->mesh(), mesh_id, FacetSupportType::ENFORCER);
+        update_vertex_buffers(&mv->mesh(), mesh_id, FacetSupportType::BLOCKER);
     }
 
     Plater::TakeSnapshot(wxGetApp().plater(), block ? _L("Block supports by angle")
@@ -669,8 +664,8 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
                 if (mv->is_model_part()) {
                     m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE);
                     mv->m_supported_facets.clear();
-                    update_vertex_buffers(mv, idx, FacetSupportType::ENFORCER);
-                    update_vertex_buffers(mv, idx, FacetSupportType::BLOCKER);
+                    update_vertex_buffers(&mv->mesh(), idx, FacetSupportType::ENFORCER);
+                    update_vertex_buffers(&mv->mesh(), idx, FacetSupportType::BLOCKER);
                     m_parent.set_as_dirty();
                 }
             }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
index 14b28909f..e7f3a2063 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
@@ -38,11 +38,12 @@ private:
     // other triangles. Enforcers and blockers are of course separate.
     std::vector<std::array<std::vector<GLIndexedVertexArray>, 2>> m_ivas;
 
-    void update_vertex_buffers(const ModelVolume* mv,
+    void update_vertex_buffers(const TriangleMesh* mesh,
                                int mesh_id,
                                FacetSupportType type, // enforcers / blockers
                                const std::vector<size_t>* new_facets = nullptr); // nullptr -> regenerate all
 
+
 public:
     GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
     ~GLGizmoFdmSupports() override;