From 23da5910c6561573f4b9a7fdf8577840b31b49a2 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 9 Apr 2020 13:16:39 +0200
Subject: [PATCH] FDM supports gizmo now uses VBOs instead of immediate mode

---
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 115 +++++++++++++------
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp |  21 +++-
 src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp     |   1 +
 3 files changed, 101 insertions(+), 36 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 668ab07df..46833ba14 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -84,6 +84,10 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
 {
     const ModelObject* mo = m_c->selection_info()->model_object();
 
+    glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
+    ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); } );
+    glsafe(::glPolygonOffset(-1.0, 1.0));
+
     int mesh_id = -1;
     for (const ModelVolume* mv : mo->volumes) {
         if (! mv->is_model_part())
@@ -94,26 +98,14 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
         const Transform3d trafo_matrix =
             mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
             mv->get_matrix();
-        const TriangleMesh* mesh = &mv->mesh();
 
-        for (size_t facet_idx=0; facet_idx<m_selected_facets[mesh_id].size(); ++facet_idx) {
-            int8_t status = m_selected_facets[mesh_id][facet_idx];
-            if (status == 0)
-                continue;
-            ::glColor4f(status==1 ? 0.2f : 1.f, 0.2f, status==1 ? 1.0f : 0.2f, 0.5f);
-
-            stl_normal normal = 0.01f * MeshRaycaster::get_triangle_normal(mesh->its, facet_idx);
-            ::glPushMatrix();
-            ::glTranslatef(normal(0), normal(1), normal(2));
-            ::glMultMatrixd(trafo_matrix.data());
-
-            ::glBegin(GL_TRIANGLES);
-            ::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](0)](0), mesh->its.vertices[mesh->its.indices[facet_idx](0)](1), mesh->its.vertices[mesh->its.indices[facet_idx](0)](2));
-            ::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](1)](0), mesh->its.vertices[mesh->its.indices[facet_idx](1)](1), mesh->its.vertices[mesh->its.indices[facet_idx](1)](2));
-            ::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](2)](0), mesh->its.vertices[mesh->its.indices[facet_idx](2)](1), mesh->its.vertices[mesh->its.indices[facet_idx](2)](2));
-            ::glEnd();
-            ::glPopMatrix();
-        }
+        glsafe(::glPushMatrix());
+        glsafe(::glMultMatrixd(trafo_matrix.data()));
+        glsafe(::glColor4f(0.2f, 0.2f, 1.0f, 0.5f));
+        m_ivas[mesh_id][0].render();
+        glsafe(::glColor4f(1.f, 0.2f, 0.2f, 0.5f));
+        m_ivas[mesh_id][1].render();
+        glsafe(::glPopMatrix());
     }
 }
 
@@ -182,6 +174,8 @@ void GLGizmoFdmSupports::update_mesh()
 
     m_selected_facets.resize(num_of_volumes);
     m_neighbors.resize(num_of_volumes);
+    m_ivas.clear();
+    m_ivas.resize(num_of_volumes);
 
     int volume_id = -1;
     for (const ModelVolume* mv : mo->volumes) {
@@ -193,7 +187,7 @@ void GLGizmoFdmSupports::update_mesh()
         // This mesh does not account for the possible Z up SLA offset.
         const TriangleMesh* mesh = &mv->mesh();
 
-        m_selected_facets[volume_id].assign(mesh->its.indices.size(), 0);
+        m_selected_facets[volume_id].assign(mesh->its.indices.size(), SelType::NONE);
         m_neighbors[volume_id].resize(3 * mesh->its.indices.size());
 
         // Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets
@@ -240,13 +234,6 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
         }
     }
 
-    if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
-        double pos = m_c->object_clipper()->get_position();
-        pos = std::max(0., pos - 0.01);
-        m_c->object_clipper()->set_position(pos, true);
-        return true;
-    }
-
     if (action == SLAGizmoEventType::ResetClippingPlane) {
         m_c->object_clipper()->set_position(-1., false);
         return true;
@@ -256,12 +243,16 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
      || action == SLAGizmoEventType::RightDown
     || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) {
 
-        int8_t new_state = 0;
+        SelType new_state = SelType::NONE;
         if (! shift_down) {
             if (action == SLAGizmoEventType::Dragging)
-                new_state = m_button_down == Button::Left ? 1 : -1;
+                new_state = m_button_down == Button::Left
+                        ? SelType::ENFORCER
+                        : SelType::BLOCKER;
             else
-                new_state = action == SLAGizmoEventType::LeftDown ? 1 : -1;
+                new_state = action == SLAGizmoEventType::LeftDown
+                        ? SelType::ENFORCER
+                        : SelType::BLOCKER;
         }
 
         const Camera& camera = wxGetApp().plater()->get_camera();
@@ -327,6 +318,7 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
                 continue;
 
             ++mesh_id;
+            bool update_both = false;
 
             // For all hits on this mesh...
             for (const std::pair<Vec3f, size_t>& hit_and_facet : hit_positions_and_facet_ids[mesh_id]) {
@@ -382,21 +374,41 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
                     }
                     ++facet_idx;
                 }
