From f100a596883c12c81c0ff9af3c333a79a4f9fd84 Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Fri, 4 Nov 2022 12:42:02 +0100
Subject: [PATCH] Cherry-picking 118f4859c472ccbc30b43101c6674dadc81d7b12

And resolve conflicts
---
 resources/shaders/110/gouraud_light_clip.fs  |  17 +
 resources/shaders/110/gouraud_light_clip.vs  |  54 +++
 resources/shaders/140/gouraud_light_clip.fs  |  19 +
 resources/shaders/140/gouraud_light_clip.vs  |  54 +++
 resources/shaders/ES/gouraud_light_clip.fs   |  19 +
 resources/shaders/ES/gouraud_light_clip.vs   |  54 +++
 src/libslic3r/SLAPrint.hpp                   |   5 +
 src/slic3r/GUI/3DScene.cpp                   |  68 ----
 src/slic3r/GUI/3DScene.hpp                   |  21 --
 src/slic3r/GUI/GLCanvas3D.cpp                | 205 +----------
 src/slic3r/GUI/GLCanvas3D.hpp                |   2 -
 src/slic3r/GUI/GLShadersManager.cpp          |   2 +
 src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp      | 249 ++++++++-----
 src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp      |  18 +-
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 349 ++++++++++---------
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp |  18 +-
 src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp     | 210 ++++-------
 src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp     |  41 +--
 src/slic3r/GUI/SceneRaycaster.cpp            |   4 +-
 19 files changed, 674 insertions(+), 735 deletions(-)
 create mode 100644 resources/shaders/110/gouraud_light_clip.fs
 create mode 100644 resources/shaders/110/gouraud_light_clip.vs
 create mode 100644 resources/shaders/140/gouraud_light_clip.fs
 create mode 100644 resources/shaders/140/gouraud_light_clip.vs
 create mode 100644 resources/shaders/ES/gouraud_light_clip.fs
 create mode 100644 resources/shaders/ES/gouraud_light_clip.vs

diff --git a/resources/shaders/110/gouraud_light_clip.fs b/resources/shaders/110/gouraud_light_clip.fs
new file mode 100644
index 000000000..5c7068709
--- /dev/null
+++ b/resources/shaders/110/gouraud_light_clip.fs
@@ -0,0 +1,17 @@
+#version 110
+
+uniform vec4 uniform_color;
+uniform float emission_factor;
+
+// x = tainted, y = specular;
+varying vec2 intensity;
+
+varying float clipping_planes_dot;
+
+void main()
+{
+    if (clipping_planes_dot < 0.0)
+        discard;
+
+    gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a);
+}
diff --git a/resources/shaders/110/gouraud_light_clip.vs b/resources/shaders/110/gouraud_light_clip.vs
new file mode 100644
index 000000000..6d7c32e1b
--- /dev/null
+++ b/resources/shaders/110/gouraud_light_clip.vs
@@ -0,0 +1,54 @@
+#version 110
+
+#define INTENSITY_CORRECTION 0.6
+
+// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
+const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
+#define LIGHT_TOP_DIFFUSE    (0.8 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SPECULAR   (0.125 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SHININESS  20.0
+
+// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
+const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
+#define LIGHT_FRONT_DIFFUSE  (0.3 * INTENSITY_CORRECTION)
+
+#define INTENSITY_AMBIENT    0.3
+
+uniform mat4 view_model_matrix;
+uniform mat4 projection_matrix;
+uniform mat3 view_normal_matrix;
+uniform mat4 volume_world_matrix;
+
+// Clipping plane - general orientation. Used by the SLA gizmo.
+uniform vec4 clipping_plane;
+
+attribute vec3 v_position;
+attribute vec3 v_normal;
+
+// x = tainted, y = specular;
+varying vec2 intensity;
+
+varying float clipping_planes_dot;
+
+void main()
+{
+    // First transform the normal into camera space and normalize the result.
+    vec3 eye_normal = normalize(view_normal_matrix * v_normal);
+    
+    // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
+    // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
+    float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
+
+    intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
+    vec4 eye_position = view_model_matrix * vec4(v_position, 1.0);
+    intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
+
+    // Perform the same lighting calculation for the 2nd light source (no specular applied).
+    NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
+    intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
+
+    gl_Position = projection_matrix * eye_position;
+	
+    // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded.
+    clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane);
+}
diff --git a/resources/shaders/140/gouraud_light_clip.fs b/resources/shaders/140/gouraud_light_clip.fs
new file mode 100644
index 000000000..714e5bcaa
--- /dev/null
+++ b/resources/shaders/140/gouraud_light_clip.fs
@@ -0,0 +1,19 @@
+#version 140
+
+uniform vec4 uniform_color;
+uniform float emission_factor;
+
+// x = tainted, y = specular;
+in vec2 intensity;
+
+in float clipping_planes_dot;
+
+out vec4 out_color;
+
+void main()
+{
+    if (clipping_planes_dot < 0.0)
+        discard;
+
+    out_color = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a);
+}
diff --git a/resources/shaders/140/gouraud_light_clip.vs b/resources/shaders/140/gouraud_light_clip.vs
new file mode 100644
index 000000000..8fca59380
--- /dev/null
+++ b/resources/shaders/140/gouraud_light_clip.vs
@@ -0,0 +1,54 @@
+#version 140
+
+#define INTENSITY_CORRECTION 0.6
+
+// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
+const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
+#define LIGHT_TOP_DIFFUSE    (0.8 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SPECULAR   (0.125 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SHININESS  20.0
+
+// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
+const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
+#define LIGHT_FRONT_DIFFUSE  (0.3 * INTENSITY_CORRECTION)
+
+#define INTENSITY_AMBIENT    0.3
+
+uniform mat4 view_model_matrix;
+uniform mat4 projection_matrix;
+uniform mat3 view_normal_matrix;
+uniform mat4 volume_world_matrix;
+
+// Clipping plane - general orientation. Used by the SLA gizmo.
+uniform vec4 clipping_plane;
+
+in vec3 v_position;
+in vec3 v_normal;
+
+// x = tainted, y = specular;
+out vec2 intensity;
+
+out float clipping_planes_dot;
+
+void main()
+{
+    // First transform the normal into camera space and normalize the result.
+    vec3 eye_normal = normalize(view_normal_matrix * v_normal);
+    
+    // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
+    // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
+    float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
+
+    intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
+    vec4 eye_position = view_model_matrix * vec4(v_position, 1.0);
+    intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
+
+    // Perform the same lighting calculation for the 2nd light source (no specular applied).
+    NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
+    intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
+
+    gl_Position = projection_matrix * eye_position;
+	
+    // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded.
+    clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane);
+}
diff --git a/resources/shaders/ES/gouraud_light_clip.fs b/resources/shaders/ES/gouraud_light_clip.fs
new file mode 100644
index 000000000..45cae0ddb
--- /dev/null
+++ b/resources/shaders/ES/gouraud_light_clip.fs
@@ -0,0 +1,19 @@
+#version 100
+
+precision highp float;
+
+uniform vec4 uniform_color;
+uniform float emission_factor;
+
+// x = tainted, y = specular;
+varying vec2 intensity;
+
+varying float clipping_planes_dot;
+
+void main()
+{
+    if (clipping_planes_dot < 0.0)
+        discard;
+
+    gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a);
+}
diff --git a/resources/shaders/ES/gouraud_light_clip.vs b/resources/shaders/ES/gouraud_light_clip.vs
new file mode 100644
index 000000000..f3ab8c3dc
--- /dev/null
+++ b/resources/shaders/ES/gouraud_light_clip.vs
@@ -0,0 +1,54 @@
+#version 100
+
+#define INTENSITY_CORRECTION 0.6
+
+// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
+const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
+#define LIGHT_TOP_DIFFUSE    (0.8 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SPECULAR   (0.125 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SHININESS  20.0
+
+// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
+const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
+#define LIGHT_FRONT_DIFFUSE  (0.3 * INTENSITY_CORRECTION)
+
+#define INTENSITY_AMBIENT    0.3
+
+uniform mat4 view_model_matrix;
+uniform mat4 projection_matrix;
+uniform mat3 view_normal_matrix;
+uniform mat4 volume_world_matrix;
+
+// Clipping plane - general orientation. Used by the SLA gizmo.
+uniform vec4 clipping_plane;
+
+attribute vec3 v_position;
+attribute vec3 v_normal;
+
+// x = tainted, y = specular;
+varying vec2 intensity;
+
+varying float clipping_planes_dot;
+
+void main()
+{
+    // First transform the normal into camera space and normalize the result.
+    vec3 eye_normal = normalize(view_normal_matrix * v_normal);
+    
+    // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
+    // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
+    float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
+
+    intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
+    vec4 eye_position = view_model_matrix * vec4(v_position, 1.0);
+    intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
+
+    // Perform the same lighting calculation for the 2nd light source (no specular applied).
+    NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
+    intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
+
+    gl_Position = projection_matrix * eye_position;
+	
+    // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded.
+    clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane);
+}
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index 9ca610087..ca74cb391 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -476,6 +476,11 @@ public:
     const PrintObjects& objects() const { return m_objects; }
     // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects
     // in the notification center.
