Full support of csg op stack. Preferring cgal in preview if feasible

This commit is contained in:
tamasmeszaros 2022-12-21 18:09:31 +01:00
parent d75b951c39
commit 1efc8191a2
8 changed files with 162 additions and 78 deletions

View File

@ -79,7 +79,10 @@ struct CSGPart {
CSGPart(AnyPtr<const indexed_triangle_set> ptr = {}, CSGPart(AnyPtr<const indexed_triangle_set> ptr = {},
CSGType op = CSGType::Union, CSGType op = CSGType::Union,
const Transform3f &tr = Transform3f::Identity()) const Transform3f &tr = Transform3f::Identity())
: its_ptr{std::move(ptr)}, operation{op}, trafo{tr} : its_ptr{std::move(ptr)}
, operation{op}
, stack_operation{CSGStackOp::Continue}
, trafo{tr}
{} {}
}; };

View File

@ -7,6 +7,7 @@
#include "CSGMesh.hpp" #include "CSGMesh.hpp"
#include "libslic3r/Execution/ExecutionTBB.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp"
//#include "libslic3r/Execution/ExecutionSeq.hpp"
#include "libslic3r/MeshBoolean.hpp" #include "libslic3r/MeshBoolean.hpp"
namespace Slic3r { namespace csg { namespace Slic3r { namespace csg {
@ -17,12 +18,21 @@ template<class CSGPartT>
MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart) MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart)
{ {
const indexed_triangle_set *its = csg::get_mesh(csgpart); const indexed_triangle_set *its = csg::get_mesh(csgpart);
indexed_triangle_set dummy;
if (!its)
its = &dummy;
MeshBoolean::cgal::CGALMeshPtr ret; MeshBoolean::cgal::CGALMeshPtr ret;
if (its) {
indexed_triangle_set m = *its; indexed_triangle_set m = *its;
its_transform(m, get_transform(csgpart)); its_transform(m, get_transform(csgpart));
try {
ret = MeshBoolean::cgal::triangle_mesh_to_cgal(m); ret = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
} catch (...) {
// errors are ignored, simply return null
ret = nullptr;
} }
return ret; return ret;
@ -34,6 +44,14 @@ using MeshBoolean::cgal::CGALMeshPtr;
inline void perform_csg(CSGType op, CGALMeshPtr &dst, CGALMeshPtr &src) inline void perform_csg(CSGType op, CGALMeshPtr &dst, CGALMeshPtr &src)
{ {
if (!dst && op == CSGType::Union && src) {
dst = std::move(src);
return;
}
if (!dst || !src)
return;
switch (op) { switch (op) {
case CSGType::Union: case CSGType::Union:
MeshBoolean::cgal::plus(*dst, *src); MeshBoolean::cgal::plus(*dst, *src);
@ -72,13 +90,17 @@ void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMeshPtr &cgalm,
using MeshBoolean::cgal::CGALMeshPtr; using MeshBoolean::cgal::CGALMeshPtr;
using namespace detail_cgal; using namespace detail_cgal;
struct Frame { CSGType op = CSGType::Union; CGALMeshPtr cgalptr; }; struct Frame {
CSGType op; CGALMeshPtr cgalptr;
explicit Frame(CSGType csgop = CSGType::Union)
: op{csgop}
, cgalptr{MeshBoolean::cgal::triangle_mesh_to_cgal(indexed_triangle_set{})}
{}
};
std::stack opstack{std::vector<Frame>{}}; std::stack opstack{std::vector<Frame>{}};
if (!csgrange.empty() && opstack.push(Frame{});
csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push)
opstack.push({});
std::vector<CGALMeshPtr> cgalmeshes = get_cgalptrs(ex_tbb, csgrange); std::vector<CGALMeshPtr> cgalmeshes = get_cgalptrs(ex_tbb, csgrange);
@ -89,17 +111,13 @@ void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMeshPtr &cgalm,
CGALMeshPtr &cgalptr = cgalmeshes[csgidx++]; CGALMeshPtr &cgalptr = cgalmeshes[csgidx++];
if (get_stack_operation(csgpart) == CSGStackOp::Push) { if (get_stack_operation(csgpart) == CSGStackOp::Push) {
opstack.push({op, nullptr}); opstack.push(Frame{op});
op = CSGType::Union; op = CSGType::Union;
} }
Frame *top = &opstack.top(); Frame *top = &opstack.top();
if (!top->cgalptr && op == CSGType::Union) {
top->cgalptr = std::move(cgalptr);
} else if (top->cgalptr && cgalptr) {
perform_csg(get_operation(csgpart), top->cgalptr, cgalptr); perform_csg(get_operation(csgpart), top->cgalptr, cgalptr);
}
if (get_stack_operation(csgpart) == CSGStackOp::Pop) { if (get_stack_operation(csgpart) == CSGStackOp::Pop) {
CGALMeshPtr src = std::move(top->cgalptr); CGALMeshPtr src = std::move(top->cgalptr);
@ -126,13 +144,24 @@ It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
auto &csgpart = *it; auto &csgpart = *it;
auto m = get_cgalmesh(csgpart); auto m = get_cgalmesh(csgpart);
// mesh can be nullptr if this is a stack push or pull
if (!get_mesh(csgpart) && get_stack_operation(csgpart) != CSGStackOp::Continue) {
cgalmeshes[i] = MeshBoolean::cgal::triangle_mesh_to_cgal(indexed_triangle_set{});
return;
}
try {
if (!m || MeshBoolean::cgal::empty(*m)) if (!m || MeshBoolean::cgal::empty(*m))
return; return;
if (!MeshBoolean::cgal::does_bound_a_volume(*m) || if (!MeshBoolean::cgal::does_bound_a_volume(*m))
MeshBoolean::cgal::does_self_intersect(*m))
return; return;
if (MeshBoolean::cgal::does_self_intersect(*m))
return;
}
catch (...) { return; }
cgalmeshes[i] = std::move(m); cgalmeshes[i] = std::move(m);
}; };
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part); execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
@ -144,12 +173,12 @@ It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
std::advance(it, i); std::advance(it, i);
vfn(it); vfn(it);
if (it == csgrange.end()) if (ret == csgrange.end())
ret = it; ret = it;
} }
} }
return csgrange.end(); return ret;
} }
template<class It> template<class It>

View File

@ -34,7 +34,7 @@ void merge_slices(csg::CSGType op, size_t i,
void collect_nonempty_indices(csg::CSGType op, void collect_nonempty_indices(csg::CSGType op,
const std::vector<float> &slicegrid, const std::vector<float> &slicegrid,
const std::vector<ExPolygons> &slices, const std::vector<ExPolygons> &slices,
std::vector<size_t> indices) std::vector<size_t> &indices)
{ {
indices.clear(); indices.clear();
for (size_t i = 0; i < slicegrid.size(); ++i) { for (size_t i = 0; i < slicegrid.size(); ++i) {
@ -62,7 +62,6 @@ std::vector<ExPolygons> slice_csgmesh_ex(
auto trafo = params.trafo; auto trafo = params.trafo;
auto nonempty_indices = reserve_vector<size_t>(slicegrid.size()); auto nonempty_indices = reserve_vector<size_t>(slicegrid.size());
if (!csgrange.empty() && csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push)
opstack.push({CSGType::Union, std::vector<ExPolygons>(slicegrid.size())}); opstack.push({CSGType::Union, std::vector<ExPolygons>(slicegrid.size())});
for (const auto &csgpart : csgrange) { for (const auto &csgpart : csgrange) {

View File

@ -32,6 +32,9 @@ namespace detail {
inline void perform_csg(CSGType op, VoxelGridPtr &dst, VoxelGridPtr &src) inline void perform_csg(CSGType op, VoxelGridPtr &dst, VoxelGridPtr &src)
{ {
if (!dst || !src)
return;
switch (op) { switch (op) {
case CSGType::Union: case CSGType::Union:
grid_union(*dst, *src); grid_union(*dst, *src);
@ -71,9 +74,7 @@ VoxelGridPtr voxelize_csgmesh(const Range<It> &csgrange,
struct Frame { CSGType op = CSGType::Union; VoxelGridPtr grid; }; struct Frame { CSGType op = CSGType::Union; VoxelGridPtr grid; };
std::stack opstack{std::vector<Frame>{}}; std::stack opstack{std::vector<Frame>{}};
if (!csgrange.empty() && opstack.push({CSGType::Union, mesh_to_grid({}, params)});
csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push)
opstack.push({});
for (auto &csgpart : csgrange) { for (auto &csgpart : csgrange) {
if (params.statusfn() && params.statusfn()(-1)) if (params.statusfn() && params.statusfn()(-1))
@ -84,17 +85,13 @@ VoxelGridPtr voxelize_csgmesh(const Range<It> &csgrange,
auto op = get_operation(csgpart); auto op = get_operation(csgpart);
if (get_stack_operation(csgpart) == CSGStackOp::Push) { if (get_stack_operation(csgpart) == CSGStackOp::Push) {
opstack.push({op, nullptr}); opstack.push({op, mesh_to_grid({}, params)});
op = CSGType::Union; op = CSGType::Union;
} }
Frame *top = &opstack.top(); Frame *top = &opstack.top();
if (!top->grid && op == CSGType::Union) {
top->grid = std::move(partgrid);
} else if (top->grid && partgrid) {
perform_csg(get_operation(csgpart), top->grid, partgrid); perform_csg(get_operation(csgpart), top->grid, partgrid);
}
if (get_stack_operation(csgpart) == CSGStackOp::Pop) { if (get_stack_operation(csgpart) == CSGStackOp::Pop) {
VoxelGridPtr popgrid = std::move(top->grid); VoxelGridPtr popgrid = std::move(top->grid);

View File

@ -137,7 +137,8 @@ inline Vec3f to_vec3f(const _EpecMesh::Point& v)
return { float(iv.x()), float(iv.y()), float(iv.z()) }; return { float(iv.x()), float(iv.y()), float(iv.z()) };
} }
template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) template<class _Mesh>
indexed_triangle_set cgal_to_indexed_triangle_set(const _Mesh &cgalmesh)
{ {
indexed_triangle_set its; indexed_triangle_set its;
its.vertices.reserve(cgalmesh.num_vertices()); its.vertices.reserve(cgalmesh.num_vertices());
@ -167,7 +168,7 @@ template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
its.indices.emplace_back(facet); its.indices.emplace_back(facet);
} }
return TriangleMesh(std::move(its)); return its;
} }
std::unique_ptr<CGALMesh, CGALMeshDeleter> std::unique_ptr<CGALMesh, CGALMeshDeleter>
@ -181,7 +182,12 @@ triangle_mesh_to_cgal(const std::vector<stl_vertex> &V,
TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh) TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh)
{ {
return cgal_to_triangle_mesh(cgalmesh.m); return TriangleMesh{cgal_to_indexed_triangle_set(cgalmesh.m)};
}
indexed_triangle_set cgal_to_indexed_triangle_set(const CGALMesh &cgalmesh)
{
return cgal_to_indexed_triangle_set(cgalmesh.m);
} }
// ///////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////
@ -236,6 +242,18 @@ bool does_self_intersect(const CGALMesh &mesh) { return CGALProc::does_self_inte
// Now the public functions for TriangleMesh input: // Now the public functions for TriangleMesh input:
// ///////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////
template<class Op> void _mesh_boolean_do(Op &&op, indexed_triangle_set &A, const indexed_triangle_set &B)
{
CGALMesh meshA;
CGALMesh meshB;
triangle_mesh_to_cgal(A.vertices, A.indices, meshA.m);
triangle_mesh_to_cgal(B.vertices, B.indices, meshB.m);
_cgal_do(op, meshA, meshB);
A = cgal_to_indexed_triangle_set(meshA.m);
}
template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const TriangleMesh &B) template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const TriangleMesh &B)
{ {
CGALMesh meshA; CGALMesh meshA;
@ -245,7 +263,7 @@ template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const Triangl
_cgal_do(op, meshA, meshB); _cgal_do(op, meshA, meshB);
A = cgal_to_triangle_mesh(meshA.m); A = cgal_to_triangle_mesh(meshA);
} }
void minus(TriangleMesh &A, const TriangleMesh &B) void minus(TriangleMesh &A, const TriangleMesh &B)
@ -263,6 +281,21 @@ void intersect(TriangleMesh &A, const TriangleMesh &B)
_mesh_boolean_do(_cgal_intersection, A, B); _mesh_boolean_do(_cgal_intersection, A, B);
} }
void minus(indexed_triangle_set &A, const indexed_triangle_set &B)
{
_mesh_boolean_do(_cgal_diff, A, B);
}
void plus(indexed_triangle_set &A, const indexed_triangle_set &B)
{
_mesh_boolean_do(_cgal_union, A, B);
}
void intersect(indexed_triangle_set &A, const indexed_triangle_set &B)
{
_mesh_boolean_do(_cgal_intersection, A, B);
}
bool does_self_intersect(const TriangleMesh &mesh) bool does_self_intersect(const TriangleMesh &mesh)
{ {
CGALMesh cgalm; CGALMesh cgalm;

View File

@ -42,12 +42,17 @@ inline CGALMeshPtr triangle_mesh_to_cgal(const TriangleMesh &M)
} }
TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh); TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh);
indexed_triangle_set cgal_to_indexed_triangle_set(const CGALMesh &cgalmesh);
// Do boolean mesh difference with CGAL bypassing igl. // Do boolean mesh difference with CGAL bypassing igl.
void minus(TriangleMesh &A, const TriangleMesh &B); void minus(TriangleMesh &A, const TriangleMesh &B);
void plus(TriangleMesh &A, const TriangleMesh &B); void plus(TriangleMesh &A, const TriangleMesh &B);
void intersect(TriangleMesh &A, const TriangleMesh &B); void intersect(TriangleMesh &A, const TriangleMesh &B);
void minus(indexed_triangle_set &A, const indexed_triangle_set &B);
void plus(indexed_triangle_set &A, const indexed_triangle_set &B);
void intersect(indexed_triangle_set &A, const indexed_triangle_set &B);
void minus(CGALMesh &A, CGALMesh &B); void minus(CGALMesh &A, CGALMesh &B);
void plus(CGALMesh &A, CGALMesh &B); void plus(CGALMesh &A, CGALMesh &B);
void intersect(CGALMesh &A, CGALMesh &B); void intersect(CGALMesh &A, CGALMesh &B);

View File

@ -104,15 +104,34 @@ inline InteriorPtr generate_interior(const indexed_triangle_set &mesh,
return grid ? generate_interior(*grid, hc, ctl) : InteriorPtr{}; return grid ? generate_interior(*grid, hc, ctl) : InteriorPtr{};
} }
template<class Cont> double csgmesh_positive_maxvolume(const Cont &csg)
{
double mesh_vol = 0;
bool skip = false;
for (const auto &m : csg) {
auto op = csg::get_operation(m);
auto stackop = csg::get_stack_operation(m);
if (stackop == csg::CSGStackOp::Push && op != csg::CSGType::Union)
skip = true;
if (!skip && csg::get_mesh(m) && op == csg::CSGType::Union)
mesh_vol = std::max(mesh_vol,
double(its_volume(*(csg::get_mesh(m)))));
if (stackop == csg::CSGStackOp::Pop)
skip = false;
}
return mesh_vol;
}
template<class It> template<class It>
InteriorPtr generate_interior(const Range<It> &csgparts, InteriorPtr generate_interior(const Range<It> &csgparts,
const HollowingConfig &hc = {}, const HollowingConfig &hc = {},
const JobController &ctl = {}) const JobController &ctl = {})
{ {
double mesh_vol = 0; double mesh_vol = csgmesh_positive_maxvolume(csgparts);
for (auto &part : csgparts)
mesh_vol = std::max(mesh_vol,
double(its_volume(*(csg::get_mesh(part)))));
auto params = csg::VoxelizeParams{} auto params = csg::VoxelizeParams{}
.voxel_scale(get_voxel_scale(mesh_vol, hc)) .voxel_scale(get_voxel_scale(mesh_vol, hc))

View File

@ -163,11 +163,7 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb(
SLAPrintObject &po, SLAPrintObjectStep step) SLAPrintObject &po, SLAPrintObjectStep step)
{ {
// Empirical upper limit to not get excessive performance hit // Empirical upper limit to not get excessive performance hit
constexpr double MaxPreviewVoxelScale = 18.; constexpr double MaxPreviewVoxelScale = 12.;
Benchmark bench;
bench.start();
// update preview mesh // update preview mesh
double vscale = std::min(MaxPreviewVoxelScale, double vscale = std::min(MaxPreviewVoxelScale,
@ -183,14 +179,40 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb(
}); });
auto r = range(po.m_mesh_to_slice); auto r = range(po.m_mesh_to_slice);
auto m = indexed_triangle_set{};
if (r.size() > 1 && !is_all_positive(r)) {
auto grid = csg::voxelize_csgmesh(r, voxparams); auto grid = csg::voxelize_csgmesh(r, voxparams);
m = grid ? grid_to_mesh(*grid) : indexed_triangle_set{}; auto m = grid ? grid_to_mesh(*grid, 0., 0.01) : indexed_triangle_set{};
// float loss_less_max_error = 1e-6; float loss_less_max_error = 1e-6;
// its_quadric_edge_collapse(m, 0U, &loss_less_max_error); its_quadric_edge_collapse(m, 0U, &loss_less_max_error);
} else {
return m;
}
void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step)
{
Benchmark bench;
bench.start();
auto r = range(po.m_mesh_to_slice);
auto m = indexed_triangle_set{};
if (r.size() == 1 || is_all_positive(r)) {
m = csgmesh_merge_positive_parts(r); m = csgmesh_merge_positive_parts(r);
} else if (csg::check_csgmesh_booleans(r) == r.end()) {
auto cgalmeshptr = csg::perform_csgmesh_booleans(r);
if (cgalmeshptr)
m = MeshBoolean::cgal::cgal_to_indexed_triangle_set(*cgalmeshptr);
} else {
po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
L("Can't do proper mesh booleans!"));
m = generate_preview_vdb(po, step);
}
po.m_preview_meshes[step] = TriangleMesh{std::move(m)};
for (size_t i = size_t(step) + 1; i < slaposCount; ++i)
{
po.m_preview_meshes[i] = {};
} }
bench.stop(); bench.stop();
@ -200,27 +222,6 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb(
else else
BOOST_LOG_TRIVIAL(error) << "Preview failed!"; BOOST_LOG_TRIVIAL(error) << "Preview failed!";
return m;
}
void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step)
{
auto r = range(po.m_mesh_to_slice);
if (csg::check_csgmesh_booleans(r) == r.end()) {
auto cgalmesh = csg::perform_csgmesh_booleans(r);
po.m_preview_meshes[step] = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalmesh);
} else {
po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
L("Can't do proper mesh booleans!"));
po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, step)};
}
for (size_t i = size_t(step) + 1; i < slaposCount; ++i)
{
po.m_preview_meshes[i] = {};
}
using namespace std::string_literals; using namespace std::string_literals;
report_status(-2, "Reload preview from step "s + std::to_string(int(step)), SlicingStatus::RELOAD_SLA_PREVIEW); report_status(-2, "Reload preview from step "s + std::to_string(int(step)), SlicingStatus::RELOAD_SLA_PREVIEW);
@ -356,9 +357,7 @@ template<class Cont> BoundingBoxf3 csgmesh_positive_bb(const Cont &csg)
{ {
// Calculate the biggest possible bounding box of the mesh to be sliced // Calculate the biggest possible bounding box of the mesh to be sliced
// from all the positive parts that it contains. // from all the positive parts that it contains.
auto bb3d = csg.empty() ? BoundingBoxf3{} : BoundingBoxf3 bb3d;
bounding_box(*csg::get_mesh(*csg.begin()),
csg::get_transform(*csg.begin()));
bool skip = false; bool skip = false;
for (const auto &m : csg) { for (const auto &m : csg) {