-                // Now just select all facets that passed
-                for (size_t next_facet : facets_to_select)
-                    m_selected_facets[mesh_id][next_facet] = new_state;
+
+                // Now just select all facets that passed.
+                for (size_t next_facet : facets_to_select) {
+                    SelType& facet = m_selected_facets[mesh_id][next_facet];
+
+                    if (facet != new_state && facet != SelType::NONE) {
+                        // this triangle is currently in the other VBA.
+                        // Both VBAs need to be refreshed.
+                        update_both = true;
+                    }
+                    facet = new_state;
+                }
             }
+
+            update_vertex_buffers(mv, mesh_id,
+                                  new_state == SelType::ENFORCER || update_both,
+                                  new_state == SelType::BLOCKER || update_both
+                                  );
         }
 
         if (some_mesh_was_hit)
         {
             if (m_button_down == Button::None)
                 m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right);
-            m_parent.set_as_dirty();
+            // 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)
+        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();
             return true;
+        }
     }
 
     if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp)
@@ -409,6 +421,35 @@ 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)
+{
+    const TriangleMesh* mesh = &mv->mesh();
+
+    for (SelType type : {SelType::ENFORCER, SelType::BLOCKER}) {
+        if ((type == SelType::ENFORCER && ! update_enforcers)
+         || (type == SelType::BLOCKER && ! update_blockers))
+            continue;
+
+        GLIndexedVertexArray& iva = m_ivas[mesh_id][type==SelType::ENFORCER ? 0 : 1];
+        iva.release_geometry();
+        size_t triangle_cnt=0;
+        for (size_t facet_idx=0; facet_idx<m_selected_facets[mesh_id].size(); ++facet_idx) {
+            SelType 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>(),
+                                  MeshRaycaster::get_triangle_normal(mesh->its, facet_idx).cast<double>());
+            iva.push_triangle(3*triangle_cnt, 3*triangle_cnt+1, 3*triangle_cnt+2);
+            ++triangle_cnt;
+        }
+        iva.finalize_geometry(true);
+    }
+}
+
 
 void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit)
 {
@@ -529,6 +570,10 @@ void GLGizmoFdmSupports::on_set_state()
     if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
         // we are actually shutting down
         Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off")));
+        m_old_mo = nullptr;
+        m_ivas.clear();
+        m_neighbors.clear();
+        m_selected_facets.clear();
     }
     m_old_state = m_state;
 }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
index 6c767db11..d80721fbf 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
@@ -3,6 +3,8 @@
 
 #include "GLGizmoBase.hpp"
 
+#include "slic3r/GUI/3DScene.hpp"
+
 #include <cereal/types/vector.hpp>
 
 
@@ -24,7 +26,24 @@ private:
     static constexpr float CursorRadiusMax  = 8.f;
     static constexpr float CursorRadiusStep = 0.2f;
 
-    std::vector<std::vector<int8_t>> m_selected_facets;
+    enum class SelType : int8_t {
+        NONE,
+        ENFORCER,
+        BLOCKER
+    };
+
+    // For each model-part volume, store a list of statuses of
+    // individual facets (one of the enum values above).
+    std::vector<std::vector<SelType>> m_selected_facets;
+
+    // Store two vertex buffer arrays (for enforcers/blockers)
+    // for each model-part volume.
+    std::vector<std::array<GLIndexedVertexArray, 2>> m_ivas;
+
+    void update_vertex_buffers(const ModelVolume* mv,
+                               int mesh_id,
+                               bool update_enforcers,
+                               bool update_blockers);
 
 public:
     GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
index d26068ca2..32a6de42b 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
@@ -232,6 +232,7 @@ const TriangleMesh* HollowedMesh::get_hollowed_mesh() const
 
 void Raycaster::on_update()
 {
+    wxBusyCursor wait;
     const ModelObject* mo = get_pool()->selection_info()->model_object();
 
     if (! mo)