diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp
index 72c7668a4..2c207bb6a 100644
--- a/src/libslic3r/OpenVDBUtils.cpp
+++ b/src/libslic3r/OpenVDBUtils.cpp
@@ -14,6 +14,7 @@
 #include <openvdb/tools/VolumeToMesh.h>
 #include <openvdb/tools/Composite.h>
 #include <openvdb/tools/LevelSetRebuild.h>
+#include <openvdb/tools/FastSweeping.h>
 
 //#include "MTUtils.hpp"
 
@@ -42,16 +43,13 @@ public:
         : its{m}, voxel_scale{voxel_sc} {};
 };
 
-// TODO: Do I need to call initialize? Seems to work without it as well but the
-// docs say it should be called ones. It does a mutex lock-unlock sequence all
-// even if was called previously.
 openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set &    mesh,
                                      const openvdb::math::Transform &tr,
                                      float voxel_scale,
                                      float exteriorBandWidth,
-                                     float interiorBandWidth,
-                                     int   flags)
+                                     float interiorBandWidth)
 {
+    // Might not be needed but this is now proven to be working
     openvdb::initialize();
 
     std::vector<indexed_triangle_set> meshparts = its_split(mesh);
@@ -64,23 +62,35 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set &    mesh,
     openvdb::FloatGrid::Ptr grid;
     for (auto &m : meshparts) {
         auto subgrid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
-            TriangleMeshDataAdapter{m, voxel_scale}, tr, exteriorBandWidth,
-            interiorBandWidth, flags);
+            TriangleMeshDataAdapter{m, voxel_scale}, tr, 1.f, 1.f);
 
         if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid);
         else if (subgrid) grid = std::move(subgrid);
     }
 
-    if (grid) {
-        grid = openvdb::tools::levelSetRebuild(*grid, 0., exteriorBandWidth,
-                                               interiorBandWidth);
-    } else if(meshparts.empty()) {
+    if (meshparts.size() > 1) {
+        // This is needed to avoid various artefacts on multipart meshes.
+        // TODO: replace with something faster
+        grid = openvdb::tools::levelSetRebuild(*grid, 0., 1.f, 1.f);
+    }
+    if(meshparts.empty()) {
         // Splitting failed, fall back to hollow the original mesh
         grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
-            TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth,
-            interiorBandWidth, flags);
+            TriangleMeshDataAdapter{mesh}, tr, 1.f, 1.f);
     }
 
+    constexpr int DilateIterations = 1;
+
+    grid = openvdb::tools::dilateSdf(
+        *grid, interiorBandWidth, openvdb::tools::NN_FACE_EDGE,
+        DilateIterations,
+        openvdb::tools::FastSweepingDomain::SWEEP_LESS_THAN_ISOVALUE);
+
+    grid = openvdb::tools::dilateSdf(
+        *grid, exteriorBandWidth, openvdb::tools::NN_FACE_EDGE,
+        DilateIterations,
+        openvdb::tools::FastSweepingDomain::SWEEP_GREATER_THAN_ISOVALUE);
+
     grid->insertMeta("voxel_scale", openvdb::FloatMetadata(voxel_scale));
 
     return grid;
@@ -133,4 +143,15 @@ openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
     return new_grid;
 }
 
+openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
+                                        double                    iso)
+{
+    auto new_grid = openvdb::tools::levelSetRebuild(grid, float(iso));
+
+       // Copies voxel_scale metadata, if it exists.
+    new_grid->insertMeta(*grid.deepCopyMeta());
+
+    return new_grid;
+}
+
 } // namespace Slic3r
diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp
index 490b3e222..254ae3583 100644
--- a/src/libslic3r/OpenVDBUtils.hpp
+++ b/src/libslic3r/OpenVDBUtils.hpp
@@ -30,18 +30,20 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set &    mesh,
                                      const openvdb::math::Transform &tr = {},
                                      float voxel_scale                  = 1.f,
                                      float exteriorBandWidth = 3.0f,
-                                     float interiorBandWidth = 3.0f,
-                                     int   flags             = 0);
+                                     float interiorBandWidth = 3.0f);
 
 indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid,
                                   double                    isovalue   = 0.0,
                                   double                    adaptivity = 0.0,
                                   bool relaxDisorientedTriangles = true);
 
+openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
+                                        double                    iso);
+
 openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
                                         double                    iso,
-                                        double ext_range = 3.,
-                                        double int_range = 3.);
+                                        double ext_range,
+                                        double int_range);
 
 } // namespace Slic3r
 
diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp
index b0d33a0ca..8cda341b5 100644
--- a/src/libslic3r/SLA/Hollowing.cpp
+++ b/src/libslic3r/SLA/Hollowing.cpp
@@ -12,6 +12,8 @@
 
 #include <boost/log/trivial.hpp>
 
