Merge branch 'tm_hollowing_optimization_2'

This commit is contained in:
tamasmeszaros 2022-04-26 14:51:39 +02:00
commit dea3c2cea3
4 changed files with 122 additions and 60 deletions

View File

@ -14,6 +14,7 @@
#include <openvdb/tools/VolumeToMesh.h> #include <openvdb/tools/VolumeToMesh.h>
#include <openvdb/tools/Composite.h> #include <openvdb/tools/Composite.h>
#include <openvdb/tools/LevelSetRebuild.h> #include <openvdb/tools/LevelSetRebuild.h>
#include <openvdb/tools/FastSweeping.h>
//#include "MTUtils.hpp" //#include "MTUtils.hpp"
@ -42,16 +43,13 @@ public:
: its{m}, voxel_scale{voxel_sc} {}; : 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, openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh,
const openvdb::math::Transform &tr, const openvdb::math::Transform &tr,
float voxel_scale, float voxel_scale,
float exteriorBandWidth, float exteriorBandWidth,
float interiorBandWidth, float interiorBandWidth)
int flags)
{ {
// Might not be needed but this is now proven to be working
openvdb::initialize(); openvdb::initialize();
std::vector<indexed_triangle_set> meshparts = its_split(mesh); 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; openvdb::FloatGrid::Ptr grid;
for (auto &m : meshparts) { for (auto &m : meshparts) {
auto subgrid = openvdb::tools::meshToVolume<openvdb::FloatGrid>( auto subgrid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
TriangleMeshDataAdapter{m, voxel_scale}, tr, exteriorBandWidth, TriangleMeshDataAdapter{m, voxel_scale}, tr, 1.f, 1.f);
interiorBandWidth, flags);
if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid); if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid);
else if (subgrid) grid = std::move(subgrid); else if (subgrid) grid = std::move(subgrid);
} }
if (grid) { if (meshparts.size() > 1) {
grid = openvdb::tools::levelSetRebuild(*grid, 0., exteriorBandWidth, // This is needed to avoid various artefacts on multipart meshes.
interiorBandWidth); // TODO: replace with something faster
} else if(meshparts.empty()) { grid = openvdb::tools::levelSetRebuild(*grid, 0., 1.f, 1.f);
}
if(meshparts.empty()) {
// Splitting failed, fall back to hollow the original mesh // Splitting failed, fall back to hollow the original mesh
grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>( grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth, TriangleMeshDataAdapter{mesh}, tr, 1.f, 1.f);
interiorBandWidth, flags);
} }
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)); grid->insertMeta("voxel_scale", openvdb::FloatMetadata(voxel_scale));
return grid; return grid;
@ -133,4 +143,15 @@ openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
return new_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 } // namespace Slic3r

View File

@ -30,18 +30,20 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh,
const openvdb::math::Transform &tr = {}, const openvdb::math::Transform &tr = {},
float voxel_scale = 1.f, float voxel_scale = 1.f,
float exteriorBandWidth = 3.0f, float exteriorBandWidth = 3.0f,
float interiorBandWidth = 3.0f, float interiorBandWidth = 3.0f);
int flags = 0);
indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid, indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid,
double isovalue = 0.0, double isovalue = 0.0,
double adaptivity = 0.0, double adaptivity = 0.0,
bool relaxDisorientedTriangles = true); bool relaxDisorientedTriangles = true);
openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
double iso);
openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
double iso, double iso,
double ext_range = 3., double ext_range,
double int_range = 3.); double int_range);
} // namespace Slic3r } // namespace Slic3r

View File