+    const SLAPrintObject* get_print_object_by_model_object_id(ObjectID object_id) const {
+        auto it = std::find_if(m_objects.begin(), m_objects.end(),
+            [object_id](const SLAPrintObject* obj) { return obj->model_object()->id() == object_id; });
+        return (it == m_objects.end()) ? nullptr : *it;
+    }
     const SLAPrintObject* get_object(ObjectID object_id) const {
         auto it = std::find_if(m_objects.begin(), m_objects.end(),
             [object_id](const SLAPrintObject *obj) { return obj->id() == object_id; });
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index 103ab339c..c6ef2d13b 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -852,74 +852,6 @@ int GLVolumeCollection::load_object_volume(
     return int(this->volumes.size() - 1);
 }
 
-//// Load SLA auxiliary GLVolumes (for support trees or pad).
-//// This function produces volumes for multiple instances in a single shot,
-//// as some object specific mesh conversions may be expensive.
-//#if ENABLE_LEGACY_OPENGL_REMOVAL
-//void GLVolumeCollection::load_object_auxiliary(
-//    const SLAPrintObject* print_object,
-//    int                             obj_idx,
-//    // pairs of <instance_idx, print_instance_idx>
-//    const std::vector<std::pair<size_t, size_t>>& instances,
-//    SLAPrintObjectStep              milestone,
-//    // Timestamp of the last change of the milestone
-//    size_t                          timestamp)
-//#else
-//void GLVolumeCollection::load_object_auxiliary(
-//    const SLAPrintObject 		   *print_object,
-//    int                             obj_idx,
-//    // pairs of <instance_idx, print_instance_idx>
-//    const std::vector<std::pair<size_t, size_t>>& instances,
-//    SLAPrintObjectStep              milestone,
-//    // Timestamp of the last change of the milestone
-//    size_t                          timestamp,
-//    bool 				 			opengl_initialized)
-//#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-//{
-//    assert(print_object->is_step_done(milestone));
-//    Transform3d  mesh_trafo_inv = print_object->trafo().inverse();
-//    // Get the support mesh.
-//    TriangleMesh mesh = print_object->get_mesh(milestone);
-//    mesh.transform(mesh_trafo_inv);
-//    // Convex hull is required for out of print bed detection.
-//    TriangleMesh convex_hull = mesh.convex_hull_3d();
-//    for (const std::pair<size_t, size_t>& instance_idx : instances) {
-//        const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
-//        this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
-//        GLVolume& v = *this->volumes.back();
-//#if ENABLE_LEGACY_OPENGL_REMOVAL
-//#if ENABLE_SMOOTH_NORMALS
-//        v.model.init_from(mesh, true);
-//#else
-//        v.model.init_from(mesh);
-//        v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR);
-//#if ENABLE_RAYCAST_PICKING
-//        v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<const TriangleMesh>(mesh));
-//#endif // ENABLE_RAYCAST_PICKING
-//#endif // ENABLE_SMOOTH_NORMALS
-//#else
-//#if ENABLE_SMOOTH_NORMALS
-//        v.indexed_vertex_array.load_mesh(mesh, true);
-//#else
-//        v.indexed_vertex_array.load_mesh(mesh);
-//#endif // ENABLE_SMOOTH_NORMALS
-//        v.indexed_vertex_array.finalize_geometry(opengl_initialized);
-//#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-//        v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
-//        v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
-//        // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
-//        if (&instance_idx == &instances.back())
-//            v.set_convex_hull(std::move(convex_hull));
-//        else
-//            v.set_convex_hull(convex_hull);
-//        v.is_modifier = false;
-//        v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
-//        v.set_instance_transformation(model_instance.get_transformation());
-//        // Leave the volume transformation at identity.
-//        // v.set_volume_transformation(model_volume->get_transformation());
-//    }
-//}
-
 #if ENABLE_LEGACY_OPENGL_REMOVAL
 #if ENABLE_OPENGL_ES
 int GLVolumeCollection::load_wipe_tower_preview(
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index abf3e9264..d1c16290f 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -658,16 +658,6 @@ public:
         int                volume_idx,
         int                instance_idx);
 
-    // Load SLA auxiliary GLVolumes (for support trees or pad).
-//    void load_object_auxiliary(
-//        const SLAPrintObject* print_object,
-//        int                             obj_idx,
-//        // pairs of <instance_idx, print_instance_idx>
-//        const std::vector<std::pair<size_t, size_t>>& instances,
-//        SLAPrintObjectStep              milestone,
-//        // Timestamp of the last change of the milestone
-//        size_t                          timestamp);
-
 #if ENABLE_OPENGL_ES
     int load_wipe_tower_preview(
         float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh = nullptr);
@@ -689,17 +679,6 @@ public:
         int                instance_idx,
         bool 			   opengl_initialized);
 
-    // Load SLA auxiliary GLVolumes (for support trees or pad).
-    void load_object_auxiliary(
-        const SLAPrintObject           *print_object,
-        int                             obj_idx,
-        // pairs of <instance_idx, print_instance_idx>
-        const std::vector<std::pair<size_t, size_t>>& instances,
-        SLAPrintObjectStep              milestone,
-        // Timestamp of the last change of the milestone
-        size_t                          timestamp,
-        bool 			   				opengl_initialized);
-
     int load_wipe_tower_preview(
         float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized);
 #endif // ENABLE_LEGACY_OPENGL_REMOVAL
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index a698633a8..636080db7 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -2011,15 +2011,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
         size_t                      volume_idx;
     };
 
-//    // SLA steps to pull the preview meshes for.
-//	typedef std::array<SLAPrintObjectStep, 3> SLASteps;
-//    SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad };
-//    struct SLASupportState {
-//        std::array<PrintStateBase::StateWithTimeStamp, std::tuple_size<SLASteps>::value> step;
-//    };
-//    // State of the sla_steps for all SLAPrintObjects.
-//    std::vector<SLASupportState>   sla_support_state;
-
     std::vector<size_t> instance_ids_selected;
     std::vector<size_t> map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1));
     std::vector<GLVolumeState> deleted_volumes;
@@ -2044,33 +2035,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
             }
         }
     }
-//    if (printer_technology == ptSLA) {
-//        const SLAPrint* sla_print = this->sla_print();
-//#ifndef NDEBUG
-//        // Verify that the SLAPrint object is synchronized with m_model.
-//        check_model_ids_equal(*m_model, sla_print->model());
-//#endif /* NDEBUG */
-//        sla_support_state.reserve(sla_print->objects().size());
-//        for (const SLAPrintObject* print_object : sla_print->objects()) {
-//            SLASupportState state;
-//            for (size_t istep = 0; istep < sla_steps.size(); ++istep) {
-//                state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]);
-//                if (state.step[istep].state == PrintStateBase::DONE) {
-//                    if (!print_object->has_mesh(sla_steps[istep]))
-//                        // Consider the DONE step without a valid mesh as invalid for the purpose
-//                        // of mesh visualization.
-//                        state.step[istep].state = PrintStateBase::INVALID;
-//                    else if (sla_steps[istep] != slaposDrillHoles)
-//                        for (const ModelInstance* model_instance : print_object->model_object()->instances)
-//                            // Only the instances, which are currently printable, will have the SLA support structures kept.
-//                            // The instances outside the print bed will have the GLVolumes of their support structures released.
-//                            if (model_instance->is_printable())
-//                                aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id());
-//                }
-//            }
-//            sla_support_state.emplace_back(state);
-//        }
-//    }
+
     std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower);
     std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower);
     // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh.
@@ -2190,139 +2155,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
     }
 
     if (printer_technology == ptSLA) {
-//        size_t idx = 0;
-//        const SLAPrint *sla_print = this->sla_print();
-//		std::vector<double> shift_zs(m_model->objects.size(), 0);
-//        double relative_correction_z = sla_print->relative_correction().z();
-//        if (relative_correction_z <= EPSILON)
-//            relative_correction_z = 1.;
-//		for (const SLAPrintObject *print_object : sla_print->objects()) {
-//            SLASupportState   &state        = sla_support_state[idx ++];
-//            const ModelObject *model_object = print_object->model_object();
-//            // Find an index of the ModelObject
-//            int object_idx;
-//            // There may be new SLA volumes added to the scene for this print_object.
-//            // Find the object index of this print_object in the Model::objects list.
-//            auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object);
-//            assert(it != sla_print->model().objects.end());
-//			object_idx = it - sla_print->model().objects.begin();
-//			// Cache the Z offset to be applied to all volumes with this object_idx.
-//			shift_zs[object_idx] = print_object->get_current_elevation() / relative_correction_z;
-//            // Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene.
-//            // pairs of <instance_idx, print_instance_idx>
-//			std::vector<std::pair<size_t, size_t>> instances[std::tuple_size<SLASteps>::value];
-//            for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) {
-//                const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx];
-//                // Find index of ModelInstance corresponding to this SLAPrintObject::Instance.
-//				auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(),
-//                    [&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; });
-//                assert(it != model_object->instances.end());
-//                int instance_idx = it - model_object->instances.begin();
-//                for (size_t istep = 0; istep < sla_steps.size(); ++ istep)
-//                    if (sla_steps[istep] == slaposDrillHoles) {
-//                    	// Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance,
-//                    	// not into its own GLVolume.
-//                        // There shall always be such a GLVolume allocated.
-//                        ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id);
-//                        auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower);
-//                        assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id);
-//                        assert(!it->new_geometry());
-//                        GLVolume &volume = *m_volumes.volumes[it->volume_idx];
-//                        if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) {
-//                        	// The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen.
-//#if ENABLE_LEGACY_OPENGL_REMOVAL
-//                            volume.model.reset();
-//#else
-//                            volume.indexed_vertex_array.release_geometry();
-//#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-//                            if (state.step[istep].state == PrintStateBase::DONE) {
-//                                TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles);
-//	                            assert(! mesh.empty());
-
-//                                // sla_trafo does not contain volume trafo. To get a mesh to create
-//                                // a new volume from, we have to apply vol trafo inverse separately.
-//                                const ModelObject& mo = *m_model->objects[volume.object_idx()];
-//                                Transform3d trafo = sla_print->sla_trafo(mo)
-//                                    * mo.volumes.front()->get_transformation().get_matrix();
-//                                mesh.transform(trafo.inverse());
-//#if ENABLE_SMOOTH_NORMALS
-//#if ENABLE_LEGACY_OPENGL_REMOVAL
-//                                volume.model.init_from(mesh, true);
-//#else
-//                                volume.indexed_vertex_array.load_mesh(mesh, true);
-//#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-//#else
-//#if ENABLE_LEGACY_OPENGL_REMOVAL
-//                                volume.model.init_from(mesh);
-//#if ENABLE_RAYCAST_PICKING
-//                                volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(mesh));
-//#endif // ENABLE_RAYCAST_PICKING
-//#else
-//                                volume.indexed_vertex_array.load_mesh(mesh);
-//#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-//#endif // ENABLE_SMOOTH_NORMALS
-//                            }
-//                            else {
-//	                        	// Reload the original volume.
-//#if ENABLE_SMOOTH_NORMALS
-//#if ENABLE_LEGACY_OPENGL_REMOVAL
-//                                volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
-//#else
-//                                volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
-//#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-//#else
-//#if ENABLE_LEGACY_OPENGL_REMOVAL
-//#if ENABLE_RAYCAST_PICKING
-//                                const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh();
-//                                volume.model.init_from(new_mesh);
-//                                volume.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(std::make_shared<TriangleMesh>(new_mesh));
-//#else
-//                                volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh());
-//#endif // ENABLE_RAYCAST_PICKING
-//#else
-//                                volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh());
-//#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-//#endif // ENABLE_SMOOTH_NORMALS
-//                            }
-//#if !ENABLE_LEGACY_OPENGL_REMOVAL
-//                            volume.finalize_geometry(true);
-//#endif // !ENABLE_LEGACY_OPENGL_REMOVAL
-//                        }
-//                    	//FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable
-//                    	// to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables
-//                    	// of various concenrs (model vs. 3D print path).
-//                    	volume.offsets = { state.step[istep].timestamp };
-//                    }
-//                    else if (state.step[istep].state == PrintStateBase::DONE) {
-//                        // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created.
-//						ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id);
-//						auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower);
-//						assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id);
-//                    	if (it->new_geometry()) {
-//                            // This can be an SLA support structure that should not be rendered (in case someone used undo
-//                            // to revert to before it was generated). If that's the case, we should not generate anything.
-//                            if (model_object->sla_points_status != sla::PointsStatus::NoPoints)
-//                                instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx));
-//                            else
-//                                shift_zs[object_idx] = 0.;
-//                        }
-//                        else {
-//                            // Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
-//                            m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx);
-//                            m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation());
-//                        }
-//                    }
-//            }
-
-//            for (size_t istep = 0; istep < sla_steps.size(); ++istep)
-//                if (!instances[istep].empty())
-//#if ENABLE_LEGACY_OPENGL_REMOVAL
-//                    m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp);
-//#else
-//                    m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized);
-//#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-//        }
-
         // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
         for (GLVolume* volume : m_volumes.volumes)
             if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) {
@@ -7638,25 +7470,24 @@ void GLCanvas3D::_load_sla_shells()
     };
 
     // adds objects' volumes 
