From 1efc8191a23248c17d33a25e9babd465ac20c3f7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 21 Dec 2022 18:09:31 +0100 Subject: [PATCH] Full support of csg op stack. Preferring cgal in preview if feasible --- src/libslic3r/CSGMesh/CSGMesh.hpp | 5 +- .../CSGMesh/PerformCSGMeshBooleans.hpp | 67 +++++++++++++----- src/libslic3r/CSGMesh/SliceCSGMesh.hpp | 5 +- src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp | 15 ++-- src/libslic3r/MeshBoolean.cpp | 45 ++++++++++-- src/libslic3r/MeshBoolean.hpp | 7 +- src/libslic3r/SLA/Hollowing.hpp | 27 ++++++-- src/libslic3r/SLAPrintSteps.cpp | 69 +++++++++---------- 8 files changed, 162 insertions(+), 78 deletions(-) diff --git a/src/libslic3r/CSGMesh/CSGMesh.hpp b/src/libslic3r/CSGMesh/CSGMesh.hpp index f30905066f..f44119c929 100644 --- a/src/libslic3r/CSGMesh/CSGMesh.hpp +++ b/src/libslic3r/CSGMesh/CSGMesh.hpp @@ -79,7 +79,10 @@ struct CSGPart { CSGPart(AnyPtr ptr = {}, CSGType op = CSGType::Union, 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} {} }; diff --git a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp index 733cdd1fe2..0a880c8fa2 100644 --- a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp +++ b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp @@ -7,6 +7,7 @@ #include "CSGMesh.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp" +//#include "libslic3r/Execution/ExecutionSeq.hpp" #include "libslic3r/MeshBoolean.hpp" namespace Slic3r { namespace csg { @@ -17,12 +18,21 @@ template MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart) { const indexed_triangle_set *its = csg::get_mesh(csgpart); + indexed_triangle_set dummy; + + if (!its) + its = &dummy; + MeshBoolean::cgal::CGALMeshPtr ret; - if (its) { - indexed_triangle_set m = *its; - its_transform(m, get_transform(csgpart)); + indexed_triangle_set m = *its; + its_transform(m, get_transform(csgpart)); + + try { ret = MeshBoolean::cgal::triangle_mesh_to_cgal(m); + } catch (...) { + // errors are ignored, simply return null + ret = nullptr; } return ret; @@ -34,6 +44,14 @@ using MeshBoolean::cgal::CGALMeshPtr; 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) { case CSGType::Union: MeshBoolean::cgal::plus(*dst, *src); @@ -72,13 +90,17 @@ void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMeshPtr &cgalm, using MeshBoolean::cgal::CGALMeshPtr; 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{}}; - if (!csgrange.empty() && - csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) - opstack.push({}); + opstack.push(Frame{}); std::vector cgalmeshes = get_cgalptrs(ex_tbb, csgrange); @@ -89,17 +111,13 @@ void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMeshPtr &cgalm, CGALMeshPtr &cgalptr = cgalmeshes[csgidx++]; if (get_stack_operation(csgpart) == CSGStackOp::Push) { - opstack.push({op, nullptr}); + opstack.push(Frame{op}); op = CSGType::Union; } 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) { CGALMeshPtr src = std::move(top->cgalptr); @@ -126,12 +144,23 @@ It check_csgmesh_booleans(const Range &csgrange, Visitor &&vfn) auto &csgpart = *it; auto m = get_cgalmesh(csgpart); - if (!m || MeshBoolean::cgal::empty(*m)) + // 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; + } - if (!MeshBoolean::cgal::does_bound_a_volume(*m) || - MeshBoolean::cgal::does_self_intersect(*m)) - return; + try { + if (!m || MeshBoolean::cgal::empty(*m)) + return; + + if (!MeshBoolean::cgal::does_bound_a_volume(*m)) + return; + + if (MeshBoolean::cgal::does_self_intersect(*m)) + return; + } + catch (...) { return; } cgalmeshes[i] = std::move(m); }; @@ -144,12 +173,12 @@ It check_csgmesh_booleans(const Range &csgrange, Visitor &&vfn) std::advance(it, i); vfn(it); - if (it == csgrange.end()) + if (ret == csgrange.end()) ret = it; } } - return csgrange.end(); + return ret; } template diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp index d9df97e956..041acedb0e 100644 --- a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -34,7 +34,7 @@ void merge_slices(csg::CSGType op, size_t i, void collect_nonempty_indices(csg::CSGType op, const std::vector &slicegrid, const std::vector &slices, - std::vector indices) + std::vector &indices) { indices.clear(); for (size_t i = 0; i < slicegrid.size(); ++i) { @@ -62,8 +62,7 @@ std::vector slice_csgmesh_ex( auto trafo = params.trafo; auto nonempty_indices = reserve_vector(slicegrid.size()); - if (!csgrange.empty() && csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) - opstack.push({CSGType::Union, std::vector(slicegrid.size())}); + opstack.push({CSGType::Union, std::vector(slicegrid.size())}); for (const auto &csgpart : csgrange) { const indexed_triangle_set *its = csg::get_mesh(csgpart); diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp index 43019ef856..c76187723d 100644 --- a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -32,6 +32,9 @@ namespace detail { inline void perform_csg(CSGType op, VoxelGridPtr &dst, VoxelGridPtr &src) { + if (!dst || !src) + return; + switch (op) { case CSGType::Union: grid_union(*dst, *src); @@ -71,9 +74,7 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, struct Frame { CSGType op = CSGType::Union; VoxelGridPtr grid; }; std::stack opstack{std::vector{}}; - if (!csgrange.empty() && - csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) - opstack.push({}); + opstack.push({CSGType::Union, mesh_to_grid({}, params)}); for (auto &csgpart : csgrange) { if (params.statusfn() && params.statusfn()(-1)) @@ -84,17 +85,13 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, auto op = get_operation(csgpart); if (get_stack_operation(csgpart) == CSGStackOp::Push) { - opstack.push({op, nullptr}); + opstack.push({op, mesh_to_grid({}, params)}); op = CSGType::Union; } 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) { VoxelGridPtr popgrid = std::move(top->grid); diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 86b50d1562..3373616a47 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -137,7 +137,8 @@ inline Vec3f to_vec3f(const _EpecMesh::Point& v) return { float(iv.x()), float(iv.y()), float(iv.z()) }; } -template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) +template +indexed_triangle_set cgal_to_indexed_triangle_set(const _Mesh &cgalmesh) { indexed_triangle_set its; its.vertices.reserve(cgalmesh.num_vertices()); @@ -167,7 +168,7 @@ template TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) its.indices.emplace_back(facet); } - return TriangleMesh(std::move(its)); + return its; } std::unique_ptr @@ -181,7 +182,12 @@ triangle_mesh_to_cgal(const std::vector &V, 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,16 +242,28 @@ bool does_self_intersect(const CGALMesh &mesh) { return CGALProc::does_self_inte // Now the public functions for TriangleMesh input: // ///////////////////////////////////////////////////////////////////////////// +template 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 void _mesh_boolean_do(Op &&op, TriangleMesh &A, const TriangleMesh &B) { CGALMesh meshA; CGALMesh meshB; 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); - - A = cgal_to_triangle_mesh(meshA.m); + + A = cgal_to_triangle_mesh(meshA); } 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); } +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) { CGALMesh cgalm; diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index 441fc78c11..4210bfe869 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -42,12 +42,17 @@ inline CGALMeshPtr triangle_mesh_to_cgal(const TriangleMesh &M) } 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. void minus(TriangleMesh &A, const TriangleMesh &B); void plus(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 plus(CGALMesh &A, CGALMesh &B); void intersect(CGALMesh &A, CGALMesh &B); diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index bcc26981e2..aa43b021bb 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -104,15 +104,34 @@ inline InteriorPtr generate_interior(const indexed_triangle_set &mesh, return grid ? generate_interior(*grid, hc, ctl) : InteriorPtr{}; } +template 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 InteriorPtr generate_interior(const Range &csgparts, const HollowingConfig &hc = {}, const JobController &ctl = {}) { - double mesh_vol = 0; - for (auto &part : csgparts) - mesh_vol = std::max(mesh_vol, - double(its_volume(*(csg::get_mesh(part))))); + double mesh_vol = csgmesh_positive_maxvolume(csgparts); auto params = csg::VoxelizeParams{} .voxel_scale(get_voxel_scale(mesh_vol, hc)) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 4e34ffbed1..c1b0356810 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -163,11 +163,7 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( SLAPrintObject &po, SLAPrintObjectStep step) { // Empirical upper limit to not get excessive performance hit - constexpr double MaxPreviewVoxelScale = 18.; - - Benchmark bench; - - bench.start(); + constexpr double MaxPreviewVoxelScale = 12.; // update preview mesh double vscale = std::min(MaxPreviewVoxelScale, @@ -182,15 +178,41 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( return po.m_print->cancel_status() != CancelStatus::NOT_CANCELED; }); + auto r = range(po.m_mesh_to_slice); + auto grid = csg::voxelize_csgmesh(r, voxparams); + auto m = grid ? grid_to_mesh(*grid, 0., 0.01) : indexed_triangle_set{}; + float loss_less_max_error = 1e-6; + its_quadric_edge_collapse(m, 0U, &loss_less_max_error); + + 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)) { - auto grid = csg::voxelize_csgmesh(r, voxparams); - m = grid ? grid_to_mesh(*grid) : indexed_triangle_set{}; -// float loss_less_max_error = 1e-6; -// its_quadric_edge_collapse(m, 0U, &loss_less_max_error); - } else { + if (r.size() == 1 || is_all_positive(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(); @@ -200,27 +222,6 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( else 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; report_status(-2, "Reload preview from step "s + std::to_string(int(step)), SlicingStatus::RELOAD_SLA_PREVIEW); @@ -356,9 +357,7 @@ template BoundingBoxf3 csgmesh_positive_bb(const Cont &csg) { // Calculate the biggest possible bounding box of the mesh to be sliced // from all the positive parts that it contains. - auto bb3d = csg.empty() ? BoundingBoxf3{} : - bounding_box(*csg::get_mesh(*csg.begin()), - csg::get_transform(*csg.begin())); + BoundingBoxf3 bb3d; bool skip = false; for (const auto &m : csg) {