@ -12,6 +12,8 @@
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <openvdb/tools/FastSweeping.h>
#include <libslic3r/MTUtils.hpp> #include <libslic3r/MTUtils.hpp>
#include <libslic3r/I18N.hpp> #include <libslic3r/I18N.hpp>
@ -27,12 +29,10 @@ struct Interior {
openvdb::FloatGrid::Ptr gridptr; openvdb::FloatGrid::Ptr gridptr;
mutable std::optional<openvdb::FloatGrid::ConstAccessor> accessor; mutable std::optional<openvdb::FloatGrid::ConstAccessor> accessor;
double closing_distance = 0.; double iso_surface = 0.;
double thickness = 0.; double thickness = 0.;
double voxel_scale = 1.; double voxel_scale = 1.;
double nb_in = 3.; // narrow band width inwards double full_narrowb = 2.;
double nb_out = 3.; // narrow band width outwards
// Full narrow band is the sum of the two above values.
void reset_accessor() const // This resets the accessor and its cache void reset_accessor() const // This resets the accessor and its cache
// Not a thread safe call! // Not a thread safe call!
@ -63,10 +63,11 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh,
double voxel_scale, double voxel_scale,
double closing_dist) double closing_dist)
{ {
double offset = voxel_scale * min_thickness; double offset = voxel_scale * min_thickness;
double D = voxel_scale * closing_dist; double D = voxel_scale * closing_dist;
float out_range = 0.1f * float(offset);
float in_range = 1.1f * float(offset + D); float in_range = 1.1f * float(offset + D);
auto narrowb = 1.;
float out_range = narrowb;
if (ctl.stopcondition()) return {}; if (ctl.stopcondition()) return {};
else ctl.statuscb(0, L("Hollowing")); else ctl.statuscb(0, L("Hollowing"));
@ -84,8 +85,21 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh,
else ctl.statuscb(30, L("Hollowing")); else ctl.statuscb(30, L("Hollowing"));
double iso_surface = D; double iso_surface = D;
auto narrowb = double(in_range); if (D > EPSILON) {
gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, narrowb); 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 {}; if (ctl.stopcondition()) return {};
else ctl.statuscb(70, L("Hollowing")); else ctl.statuscb(70, L("Hollowing"));
@ -99,11 +113,10 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh,
if (ctl.stopcondition()) return {}; if (ctl.stopcondition()) return {};
else ctl.statuscb(100, L("Hollowing")); else ctl.statuscb(100, L("Hollowing"));
interior->closing_distance = D; interior->iso_surface = iso_surface;
interior->thickness = offset; interior->thickness = offset;
interior->voxel_scale = voxel_scale; interior->voxel_scale = voxel_scale;
interior->nb_in = narrowb; interior->full_narrowb = out_range + in_range;
interior->nb_out = narrowb;
return interior; return interior;
} }
@ -112,8 +125,9 @@ InteriorPtr generate_interior(const TriangleMesh & mesh,
const HollowingConfig &hc, const HollowingConfig &hc,
const JobController & ctl) const JobController & ctl)
{ {
static const double MIN_OVERSAMPL = 3.5; static constexpr double MIN_SAMPLES_IN_WALL = 3.5;
static const double MAX_OVERSAMPL = 8.; 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 // 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 // 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 // scales the whole geometry down, and doesn't increase the number of
// voxels. // voxels.
// //
// max 8x upscale, min is native voxel size // First an allowed range for voxel scale is determined from an initial
auto voxel_scale = MIN_OVERSAMPL + (MAX_OVERSAMPL - MIN_OVERSAMPL) * hc.quality; // 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 = BOOST_LOG_TRIVIAL(debug) << "Hollowing: max oversampl will be: " << max_oversampl_scaled;
generate_interior_verbose(mesh, ctl, hc.min_thickness, voxel_scale, BOOST_LOG_TRIVIAL(debug) << "Hollowing: voxel scale will be: " << voxel_scale;
hc.closing_distance); 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()) { 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) static double get_distance(const TriangleBubble &b, const Interior &interior)
{ {
double R = b.R * interior.voxel_scale; 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) || return D > interior.full_narrowb ||
(D < 0. && R >= interior.nb_in) || ((Dst - R) < 0. && 2 * R > interior.thickness) ?
((D - R) < 0. && 2 * R > interior.thickness) ?
std::nan("") : std::nan("") :
// FIXME: Adding interior.voxel_scale is a compromise supposed Dst - interior.iso_surface;
// 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*/;
} }
double get_distance(const Vec3f &p, const Interior &interior) 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; 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 if (f.parent != NEW_FACE) // Top parent needs to be removed as well
mesh_mods.to_remove[f.parent] = true; 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. // (inside the wall being invisible), no further division is needed.
if ((R + D) < interior.thickness) if ((R + D) < interior.thickness)
return false; return false;

View File

@ -21,6 +21,8 @@
#include "I18N.hpp" #include "I18N.hpp"
#include <libnest2d/tools/benchmark.h>
//! macro used to mark string used at localization, //! macro used to mark string used at localization,
//! return same string //! return same string
#define L(s) Slic3r::I18N::translate(s) #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. // Create exclude mask for triangle removal inside hollowed interiors.
// This is necessary when the interior is already part of the mesh which was // 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 // 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}; 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) { for (size_t fi = 0; fi < its.indices.size(); ++fi) {
auto &face = its.indices[fi]; auto &face = its.indices[fi];
@ -249,7 +258,7 @@ static std::vector<bool> create_exclude_mask(
} }
if (exclude_mask[fi]) { if (exclude_mask[fi]) {
exclude_neighbors(face); exclude_neighbors(face, exclude_mask, its, neighbor_index, 1);
continue; continue;
} }
@ -294,7 +303,7 @@ static std::vector<bool> create_exclude_mask(
if (D_hole < D_tol && std::abs(dot) < normal_angle_tol) { if (D_hole < D_tol && std::abs(dot) < normal_angle_tol) {
exclude_mask[fi] = true; 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) void SLAPrint::Steps::execute(SLAPrintObjectStep step, SLAPrintObject &obj)
{ {
switch(step) { 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 slaposDrillHoles: drill_holes(obj); break;
case slaposObjectSlice: slice_model(obj); break; case slaposObjectSlice: slice_model(obj); break;
case slaposSupportPoints: support_points(obj); break; case slaposSupportPoints: support_points(obj); break;