-    for (const SLAPrintObject* obj : print->objects())
-        /*if (obj->is_step_done(slaposSliceSupports))*/ {
-            unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size();
-            for (const SLAPrintObject::Instance& instance : obj->instances()) {
-                add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true);
-                // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when
-                // through the update_volumes_colors_by_extruder() call.
-                m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id();
-                if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty())
-                    add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true);
-                if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty())
-                    add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false);
-            }
-            double shift_z = obj->get_current_elevation();
-            for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) {
-                // apply shift z
-                m_volumes.volumes[i]->set_sla_shift_z(shift_z);
-            }
+    for (const SLAPrintObject* obj : print->objects()) {
+        unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size();
+        for (const SLAPrintObject::Instance& instance : obj->instances()) {
+            add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true);
+            // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when
+            // through the update_volumes_colors_by_extruder() call.
+            m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id();
+            if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty())
+                add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true);
+            if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty())
+                add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false);
         }
+        double shift_z = obj->get_current_elevation();
+        for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) {
+            // apply shift z
+            m_volumes.volumes[i]->set_sla_shift_z(shift_z);
+        }
+    }
 
     update_volumes_colors_by_extruder();
 }
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 140cbaf74..a519a26a1 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -688,8 +688,6 @@ public:
     void set_raycaster_gizmos_on_top(bool value) {
         m_scene_raycaster.set_gizmos_on_top(value);
     }
-
-    const SceneRaycaster & raycaster() const { return m_scene_raycaster; }
 #endif // ENABLE_RAYCAST_PICKING
 
     void set_as_dirty();
diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp
index f0b91faf3..a60a5121b 100644
--- a/src/slic3r/GUI/GLShadersManager.cpp
+++ b/src/slic3r/GUI/GLShadersManager.cpp
@@ -72,6 +72,8 @@ std::pair<bool, std::string> GLShadersManager::init()
 #if ENABLE_LEGACY_OPENGL_REMOVAL
     // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview
     valid &= append_shader("gouraud_light", { prefix + "gouraud_light.vs", prefix + "gouraud_light.fs" });
+    // extend "gouraud_light" by adding clipping, used in sla gizmos
+    valid &= append_shader("gouraud_light_clip", { prefix + "gouraud_light_clip.vs", prefix + "gouraud_light_clip.fs" });
     // used to render printbed
     valid &= append_shader("printbed", { prefix + "printbed.vs", prefix + "printbed.fs" });
 #else
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
index 52aed6bfc..d098d41f5 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
@@ -10,6 +10,7 @@
 #include "slic3r/GUI/GUI_ObjectList.hpp"
 #include "slic3r/GUI/Plater.hpp"
 #include "libslic3r/PresetBundle.hpp"
+#include "libslic3r/SLAPrint.hpp"
 
 #include "libslic3r/Model.hpp"
 
@@ -53,14 +54,21 @@ void GLGizmoHollow::data_changed()
             reload_cache();
             m_old_mo_id = mo->id();
         }
-//        if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh())
-//            m_holes_in_drilled_mesh = mo->sla_drain_holes;
+
+        const SLAPrintObject* po = m_c->selection_info()->print_object();
+        if (po != nullptr && po->get_mesh_to_print().empty())
+            process_mesh(slaposAssembly);
+
+        update_volumes();
+
 #if ENABLE_RAYCAST_PICKING
-        if (m_raycasters.empty())
-            on_register_raycasters_for_picking();
+        if (m_hole_raycasters.empty())
+            register_hole_raycasters_for_picking();
         else
-            update_raycasters_for_picking_transform();
+            update_hole_raycasters_for_picking_transform();
 #endif // ENABLE_RAYCAST_PICKING
+
+        m_c->instances_hider()->set_hide_full_scene(true);
     }
 }
 
@@ -87,16 +95,15 @@ void GLGizmoHollow::on_render()
     glsafe(::glEnable(GL_BLEND));
     glsafe(::glEnable(GL_DEPTH_TEST));
 
-    if (selection.is_from_single_instance())
 #if ENABLE_RAYCAST_PICKING
-        render_points(selection);
+    render_volumes();
+    render_points(selection);
 #else
-        render_points(selection, false);
+    render_points(selection, false);
 #endif // ENABLE_RAYCAST_PICKING
 
     m_selection_rectangle.render(m_parent);
     m_c->object_clipper()->render_cut();
-    m_c->supports_clipper()->render_cut();
 
     glsafe(::glDisable(GL_BLEND));
 }
@@ -104,27 +111,14 @@ void GLGizmoHollow::on_render()
 #if ENABLE_RAYCAST_PICKING
 void GLGizmoHollow::on_register_raycasters_for_picking()
 {
-    assert(m_raycasters.empty());
-
-    init_cylinder_model();
-
-    set_sla_auxiliary_volumes_picking_state(false);
-
-    const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info();
-    if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) {
-        const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes;
-        for (int i = 0; i < (int)drain_holes.size(); ++i) {
-            m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster));
-        }
-        update_raycasters_for_picking_transform();
-    }
+    register_hole_raycasters_for_picking();
+    register_volume_raycasters_for_picking();
 }
 
 void GLGizmoHollow::on_unregister_raycasters_for_picking()
 {
-    m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo);
-    m_raycasters.clear();
-    set_sla_auxiliary_volumes_picking_state(true);
+    unregister_hole_raycasters_for_picking();
+    unregister_volume_raycasters_for_picking();
 }
 #else
 void GLGizmoHollow::on_render_for_picking()
@@ -190,7 +184,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking)
 
 #if ENABLE_RAYCAST_PICKING
         const bool clipped = is_mesh_point_clipped(drain_hole.pos.cast<double>());
-        m_raycasters[i]->set_active(!clipped);
+        m_hole_raycasters[i]->set_active(!clipped);
         if (clipped)
             continue;
 #else
@@ -270,6 +264,25 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking)
 #endif // !ENABLE_LEGACY_OPENGL_REMOVAL
 }
 
+void GLGizmoHollow::render_volumes()
+{
+    GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip");
+    if (shader == nullptr)
+        return;
+
+    shader->start_using();
+    shader->set_uniform("emission_factor", 0.0f);
+    const Camera& camera = wxGetApp().plater()->get_camera();
+
+    ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0)  ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane();
+    clipping_plane.set_normal(-clipping_plane.get_normal());
+    m_volumes.set_clipping_plane(clipping_plane.get_data());
+
+    m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix());
+    shader->stop_using();
+
+}
+
 bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const
 {
     if (m_c->object_clipper()->get_position() == 0.)
@@ -291,48 +304,26 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const
 // Return false if no intersection was found, true otherwise.
 bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal)
 {
+    if (m_c->raycaster()->raycasters().size() != 1)
+        return false;
     if (! m_c->raycaster()->raycaster())
         return false;
 
-    const Camera& camera = wxGetApp().plater()->get_camera();
-    const Selection& selection = m_parent.get_selection();
-    const GLVolume* volume = selection.get_first_volume();
-    Geometry::Transformation trafo = volume->get_instance_transformation() * volume->get_volume_transformation();
-    trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
-
-    double clp_dist = m_c->object_clipper()->get_position();
-    const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane();
-
     // The raycaster query
     Vec3f hit;
     Vec3f normal;
     if (m_c->raycaster()->raycaster()->unproject_on_mesh(
             mouse_pos,
-            trafo.get_matrix(),
-            camera,
+            m_volumes.volumes.front()->world_matrix(),
+            wxGetApp().plater()->get_camera(),
             hit,
             normal,
-            clp_dist != 0. ? clp : nullptr))
-    {
-//        if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) {
-//            // in this case the raycaster sees the hollowed and drilled mesh.
-//            // if the point lies on the surface created by the hole, we want
-//            // to ignore it.
-//            for (const sla::DrainHole& hole : m_holes_in_drilled_mesh) {
-//                sla::DrainHole outer(hole);
-//                outer.radius *= 1.001f;
-//                outer.height *= 1.001f;
-//                if (outer.is_inside(hit))
-//                    return false;
-//            }
-//        }
-
+            m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) {
         // Return both the point and the facet normal.
         pos_and_normal = std::make_pair(hit, normal);
         return true;
     }
-    else
-        return false;
+    return false;
 }
 
 // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
