diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp
index b11c570f6..76aa36194 100644
--- a/src/libslic3r/AABBTreeIndirect.hpp
+++ b/src/libslic3r/AABBTreeIndirect.hpp
@@ -11,6 +11,8 @@
 #include <type_traits>
 #include <vector>
 
+#include <Eigen/Geometry>
+
 #include "Utils.hpp" // for next_highest_power_of_2()
 
 extern "C"
@@ -752,6 +754,83 @@ void get_candidate_idxs(const TreeType& tree, const VectorType& v, std::vector<s
     return;
 }
 
+// Predicate: need to be specialized for intersections of different geomteries
+template<class G> struct Intersecting {};
+
+// Intersection predicate specialization for box-box intersections
+template<class CoordType, int NumD>
+struct Intersecting<Eigen::AlignedBox<CoordType, NumD>> {
+    Eigen::AlignedBox<CoordType, NumD> box;
+
+    Intersecting(const Eigen::AlignedBox<CoordType, NumD> &bb): box{bb} {}
+
+    bool operator() (const typename Tree<NumD, CoordType>::Node &node) const
+    {
+        return box.intersects(node.bbox);
+    }
+};
+
+template<class G> auto intersecting(const G &g) { return Intersecting<G>{g}; }
+
+template<class G> struct Containing {};
+
+// Intersection predicate specialization for box-box intersections
+template<class CoordType, int NumD>
+struct Containing<Eigen::AlignedBox<CoordType, NumD>> {
+    Eigen::AlignedBox<CoordType, NumD> box;
+
+    Containing(const Eigen::AlignedBox<CoordType, NumD> &bb): box{bb} {}
+
+    bool operator() (const typename Tree<NumD, CoordType>::Node &node) const
+    {
+        return box.contains(node.bbox);
+    }
+};
+
+template<class G> auto containing(const G &g) { return Containing<G>{g}; }
+
+namespace detail {
+
+template<int Dims, typename T, typename Pred, typename Fn>
+void traverse_recurse(const Tree<Dims, T> &tree,
+                      size_t               idx,
+                      Pred &&              pred,
+                      Fn &&                callback)
+{
+    assert(tree.node(idx).is_valid());
+
+    if (!pred(tree.node(idx))) return;
+
+    if (tree.node(idx).is_leaf()) {
+        callback(tree.node(idx).idx);
+    } else {
+
+        // call this with left and right node idx:
+        auto trv = [&](size_t idx) {
+            traverse_recurse(tree, idx, std::forward<Pred>(pred),
+                             std::forward<Fn>(callback));
+        };
+
+        // Left / right child node index.
+        trv(Tree<Dims, T>::left_child_idx(idx));
+        trv(Tree<Dims, T>::right_child_idx(idx));
+    }
+}
+
+} // namespace detail
+
+// Tree traversal with a predicate. Example usage:
+// traverse(tree, intersecting(QueryBox), [](size_t face_idx) {
+//      /* ... */
+// });
+template<int Dims, typename T, typename Predicate, typename Fn>
+void traverse(const Tree<Dims, T> &tree, Predicate &&pred, Fn &&callback)
+{
+    if (tree.empty()) return;
+
+    detail::traverse_recurse(tree, size_t(0), std::forward<Predicate>(pred),
+                             std::forward<Fn>(callback));
+}
 
 } // namespace AABBTreeIndirect
 } // namespace Slic3r
diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp
index 4b18d6c1c..5a518d580 100644
--- a/src/libslic3r/MeshBoolean.cpp
+++ b/src/libslic3r/MeshBoolean.cpp
@@ -110,15 +110,18 @@ struct CGALMesh { _EpicMesh m; };
 // Converions from and to CGAL mesh
 // /////////////////////////////////////////////////////////////////////////////
 
