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 #include #include +#include //#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 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( - 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( - 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 +#include + #include #include @@ -27,12 +29,10 @@ struct Interior { openvdb::FloatGrid::Ptr gridptr; mutable std::optional 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 . 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 435e8c8e3..b8aa62064 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -21,6 +21,8 @@ #include "I18N.hpp" +#include + //! 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 &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 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 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 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); } } } @@ -1116,7 +1125,14 @@ double SLAPrint::Steps::progressrange(SLAPrintStep step) const void SLAPrint::Steps::execute(SLAPrintObjectStep step, SLAPrintObject &obj) { switch(step) { - case slaposHollowing: hollow_model(obj); break; + case slaposHollowing: { + Benchmark bench; + bench.start(); + hollow_model(obj); + bench.stop(); + std::cout << "Hollowing took " << bench.getElapsedSec() << " seconds" << std::endl; + break; + } case slaposDrillHoles: drill_holes(obj); break; case slaposObjectSlice: slice_model(obj); break; case slaposSupportPoints: support_points(obj); break;