@@ -348,9 +339,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
     // left down with shift - show the selection rectangle:
     if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
         if (m_hover_id == -1) {
-            if (shift_down || alt_down) {
+            if (shift_down || alt_down)
                 m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect);
-            }
         }
         else {
             if (m_selected[m_hover_id])
@@ -383,8 +373,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
                 m_parent.set_as_dirty();
                 m_wait_for_up_event = true;
 #if ENABLE_RAYCAST_PICKING
-                on_unregister_raycasters_for_picking();
-                on_register_raycasters_for_picking();
+                unregister_hole_raycasters_for_picking();
+                register_hole_raycasters_for_picking();
 #endif // ENABLE_RAYCAST_PICKING
             }
             else
@@ -510,8 +500,8 @@ void GLGizmoHollow::delete_selected_points()
     }
 
 #if ENABLE_RAYCAST_PICKING
-    on_unregister_raycasters_for_picking();
-    on_register_raycasters_for_picking();
+    unregister_hole_raycasters_for_picking();
+    register_hole_raycasters_for_picking();
 #endif // ENABLE_RAYCAST_PICKING
     select_point(NoPoints);
 }
@@ -580,39 +570,61 @@ bool GLGizmoHollow::on_mouse(const wxMouseEvent &mouse_event)
     return false;
 }
 
-void GLGizmoHollow::hollow_mesh(bool postpone_error_messages)
+void GLGizmoHollow::process_mesh(SLAPrintObjectStep step, bool postpone_error_messages)
 {
-    wxGetApp().CallAfter([this, postpone_error_messages]() {
-        wxGetApp().plater()->reslice_SLA_hollowing(
-            *m_c->selection_info()->model_object(), postpone_error_messages);
-    });
+    wxGetApp().CallAfter([this, step, postpone_error_messages]() {
+        wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages);
+        });
 }
 
 #if ENABLE_RAYCAST_PICKING
-void GLGizmoHollow::set_sla_auxiliary_volumes_picking_state(bool state)
+void GLGizmoHollow::register_hole_raycasters_for_picking()
 {
-    std::vector<std::shared_ptr<SceneRaycasterItem>>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume);
-    if (raycasters != nullptr) {
-        const Selection& selection = m_parent.get_selection();
-        const Selection::IndicesList ids = selection.get_volume_idxs();
-        for (unsigned int id : ids) {
-            const GLVolume* v = selection.get_volume(id);
-            if (v->is_sla_pad() || v->is_sla_support()) {
-                auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr<SceneRaycasterItem> item) { return item->get_raycaster() == v->mesh_raycaster.get(); });
-                if (it != raycasters->end())
-                    (*it)->set_active(state);
-            }
+    assert(m_hole_raycasters.empty());
+
+    init_cylinder_model();
+
+    const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info();
+    if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) {
+        const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes;
+        for (int i = 0; i < (int)drain_holes.size(); ++i) {
+            m_hole_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster, Transform3d::Identity()));
         }
+        update_hole_raycasters_for_picking_transform();
     }
 }
 
-void GLGizmoHollow::update_raycasters_for_picking_transform()
+void GLGizmoHollow::unregister_hole_raycasters_for_picking()
+{
+    for (size_t i = 0; i < m_hole_raycasters.size(); ++i) {
+        m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, i);
+    }
+    m_hole_raycasters.clear();
+}
+
+void GLGizmoHollow::register_volume_raycasters_for_picking()
+{
+    for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
+        const GLVolume* v = m_volumes.volumes[i];
+        m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i, *v->mesh_raycaster, v->world_matrix()));
+    }
+}
+
+void GLGizmoHollow::unregister_volume_raycasters_for_picking()
+{
+    for (size_t i = 0; i < m_volume_raycasters.size(); ++i) {
+        m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i);
+    }
+    m_volume_raycasters.clear();
+}
+
+void GLGizmoHollow::update_hole_raycasters_for_picking_transform()
 {
     const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info();
     if (info != nullptr) {
         const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes;
         if (!drain_holes.empty()) {
-            assert(!m_raycasters.empty());
+            assert(!m_hole_raycasters.empty());
 
             const GLVolume* vol = m_parent.get_selection().get_first_volume();
             const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_scaling_factor_matrix().inverse();
@@ -625,13 +637,62 @@ void GLGizmoHollow::update_raycasters_for_picking_transform()
                 const Eigen::AngleAxisd aa(q);
                 const Transform3d matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) *
                     Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength));
-                m_raycasters[i]->set_transform(matrix);
+                m_hole_raycasters[i]->set_transform(matrix);
             }
         }
     }
 }
 #endif // ENABLE_RAYCAST_PICKING
 
+void GLGizmoHollow::update_volumes()
+{
+    m_volumes.clear();
+    unregister_volume_raycasters_for_picking();
+
+    const ModelObject* mo = m_c->selection_info()->model_object();
+    if (mo == nullptr)
+        return;
+
+    const SLAPrintObject* po = m_c->selection_info()->print_object();
+    if (po == nullptr)
+        return;
+
+    TriangleMesh backend_mesh = po->get_mesh_to_print();
+    if (!backend_mesh.empty()) {
+        // The backend has generated a valid mesh. Use it
+        backend_mesh.transform(po->trafo().inverse());
+        m_volumes.volumes.emplace_back(new GLVolume());
+        GLVolume* new_volume = m_volumes.volumes.back();
+        new_volume->model.init_from(backend_mesh);
+        new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation());
+        new_volume->set_sla_shift_z(po->get_current_elevation());
+        new_volume->selected = true; // to set the proper color
+        new_volume->mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(backend_mesh);
+    }
+
+    if (m_volumes.volumes.empty()) {
+        // No valid mesh found in the backend. Use the selection to duplicate the volumes
+        const Selection& selection = m_parent.get_selection();
+        const Selection::IndicesList& idxs = selection.get_volume_idxs();
+        for (unsigned int idx : idxs) {
+            const GLVolume* v = selection.get_volume(idx);
+            if (!v->is_modifier) {
+                m_volumes.volumes.emplace_back(new GLVolume());
+                GLVolume* new_volume = m_volumes.volumes.back();
+                const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh();
+                new_volume->model.init_from(mesh);
+                new_volume->set_instance_transformation(v->get_instance_transformation());
+                new_volume->set_volume_transformation(v->get_volume_transformation());
+                new_volume->set_sla_shift_z(v->get_sla_shift_z());
+                new_volume->selected = true; // to set the proper color
+                new_volume->mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
+            }
+        }
+    }
+
+    register_volume_raycasters_for_picking();
+}
+
 std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>>
 GLGizmoHollow::get_config_options(const std::vector<std::string>& keys) const
 {
@@ -724,8 +785,8 @@ RENDER_AGAIN:
     window_width = std::max(window_width, button_preview_width);
 
     if (m_imgui->button(m_desc["preview"]))
-        hollow_mesh();
-    
+        process_mesh(slaposDrillHoles);
+
     bool config_changed = false;
 
     ImGui::Separator();
@@ -896,13 +957,6 @@ RENDER_AGAIN:
     if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f"))
         m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
 
-    // make sure supports are shown/hidden as appropriate
-    bool show_sups = m_c->instances_hider()->are_supports_shown();
-    if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) {
-        m_c->instances_hider()->show_supports(show_sups);
-        force_refresh = true;
-    }
-
     m_imgui->end();
 
 
@@ -935,7 +989,7 @@ bool GLGizmoHollow::on_is_activable() const
     const Selection& selection = m_parent.get_selection();
 
     if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA
-        || !selection.is_from_single_instance())
+        || !selection.is_single_full_instance())
         return false;
 
     // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside.
@@ -964,9 +1018,7 @@ CommonGizmosDataID GLGizmoHollow::on_get_requirements() const
                 int(CommonGizmosDataID::SelectionInfo)
               | int(CommonGizmosDataID::InstancesHider)
               | int(CommonGizmosDataID::Raycaster)
-              | int(CommonGizmosDataID::HollowedMesh)
-              | int(CommonGizmosDataID::ObjectClipper)
-              | int(CommonGizmosDataID::SupportsClipper));
+              | int(CommonGizmosDataID::ObjectClipper));
 }
 
 
@@ -975,8 +1027,12 @@ void GLGizmoHollow::on_set_state()
     if (m_state == m_old_state)
         return;
 
-    if (m_state == Off && m_old_state != Off) // the gizmo was just turned Off
+    if (m_state == Off && m_old_state != Off) {
+        // the gizmo was just turned Off
         m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
+        m_c->instances_hider()->set_hide_full_scene(false);
+    }
+
     m_old_state = m_state;
 }
 
@@ -1091,6 +1147,9 @@ void GLGizmoHollow::reload_cache()
 
 void GLGizmoHollow::on_set_hover_id()
 {
+    if (m_c->selection_info()->model_object() == nullptr)
+        return;
+
     if (int(m_c->selection_info()->model_object()->sla_drain_holes.size()) <= m_hover_id)
         m_hover_id = -1;
 }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
index 0fdc3b2db..e7a324a1e 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
@@ -3,6 +3,7 @@
 
 #include "GLGizmoBase.hpp"
 #include "slic3r/GUI/GLSelectionRectangle.hpp"
+#include "slic3r/GUI/3DScene.hpp"
 
 #include <libslic3r/SLA/Hollowing.hpp>
 #include <libslic3r/ObjectID.hpp>
@@ -25,7 +26,6 @@ class GLGizmoHollow : public GLGizmoBase
 private:
     bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
 
-
 public:
     GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
     void data_changed() override;