-template<class _Mesh> void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &out)
-{ 
-    if (M.empty()) return;
+template<class _Mesh>
+void triangle_mesh_to_cgal(const std::vector<stl_vertex> &                 V,
+                           const std::vector<stl_triangle_vertex_indices> &F,
+                           _Mesh &out)
+{
+    if (F.empty()) return;
 
-    for (auto &v : M.its.vertices)
+    for (auto &v : V)
         out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()});
 
     using VI = typename _Mesh::Vertex_index;
-    for (auto &f : M.its.indices)
+    for (auto &f : F)
         out.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
 }
 
@@ -155,14 +158,16 @@ template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
     }
     
     TriangleMesh out{points, facets};
-    out.require_shared_vertices();
+    out.repair();
     return out;
 }
 
-std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M)
+std::unique_ptr<CGALMesh, CGALMeshDeleter>
+triangle_mesh_to_cgal(const std::vector<stl_vertex> &V,
+                      const std::vector<stl_triangle_vertex_indices> &F)
 {
     std::unique_ptr<CGALMesh, CGALMeshDeleter> out(new CGALMesh{});
-    triangle_mesh_to_cgal(M, out->m);
+    triangle_mesh_to_cgal(V, F, out->m);
     return out;
 }
 
@@ -221,8 +226,8 @@ template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const Triangl
 {
     CGALMesh meshA;
     CGALMesh meshB;
-    triangle_mesh_to_cgal(A, meshA.m);
-    triangle_mesh_to_cgal(B, meshB.m);
+    triangle_mesh_to_cgal(A.its.vertices, A.its.indices, meshA.m);
+    triangle_mesh_to_cgal(B.its.vertices, B.its.indices, meshB.m);
     
     _cgal_do(op, meshA, meshB);
     
@@ -247,7 +252,7 @@ void intersect(TriangleMesh &A, const TriangleMesh &B)
 bool does_self_intersect(const TriangleMesh &mesh)
 {
     CGALMesh cgalm;
-    triangle_mesh_to_cgal(mesh, cgalm.m);
+    triangle_mesh_to_cgal(mesh.its.vertices, mesh.its.indices, cgalm.m);
     return CGALProc::does_self_intersect(cgalm.m);
 }
 
diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp
index ce17a1328..8e88e2536 100644
--- a/src/libslic3r/MeshBoolean.hpp
+++ b/src/libslic3r/MeshBoolean.hpp
@@ -27,7 +27,19 @@ namespace cgal {
 struct CGALMesh;
 struct CGALMeshDeleter { void operator()(CGALMesh *ptr); };
 
-std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M);
+std::unique_ptr<CGALMesh, CGALMeshDeleter>
+triangle_mesh_to_cgal(const std::vector<stl_vertex> &V,
+                      const std::vector<stl_triangle_vertex_indices> &F);
+
+inline std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const indexed_triangle_set &M)
+{
+    return triangle_mesh_to_cgal(M.vertices, M.indices);
+}
+inline std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M)
+{
+    return triangle_mesh_to_cgal(M.its);
+}
+
 TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh);
     
 // Do boolean mesh difference with CGAL bypassing igl.
diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp
index 4d8abb798..fc6686201 100644
--- a/src/libslic3r/SLAPrintSteps.cpp
+++ b/src/libslic3r/SLAPrintSteps.cpp
@@ -12,6 +12,7 @@
 #include <libslic3r/SLA/SupportPointGenerator.hpp>
 
 #include <libslic3r/ElephantFootCompensation.hpp>
+#include <libslic3r/AABBTreeIndirect.hpp>
 
 #include <libslic3r/ClipperUtils.hpp>
 
@@ -272,6 +273,36 @@ static std::vector<bool> create_exclude_mask(
     return exclude_mask;
 }
 
