mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 15:25:53 +08:00
Full support of csg op stack. Preferring cgal in preview if feasible
This commit is contained in:
parent
d75b951c39
commit
1efc8191a2
@ -79,7 +79,10 @@ struct CSGPart {
|
||||
CSGPart(AnyPtr<const indexed_triangle_set> 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}
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -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<class CSGPartT>
|
||||
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));
|
||||
|
||||
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<Frame>{}};
|
||||
|
||||
if (!csgrange.empty() &&
|
||||
csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push)
|
||||
opstack.push({});
|
||||
opstack.push(Frame{});
|
||||
|
||||
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++];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (get_stack_operation(csgpart) == CSGStackOp::Pop) {
|
||||
CGALMeshPtr src = std::move(top->cgalptr);
|
||||
@ -126,13 +144,24 @@ It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
|
||||
auto &csgpart = *it;
|
||||
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))
|
||||
return;
|
||||
|
||||
if (!MeshBoolean::cgal::does_bound_a_volume(*m) ||
|
||||
MeshBoolean::cgal::does_self_intersect(*m))
|
||||
if (!MeshBoolean::cgal::does_bound_a_volume(*m))
|
||||
return;
|
||||
|
||||
if (MeshBoolean::cgal::does_self_intersect(*m))
|
||||
return;
|
||||
}
|
||||
catch (...) { return; }
|
||||
|
||||
cgalmeshes[i] = std::move(m);
|
||||
};
|
||||
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);
|
||||
vfn(it);
|
||||
|
||||
if (it == csgrange.end())
|
||||
if (ret == csgrange.end())
|
||||
ret = it;
|
||||
}
|
||||
}
|
||||
|
||||
return csgrange.end();
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
|
@ -34,7 +34,7 @@ void merge_slices(csg::CSGType op, size_t i,
|
||||
void collect_nonempty_indices(csg::CSGType op,
|
||||
const std::vector<float> &slicegrid,
|
||||
const std::vector<ExPolygons> &slices,
|
||||
std::vector<size_t> indices)
|
||||
std::vector<size_t> &indices)
|
||||
{
|
||||
indices.clear();
|
||||
for (size_t i = 0; i < slicegrid.size(); ++i) {
|
||||
@ -62,7 +62,6 @@ std::vector<ExPolygons> slice_csgmesh_ex(
|
||||
auto trafo = params.trafo;
|
||||
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())});
|
||||
|
||||
for (const auto &csgpart : csgrange) {
|
||||
|
@ -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<It> &csgrange,
|
||||
struct Frame { CSGType op = CSGType::Union; VoxelGridPtr grid; };
|
||||
std::stack opstack{std::vector<Frame>{}};
|
||||
|
||||
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<It> &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);
|
||||
}
|
||||
|
||||
if (get_stack_operation(csgpart) == CSGStackOp::Pop) {
|
||||
VoxelGridPtr popgrid = std::move(top->grid);
|
||||
|
@ -137,7 +137,8 @@ inline Vec3f to_vec3f(const _EpecMesh::Point& v)
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
return TriangleMesh(std::move(its));
|
||||
return its;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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:
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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)
|
||||
{
|
||||
CGALMesh meshA;
|
||||
@ -245,7 +263,7 @@ template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const Triangl
|
||||
|
||||
_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;
|
||||
|
@ -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);
|
||||
|
@ -104,15 +104,34 @@ inline InteriorPtr generate_interior(const indexed_triangle_set &mesh,
|
||||
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>
|
||||
InteriorPtr generate_interior(const Range<It> &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))
|
||||
|
@ -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,
|
||||
@ -183,14 +179,40 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb(
|
||||
});
|
||||
|
||||
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 {
|
||||
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)) {
|
||||
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<class Cont> 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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user