@@ -59,21 +59,29 @@ private:
 #else
     void render_points(const Selection& selection, bool picking = false);
 #endif // ENABLE_RAYCAST_PICKING
-    void hollow_mesh(bool postpone_error_messages = false);
+    void render_volumes();
+    void process_mesh(SLAPrintObjectStep step, bool postpone_error_messages = false);
 #if ENABLE_RAYCAST_PICKING
-    void set_sla_auxiliary_volumes_picking_state(bool state);
-    void update_raycasters_for_picking_transform();
+    void register_hole_raycasters_for_picking();
+    void unregister_hole_raycasters_for_picking();
+    void register_volume_raycasters_for_picking();
+    void unregister_volume_raycasters_for_picking();
+    void update_hole_raycasters_for_picking_transform();
 #endif // ENABLE_RAYCAST_PICKING
+    void update_volumes();
 
     ObjectID m_old_mo_id = -1;
 
 #if ENABLE_RAYCAST_PICKING
     PickingModel m_cylinder;
-    std::vector<std::shared_ptr<SceneRaycasterItem>> m_raycasters;
+    std::vector<std::shared_ptr<SceneRaycasterItem>> m_hole_raycasters;
+    std::vector<std::shared_ptr<SceneRaycasterItem>> m_volume_raycasters;
 #else
     GLModel m_cylinder;
 #endif // ENABLE_RAYCAST_PICKING
 
+    GLVolumeCollection m_volumes;
+
     float m_new_hole_radius = 2.f;        // Size of a new hole.
     float m_new_hole_height = 6.f;
     mutable std::vector<bool> m_selected; // which holes are currently selected
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index d0dd3baa0..cc45a8e73 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -60,6 +60,16 @@ bool GLGizmoSlaSupports::on_init()
     return true;
 }
 
+static int last_completed_step(const SLAPrint& sla)
+{
+    int step = -1;
+    for (int i = 0; i < (int)SLAPrintObjectStep::slaposCount; ++i) {
+        if (sla.is_step_done((SLAPrintObjectStep)i))
+            ++step;
+    }
+    return step;
+}
+
 void GLGizmoSlaSupports::data_changed()
 {
     if (! m_c->selection_info())
@@ -71,19 +81,25 @@ void GLGizmoSlaSupports::data_changed()
         disable_editing_mode();
         reload_cache();
         m_old_mo_id = mo->id();
-        m_c->instances_hider()->show_supports(true);
     }
 
     // If we triggered autogeneration before, check backend and fetch results if they are there
     if (mo) {
+        m_c->instances_hider()->set_hide_full_scene(true);
+        const SLAPrintObject* po = m_c->selection_info()->print_object();
+        if (po != nullptr && last_completed_step(*po->print()) < (int)slaposDrillHoles)
+            process_mesh(slaposDrillHoles, false);
+
+        update_volumes();
+
         if (mo->sla_points_status == sla::PointsStatus::Generating)
             get_data_from_backend();
 
 #if ENABLE_RAYCAST_PICKING
-        if (m_raycasters.empty())
-            on_register_raycasters_for_picking();
+        if (m_point_raycasters.empty())
+            register_point_raycasters_for_picking();
         else
-            update_raycasters_for_picking_transform();
+            update_point_raycasters_for_picking_transform();
 #endif // ENABLE_RAYCAST_PICKING
     }
 
@@ -111,8 +127,6 @@ void GLGizmoSlaSupports::on_render()
     if (!m_sphere.is_initialized())
         m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0));
 #endif // ENABLE_RAYCAST_PICKING
-    if (!m_cylinder.is_initialized())
-        m_cylinder.init_from(its_make_cylinder(1.0, 1.0, double(PI) / 12.0));
 
     ModelObject* mo = m_c->selection_info()->model_object();
     const Selection& selection = m_parent.get_selection();
@@ -128,16 +142,15 @@ void GLGizmoSlaSupports::on_render()
     glsafe(::glEnable(GL_BLEND));
     glsafe(::glEnable(GL_DEPTH_TEST));
 
-    if (selection.is_from_single_instance())
 #if ENABLE_RAYCAST_PICKING
-        render_points(selection);
+    render_volumes();
+    render_points(selection);
 #else
-        render_points(selection, false);
+    render_points(selection, false);
 #endif // ENABLE_RAYCAST_PICKING
 
     m_selection_rectangle.render(m_parent);
     m_c->object_clipper()->render_cut();
-    m_c->supports_clipper()->render_cut();
 
     glsafe(::glDisable(GL_BLEND));
 }
@@ -145,23 +158,14 @@ void GLGizmoSlaSupports::on_render()
 #if ENABLE_RAYCAST_PICKING
 void GLGizmoSlaSupports::on_register_raycasters_for_picking()
 {
-    assert(m_raycasters.empty());
-    set_sla_auxiliary_volumes_picking_state(false);
-
-    if (m_editing_mode && !m_editing_cache.empty()) {
-        for (size_t i = 0; i < m_editing_cache.size(); ++i) {
-            m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster),
-                m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster));
-        }
-        update_raycasters_for_picking_transform();
-    }
+    register_point_raycasters_for_picking();
+    register_volume_raycasters_for_picking();
 }
 
 void GLGizmoSlaSupports::on_unregister_raycasters_for_picking()
 {
-    m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo);
-    m_raycasters.clear();
-    set_sla_auxiliary_volumes_picking_state(true);
+    unregister_point_raycasters_for_picking();
+    unregister_volume_raycasters_for_picking();
 }
 #else
 void GLGizmoSlaSupports::on_render_for_picking()
@@ -181,10 +185,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
     const size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size();
 
     const bool has_points = (cache_size != 0);
-    const bool has_holes = (/*! m_c->hollowed_mesh()->get_hollowed_mesh()
-                   &&*/ ! m_c->selection_info()->model_object()->sla_drain_holes.empty());
-
-    if (! has_points && ! has_holes)
+    if (!has_points)
         return;
 
 #if ENABLE_LEGACY_OPENGL_REMOVAL
@@ -234,9 +235,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
 
 #if ENABLE_RAYCAST_PICKING
         const bool clipped = is_mesh_point_clipped(support_point.pos.cast<double>());
-        if (!m_raycasters.empty()) {
-            m_raycasters[i].first->set_active(!clipped);
-            m_raycasters[i].second->set_active(!clipped);
+        if (i < m_point_raycasters.size()) {
+            m_point_raycasters[i].first->set_active(!clipped);
+            m_point_raycasters[i].second->set_active(!clipped);
         }
         if (clipped)
             continue;
@@ -306,8 +307,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
         if (m_editing_mode) {
             // in case the normal is not yet cached, find and cache it
             if (m_editing_cache[i].normal == Vec3f::Zero())
-                m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
-                //m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
+                m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
 
             Eigen::Quaterniond q;
             q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>());
@@ -373,67 +373,28 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
 #endif // !ENABLE_LEGACY_OPENGL_REMOVAL
     }
 
-    // Now render the drain holes:
-#if ENABLE_RAYCAST_PICKING
-    if (has_holes) {
-#else
-    if (has_holes && ! picking) {
-#endif // ENABLE_RAYCAST_PICKING
-        render_color = { 0.7f, 0.7f, 0.7f, 0.7f };
-#if ENABLE_LEGACY_OPENGL_REMOVAL
-        m_cylinder.set_color(render_color);
-#else
-        m_cylinder.set_color(-1, render_color);
-        if (shader != nullptr)
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-        shader->set_uniform("emission_factor", 0.5f);
-        for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) {
-            if (is_mesh_point_clipped(drain_hole.pos.cast<double>()))
-                continue;
-
-#if ENABLE_LEGACY_OPENGL_REMOVAL
-            const Transform3d hole_matrix = Geometry::translation_transform(drain_hole.pos.cast<double>()) * instance_scaling_matrix_inverse;
-#else
-            // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
-            glsafe(::glPushMatrix());
-            glsafe(::glTranslatef(drain_hole.pos.x(), drain_hole.pos.y(), drain_hole.pos.z()));
-            glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-
-            if (vol->is_left_handed())
-                glsafe(::glFrontFace(GL_CW));
-
-            // Matrices set, we can render the point mark now.
-            Eigen::Quaterniond q;
-            q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast<double>());
-            const Eigen::AngleAxisd aa(q);
-#if ENABLE_LEGACY_OPENGL_REMOVAL
-            const Transform3d model_matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) *
-                Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength));
-            shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
-            const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
-            shader->set_uniform("view_normal_matrix", view_normal_matrix);
-#else
-            glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis().x(), aa.axis().y(), aa.axis().z()));
-            glsafe(::glTranslated(0., 0., -drain_hole.height));
-            glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength));
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-            m_cylinder.render();
-
-            if (vol->is_left_handed())
-                glsafe(::glFrontFace(GL_CCW));
-#if !ENABLE_LEGACY_OPENGL_REMOVAL
-            glsafe(::glPopMatrix());
-#endif // !ENABLE_LEGACY_OPENGL_REMOVAL
-        }
-    }
-
 #if !ENABLE_LEGACY_OPENGL_REMOVAL
     glsafe(::glPopMatrix());
 #endif // !ENABLE_LEGACY_OPENGL_REMOVAL
 }
 