+static indexed_triangle_set
+remove_unconnected_vertices(const indexed_triangle_set &its)
+{
+    if (its.indices.empty()) {};
+
+    indexed_triangle_set M;
+
+    std::vector<int> vtransl(its.vertices.size(), -1);
+    int vcnt = 0;
+    for (auto &f : its.indices) {
+
+        for (int i = 0; i < 3; ++i)
+            if (vtransl[size_t(f(i))] < 0) {
+
+                M.vertices.emplace_back(its.vertices[size_t(f(i))]);
+                vtransl[size_t(f(i))] = vcnt++;
+            }
+
+        std::array<int, 3> new_f = {
+            vtransl[size_t(f(0))],
+            vtransl[size_t(f(1))],
+            vtransl[size_t(f(2))]
+        };
+
+        M.indices.emplace_back(new_f[0], new_f[1], new_f[2]);
+    }
+
+    return M;
+}
+
 // Drill holes into the hollowed/original mesh.
 void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
 {
@@ -314,43 +345,83 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
 
     BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
     sla::DrainHoles drainholes = po.transformed_drainhole_points();
-    
-    auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh);
+
+    auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
+        hollowed_mesh.its.vertices,
+        hollowed_mesh.its.indices
+    );
+
+    std::uniform_real_distribution<float> dist(0., float(EPSILON));
+    auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}, {});
+    indexed_triangle_set part_to_drill = hollowed_mesh.its;
 
     bool hole_fail = false;
     for (size_t i = 0; i < drainholes.size(); ++i) {
-        const sla::DrainHole &holept = drainholes[i];
-        po.model_object()->sla_drain_holes[i].failed = false;
+        sla::DrainHole holept = drainholes[i];
 
+        holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)};
+        holept.normal.normalize();
+        holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)};
         TriangleMesh m = sla::to_triangle_mesh(holept.to_mesh());
         m.require_shared_vertices();
-        auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
 
-        try {
-            MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *cgal_m);
-        } catch (const std::runtime_error &) {
+        part_to_drill.indices.clear();
+        auto bb = m.bounding_box();
+        Eigen::AlignedBox<float, 3> ebb{bb.min.cast<float>(),
+                                        bb.max.cast<float>()};
+
+        AABBTreeIndirect::traverse(
+                    tree,
+                    AABBTreeIndirect::intersecting(ebb),
+                    [&part_to_drill, &hollowed_mesh](size_t faceid)
+        {
+            part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[faceid]);
+        });
+
+        auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal(
+            remove_unconnected_vertices(part_to_drill));
+
+        if (MeshBoolean::cgal::does_self_intersect(*cgal_meshpart)) {
             BOOST_LOG_TRIVIAL(error) << "Failed to drill hole";
 
             hole_fail = drainholes[i].failed =
                     po.model_object()->sla_drain_holes[i].failed = true;
+
+            continue;
         }
+
+        auto cgal_hole = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
+        MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_hole);
+    }
+
+    if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal))
+        throw Slic3r::SlicingError(L("Too many overlapping holes."));
+
+    auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh);
+
+    try {
+        MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal);
+
+        hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal);
+        mesh_view = hollowed_mesh;
+
+        if (is_hollowed) {
+            auto &interior = *po.m_hollowing_data->interior;
+            std::vector<bool> exclude_mask =
+                    create_exclude_mask(mesh_view.its, interior, drainholes);
+
+            sla::remove_inside_triangles(mesh_view, interior, exclude_mask);
+        }
+
+    } catch (const std::runtime_error &) {
+        throw Slic3r::SlicingError(L(
+            "Drilling holes into the mesh failed. "
+            "This is usually caused by broken model. Try to fix it first."));
     }
 
     if (hole_fail)
         po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
                                    L("Failed to drill some holes into the model"));
-
-
-    hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal);
-    mesh_view     = hollowed_mesh;
-
-    if (is_hollowed) {
-        auto &interior = *po.m_hollowing_data->interior;
-        std::vector<bool> exclude_mask =
-                create_exclude_mask(mesh_view.its, interior, drainholes);
-
-        sla::remove_inside_triangles(mesh_view, interior, exclude_mask);
-    }
 }
 
 // The slicing will be performed on an imaginary 1D grid which starts from