+#include <openvdb/tools/FastSweeping.h>
+
 #include <libslic3r/MTUtils.hpp>
 #include <libslic3r/I18N.hpp>
 
@@ -27,12 +29,10 @@ struct Interior {
     openvdb::FloatGrid::Ptr gridptr;
     mutable std::optional<openvdb::FloatGrid::ConstAccessor> accessor;
 
-    double closing_distance = 0.;
+    double iso_surface = 0.;
     double thickness = 0.;
     double voxel_scale = 1.;
-    double nb_in = 3.;  // narrow band width inwards
-    double nb_out = 3.; // narrow band width outwards
-    // Full narrow band is the sum of the two above values.
+    double full_narrowb = 2.;
 
     void reset_accessor() const  // This resets the accessor and its cache
     // Not a thread safe call!
@@ -63,10 +63,11 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh,
                                              double voxel_scale,
                                              double closing_dist)
 {
-    double offset = voxel_scale * min_thickness;
-    double D = voxel_scale * closing_dist;
-    float  out_range = 0.1f * float(offset);
+    double offset   = voxel_scale * min_thickness;
+    double D        = voxel_scale * closing_dist;
     float  in_range = 1.1f * float(offset + D);
+    auto   narrowb  = 1.;
+    float  out_range = narrowb;
 
     if (ctl.stopcondition()) return {};
     else ctl.statuscb(0, L("Hollowing"));
@@ -84,8 +85,21 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh,
     else ctl.statuscb(30, L("Hollowing"));
 
     double iso_surface = D;
-    auto   narrowb = double(in_range);
-    gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, narrowb);
+    if (D > EPSILON) {
+        in_range = narrowb;
+        gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, in_range);
+
+        constexpr int DilateIterations = 1;
+
+        gridptr = openvdb::tools::dilateSdf(
+            *gridptr, std::ceil(iso_surface),
+            openvdb::tools::NN_FACE_EDGE_VERTEX, DilateIterations,
+            openvdb::tools::FastSweepingDomain::SWEEP_GREATER_THAN_ISOVALUE);
+
+        out_range = iso_surface;
+    } else {
+        iso_surface = -offset;
+    }
 
     if (ctl.stopcondition()) return {};
     else ctl.statuscb(70, L("Hollowing"));
@@ -99,11 +113,10 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh,
     if (ctl.stopcondition()) return {};
     else ctl.statuscb(100, L("Hollowing"));
 
-    interior->closing_distance = D;
-    interior->thickness = offset;
+    interior->iso_surface = iso_surface;
+    interior->thickness   = offset;
     interior->voxel_scale = voxel_scale;
-    interior->nb_in = narrowb;
-    interior->nb_out = narrowb;
+    interior->full_narrowb = out_range + in_range;
 
     return interior;
 }