+void GLGizmoSlaSupports::render_volumes()
+{
+    GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip");
+    if (shader == nullptr)
+        return;
 
+    shader->start_using();
+    shader->set_uniform("emission_factor", 0.0f);
+    const Camera& camera = wxGetApp().plater()->get_camera();
+
+    ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane();
+    clipping_plane.set_normal(-clipping_plane.get_normal());
+    m_volumes.set_clipping_plane(clipping_plane.get_data());
+
+    m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix());
+    shader->stop_using();
+}
 
 bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
 {
@@ -456,48 +417,22 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
 // Return false if no intersection was found, true otherwise.
 bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal)
 {
-    if (m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->empty())
+    if (!m_c->raycaster()->raycaster())
         return false;
 
-    const Camera& camera = wxGetApp().plater()->get_camera();
-    const Selection& selection = m_parent.get_selection();
-    const GLVolume* volume = selection.get_first_volume();
-    Geometry::Transformation trafo = volume->get_instance_transformation() * volume->get_volume_transformation();
-    trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
-
-    double clp_dist = m_c->object_clipper()->get_position();
-    const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane();
-
     // The raycaster query
     Vec3f hit;
     Vec3f normal;
-    if (m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->unproject_on_mesh(
+    if (m_c->raycaster()->raycaster()->unproject_on_mesh(
             mouse_pos,
-            trafo.get_matrix(),
-            camera,
+            m_volumes.volumes.front()->world_matrix(),
+            wxGetApp().plater()->get_camera(),
             hit,
             normal,
-            clp_dist != 0. ? clp : nullptr))
-    {
-        // Check whether the hit is in a hole
-//        bool in_hole = false;
-        // In case the hollowed and drilled mesh is available, we can allow
-        // placing points in holes, because they should never end up
-        // on surface that's been drilled away.
-//        if (! m_c->hollowed_mesh()->get_hollowed_mesh()) {
-//            sla::DrainHoles drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
-//            for (const sla::DrainHole& hole : drain_holes) {
-//                if (hole.is_inside(hit)) {
-//                    in_hole = true;
-//                    break;
-//                }
-//            }
-//        }
-//        if (! in_hole) {
-            // Return both the point and the facet normal.
-            pos_and_normal = std::make_pair(hit, normal);
-            return true;
-//        }
+            m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) { 
+        // Return both the point and the facet normal.
+        pos_and_normal = std::make_pair(hit, normal);
+        return true;
     }
 
     return false;
@@ -548,8 +483,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
                     m_parent.set_as_dirty();
                     m_wait_for_up_event = true;
 #if ENABLE_RAYCAST_PICKING
-                    on_unregister_raycasters_for_picking();
-                    on_register_raycasters_for_picking();
+                    unregister_point_raycasters_for_picking();
+                    register_point_raycasters_for_picking();
 #endif // ENABLE_RAYCAST_PICKING
                 }
                 else
@@ -592,9 +527,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
             for (size_t idx : points_idxs)
                 points_inside.emplace_back((trafo.get_matrix().cast<float>() * (m_editing_cache[idx].support_point.pos + m_editing_cache[idx].normal)).cast<float>());
 
-            assert(!m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume).emtpy());
-            for (size_t idx : m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_unobscured_idxs(
-                     trafo, wxGetApp().plater()->get_camera(), points_inside,
+            for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs(
+                    trafo, wxGetApp().plater()->get_camera(), points_inside,
                      m_c->object_clipper()->get_clipping_plane()))
             {
                 if (idx >= orig_pts_num) // this is a cone-base, get index of point it belongs to
@@ -712,8 +646,8 @@ void GLGizmoSlaSupports::delete_selected_points(bool force)
     }
 
 #if ENABLE_RAYCAST_PICKING
-    on_unregister_raycasters_for_picking();
-    on_register_raycasters_for_picking();
+    unregister_point_raycasters_for_picking();
+    register_point_raycasters_for_picking();
 #endif // ENABLE_RAYCAST_PICKING
 
     select_point(NoPoints);
@@ -1027,7 +961,7 @@ bool GLGizmoSlaSupports::on_is_activable() const
     const Selection& selection = m_parent.get_selection();
 
     if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA
-        || !selection.is_from_single_instance())
+        || !selection.is_single_full_instance())
         return false;
 
     // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside.
@@ -1055,9 +989,7 @@ CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
                 int(CommonGizmosDataID::SelectionInfo)
               | int(CommonGizmosDataID::InstancesHider)
               | int(CommonGizmosDataID::Raycaster)
-              | int(CommonGizmosDataID::HollowedMesh)
-              | int(CommonGizmosDataID::ObjectClipper)
-              | int(CommonGizmosDataID::SupportsClipper));
+              | int(CommonGizmosDataID::ObjectClipper));
 }
 
 
@@ -1101,6 +1033,9 @@ void GLGizmoSlaSupports::on_set_state()
             disable_editing_mode(); // so it is not active next time the gizmo opens
             m_old_mo_id = -1;
         }
+
+        if (m_state == Off)
+            m_c->instances_hider()->set_hide_full_scene(false);
     }
     m_old_state = m_state;
 }
@@ -1395,10 +1330,8 @@ void GLGizmoSlaSupports::switch_to_editing_mode()
         m_editing_cache.emplace_back(sp);
     select_point(NoPoints);
 #if ENABLE_RAYCAST_PICKING
-    on_register_raycasters_for_picking();
+    register_point_raycasters_for_picking();
 #endif // ENABLE_RAYCAST_PICKING
-
-    m_c->instances_hider()->show_supports(false);
     m_parent.set_as_dirty();
 }
 
@@ -1408,10 +1341,9 @@ void GLGizmoSlaSupports::disable_editing_mode()
     if (m_editing_mode) {
         m_editing_mode = false;
         wxGetApp().plater()->leave_gizmos_stack();
-        m_c->instances_hider()->show_supports(true);
         m_parent.set_as_dirty();
 #if ENABLE_RAYCAST_PICKING
-        on_unregister_raycasters_for_picking();
+        unregister_point_raycasters_for_picking();
 #endif // ENABLE_RAYCAST_PICKING
     }
     wxGetApp().plater()->get_notification_manager()->close_notification_of_type(NotificationType::QuitSLAManualMode);
@@ -1432,53 +1364,130 @@ bool GLGizmoSlaSupports::unsaved_changes() const
 }
 
 #if ENABLE_RAYCAST_PICKING
-void GLGizmoSlaSupports::set_sla_auxiliary_volumes_picking_state(bool state)
+void GLGizmoSlaSupports::register_point_raycasters_for_picking()
 {
-    std::vector<std::shared_ptr<SceneRaycasterItem>>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume);
-    if (raycasters != nullptr) {
-        const Selection& selection = m_parent.get_selection();
-        const Selection::IndicesList ids = selection.get_volume_idxs();
-        for (unsigned int id : ids) {
-            const GLVolume* v = selection.get_volume(id);
-            if (v->is_sla_pad() || v->is_sla_support()) {
-                auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr<SceneRaycasterItem> item) { return item->get_raycaster() == v->mesh_raycaster.get(); });
-                if (it != raycasters->end())
-                    (*it)->set_active(state);
-            }
+    assert(m_point_raycasters.empty());
+
+    if (m_editing_mode && !m_editing_cache.empty()) {
+        for (size_t i = 0; i < m_editing_cache.size(); ++i) {
+            m_point_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster, Transform3d::Identity()),
+                m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster, Transform3d::Identity()));
         }
+        update_point_raycasters_for_picking_transform();
     }
 }
 
-void GLGizmoSlaSupports::update_raycasters_for_picking_transform()
+void GLGizmoSlaSupports::unregister_point_raycasters_for_picking()
 {
-    if (!m_editing_cache.empty()) {
-        assert(!m_raycasters.empty());
+    for (size_t i = 0; i < m_point_raycasters.size(); ++i) {
+        m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, i);
+    }
+    m_point_raycasters.clear();
+}
 
-        const GLVolume* vol = m_parent.get_selection().get_first_volume();
-        const Geometry::Transformation transformation(vol->world_matrix());
-        const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse();
-        for (size_t i = 0; i < m_editing_cache.size(); ++i) {
-            const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast<double>()) * instance_scaling_matrix_inverse;
+void GLGizmoSlaSupports::register_volume_raycasters_for_picking()
+{
+    for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
+        const GLVolume* v = m_volumes.volumes[i];
+        m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i, *v->mesh_raycaster, v->world_matrix()));
+    }
+}
 
-            if (m_editing_cache[i].normal == Vec3f::Zero())
-                m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
+void GLGizmoSlaSupports::unregister_volume_raycasters_for_picking()
+{
+    for (size_t i = 0; i < m_volume_raycasters.size(); ++i) {
+        m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i);
+    }
+    m_volume_raycasters.clear();
+}
 
-            Eigen::Quaterniond q;
-            q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>());
-            const Eigen::AngleAxisd aa(q);
-            const Transform3d cone_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) *
-              Geometry::translation_transform((CONE_HEIGHT + m_editing_cache[i].support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ()) *
-              Geometry::rotation_transform({ double(PI), 0.0, 0.0 }) * Geometry::scale_transform({ CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT });
-            m_raycasters[i].second->set_transform(cone_matrix);
+void GLGizmoSlaSupports::update_point_raycasters_for_picking_transform()
+{
+    if (m_editing_cache.empty())
+        return;
 
-            const double radius = (double)m_editing_cache[i].support_point.head_front_radius * RenderPointScale;
-            const Transform3d sphere_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius);
-            m_raycasters[i].first->set_transform(sphere_matrix);
-        }
+    assert(!m_point_raycasters.empty());
+
+    const GLVolume* vol = m_parent.get_selection().get_first_volume();
+    const Geometry::Transformation transformation(vol->world_matrix());
+    const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse();
+    for (size_t i = 0; i < m_editing_cache.size(); ++i) {
+        const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast<double>()) * instance_scaling_matrix_inverse;
+
+        if (m_editing_cache[i].normal == Vec3f::Zero())
+            m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal);
+
+        Eigen::Quaterniond q;
+        q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>());
+        const Eigen::AngleAxisd aa(q);
+        const Transform3d cone_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) *
+            Geometry::assemble_transform((CONE_HEIGHT + m_editing_cache[i].support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ(),
+                Vec3d(PI, 0.0, 0.0), Vec3d(CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT));
+        m_point_raycasters[i].second->set_transform(cone_matrix);
+
+        const double radius = (double)m_editing_cache[i].support_point.head_front_radius * RenderPointScale;
+        const Transform3d sphere_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius);
+        m_point_raycasters[i].first->set_transform(sphere_matrix);
     }
 }
 #endif // ENABLE_RAYCAST_PICKING
 
+void GLGizmoSlaSupports::update_volumes()
+{
+    m_volumes.clear();
+    unregister_volume_raycasters_for_picking();
+
+    const ModelObject* mo = m_c->selection_info()->model_object();
+    if (mo == nullptr)
+        return;
+
+    const SLAPrintObject* po = m_c->selection_info()->print_object();
+    if (po == nullptr)
+        return;
+
+    TriangleMesh backend_mesh = po->get_mesh_to_print();
+    if (!backend_mesh.empty()) {
+        // The backend has generated a valid mesh. Use it
+        backend_mesh.transform(po->trafo().inverse());
+        m_volumes.volumes.emplace_back(new GLVolume());
+        GLVolume* new_volume = m_volumes.volumes.back();
+        new_volume->model.init_from(backend_mesh);
+        new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation());
+        new_volume->set_sla_shift_z(po->get_current_elevation());
+        new_volume->selected = true; // to set the proper color
+        new_volume->mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(backend_mesh);
+    }
+
+    if (m_volumes.volumes.empty()) {
+        // No valid mesh found in the backend. Use the selection to duplicate the volumes
+        const Selection& selection = m_parent.get_selection();
+        const Selection::IndicesList& idxs = selection.get_volume_idxs();
+        for (unsigned int idx : idxs) {
+            const GLVolume* v = selection.get_volume(idx);
+            if (!v->is_modifier) {
+                m_volumes.volumes.emplace_back(new GLVolume());
+                GLVolume* new_volume = m_volumes.volumes.back();
+                const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh();
+                new_volume->model.init_from(mesh);
+                new_volume->set_instance_transformation(v->get_instance_transformation());
+                new_volume->set_volume_transformation(v->get_volume_transformation());
+                new_volume->set_sla_shift_z(v->get_sla_shift_z());
+                new_volume->selected = true; // to set the proper color
+                new_volume->mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
+            }
+        }
+    }
+
+    register_volume_raycasters_for_picking();
+}
+
+void GLGizmoSlaSupports::process_mesh(SLAPrintObjectStep step, bool postpone_error_messages)
+{
+    wxGetApp().CallAfter([this, step, postpone_error_messages]() {
+        wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages);
+        });
+}
+
 SlaGizmoHelpDialog::SlaGizmoHelpDialog()
 : wxDialog(nullptr, wxID_ANY, _L("SLA gizmo keyboard shortcuts"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
 {
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index c4232ed48..6729d6eda 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -3,6 +3,7 @@
 
 #include "GLGizmoBase.hpp"
 #include "slic3r/GUI/GLSelectionRectangle.hpp"
+#include "slic3r/GUI/3DScene.hpp"
 
 #include "libslic3r/SLA/SupportPoint.hpp"
 #include "libslic3r/ObjectID.hpp"
@@ -22,7 +23,6 @@ enum class SLAGizmoEventType : unsigned char;
 class GLGizmoSlaSupports : public GLGizmoBase
 {
 private:
-
     bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
 
     static constexpr float RenderPointScale = 1.f;
@@ -93,11 +93,17 @@ private:
 #else
     void render_points(const Selection& selection, bool picking = false);
 #endif // ENABLE_RAYCAST_PICKING
+    void render_volumes();
     bool unsaved_changes() const;
 #if ENABLE_RAYCAST_PICKING
-    void set_sla_auxiliary_volumes_picking_state(bool state);
-    void update_raycasters_for_picking_transform();
+    void register_point_raycasters_for_picking();
+    void unregister_point_raycasters_for_picking();
+    void register_volume_raycasters_for_picking();
+    void unregister_volume_raycasters_for_picking();
+    void update_point_raycasters_for_picking_transform();
 #endif // ENABLE_RAYCAST_PICKING
+    void update_volumes();
+    void process_mesh(SLAPrintObjectStep step, bool postpone_error_messages = false);
 
     bool m_lock_unique_islands = false;
     bool m_editing_mode = false;            // Is editing mode active?
@@ -113,12 +119,14 @@ private:
 #if ENABLE_RAYCAST_PICKING
     PickingModel m_sphere;
     PickingModel m_cone;
-    std::vector<std::pair<std::shared_ptr<SceneRaycasterItem>, std::shared_ptr<SceneRaycasterItem>>> m_raycasters;
+    std::vector<std::pair<std::shared_ptr<SceneRaycasterItem>, std::shared_ptr<SceneRaycasterItem>>> m_point_raycasters;
+    std::vector<std::shared_ptr<SceneRaycasterItem>> m_volume_raycasters;
 #else
     GLModel m_cone;
     GLModel m_sphere;
 #endif // ENABLE_RAYCAST_PICKING
-    GLModel m_cylinder;
+
+    GLVolumeCollection m_volumes;
 
     // This map holds all translated description texts, so they can be easily referenced during layout calculations
     // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
index 31fbb0f7a..d8a858bac 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp
@@ -59,13 +59,6 @@ InstancesHider* CommonGizmosDataPool::instances_hider() const
     return inst_hider->is_valid() ? inst_hider : nullptr;
 }
 
-//HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const
-//{
-//    HollowedMesh* hol_mesh = dynamic_cast<HollowedMesh*>(m_data.at(CommonGizmosDataID::HollowedMesh).get());
-//    assert(hol_mesh);
-//    return hol_mesh->is_valid() ? hol_mesh : nullptr;
-//}
-
 Raycaster* CommonGizmosDataPool::raycaster() const
 {
     Raycaster* rc = dynamic_cast<Raycaster*>(m_data.at(CommonGizmosDataID::Raycaster).get());
@@ -123,9 +116,8 @@ void SelectionInfo::on_update()
 
     if (selection.is_single_full_instance()) {
         m_model_object = selection.get_model()->objects[selection.get_object_idx()];
-
         if (m_model_object)
-            m_print_object = get_pool()->get_canvas()->sla_print()->get_object(m_model_object->id());
+            m_print_object = get_pool()->get_canvas()->sla_print()->get_print_object_by_model_object_id(m_model_object->id());
 
         m_z_shift = selection.get_first_volume()->get_sla_shift_z();
     }
@@ -154,8 +146,10 @@ void InstancesHider::on_update()
 
     if (mo && active_inst != -1) {
         canvas->toggle_model_objects_visibility(false);
-        canvas->toggle_model_objects_visibility(true, mo, active_inst);
-        canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst);
+        if (!m_hide_full_scene) {
+            canvas->toggle_model_objects_visibility(true, mo, active_inst);
+            canvas->toggle_sla_auxiliaries_visibility(false, mo, active_inst);
+        }
         canvas->set_use_clipping_planes(true);
         // Some objects may be sinking, do not show whatever is below the bed.
         canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
@@ -188,9 +182,10 @@ void InstancesHider::on_release()
     m_clippers.clear();
 }
 
-void InstancesHider::show_supports(bool show) {
-    if (m_show_supports != show) {
-        m_show_supports = show;
+void InstancesHider::set_hide_full_scene(bool hide)
+{
+    if (m_hide_full_scene != hide) {
+        m_hide_full_scene = hide;
         on_update();
     }
 }
@@ -255,81 +250,6 @@ void InstancesHider::render_cut() const
 
 
 
-//void HollowedMesh::on_update()
-//{
-//    const ModelObject* mo = get_pool()->selection_info()->model_object();
-//    bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA;
-//    if (! mo || ! is_sla)
-//        return;
-
-//    const GLCanvas3D* canvas = get_pool()->get_canvas();
-//    const PrintObjects& print_objects = canvas->sla_print()->objects();
-//    const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size()))
-//            ? print_objects[m_print_object_idx]
-//            : nullptr;
-
-//    // Find the respective SLAPrintObject.
-//    if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) {
-//        m_print_objects_count = print_objects.size();
-//        m_print_object_idx = -1;
-//        for (const SLAPrintObject* po : print_objects) {
-//            ++m_print_object_idx;
-//            if (po->model_object()->id() == mo->id()) {
-//                print_object = po;
-//                break;
-//            }
-//        }
-//    }
-
-//    // If there is a valid SLAPrintObject, check state of Hollowing step.
-//    if (print_object) {
-//        if (print_object->is_step_done(slaposDrillHoles) && !print_object->get_mesh_to_print().empty()) {
-//            size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp;
-//            if (timestamp > m_old_hollowing_timestamp) {
-//                const TriangleMesh& backend_mesh = print_object->get_mesh_to_print();
-//                if (! backend_mesh.empty()) {
-//                    m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh));
-//                    Transform3d trafo_inv = (canvas->sla_print()->sla_trafo(*mo) * print_object->model_object()->volumes.front()->get_transformation().get_matrix()).inverse();
-//                    m_hollowed_mesh_transformed->transform(trafo_inv);
-//                    m_drainholes = print_object->model_object()->sla_drain_holes;
-//                    m_old_hollowing_timestamp = timestamp;
-
-////                    indexed_triangle_set interior = print_object->hollowed_interior_mesh();
-////                    its_flip_triangles(interior);
-////                    m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(std::move(interior));
-////                    m_hollowed_interior_transformed->transform(trafo_inv);
-//                }
-//                else {
-//                    m_hollowed_mesh_transformed.reset(nullptr);
-//                }
-//            }
-//        }
-//        else
-//            m_hollowed_mesh_transformed.reset(nullptr);
-//    }
-//}
-
-
-//void HollowedMesh::on_release()
-//{
-//    m_hollowed_mesh_transformed.reset();
-//    m_old_hollowing_timestamp = 0;
-//    m_print_object_idx = -1;
-//}
-
-
-//const TriangleMesh* HollowedMesh::get_hollowed_mesh() const
-//{
-//    return m_hollowed_mesh_transformed.get();
-//}
-
-//const TriangleMesh* HollowedMesh::get_hollowed_interior() const
-//{
-//    return m_hollowed_interior_transformed.get();
-//}
-
-
-
 
 void Raycaster::on_update()
 {
@@ -347,21 +267,28 @@ void Raycaster::on_update()
         mvs = mo->volumes;
 
     std::vector<const TriangleMesh*> meshes;
-    const std::vector<ModelVolume*>& mvs = mo->volumes;
-//    if (mvs.size() == 1) {
-//        assert(mvs.front()->is_model_part());
-//        const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh();
-//        if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh())
-//            meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh());
-//    }
-    if (meshes.empty()) {
-        for (const ModelVolume* v : mvs) {
-            if (v->is_model_part())
-                meshes.push_back(&v->mesh());
+    bool force_raycaster_regeneration = false;
+    if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) {
+        // For sla printers we use the mesh generated by the backend
+        const SLAPrintObject* po = get_pool()->selection_info()->print_object();
+        assert(po != nullptr);
+        m_sla_mesh_cache = po->get_mesh_to_print();
+        if (!m_sla_mesh_cache.empty()) {
+            m_sla_mesh_cache.transform(po->trafo().inverse());
+            meshes.emplace_back(&m_sla_mesh_cache);
+            force_raycaster_regeneration = true;
         }
     }
 
-    if (meshes != m_old_meshes) {
+    if (meshes.empty()) {
+        const std::vector<ModelVolume*>& mvs = mo->volumes;
+        for (const ModelVolume* mv : mvs) {
+            if (mv->is_model_part())
+                meshes.push_back(&mv->mesh());
+        }
+    }
+
+    if (force_raycaster_regeneration || meshes != m_old_meshes) {
         m_raycasters.clear();
         for (const TriangleMesh* mesh : meshes)
 #if ENABLE_RAYCAST_PICKING
@@ -399,25 +326,36 @@ void ObjectClipper::on_update()
 
     // which mesh should be cut?
     std::vector<const TriangleMesh*> meshes;
-//    bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh();
-//    if (has_hollowed)
-//        meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh());
+    std::vector<Geometry::Transformation> trafos;
+    bool force_clipper_regeneration = false;
+    if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) {
+        // For sla printers we use the mesh generated by the backend
+        const SLAPrintObject* po = get_pool()->selection_info()->print_object();
+        assert(po != nullptr);
+        m_sla_mesh_cache = po->get_mesh_to_print();
+        if (!m_sla_mesh_cache.empty()) {
+            m_sla_mesh_cache.transform(po->trafo().inverse());
+            meshes.emplace_back(&m_sla_mesh_cache);
+            trafos.emplace_back(Geometry::Transformation());
+            force_clipper_regeneration = true;
+        }
+    }
 
-    if (meshes.empty())
-        for (const ModelVolume* mv : mo->volumes)
-            meshes.push_back(&mv->mesh());
+    if (meshes.empty()) {
+        for (const ModelVolume* mv : mo->volumes) {
+            meshes.emplace_back(&mv->mesh());
+            trafos.emplace_back(mv->get_transformation());
+        }
+    }
 
-    if (meshes != m_old_meshes) {
+    if (force_clipper_regeneration || meshes != m_old_meshes) {
         m_clippers.clear();
-        for (const TriangleMesh* mesh : meshes) {
-            m_clippers.emplace_back(new MeshClipper);
-            m_clippers.back()->set_mesh(*mesh);
+        for (size_t i = 0; i < meshes.size(); ++i) {
+            m_clippers.emplace_back(new MeshClipper, trafos[i]);
+            m_clippers.back().first->set_mesh(*meshes[i]);
         }
         m_old_meshes = meshes;
 
-//        if (has_hollowed)
-//            m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior());
-
         m_active_inst_bb_radius =
             mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius();
     }
@@ -437,46 +375,27 @@ void ObjectClipper::render_cut() const
 {
     if (m_clp_ratio == 0.)
         return;
+
     const SelectionInfo* sel_info = get_pool()->selection_info();
-    int sel_instance_idx = sel_info->get_active_instance();
-    if (sel_instance_idx < 0)
-        return;
-    const ModelObject* mo = sel_info->model_object();
-    const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation();
-
-    size_t clipper_id = 0;
-    for (const ModelVolume* mv : mo->volumes) {
-        const Geometry::Transformation vol_trafo  = mv->get_transformation();
-        Geometry::Transformation trafo = inst_trafo * vol_trafo;
+    const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation();
+    for (auto& clipper : m_clippers) {
+        Geometry::Transformation trafo = inst_trafo * clipper.second;
         trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
-
-        auto& clipper = m_clippers[clipper_id];
-        clipper->set_plane(*m_clp);
-        clipper->set_transformation(trafo);
-        clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
-#if ENABLE_LEGACY_OPENGL_REMOVAL
-        clipper->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f });
-        clipper->render_contour({ 1.f, 1.f, 1.f, 1.f});
-#else
-        glsafe(::glPushMatrix());
-        glsafe(::glColor3f(1.0f, 0.37f, 0.0f));
-        clipper->render_cut();
-        glsafe(::glColor3f(1.f, 1.f, 1.f));
-        clipper->render_contour();
-#endif // ENABLE_LEGACY_OPENGL_REMOVAL
-
-        ++clipper_id;
+        clipper.first->set_plane(*m_clp);
+        clipper.first->set_transformation(trafo);
+        clipper.first->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
+        clipper.first->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f });
     }
 }
 
 bool ObjectClipper::is_projection_inside_cut(const Vec3d& point) const
 {
-    return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const std::unique_ptr<MeshClipper>& cl) { return cl->is_projection_inside_cut(point); });
+    return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const auto& cl) { return cl.first->is_projection_inside_cut(point); });
 }
 
 bool ObjectClipper::has_valid_contour() const
 {
-    return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const std::unique_ptr<MeshClipper>& cl) { return cl->has_valid_contour(); });
+    return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); });
 }
 
 void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal)