@@ -112,8 +125,9 @@ InteriorPtr generate_interior(const TriangleMesh &   mesh,
                               const HollowingConfig &hc,
                               const JobController &  ctl)
 {
-    static const double MIN_OVERSAMPL = 3.5;
-    static const double MAX_OVERSAMPL = 8.;
+    static constexpr double MIN_SAMPLES_IN_WALL = 3.5;
+    static constexpr double MAX_OVERSAMPL = 8.;
+    static constexpr double UNIT_VOLUME   = 500000; // empiric
 
     // I can't figure out how to increase the grid resolution through openvdb
     // API so the model will be scaled up before conversion and the result
@@ -121,12 +135,25 @@ InteriorPtr generate_interior(const TriangleMesh &   mesh,
     // scales the whole geometry down, and doesn't increase the number of
     // voxels.
     //
-    // max 8x upscale, min is native voxel size
-    auto voxel_scale = MIN_OVERSAMPL + (MAX_OVERSAMPL - MIN_OVERSAMPL) * hc.quality;
+    // First an allowed range for voxel scale is determined from an initial
+    // range of <MIN_SAMPLES_IN_WALL, MAX_OVERSAMPL>. The final voxel scale is
+    // then chosen from this range using the 'quality:<0, 1>' parameter.
+    // The minimum can be lowered if the wall thickness is great enough and
+    // the maximum is lowered if the model volume very big.
+    double mesh_vol      = its_volume(mesh.its);
+    double sc_divider    = std::max(1.0, (mesh_vol / UNIT_VOLUME));
+    double min_oversampl = std::max(MIN_SAMPLES_IN_WALL / hc.min_thickness, 1.);
+    double max_oversampl_scaled = std::max(min_oversampl, MAX_OVERSAMPL / sc_divider);
+    auto   voxel_scale          = min_oversampl + (max_oversampl_scaled - min_oversampl) * hc.quality;
 
-    InteriorPtr interior =
-        generate_interior_verbose(mesh, ctl, hc.min_thickness, voxel_scale,
-                                  hc.closing_distance);
+    BOOST_LOG_TRIVIAL(debug) << "Hollowing: max oversampl will be: " << max_oversampl_scaled;
+    BOOST_LOG_TRIVIAL(debug) << "Hollowing: voxel scale will be: " << voxel_scale;
+    BOOST_LOG_TRIVIAL(debug) << "Hollowing: mesh volume is: " << mesh_vol;
+
+    InteriorPtr interior = generate_interior_verbose(mesh, ctl,
+                                                     hc.min_thickness,
+                                                     voxel_scale,
+                                                     hc.closing_distance);
 
     if (interior && !interior->mesh.empty()) {
 
@@ -342,22 +369,18 @@ struct TriangleBubble { Vec3f center; double R; };
 static double get_distance(const TriangleBubble &b, const Interior &interior)
 {
     double R = b.R * interior.voxel_scale;
-    double D = get_distance_raw(b.center, interior);
+    double D = 2. * R;
+    double Dst = get_distance_raw(b.center, interior);
 
-    return (D > 0. && R >= interior.nb_out) ||
-           (D < 0. && R >= interior.nb_in)  ||
-           ((D - R) < 0. && 2 * R > interior.thickness) ?
+    return D > interior.full_narrowb ||
+           ((Dst - R) < 0. && 2 * R > interior.thickness) ?
                 std::nan("") :
-                // FIXME: Adding interior.voxel_scale is a compromise supposed
-                // to prevent the deletion of the triangles forming the interior
-                // itself. This has a side effect that a small portion of the
-                // bad triangles will still be visible.
-                D - interior.closing_distance /*+ 2 * interior.voxel_scale*/;
+                Dst - interior.iso_surface;
 }
 
 double get_distance(const Vec3f &p, const Interior &interior)
 {
-    double d = get_distance_raw(p, interior) - interior.closing_distance;
+    double d = get_distance_raw(p, interior) - interior.iso_surface;
     return d / interior.voxel_scale;
 }
 
@@ -494,7 +517,7 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
             if (f.parent != NEW_FACE) // Top parent needs to be removed as well
                 mesh_mods.to_remove[f.parent] = true;
 
-            // If the outside part is between the interior end the exterior
+            // If the outside part is between the interior and the exterior
             // (inside the wall being invisible), no further division is needed.
             if ((R + D) < interior.thickness)
                 return false;
diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp
index fa7348781..dfede793c 100644
--- a/src/libslic3r/SLAPrintSteps.cpp
+++ b/src/libslic3r/SLAPrintSteps.cpp
@@ -21,6 +21,8 @@
 
 #include "I18N.hpp"
 
+#include <libnest2d/tools/benchmark.h>
+
 //! macro used to mark string used at localization,
 //! return same string
 #define L(s) Slic3r::I18N::translate(s)
@@ -215,6 +217,22 @@ struct FaceHash {
     }
 };
 
+static void exclude_neighbors(const Vec3i                &face,
+                              std::vector<bool>          &mask,
+                              const indexed_triangle_set &its,
+                              const VertexFaceIndex      &index,
+                              size_t                      recursions)
+{
+    for (int i = 0; i < 3; ++i) {
+        const auto &neighbors_range = index[face(i)];
+        for (size_t fi_n : neighbors_range) {
+            mask[fi_n] = true;
+            if (recursions > 0)
+                exclude_neighbors(its.indices[fi_n], mask, its, index, recursions - 1);
+        }
+    }
+}
+
 // Create exclude mask for triangle removal inside hollowed interiors.
 // This is necessary when the interior is already part of the mesh which was
 // drilled using CGAL mesh boolean operation. Excluded will be the triangles
@@ -231,15 +249,6 @@ static std::vector<bool> create_exclude_mask(
 
     VertexFaceIndex neighbor_index{its};
 
-    auto exclude_neighbors = [&neighbor_index, &exclude_mask](const Vec3i &face)
-    {
-        for (int i = 0; i < 3; ++i) {
-            const auto &neighbors_range = neighbor_index[face(i)];
-            for (size_t fi_n : neighbors_range)
-                exclude_mask[fi_n] = true;
-        }
-    };
-
     for (size_t fi = 0; fi < its.indices.size(); ++fi) {
         auto &face = its.indices[fi];
 
@@ -249,7 +258,7 @@ static std::vector<bool> create_exclude_mask(
         }
 
         if (exclude_mask[fi]) {
-            exclude_neighbors(face);
+            exclude_neighbors(face, exclude_mask, its, neighbor_index, 1);
             continue;
         }
 
@@ -294,7 +303,7 @@ static std::vector<bool> create_exclude_mask(
 
             if (D_hole < D_tol && std::abs(dot) < normal_angle_tol) {
                 exclude_mask[fi] = true;
-                exclude_neighbors(face);
+                exclude_neighbors(face, exclude_mask, its, neighbor_index, 1);
             }
         }
     }