@@ -514,13 +433,13 @@ void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contou
 {
     m_hide_clipped = hide_clipped;
     for (auto& clipper : m_clippers)
-        clipper->set_behaviour(fill_cut, contour_width);
+        clipper.first->set_behaviour(fill_cut, contour_width);
 }
 
 void ObjectClipper::pass_mouse_click(const Vec3d& pt)
 {
     for (auto& clipper : m_clippers)
-        clipper->pass_mouse_click(pt);
+        clipper.first->pass_mouse_click(pt);
 }
 
 std::vector<Vec3d> ObjectClipper::get_disabled_contours() const
@@ -586,7 +505,6 @@ void SupportsClipper::render_cut() const
 {
     const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper();
     if (ocl->get_position() == 0.
-     || ! get_pool()->instances_hider()->are_supports_shown()
      || ! m_clipper)
         return;
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp
index d6c580123..aa9493d9d 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp
@@ -63,7 +63,6 @@ enum class CommonGizmosDataID {
     None                 = 0,
     SelectionInfo        = 1 << 0,
     InstancesHider       = 1 << 1,
-    HollowedMesh         = 1 << 2,
     Raycaster            = 1 << 3,
     ObjectClipper        = 1 << 4,
     SupportsClipper      = 1 << 5,
@@ -187,8 +186,7 @@ public:
     CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
 #endif // NDEBUG
 
-    void show_supports(bool show);
-    bool are_supports_shown() const { return m_show_supports; }
+    void set_hide_full_scene(bool hide);
     void render_cut() const;
 
 protected:
@@ -196,42 +194,13 @@ protected:
     void on_release() override;
 
 private:
-    bool m_show_supports = false;
+    bool m_hide_full_scene{ false };
     std::vector<const TriangleMesh*> m_old_meshes;
     std::vector<std::unique_ptr<MeshClipper>> m_clippers;
 };
 
 
 
-//class HollowedMesh : public CommonGizmosDataBase
-//{
-//public:
-//    explicit HollowedMesh(CommonGizmosDataPool* cgdp)
-//        : CommonGizmosDataBase(cgdp) {}
-//#ifndef NDEBUG
-//    CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
-//#endif // NDEBUG
-
-//    const sla::DrainHoles &get_drainholes() const { return m_drainholes; }
-
-//    const TriangleMesh* get_hollowed_mesh() const;
-//    const TriangleMesh* get_hollowed_interior() const;
-
-//protected:
-//    void on_update() override;
-//    void on_release() override;
-
-//private:
-//    std::unique_ptr<TriangleMesh> m_hollowed_mesh_transformed;
-//    std::unique_ptr<TriangleMesh> m_hollowed_interior_transformed;
-//    size_t m_old_hollowing_timestamp = 0;
-//    int m_print_object_idx = -1;
-//    int m_print_objects_count = 0;
-//    sla::DrainHoles m_drainholes;
-//};
-
-
-
 class Raycaster : public CommonGizmosDataBase
 {
 public:
@@ -251,6 +220,8 @@ protected:
 private:
     std::vector<std::unique_ptr<MeshRaycaster>> m_raycasters;
     std::vector<const TriangleMesh*> m_old_meshes;
+    // Used to store the sla mesh coming from the backend
+    TriangleMesh m_sla_mesh_cache;
 };
 
 
@@ -285,7 +256,9 @@ protected:
 
 private:
     std::vector<const TriangleMesh*> m_old_meshes;
-    std::vector<std::unique_ptr<MeshClipper>> m_clippers;
+    // Used to store the sla mesh coming from the backend
+    TriangleMesh m_sla_mesh_cache;
+    std::vector<std::pair<std::unique_ptr<MeshClipper>, Geometry::Transformation>> m_clippers;
     std::unique_ptr<ClippingPlane> m_clp;
     double m_clp_ratio = 0.;
     double m_active_inst_bb_radius = 0.;
diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp
index f309d6eaa..9e726b7f4 100644
--- a/src/slic3r/GUI/SceneRaycaster.cpp
+++ b/src/slic3r/GUI/SceneRaycaster.cpp
@@ -100,7 +100,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
     };
 
 #if ENABLE_RAYCAST_PICKING_DEBUG
-    m_last_hit.reset();
+    const_cast<std::optional<HitResult>*>(&m_last_hit)->reset();
 #endif // ENABLE_RAYCAST_PICKING_DEBUG
 
     HitResult ret;
@@ -142,7 +142,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
         ret.raycaster_id = decode_id(ret.type, ret.raycaster_id);
 
 #if ENABLE_RAYCAST_PICKING_DEBUG
-    m_last_hit = ret;
+    *const_cast<std::optional<HitResult>*>(&m_last_hit) = ret;
 #endif // ENABLE_RAYCAST_PICKING_DEBUG
     return ret;
 }