From 2144f81bf1d890e214a7db7a98148b1c61d73464 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Oct 2022 13:18:02 +0200 Subject: [PATCH 01/75] Useful backend improvements from sla volumes branch --- src/libslic3r/AABBMesh.cpp | 3 - src/libslic3r/AABBMesh.hpp | 4 +- src/libslic3r/CMakeLists.txt | 7 +- src/libslic3r/CSGMesh/CSGMesh.hpp | 76 ++++ src/libslic3r/CSGMesh/ModelToCSGMesh.hpp | 58 +++ .../CSGMesh/PerformCSGMeshBooleans.hpp | 19 + src/libslic3r/CSGMesh/SliceCSGMesh.hpp | 56 +++ src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp | 85 +++++ src/libslic3r/MTUtils.hpp | 83 +++++ src/libslic3r/MeshSplitImpl.hpp | 18 +- src/libslic3r/OpenVDBUtils.cpp | 243 +++++++++--- src/libslic3r/OpenVDBUtils.hpp | 67 ++-- src/libslic3r/OpenVDBUtilsLegacy.hpp | 100 +++++ src/libslic3r/SLA/Hollowing.cpp | 351 +++++++++++++----- src/libslic3r/SLA/Hollowing.hpp | 77 +++- src/libslic3r/SLA/SupportTree.hpp | 3 +- src/libslic3r/SLAPrint.cpp | 25 +- src/libslic3r/SLAPrint.hpp | 4 + src/libslic3r/SLAPrintSteps.cpp | 202 +--------- src/libslic3r/SlicesToTriangleMesh.cpp | 19 +- src/libslic3r/TreeSupport.cpp | 4 +- src/libslic3r/TriangleMesh.hpp | 28 +- src/libslic3r/libslic3r.h | 9 +- src/slic3r/GUI/MeshUtils.hpp | 12 +- tests/sla_print/sla_test_utils.cpp | 2 +- 25 files changed, 1126 insertions(+), 429 deletions(-) create mode 100644 src/libslic3r/CSGMesh/CSGMesh.hpp create mode 100644 src/libslic3r/CSGMesh/ModelToCSGMesh.hpp create mode 100644 src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp create mode 100644 src/libslic3r/CSGMesh/SliceCSGMesh.hpp create mode 100644 src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp create mode 100644 src/libslic3r/OpenVDBUtilsLegacy.hpp diff --git a/src/libslic3r/AABBMesh.cpp b/src/libslic3r/AABBMesh.cpp index 835df2ebec..b6dc68aaea 100644 --- a/src/libslic3r/AABBMesh.cpp +++ b/src/libslic3r/AABBMesh.cpp @@ -69,7 +69,6 @@ public: template void AABBMesh::init(const M &mesh, bool calculate_epsilon) { BoundingBoxf3 bb = bounding_box(mesh); - m_ground_level += bb.min(Z); // Build the AABB accelaration tree m_aabb->init(*m_tm, calculate_epsilon); @@ -97,7 +96,6 @@ AABBMesh::~AABBMesh() {} AABBMesh::AABBMesh(const AABBMesh &other) : m_tm(other.m_tm) - , m_ground_level(other.m_ground_level) , m_aabb(new AABBImpl(*other.m_aabb)) , m_vfidx{other.m_vfidx} , m_fnidx{other.m_fnidx} @@ -106,7 +104,6 @@ AABBMesh::AABBMesh(const AABBMesh &other) AABBMesh &AABBMesh::operator=(const AABBMesh &other) { m_tm = other.m_tm; - m_ground_level = other.m_ground_level; m_aabb.reset(new AABBImpl(*other.m_aabb)); m_vfidx = other.m_vfidx; m_fnidx = other.m_fnidx; diff --git a/src/libslic3r/AABBMesh.hpp b/src/libslic3r/AABBMesh.hpp index 6a08c4303b..179bf8c4c1 100644 --- a/src/libslic3r/AABBMesh.hpp +++ b/src/libslic3r/AABBMesh.hpp @@ -28,7 +28,7 @@ class AABBMesh { class AABBImpl; const indexed_triangle_set* m_tm; - double m_ground_level = 0/*, m_gnd_offset = 0*/; +// double m_ground_level = 0/*, m_gnd_offset = 0*/; std::unique_ptr m_aabb; VertexFaceIndex m_vfidx; // vertex-face index @@ -57,7 +57,7 @@ public: ~AABBMesh(); - inline double ground_level() const { return m_ground_level /*+ m_gnd_offset*/; } +// inline double ground_level() const { return m_ground_level /*+ m_gnd_offset*/; } // inline void ground_level_offset(double o) { m_gnd_offset = o; } // inline double ground_level_offset() const { return m_gnd_offset; } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index bee7ceb628..e15848603d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -11,7 +11,7 @@ endif () set(OpenVDBUtils_SOURCES "") if (TARGET OpenVDB::openvdb) - set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp) + set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp OpenVDBUtilsLegacy.hpp) endif() set(SLIC3R_SOURCES @@ -39,6 +39,11 @@ set(SLIC3R_SOURCES Color.hpp Config.cpp Config.hpp + CSGMesh/CSGMesh.hpp + CSGMesh/SliceCSGMesh.hpp + CSGMesh/ModelToCSGMesh.hpp + CSGMesh/PerformCSGMeshBooleans.hpp + CSGMesh/VoxelizeCSGMesh.hpp EdgeGrid.cpp EdgeGrid.hpp ElephantFootCompensation.cpp diff --git a/src/libslic3r/CSGMesh/CSGMesh.hpp b/src/libslic3r/CSGMesh/CSGMesh.hpp new file mode 100644 index 0000000000..4655f684a3 --- /dev/null +++ b/src/libslic3r/CSGMesh/CSGMesh.hpp @@ -0,0 +1,76 @@ +#ifndef CSGMESH_HPP +#define CSGMESH_HPP + +#include // for AnyPtr +#include + +namespace Slic3r { namespace csg { + +// A CSGPartT should be an object that can provide at least a mesh + trafo and an +// associated csg operation. A collection of CSGPartT objects can then +// be interpreted as one model and used in various contexts. It can be assembled +// with CGAL or OpenVDB, rendered with OpenCSG or provided to a ray-tracer to +// deal with various parts of it according to the supported CSG types... +// +// A few simple templated interface functions are provided here and a default +// CSGPart class that implements the necessary means to be usable as a +// CSGPartT object. + +// Supported CSG operation types +enum class CSGType { Union, Difference, Intersection }; + +// Get the CSG operation of the part. Can be overriden for any type +template CSGType get_operation(const CSGPartT &part) +{ + return part.operation; +} + +// Get the mesh for the part. Can be overriden for any type +template +const indexed_triangle_set *get_mesh(const CSGPartT &part) +{ + return part.its_ptr.get(); +} + +// Get the transformation associated with the mesh inside a CSGPartT object. +// Can be overriden for any type. +template +Transform3f get_transform(const CSGPartT &part) +{ + return part.trafo; +} + +// Provide default overloads for indexed_triangle_set to be usable as a plain +// CSGPart with an implicit union operation + +inline CSGType get_operation(const indexed_triangle_set &part) +{ + return CSGType::Union; +} + +inline const indexed_triangle_set * get_mesh(const indexed_triangle_set &part) +{ + return ∂ +} + +inline Transform3f get_transform(const indexed_triangle_set &part) +{ + return Transform3f::Identity(); +} + +// Default implementation +struct CSGPart { + AnyPtr its_ptr; + Transform3f trafo; + CSGType operation; + + CSGPart(AnyPtr ptr = {}, + CSGType op = CSGType::Union, + const Transform3f &tr = Transform3f::Identity()) + : its_ptr{std::move(ptr)}, operation{op}, trafo{tr} + {} +}; + +}} // namespace Slic3r::csg + +#endif // CSGMESH_HPP diff --git a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp new file mode 100644 index 0000000000..f7247ea706 --- /dev/null +++ b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp @@ -0,0 +1,58 @@ +#ifndef MODELTOCSGMESH_HPP +#define MODELTOCSGMESH_HPP + +#include "CSGMesh.hpp" + +#include "libslic3r/Model.hpp" +#include "libslic3r/SLA/Hollowing.hpp" + +namespace Slic3r { namespace csg { + +enum ModelParts { + mpartsPositive = 1, + mpartsNegative = 2, + mpartsDrillHoles = 4 +}; + +template +void model_to_csgmesh(const ModelObject &mo, + const Transform3d &trafo, + OutIt out, + // values of ModelParts ORed + int parts_to_include = mpartsPositive + ) +{ + bool do_positives = parts_to_include & mpartsPositive; + bool do_negatives = parts_to_include & mpartsNegative; + bool do_drillholes = parts_to_include & mpartsDrillHoles; + + for (const ModelVolume *vol : mo.volumes) { + if (vol && vol->mesh_ptr() && + ((do_positives && vol->is_model_part()) || + (do_negatives && vol->is_negative_volume()))) { + CSGPart part{&(vol->mesh().its), + vol->is_model_part() ? CSGType::Union : CSGType::Difference, + (trafo * vol->get_matrix()).cast()}; + + *out = std::move(part); + ++out; + } + } + + if (do_drillholes) { + sla::DrainHoles drainholes = sla::transformed_drainhole_points(mo, trafo); + + for (const sla::DrainHole &dhole : drainholes) { + CSGPart part{std::make_unique( + dhole.to_mesh()), + CSGType::Difference}; + + *out = std::move(part); + ++out; + } + } +} + +}} // namespace Slic3r::csg + +#endif // MODELTOCSGMESH_HPP diff --git a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp new file mode 100644 index 0000000000..5c64c65e0d --- /dev/null +++ b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp @@ -0,0 +1,19 @@ +#ifndef PERFORMCSGMESHBOOLEANS_HPP +#define PERFORMCSGMESHBOOLEANS_HPP + +#include "CSGMesh.hpp" + +#include "libslic3r/MeshBoolean.hpp" + +namespace Slic3r { + +template +void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMesh &cgalm, + const Range &csg) +{ + +} + +} // namespace Slic3r + +#endif // PERFORMCSGMESHBOOLEANS_HPP diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp new file mode 100644 index 0000000000..95035b5ef5 --- /dev/null +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -0,0 +1,56 @@ +#ifndef SLICECSGMESH_HPP +#define SLICECSGMESH_HPP + +#include "CSGMesh.hpp" +#include "libslic3r/TriangleMeshSlicer.hpp" +#include "libslic3r/ClipperUtils.hpp" + +namespace Slic3r { namespace csg { + +template +std::vector slice_csgmesh_ex( + const Range &csg, + const std::vector &slicegrid, + const MeshSlicingParamsEx ¶ms, + const std::function &throw_on_cancel = [] {}) +{ + std::vector ret(slicegrid.size()); + + MeshSlicingParamsEx params_cpy = params; + auto trafo = params.trafo; + for (const auto &m : csg) { + const indexed_triangle_set *its = csg::get_mesh(m); + if (!its) + continue; + + params_cpy.trafo = trafo * csg::get_transform(m).template cast(); + std::vector slices = slice_mesh_ex(*its, + slicegrid, params_cpy, + throw_on_cancel); + + assert(slices.size() == slicegrid.size()); + + for (size_t i = 0; i < slicegrid.size(); ++i) { + switch(get_operation(m)) { + case CSGType::Union: + for (ExPolygon &expoly : slices[i]) + ret[i].emplace_back(std::move(expoly)); + + ret[i] = union_ex(ret[i]); + break; + case CSGType::Difference: + ret[i] = diff_ex(ret[i], slices[i]); + break; + case CSGType::Intersection: + ret[i] = intersection_ex(ret[i], slices[i]); + break; + } + } + } + + return ret; +} + +}} // namespace Slic3r::csg + +#endif // SLICECSGMESH_HPP diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp new file mode 100644 index 0000000000..9d72e3ea88 --- /dev/null +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -0,0 +1,85 @@ +#ifndef VOXELIZECSGMESH_HPP +#define VOXELIZECSGMESH_HPP + +#include "CSGMesh.hpp" +#include "libslic3r/OpenVDBUtils.hpp" + +namespace Slic3r { namespace csg { + +class VoxelizeParams { + float m_voxel_scale = 1.f; + float m_exterior_bandwidth = 3.0f; + float m_interior_bandwidth = 3.0f; + +public: + float voxel_scale() const noexcept { return m_voxel_scale; } + float exterior_bandwidth() const noexcept { return m_exterior_bandwidth; } + float interior_bandwidth() const noexcept { return m_interior_bandwidth; } + + VoxelizeParams &voxel_scale(float val) noexcept + { + m_voxel_scale = val; + return *this; + } + VoxelizeParams &exterior_bandwidth(float val) noexcept + { + m_exterior_bandwidth = val; + return *this; + } + VoxelizeParams &interior_bandwidth(float val) noexcept + { + m_interior_bandwidth = val; + return *this; + } +}; + +// This method can be overriden when a specific CSGPart type supports caching +// of the voxel grid +template +VoxelGridPtr get_voxelgrid(const CSGPartT &csgpart, const VoxelizeParams ¶ms) +{ + const indexed_triangle_set *its = csg::get_mesh(csgpart); + VoxelGridPtr ret; + + if (its) + ret = mesh_to_grid(*csg::get_mesh(csgpart), + csg::get_transform(csgpart), + params.voxel_scale(), + params.exterior_bandwidth(), + params.interior_bandwidth()); + + return ret; +} + +template +VoxelGridPtr voxelize_csgmesh(const Range &csgrange, + const VoxelizeParams ¶ms = {}) +{ + VoxelGridPtr ret; + + for (auto &csgpart : csgrange) { + VoxelGridPtr partgrid = get_voxelgrid(csgpart, params); + + if (!ret && get_operation(csgpart) == CSGType::Union) { + ret = std::move(partgrid); + } else if (ret) { + switch (get_operation(csgpart)) { + case CSGType::Union: + grid_union(*ret, *partgrid); + break; + case CSGType::Difference: + grid_difference(*ret, *partgrid); + break; + case CSGType::Intersection: + grid_intersection(*ret, *partgrid); + break; + } + } + } + + return ret; +} + +}} // namespace Slic3r::csg + +#endif // VOXELIZECSGMESH_HPP diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index ab99ea5f68..74c44aa5e5 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "libslic3r.h" @@ -136,6 +137,88 @@ inline std::vector> grid(const T &start, return vals; } +// A general purpose pointer holder that can hold any type of smart pointer +// or raw pointer which can own or not own any object they point to. +// In case a raw pointer is stored, it is not destructed so ownership is +// assumed to be foreign. +// +// The stored pointer is not checked for being null when dereferenced. +// +// This is a movable only object due to the fact that it can possibly hold +// a unique_ptr which a non-copy. +template +class AnyPtr { + enum { RawPtr, UPtr, ShPtr, WkPtr }; + + boost::variant, std::shared_ptr, std::weak_ptr> ptr; + + template static T *get_ptr(Self &&s) + { + switch (s.ptr.which()) { + case RawPtr: return boost::get(s.ptr); + case UPtr: return boost::get>(s.ptr).get(); + case ShPtr: return boost::get>(s.ptr).get(); + case WkPtr: { + auto shptr = boost::get>(s.ptr).lock(); + return shptr.get(); + } + } + + return nullptr; + } + +public: + template>> + AnyPtr(TT *p = nullptr) : ptr{p} + {} + template>> + AnyPtr(std::unique_ptr p) : ptr{std::unique_ptr(std::move(p))} + {} + template>> + AnyPtr(std::shared_ptr p) : ptr{std::shared_ptr(std::move(p))} + {} + template>> + AnyPtr(std::weak_ptr p) : ptr{std::weak_ptr(std::move(p))} + {} + + ~AnyPtr() = default; + + AnyPtr(AnyPtr &&other) noexcept : ptr{std::move(other.ptr)} {} + AnyPtr(const AnyPtr &other) = delete; + + AnyPtr &operator=(AnyPtr &&other) noexcept { ptr = std::move(other.ptr); return *this; } + AnyPtr &operator=(const AnyPtr &other) = delete; + + AnyPtr &operator=(T *p) { ptr = p; return *this; } + AnyPtr &operator=(std::unique_ptr p) { ptr = std::move(p); return *this; } + AnyPtr &operator=(std::shared_ptr p) { ptr = p; return *this; } + AnyPtr &operator=(std::weak_ptr p) { ptr = std::move(p); return *this; } + + const T &operator*() const { return *get_ptr(*this); } + T &operator*() { return *get_ptr(*this); } + + T *operator->() { return get_ptr(*this); } + const T *operator->() const { return get_ptr(*this); } + + T *get() { return get_ptr(*this); } + const T *get() const { return get_ptr(*this); } + + operator bool() const + { + switch (ptr.which()) { + case RawPtr: return bool(boost::get(ptr)); + case UPtr: return bool(boost::get>(ptr)); + case ShPtr: return bool(boost::get>(ptr)); + case WkPtr: { + auto shptr = boost::get>(ptr).lock(); + return bool(shptr); + } + } + + return false; + } +}; + } // namespace Slic3r #endif // MTUTILS_HPP diff --git a/src/libslic3r/MeshSplitImpl.hpp b/src/libslic3r/MeshSplitImpl.hpp index c9df648fb9..0f1fe44c35 100644 --- a/src/libslic3r/MeshSplitImpl.hpp +++ b/src/libslic3r/MeshSplitImpl.hpp @@ -108,6 +108,21 @@ template struct ItsNeighborsWrapper const auto& get_index() const noexcept { return index_ref; } }; +// Can be used as the second argument to its_split to apply a functor on each +// part, instead of collecting them into a container. +template +struct SplitOutputFn { + + Fn fn; + + SplitOutputFn(Fn f): fn{std::move(f)} {} + + SplitOutputFn &operator *() { return *this; } + void operator=(indexed_triangle_set &&its) { fn(std::move(its)); } + void operator=(indexed_triangle_set &its) { fn(its); } + SplitOutputFn& operator++() { return *this; }; +}; + // Splits a mesh into multiple meshes when possible. template void its_split(const Its &m, OutputIt out_it) @@ -155,7 +170,8 @@ void its_split(const Its &m, OutputIt out_it) mesh.indices.emplace_back(new_face); } - out_it = std::move(mesh); + *out_it = std::move(mesh); + ++out_it; } } diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 2c207bb6a7..8ea1100002 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -6,6 +6,7 @@ #pragma warning(push) #pragma warning(disable : 4146) #endif // _MSC_VER +#include #include #ifdef _MSC_VER #pragma warning(pop) @@ -16,14 +17,44 @@ #include #include -//#include "MTUtils.hpp" - namespace Slic3r { +struct VoxelGrid +{ + openvdb::FloatGrid grid; + + mutable std::optional accessor; + + template + VoxelGrid(Args &&...args): grid{std::forward(args)...} {} +}; + +void VoxelGridDeleter::operator()(VoxelGrid *ptr) { delete ptr; } + +// Similarly to std::make_unique() +template +VoxelGridPtr make_voxelgrid(Args &&...args) +{ + VoxelGrid *ptr = nullptr; + try { + ptr = new VoxelGrid(std::forward(args)...); + } catch(...) { + delete ptr; + } + + return VoxelGridPtr{ptr}; +} + +template VoxelGridPtr make_voxelgrid<>(); + +inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } +inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast(); } +inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } + class TriangleMeshDataAdapter { public: const indexed_triangle_set &its; - float voxel_scale; + Transform3d trafo; size_t polygonCount() const { return its.indices.size(); } size_t pointCount() const { return its.vertices.size(); } @@ -35,19 +66,19 @@ public: void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const { auto vidx = size_t(its.indices[n](Eigen::Index(v))); - Slic3r::Vec3d p = its.vertices[vidx].cast() * voxel_scale; + Slic3r::Vec3d p = trafo * its.vertices[vidx].cast(); pos = {p.x(), p.y(), p.z()}; } - TriangleMeshDataAdapter(const indexed_triangle_set &m, float voxel_sc = 1.f) - : its{m}, voxel_scale{voxel_sc} {}; + TriangleMeshDataAdapter(const indexed_triangle_set &m, const Transform3d tr = Transform3d::Identity()) + : its{m}, trafo{tr} {} }; -openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh, - const openvdb::math::Transform &tr, - float voxel_scale, - float exteriorBandWidth, - float interiorBandWidth) +VoxelGridPtr mesh_to_grid(const indexed_triangle_set &mesh, + const Transform3f &tr, + float voxel_scale, + float exteriorBandWidth, + float interiorBandWidth) { // Might not be needed but this is now proven to be working openvdb::initialize(); @@ -55,51 +86,47 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh, std::vector meshparts = its_split(mesh); auto it = std::remove_if(meshparts.begin(), meshparts.end(), - [](auto &m) { return its_volume(m) < EPSILON; }); + [](auto &m) { + return its_volume(m) < EPSILON; + }); meshparts.erase(it, meshparts.end()); + Transform3d trafo = tr.cast(); + trafo.prescale(voxel_scale); + openvdb::FloatGrid::Ptr grid; for (auto &m : meshparts) { auto subgrid = openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{m, voxel_scale}, tr, 1.f, 1.f); + TriangleMeshDataAdapter{m, trafo}, {}, + exteriorBandWidth, interiorBandWidth); - if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid); - else if (subgrid) grid = std::move(subgrid); + if (grid && subgrid) + openvdb::tools::csgUnion(*grid, *subgrid); + else if (subgrid) + grid = std::move(subgrid); } - 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()) { + if (meshparts.empty()) { // Splitting failed, fall back to hollow the original mesh grid = openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{mesh}, tr, 1.f, 1.f); + TriangleMeshDataAdapter{mesh, trafo}, {}, exteriorBandWidth, + interiorBandWidth); } - 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->transform().preScale(1./voxel_scale); grid->insertMeta("voxel_scale", openvdb::FloatMetadata(voxel_scale)); - return grid; + VoxelGridPtr ret = make_voxelgrid(std::move(*grid)); + + return ret; } -indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) +indexed_triangle_set grid_to_mesh(const VoxelGrid &vgrid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) { openvdb::initialize(); @@ -107,51 +134,147 @@ indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid, std::vector triangles; std::vector quads; + auto &grid = vgrid.grid; + openvdb::tools::volumeToMesh(grid, points, triangles, quads, isovalue, adaptivity, relaxDisorientedTriangles); - float scale = 1.; - try { - scale = grid.template metaValue("voxel_scale"); - } catch (...) { } - indexed_triangle_set ret; ret.vertices.reserve(points.size()); ret.indices.reserve(triangles.size() + quads.size() * 2); - for (auto &v : points) ret.vertices.emplace_back(to_vec3f(v) / scale); + for (auto &v : points) ret.vertices.emplace_back(to_vec3f(v) /*/ scale*/); for (auto &v : triangles) ret.indices.emplace_back(to_vec3i(v)); for (auto &quad : quads) { - ret.indices.emplace_back(quad(0), quad(1), quad(2)); - ret.indices.emplace_back(quad(2), quad(3), quad(0)); + ret.indices.emplace_back(quad(2), quad(1), quad(0)); + ret.indices.emplace_back(quad(0), quad(3), quad(2)); } return ret; } -openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, - double iso, - double er, - double ir) +VoxelGridPtr dilate_grid(const VoxelGrid &vgrid, + float exteriorBandWidth, + float interiorBandWidth) { - auto new_grid = openvdb::tools::levelSetRebuild(grid, float(iso), - float(er), float(ir)); + constexpr int DilateIterations = 1; + + openvdb::FloatGrid::Ptr new_grid; + + float scale = get_voxel_scale(vgrid); + + if (interiorBandWidth > 0.f) + new_grid = openvdb::tools::dilateSdf( + vgrid.grid, scale * interiorBandWidth, openvdb::tools::NN_FACE_EDGE, + DilateIterations, + openvdb::tools::FastSweepingDomain::SWEEP_LESS_THAN_ISOVALUE); + + auto &arg = new_grid? *new_grid : vgrid.grid; + + if (exteriorBandWidth > 0.f) + new_grid = openvdb::tools::dilateSdf( + arg, scale * exteriorBandWidth, openvdb::tools::NN_FACE_EDGE, + DilateIterations, + openvdb::tools::FastSweepingDomain::SWEEP_GREATER_THAN_ISOVALUE); + + VoxelGridPtr ret; + + if (new_grid) + ret = make_voxelgrid(std::move(*new_grid)); + else + ret = make_voxelgrid(vgrid.grid); // Copies voxel_scale metadata, if it exists. - new_grid->insertMeta(*grid.deepCopyMeta()); + ret->grid.insertMeta(*vgrid.grid.deepCopyMeta()); - return new_grid; + return ret; } -openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, - double iso) +VoxelGridPtr redistance_grid(const VoxelGrid &vgrid, + float iso, + float er, + float ir) { - auto new_grid = openvdb::tools::levelSetRebuild(grid, float(iso)); + auto new_grid = openvdb::tools::levelSetRebuild(vgrid.grid, iso, er, ir); - // Copies voxel_scale metadata, if it exists. - new_grid->insertMeta(*grid.deepCopyMeta()); + auto ret = make_voxelgrid(std::move(*new_grid)); - return new_grid; + // Copies voxel_scale metadata, if it exists. + ret->grid.insertMeta(*vgrid.grid.deepCopyMeta()); + + return ret; +} + +VoxelGridPtr redistance_grid(const VoxelGrid &vgrid, float iso) +{ + auto new_grid = openvdb::tools::levelSetRebuild(vgrid.grid, iso); + + auto ret = make_voxelgrid(std::move(*new_grid)); + + // Copies voxel_scale metadata, if it exists. + ret->grid.insertMeta(*vgrid.grid.deepCopyMeta()); + + return ret; +} + +void grid_union(VoxelGrid &grid, VoxelGrid &arg) +{ + openvdb::tools::csgUnion(grid.grid, arg.grid); +} + +void grid_difference(VoxelGrid &grid, VoxelGrid &arg) +{ + openvdb::tools::csgDifference(grid.grid, arg.grid); +} + +void grid_intersection(VoxelGrid &grid, VoxelGrid &arg) +{ + openvdb::tools::csgIntersection(grid.grid, arg.grid); +} + +void reset_accessor(const VoxelGrid &vgrid) +{ + vgrid.accessor = vgrid.grid.getConstAccessor(); +} + +double get_distance_raw(const Vec3f &p, const VoxelGrid &vgrid) +{ + if (!vgrid.accessor) + reset_accessor(vgrid); + + auto v = (p).cast(); + auto grididx = vgrid.grid.transform().worldToIndexCellCentered( + {v.x(), v.y(), v.z()}); + + return vgrid.accessor->getValue(grididx) ; +} + +float get_voxel_scale(const VoxelGrid &vgrid) +{ + float scale = 1.; + try { + scale = vgrid.grid.template metaValue("voxel_scale"); + } catch (...) { } + + return scale; +} + +VoxelGridPtr clone(const VoxelGrid &grid) +{ + return make_voxelgrid(grid); +} + +void rescale_grid(VoxelGrid &grid, float scale) +{/* + float old_scale = get_voxel_scale(grid); + + float nscale = scale / old_scale;*/ +// auto tr = openvdb::math::Transform::createLinearTransform(scale); + grid.grid.transform().preScale(scale); + +// grid.grid.insertMeta("voxel_scale", openvdb::FloatMetadata(nscale)); + +// grid.grid.setTransform(tr); } } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index 254ae35833..959cd854d7 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -3,21 +3,25 @@ #include -#ifdef _MSC_VER -// Suppress warning C4146 in include/gmp.h(2177,31): unary minus operator applied to unsigned type, result still unsigned -#pragma warning(push) -#pragma warning(disable : 4146) -#endif // _MSC_VER -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER - namespace Slic3r { -inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } -inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast(); } -inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } +struct VoxelGrid; +struct VoxelGridDeleter { void operator()(VoxelGrid *ptr); }; +using VoxelGridPtr = std::unique_ptr; + +// This is like std::make_unique for a voxelgrid +template VoxelGridPtr make_voxelgrid(Args &&...args); + +// Default constructed voxelgrid can be obtained this way. +extern template VoxelGridPtr make_voxelgrid<>(); + +void reset_accessor(const VoxelGrid &vgrid); + +double get_distance_raw(const Vec3f &p, const VoxelGrid &interior); + +float get_voxel_scale(const VoxelGrid &grid); + +VoxelGridPtr clone(const VoxelGrid &grid); // Here voxel_scale defines the scaling of voxels which affects the voxel count. // 1.0 value means a voxel for every unit cube. 2 means the model is scaled to @@ -26,24 +30,33 @@ inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1 // achievable through the Transform parameter. (TODO: or is it?) // The resulting grid will contain the voxel_scale in its metadata under the // "voxel_scale" key to be used in grid_to_mesh function. -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); +VoxelGridPtr mesh_to_grid(const indexed_triangle_set &mesh, + const Transform3f &tr = Transform3f::Identity(), + float voxel_scale = 1.f, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f); -indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid, - double isovalue = 0.0, - double adaptivity = 0.0, +indexed_triangle_set grid_to_mesh(const VoxelGrid &grid, + double isovalue = 0.0, + double adaptivity = 0.0, bool relaxDisorientedTriangles = true); -openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, - double iso); +VoxelGridPtr dilate_grid(const VoxelGrid &grid, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f); -openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, - double iso, - double ext_range, - double int_range); +VoxelGridPtr redistance_grid(const VoxelGrid &grid, float iso); + +VoxelGridPtr redistance_grid(const VoxelGrid &grid, + float iso, + float ext_range, + float int_range); + +void rescale_grid(VoxelGrid &grid, float scale); + +void grid_union(VoxelGrid &grid, VoxelGrid &arg); +void grid_difference(VoxelGrid &grid, VoxelGrid &arg); +void grid_intersection(VoxelGrid &grid, VoxelGrid &arg); } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtilsLegacy.hpp b/src/libslic3r/OpenVDBUtilsLegacy.hpp new file mode 100644 index 0000000000..f292af1c89 --- /dev/null +++ b/src/libslic3r/OpenVDBUtilsLegacy.hpp @@ -0,0 +1,100 @@ +#ifndef OPENVDBUTILSLEGACY_HPP +#define OPENVDBUTILSLEGACY_HPP + +#include "libslic3r/TriangleMesh.hpp" + +#ifdef _MSC_VER +// Suppress warning C4146 in OpenVDB: unary minus operator applied to unsigned type, result still unsigned +#pragma warning(push) +#pragma warning(disable : 4146) +#endif // _MSC_VER +#include +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +namespace Slic3r { + +openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh, + const openvdb::math::Transform &tr, + float voxel_scale, + float exteriorBandWidth, + float interiorBandWidth) +{ + class TriangleMeshDataAdapter { + public: + const indexed_triangle_set &its; + float voxel_scale; + + size_t polygonCount() const { return its.indices.size(); } + size_t pointCount() const { return its.vertices.size(); } + size_t vertexCount(size_t) const { return 3; } + + // Return position pos in local grid index space for polygon n and vertex v + // The actual mesh will appear to openvdb as scaled uniformly by voxel_size + // And the voxel count per unit volume can be affected this way. + void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const + { + auto vidx = size_t(its.indices[n](Eigen::Index(v))); + Slic3r::Vec3d p = its.vertices[vidx].cast() * voxel_scale; + pos = {p.x(), p.y(), p.z()}; + } + + TriangleMeshDataAdapter(const indexed_triangle_set &m, float voxel_sc = 1.f) + : its{m}, voxel_scale{voxel_sc} {}; + }; + + // Might not be needed but this is now proven to be working + openvdb::initialize(); + + std::vector meshparts = its_split(mesh); + + auto it = std::remove_if(meshparts.begin(), meshparts.end(), + [](auto &m) { return its_volume(m) < EPSILON; }); + + meshparts.erase(it, meshparts.end()); + + openvdb::FloatGrid::Ptr grid; + for (auto &m : meshparts) { + auto subgrid = openvdb::tools::meshToVolume( + 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 (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, 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; +} + +} // namespace Slic3r + +#endif // OPENVDBUTILSLEGACY_HPP diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 74eb076959..3cc8ea340f 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -10,11 +10,10 @@ #include #include #include +#include #include -#include - #include #include @@ -27,19 +26,17 @@ namespace sla { struct Interior { indexed_triangle_set mesh; - openvdb::FloatGrid::Ptr gridptr; - mutable std::optional accessor; + VoxelGridPtr gridptr; double iso_surface = 0.; double thickness = 0.; - double voxel_scale = 1.; double full_narrowb = 2.; void reset_accessor() const // This resets the accessor and its cache // Not a thread safe call! { if (gridptr) - accessor = gridptr->getConstAccessor(); + Slic3r::reset_accessor(*gridptr); } }; @@ -58,29 +55,30 @@ const indexed_triangle_set &get_mesh(const Interior &interior) return interior.mesh; } -static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh, - const JobController &ctl, - double min_thickness, - double voxel_scale, - double closing_dist) +const VoxelGrid &get_grid(const Interior &interior) { - double offset = voxel_scale * min_thickness; - double D = voxel_scale * closing_dist; + return *interior.gridptr; +} + +VoxelGrid &get_grid(Interior &interior) +{ + return *interior.gridptr; +} + +InteriorPtr generate_interior(const VoxelGrid &vgrid, + const HollowingConfig &hc, + const JobController &ctl) +{ + double offset = hc.min_thickness; + double D = hc.closing_distance; float in_range = 1.1f * float(offset + D); - auto narrowb = 1.; + auto narrowb = 3.f / get_voxel_scale(vgrid); float out_range = narrowb; if (ctl.stopcondition()) return {}; else ctl.statuscb(0, L("Hollowing")); - auto gridptr = mesh_to_grid(mesh.its, {}, voxel_scale, out_range, in_range); - - assert(gridptr); - - if (!gridptr) { - BOOST_LOG_TRIVIAL(error) << "Returned OpenVDB grid is NULL"; - return {}; - } + auto gridptr = dilate_grid(vgrid, out_range, in_range); if (ctl.stopcondition()) return {}; else ctl.statuscb(30, L("Hollowing")); @@ -90,12 +88,7 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh, 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); + gridptr = dilate_grid(*gridptr, std::ceil(iso_surface), 0.f); out_range = iso_surface; } else { @@ -109,68 +102,14 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh, InteriorPtr interior = InteriorPtr{new Interior{}}; interior->mesh = grid_to_mesh(*gridptr, iso_surface, adaptivity); - interior->gridptr = gridptr; + interior->gridptr = std::move(gridptr); if (ctl.stopcondition()) return {}; else ctl.statuscb(100, L("Hollowing")); interior->iso_surface = iso_surface; interior->thickness = offset; - interior->voxel_scale = voxel_scale; - interior->full_narrowb = out_range + in_range; - - return interior; -} - -InteriorPtr generate_interior(const TriangleMesh & mesh, - const HollowingConfig &hc, - const JobController & ctl) -{ - 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 - // scaled down. Voxels have a unit size. If I set voxelSize smaller, it - // scales the whole geometry down, and doesn't increase the number of - // voxels. - // - // 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; - - 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()) { - - // flip normals back... - swap_normals(interior->mesh); - - // simplify mesh lossless - float loss_less_max_error = 2*std::numeric_limits::epsilon(); - its_quadric_edge_collapse(interior->mesh, 0U, &loss_less_max_error); - - its_compactify_vertices(interior->mesh); - its_merge_vertices(interior->mesh); - - // flip normals back... - swap_normals(interior->mesh); - } + interior->full_narrowb = (out_range + in_range) / 2.; return interior; } @@ -208,7 +147,6 @@ bool DrainHole::is_inside(const Vec3f& pt) const return false; } - // Given a line s+dir*t, find parameter t of intersections with the hole // and the normal (points inside the hole). Outputs through out reference, // returns true if two intersections were found. @@ -331,7 +269,7 @@ void cut_drainholes(std::vector & obj_slices, void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags) { - InteriorPtr interior = generate_interior(mesh, cfg, JobController{}); + InteriorPtr interior = generate_interior(mesh.its, cfg, JobController{}); if (!interior) return; hollow_mesh(mesh, *interior, flags); @@ -354,13 +292,7 @@ static double get_distance_raw(const Vec3f &p, const Interior &interior) { assert(interior.gridptr); - if (!interior.accessor) interior.reset_accessor(); - - auto v = (p * interior.voxel_scale).cast(); - auto grididx = interior.gridptr->transform().worldToIndexCellCentered( - {v.x(), v.y(), v.z()}); - - return interior.accessor->getValue(grididx) ; + return Slic3r::get_distance_raw(p, *interior.gridptr); } struct TriangleBubble { Vec3f center; double R; }; @@ -369,7 +301,7 @@ struct TriangleBubble { Vec3f center; double R; }; // triangle is too big to be measured. static double get_distance(const TriangleBubble &b, const Interior &interior) { - double R = b.R * interior.voxel_scale; + double R = b.R; double D = 2. * R; double Dst = get_distance_raw(b.center, interior); @@ -379,10 +311,16 @@ static double get_distance(const TriangleBubble &b, const Interior &interior) Dst - interior.iso_surface; } -double get_distance(const Vec3f &p, const Interior &interior) +inline double get_distance(const Vec3f &p, const Interior &interior) { double d = get_distance_raw(p, interior) - interior.iso_surface; - return d / interior.voxel_scale; + return d; +} + +template +FloatingOnly get_distance(const Vec<3, T> &p, const Interior &interior) +{ + return get_distance(Vec3f(p.template cast()), interior); } // A face that can be divided. Stores the indices into the original mesh if its @@ -500,7 +438,7 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, TriangleBubble bubble{facebb.center().cast(), facebb.radius()}; double D = get_distance(bubble, interior); - double R = bubble.R * interior.voxel_scale; + double R = bubble.R; if (std::isnan(D)) // The distance cannot be measured, triangle too big return true; @@ -585,4 +523,223 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, //FIXME do we want to repair the mesh? Are there duplicate vertices or flipped triangles? } +struct FaceHash { + + // A 64 bit number's max hex digits + static constexpr size_t MAX_NUM_CHARS = 16; + + // A hash is created for each triangle to be identifiable. The hash uses + // only the triangle's geometric traits, not the index in a particular mesh. + std::unordered_set facehash; + + // Returns the string in reverse, but that is ok for hashing + static std::array to_chars(int64_t val) + { + std::array ret; + + static const constexpr char * Conv = "0123456789abcdef"; + + auto ptr = ret.begin(); + auto uval = static_cast(std::abs(val)); + while (uval) { + *ptr = Conv[uval & 0xf]; + ++ptr; + uval = uval >> 4; + } + if (val < 0) { *ptr = '-'; ++ptr; } + *ptr = '\0'; // C style string ending + + return ret; + } + + static std::string hash(const Vec<3, int64_t> &v) + { + std::string ret; + ret.reserve(3 * MAX_NUM_CHARS); + + for (auto val : v) + ret += to_chars(val).data(); + + return ret; + } + + static std::string facekey(const Vec3i &face, const std::vector &vertices) + { + // Scale to integer to avoid floating points + std::array, 3> pts = { + scaled(vertices[face(0)]), + scaled(vertices[face(1)]), + scaled(vertices[face(2)]) + }; + + // Get the first two sides of the triangle, do a cross product and move + // that vector to the center of the triangle. This encodes all + // information to identify an identical triangle at the same position. + Vec<3, int64_t> a = pts[0] - pts[2], b = pts[1] - pts[2]; + Vec<3, int64_t> c = a.cross(b) + (pts[0] + pts[1] + pts[2]) / 3; + + // Return a concatenated string representation of the coordinates + return hash(c); + } + + FaceHash (const indexed_triangle_set &its): facehash(its.indices.size()) + { + for (const Vec3i &face : its.indices) + facehash.insert(facekey(face, its.vertices)); + } + + bool find(const std::string &key) + { + auto it = facehash.find(key); + return it != facehash.end(); + } +}; + + +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); + } + } +} + +std::vector create_exclude_mask(const indexed_triangle_set &its, + const Interior &interior, + const std::vector &holes) +{ + FaceHash interior_hash{sla::get_mesh(interior)}; + + std::vector exclude_mask(its.indices.size(), false); + + VertexFaceIndex neighbor_index{its}; + + for (size_t fi = 0; fi < its.indices.size(); ++fi) { + auto &face = its.indices[fi]; + + if (interior_hash.find(FaceHash::facekey(face, its.vertices))) { + exclude_mask[fi] = true; + continue; + } + + if (exclude_mask[fi]) { + exclude_neighbors(face, exclude_mask, its, neighbor_index, 1); + continue; + } + + // Lets deal with the holes. All the triangles of a hole and all the + // neighbors of these triangles need to be kept. The neigbors were + // created by CGAL mesh boolean operation that modified the original + // interior inside the input mesh to contain the holes. + Vec3d tr_center = ( + its.vertices[face(0)] + + its.vertices[face(1)] + + its.vertices[face(2)] + ).cast() / 3.; + + // If the center is more than half a mm inside the interior, + // it cannot possibly be part of a hole wall. + if (sla::get_distance(tr_center, interior) < -0.5) + continue; + + Vec3f U = its.vertices[face(1)] - its.vertices[face(0)]; + Vec3f V = its.vertices[face(2)] - its.vertices[face(0)]; + Vec3f C = U.cross(V); + Vec3f face_normal = C.normalized(); + + for (const sla::DrainHole &dh : holes) { + if (dh.failed) continue; + + Vec3d dhpos = dh.pos.cast(); + Vec3d dhend = dhpos + dh.normal.cast() * dh.height; + + Linef3 holeaxis{dhpos, dhend}; + + double D_hole_center = line_alg::distance_to(holeaxis, tr_center); + double D_hole = std::abs(D_hole_center - dh.radius); + float dot = dh.normal.dot(face_normal); + + // Empiric tolerances for center distance and normals angle. + // For triangles that are part of a hole wall the angle of + // triangle normal and the hole axis is around 90 degrees, + // so the dot product is around zero. + double D_tol = dh.radius / sla::DrainHole::steps; + float normal_angle_tol = 1.f / sla::DrainHole::steps; + + if (D_hole < D_tol && std::abs(dot) < normal_angle_tol) { + exclude_mask[fi] = true; + exclude_neighbors(face, exclude_mask, its, neighbor_index, 1); + } + } + } + + return exclude_mask; +} + +DrainHoles transformed_drainhole_points(const ModelObject &mo, + const Transform3d &trafo) +{ + auto pts = mo.sla_drain_holes; + const Transform3d& vol_trafo = mo.volumes.front()->get_transformation().get_matrix(); + const Geometry::Transformation trans(trafo * vol_trafo); + const Transform3f& tr = trans.get_matrix().cast(); + const Vec3f sc = trans.get_scaling_factor().cast(); + for (sla::DrainHole &hl : pts) { + hl.pos = tr * hl.pos; + hl.normal = tr * hl.normal - tr.translation(); + + // The normal scales as a covector (and we must also + // undo the damage already done). + hl.normal = Vec3f(hl.normal(0)/(sc(0)*sc(0)), + hl.normal(1)/(sc(1)*sc(1)), + hl.normal(2)/(sc(2)*sc(2))); + + // Now shift the hole a bit above the object and make it deeper to + // compensate for it. This is to avoid problems when the hole is placed + // on (nearly) flat surface. + hl.pos -= hl.normal.normalized() * sla::HoleStickOutLength; + hl.height += sla::HoleStickOutLength; + } + + return pts; +} + +double get_voxel_scale(double mesh_volume, const HollowingConfig &hc) +{ + 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 + // scaled down. Voxels have a unit size. If I set voxelSize smaller, it + // scales the whole geometry down, and doesn't increase the number of + // voxels. + // + // 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 sc_divider = std::max(1.0, (mesh_volume / 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; + + 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_volume; + + return voxel_scale; +} + }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index b57513fe72..6ff4660bae 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -3,10 +3,14 @@ #include #include +#include #include +#include namespace Slic3r { +class ModelObject; + namespace sla { struct HollowingConfig @@ -28,6 +32,9 @@ using InteriorPtr = std::unique_ptr; indexed_triangle_set & get_mesh(Interior &interior); const indexed_triangle_set &get_mesh(const Interior &interior); +const VoxelGrid & get_grid(const Interior &interior); +VoxelGrid &get_grid(Interior &interior); + struct DrainHole { Vec3f pos; @@ -46,18 +53,18 @@ struct DrainHole DrainHole(const DrainHole& rhs) : DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {} - + bool operator==(const DrainHole &sp) const; - + bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); } bool is_inside(const Vec3f& pt) const; bool get_intersections(const Vec3f& s, const Vec3f& dir, std::array, 2>& out) const; - + indexed_triangle_set to_mesh() const; - + template inline void serialize(Archive &ar) { ar(pos, normal, radius, height, failed); @@ -70,10 +77,51 @@ using DrainHoles = std::vector; constexpr float HoleStickOutLength = 1.f; -InteriorPtr generate_interior(const TriangleMesh &mesh, +double get_voxel_scale(double mesh_volume, const HollowingConfig &hc); + +InteriorPtr generate_interior(const VoxelGrid &mesh, const HollowingConfig & = {}, const JobController &ctl = {}); +inline InteriorPtr generate_interior(const indexed_triangle_set &mesh, + const HollowingConfig &hc = {}, + const JobController &ctl = {}) +{ + auto voxel_scale = get_voxel_scale(its_volume(mesh), hc); + auto grid = mesh_to_grid(mesh, Transform3f::Identity(), voxel_scale, 1.f, 1.f); + + if (its_is_splittable(mesh)) + grid = redistance_grid(*grid, 0.0f, 6.f / voxel_scale, 6.f / voxel_scale); + + return generate_interior(*grid, hc, ctl); +} + +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))))); + + auto params = csg::VoxelizeParams{} + .voxel_scale(get_voxel_scale(mesh_vol, hc)) + .exterior_bandwidth(1.f) + .interior_bandwidth(1.f); + + auto ptr = csg::voxelize_csgmesh(csgparts, params); + + if (csgparts.size() > 1 || its_is_splittable(*csg::get_mesh(*csgparts.begin()))) + ptr = redistance_grid(*ptr, + 0.0f, + 6.f / params.voxel_scale(), + 6.f / params.voxel_scale()); + + return generate_interior(*ptr, hc, ctl); +} + // Will do the hollowing void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags = 0); @@ -83,13 +131,8 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags = 0); void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, const std::vector &exclude_mask = {}); -double get_distance(const Vec3f &p, const Interior &interior); - -template -FloatingOnly get_distance(const Vec<3, T> &p, const Interior &interior) -{ - return get_distance(Vec3f(p.template cast()), interior); -} +sla::DrainHoles transformed_drainhole_points(const ModelObject &mo, + const Transform3d &trafo); void cut_drainholes(std::vector & obj_slices, const std::vector &slicegrid, @@ -103,6 +146,16 @@ inline void swap_normals(indexed_triangle_set &its) std::swap(face(0), face(2)); } +// 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 +// originally part of the interior mesh and triangles that make up the drilled +// hole walls. +std::vector create_exclude_mask( + const indexed_triangle_set &its, + const sla::Interior &interior, + const std::vector &holes); + } // namespace sla } // namespace Slic3r diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index 0ac29ff7ab..ea785e640a 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -108,6 +108,7 @@ struct SupportableMesh SupportPoints pts; SupportTreeConfig cfg; PadConfig pad_cfg; + double zoffset = 0.; explicit SupportableMesh(const indexed_triangle_set &trmsh, const SupportPoints &sp, @@ -124,7 +125,7 @@ struct SupportableMesh inline double ground_level(const SupportableMesh &sm) { - double lvl = sm.emesh.ground_level() - + double lvl = sm.zoffset - !bool(sm.pad_cfg.embed_object) * sm.cfg.enabled * sm.cfg.object_elevation_mm + bool(sm.pad_cfg.embed_object) * sm.pad_cfg.wall_thickness_mm; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d9b8e33df0..3a93571c1a 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1081,30 +1081,7 @@ sla::SupportPoints SLAPrintObject::transformed_support_points() const sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const { - assert(m_model_object != nullptr); - auto pts = m_model_object->sla_drain_holes; - const Transform3d& vol_trafo = m_model_object->volumes.front()->get_transformation().get_matrix(); - const Geometry::Transformation trans(trafo() * vol_trafo); - const Transform3f& tr = trans.get_matrix().cast(); - const Vec3f sc = trans.get_scaling_factor().cast(); - for (sla::DrainHole &hl : pts) { - hl.pos = tr * hl.pos; - hl.normal = tr * hl.normal - tr.translation(); - - // The normal scales as a covector (and we must also - // undo the damage already done). - hl.normal = Vec3f(hl.normal(0)/(sc(0)*sc(0)), - hl.normal(1)/(sc(1)*sc(1)), - hl.normal(2)/(sc(2)*sc(2))); - - // Now shift the hole a bit above the object and make it deeper to - // compensate for it. This is to avoid problems when the hole is placed - // on (nearly) flat surface. - hl.pos -= hl.normal.normalized() * sla::HoleStickOutLength; - hl.height += sla::HoleStickOutLength; - } - - return pts; + return sla::transformed_drainhole_points(*this->model_object(), trafo()); } DynamicConfig SLAPrintStatistics::config() const diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 528d3c28b8..c18f877b5b 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -318,6 +318,10 @@ private: inline SupportData(const TriangleMesh &t) : input{t.its, {}, {}} {} + + inline SupportData(const indexed_triangle_set &t) + : input{t, {}, {}} + {} void create_support_tree(const sla::JobController &ctl) { diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 4498cb6b01..3ce185bd00 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -16,6 +16,7 @@ #include #include +#include #include @@ -134,209 +135,41 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) double quality = po.m_config.hollowing_quality.getFloat(); double closing_d = po.m_config.hollowing_closing_distance.getFloat(); sla::HollowingConfig hlwcfg{thickness, quality, closing_d}; + sla::JobController ctl; + ctl.stopcondition = [this]() { return canceled(); }; + ctl.cancelfn = [this]() { throw_if_canceled(); }; - sla::InteriorPtr interior = generate_interior(po.transformed_mesh(), hlwcfg); + sla::InteriorPtr interior = generate_interior(po.transformed_mesh().its, hlwcfg, ctl); if (!interior || sla::get_mesh(*interior).empty()) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; else { po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); po.m_hollowing_data->interior = std::move(interior); - } -} -struct FaceHash { + indexed_triangle_set &m = sla::get_mesh(*po.m_hollowing_data->interior); - // A 64 bit number's max hex digits - static constexpr size_t MAX_NUM_CHARS = 16; + if (!m.empty()) { + // simplify mesh lossless + float loss_less_max_error = 2*std::numeric_limits::epsilon(); + its_quadric_edge_collapse(m, 0U, &loss_less_max_error); - // A hash is created for each triangle to be identifiable. The hash uses - // only the triangle's geometric traits, not the index in a particular mesh. - std::unordered_set facehash; + its_compactify_vertices(m); + its_merge_vertices(m); - // Returns the string in reverse, but that is ok for hashing - static std::array to_chars(int64_t val) - { - std::array ret; - - static const constexpr char * Conv = "0123456789abcdef"; - - auto ptr = ret.begin(); - auto uval = static_cast(std::abs(val)); - while (uval) { - *ptr = Conv[uval & 0xf]; - ++ptr; - uval = uval >> 4; - } - if (val < 0) { *ptr = '-'; ++ptr; } - *ptr = '\0'; // C style string ending - - return ret; - } - - static std::string hash(const Vec<3, int64_t> &v) - { - std::string ret; - ret.reserve(3 * MAX_NUM_CHARS); - - for (auto val : v) - ret += to_chars(val).data(); - - return ret; - } - - static std::string facekey(const Vec3i &face, const std::vector &vertices) - { - // Scale to integer to avoid floating points - std::array, 3> pts = { - scaled(vertices[face(0)]), - scaled(vertices[face(1)]), - scaled(vertices[face(2)]) - }; - - // Get the first two sides of the triangle, do a cross product and move - // that vector to the center of the triangle. This encodes all - // information to identify an identical triangle at the same position. - Vec<3, int64_t> a = pts[0] - pts[2], b = pts[1] - pts[2]; - Vec<3, int64_t> c = a.cross(b) + (pts[0] + pts[1] + pts[2]) / 3; - - // Return a concatenated string representation of the coordinates - return hash(c); - } - - FaceHash (const indexed_triangle_set &its): facehash(its.indices.size()) - { - for (const Vec3i &face : its.indices) - facehash.insert(facekey(face, its.vertices)); - } - - bool find(const std::string &key) - { - auto it = facehash.find(key); - return it != facehash.end(); - } -}; - -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); + // flip normals back... + sla::swap_normals(m); } } } -// 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 -// originally part of the interior mesh and triangles that make up the drilled -// hole walls. -static std::vector create_exclude_mask( - const indexed_triangle_set &its, - const sla::Interior &interior, - const std::vector &holes) -{ - FaceHash interior_hash{sla::get_mesh(interior)}; - - std::vector exclude_mask(its.indices.size(), false); - - VertexFaceIndex neighbor_index{its}; - - for (size_t fi = 0; fi < its.indices.size(); ++fi) { - auto &face = its.indices[fi]; - - if (interior_hash.find(FaceHash::facekey(face, its.vertices))) { - exclude_mask[fi] = true; - continue; - } - - if (exclude_mask[fi]) { - exclude_neighbors(face, exclude_mask, its, neighbor_index, 1); - continue; - } - - // Lets deal with the holes. All the triangles of a hole and all the - // neighbors of these triangles need to be kept. The neigbors were - // created by CGAL mesh boolean operation that modified the original - // interior inside the input mesh to contain the holes. - Vec3d tr_center = ( - its.vertices[face(0)] + - its.vertices[face(1)] + - its.vertices[face(2)] - ).cast() / 3.; - - // If the center is more than half a mm inside the interior, - // it cannot possibly be part of a hole wall. - if (sla::get_distance(tr_center, interior) < -0.5) - continue; - - Vec3f U = its.vertices[face(1)] - its.vertices[face(0)]; - Vec3f V = its.vertices[face(2)] - its.vertices[face(0)]; - Vec3f C = U.cross(V); - Vec3f face_normal = C.normalized(); - - for (const sla::DrainHole &dh : holes) { - if (dh.failed) continue; - - Vec3d dhpos = dh.pos.cast(); - Vec3d dhend = dhpos + dh.normal.cast() * dh.height; - - Linef3 holeaxis{dhpos, dhend}; - - double D_hole_center = line_alg::distance_to(holeaxis, tr_center); - double D_hole = std::abs(D_hole_center - dh.radius); - float dot = dh.normal.dot(face_normal); - - // Empiric tolerances for center distance and normals angle. - // For triangles that are part of a hole wall the angle of - // triangle normal and the hole axis is around 90 degrees, - // so the dot product is around zero. - double D_tol = dh.radius / sla::DrainHole::steps; - float normal_angle_tol = 1.f / sla::DrainHole::steps; - - if (D_hole < D_tol && std::abs(dot) < normal_angle_tol) { - exclude_mask[fi] = true; - exclude_neighbors(face, exclude_mask, its, neighbor_index, 1); - } - } - } - - return exclude_mask; -} - static indexed_triangle_set remove_unconnected_vertices(const indexed_triangle_set &its) { if (its.indices.empty()) {}; indexed_triangle_set M; - - std::vector vtransl(its.vertices.size(), -1); - int vcnt = 0; - for (auto &f : its.indices) { - - for (int i = 0; i < 3; ++i) - if (vtransl[size_t(f(i))] < 0) { - - M.vertices.emplace_back(its.vertices[size_t(f(i))]); - vtransl[size_t(f(i))] = vcnt++; - } - - std::array new_f = { - vtransl[size_t(f(0))], - vtransl[size_t(f(1))], - vtransl[size_t(f(2))] - }; - - M.indices.emplace_back(new_f[0], new_f[1], new_f[2]); - } + its_compactify_vertices(M); return M; } @@ -583,6 +416,9 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) if (!po.m_supportdata) po.m_supportdata.reset(new SLAPrintObject::SupportData(po.get_mesh_to_print())); + po.m_supportdata->input.zoffset = bounding_box(po.get_mesh_to_print()) + .min.z(); + const ModelObject& mo = *po.m_model_object; BOOST_LOG_TRIVIAL(debug) << "Support point count " @@ -661,7 +497,7 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) if (is_zero_elevation(po.config())) { remove_bottom_points(po.m_supportdata->input.pts, float( - po.m_supportdata->input.emesh.ground_level() + + po.m_supportdata->input.zoffset + EPSILON)); } diff --git a/src/libslic3r/SlicesToTriangleMesh.cpp b/src/libslic3r/SlicesToTriangleMesh.cpp index 969fa8dacf..3ea64e8781 100644 --- a/src/libslic3r/SlicesToTriangleMesh.cpp +++ b/src/libslic3r/SlicesToTriangleMesh.cpp @@ -52,7 +52,8 @@ indexed_triangle_set slices_to_mesh( Layers layers(slices.size()); size_t len = slices.size() - 1; - tbb::parallel_for(size_t(0), len, [&slices, &layers, &grid](size_t i) { + auto threads_cnt = execution::max_concurrency(ex_tbb); + execution::for_each(ex_tbb, size_t(0), len, [&slices, &layers, &grid](size_t i) { const ExPolygons &upper = slices[i + 1]; const ExPolygons &lower = slices[i]; @@ -64,14 +65,15 @@ indexed_triangle_set slices_to_mesh( its_merge(layers[i], triangulate_expolygons_3d(free_top, grid[i], NORMALS_UP)); its_merge(layers[i], triangulate_expolygons_3d(overhang, grid[i], NORMALS_DOWN)); its_merge(layers[i], straight_walls(upper, grid[i], grid[i + 1])); - }); + }, threads_cnt); auto merge_fn = []( const indexed_triangle_set &a, const indexed_triangle_set &b ) { indexed_triangle_set res{a}; its_merge(res, b); return res; }; auto ret = execution::reduce(ex_tbb, layers.begin(), layers.end(), - indexed_triangle_set{}, merge_fn); + indexed_triangle_set{}, merge_fn, + threads_cnt); its_merge(ret, triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN)); its_merge(ret, straight_walls(slices.front(), zmin, grid.front())); @@ -80,9 +82,14 @@ indexed_triangle_set slices_to_mesh( // FIXME: these repairs do not fix the mesh entirely. There will be cracks // in the output. It is very hard to do the meshing in a way that does not // leave errors. - its_merge_vertices(ret); - its_remove_degenerate_faces(ret); - its_compactify_vertices(ret); + int num_mergedv = its_merge_vertices(ret); + BOOST_LOG_TRIVIAL(debug) << "Merged vertices count: " << num_mergedv; + + int remcnt = its_remove_degenerate_faces(ret); + BOOST_LOG_TRIVIAL(debug) << "Removed degenerate faces count: " << remcnt; + + int num_erasedv = its_compactify_vertices(ret); + BOOST_LOG_TRIVIAL(debug) << "Erased vertices count: " << num_erasedv; return ret; } diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 1f0aa00b23..46a43b5a8f 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -20,7 +20,7 @@ #include "MutablePolygon.hpp" #include "SupportMaterial.hpp" #include "TriangleMeshSlicer.hpp" -#include "OpenVDBUtils.hpp" +#include "OpenVDBUtilsLegacy.hpp" #include #include @@ -3441,7 +3441,7 @@ static void draw_branches( TriangleMesh mesh = print_object.model_object()->raw_mesh(); mesh.transform(print_object.trafo_centered()); double scale = 10.; - openvdb::FloatGrid::Ptr grid = mesh_to_grid(mesh.its, {}, scale, 0., 0.); + openvdb::FloatGrid::Ptr grid = mesh_to_grid(mesh.its, openvdb::math::Transform{}, scale, 0., 0.); closest_surface_point = openvdb::tools::ClosestSurfacePoint::create(*grid); std::vector pts, prev, projections; std::vector distances; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index c288b6d3a6..2496b93d0f 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -267,12 +267,18 @@ inline int its_triangle_edge_index(const stl_triangle_vertex_indices &triangle_i using its_triangle = std::array; +inline its_triangle its_triangle_vertices(const indexed_triangle_set &its, + const Vec3i &face) +{ + return {its.vertices[face(0)], + its.vertices[face(1)], + its.vertices[face(2)]}; +} + inline its_triangle its_triangle_vertices(const indexed_triangle_set &its, size_t face_id) { - return {its.vertices[its.indices[face_id](0)], - its.vertices[its.indices[face_id](1)], - its.vertices[its.indices[face_id](2)]}; + return its_triangle_vertices(its, its.indices[face_id]); } inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its, @@ -344,6 +350,22 @@ inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its) return {bmin.cast(), bmax.cast()}; } +inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its, const Transform3f &tr) +{ + if (its.vertices.empty()) + return {}; + + Vec3f bmin = tr * its.vertices.front(), bmax = tr * its.vertices.front(); + + for (const Vec3f &p : its.vertices) { + Vec3f pp = tr * p; + bmin = pp.cwiseMin(bmin); + bmax = pp.cwiseMax(bmax); + } + + return {bmin.cast(), bmax.cast()}; +} + } // Serialization through the Cereal library diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 1afd368aae..9b8a823487 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -352,10 +352,15 @@ public: Range(It b, It e) : from(std::move(b)), to(std::move(e)) {} // Some useful container-like methods... - inline size_t size() const { return end() - begin(); } - inline bool empty() const { return size() == 0; } + inline size_t size() const { return std::distance(from, to); } + inline bool empty() const { return from == to; } }; +template auto range(Cont &&cont) +{ + return Range{std::begin(cont), std::end(cont)}; +} + template> constexpr T NaN = std::numeric_limits::quiet_NaN(); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 9db2ed1b1f..1815df9cac 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -157,13 +157,17 @@ class MeshRaycaster { public: #if ENABLE_RAYCAST_PICKING explicit MeshRaycaster(std::shared_ptr mesh) - : m_mesh(mesh) - , m_emesh(*mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length - , m_normals(its_face_normals(mesh->its)) + : m_mesh(std::move(mesh)) + , m_emesh(*m_mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length + , m_normals(its_face_normals(m_mesh->its)) { - assert(m_mesh != nullptr); + assert(m_mesh); } + explicit MeshRaycaster(const TriangleMesh &mesh) + : MeshRaycaster(std::make_unique(mesh)) + {} + static void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction); #else diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 737659eed9..00825997c9 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -101,7 +101,7 @@ void test_supports(const std::string &obj_filename, REQUIRE_FALSE(mesh.empty()); if (hollowingcfg.enabled) { - sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg); + sla::InteriorPtr interior = sla::generate_interior(mesh.its, hollowingcfg); REQUIRE(interior); mesh.merge(TriangleMesh{sla::get_mesh(*interior)}); } From c448b312043aa7ec54107bf4e862a252c84d0ef4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Oct 2022 17:23:34 +0200 Subject: [PATCH 02/75] wip --- src/libslic3r/SLAPrint.cpp | 57 ++--- src/libslic3r/SLAPrint.hpp | 78 +++++-- src/libslic3r/SLAPrintSteps.cpp | 398 ++++++++++++++++---------------- src/libslic3r/SLAPrintSteps.hpp | 1 + 4 files changed, 295 insertions(+), 239 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 3a93571c1a..9982a14768 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1,13 +1,7 @@ #include "SLAPrint.hpp" #include "SLAPrintSteps.hpp" -#include "Format/SL1.hpp" -#include "Format/SL1_SVG.hpp" -#include "Format/pwmx.hpp" - -#include "ClipperUtils.hpp" #include "Geometry.hpp" -#include "MTUtils.hpp" #include "Thread.hpp" #include @@ -388,7 +382,12 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id()) it_print_object_status = print_object_status.end(); // Check whether a model part volume was added or removed, their transformations or order changed. - bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART); + bool model_parts_differ = + model_volume_list_changed(model_object, model_object_new, + {ModelVolumeType::MODEL_PART, + ModelVolumeType::NEGATIVE_VOLUME, + ModelVolumeType::SUPPORT_ENFORCER, + ModelVolumeType::SUPPORT_BLOCKER}); bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() || (! model_object.instances.empty() && @@ -597,7 +596,7 @@ void SLAPrint::process() // We want to first process all objects... std::vector level1_obj_steps = { - slaposHollowing, slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad + slaposAssembly, slaposHollowing, slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad }; // and then slice all supports to allow preview to be displayed ASAP @@ -793,12 +792,13 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object) : Inherited(print, model_object) - , m_transformed_rmesh([this](TriangleMesh &obj) { - obj = m_model_object->raw_mesh(); - if (!obj.empty()) { - obj.transform(m_trafo); - } - }) +// , m_transformed_rmesh([this](TriangleMesh &obj) { +//// obj = m_model_object->raw_mesh(); +//// if (!obj.empty()) { +//// obj.transform(m_trafo); +//// } +// obj = transformed_mesh_csg(*this); +// }) {} SLAPrintObject::~SLAPrintObject() {} @@ -882,8 +882,10 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) { bool invalidated = Inherited::invalidate_step(step); // propagate to dependent steps - if (step == slaposHollowing) { + if (step == slaposAssembly) { invalidated |= this->invalidate_all_steps(); + } else if (step == slaposHollowing) { + invalidated |= invalidated |= this->invalidate_steps({ slaposDrillHoles, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports }); } else if (step == slaposDrillHoles) { invalidated |= this->invalidate_steps({ slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports }); invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); @@ -907,7 +909,7 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) bool SLAPrintObject::invalidate_all_steps() { - return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps(); + return Inherited::invalidate_all_steps() || m_print->invalidate_all_steps(); } double SLAPrintObject::get_elevation() const { @@ -1001,8 +1003,12 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const { switch (step) { + case slaposAssembly: + return true; + case slaposObjectSlice: + return ! this->m_mesh_from_slices.empty(); case slaposDrillHoles: - return m_hollowing_data && !m_hollowing_data->hollow_mesh_with_holes.empty(); + return ! this->m_mesh_from_slices.empty(); case slaposSupportTree: return ! this->support_mesh().empty(); case slaposPad: @@ -1015,12 +1021,16 @@ bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const { switch (step) { + case slaposAssembly: + return m_transformed_rmesh; + case slaposObjectSlice: + return this->m_mesh_from_slices; case slaposSupportTree: return this->support_mesh(); case slaposPad: return this->pad_mesh(); case slaposDrillHoles: - if (m_hollowing_data) +// if (m_hollowing_data) return get_mesh_to_print(); [[fallthrough]]; default: @@ -1054,16 +1064,7 @@ const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const } const TriangleMesh &SLAPrintObject::transformed_mesh() const { - // we need to transform the raw mesh... - // currently all the instances share the same x and y rotation and scaling - // so we have to extract those from e.g. the first instance and apply to the - // raw mesh. This is also true for the support points. - // BUT: when the support structure is spawned for each instance than it has - // to omit the X, Y rotation and scaling as those have been already applied - // or apply an inverse transformation on the support structure after it - // has been created. - - return m_transformed_rmesh.get(); + return m_transformed_rmesh; } sla::SupportPoints SLAPrintObject::transformed_support_points() const diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index c18f877b5b..f6d23cf896 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -3,16 +3,15 @@ #include #include +#include + #include "PrintBase.hpp" -#include "SLA/RasterBase.hpp" #include "SLA/SupportTree.hpp" #include "Point.hpp" -#include "MTUtils.hpp" -#include "Zipper.hpp" #include "Format/SLAArchiveWriter.hpp" #include "GCode/ThumbnailData.hpp" - -#include "libslic3r/Execution/ExecutionTBB.hpp" +#include "libslic3r/CSGMesh/CSGMesh.hpp" +#include "libslic3r/OpenVDBUtils.hpp" namespace Slic3r { @@ -23,6 +22,7 @@ enum SLAPrintStep : unsigned int { }; enum SLAPrintObjectStep : unsigned int { + slaposAssembly, slaposHollowing, slaposDrillHoles, slaposObjectSlice, @@ -45,6 +45,53 @@ using _SLAPrintObjectBase = enum SliceOrigin { soSupport, soModel }; +// Each sla object step can hold a collection of csg operations on the +// sla model to be sliced. Currently, Assembly step adds negative and positive +// volumes, hollowing adds the negative interior, drilling adds the hole cylinders. +// They need to be processed in this specific order. If CSGPartForStep instances +// are put into a multiset container the key being the sla step, +// iterating over the container will maintain the correct order of csg operations. +struct CSGPartForStep : public csg::CSGPart +{ + SLAPrintObjectStep key; + mutable struct { VoxelGridPtr gridptr; csg::VoxelizeParams params; } cache; + + CSGPartForStep(SLAPrintObjectStep k, CSGPart &&p = {}) + : key{k}, CSGPart{std::move(p)} + {} + + CSGPartForStep &operator=(CSGPart &&part) + { + this->its_ptr = std::move(part.its_ptr); + this->operation = part.operation; + return *this; + } + + bool operator<(const CSGPartForStep &other) const { return key < other.key; } +}; + +namespace csg { + +//inline VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, +// const VoxelizeParams &p) +//{ +// if (!part.cache.gridptr || get_voxel_scale(*part.cache.gridptr) < p.voxel_scale()) { +// part.cache.gridptr = mesh_to_grid(*csg::get_mesh(part), p.voxel_scale(), +// p.exterior_bandwidth(), +// p.interior_bandwidth()); + +// } /*else { +// float vscale = p.voxel_scale(); +// float oscale = get_voxel_scale(*part.cache.gridptr); + +// rescale_grid(*part.cache.gridptr, oscale/vscale); +// }*/ + +// return clone(*part.cache.gridptr); +//} + +} // namespace csg + class SLAPrintObject : public _SLAPrintObjectBase { private: // Prevents erroneous use by other classes. @@ -88,11 +135,7 @@ public: // Get the mesh that is going to be printed with all the modifications // like hollowing and drilled holes. const TriangleMesh & get_mesh_to_print() const { - return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes_trimmed : transformed_mesh(); - } - - const TriangleMesh & get_mesh_to_slice() const { - return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh(); + return !m_mesh_from_slices.empty() ? m_mesh_from_slices : transformed_mesh(); } // This will return the transformed mesh which is cached @@ -275,7 +318,8 @@ protected: { m_config.apply_only(other, keys, ignore_nonexistent); } void set_trafo(const Transform3d& trafo, bool left_handed) { - m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; }); + m_trafo = trafo; + m_left_handed = left_handed; } template inline void set_instances(InstVec&& instances) { m_instances = std::forward(instances); } @@ -307,7 +351,7 @@ private: std::vector m_model_height_levels; // Caching the transformed (m_trafo) raw mesh of the object - mutable CachedObject m_transformed_rmesh; + TriangleMesh m_transformed_rmesh; struct SupportData { @@ -333,16 +377,16 @@ private: pad_mesh = TriangleMesh{sla::create_pad(input, tree_mesh.its, ctl)}; } }; - - std::unique_ptr m_supportdata; - + + std::unique_ptr m_supportdata; + TriangleMesh m_mesh_from_slices; + std::multiset m_mesh_to_slice; + class HollowingData { public: sla::InteriorPtr interior; - mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh - mutable TriangleMesh hollow_mesh_with_holes_trimmed; }; std::unique_ptr m_hollowing_data; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 3ce185bd00..74041c6358 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -14,6 +14,12 @@ #include #include +#include +#include +#include +#include +#include +#include #include #include @@ -33,7 +39,8 @@ namespace Slic3r { namespace { const std::array OBJ_STEP_LEVELS = { - 10, // slaposHollowing, + 5, // slaposAssembly + 5, // slaposHollowing, 10, // slaposDrillHoles 10, // slaposObjectSlice, 20, // slaposSupportPoints, @@ -45,6 +52,7 @@ const std::array OBJ_STEP_LEVELS = { std::string OBJ_STEP_LABELS(size_t idx) { switch (idx) { + case slaposAssembly: return L("Assembling model from parts"); case slaposHollowing: return L("Hollowing model"); case slaposDrillHoles: return L("Drilling holes into model."); case slaposObjectSlice: return L("Slicing model"); @@ -120,9 +128,38 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin } } +void clear_csg(std::multiset &s, SLAPrintObjectStep step) +{ + auto r = s.equal_range(step); + s.erase(r.first, r.second); +} + +struct csg_inserter { + std::multiset &m; + SLAPrintObjectStep key; + + csg_inserter &operator*() { return *this; } + void operator=(csg::CSGPart &&part) { m.emplace(key, std::move(part)); } + csg_inserter& operator++() { return *this; } +}; + +void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po) +{ + po.m_mesh_to_slice.clear(); + + po.m_transformed_rmesh = po.m_model_object->raw_mesh(); + po.m_transformed_rmesh.transform(po.trafo()); + + csg::model_to_csgmesh(*po.model_object(), po.trafo(), + csg_inserter{po.m_mesh_to_slice, slaposAssembly}, + csg::mpartsPositive | csg::mpartsNegative); +} + void SLAPrint::Steps::hollow_model(SLAPrintObject &po) { po.m_hollowing_data.reset(); + clear_csg(po.m_mesh_to_slice, slaposDrillHoles); + clear_csg(po.m_mesh_to_slice, slaposHollowing); if (! po.m_config.hollowing_enable.getBool()) { BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; @@ -139,7 +176,12 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) ctl.stopcondition = [this]() { return canceled(); }; ctl.cancelfn = [this]() { throw_if_canceled(); }; - sla::InteriorPtr interior = generate_interior(po.transformed_mesh().its, hlwcfg, ctl); + sla::JobController ctl; + ctl.stopcondition = [this]() { return canceled(); }; + ctl.cancelfn = [this]() { throw_if_canceled(); }; + + sla::InteriorPtr interior = + generate_interior(range(po.m_mesh_to_slice), hlwcfg, ctl); if (!interior || sla::get_mesh(*interior).empty()) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; @@ -147,166 +189,74 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); po.m_hollowing_data->interior = std::move(interior); - indexed_triangle_set &m = sla::get_mesh(*po.m_hollowing_data->interior); - - if (!m.empty()) { - // simplify mesh lossless - float loss_less_max_error = 2*std::numeric_limits::epsilon(); - its_quadric_edge_collapse(m, 0U, &loss_less_max_error); - - its_compactify_vertices(m); - its_merge_vertices(m); - - // flip normals back... - sla::swap_normals(m); - } + // Put the interior into the target mesh as a negative + po.m_mesh_to_slice + .emplace(slaposHollowing, + csg::CSGPart{&sla::get_mesh(*po.m_hollowing_data->interior), + csg::CSGType::Difference}); } } -static indexed_triangle_set -remove_unconnected_vertices(const indexed_triangle_set &its) -{ - if (its.indices.empty()) {}; - - indexed_triangle_set M; - its_compactify_vertices(M); - - return M; -} - // Drill holes into the hollowed/original mesh. void SLAPrint::Steps::drill_holes(SLAPrintObject &po) { - bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty(); - bool is_hollowed = - (po.m_hollowing_data && po.m_hollowing_data->interior && - !sla::get_mesh(*po.m_hollowing_data->interior).empty()); + clear_csg(po.m_mesh_to_slice, slaposDrillHoles); - if (! is_hollowed && ! needs_drilling) { - // In this case we can dump any data that might have been - // generated on previous runs. - po.m_hollowing_data.reset(); - return; - } + csg::model_to_csgmesh(*po.model_object(), po.trafo(), + csg_inserter{po.m_mesh_to_slice, slaposDrillHoles}, + csg::mpartsDrillHoles); - if (! po.m_hollowing_data) - po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); + // update preview mesh + double vscale = 1. / 0.05; + auto voxparams = csg::VoxelizeParams{} + .voxel_scale(vscale) + .exterior_bandwidth(1.f) + .interior_bandwidth(1.f); + auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams); + indexed_triangle_set mesh_from_slices = grid_to_mesh(*grid); + po.m_mesh_from_slices = TriangleMesh{mesh_from_slices}; +} - // Hollowing and/or drilling is active, m_hollowing_data is valid. - - // Regenerate hollowed mesh, even if it was there already. It may contain - // holes that are no longer on the frontend. - TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; - hollowed_mesh = po.transformed_mesh(); - if (is_hollowed) - sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior); - - TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed; - - if (! needs_drilling) { - mesh_view = po.transformed_mesh(); - - if (is_hollowed) - sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior, - sla::hfRemoveInsideTriangles); - - BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; - return; - } - - BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes."; - sla::DrainHoles drainholes = po.transformed_drainhole_points(); - - auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( - hollowed_mesh.its.vertices, - hollowed_mesh.its.indices - ); - - std::uniform_real_distribution dist(0., float(EPSILON)); - auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}, {}); - indexed_triangle_set part_to_drill = hollowed_mesh.its; - - bool hole_fail = false; - for (size_t i = 0; i < drainholes.size(); ++i) { - sla::DrainHole holept = drainholes[i]; - - holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; - holept.normal.normalize(); - holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; - indexed_triangle_set m = holept.to_mesh(); - - part_to_drill.indices.clear(); - auto bb = bounding_box(m); - Eigen::AlignedBox ebb{bb.min.cast(), - bb.max.cast()}; - - AABBTreeIndirect::traverse( - tree, - AABBTreeIndirect::intersecting(ebb), - [&part_to_drill, &hollowed_mesh](const auto& node) - { - part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[node.idx]); - }); - - auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal( - remove_unconnected_vertices(part_to_drill)); - - if (MeshBoolean::cgal::does_self_intersect(*cgal_meshpart)) { - BOOST_LOG_TRIVIAL(error) << "Failed to drill hole"; - - hole_fail = drainholes[i].failed = - po.model_object()->sla_drain_holes[i].failed = true; - - continue; +template +static std::vector slice_volumes( + const ModelVolumePtrs &volumes, + const std::vector &slice_grid, + const Transform3d &trafo, + const MeshSlicingParamsEx &slice_params, + Pred &&predicate) +{ + indexed_triangle_set mesh; + for (const ModelVolume *vol : volumes) { + if (predicate(vol)) { + indexed_triangle_set vol_mesh = vol->mesh().its; + its_transform(vol_mesh, trafo * vol->get_matrix()); + its_merge(mesh, vol_mesh); } - - auto cgal_hole = MeshBoolean::cgal::triangle_mesh_to_cgal(m); - MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_hole); } - if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) - throw Slic3r::SlicingError(L("Too many overlapping holes.")); + std::vector out; - auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); - - if (!MeshBoolean::cgal::does_bound_a_volume(*hollowed_mesh_cgal)) { - po.active_step_add_warning( - PrintStateBase::WarningLevel::NON_CRITICAL, - L("Mesh to be hollowed is not suitable for hollowing (does not " - "bound a volume).")); + if (!mesh.empty()) { + out = slice_mesh_ex(mesh, slice_grid, slice_params); } - if (!MeshBoolean::cgal::empty(*holes_mesh_cgal) - && !MeshBoolean::cgal::does_bound_a_volume(*holes_mesh_cgal)) { - po.active_step_add_warning( - PrintStateBase::WarningLevel::NON_CRITICAL, - L("Unable to drill the current configuration of holes into the " - "model.")); + return out; +} + +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())); + + for (const auto &m : csg) { + if (m.operation == csg::CSGType::Union) + bb3d.merge(bounding_box(*csg::get_mesh(m), csg::get_transform(m))); } - try { - if (!MeshBoolean::cgal::empty(*holes_mesh_cgal)) - MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); - - hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); - mesh_view = hollowed_mesh; - - if (is_hollowed) { - auto &interior = *po.m_hollowing_data->interior; - std::vector exclude_mask = - create_exclude_mask(mesh_view.its, interior, drainholes); - - sla::remove_inside_triangles(mesh_view, interior, exclude_mask); - } - } catch (const Slic3r::RuntimeError &) { - throw Slic3r::SlicingError(L( - "Drilling holes into the mesh failed. " - "This is usually caused by broken model. Try to fix it first.")); - } - - if (hole_fail) - po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, - L("Failed to drill some holes into the model")); + return bb3d; } // The slicing will be performed on an imaginary 1D grid which starts from @@ -319,14 +269,17 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) // same imaginary grid (the height vector argument to TriangleMeshSlicer). void SLAPrint::Steps::slice_model(SLAPrintObject &po) { - const TriangleMesh &mesh = po.get_mesh_to_slice(); + // The first mesh in the csg sequence is assumed to be a positive part + assert(po.m_mesh_to_slice.empty() || + csg::get_operation(*po.m_mesh_to_slice.begin()) == csg::CSGType::Union); + + auto bb3d = csgmesh_positive_bb(po.m_mesh_to_slice); // We need to prepare the slice index... double lhd = m_print->m_objects.front()->m_config.layer_height.getFloat(); float lh = float(lhd); coord_t lhs = scaled(lhd); - auto && bb3d = mesh.bounding_box(); double minZ = bb3d.min(Z) - po.get_elevation(); double maxZ = bb3d.max(Z); auto minZf = float(minZ); @@ -368,27 +321,9 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) } auto thr = [this]() { m_print->throw_if_canceled(); }; auto &slice_grid = po.m_model_height_levels; - po.m_model_slices = slice_mesh_ex(mesh.its, slice_grid, params, thr); - sla::Interior *interior = po.m_hollowing_data ? - po.m_hollowing_data->interior.get() : - nullptr; - - if (interior && ! sla::get_mesh(*interior).empty()) { - indexed_triangle_set interiormesh = sla::get_mesh(*interior); - sla::swap_normals(interiormesh); - params.mode = MeshSlicingParams::SlicingMode::Regular; - - std::vector interior_slices = slice_mesh_ex(interiormesh, slice_grid, params, thr); - - execution::for_each( - ex_tbb, size_t(0), interior_slices.size(), - [&po, &interior_slices](size_t i) { - const ExPolygons &slice = interior_slices[i]; - po.m_model_slices[i] = diff_ex(po.m_model_slices[i], slice); - }, - execution::max_concurrency(ex_tbb)); - } + Range csgrange = {po.m_mesh_to_slice.begin(), po.m_mesh_to_slice.end()}; + po.m_model_slices = slice_csgmesh_ex(csgrange, slice_grid, params, thr); auto mit = slindex_it; for (size_t id = 0; @@ -400,10 +335,86 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) // We apply the printer correction offset here. apply_printer_corrections(po, soModel); - if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool()) - { - po.m_supportdata.reset(new SLAPrintObject::SupportData(po.get_mesh_to_print())); +// auto simpl_slices = reserve_vector(po.m_model_slices.size()); +// for (const ExPolygons &slice : po.m_model_slices) { +// simpl_slices.emplace_back(expolygons_simplify(slice, scaled(1e-2))); +// } + +// po.m_mesh_from_slices = TriangleMesh{ +// slices_to_mesh(simpl_slices, slice_grid.front(), lhd, ilhd)}; + + double vscale = 1. / lhd; + auto voxparams = csg::VoxelizeParams{} + .voxel_scale(vscale) + .exterior_bandwidth(1.f) + .interior_bandwidth(1.f); + auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams); + + assert(grid); + +// size_t max_face_cnt = 0; +// for (const CSGMesh &part : po.m_mesh_to_slice) +// max_face_cnt += part.its_ptr->indices.size(); + + indexed_triangle_set mesh_from_slices = grid_to_mesh(*grid); + +// its_quadric_edge_collapse(mesh_from_slices, vscale * max_face_cnt); + +// its_compactify_vertices(mesh_from_slices); +// its_merge_vertices(mesh_from_slices); + + po.m_mesh_from_slices = TriangleMesh{mesh_from_slices}; + +// po.m_mesh_from_slices = TriangleMesh{sla::get_mesh(*po.m_hollowing_data->interior)}; + +} + +static void filter_support_points_by_modifiers( + sla::SupportPoints &pts, + const std::vector &blockers, + const std::vector &enforcers, + const std::vector &slice_grid) +{ + assert((blockers.empty() || blockers.size() == slice_grid.size()) && + (enforcers.empty() || enforcers.size() == slice_grid.size())); + + auto new_pts = reserve_vector(pts.size()); + + for (size_t i = 0; i < pts.size(); ++i) { + const sla::SupportPoint &sp = pts[i]; + Point sp2d = scaled(to_2d(sp.pos)); + + auto it = std::lower_bound(slice_grid.begin(), slice_grid.end(), sp.pos.z()); + if (it != slice_grid.end()) { + size_t idx = std::distance(slice_grid.begin(), it); + bool is_enforced = false; + if (idx < enforcers.size()) { + for (size_t enf_idx = 0; + !is_enforced && enf_idx < enforcers[idx].size(); + ++enf_idx) + { + if (enforcers[idx][enf_idx].contains_b(sp2d)) + is_enforced = true; + } + } + + bool is_blocked = false; + if (!is_enforced && idx < blockers.size()) { + for (size_t blk_idx = 0; + !is_blocked && blk_idx < blockers[idx].size(); + ++blk_idx) + { + if (blockers[idx][blk_idx].contains_b(sp2d)) + is_blocked = true; + } + } + + if (!is_blocked) + new_pts.emplace_back(sp); + } } + + pts.swap(new_pts); } // In this step we check the slices, identify island and cover them with @@ -414,9 +425,9 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) if(!po.m_config.supports_enable.getBool()) return; if (!po.m_supportdata) - po.m_supportdata.reset(new SLAPrintObject::SupportData(po.get_mesh_to_print())); + po.m_supportdata.reset(new SLAPrintObject::SupportData(po.m_mesh_from_slices)); - po.m_supportdata->input.zoffset = bounding_box(po.get_mesh_to_print()) + po.m_supportdata->input.zoffset = csgmesh_positive_bb(po.m_mesh_to_slice) .min.z(); const ModelObject& mo = *po.m_model_object; @@ -432,11 +443,6 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // calculate heights of slices (slices are calculated already) const std::vector& heights = po.m_model_height_levels; - // Tell the mesh where drain holes are. Although the points are - // calculated on slices, the algorithm then raycasts the points - // so they actually lie on the mesh. -// po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); - throw_if_canceled(); sla::SupportPointGenerator::Config config; const SLAPrintObjectConfig& cfg = po.config(); @@ -464,8 +470,27 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) heights, config, [this]() { throw_if_canceled(); }, statuscb); // Now let's extract the result. - const std::vector& points = auto_supports.output(); + std::vector& points = auto_supports.output(); throw_if_canceled(); + + MeshSlicingParamsEx params; + params.closing_radius = float(po.config().slice_closing_radius.value); + std::vector blockers = + slice_volumes(po.model_object()->volumes, + po.m_model_height_levels, po.trafo(), params, + [](const ModelVolume *vol) { + return vol->is_support_blocker(); + }); + + std::vector enforcers = + slice_volumes(po.model_object()->volumes, + po.m_model_height_levels, po.trafo(), params, + [](const ModelVolume *vol) { + return vol->is_support_enforcer(); + }); + + filter_support_points_by_modifiers(points, blockers, enforcers, po.m_model_height_levels); + po.m_supportdata->input.pts = points; BOOST_LOG_TRIVIAL(debug) @@ -487,11 +512,6 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) { if(!po.m_supportdata) return; -// sla::PadConfig pcfg = make_pad_cfg(po.m_config); - -// if (pcfg.embed_object) -// po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); - // If the zero elevation mode is engaged, we have to filter out all the // points that are on the bottom of the object if (is_zero_elevation(po.config())) { @@ -503,7 +523,6 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) po.m_supportdata->input.cfg = make_support_cfg(po.m_config); po.m_supportdata->input.pad_cfg = make_pad_cfg(po.m_config); -// po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); // scaling for the sub operations double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0; @@ -554,16 +573,6 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { sla::PadConfig pcfg = make_pad_cfg(po.m_config); po.m_supportdata->input.pad_cfg = pcfg; -// if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { -// // No support (thus no elevation) or zero elevation mode -// // we sometimes call it "builtin pad" is enabled so we will -// // get a sample from the bottom of the mesh and use it for pad -// // creation. -// sla::pad_blueprint(trmesh.its, bp, float(pad_h), -// float(po.m_config.layer_height.getFloat()), -// [this](){ throw_if_canceled(); }); -// } - sla::JobController ctl; ctl.stopcondition = [this]() { return canceled(); }; ctl.cancelfn = [this]() { throw_if_canceled(); }; @@ -996,6 +1005,7 @@ double SLAPrint::Steps::progressrange(SLAPrintStep step) const void SLAPrint::Steps::execute(SLAPrintObjectStep step, SLAPrintObject &obj) { switch(step) { + case slaposAssembly: mesh_assembly(obj); break; case slaposHollowing: hollow_model(obj); break; case slaposDrillHoles: drill_holes(obj); break; case slaposObjectSlice: slice_model(obj); break; diff --git a/src/libslic3r/SLAPrintSteps.hpp b/src/libslic3r/SLAPrintSteps.hpp index 19b64d4a98..1a1900153d 100644 --- a/src/libslic3r/SLAPrintSteps.hpp +++ b/src/libslic3r/SLAPrintSteps.hpp @@ -48,6 +48,7 @@ private: public: explicit Steps(SLAPrint *print); + void mesh_assembly(SLAPrintObject &po); void hollow_model(SLAPrintObject &po); void drill_holes (SLAPrintObject &po); void slice_model(SLAPrintObject& po); From 99581a5e53428e7bca4b00cfdd67da9e887c6929 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Oct 2022 17:50:24 +0200 Subject: [PATCH 03/75] Fix unguarded header --- src/libslic3r/QuadricEdgeCollapse.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libslic3r/QuadricEdgeCollapse.hpp b/src/libslic3r/QuadricEdgeCollapse.hpp index 956ad35113..0043fc24a3 100644 --- a/src/libslic3r/QuadricEdgeCollapse.hpp +++ b/src/libslic3r/QuadricEdgeCollapse.hpp @@ -4,6 +4,8 @@ // paper: https://people.eecs.berkeley.edu/~jrs/meshpapers/GarlandHeckbert2.pdf // sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/ // inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification +#ifndef PRUSASLICER_QUADRIC_EDGE_COLLAPSE_HPP +#define PRUSASLICER_QUADRIC_EDGE_COLLAPSE_HPP #include #include @@ -30,3 +32,5 @@ void its_quadric_edge_collapse( } // namespace Slic3r #endif // slic3r_quadric_edge_collapse_hpp_ + +#endif // PRUSASLICER_QUADRIC_EDGE_COLLAPSE_HPP From ab9f231c0c303511cfb565738fa7e05a7dcf37d2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Oct 2022 17:52:00 +0200 Subject: [PATCH 04/75] Fix broken compilation --- src/libslic3r/SLAPrintSteps.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 74041c6358..a024a799c0 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -176,10 +176,6 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) ctl.stopcondition = [this]() { return canceled(); }; ctl.cancelfn = [this]() { throw_if_canceled(); }; - sla::JobController ctl; - ctl.stopcondition = [this]() { return canceled(); }; - ctl.cancelfn = [this]() { throw_if_canceled(); }; - sla::InteriorPtr interior = generate_interior(range(po.m_mesh_to_slice), hlwcfg, ctl); From 39a1ed0e1ae16d8b12648d344f67b68ac14ff810 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Oct 2022 18:29:00 +0200 Subject: [PATCH 05/75] WIP: prepare frontend to use new sla backend interface --- src/libslic3r/SLAPrint.cpp | 100 +++++++++++++++++----------------- src/libslic3r/SLAPrint.hpp | 14 ++--- src/slic3r/GUI/GLCanvas3D.cpp | 78 +++++++++++++------------- 3 files changed, 97 insertions(+), 95 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 9982a14768..716029257b 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1000,72 +1000,74 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const return idx >= v.size() ? EMPTY_SLICE : v[idx]; } -bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const -{ - switch (step) { - case slaposAssembly: - return true; - case slaposObjectSlice: - return ! this->m_mesh_from_slices.empty(); - case slaposDrillHoles: - return ! this->m_mesh_from_slices.empty(); - case slaposSupportTree: - return ! this->support_mesh().empty(); - case slaposPad: - return ! this->pad_mesh().empty(); - default: - return false; - } -} +//bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const +//{ +// switch (step) { +// case slaposAssembly: +// return true; +// case slaposObjectSlice: +// return ! this->m_mesh_from_slices.empty(); +// case slaposDrillHoles: +// return ! this->m_mesh_from_slices.empty(); +// case slaposSupportTree: +// return ! this->support_mesh().empty(); +// case slaposPad: +// return ! this->pad_mesh().empty(); +// default: +// return false; +// } +//} -TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const -{ - switch (step) { - case slaposAssembly: - return m_transformed_rmesh; - case slaposObjectSlice: - return this->m_mesh_from_slices; - case slaposSupportTree: - return this->support_mesh(); - case slaposPad: - return this->pad_mesh(); - case slaposDrillHoles: -// if (m_hollowing_data) - return get_mesh_to_print(); - [[fallthrough]]; - default: - return TriangleMesh(); - } -} +//TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const +//{ +// switch (step) { +// case slaposAssembly: +// return m_transformed_rmesh; +// case slaposObjectSlice: +// return this->m_mesh_from_slices; +// case slaposSupportTree: +// return this->support_mesh(); +// case slaposPad: +// return this->pad_mesh(); +// case slaposDrillHoles: +//// if (m_hollowing_data) +// return get_mesh_to_print(); +// [[fallthrough]]; +// default: +// return TriangleMesh(); +// } +//} const TriangleMesh& SLAPrintObject::support_mesh() const { - if(m_config.supports_enable.getBool() && m_supportdata) + if (m_config.supports_enable.getBool() && + is_step_done(slaposSupportTree) && + m_supportdata) return m_supportdata->tree_mesh; - + return EMPTY_MESH; } const TriangleMesh& SLAPrintObject::pad_mesh() const { - if(m_config.pad_enable.getBool() && m_supportdata) + if(m_config.pad_enable.getBool() && is_step_done(slaposPad) && m_supportdata) return m_supportdata->pad_mesh; return EMPTY_MESH; } -const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const -{ - if (m_hollowing_data && m_hollowing_data->interior && - m_config.hollowing_enable.getBool()) - return sla::get_mesh(*m_hollowing_data->interior); +//const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const +//{ +// if (m_hollowing_data && m_hollowing_data->interior && +// m_config.hollowing_enable.getBool()) +// return sla::get_mesh(*m_hollowing_data->interior); - return EMPTY_TRIANGLE_SET; -} +// return EMPTY_TRIANGLE_SET; +//} -const TriangleMesh &SLAPrintObject::transformed_mesh() const { - return m_transformed_rmesh; -} +//const TriangleMesh &SLAPrintObject::transformed_mesh() const { +// return m_transformed_rmesh; +//} sla::SupportPoints SLAPrintObject::transformed_support_points() const { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index f6d23cf896..a021645084 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -119,8 +119,8 @@ public: }; const std::vector& instances() const { return m_instances; } - bool has_mesh(SLAPrintObjectStep step) const; - TriangleMesh get_mesh(SLAPrintObjectStep step) const; +// bool has_mesh(SLAPrintObjectStep step) const; +// TriangleMesh get_mesh(SLAPrintObjectStep step) const; // Get a support mesh centered around origin in XY, and with zero rotation around Z applied. // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true. @@ -129,17 +129,17 @@ public: // Support mesh is only valid if this->is_step_done(slaposPad) is true. const TriangleMesh& pad_mesh() const; - // Ready after this->is_step_done(slaposDrillHoles) is true - const indexed_triangle_set &hollowed_interior_mesh() const; +// // Ready after this->is_step_done(slaposDrillHoles) is true +// const indexed_triangle_set &hollowed_interior_mesh() const; // Get the mesh that is going to be printed with all the modifications // like hollowing and drilled holes. const TriangleMesh & get_mesh_to_print() const { - return !m_mesh_from_slices.empty() ? m_mesh_from_slices : transformed_mesh(); + return !m_mesh_from_slices.empty() ? m_mesh_from_slices : m_transformed_rmesh; } - // This will return the transformed mesh which is cached - const TriangleMesh& transformed_mesh() const; +// // This will return the transformed mesh which is cached +// const TriangleMesh& transformed_mesh() const; sla::SupportPoints transformed_support_points() const; sla::DrainHoles transformed_drainhole_points() const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 11d1bb80c3..8c98c603d5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2011,14 +2011,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re size_t volume_idx; }; - // SLA steps to pull the preview meshes for. - typedef std::array SLASteps; - SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; - struct SLASupportState { - std::array::value> step; - }; - // State of the sla_steps for all SLAPrintObjects. - std::vector sla_support_state; +// // SLA steps to pull the preview meshes for. +// typedef std::array SLASteps; +// SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; +// struct SLASupportState { +// std::array::value> step; +// }; +// // State of the sla_steps for all SLAPrintObjects. +// std::vector sla_support_state; std::vector instance_ids_selected; std::vector map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1)); @@ -2044,33 +2044,33 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } } - if (printer_technology == ptSLA) { - const SLAPrint* sla_print = this->sla_print(); -#ifndef NDEBUG - // Verify that the SLAPrint object is synchronized with m_model. - check_model_ids_equal(*m_model, sla_print->model()); -#endif /* NDEBUG */ - sla_support_state.reserve(sla_print->objects().size()); - for (const SLAPrintObject* print_object : sla_print->objects()) { - SLASupportState state; - for (size_t istep = 0; istep < sla_steps.size(); ++istep) { - state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]); - if (state.step[istep].state == PrintStateBase::DONE) { - if (!print_object->has_mesh(sla_steps[istep])) - // Consider the DONE step without a valid mesh as invalid for the purpose - // of mesh visualization. - state.step[istep].state = PrintStateBase::INVALID; - else if (sla_steps[istep] != slaposDrillHoles) - for (const ModelInstance* model_instance : print_object->model_object()->instances) - // Only the instances, which are currently printable, will have the SLA support structures kept. - // The instances outside the print bed will have the GLVolumes of their support structures released. - if (model_instance->is_printable()) - aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); - } - } - sla_support_state.emplace_back(state); - } - } +// if (printer_technology == ptSLA) { +// const SLAPrint* sla_print = this->sla_print(); +//#ifndef NDEBUG +// // Verify that the SLAPrint object is synchronized with m_model. +// check_model_ids_equal(*m_model, sla_print->model()); +//#endif /* NDEBUG */ +// sla_support_state.reserve(sla_print->objects().size()); +// for (const SLAPrintObject* print_object : sla_print->objects()) { +// SLASupportState state; +// for (size_t istep = 0; istep < sla_steps.size(); ++istep) { +// state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]); +// if (state.step[istep].state == PrintStateBase::DONE) { +// if (!print_object->has_mesh(sla_steps[istep])) +// // Consider the DONE step without a valid mesh as invalid for the purpose +// // of mesh visualization. +// state.step[istep].state = PrintStateBase::INVALID; +// else if (sla_steps[istep] != slaposDrillHoles) +// for (const ModelInstance* model_instance : print_object->model_object()->instances) +// // Only the instances, which are currently printable, will have the SLA support structures kept. +// // The instances outside the print bed will have the GLVolumes of their support structures released. +// if (model_instance->is_printable()) +// aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); +// } +// } +// sla_support_state.emplace_back(state); +// } +// } std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower); // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh. @@ -7642,10 +7642,10 @@ void GLCanvas3D::_load_sla_shells() // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when // through the update_volumes_colors_by_extruder() call. m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); - if (obj->is_step_done(slaposSupportTree) && obj->has_mesh(slaposSupportTree)) - add_volume(*obj, -int(slaposSupportTree), instance, obj->support_mesh(), GLVolume::SLA_SUPPORT_COLOR, true); - if (obj->is_step_done(slaposPad) && obj->has_mesh(slaposPad)) - add_volume(*obj, -int(slaposPad), instance, obj->pad_mesh(), GLVolume::SLA_PAD_COLOR, false); + if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty()) + add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true); + if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty()) + add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); } double shift_z = obj->get_current_elevation(); for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { From 15fa4c42d6d686cc6fdf720c14cdc1f7d7c00cde Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Oct 2022 16:30:58 +0200 Subject: [PATCH 06/75] Finalize new sla backend interface no has_mesh or get_mesh based on states, but specific methods to get the mesh type needed (support, pad, object) Commented out everything that does not conform in frontend --- src/libslic3r/SLAPrint.cpp | 17 ++ src/libslic3r/SLAPrint.hpp | 66 ++++-- src/libslic3r/SLAPrintSteps.cpp | 112 +++++---- src/libslic3r/SLAPrintSteps.hpp | 2 + src/slic3r/GUI/3DScene.cpp | 134 +++++------ src/slic3r/GUI/3DScene.hpp | 16 +- src/slic3r/GUI/GLCanvas3D.cpp | 275 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 12 +- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 24 +- 10 files changed, 366 insertions(+), 294 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 716029257b..c3b90b1928 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1056,6 +1056,23 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const return EMPTY_MESH; } +const TriangleMesh &SLAPrintObject::get_mesh_to_print() const { + const TriangleMesh *ret = nullptr; + + int s = SLAPrintObjectStep::slaposCount; + + while (s > 0 && !ret) { + --s; + if (is_step_done(SLAPrintObjectStep(s)) && !m_preview_meshes[s].empty()) + ret = &m_preview_meshes[s]; + } + + if (!ret) + ret = &m_transformed_rmesh; + + return *ret; +} + //const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const //{ // if (m_hollowing_data && m_hollowing_data->interior && diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index a021645084..084c78024a 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -45,6 +45,24 @@ using _SLAPrintObjectBase = enum SliceOrigin { soSupport, soModel }; +} // namespace Slic3r + +namespace std { + +template<> struct hash { + size_t operator() (const Slic3r::csg::VoxelizeParams &p) const { + std::string str = Slic3r::float_to_string_decimal_point(p.voxel_scale()); + str += Slic3r::float_to_string_decimal_point(p.exterior_bandwidth()); + str += Slic3r::float_to_string_decimal_point(p.interior_bandwidth()); + + return std::hash{}(str); + } +}; + +} // namespace std + +namespace Slic3r { + // Each sla object step can hold a collection of csg operations on the // sla model to be sliced. Currently, Assembly step adds negative and positive // volumes, hollowing adds the negative interior, drilling adds the hole cylinders. @@ -54,7 +72,7 @@ enum SliceOrigin { soSupport, soModel }; struct CSGPartForStep : public csg::CSGPart { SLAPrintObjectStep key; - mutable struct { VoxelGridPtr gridptr; csg::VoxelizeParams params; } cache; + mutable std::unordered_map gridcache; CSGPartForStep(SLAPrintObjectStep k, CSGPart &&p = {}) : key{k}, CSGPart{std::move(p)} @@ -64,6 +82,7 @@ struct CSGPartForStep : public csg::CSGPart { this->its_ptr = std::move(part.its_ptr); this->operation = part.operation; + return *this; } @@ -72,23 +91,27 @@ struct CSGPartForStep : public csg::CSGPart namespace csg { -//inline VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, -// const VoxelizeParams &p) -//{ -// if (!part.cache.gridptr || get_voxel_scale(*part.cache.gridptr) < p.voxel_scale()) { -// part.cache.gridptr = mesh_to_grid(*csg::get_mesh(part), p.voxel_scale(), -// p.exterior_bandwidth(), -// p.interior_bandwidth()); +inline bool operator==(const VoxelizeParams &a, const VoxelizeParams &b) +{ + std::hash h; + return h(a) == h(b); +} -// } /*else { -// float vscale = p.voxel_scale(); -// float oscale = get_voxel_scale(*part.cache.gridptr); +inline VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, + const VoxelizeParams &p) +{ + VoxelGridPtr &ret = part.gridcache[p]; -// rescale_grid(*part.cache.gridptr, oscale/vscale); -// }*/ + if (!ret) { + ret = mesh_to_grid(*csg::get_mesh(part), + csg::get_transform(part), + p.voxel_scale(), + p.exterior_bandwidth(), + p.interior_bandwidth()); + } -// return clone(*part.cache.gridptr); -//} + return clone(*ret); +} } // namespace csg @@ -134,9 +157,7 @@ public: // Get the mesh that is going to be printed with all the modifications // like hollowing and drilled holes. - const TriangleMesh & get_mesh_to_print() const { - return !m_mesh_from_slices.empty() ? m_mesh_from_slices : m_transformed_rmesh; - } + const TriangleMesh & get_mesh_to_print() const; // // This will return the transformed mesh which is cached // const TriangleMesh& transformed_mesh() const; @@ -379,9 +400,16 @@ private: }; std::unique_ptr m_supportdata; - TriangleMesh m_mesh_from_slices; + + // Holds CSG operations for the printed object, prioritized by print steps. std::multiset m_mesh_to_slice; + // Holds the preview of the object to be printed (as it will look like with + // all its holes and cavities, negatives and positive volumes unified. + // Essentially this should be a m_mesh_to_slice after the CSG operations + // or an approximation of that. + std::array m_preview_meshes; + class HollowingData { public: diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index a024a799c0..b0efa31fa2 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include @@ -128,6 +128,54 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin } } + +void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) +{ + Benchmark bench; + + bench.start(); + // update preview mesh + double vscale = 1. / (2. * po.m_config.layer_height.getFloat()); + auto voxparams = csg::VoxelizeParams{} + .voxel_scale(vscale) + .exterior_bandwidth(1.f) + .interior_bandwidth(1.f); + auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams); + + indexed_triangle_set m = grid_to_mesh(*grid); + +// if (!m.empty()) { +// // simplify mesh lossless + +// std::cout << "simplify started" << std::endl; +// int expected_cnt = m.indices.size() * 0.8; //std::pow(po.m_transformed_rmesh.volume() / std::pow(1./vscale, 3), 2./3.); +// std::cout << "expected triangles " << expected_cnt << std::endl; +// float err = std::pow(vscale, 3); +// its_quadric_edge_collapse(m, 0U, &err); +// std::cout << "simplify ended " << m.indices.size() << " triangles" << std::endl; + +// std::cout << "cleanup started" << std::endl; +// its_compactify_vertices(m); +// its_merge_vertices(m); +// std::cout << "cleanup ended" << std::endl; +// } + + 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(); + + std::cout << "Preview gen took: " << bench.getElapsedSec() << std::endl; + using namespace std::string_literals; + + report_status(-2, "Reload preview from step "s + std::to_string(int(step)), SlicingStatus::RELOAD_SLA_PREVIEW); +} + +static inline void clear_csg(std::multiset &s, SLAPrintObjectStep step) { auto r = s.equal_range(step); @@ -153,6 +201,8 @@ void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po) csg::model_to_csgmesh(*po.model_object(), po.trafo(), csg_inserter{po.m_mesh_to_slice, slaposAssembly}, csg::mpartsPositive | csg::mpartsNegative); + + generate_preview(po, slaposAssembly); } void SLAPrint::Steps::hollow_model(SLAPrintObject &po) @@ -185,11 +235,24 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); po.m_hollowing_data->interior = std::move(interior); + indexed_triangle_set &m = sla::get_mesh(*po.m_hollowing_data->interior); + + if (!m.empty()) { + // simplify mesh lossless + float loss_less_max_error = 2*std::numeric_limits::epsilon(); + its_quadric_edge_collapse(m, 0U, &loss_less_max_error); + + its_compactify_vertices(m); + its_merge_vertices(m); + } + // Put the interior into the target mesh as a negative po.m_mesh_to_slice .emplace(slaposHollowing, csg::CSGPart{&sla::get_mesh(*po.m_hollowing_data->interior), csg::CSGType::Difference}); + + generate_preview(po, slaposHollowing); } } @@ -203,14 +266,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) csg::mpartsDrillHoles); // update preview mesh - double vscale = 1. / 0.05; - auto voxparams = csg::VoxelizeParams{} - .voxel_scale(vscale) - .exterior_bandwidth(1.f) - .interior_bandwidth(1.f); - auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams); - indexed_triangle_set mesh_from_slices = grid_to_mesh(*grid); - po.m_mesh_from_slices = TriangleMesh{mesh_from_slices}; + generate_preview(po, slaposDrillHoles); } template @@ -331,38 +387,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) // We apply the printer correction offset here. apply_printer_corrections(po, soModel); -// auto simpl_slices = reserve_vector(po.m_model_slices.size()); -// for (const ExPolygons &slice : po.m_model_slices) { -// simpl_slices.emplace_back(expolygons_simplify(slice, scaled(1e-2))); -// } - -// po.m_mesh_from_slices = TriangleMesh{ -// slices_to_mesh(simpl_slices, slice_grid.front(), lhd, ilhd)}; - - double vscale = 1. / lhd; - auto voxparams = csg::VoxelizeParams{} - .voxel_scale(vscale) - .exterior_bandwidth(1.f) - .interior_bandwidth(1.f); - auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams); - - assert(grid); - -// size_t max_face_cnt = 0; -// for (const CSGMesh &part : po.m_mesh_to_slice) -// max_face_cnt += part.its_ptr->indices.size(); - - indexed_triangle_set mesh_from_slices = grid_to_mesh(*grid); - -// its_quadric_edge_collapse(mesh_from_slices, vscale * max_face_cnt); - -// its_compactify_vertices(mesh_from_slices); -// its_merge_vertices(mesh_from_slices); - - po.m_mesh_from_slices = TriangleMesh{mesh_from_slices}; - -// po.m_mesh_from_slices = TriangleMesh{sla::get_mesh(*po.m_hollowing_data->interior)}; - + generate_preview(po, slaposObjectSlice); } static void filter_support_points_by_modifiers( @@ -421,7 +446,10 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) if(!po.m_config.supports_enable.getBool()) return; if (!po.m_supportdata) - po.m_supportdata.reset(new SLAPrintObject::SupportData(po.m_mesh_from_slices)); + po.m_supportdata = + std::make_unique( + po.m_preview_meshes[slaposObjectSlice] + ); po.m_supportdata->input.zoffset = csgmesh_positive_bb(po.m_mesh_to_slice) .min.z(); diff --git a/src/libslic3r/SLAPrintSteps.hpp b/src/libslic3r/SLAPrintSteps.hpp index 1a1900153d..30dff8628e 100644 --- a/src/libslic3r/SLAPrintSteps.hpp +++ b/src/libslic3r/SLAPrintSteps.hpp @@ -44,6 +44,8 @@ private: void initialize_printer_input(); void apply_printer_corrections(SLAPrintObject &po, SliceOrigin o); + + void generate_preview(SLAPrintObject &po, SLAPrintObjectStep step); public: explicit Steps(SLAPrint *print); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index e65a321f1d..103ab339c2 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -852,73 +852,73 @@ int GLVolumeCollection::load_object_volume( return int(this->volumes.size() - 1); } -// Load SLA auxiliary GLVolumes (for support trees or pad). -// This function produces volumes for multiple instances in a single shot, -// as some object specific mesh conversions may be expensive. -#if ENABLE_LEGACY_OPENGL_REMOVAL -void GLVolumeCollection::load_object_auxiliary( - const SLAPrintObject* print_object, - int obj_idx, - // pairs of - const std::vector>& instances, - SLAPrintObjectStep milestone, - // Timestamp of the last change of the milestone - size_t timestamp) -#else -void GLVolumeCollection::load_object_auxiliary( - const SLAPrintObject *print_object, - int obj_idx, - // pairs of - const std::vector>& instances, - SLAPrintObjectStep milestone, - // Timestamp of the last change of the milestone - size_t timestamp, - bool opengl_initialized) -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -{ - assert(print_object->is_step_done(milestone)); - Transform3d mesh_trafo_inv = print_object->trafo().inverse(); - // Get the support mesh. - TriangleMesh mesh = print_object->get_mesh(milestone); - mesh.transform(mesh_trafo_inv); - // Convex hull is required for out of print bed detection. - TriangleMesh convex_hull = mesh.convex_hull_3d(); - for (const std::pair& instance_idx : instances) { - const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; - this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); - GLVolume& v = *this->volumes.back(); -#if ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_SMOOTH_NORMALS - v.model.init_from(mesh, true); -#else - v.model.init_from(mesh); - v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR); -#if ENABLE_RAYCAST_PICKING - v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); -#endif // ENABLE_RAYCAST_PICKING -#endif // ENABLE_SMOOTH_NORMALS -#else -#if ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.load_mesh(mesh, true); -#else - v.indexed_vertex_array.load_mesh(mesh); -#endif // ENABLE_SMOOTH_NORMALS - v.indexed_vertex_array.finalize_geometry(opengl_initialized); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); - v.geometry_id = std::pair(timestamp, model_instance.id().id); - // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. - if (&instance_idx == &instances.back()) - v.set_convex_hull(std::move(convex_hull)); - else - v.set_convex_hull(convex_hull); - v.is_modifier = false; - v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree); - v.set_instance_transformation(model_instance.get_transformation()); - // Leave the volume transformation at identity. - // v.set_volume_transformation(model_volume->get_transformation()); - } -} +//// Load SLA auxiliary GLVolumes (for support trees or pad). +//// This function produces volumes for multiple instances in a single shot, +//// as some object specific mesh conversions may be expensive. +//#if ENABLE_LEGACY_OPENGL_REMOVAL +//void GLVolumeCollection::load_object_auxiliary( +// const SLAPrintObject* print_object, +// int obj_idx, +// // pairs of +// const std::vector>& instances, +// SLAPrintObjectStep milestone, +// // Timestamp of the last change of the milestone +// size_t timestamp) +//#else +//void GLVolumeCollection::load_object_auxiliary( +// const SLAPrintObject *print_object, +// int obj_idx, +// // pairs of +// const std::vector>& instances, +// SLAPrintObjectStep milestone, +// // Timestamp of the last change of the milestone +// size_t timestamp, +// bool opengl_initialized) +//#endif // ENABLE_LEGACY_OPENGL_REMOVAL +//{ +// assert(print_object->is_step_done(milestone)); +// Transform3d mesh_trafo_inv = print_object->trafo().inverse(); +// // Get the support mesh. +// TriangleMesh mesh = print_object->get_mesh(milestone); +// mesh.transform(mesh_trafo_inv); +// // Convex hull is required for out of print bed detection. +// TriangleMesh convex_hull = mesh.convex_hull_3d(); +// for (const std::pair& instance_idx : instances) { +// const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; +// this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); +// GLVolume& v = *this->volumes.back(); +//#if ENABLE_LEGACY_OPENGL_REMOVAL +//#if ENABLE_SMOOTH_NORMALS +// v.model.init_from(mesh, true); +//#else +// v.model.init_from(mesh); +// v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR); +//#if ENABLE_RAYCAST_PICKING +// v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +//#endif // ENABLE_RAYCAST_PICKING +//#endif // ENABLE_SMOOTH_NORMALS +//#else +//#if ENABLE_SMOOTH_NORMALS +// v.indexed_vertex_array.load_mesh(mesh, true); +//#else +// v.indexed_vertex_array.load_mesh(mesh); +//#endif // ENABLE_SMOOTH_NORMALS +// v.indexed_vertex_array.finalize_geometry(opengl_initialized); +//#endif // ENABLE_LEGACY_OPENGL_REMOVAL +// v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); +// v.geometry_id = std::pair(timestamp, model_instance.id().id); +// // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. +// if (&instance_idx == &instances.back()) +// v.set_convex_hull(std::move(convex_hull)); +// else +// v.set_convex_hull(convex_hull); +// v.is_modifier = false; +// v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree); +// v.set_instance_transformation(model_instance.get_transformation()); +// // Leave the volume transformation at identity. +// // v.set_volume_transformation(model_volume->get_transformation()); +// } +//} #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_OPENGL_ES diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 1e8897c4e0..e63f095e47 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -659,14 +659,14 @@ public: int instance_idx); // Load SLA auxiliary GLVolumes (for support trees or pad). - void load_object_auxiliary( - const SLAPrintObject* print_object, - int obj_idx, - // pairs of - const std::vector>& instances, - SLAPrintObjectStep milestone, - // Timestamp of the last change of the milestone - size_t timestamp); +// void load_object_auxiliary( +// const SLAPrintObject* print_object, +// int obj_idx, +// // pairs of +// const std::vector>& instances, +// SLAPrintObjectStep milestone, +// // Timestamp of the last change of the milestone +// size_t timestamp); #if ENABLE_OPENGL_ES int load_wipe_tower_preview( diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8c98c603d5..d20e53643c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2189,143 +2189,146 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } if (printer_technology == ptSLA) { - size_t idx = 0; - const SLAPrint *sla_print = this->sla_print(); - std::vector shift_zs(m_model->objects.size(), 0); - double relative_correction_z = sla_print->relative_correction().z(); - if (relative_correction_z <= EPSILON) - relative_correction_z = 1.; - for (const SLAPrintObject *print_object : sla_print->objects()) { - SLASupportState &state = sla_support_state[idx ++]; - const ModelObject *model_object = print_object->model_object(); - // Find an index of the ModelObject - int object_idx; - // There may be new SLA volumes added to the scene for this print_object. - // Find the object index of this print_object in the Model::objects list. - auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object); - assert(it != sla_print->model().objects.end()); - object_idx = it - sla_print->model().objects.begin(); - // Cache the Z offset to be applied to all volumes with this object_idx. - shift_zs[object_idx] = print_object->get_current_elevation() / relative_correction_z; - // Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene. - // pairs of - std::vector> instances[std::tuple_size::value]; - for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) { - const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx]; - // Find index of ModelInstance corresponding to this SLAPrintObject::Instance. - auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), - [&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; }); - assert(it != model_object->instances.end()); - int instance_idx = it - model_object->instances.begin(); - for (size_t istep = 0; istep < sla_steps.size(); ++ istep) - if (sla_steps[istep] == slaposDrillHoles) { - // Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance, - // not into its own GLVolume. - // There shall always be such a GLVolume allocated. - ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); - auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); - assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); - assert(!it->new_geometry()); - GLVolume &volume = *m_volumes.volumes[it->volume_idx]; - if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { - // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. -#if ENABLE_LEGACY_OPENGL_REMOVAL - volume.model.reset(); -#else - volume.indexed_vertex_array.release_geometry(); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - if (state.step[istep].state == PrintStateBase::DONE) { - TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); - assert(! mesh.empty()); +// size_t idx = 0; +// const SLAPrint *sla_print = this->sla_print(); +// std::vector shift_zs(m_model->objects.size(), 0); +// double relative_correction_z = sla_print->relative_correction().z(); +// if (relative_correction_z <= EPSILON) +// relative_correction_z = 1.; +// for (const SLAPrintObject *print_object : sla_print->objects()) { +// SLASupportState &state = sla_support_state[idx ++]; +// const ModelObject *model_object = print_object->model_object(); +// // Find an index of the ModelObject +// int object_idx; +// // There may be new SLA volumes added to the scene for this print_object. +// // Find the object index of this print_object in the Model::objects list. +// auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object); +// assert(it != sla_print->model().objects.end()); +// object_idx = it - sla_print->model().objects.begin(); +// // Cache the Z offset to be applied to all volumes with this object_idx. +// shift_zs[object_idx] = print_object->get_current_elevation() / relative_correction_z; +// // Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene. +// // pairs of +// std::vector> instances[std::tuple_size::value]; +// for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) { +// const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx]; +// // Find index of ModelInstance corresponding to this SLAPrintObject::Instance. +// auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), +// [&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; }); +// assert(it != model_object->instances.end()); +// int instance_idx = it - model_object->instances.begin(); +// for (size_t istep = 0; istep < sla_steps.size(); ++ istep) +// if (sla_steps[istep] == slaposDrillHoles) { +// // Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance, +// // not into its own GLVolume. +// // There shall always be such a GLVolume allocated. +// ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); +// auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); +// assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); +// assert(!it->new_geometry()); +// GLVolume &volume = *m_volumes.volumes[it->volume_idx]; +// if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { +// // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. +//#if ENABLE_LEGACY_OPENGL_REMOVAL +// volume.model.reset(); +//#else +// volume.indexed_vertex_array.release_geometry(); +//#endif // ENABLE_LEGACY_OPENGL_REMOVAL +// if (state.step[istep].state == PrintStateBase::DONE) { +// TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); +// assert(! mesh.empty()); - // sla_trafo does not contain volume trafo. To get a mesh to create - // a new volume from, we have to apply vol trafo inverse separately. - const ModelObject& mo = *m_model->objects[volume.object_idx()]; - Transform3d trafo = sla_print->sla_trafo(mo) - * mo.volumes.front()->get_transformation().get_matrix(); - mesh.transform(trafo.inverse()); -#if ENABLE_SMOOTH_NORMALS -#if ENABLE_LEGACY_OPENGL_REMOVAL - volume.model.init_from(mesh, true); -#else - volume.indexed_vertex_array.load_mesh(mesh, true); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -#else -#if ENABLE_LEGACY_OPENGL_REMOVAL - volume.model.init_from(mesh); -#if ENABLE_RAYCAST_PICKING - volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); -#endif // ENABLE_RAYCAST_PICKING -#else - volume.indexed_vertex_array.load_mesh(mesh); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -#endif // ENABLE_SMOOTH_NORMALS - } - else { - // Reload the original volume. -#if ENABLE_SMOOTH_NORMALS -#if ENABLE_LEGACY_OPENGL_REMOVAL - volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -#else - volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -#else -#if ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_RAYCAST_PICKING - const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); - volume.model.init_from(new_mesh); - volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); -#else - volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); -#endif // ENABLE_RAYCAST_PICKING -#else - volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -#endif // ENABLE_SMOOTH_NORMALS - } -#if !ENABLE_LEGACY_OPENGL_REMOVAL - volume.finalize_geometry(true); -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - } - //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable - // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables - // of various concenrs (model vs. 3D print path). - volume.offsets = { state.step[istep].timestamp }; - } - else if (state.step[istep].state == PrintStateBase::DONE) { - // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. - ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); - auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); - assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); - if (it->new_geometry()) { - // This can be an SLA support structure that should not be rendered (in case someone used undo - // to revert to before it was generated). If that's the case, we should not generate anything. - if (model_object->sla_points_status != sla::PointsStatus::NoPoints) - instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); - else - shift_zs[object_idx] = 0.; - } - else { - // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. - m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); - m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); - } - } +// // sla_trafo does not contain volume trafo. To get a mesh to create +// // a new volume from, we have to apply vol trafo inverse separately. +// const ModelObject& mo = *m_model->objects[volume.object_idx()]; +// Transform3d trafo = sla_print->sla_trafo(mo) +// * mo.volumes.front()->get_transformation().get_matrix(); +// mesh.transform(trafo.inverse()); +//#if ENABLE_SMOOTH_NORMALS +//#if ENABLE_LEGACY_OPENGL_REMOVAL +// volume.model.init_from(mesh, true); +//#else +// volume.indexed_vertex_array.load_mesh(mesh, true); +//#endif // ENABLE_LEGACY_OPENGL_REMOVAL +//#else +//#if ENABLE_LEGACY_OPENGL_REMOVAL +// volume.model.init_from(mesh); +//#if ENABLE_RAYCAST_PICKING +// volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); +//#endif // ENABLE_RAYCAST_PICKING +//#else +// volume.indexed_vertex_array.load_mesh(mesh); +//#endif // ENABLE_LEGACY_OPENGL_REMOVAL +//#endif // ENABLE_SMOOTH_NORMALS +// } +// else { +// // Reload the original volume. +//#if ENABLE_SMOOTH_NORMALS +//#if ENABLE_LEGACY_OPENGL_REMOVAL +// volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +//#else +// volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +//#endif // ENABLE_LEGACY_OPENGL_REMOVAL +//#else +//#if ENABLE_LEGACY_OPENGL_REMOVAL +//#if ENABLE_RAYCAST_PICKING +// const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); +// volume.model.init_from(new_mesh); +// volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); +//#else +// volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +//#endif // ENABLE_RAYCAST_PICKING +//#else +// volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +//#endif // ENABLE_LEGACY_OPENGL_REMOVAL +//#endif // ENABLE_SMOOTH_NORMALS +// } +//#if !ENABLE_LEGACY_OPENGL_REMOVAL +// volume.finalize_geometry(true); +//#endif // !ENABLE_LEGACY_OPENGL_REMOVAL +// } +// //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable +// // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables +// // of various concenrs (model vs. 3D print path). +// volume.offsets = { state.step[istep].timestamp }; +// } +// else if (state.step[istep].state == PrintStateBase::DONE) { +// // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. +// ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); +// auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); +// assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); +// if (it->new_geometry()) { +// // This can be an SLA support structure that should not be rendered (in case someone used undo +// // to revert to before it was generated). If that's the case, we should not generate anything. +// if (model_object->sla_points_status != sla::PointsStatus::NoPoints) +// instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); +// else +// shift_zs[object_idx] = 0.; +// } +// else { +// // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. +// m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); +// m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); +// } +// } +// } + +// for (size_t istep = 0; istep < sla_steps.size(); ++istep) +// if (!instances[istep].empty()) +//#if ENABLE_LEGACY_OPENGL_REMOVAL +// m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); +//#else +// m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); +//#endif // ENABLE_LEGACY_OPENGL_REMOVAL +// } + + // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed + for (GLVolume* volume : m_volumes.volumes) + if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { + const SLAPrintObject *po = sla_print()->objects()[volume->object_idx()]; + float zoffs = po->get_current_elevation() / sla_print()->relative_correction().z(); + volume->set_sla_shift_z(zoffs); } - - for (size_t istep = 0; istep < sla_steps.size(); ++istep) - if (!instances[istep].empty()) -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); -#else - m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - } - - // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed - for (GLVolume* volume : m_volumes.volumes) - if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) - volume->set_sla_shift_z(shift_zs[volume->object_idx()]); } if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) { @@ -7635,7 +7638,7 @@ void GLCanvas3D::_load_sla_shells() // adds objects' volumes for (const SLAPrintObject* obj : print->objects()) - if (obj->is_step_done(slaposSliceSupports)) { + /*if (obj->is_step_done(slaposSliceSupports))*/ { unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); for (const SLAPrintObject::Instance& instance : obj->instances()) { add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 394e879b7c..52ae9b25b4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -281,10 +281,10 @@ void HollowedMesh::on_update() // If there is a valid SLAPrintObject, check state of Hollowing step. if (print_object) { - if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) { + if (print_object->is_step_done(slaposDrillHoles) && !print_object->get_mesh_to_print().empty()) { size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; if (timestamp > m_old_hollowing_timestamp) { - const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice(); + const TriangleMesh& backend_mesh = print_object->get_mesh_to_print(); if (! backend_mesh.empty()) { m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); Transform3d trafo_inv = (canvas->sla_print()->sla_trafo(*mo) * print_object->model_object()->volumes.front()->get_transformation().get_matrix()).inverse(); @@ -292,10 +292,10 @@ void HollowedMesh::on_update() m_drainholes = print_object->model_object()->sla_drain_holes; m_old_hollowing_timestamp = timestamp; - indexed_triangle_set interior = print_object->hollowed_interior_mesh(); - its_flip_triangles(interior); - m_hollowed_interior_transformed = std::make_unique(std::move(interior)); - m_hollowed_interior_transformed->transform(trafo_inv); +// indexed_triangle_set interior = print_object->hollowed_interior_mesh(); +// its_flip_triangles(interior); +// m_hollowed_interior_transformed = std::make_unique(std::move(interior)); +// m_hollowed_interior_transformed->transform(trafo_inv); } else { m_hollowed_mesh_transformed.reset(nullptr); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5764b283c6..b0f06541c1 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -944,7 +944,7 @@ bool MainFrame::can_export_supports() const const PrintObjects& objects = m_plater->sla_print().objects(); for (const SLAPrintObject* object : objects) { - if (object->has_mesh(slaposPad) || object->has_mesh(slaposSupportTree)) + if (!object->support_mesh().empty()) { can_export = true; break; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e11e08d043..d4b279f606 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6155,19 +6155,13 @@ void Plater::export_stl_obj(bool extended, bool selection_only) const Transform3d mesh_trafo_inv = object->trafo().inverse(); const bool is_left_handed = object->is_left_handed(); - TriangleMesh pad_mesh; - const bool has_pad_mesh = extended && object->has_mesh(slaposPad); - if (has_pad_mesh) { - pad_mesh = object->get_mesh(slaposPad); - pad_mesh.transform(mesh_trafo_inv); - } + auto pad_mesh = extended? object->pad_mesh() : TriangleMesh{}; + pad_mesh = object->pad_mesh(); + pad_mesh.transform(mesh_trafo_inv); + + auto supports_mesh = extended ? object->support_mesh() : TriangleMesh{}; + supports_mesh.transform(mesh_trafo_inv); - TriangleMesh supports_mesh; - const bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree); - if (has_supports_mesh) { - supports_mesh = object->get_mesh(slaposSupportTree); - supports_mesh.transform(mesh_trafo_inv); - } const std::vector& obj_instances = object->instances(); for (const SLAPrintObject::Instance& obj_instance : obj_instances) { auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), @@ -6184,19 +6178,19 @@ void Plater::export_stl_obj(bool extended, bool selection_only) TriangleMesh inst_mesh; - if (has_pad_mesh) { + if (!pad_mesh.empty()) { TriangleMesh inst_pad_mesh = pad_mesh; inst_pad_mesh.transform(inst_transform, is_left_handed); inst_mesh.merge(inst_pad_mesh); } - if (has_supports_mesh) { + if (!supports_mesh.empty()) { TriangleMesh inst_supports_mesh = supports_mesh; inst_supports_mesh.transform(inst_transform, is_left_handed); inst_mesh.merge(inst_supports_mesh); } - TriangleMesh inst_object_mesh = object->get_mesh_to_slice(); + TriangleMesh inst_object_mesh = object->get_mesh_to_print(); inst_object_mesh.transform(mesh_trafo_inv); inst_object_mesh.transform(inst_transform, is_left_handed); From 9bc34104743f5b158a6c0f98a2413753836387dc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 20 Oct 2022 14:14:25 +0200 Subject: [PATCH 07/75] Re-enable volumes in SLA, use raycasters from canvas in supports gizmo Got rid of HollowedMesh and Raycaster usage from GizmosCommon pool to prevent crashes --- src/libslic3r/AABBMesh.hpp | 19 +- src/slic3r/GUI/3DScene.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 5 +- src/slic3r/GUI/GLCanvas3D.hpp | 2 + src/slic3r/GUI/GUI_Factories.cpp | 70 ++++++-- src/slic3r/GUI/GUI_Factories.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 38 ++-- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 42 +++-- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 177 ++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 50 +++--- src/slic3r/GUI/Plater.cpp | 11 -- src/slic3r/GUI/SceneRaycaster.cpp | 18 +- src/slic3r/GUI/SceneRaycaster.hpp | 3 +- src/slic3r/GUI/Selection.cpp | 32 ++-- src/slic3r/GUI/Tab.cpp | 4 +- 16 files changed, 269 insertions(+), 207 deletions(-) diff --git a/src/libslic3r/AABBMesh.hpp b/src/libslic3r/AABBMesh.hpp index 179bf8c4c1..337935839d 100644 --- a/src/libslic3r/AABBMesh.hpp +++ b/src/libslic3r/AABBMesh.hpp @@ -26,10 +26,9 @@ class TriangleMesh; // casting and other higher level operations. class AABBMesh { class AABBImpl; - + const indexed_triangle_set* m_tm; -// double m_ground_level = 0/*, m_gnd_offset = 0*/; - + std::unique_ptr m_aabb; VertexFaceIndex m_vfidx; // vertex-face index std::vector m_fnidx; // face-neighbor index @@ -43,7 +42,7 @@ class AABBMesh { template void init(const M &mesh, bool calculate_epsilon); public: - + // calculate_epsilon ... calculate epsilon for triangle-ray intersection from an average triangle edge length. // If set to false, a default epsilon is used, which works for "reasonable" meshes. explicit AABBMesh(const indexed_triangle_set &tmesh, bool calculate_epsilon = false); @@ -51,21 +50,17 @@ public: AABBMesh(const AABBMesh& other); AABBMesh& operator=(const AABBMesh&); - + AABBMesh(AABBMesh &&other); AABBMesh& operator=(AABBMesh &&other); - + ~AABBMesh(); - -// inline double ground_level() const { return m_ground_level /*+ m_gnd_offset*/; } -// inline void ground_level_offset(double o) { m_gnd_offset = o; } -// inline double ground_level_offset() const { return m_gnd_offset; } - + const std::vector& vertices() const; const std::vector& indices() const; const Vec3f& vertices(size_t idx) const; const Vec3i& indices(size_t idx) const; - + // Result of a raycast class hit_result { // m_t holds a distance from m_source to the intersection. diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index e63f095e47..abf3e9264c 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -386,7 +386,7 @@ public: bool force_neutral_color : 1; // Whether or not to force rendering of sinking contours bool force_sinking_contours : 1; - }; + }; // this gets instantiated automatically in the parent struct // Is mouse or rectangle selection over this object to select/deselect it ? EHoverState hover; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d20e53643c..a698633a84 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2188,6 +2188,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } } + if (printer_technology == ptSLA) { // size_t idx = 0; // const SLAPrint *sla_print = this->sla_print(); @@ -3846,7 +3847,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) #else model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); #endif // ENABLE_WORLD_COORDINATE - else if (selection_mode == Selection::Volume) + else if (volume_idx >= 0 && selection_mode == Selection::Volume) #if ENABLE_WORLD_COORDINATE model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); #else @@ -4020,7 +4021,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); #endif // ENABLE_WORLD_COORDINATE } - else if (selection_mode == Selection::Volume) { + else if (selection_mode == Selection::Volume && volume_idx >= 0) { #if ENABLE_WORLD_COORDINATE model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation()); model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a519a26a19..140cbaf748 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -688,6 +688,8 @@ public: void set_raycaster_gizmos_on_top(bool value) { m_scene_raycaster.set_gizmos_on_top(value); } + + const SceneRaycaster & raycaster() const { return m_scene_raycaster; } #endif // ENABLE_RAYCAST_PICKING void set_as_dirty(); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 0ae1b8870a..39cf44eb4e 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -44,7 +44,6 @@ static bool is_improper_category(const std::string& category, const int extruder (!is_object_settings && category == "Support material"); } - //------------------------------------- // SettingsFactory //------------------------------------- @@ -155,14 +154,14 @@ wxBitmapBundle* SettingsFactory::get_category_bitmap(const std::string& category //------------------------------------- // Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important -const std::vector> MenuFactory::ADD_VOLUME_MENU_ITEMS { -// menu_item Name menu_item bitmap name - {L("Add part"), "add_part" }, // ~ModelVolumeType::MODEL_PART - {L("Add negative volume"), "add_negative" }, // ~ModelVolumeType::NEGATIVE_VOLUME - {L("Add modifier"), "add_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER - {L("Add support blocker"), "support_blocker"}, // ~ModelVolumeType::SUPPORT_BLOCKER - {L("Add support enforcer"), "support_enforcer"}, // ~ModelVolumeType::SUPPORT_ENFORCER -}; +static const constexpr std::array, 5> ADD_VOLUME_MENU_ITEMS = {{ + // menu_item Name menu_item bitmap name + {L("Add part"), "add_part" }, // ~ModelVolumeType::MODEL_PART + {L("Add negative volume"), "add_negative" }, // ~ModelVolumeType::NEGATIVE_VOLUME + {L("Add modifier"), "add_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER + {L("Add support blocker"), "support_blocker"}, // ~ModelVolumeType::SUPPORT_BLOCKER + {L("Add support enforcer"), "support_enforcer"}, // ~ModelVolumeType::SUPPORT_ENFORCER +}}; // Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important const std::vector> MenuFactory::TEXT_VOLUME_ICONS { @@ -583,6 +582,55 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu) append_menu_item_layers_editing(menu); } +void MenuFactory::append_menu_items_add_sla_volume(wxMenu *menu) +{ + // Update "add" items(delete old & create new) settings popupmenu + for (auto& item : ADD_VOLUME_MENU_ITEMS) { + const auto settings_id = menu->FindItem(_(item.first)); + if (settings_id != wxNOT_FOUND) + menu->Destroy(settings_id); + } + + const ConfigOptionMode mode = wxGetApp().get_mode(); + + if (mode == comAdvanced) { + append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].first), "", + [](wxCommandEvent&) { obj_list()->load_subobject(ModelVolumeType::MODEL_PART); }, + ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].second, nullptr, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + } else { + auto& item = ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)]; + + wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::MODEL_PART); + append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + } + + { + auto& item = ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::NEGATIVE_VOLUME)]; + + wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::NEGATIVE_VOLUME); + append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + } + + { + auto& item = ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)]; + + wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::SUPPORT_ENFORCER); + append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + } + + { + auto& item = ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)]; + + wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::SUPPORT_BLOCKER); + append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + } +} + wxMenuItem* MenuFactory::append_menu_item_layers_editing(wxMenu* menu) { return append_menu_item(menu, wxID_ANY, _L("Height range Modifier"), "", @@ -631,7 +679,7 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) // If there are selected more then one instance but not all of them // don't add settings menu items const Selection& selection = get_selection(); - if ((selection.is_multiple_full_instance() && !selection.is_single_full_object()) || + if ((selection.is_multiple_full_instance() && !selection.is_single_full_object()) || (printer_technology() == ptSLA && selection.is_single_volume()) || selection.is_multiple_volume() || selection.is_mixed()) // more than one volume(part) is selected on the scene return nullptr; @@ -1066,6 +1114,8 @@ void MenuFactory::create_sla_object_menu() []() { return plater()->can_split(true); }, m_parent); m_sla_object_menu.AppendSeparator(); + append_menu_items_add_sla_volume(&m_sla_object_menu); + m_sla_object_menu.AppendSeparator(); } void MenuFactory::append_immutable_part_menu_items(wxMenu* menu) diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index ed27655be0..e418cf6751 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -92,6 +92,7 @@ private: wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type); void append_menu_item_add_text(wxMenu* menu, ModelVolumeType type, bool is_submenu_item = true); void append_menu_items_add_volume(wxMenu* menu); + void append_menu_items_add_sla_volume(wxMenu* menu); wxMenuItem* append_menu_item_layers_editing(wxMenu* menu); wxMenuItem* append_menu_item_settings(wxMenu* menu); wxMenuItem* append_menu_item_change_type(wxMenu* menu); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 1ce118d71d..52aed6bfc8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -53,8 +53,8 @@ void GLGizmoHollow::data_changed() reload_cache(); m_old_mo_id = mo->id(); } - if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) - m_holes_in_drilled_mesh = mo->sla_drain_holes; +// if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) +// m_holes_in_drilled_mesh = mo->sla_drain_holes; #if ENABLE_RAYCAST_PICKING if (m_raycasters.empty()) on_register_raycasters_for_picking(); @@ -206,11 +206,11 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #endif // !ENABLE_RAYCAST_PICKING if (size_t(m_hover_id) == i) render_color = ColorRGBA::CYAN(); - else if (m_c->hollowed_mesh() && - i < m_c->hollowed_mesh()->get_drainholes().size() && - m_c->hollowed_mesh()->get_drainholes()[i].failed) { - render_color = { 1.0f, 0.0f, 0.0f, 0.5f }; - } +// else if (m_c->hollowed_mesh() && +// i < m_c->hollowed_mesh()->get_drainholes().size() && +// m_c->hollowed_mesh()->get_drainholes()[i].failed) { +// render_color = { 1.0f, 0.0f, 0.0f, 0.5f }; +// } else render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); #if !ENABLE_RAYCAST_PICKING @@ -314,18 +314,18 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairhollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) { - // in this case the raycaster sees the hollowed and drilled mesh. - // if the point lies on the surface created by the hole, we want - // to ignore it. - for (const sla::DrainHole& hole : m_holes_in_drilled_mesh) { - sla::DrainHole outer(hole); - outer.radius *= 1.001f; - outer.height *= 1.001f; - if (outer.is_inside(hit)) - return false; - } - } +// if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) { +// // in this case the raycaster sees the hollowed and drilled mesh. +// // if the point lies on the surface created by the hole, we want +// // to ignore it. +// for (const sla::DrainHole& hole : m_holes_in_drilled_mesh) { +// sla::DrainHole outer(hole); +// outer.radius *= 1.001f; +// outer.height *= 1.001f; +// if (outer.is_inside(hit)) +// return false; +// } +// } // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 1646f31c7f..d0dd3baa08 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -86,6 +86,8 @@ void GLGizmoSlaSupports::data_changed() update_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING } + +// m_parent.toggle_model_objects_visibility(false); } @@ -179,8 +181,8 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); const bool has_points = (cache_size != 0); - const bool has_holes = (! m_c->hollowed_mesh()->get_hollowed_mesh() - && ! m_c->selection_info()->model_object()->sla_drain_holes.empty()); + const bool has_holes = (/*! m_c->hollowed_mesh()->get_hollowed_mesh() + &&*/ ! m_c->selection_info()->model_object()->sla_drain_holes.empty()); if (! has_points && ! has_holes) return; @@ -304,7 +306,8 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) if (m_editing_mode) { // in case the normal is not yet cached, find and cache it if (m_editing_cache[i].normal == Vec3f::Zero()) - m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + //m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); @@ -453,7 +456,7 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const // Return false if no intersection was found, true otherwise. bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { - if (! m_c->raycaster()->raycaster()) + if (m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->empty()) return false; const Camera& camera = wxGetApp().plater()->get_camera(); @@ -468,7 +471,7 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairraycaster()->raycaster()->unproject_on_mesh( + if (m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->unproject_on_mesh( mouse_pos, trafo.get_matrix(), camera, @@ -477,24 +480,24 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairhollowed_mesh()->get_hollowed_mesh()) { - sla::DrainHoles drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; - for (const sla::DrainHole& hole : drain_holes) { - if (hole.is_inside(hit)) { - in_hole = true; - break; - } - } - } - if (! in_hole) { +// if (! m_c->hollowed_mesh()->get_hollowed_mesh()) { +// sla::DrainHoles drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; +// for (const sla::DrainHole& hole : drain_holes) { +// if (hole.is_inside(hit)) { +// in_hole = true; +// break; +// } +// } +// } +// if (! in_hole) { // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); return true; - } +// } } return false; @@ -589,7 +592,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous for (size_t idx : points_idxs) points_inside.emplace_back((trafo.get_matrix().cast() * (m_editing_cache[idx].support_point.pos + m_editing_cache[idx].normal)).cast()); - for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs( + assert(!m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume).emtpy()); + for (size_t idx : m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_unobscured_idxs( trafo, wxGetApp().plater()->get_camera(), points_inside, m_c->object_clipper()->get_clipping_plane())) { @@ -1457,7 +1461,7 @@ void GLGizmoSlaSupports::update_raycasters_for_picking_transform() const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast()) * instance_scaling_matrix_inverse; if (m_editing_cache[i].normal == Vec3f::Zero()) - m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 136276803e..c4232ed48f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -25,7 +25,7 @@ private: bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); - const float RenderPointScale = 1.f; + static constexpr float RenderPointScale = 1.f; class CacheEntry { public: diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 52ae9b25b4..31fbb0f7ab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -23,7 +23,7 @@ CommonGizmosDataPool::CommonGizmosDataPool(GLCanvas3D* canvas) using c = CommonGizmosDataID; m_data[c::SelectionInfo].reset( new SelectionInfo(this)); m_data[c::InstancesHider].reset( new InstancesHider(this)); - m_data[c::HollowedMesh].reset( new HollowedMesh(this)); +// m_data[c::HollowedMesh].reset( new HollowedMesh(this)); m_data[c::Raycaster].reset( new Raycaster(this)); m_data[c::ObjectClipper].reset( new ObjectClipper(this)); m_data[c::SupportsClipper].reset( new SupportsClipper(this)); @@ -59,12 +59,12 @@ InstancesHider* CommonGizmosDataPool::instances_hider() const return inst_hider->is_valid() ? inst_hider : nullptr; } -HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const -{ - HollowedMesh* hol_mesh = dynamic_cast(m_data.at(CommonGizmosDataID::HollowedMesh).get()); - assert(hol_mesh); - return hol_mesh->is_valid() ? hol_mesh : nullptr; -} +//HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const +//{ +// HollowedMesh* hol_mesh = dynamic_cast(m_data.at(CommonGizmosDataID::HollowedMesh).get()); +// assert(hol_mesh); +// return hol_mesh->is_valid() ? hol_mesh : nullptr; +//} Raycaster* CommonGizmosDataPool::raycaster() const { @@ -117,16 +117,18 @@ bool CommonGizmosDataPool::check_dependencies(CommonGizmosDataID required) const void SelectionInfo::on_update() { const Selection& selection = get_pool()->get_canvas()->get_selection(); + + m_model_object = nullptr; + m_print_object = nullptr; + if (selection.is_single_full_instance()) { m_model_object = selection.get_model()->objects[selection.get_object_idx()]; - m_model_volume = nullptr; + + if (m_model_object) + m_print_object = get_pool()->get_canvas()->sla_print()->get_object(m_model_object->id()); + m_z_shift = selection.get_first_volume()->get_sla_shift_z(); } - else { - m_model_object = nullptr; - if (selection.is_single_volume()) - m_model_volume = selection.get_model()->objects[selection.get_object_idx()]->volumes[selection.get_first_volume()->volume_idx()]; - } } void SelectionInfo::on_release() @@ -253,78 +255,78 @@ void InstancesHider::render_cut() const -void HollowedMesh::on_update() -{ - const ModelObject* mo = get_pool()->selection_info()->model_object(); - bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; - if (! mo || ! is_sla) - return; +//void HollowedMesh::on_update() +//{ +// const ModelObject* mo = get_pool()->selection_info()->model_object(); +// bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; +// if (! mo || ! is_sla) +// return; - const GLCanvas3D* canvas = get_pool()->get_canvas(); - const PrintObjects& print_objects = canvas->sla_print()->objects(); - const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size())) - ? print_objects[m_print_object_idx] - : nullptr; +// const GLCanvas3D* canvas = get_pool()->get_canvas(); +// const PrintObjects& print_objects = canvas->sla_print()->objects(); +// const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size())) +// ? print_objects[m_print_object_idx] +// : nullptr; - // Find the respective SLAPrintObject. - if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { - m_print_objects_count = print_objects.size(); - m_print_object_idx = -1; - for (const SLAPrintObject* po : print_objects) { - ++m_print_object_idx; - if (po->model_object()->id() == mo->id()) { - print_object = po; - break; - } - } - } +// // Find the respective SLAPrintObject. +// if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { +// m_print_objects_count = print_objects.size(); +// m_print_object_idx = -1; +// for (const SLAPrintObject* po : print_objects) { +// ++m_print_object_idx; +// if (po->model_object()->id() == mo->id()) { +// print_object = po; +// break; +// } +// } +// } - // If there is a valid SLAPrintObject, check state of Hollowing step. - if (print_object) { - if (print_object->is_step_done(slaposDrillHoles) && !print_object->get_mesh_to_print().empty()) { - size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; - if (timestamp > m_old_hollowing_timestamp) { - const TriangleMesh& backend_mesh = print_object->get_mesh_to_print(); - if (! backend_mesh.empty()) { - m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); - Transform3d trafo_inv = (canvas->sla_print()->sla_trafo(*mo) * print_object->model_object()->volumes.front()->get_transformation().get_matrix()).inverse(); - m_hollowed_mesh_transformed->transform(trafo_inv); - m_drainholes = print_object->model_object()->sla_drain_holes; - m_old_hollowing_timestamp = timestamp; +// // If there is a valid SLAPrintObject, check state of Hollowing step. +// if (print_object) { +// if (print_object->is_step_done(slaposDrillHoles) && !print_object->get_mesh_to_print().empty()) { +// size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; +// if (timestamp > m_old_hollowing_timestamp) { +// const TriangleMesh& backend_mesh = print_object->get_mesh_to_print(); +// if (! backend_mesh.empty()) { +// m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); +// Transform3d trafo_inv = (canvas->sla_print()->sla_trafo(*mo) * print_object->model_object()->volumes.front()->get_transformation().get_matrix()).inverse(); +// m_hollowed_mesh_transformed->transform(trafo_inv); +// m_drainholes = print_object->model_object()->sla_drain_holes; +// m_old_hollowing_timestamp = timestamp; -// indexed_triangle_set interior = print_object->hollowed_interior_mesh(); -// its_flip_triangles(interior); -// m_hollowed_interior_transformed = std::make_unique(std::move(interior)); -// m_hollowed_interior_transformed->transform(trafo_inv); - } - else { - m_hollowed_mesh_transformed.reset(nullptr); - } - } - } - else - m_hollowed_mesh_transformed.reset(nullptr); - } -} +//// indexed_triangle_set interior = print_object->hollowed_interior_mesh(); +//// its_flip_triangles(interior); +//// m_hollowed_interior_transformed = std::make_unique(std::move(interior)); +//// m_hollowed_interior_transformed->transform(trafo_inv); +// } +// else { +// m_hollowed_mesh_transformed.reset(nullptr); +// } +// } +// } +// else +// m_hollowed_mesh_transformed.reset(nullptr); +// } +//} -void HollowedMesh::on_release() -{ - m_hollowed_mesh_transformed.reset(); - m_old_hollowing_timestamp = 0; - m_print_object_idx = -1; -} +//void HollowedMesh::on_release() +//{ +// m_hollowed_mesh_transformed.reset(); +// m_old_hollowing_timestamp = 0; +// m_print_object_idx = -1; +//} -const TriangleMesh* HollowedMesh::get_hollowed_mesh() const -{ - return m_hollowed_mesh_transformed.get(); -} +//const TriangleMesh* HollowedMesh::get_hollowed_mesh() const +//{ +// return m_hollowed_mesh_transformed.get(); +//} -const TriangleMesh* HollowedMesh::get_hollowed_interior() const -{ - return m_hollowed_interior_transformed.get(); -} +//const TriangleMesh* HollowedMesh::get_hollowed_interior() const +//{ +// return m_hollowed_interior_transformed.get(); +//} @@ -345,12 +347,13 @@ void Raycaster::on_update() mvs = mo->volumes; std::vector meshes; - if (mvs.size() == 1) { - assert(mvs.front()->is_model_part()); - const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); - if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh()) - meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); - } + const std::vector& mvs = mo->volumes; +// if (mvs.size() == 1) { +// assert(mvs.front()->is_model_part()); +// const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); +// if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh()) +// meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); +// } if (meshes.empty()) { for (const ModelVolume* v : mvs) { if (v->is_model_part()) @@ -396,9 +399,9 @@ void ObjectClipper::on_update() // which mesh should be cut? std::vector meshes; - bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh(); - if (has_hollowed) - meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh()); +// bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh(); +// if (has_hollowed) +// meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh()); if (meshes.empty()) for (const ModelVolume* mv : mo->volumes) @@ -412,8 +415,8 @@ void ObjectClipper::on_update() } m_old_meshes = meshes; - if (has_hollowed) - m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior()); +// if (has_hollowed) +// m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior()); m_active_inst_bb_radius = mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 42faa25f84..d6c5801230 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -5,11 +5,11 @@ #include #include "slic3r/GUI/MeshUtils.hpp" -#include "libslic3r/SLA/Hollowing.hpp" namespace Slic3r { class ModelObject; +class SLAPrintObject; class ModelVolume; namespace GUI { @@ -85,7 +85,7 @@ public: // Getters for the data that need to be accessed from the gizmos directly. CommonGizmosDataObjects::SelectionInfo* selection_info() const; CommonGizmosDataObjects::InstancesHider* instances_hider() const; - CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const; +// CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const; CommonGizmosDataObjects::Raycaster* raycaster() const; CommonGizmosDataObjects::ObjectClipper* object_clipper() const; CommonGizmosDataObjects::SupportsClipper* supports_clipper() const; @@ -158,6 +158,7 @@ public: // Returns a non-null pointer if the selection is a single full instance ModelObject* model_object() const { return m_model_object; } + const SLAPrintObject *print_object() const { return m_print_object; } // Returns a non-null pointer if the selection is a single volume ModelVolume* model_volume() const { return m_model_volume; } int get_active_instance() const; @@ -169,6 +170,7 @@ protected: private: ModelObject* m_model_object = nullptr; + const SLAPrintObject *m_print_object = nullptr; ModelVolume* m_model_volume = nullptr; // int m_active_inst = -1; float m_z_shift = 0.f; @@ -201,32 +203,32 @@ private: -class HollowedMesh : public CommonGizmosDataBase -{ -public: - explicit HollowedMesh(CommonGizmosDataPool* cgdp) - : CommonGizmosDataBase(cgdp) {} -#ifndef NDEBUG - CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } -#endif // NDEBUG +//class HollowedMesh : public CommonGizmosDataBase +//{ +//public: +// explicit HollowedMesh(CommonGizmosDataPool* cgdp) +// : CommonGizmosDataBase(cgdp) {} +//#ifndef NDEBUG +// CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } +//#endif // NDEBUG - const sla::DrainHoles &get_drainholes() const { return m_drainholes; } +// const sla::DrainHoles &get_drainholes() const { return m_drainholes; } - const TriangleMesh* get_hollowed_mesh() const; - const TriangleMesh* get_hollowed_interior() const; +// const TriangleMesh* get_hollowed_mesh() const; +// const TriangleMesh* get_hollowed_interior() const; -protected: - void on_update() override; - void on_release() override; +//protected: +// void on_update() override; +// void on_release() override; -private: - std::unique_ptr m_hollowed_mesh_transformed; - std::unique_ptr m_hollowed_interior_transformed; - size_t m_old_hollowing_timestamp = 0; - int m_print_object_idx = -1; - int m_print_objects_count = 0; - sla::DrainHoles m_drainholes; -}; +//private: +// std::unique_ptr m_hollowed_mesh_transformed; +// std::unique_ptr m_hollowed_interior_transformed; +// size_t m_old_hollowing_timestamp = 0; +// int m_print_object_idx = -1; +// int m_print_objects_count = 0; +// sla::DrainHoles m_drainholes; +//}; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d4b279f606..d57963e258 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2649,17 +2649,6 @@ std::vector Plater::priv::load_files(const std::vector& input_ model_object->ensure_on_bed(is_project_file); } - // check multi-part object adding for the SLA-printing - if (printer_technology == ptSLA) { - for (auto obj : model.objects) - if ( obj->volumes.size()>1 ) { - Slic3r::GUI::show_error(nullptr, - format_wxstr(_L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part"), - from_path(filename))); - return obj_idxs; - } - } - if (one_by_one) { if ((type_3mf && !is_project_file) || (type_any_amf && !type_zip_amf)) model.center_instances_around_point(this->bed.build_volume().bed_center()); diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index a92c622c1b..f309d6eaaa 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -88,7 +88,7 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr item) } } -SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) +SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const { double closest_hit_squared_distance = std::numeric_limits::max(); auto is_closest = [&closest_hit_squared_distance](const Camera& camera, const Vec3f& hit) { @@ -107,7 +107,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came auto test_raycasters = [this, is_closest, clipping_plane](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr; - std::vector>* raycasters = get_raycasters(type); + const std::vector>* raycasters = get_raycasters(type); const Vec3f camera_forward = camera.get_dir_forward().cast(); HitResult current_hit = { type }; for (std::shared_ptr item : *raycasters) { @@ -214,6 +214,20 @@ std::vector>* SceneRaycaster::get_raycasters return ret; } +const std::vector>* SceneRaycaster::get_raycasters(EType type) const +{ + const std::vector>* ret = nullptr; + switch (type) + { + case EType::Bed: { ret = &m_bed; break; } + case EType::Volume: { ret = &m_volumes; break; } + case EType::Gizmo: { ret = &m_gizmos; break; } + default: { break; } + } + assert(ret != nullptr); + return ret; +} + int SceneRaycaster::base_id(EType type) { switch (type) diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index 2254a2022d..bf8ab2b1bf 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -89,10 +89,11 @@ public: void remove_raycaster(std::shared_ptr item); std::vector>* get_raycasters(EType type); + const std::vector>* get_raycasters(EType type) const; void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; } - HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr); + HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr) const; #if ENABLE_RAYCAST_PICKING_DEBUG void render_hit(const Camera& camera); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index b27f7a7381..7caf2d9ea7 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -62,18 +62,18 @@ Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_trans bool Selection::Clipboard::is_sla_compliant() const { - if (m_mode == Selection::Volume) - return false; +// if (m_mode == Selection::Volume) +// return false; - for (const ModelObject* o : m_model->objects) { - if (o->is_multiparts()) - return false; +// for (const ModelObject* o : m_model->objects) { +// if (o->is_multiparts()) +// return false; - for (const ModelVolume* v : o->volumes) { - if (v->is_modifier()) - return false; - } - } +// for (const ModelVolume* v : o->volumes) { +// if (v->is_modifier()) +// return false; +// } +// } return true; } @@ -571,13 +571,13 @@ bool Selection::is_from_single_object() const bool Selection::is_sla_compliant() const { - if (m_mode == Volume) - return false; +// if (m_mode == Volume) +// return false; - for (unsigned int i : m_list) { - if ((*m_volumes)[i]->is_modifier) - return false; - } +// for (unsigned int i : m_list) { +// if ((*m_volumes)[i]->is_modifier) +// return false; +// } return true; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d5b909f370..9515fa2b6b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3279,9 +3279,9 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, const PresetWithVendorProfile new_printer_preset_with_vendor_profile = m_presets->get_preset_with_vendor_profile(new_printer_preset); PrinterTechnology old_printer_technology = m_presets->get_edited_preset().printer_technology(); PrinterTechnology new_printer_technology = new_printer_preset.printer_technology(); - if (new_printer_technology == ptSLA && old_printer_technology == ptFFF && !wxGetApp().may_switch_to_SLA_preset(_L("New printer preset selected"))) + /*if (new_printer_technology == ptSLA && old_printer_technology == ptFFF && !wxGetApp().may_switch_to_SLA_preset(_L("New printer preset selected"))) canceled = true; - else { + else */{ struct PresetUpdate { Preset::Type tab_type; PresetCollection *presets; From 2a8c9d74626bafb181b5ed023b119345669558b4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 21 Oct 2022 16:59:00 +0200 Subject: [PATCH 08/75] Refinements --- src/libslic3r/CSGMesh/SliceCSGMesh.hpp | 8 ++++++ src/libslic3r/SLAPrint.cpp | 37 +++++++++++++++++++------- src/libslic3r/SLAPrint.hpp | 24 ++--------------- src/libslic3r/SLAPrintSteps.cpp | 17 ++++++------ 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp index 95035b5ef5..32f7905465 100644 --- a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -46,6 +46,14 @@ std::vector slice_csgmesh_ex( break; } } + + for (ExPolygons &slice : ret) { + auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){ + return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON); + }); + + slice.erase(it, slice.end()); + } } return ret; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index c3b90b1928..b8000f3849 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -792,13 +792,6 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object) : Inherited(print, model_object) -// , m_transformed_rmesh([this](TriangleMesh &obj) { -//// obj = m_model_object->raw_mesh(); -//// if (!obj.empty()) { -//// obj.transform(m_trafo); -//// } -// obj = transformed_mesh_csg(*this); -// }) {} SLAPrintObject::~SLAPrintObject() {} @@ -1056,7 +1049,8 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const return EMPTY_MESH; } -const TriangleMesh &SLAPrintObject::get_mesh_to_print() const { +const TriangleMesh &SLAPrintObject::get_mesh_to_print() const +{ const TriangleMesh *ret = nullptr; int s = SLAPrintObjectStep::slaposCount; @@ -1068,7 +1062,7 @@ const TriangleMesh &SLAPrintObject::get_mesh_to_print() const { } if (!ret) - ret = &m_transformed_rmesh; + ret = &EMPTY_MESH; return *ret; } @@ -1158,4 +1152,29 @@ void SLAPrint::StatusReporter::operator()(SLAPrint & p, p.set_status(int(std::round(st)), msg, flags); } +namespace csg { + +inline bool operator==(const VoxelizeParams &a, const VoxelizeParams &b) +{ + std::hash h; + return h(a) == h(b); +} + +VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, const VoxelizeParams &p) +{ + VoxelGridPtr &ret = part.gridcache[p]; + + if (!ret) { + ret = mesh_to_grid(*csg::get_mesh(part), + csg::get_transform(part), + p.voxel_scale(), + p.exterior_bandwidth(), + p.interior_bandwidth()); + } + + return clone(*ret); +} + +} // namespace csg + } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 084c78024a..9ca6100870 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -91,27 +91,7 @@ struct CSGPartForStep : public csg::CSGPart namespace csg { -inline bool operator==(const VoxelizeParams &a, const VoxelizeParams &b) -{ - std::hash h; - return h(a) == h(b); -} - -inline VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, - const VoxelizeParams &p) -{ - VoxelGridPtr &ret = part.gridcache[p]; - - if (!ret) { - ret = mesh_to_grid(*csg::get_mesh(part), - csg::get_transform(part), - p.voxel_scale(), - p.exterior_bandwidth(), - p.interior_bandwidth()); - } - - return clone(*ret); -} +VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, const VoxelizeParams &p); } // namespace csg @@ -372,7 +352,7 @@ private: std::vector m_model_height_levels; // Caching the transformed (m_trafo) raw mesh of the object - TriangleMesh m_transformed_rmesh; +// TriangleMesh m_transformed_rmesh; struct SupportData { diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index b0efa31fa2..52915a0281 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -194,10 +194,6 @@ struct csg_inserter { void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po) { po.m_mesh_to_slice.clear(); - - po.m_transformed_rmesh = po.m_model_object->raw_mesh(); - po.m_transformed_rmesh.transform(po.trafo()); - csg::model_to_csgmesh(*po.model_object(), po.trafo(), csg_inserter{po.m_mesh_to_slice, slaposAssembly}, csg::mpartsPositive | csg::mpartsNegative); @@ -265,8 +261,11 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) csg_inserter{po.m_mesh_to_slice, slaposDrillHoles}, csg::mpartsDrillHoles); + auto r = po.m_mesh_to_slice.equal_range(slaposDrillHoles); + // update preview mesh - generate_preview(po, slaposDrillHoles); + if (r.first != r.second) + generate_preview(po, slaposDrillHoles); } template @@ -374,8 +373,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) auto thr = [this]() { m_print->throw_if_canceled(); }; auto &slice_grid = po.m_model_height_levels; - Range csgrange = {po.m_mesh_to_slice.begin(), po.m_mesh_to_slice.end()}; - po.m_model_slices = slice_csgmesh_ex(csgrange, slice_grid, params, thr); + po.m_model_slices = slice_csgmesh_ex(range(po.m_mesh_to_slice), slice_grid, params, thr); auto mit = slindex_it; for (size_t id = 0; @@ -387,7 +385,8 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) // We apply the printer correction offset here. apply_printer_corrections(po, soModel); - generate_preview(po, slaposObjectSlice); +// po.m_preview_meshes[slaposObjectSlice] = po.get_mesh_to_print(); +// report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); } static void filter_support_points_by_modifiers( @@ -448,7 +447,7 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) if (!po.m_supportdata) po.m_supportdata = std::make_unique( - po.m_preview_meshes[slaposObjectSlice] + po.get_mesh_to_print() ); po.m_supportdata->input.zoffset = csgmesh_positive_bb(po.m_mesh_to_slice) From 191f04568d6c86359950a24c6f40fbc111d5ecf0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 21 Oct 2022 17:55:30 +0200 Subject: [PATCH 09/75] Do mesh booleans with cgal if possible. --- .../CSGMesh/PerformCSGMeshBooleans.hpp | 49 +++++++++++++++++-- src/libslic3r/CSGMesh/SliceCSGMesh.hpp | 2 + src/libslic3r/MeshBoolean.hpp | 11 +++-- src/libslic3r/SLAPrintSteps.cpp | 44 +++++++++-------- src/libslic3r/SLAPrintSteps.hpp | 3 +- 5 files changed, 80 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp index 5c64c65e0d..da1ddce3f9 100644 --- a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp +++ b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp @@ -5,15 +5,58 @@ #include "libslic3r/MeshBoolean.hpp" -namespace Slic3r { +namespace Slic3r { namespace csg { + +// This method can be overriden when a specific CSGPart type supports caching +// of the voxel grid +template +MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart) +{ + const indexed_triangle_set *its = csg::get_mesh(csgpart); + MeshBoolean::cgal::CGALMeshPtr ret; + + if (its) { + indexed_triangle_set m = *its; + its_transform(m, get_transform(csgpart)); + ret = MeshBoolean::cgal::triangle_mesh_to_cgal(m); + } + + return ret; +} template void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMesh &cgalm, - const Range &csg) + const Range &csgparts) { - + for (auto &csgpart : csgparts) { + auto m = get_cgalmesh(csgpart); + if (m) { + switch (get_operation(csgpart)) { + case CSGType::Union: + MeshBoolean::cgal::plus(cgalm, *m); + break; + case CSGType::Difference: + MeshBoolean::cgal::minus(cgalm, *m); + break; + case CSGType::Intersection: + MeshBoolean::cgal::intersect(cgalm, *m); + break; + } + } + } } +template +MeshBoolean::cgal::CGALMeshPtr perform_csgmesh_booleans(const Range &csgparts) +{ + auto ret = MeshBoolean::cgal::triangle_mesh_to_cgal(indexed_triangle_set{}); + if (ret) + perform_csgmesh_booleans(*ret, csgparts); + + return ret; +} + +} // namespace csg } // namespace Slic3r #endif // PERFORMCSGMESHBOOLEANS_HPP diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp index 32f7905465..f8a7354204 100644 --- a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -52,6 +52,8 @@ std::vector slice_csgmesh_ex( return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON); }); + // Hopefully, ExPolygons are moved, not copied to new positions + // and that is cheap for expolygons slice.erase(it, slice.end()); } } diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index 140c969310..441fc78c11 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -26,16 +26,17 @@ namespace cgal { struct CGALMesh; struct CGALMeshDeleter { void operator()(CGALMesh *ptr); }; +using CGALMeshPtr = std::unique_ptr; -std::unique_ptr -triangle_mesh_to_cgal(const std::vector &V, - const std::vector &F); +CGALMeshPtr triangle_mesh_to_cgal( + const std::vector &V, + const std::vector &F); -inline std::unique_ptr triangle_mesh_to_cgal(const indexed_triangle_set &M) +inline CGALMeshPtr triangle_mesh_to_cgal(const indexed_triangle_set &M) { return triangle_mesh_to_cgal(M.vertices, M.indices); } -inline std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M) +inline CGALMeshPtr triangle_mesh_to_cgal(const TriangleMesh &M) { return triangle_mesh_to_cgal(M.its); } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 52915a0281..a5873155c4 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -18,11 +18,13 @@ #include #include #include +#include > +#include #include #include #include -#include +//#include #include @@ -128,48 +130,50 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin } } - -void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) +indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( + SLAPrintObject &po, SLAPrintObjectStep step) { Benchmark bench; bench.start(); + // update preview mesh double vscale = 1. / (2. * po.m_config.layer_height.getFloat()); auto voxparams = csg::VoxelizeParams{} .voxel_scale(vscale) .exterior_bandwidth(1.f) .interior_bandwidth(1.f); + auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams); indexed_triangle_set m = grid_to_mesh(*grid); -// if (!m.empty()) { -// // simplify mesh lossless + bench.stop(); -// std::cout << "simplify started" << std::endl; -// int expected_cnt = m.indices.size() * 0.8; //std::pow(po.m_transformed_rmesh.volume() / std::pow(1./vscale, 3), 2./3.); -// std::cout << "expected triangles " << expected_cnt << std::endl; -// float err = std::pow(vscale, 3); -// its_quadric_edge_collapse(m, 0U, &err); -// std::cout << "simplify ended " << m.indices.size() << " triangles" << std::endl; + std::cout << "Preview gen took: " << bench.getElapsedSec() << std::endl; -// std::cout << "cleanup started" << std::endl; -// its_compactify_vertices(m); -// its_merge_vertices(m); -// std::cout << "cleanup ended" << std::endl; -// } + return m; +} - po.m_preview_meshes[step] = TriangleMesh{std::move(m)}; + +void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) +{ + MeshBoolean::cgal::CGALMeshPtr cgalptr; + + try { + cgalptr = csg::perform_csgmesh_booleans(range(po.m_mesh_to_slice)); + } catch(...) {} + + if (cgalptr) { + po.m_preview_meshes[step] = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalptr); + } else + 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] = {}; } - bench.stop(); - - std::cout << "Preview gen took: " << bench.getElapsedSec() << std::endl; using namespace std::string_literals; report_status(-2, "Reload preview from step "s + std::to_string(int(step)), SlicingStatus::RELOAD_SLA_PREVIEW); diff --git a/src/libslic3r/SLAPrintSteps.hpp b/src/libslic3r/SLAPrintSteps.hpp index 30dff8628e..3d63cbbadb 100644 --- a/src/libslic3r/SLAPrintSteps.hpp +++ b/src/libslic3r/SLAPrintSteps.hpp @@ -46,7 +46,8 @@ private: void apply_printer_corrections(SLAPrintObject &po, SliceOrigin o); void generate_preview(SLAPrintObject &po, SLAPrintObjectStep step); - + indexed_triangle_set generate_preview_vdb(SLAPrintObject &po, SLAPrintObjectStep step); + public: explicit Steps(SLAPrint *print); From ce5d242f76be4a94b074ba1db2da9eb8bbd4441d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Nov 2022 09:54:55 +0100 Subject: [PATCH 10/75] Fix failing test for hollowing --- src/libslic3r/SLA/Hollowing.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 3cc8ea340f..974e525638 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -282,7 +282,11 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags) if (flags & hfRemoveInsideTriangles && interior.gridptr) remove_inside_triangles(mesh, interior); - mesh.merge(TriangleMesh{interior.mesh}); + indexed_triangle_set interi = interior.mesh; + sla::swap_normals(interi); + TriangleMesh inter{std::move(interi)}; + + mesh.merge(inter); } // Get the distance of p to the interior's zero iso_surface. Interior should From f100a596883c12c81c0ff9af3c333a79a4f9fd84 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Nov 2022 12:42:02 +0100 Subject: [PATCH 11/75] Cherry-picking 118f4859c472ccbc30b43101c6674dadc81d7b12 And resolve conflicts --- resources/shaders/110/gouraud_light_clip.fs | 17 + resources/shaders/110/gouraud_light_clip.vs | 54 +++ resources/shaders/140/gouraud_light_clip.fs | 19 + resources/shaders/140/gouraud_light_clip.vs | 54 +++ resources/shaders/ES/gouraud_light_clip.fs | 19 + resources/shaders/ES/gouraud_light_clip.vs | 54 +++ src/libslic3r/SLAPrint.hpp | 5 + src/slic3r/GUI/3DScene.cpp | 68 ---- src/slic3r/GUI/3DScene.hpp | 21 -- src/slic3r/GUI/GLCanvas3D.cpp | 205 +---------- src/slic3r/GUI/GLCanvas3D.hpp | 2 - src/slic3r/GUI/GLShadersManager.cpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 249 ++++++++----- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 18 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 349 ++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 18 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 210 ++++------- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 41 +-- src/slic3r/GUI/SceneRaycaster.cpp | 4 +- 19 files changed, 674 insertions(+), 735 deletions(-) create mode 100644 resources/shaders/110/gouraud_light_clip.fs create mode 100644 resources/shaders/110/gouraud_light_clip.vs create mode 100644 resources/shaders/140/gouraud_light_clip.fs create mode 100644 resources/shaders/140/gouraud_light_clip.vs create mode 100644 resources/shaders/ES/gouraud_light_clip.fs create mode 100644 resources/shaders/ES/gouraud_light_clip.vs diff --git a/resources/shaders/110/gouraud_light_clip.fs b/resources/shaders/110/gouraud_light_clip.fs new file mode 100644 index 0000000000..5c70687093 --- /dev/null +++ b/resources/shaders/110/gouraud_light_clip.fs @@ -0,0 +1,17 @@ +#version 110 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float clipping_planes_dot; + +void main() +{ + if (clipping_planes_dot < 0.0) + discard; + + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/110/gouraud_light_clip.vs b/resources/shaders/110/gouraud_light_clip.vs new file mode 100644 index 0000000000..6d7c32e1b3 --- /dev/null +++ b/resources/shaders/110/gouraud_light_clip.vs @@ -0,0 +1,54 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float clipping_planes_dot; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 eye_position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * eye_position; + + // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded. + clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane); +} diff --git a/resources/shaders/140/gouraud_light_clip.fs b/resources/shaders/140/gouraud_light_clip.fs new file mode 100644 index 0000000000..714e5bcaae --- /dev/null +++ b/resources/shaders/140/gouraud_light_clip.fs @@ -0,0 +1,19 @@ +#version 140 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +in vec2 intensity; + +in float clipping_planes_dot; + +out vec4 out_color; + +void main() +{ + if (clipping_planes_dot < 0.0) + discard; + + out_color = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/140/gouraud_light_clip.vs b/resources/shaders/140/gouraud_light_clip.vs new file mode 100644 index 0000000000..8fca59380f --- /dev/null +++ b/resources/shaders/140/gouraud_light_clip.vs @@ -0,0 +1,54 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +in vec3 v_position; +in vec3 v_normal; + +// x = tainted, y = specular; +out vec2 intensity; + +out float clipping_planes_dot; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 eye_position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * eye_position; + + // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded. + clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane); +} diff --git a/resources/shaders/ES/gouraud_light_clip.fs b/resources/shaders/ES/gouraud_light_clip.fs new file mode 100644 index 0000000000..45cae0ddb0 --- /dev/null +++ b/resources/shaders/ES/gouraud_light_clip.fs @@ -0,0 +1,19 @@ +#version 100 + +precision highp float; + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float clipping_planes_dot; + +void main() +{ + if (clipping_planes_dot < 0.0) + discard; + + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/ES/gouraud_light_clip.vs b/resources/shaders/ES/gouraud_light_clip.vs new file mode 100644 index 0000000000..f3ab8c3dc0 --- /dev/null +++ b/resources/shaders/ES/gouraud_light_clip.vs @@ -0,0 +1,54 @@ +#version 100 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; + +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +attribute vec3 v_position; +attribute vec3 v_normal; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float clipping_planes_dot; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 eye_position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + gl_Position = projection_matrix * eye_position; + + // Fill in the scalar for fragment shader clipping. Fragments with this value lower than zero are discarded. + clipping_planes_dot = dot(volume_world_matrix * vec4(v_position, 1.0), clipping_plane); +} diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 9ca6100870..ca74cb3914 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -476,6 +476,11 @@ public: const PrintObjects& objects() const { return m_objects; } // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects // in the notification center. + const SLAPrintObject* get_print_object_by_model_object_id(ObjectID object_id) const { + auto it = std::find_if(m_objects.begin(), m_objects.end(), + [object_id](const SLAPrintObject* obj) { return obj->model_object()->id() == object_id; }); + return (it == m_objects.end()) ? nullptr : *it; + } const SLAPrintObject* get_object(ObjectID object_id) const { auto it = std::find_if(m_objects.begin(), m_objects.end(), [object_id](const SLAPrintObject *obj) { return obj->id() == object_id; }); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 103ab339c2..c6ef2d13b1 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -852,74 +852,6 @@ int GLVolumeCollection::load_object_volume( return int(this->volumes.size() - 1); } -//// Load SLA auxiliary GLVolumes (for support trees or pad). -//// This function produces volumes for multiple instances in a single shot, -//// as some object specific mesh conversions may be expensive. -//#if ENABLE_LEGACY_OPENGL_REMOVAL -//void GLVolumeCollection::load_object_auxiliary( -// const SLAPrintObject* print_object, -// int obj_idx, -// // pairs of -// const std::vector>& instances, -// SLAPrintObjectStep milestone, -// // Timestamp of the last change of the milestone -// size_t timestamp) -//#else -//void GLVolumeCollection::load_object_auxiliary( -// const SLAPrintObject *print_object, -// int obj_idx, -// // pairs of -// const std::vector>& instances, -// SLAPrintObjectStep milestone, -// // Timestamp of the last change of the milestone -// size_t timestamp, -// bool opengl_initialized) -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//{ -// assert(print_object->is_step_done(milestone)); -// Transform3d mesh_trafo_inv = print_object->trafo().inverse(); -// // Get the support mesh. -// TriangleMesh mesh = print_object->get_mesh(milestone); -// mesh.transform(mesh_trafo_inv); -// // Convex hull is required for out of print bed detection. -// TriangleMesh convex_hull = mesh.convex_hull_3d(); -// for (const std::pair& instance_idx : instances) { -// const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; -// this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); -// GLVolume& v = *this->volumes.back(); -//#if ENABLE_LEGACY_OPENGL_REMOVAL -//#if ENABLE_SMOOTH_NORMALS -// v.model.init_from(mesh, true); -//#else -// v.model.init_from(mesh); -// v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR); -//#if ENABLE_RAYCAST_PICKING -// v.mesh_raycaster = std::make_unique(std::make_shared(mesh)); -//#endif // ENABLE_RAYCAST_PICKING -//#endif // ENABLE_SMOOTH_NORMALS -//#else -//#if ENABLE_SMOOTH_NORMALS -// v.indexed_vertex_array.load_mesh(mesh, true); -//#else -// v.indexed_vertex_array.load_mesh(mesh); -//#endif // ENABLE_SMOOTH_NORMALS -// v.indexed_vertex_array.finalize_geometry(opengl_initialized); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -// v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); -// v.geometry_id = std::pair(timestamp, model_instance.id().id); -// // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. -// if (&instance_idx == &instances.back()) -// v.set_convex_hull(std::move(convex_hull)); -// else -// v.set_convex_hull(convex_hull); -// v.is_modifier = false; -// v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree); -// v.set_instance_transformation(model_instance.get_transformation()); -// // Leave the volume transformation at identity. -// // v.set_volume_transformation(model_volume->get_transformation()); -// } -//} - #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_OPENGL_ES int GLVolumeCollection::load_wipe_tower_preview( diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index abf3e9264c..d1c16290f1 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -658,16 +658,6 @@ public: int volume_idx, int instance_idx); - // Load SLA auxiliary GLVolumes (for support trees or pad). -// void load_object_auxiliary( -// const SLAPrintObject* print_object, -// int obj_idx, -// // pairs of -// const std::vector>& instances, -// SLAPrintObjectStep milestone, -// // Timestamp of the last change of the milestone -// size_t timestamp); - #if ENABLE_OPENGL_ES int load_wipe_tower_preview( float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh = nullptr); @@ -689,17 +679,6 @@ public: int instance_idx, bool opengl_initialized); - // Load SLA auxiliary GLVolumes (for support trees or pad). - void load_object_auxiliary( - const SLAPrintObject *print_object, - int obj_idx, - // pairs of - const std::vector>& instances, - SLAPrintObjectStep milestone, - // Timestamp of the last change of the milestone - size_t timestamp, - bool opengl_initialized); - int load_wipe_tower_preview( float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized); #endif // ENABLE_LEGACY_OPENGL_REMOVAL diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a698633a84..636080db79 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2011,15 +2011,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re size_t volume_idx; }; -// // SLA steps to pull the preview meshes for. -// typedef std::array SLASteps; -// SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; -// struct SLASupportState { -// std::array::value> step; -// }; -// // State of the sla_steps for all SLAPrintObjects. -// std::vector sla_support_state; - std::vector instance_ids_selected; std::vector map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1)); std::vector deleted_volumes; @@ -2044,33 +2035,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } } -// if (printer_technology == ptSLA) { -// const SLAPrint* sla_print = this->sla_print(); -//#ifndef NDEBUG -// // Verify that the SLAPrint object is synchronized with m_model. -// check_model_ids_equal(*m_model, sla_print->model()); -//#endif /* NDEBUG */ -// sla_support_state.reserve(sla_print->objects().size()); -// for (const SLAPrintObject* print_object : sla_print->objects()) { -// SLASupportState state; -// for (size_t istep = 0; istep < sla_steps.size(); ++istep) { -// state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]); -// if (state.step[istep].state == PrintStateBase::DONE) { -// if (!print_object->has_mesh(sla_steps[istep])) -// // Consider the DONE step without a valid mesh as invalid for the purpose -// // of mesh visualization. -// state.step[istep].state = PrintStateBase::INVALID; -// else if (sla_steps[istep] != slaposDrillHoles) -// for (const ModelInstance* model_instance : print_object->model_object()->instances) -// // Only the instances, which are currently printable, will have the SLA support structures kept. -// // The instances outside the print bed will have the GLVolumes of their support structures released. -// if (model_instance->is_printable()) -// aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id()); -// } -// } -// sla_support_state.emplace_back(state); -// } -// } + std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower); // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh. @@ -2190,139 +2155,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } if (printer_technology == ptSLA) { -// size_t idx = 0; -// const SLAPrint *sla_print = this->sla_print(); -// std::vector shift_zs(m_model->objects.size(), 0); -// double relative_correction_z = sla_print->relative_correction().z(); -// if (relative_correction_z <= EPSILON) -// relative_correction_z = 1.; -// for (const SLAPrintObject *print_object : sla_print->objects()) { -// SLASupportState &state = sla_support_state[idx ++]; -// const ModelObject *model_object = print_object->model_object(); -// // Find an index of the ModelObject -// int object_idx; -// // There may be new SLA volumes added to the scene for this print_object. -// // Find the object index of this print_object in the Model::objects list. -// auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object); -// assert(it != sla_print->model().objects.end()); -// object_idx = it - sla_print->model().objects.begin(); -// // Cache the Z offset to be applied to all volumes with this object_idx. -// shift_zs[object_idx] = print_object->get_current_elevation() / relative_correction_z; -// // Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene. -// // pairs of -// std::vector> instances[std::tuple_size::value]; -// for (size_t print_instance_idx = 0; print_instance_idx < print_object->instances().size(); ++ print_instance_idx) { -// const SLAPrintObject::Instance &instance = print_object->instances()[print_instance_idx]; -// // Find index of ModelInstance corresponding to this SLAPrintObject::Instance. -// auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), -// [&instance](const ModelInstance *mi) { return mi->id() == instance.instance_id; }); -// assert(it != model_object->instances.end()); -// int instance_idx = it - model_object->instances.begin(); -// for (size_t istep = 0; istep < sla_steps.size(); ++ istep) -// if (sla_steps[istep] == slaposDrillHoles) { -// // Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance, -// // not into its own GLVolume. -// // There shall always be such a GLVolume allocated. -// ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); -// auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); -// assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); -// assert(!it->new_geometry()); -// GLVolume &volume = *m_volumes.volumes[it->volume_idx]; -// if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { -// // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// volume.model.reset(); -//#else -// volume.indexed_vertex_array.release_geometry(); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -// if (state.step[istep].state == PrintStateBase::DONE) { -// TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); -// assert(! mesh.empty()); - -// // sla_trafo does not contain volume trafo. To get a mesh to create -// // a new volume from, we have to apply vol trafo inverse separately. -// const ModelObject& mo = *m_model->objects[volume.object_idx()]; -// Transform3d trafo = sla_print->sla_trafo(mo) -// * mo.volumes.front()->get_transformation().get_matrix(); -// mesh.transform(trafo.inverse()); -//#if ENABLE_SMOOTH_NORMALS -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// volume.model.init_from(mesh, true); -//#else -// volume.indexed_vertex_array.load_mesh(mesh, true); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//#else -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// volume.model.init_from(mesh); -//#if ENABLE_RAYCAST_PICKING -// volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); -//#endif // ENABLE_RAYCAST_PICKING -//#else -// volume.indexed_vertex_array.load_mesh(mesh); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//#endif // ENABLE_SMOOTH_NORMALS -// } -// else { -// // Reload the original volume. -//#if ENABLE_SMOOTH_NORMALS -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -//#else -// volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//#else -//#if ENABLE_LEGACY_OPENGL_REMOVAL -//#if ENABLE_RAYCAST_PICKING -// const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); -// volume.model.init_from(new_mesh); -// volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); -//#else -// volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); -//#endif // ENABLE_RAYCAST_PICKING -//#else -// volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -//#endif // ENABLE_SMOOTH_NORMALS -// } -//#if !ENABLE_LEGACY_OPENGL_REMOVAL -// volume.finalize_geometry(true); -//#endif // !ENABLE_LEGACY_OPENGL_REMOVAL -// } -// //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable -// // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables -// // of various concenrs (model vs. 3D print path). -// volume.offsets = { state.step[istep].timestamp }; -// } -// else if (state.step[istep].state == PrintStateBase::DONE) { -// // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. -// ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); -// auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); -// assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); -// if (it->new_geometry()) { -// // This can be an SLA support structure that should not be rendered (in case someone used undo -// // to revert to before it was generated). If that's the case, we should not generate anything. -// if (model_object->sla_points_status != sla::PointsStatus::NoPoints) -// instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); -// else -// shift_zs[object_idx] = 0.; -// } -// else { -// // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. -// m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); -// m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); -// } -// } -// } - -// for (size_t istep = 0; istep < sla_steps.size(); ++istep) -// if (!instances[istep].empty()) -//#if ENABLE_LEGACY_OPENGL_REMOVAL -// m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); -//#else -// m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); -//#endif // ENABLE_LEGACY_OPENGL_REMOVAL -// } - // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed for (GLVolume* volume : m_volumes.volumes) if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { @@ -7638,25 +7470,24 @@ void GLCanvas3D::_load_sla_shells() }; // adds objects' volumes - for (const SLAPrintObject* obj : print->objects()) - /*if (obj->is_step_done(slaposSliceSupports))*/ { - unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); - for (const SLAPrintObject::Instance& instance : obj->instances()) { - add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); - // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when - // through the update_volumes_colors_by_extruder() call. - m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); - if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty()) - add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true); - if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty()) - add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); - } - double shift_z = obj->get_current_elevation(); - for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { - // apply shift z - m_volumes.volumes[i]->set_sla_shift_z(shift_z); - } + for (const SLAPrintObject* obj : print->objects()) { + unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); + for (const SLAPrintObject::Instance& instance : obj->instances()) { + add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); + // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when + // through the update_volumes_colors_by_extruder() call. + m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); + if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty()) + add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true); + if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty()) + add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); } + double shift_z = obj->get_current_elevation(); + for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { + // apply shift z + m_volumes.volumes[i]->set_sla_shift_z(shift_z); + } + } update_volumes_colors_by_extruder(); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 140cbaf748..a519a26a19 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -688,8 +688,6 @@ public: void set_raycaster_gizmos_on_top(bool value) { m_scene_raycaster.set_gizmos_on_top(value); } - - const SceneRaycaster & raycaster() const { return m_scene_raycaster; } #endif // ENABLE_RAYCAST_PICKING void set_as_dirty(); diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index f0b91faf32..a60a5121ba 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -72,6 +72,8 @@ std::pair GLShadersManager::init() #if ENABLE_LEGACY_OPENGL_REMOVAL // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview valid &= append_shader("gouraud_light", { prefix + "gouraud_light.vs", prefix + "gouraud_light.fs" }); + // extend "gouraud_light" by adding clipping, used in sla gizmos + valid &= append_shader("gouraud_light_clip", { prefix + "gouraud_light_clip.vs", prefix + "gouraud_light_clip.fs" }); // used to render printbed valid &= append_shader("printbed", { prefix + "printbed.vs", prefix + "printbed.fs" }); #else diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 52aed6bfc8..d098d41f5d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -10,6 +10,7 @@ #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/Plater.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/SLAPrint.hpp" #include "libslic3r/Model.hpp" @@ -53,14 +54,21 @@ void GLGizmoHollow::data_changed() reload_cache(); m_old_mo_id = mo->id(); } -// if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) -// m_holes_in_drilled_mesh = mo->sla_drain_holes; + + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po != nullptr && po->get_mesh_to_print().empty()) + process_mesh(slaposAssembly); + + update_volumes(); + #if ENABLE_RAYCAST_PICKING - if (m_raycasters.empty()) - on_register_raycasters_for_picking(); + if (m_hole_raycasters.empty()) + register_hole_raycasters_for_picking(); else - update_raycasters_for_picking_transform(); + update_hole_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING + + m_c->instances_hider()->set_hide_full_scene(true); } } @@ -87,16 +95,15 @@ void GLGizmoHollow::on_render() glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - if (selection.is_from_single_instance()) #if ENABLE_RAYCAST_PICKING - render_points(selection); + render_volumes(); + render_points(selection); #else - render_points(selection, false); + render_points(selection, false); #endif // ENABLE_RAYCAST_PICKING m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); - m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } @@ -104,27 +111,14 @@ void GLGizmoHollow::on_render() #if ENABLE_RAYCAST_PICKING void GLGizmoHollow::on_register_raycasters_for_picking() { - assert(m_raycasters.empty()); - - init_cylinder_model(); - - set_sla_auxiliary_volumes_picking_state(false); - - const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); - if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) { - const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; - for (int i = 0; i < (int)drain_holes.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster)); - } - update_raycasters_for_picking_transform(); - } + register_hole_raycasters_for_picking(); + register_volume_raycasters_for_picking(); } void GLGizmoHollow::on_unregister_raycasters_for_picking() { - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); - m_raycasters.clear(); - set_sla_auxiliary_volumes_picking_state(true); + unregister_hole_raycasters_for_picking(); + unregister_volume_raycasters_for_picking(); } #else void GLGizmoHollow::on_render_for_picking() @@ -190,7 +184,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #if ENABLE_RAYCAST_PICKING const bool clipped = is_mesh_point_clipped(drain_hole.pos.cast()); - m_raycasters[i]->set_active(!clipped); + m_hole_raycasters[i]->set_active(!clipped); if (clipped) continue; #else @@ -270,6 +264,25 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } +void GLGizmoHollow::render_volumes() +{ + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("emission_factor", 0.0f); + const Camera& camera = wxGetApp().plater()->get_camera(); + + ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane(); + clipping_plane.set_normal(-clipping_plane.get_normal()); + m_volumes.set_clipping_plane(clipping_plane.get_data()); + + m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); + shader->stop_using(); + +} + bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const { if (m_c->object_clipper()->get_position() == 0.) @@ -291,48 +304,26 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const // Return false if no intersection was found, true otherwise. bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { + if (m_c->raycaster()->raycasters().size() != 1) + return false; if (! m_c->raycaster()->raycaster()) return false; - const Camera& camera = wxGetApp().plater()->get_camera(); - const Selection& selection = m_parent.get_selection(); - const GLVolume* volume = selection.get_first_volume(); - Geometry::Transformation trafo = volume->get_instance_transformation() * volume->get_volume_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); - - double clp_dist = m_c->object_clipper()->get_position(); - const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); - // The raycaster query Vec3f hit; Vec3f normal; if (m_c->raycaster()->raycaster()->unproject_on_mesh( mouse_pos, - trafo.get_matrix(), - camera, + m_volumes.volumes.front()->world_matrix(), + wxGetApp().plater()->get_camera(), hit, normal, - clp_dist != 0. ? clp : nullptr)) - { -// if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) { -// // in this case the raycaster sees the hollowed and drilled mesh. -// // if the point lies on the surface created by the hole, we want -// // to ignore it. -// for (const sla::DrainHole& hole : m_holes_in_drilled_mesh) { -// sla::DrainHole outer(hole); -// outer.radius *= 1.001f; -// outer.height *= 1.001f; -// if (outer.is_inside(hit)) -// return false; -// } -// } - + m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) { // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); return true; } - else - return false; + return false; } // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. @@ -348,9 +339,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos // left down with shift - show the selection rectangle: if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) { if (m_hover_id == -1) { - if (shift_down || alt_down) { + if (shift_down || alt_down) m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); - } } else { if (m_selected[m_hover_id]) @@ -383,8 +373,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos m_parent.set_as_dirty(); m_wait_for_up_event = true; #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + unregister_hole_raycasters_for_picking(); + register_hole_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING } else @@ -510,8 +500,8 @@ void GLGizmoHollow::delete_selected_points() } #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + unregister_hole_raycasters_for_picking(); + register_hole_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING select_point(NoPoints); } @@ -580,39 +570,61 @@ bool GLGizmoHollow::on_mouse(const wxMouseEvent &mouse_event) return false; } -void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) +void GLGizmoHollow::process_mesh(SLAPrintObjectStep step, bool postpone_error_messages) { - wxGetApp().CallAfter([this, postpone_error_messages]() { - wxGetApp().plater()->reslice_SLA_hollowing( - *m_c->selection_info()->model_object(), postpone_error_messages); - }); + wxGetApp().CallAfter([this, step, postpone_error_messages]() { + wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); + }); } #if ENABLE_RAYCAST_PICKING -void GLGizmoHollow::set_sla_auxiliary_volumes_picking_state(bool state) +void GLGizmoHollow::register_hole_raycasters_for_picking() { - std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - if (raycasters != nullptr) { - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList ids = selection.get_volume_idxs(); - for (unsigned int id : ids) { - const GLVolume* v = selection.get_volume(id); - if (v->is_sla_pad() || v->is_sla_support()) { - auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); - if (it != raycasters->end()) - (*it)->set_active(state); - } + assert(m_hole_raycasters.empty()); + + init_cylinder_model(); + + const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); + if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) { + const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; + for (int i = 0; i < (int)drain_holes.size(); ++i) { + m_hole_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster, Transform3d::Identity())); } + update_hole_raycasters_for_picking_transform(); } } -void GLGizmoHollow::update_raycasters_for_picking_transform() +void GLGizmoHollow::unregister_hole_raycasters_for_picking() +{ + for (size_t i = 0; i < m_hole_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, i); + } + m_hole_raycasters.clear(); +} + +void GLGizmoHollow::register_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { + const GLVolume* v = m_volumes.volumes[i]; + m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i, *v->mesh_raycaster, v->world_matrix())); + } +} + +void GLGizmoHollow::unregister_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volume_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i); + } + m_volume_raycasters.clear(); +} + +void GLGizmoHollow::update_hole_raycasters_for_picking_transform() { const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); if (info != nullptr) { const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; if (!drain_holes.empty()) { - assert(!m_raycasters.empty()); + assert(!m_hole_raycasters.empty()); const GLVolume* vol = m_parent.get_selection().get_first_volume(); const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_scaling_factor_matrix().inverse(); @@ -625,13 +637,62 @@ void GLGizmoHollow::update_raycasters_for_picking_transform() const Eigen::AngleAxisd aa(q); const Transform3d matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) * Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); - m_raycasters[i]->set_transform(matrix); + m_hole_raycasters[i]->set_transform(matrix); } } } } #endif // ENABLE_RAYCAST_PICKING +void GLGizmoHollow::update_volumes() +{ + m_volumes.clear(); + unregister_volume_raycasters_for_picking(); + + const ModelObject* mo = m_c->selection_info()->model_object(); + if (mo == nullptr) + return; + + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po == nullptr) + return; + + TriangleMesh backend_mesh = po->get_mesh_to_print(); + if (!backend_mesh.empty()) { + // The backend has generated a valid mesh. Use it + backend_mesh.transform(po->trafo().inverse()); + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + new_volume->model.init_from(backend_mesh); + new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); + new_volume->set_sla_shift_z(po->get_current_elevation()); + new_volume->selected = true; // to set the proper color + new_volume->mesh_raycaster = std::make_unique(backend_mesh); + } + + if (m_volumes.volumes.empty()) { + // No valid mesh found in the backend. Use the selection to duplicate the volumes + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int idx : idxs) { + const GLVolume* v = selection.get_volume(idx); + if (!v->is_modifier) { + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh(); + new_volume->model.init_from(mesh); + new_volume->set_instance_transformation(v->get_instance_transformation()); + new_volume->set_volume_transformation(v->get_volume_transformation()); + new_volume->set_sla_shift_z(v->get_sla_shift_z()); + new_volume->selected = true; // to set the proper color + new_volume->mesh_raycaster = std::make_unique(mesh); + } + } + } + + register_volume_raycasters_for_picking(); +} + std::vector> GLGizmoHollow::get_config_options(const std::vector& keys) const { @@ -724,8 +785,8 @@ RENDER_AGAIN: window_width = std::max(window_width, button_preview_width); if (m_imgui->button(m_desc["preview"])) - hollow_mesh(); - + process_mesh(slaposDrillHoles); + bool config_changed = false; ImGui::Separator(); @@ -896,13 +957,6 @@ RENDER_AGAIN: if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position_by_ratio(clp_dist, true); - // make sure supports are shown/hidden as appropriate - bool show_sups = m_c->instances_hider()->are_supports_shown(); - if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) { - m_c->instances_hider()->show_supports(show_sups); - force_refresh = true; - } - m_imgui->end(); @@ -935,7 +989,7 @@ bool GLGizmoHollow::on_is_activable() const const Selection& selection = m_parent.get_selection(); if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA - || !selection.is_from_single_instance()) + || !selection.is_single_full_instance()) return false; // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. @@ -964,9 +1018,7 @@ CommonGizmosDataID GLGizmoHollow::on_get_requirements() const int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::HollowedMesh) - | int(CommonGizmosDataID::ObjectClipper) - | int(CommonGizmosDataID::SupportsClipper)); + | int(CommonGizmosDataID::ObjectClipper)); } @@ -975,8 +1027,12 @@ void GLGizmoHollow::on_set_state() if (m_state == m_old_state) return; - if (m_state == Off && m_old_state != Off) // the gizmo was just turned Off + if (m_state == Off && m_old_state != Off) { + // the gizmo was just turned Off m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); + m_c->instances_hider()->set_hide_full_scene(false); + } + m_old_state = m_state; } @@ -1091,6 +1147,9 @@ void GLGizmoHollow::reload_cache() void GLGizmoHollow::on_set_hover_id() { + if (m_c->selection_info()->model_object() == nullptr) + return; + if (int(m_c->selection_info()->model_object()->sla_drain_holes.size()) <= m_hover_id) m_hover_id = -1; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 0fdc3b2db7..e7a324a1e1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -3,6 +3,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" +#include "slic3r/GUI/3DScene.hpp" #include #include @@ -25,7 +26,6 @@ class GLGizmoHollow : public GLGizmoBase private: bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); - public: GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); void data_changed() override; @@ -59,21 +59,29 @@ private: #else void render_points(const Selection& selection, bool picking = false); #endif // ENABLE_RAYCAST_PICKING - void hollow_mesh(bool postpone_error_messages = false); + void render_volumes(); + void process_mesh(SLAPrintObjectStep step, bool postpone_error_messages = false); #if ENABLE_RAYCAST_PICKING - void set_sla_auxiliary_volumes_picking_state(bool state); - void update_raycasters_for_picking_transform(); + void register_hole_raycasters_for_picking(); + void unregister_hole_raycasters_for_picking(); + void register_volume_raycasters_for_picking(); + void unregister_volume_raycasters_for_picking(); + void update_hole_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING + void update_volumes(); ObjectID m_old_mo_id = -1; #if ENABLE_RAYCAST_PICKING PickingModel m_cylinder; - std::vector> m_raycasters; + std::vector> m_hole_raycasters; + std::vector> m_volume_raycasters; #else GLModel m_cylinder; #endif // ENABLE_RAYCAST_PICKING + GLVolumeCollection m_volumes; + float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 6.f; mutable std::vector m_selected; // which holes are currently selected diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index d0dd3baa08..cc45a8e73a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -60,6 +60,16 @@ bool GLGizmoSlaSupports::on_init() return true; } +static int last_completed_step(const SLAPrint& sla) +{ + int step = -1; + for (int i = 0; i < (int)SLAPrintObjectStep::slaposCount; ++i) { + if (sla.is_step_done((SLAPrintObjectStep)i)) + ++step; + } + return step; +} + void GLGizmoSlaSupports::data_changed() { if (! m_c->selection_info()) @@ -71,19 +81,25 @@ void GLGizmoSlaSupports::data_changed() disable_editing_mode(); reload_cache(); m_old_mo_id = mo->id(); - m_c->instances_hider()->show_supports(true); } // If we triggered autogeneration before, check backend and fetch results if they are there if (mo) { + m_c->instances_hider()->set_hide_full_scene(true); + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po != nullptr && last_completed_step(*po->print()) < (int)slaposDrillHoles) + process_mesh(slaposDrillHoles, false); + + update_volumes(); + if (mo->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); #if ENABLE_RAYCAST_PICKING - if (m_raycasters.empty()) - on_register_raycasters_for_picking(); + if (m_point_raycasters.empty()) + register_point_raycasters_for_picking(); else - update_raycasters_for_picking_transform(); + update_point_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING } @@ -111,8 +127,6 @@ void GLGizmoSlaSupports::on_render() if (!m_sphere.is_initialized()) m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0)); #endif // ENABLE_RAYCAST_PICKING - if (!m_cylinder.is_initialized()) - m_cylinder.init_from(its_make_cylinder(1.0, 1.0, double(PI) / 12.0)); ModelObject* mo = m_c->selection_info()->model_object(); const Selection& selection = m_parent.get_selection(); @@ -128,16 +142,15 @@ void GLGizmoSlaSupports::on_render() glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - if (selection.is_from_single_instance()) #if ENABLE_RAYCAST_PICKING - render_points(selection); + render_volumes(); + render_points(selection); #else - render_points(selection, false); + render_points(selection, false); #endif // ENABLE_RAYCAST_PICKING m_selection_rectangle.render(m_parent); m_c->object_clipper()->render_cut(); - m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } @@ -145,23 +158,14 @@ void GLGizmoSlaSupports::on_render() #if ENABLE_RAYCAST_PICKING void GLGizmoSlaSupports::on_register_raycasters_for_picking() { - assert(m_raycasters.empty()); - set_sla_auxiliary_volumes_picking_state(false); - - if (m_editing_mode && !m_editing_cache.empty()) { - for (size_t i = 0; i < m_editing_cache.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster), - m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster)); - } - update_raycasters_for_picking_transform(); - } + register_point_raycasters_for_picking(); + register_volume_raycasters_for_picking(); } void GLGizmoSlaSupports::on_unregister_raycasters_for_picking() { - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); - m_raycasters.clear(); - set_sla_auxiliary_volumes_picking_state(true); + unregister_point_raycasters_for_picking(); + unregister_volume_raycasters_for_picking(); } #else void GLGizmoSlaSupports::on_render_for_picking() @@ -181,10 +185,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); const bool has_points = (cache_size != 0); - const bool has_holes = (/*! m_c->hollowed_mesh()->get_hollowed_mesh() - &&*/ ! m_c->selection_info()->model_object()->sla_drain_holes.empty()); - - if (! has_points && ! has_holes) + if (!has_points) return; #if ENABLE_LEGACY_OPENGL_REMOVAL @@ -234,9 +235,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) #if ENABLE_RAYCAST_PICKING const bool clipped = is_mesh_point_clipped(support_point.pos.cast()); - if (!m_raycasters.empty()) { - m_raycasters[i].first->set_active(!clipped); - m_raycasters[i].second->set_active(!clipped); + if (i < m_point_raycasters.size()) { + m_point_raycasters[i].first->set_active(!clipped); + m_point_raycasters[i].second->set_active(!clipped); } if (clipped) continue; @@ -306,8 +307,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) if (m_editing_mode) { // in case the normal is not yet cached, find and cache it if (m_editing_cache[i].normal == Vec3f::Zero()) - m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); - //m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); @@ -373,67 +373,28 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } - // Now render the drain holes: -#if ENABLE_RAYCAST_PICKING - if (has_holes) { -#else - if (has_holes && ! picking) { -#endif // ENABLE_RAYCAST_PICKING - render_color = { 0.7f, 0.7f, 0.7f, 0.7f }; -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_cylinder.set_color(render_color); -#else - m_cylinder.set_color(-1, render_color); - if (shader != nullptr) -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - shader->set_uniform("emission_factor", 0.5f); - for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { - if (is_mesh_point_clipped(drain_hole.pos.cast())) - continue; - -#if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d hole_matrix = Geometry::translation_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; -#else - // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.pos.x(), drain_hole.pos.y(), drain_hole.pos.z())); - glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - if (vol->is_left_handed()) - glsafe(::glFrontFace(GL_CW)); - - // Matrices set, we can render the point mark now. - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); - const Eigen::AngleAxisd aa(q); -#if ENABLE_LEGACY_OPENGL_REMOVAL - const Transform3d model_matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) * - Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); - shader->set_uniform("view_model_matrix", view_matrix * model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); -#else - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis().x(), aa.axis().y(), aa.axis().z())); - glsafe(::glTranslated(0., 0., -drain_hole.height)); - glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - m_cylinder.render(); - - if (vol->is_left_handed()) - glsafe(::glFrontFace(GL_CCW)); -#if !ENABLE_LEGACY_OPENGL_REMOVAL - glsafe(::glPopMatrix()); -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - } - } - #if !ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glPopMatrix()); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } +void GLGizmoSlaSupports::render_volumes() +{ + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip"); + if (shader == nullptr) + return; + shader->start_using(); + shader->set_uniform("emission_factor", 0.0f); + const Camera& camera = wxGetApp().plater()->get_camera(); + + ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane(); + clipping_plane.set_normal(-clipping_plane.get_normal()); + m_volumes.set_clipping_plane(clipping_plane.get_data()); + + m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); + shader->stop_using(); +} bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const { @@ -456,48 +417,22 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const // Return false if no intersection was found, true otherwise. bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { - if (m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->empty()) + if (!m_c->raycaster()->raycaster()) return false; - const Camera& camera = wxGetApp().plater()->get_camera(); - const Selection& selection = m_parent.get_selection(); - const GLVolume* volume = selection.get_first_volume(); - Geometry::Transformation trafo = volume->get_instance_transformation() * volume->get_volume_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); - - double clp_dist = m_c->object_clipper()->get_position(); - const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); - // The raycaster query Vec3f hit; Vec3f normal; - if (m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->unproject_on_mesh( + if (m_c->raycaster()->raycaster()->unproject_on_mesh( mouse_pos, - trafo.get_matrix(), - camera, + m_volumes.volumes.front()->world_matrix(), + wxGetApp().plater()->get_camera(), hit, normal, - clp_dist != 0. ? clp : nullptr)) - { - // Check whether the hit is in a hole -// bool in_hole = false; - // In case the hollowed and drilled mesh is available, we can allow - // placing points in holes, because they should never end up - // on surface that's been drilled away. -// if (! m_c->hollowed_mesh()->get_hollowed_mesh()) { -// sla::DrainHoles drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; -// for (const sla::DrainHole& hole : drain_holes) { -// if (hole.is_inside(hit)) { -// in_hole = true; -// break; -// } -// } -// } -// if (! in_hole) { - // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); - return true; -// } + m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) { + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); + return true; } return false; @@ -548,8 +483,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous m_parent.set_as_dirty(); m_wait_for_up_event = true; #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + unregister_point_raycasters_for_picking(); + register_point_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING } else @@ -592,9 +527,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous for (size_t idx : points_idxs) points_inside.emplace_back((trafo.get_matrix().cast() * (m_editing_cache[idx].support_point.pos + m_editing_cache[idx].normal)).cast()); - assert(!m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume).emtpy()); - for (size_t idx : m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_unobscured_idxs( - trafo, wxGetApp().plater()->get_camera(), points_inside, + for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs( + trafo, wxGetApp().plater()->get_camera(), points_inside, m_c->object_clipper()->get_clipping_plane())) { if (idx >= orig_pts_num) // this is a cone-base, get index of point it belongs to @@ -712,8 +646,8 @@ void GLGizmoSlaSupports::delete_selected_points(bool force) } #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + unregister_point_raycasters_for_picking(); + register_point_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING select_point(NoPoints); @@ -1027,7 +961,7 @@ bool GLGizmoSlaSupports::on_is_activable() const const Selection& selection = m_parent.get_selection(); if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA - || !selection.is_from_single_instance()) + || !selection.is_single_full_instance()) return false; // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. @@ -1055,9 +989,7 @@ CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::HollowedMesh) - | int(CommonGizmosDataID::ObjectClipper) - | int(CommonGizmosDataID::SupportsClipper)); + | int(CommonGizmosDataID::ObjectClipper)); } @@ -1101,6 +1033,9 @@ void GLGizmoSlaSupports::on_set_state() disable_editing_mode(); // so it is not active next time the gizmo opens m_old_mo_id = -1; } + + if (m_state == Off) + m_c->instances_hider()->set_hide_full_scene(false); } m_old_state = m_state; } @@ -1395,10 +1330,8 @@ void GLGizmoSlaSupports::switch_to_editing_mode() m_editing_cache.emplace_back(sp); select_point(NoPoints); #if ENABLE_RAYCAST_PICKING - on_register_raycasters_for_picking(); + register_point_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING - - m_c->instances_hider()->show_supports(false); m_parent.set_as_dirty(); } @@ -1408,10 +1341,9 @@ void GLGizmoSlaSupports::disable_editing_mode() if (m_editing_mode) { m_editing_mode = false; wxGetApp().plater()->leave_gizmos_stack(); - m_c->instances_hider()->show_supports(true); m_parent.set_as_dirty(); #if ENABLE_RAYCAST_PICKING - on_unregister_raycasters_for_picking(); + unregister_point_raycasters_for_picking(); #endif // ENABLE_RAYCAST_PICKING } wxGetApp().plater()->get_notification_manager()->close_notification_of_type(NotificationType::QuitSLAManualMode); @@ -1432,53 +1364,130 @@ bool GLGizmoSlaSupports::unsaved_changes() const } #if ENABLE_RAYCAST_PICKING -void GLGizmoSlaSupports::set_sla_auxiliary_volumes_picking_state(bool state) +void GLGizmoSlaSupports::register_point_raycasters_for_picking() { - std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - if (raycasters != nullptr) { - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList ids = selection.get_volume_idxs(); - for (unsigned int id : ids) { - const GLVolume* v = selection.get_volume(id); - if (v->is_sla_pad() || v->is_sla_support()) { - auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); - if (it != raycasters->end()) - (*it)->set_active(state); - } + assert(m_point_raycasters.empty()); + + if (m_editing_mode && !m_editing_cache.empty()) { + for (size_t i = 0; i < m_editing_cache.size(); ++i) { + m_point_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster, Transform3d::Identity()), + m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster, Transform3d::Identity())); } + update_point_raycasters_for_picking_transform(); } } -void GLGizmoSlaSupports::update_raycasters_for_picking_transform() +void GLGizmoSlaSupports::unregister_point_raycasters_for_picking() { - if (!m_editing_cache.empty()) { - assert(!m_raycasters.empty()); + for (size_t i = 0; i < m_point_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, i); + } + m_point_raycasters.clear(); +} - const GLVolume* vol = m_parent.get_selection().get_first_volume(); - const Geometry::Transformation transformation(vol->world_matrix()); - const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); - for (size_t i = 0; i < m_editing_cache.size(); ++i) { - const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast()) * instance_scaling_matrix_inverse; +void GLGizmoSlaSupports::register_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { + const GLVolume* v = m_volumes.volumes[i]; + m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i, *v->mesh_raycaster, v->world_matrix())); + } +} - if (m_editing_cache[i].normal == Vec3f::Zero()) - m_parent.raycaster().get_raycasters(SceneRaycaster::EType::Volume)->front()->get_raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); +void GLGizmoSlaSupports::unregister_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volume_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i); + } + m_volume_raycasters.clear(); +} - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); - const Eigen::AngleAxisd aa(q); - const Transform3d cone_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * - Geometry::translation_transform((CONE_HEIGHT + m_editing_cache[i].support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ()) * - Geometry::rotation_transform({ double(PI), 0.0, 0.0 }) * Geometry::scale_transform({ CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT }); - m_raycasters[i].second->set_transform(cone_matrix); +void GLGizmoSlaSupports::update_point_raycasters_for_picking_transform() +{ + if (m_editing_cache.empty()) + return; - const double radius = (double)m_editing_cache[i].support_point.head_front_radius * RenderPointScale; - const Transform3d sphere_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius); - m_raycasters[i].first->set_transform(sphere_matrix); - } + assert(!m_point_raycasters.empty()); + + const GLVolume* vol = m_parent.get_selection().get_first_volume(); + const Geometry::Transformation transformation(vol->world_matrix()); + const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); + for (size_t i = 0; i < m_editing_cache.size(); ++i) { + const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast()) * instance_scaling_matrix_inverse; + + if (m_editing_cache[i].normal == Vec3f::Zero()) + m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); + const Eigen::AngleAxisd aa(q); + const Transform3d cone_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * + Geometry::assemble_transform((CONE_HEIGHT + m_editing_cache[i].support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ(), + Vec3d(PI, 0.0, 0.0), Vec3d(CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT)); + m_point_raycasters[i].second->set_transform(cone_matrix); + + const double radius = (double)m_editing_cache[i].support_point.head_front_radius * RenderPointScale; + const Transform3d sphere_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius); + m_point_raycasters[i].first->set_transform(sphere_matrix); } } #endif // ENABLE_RAYCAST_PICKING +void GLGizmoSlaSupports::update_volumes() +{ + m_volumes.clear(); + unregister_volume_raycasters_for_picking(); + + const ModelObject* mo = m_c->selection_info()->model_object(); + if (mo == nullptr) + return; + + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po == nullptr) + return; + + TriangleMesh backend_mesh = po->get_mesh_to_print(); + if (!backend_mesh.empty()) { + // The backend has generated a valid mesh. Use it + backend_mesh.transform(po->trafo().inverse()); + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + new_volume->model.init_from(backend_mesh); + new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); + new_volume->set_sla_shift_z(po->get_current_elevation()); + new_volume->selected = true; // to set the proper color + new_volume->mesh_raycaster = std::make_unique(backend_mesh); + } + + if (m_volumes.volumes.empty()) { + // No valid mesh found in the backend. Use the selection to duplicate the volumes + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int idx : idxs) { + const GLVolume* v = selection.get_volume(idx); + if (!v->is_modifier) { + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh(); + new_volume->model.init_from(mesh); + new_volume->set_instance_transformation(v->get_instance_transformation()); + new_volume->set_volume_transformation(v->get_volume_transformation()); + new_volume->set_sla_shift_z(v->get_sla_shift_z()); + new_volume->selected = true; // to set the proper color + new_volume->mesh_raycaster = std::make_unique(mesh); + } + } + } + + register_volume_raycasters_for_picking(); +} + +void GLGizmoSlaSupports::process_mesh(SLAPrintObjectStep step, bool postpone_error_messages) +{ + wxGetApp().CallAfter([this, step, postpone_error_messages]() { + wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); + }); +} + SlaGizmoHelpDialog::SlaGizmoHelpDialog() : wxDialog(nullptr, wxID_ANY, _L("SLA gizmo keyboard shortcuts"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index c4232ed48f..6729d6edac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -3,6 +3,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" +#include "slic3r/GUI/3DScene.hpp" #include "libslic3r/SLA/SupportPoint.hpp" #include "libslic3r/ObjectID.hpp" @@ -22,7 +23,6 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoSlaSupports : public GLGizmoBase { private: - bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); static constexpr float RenderPointScale = 1.f; @@ -93,11 +93,17 @@ private: #else void render_points(const Selection& selection, bool picking = false); #endif // ENABLE_RAYCAST_PICKING + void render_volumes(); bool unsaved_changes() const; #if ENABLE_RAYCAST_PICKING - void set_sla_auxiliary_volumes_picking_state(bool state); - void update_raycasters_for_picking_transform(); + void register_point_raycasters_for_picking(); + void unregister_point_raycasters_for_picking(); + void register_volume_raycasters_for_picking(); + void unregister_volume_raycasters_for_picking(); + void update_point_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING + void update_volumes(); + void process_mesh(SLAPrintObjectStep step, bool postpone_error_messages = false); bool m_lock_unique_islands = false; bool m_editing_mode = false; // Is editing mode active? @@ -113,12 +119,14 @@ private: #if ENABLE_RAYCAST_PICKING PickingModel m_sphere; PickingModel m_cone; - std::vector, std::shared_ptr>> m_raycasters; + std::vector, std::shared_ptr>> m_point_raycasters; + std::vector> m_volume_raycasters; #else GLModel m_cone; GLModel m_sphere; #endif // ENABLE_RAYCAST_PICKING - GLModel m_cylinder; + + GLVolumeCollection m_volumes; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 31fbb0f7ab..d8a858bacd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -59,13 +59,6 @@ InstancesHider* CommonGizmosDataPool::instances_hider() const return inst_hider->is_valid() ? inst_hider : nullptr; } -//HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const -//{ -// HollowedMesh* hol_mesh = dynamic_cast(m_data.at(CommonGizmosDataID::HollowedMesh).get()); -// assert(hol_mesh); -// return hol_mesh->is_valid() ? hol_mesh : nullptr; -//} - Raycaster* CommonGizmosDataPool::raycaster() const { Raycaster* rc = dynamic_cast(m_data.at(CommonGizmosDataID::Raycaster).get()); @@ -123,9 +116,8 @@ void SelectionInfo::on_update() if (selection.is_single_full_instance()) { m_model_object = selection.get_model()->objects[selection.get_object_idx()]; - if (m_model_object) - m_print_object = get_pool()->get_canvas()->sla_print()->get_object(m_model_object->id()); + m_print_object = get_pool()->get_canvas()->sla_print()->get_print_object_by_model_object_id(m_model_object->id()); m_z_shift = selection.get_first_volume()->get_sla_shift_z(); } @@ -154,8 +146,10 @@ void InstancesHider::on_update() if (mo && active_inst != -1) { canvas->toggle_model_objects_visibility(false); - canvas->toggle_model_objects_visibility(true, mo, active_inst); - canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst); + if (!m_hide_full_scene) { + canvas->toggle_model_objects_visibility(true, mo, active_inst); + canvas->toggle_sla_auxiliaries_visibility(false, mo, active_inst); + } canvas->set_use_clipping_planes(true); // Some objects may be sinking, do not show whatever is below the bed. canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); @@ -188,9 +182,10 @@ void InstancesHider::on_release() m_clippers.clear(); } -void InstancesHider::show_supports(bool show) { - if (m_show_supports != show) { - m_show_supports = show; +void InstancesHider::set_hide_full_scene(bool hide) +{ + if (m_hide_full_scene != hide) { + m_hide_full_scene = hide; on_update(); } } @@ -255,81 +250,6 @@ void InstancesHider::render_cut() const -//void HollowedMesh::on_update() -//{ -// const ModelObject* mo = get_pool()->selection_info()->model_object(); -// bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA; -// if (! mo || ! is_sla) -// return; - -// const GLCanvas3D* canvas = get_pool()->get_canvas(); -// const PrintObjects& print_objects = canvas->sla_print()->objects(); -// const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size())) -// ? print_objects[m_print_object_idx] -// : nullptr; - -// // Find the respective SLAPrintObject. -// if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { -// m_print_objects_count = print_objects.size(); -// m_print_object_idx = -1; -// for (const SLAPrintObject* po : print_objects) { -// ++m_print_object_idx; -// if (po->model_object()->id() == mo->id()) { -// print_object = po; -// break; -// } -// } -// } - -// // If there is a valid SLAPrintObject, check state of Hollowing step. -// if (print_object) { -// if (print_object->is_step_done(slaposDrillHoles) && !print_object->get_mesh_to_print().empty()) { -// size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; -// if (timestamp > m_old_hollowing_timestamp) { -// const TriangleMesh& backend_mesh = print_object->get_mesh_to_print(); -// if (! backend_mesh.empty()) { -// m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); -// Transform3d trafo_inv = (canvas->sla_print()->sla_trafo(*mo) * print_object->model_object()->volumes.front()->get_transformation().get_matrix()).inverse(); -// m_hollowed_mesh_transformed->transform(trafo_inv); -// m_drainholes = print_object->model_object()->sla_drain_holes; -// m_old_hollowing_timestamp = timestamp; - -//// indexed_triangle_set interior = print_object->hollowed_interior_mesh(); -//// its_flip_triangles(interior); -//// m_hollowed_interior_transformed = std::make_unique(std::move(interior)); -//// m_hollowed_interior_transformed->transform(trafo_inv); -// } -// else { -// m_hollowed_mesh_transformed.reset(nullptr); -// } -// } -// } -// else -// m_hollowed_mesh_transformed.reset(nullptr); -// } -//} - - -//void HollowedMesh::on_release() -//{ -// m_hollowed_mesh_transformed.reset(); -// m_old_hollowing_timestamp = 0; -// m_print_object_idx = -1; -//} - - -//const TriangleMesh* HollowedMesh::get_hollowed_mesh() const -//{ -// return m_hollowed_mesh_transformed.get(); -//} - -//const TriangleMesh* HollowedMesh::get_hollowed_interior() const -//{ -// return m_hollowed_interior_transformed.get(); -//} - - - void Raycaster::on_update() { @@ -347,21 +267,28 @@ void Raycaster::on_update() mvs = mo->volumes; std::vector meshes; - const std::vector& mvs = mo->volumes; -// if (mvs.size() == 1) { -// assert(mvs.front()->is_model_part()); -// const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); -// if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh()) -// meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); -// } - if (meshes.empty()) { - for (const ModelVolume* v : mvs) { - if (v->is_model_part()) - meshes.push_back(&v->mesh()); + bool force_raycaster_regeneration = false; + if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) { + // For sla printers we use the mesh generated by the backend + const SLAPrintObject* po = get_pool()->selection_info()->print_object(); + assert(po != nullptr); + m_sla_mesh_cache = po->get_mesh_to_print(); + if (!m_sla_mesh_cache.empty()) { + m_sla_mesh_cache.transform(po->trafo().inverse()); + meshes.emplace_back(&m_sla_mesh_cache); + force_raycaster_regeneration = true; } } - if (meshes != m_old_meshes) { + if (meshes.empty()) { + const std::vector& mvs = mo->volumes; + for (const ModelVolume* mv : mvs) { + if (mv->is_model_part()) + meshes.push_back(&mv->mesh()); + } + } + + if (force_raycaster_regeneration || meshes != m_old_meshes) { m_raycasters.clear(); for (const TriangleMesh* mesh : meshes) #if ENABLE_RAYCAST_PICKING @@ -399,25 +326,36 @@ void ObjectClipper::on_update() // which mesh should be cut? std::vector meshes; -// bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh(); -// if (has_hollowed) -// meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh()); + std::vector trafos; + bool force_clipper_regeneration = false; + if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) { + // For sla printers we use the mesh generated by the backend + const SLAPrintObject* po = get_pool()->selection_info()->print_object(); + assert(po != nullptr); + m_sla_mesh_cache = po->get_mesh_to_print(); + if (!m_sla_mesh_cache.empty()) { + m_sla_mesh_cache.transform(po->trafo().inverse()); + meshes.emplace_back(&m_sla_mesh_cache); + trafos.emplace_back(Geometry::Transformation()); + force_clipper_regeneration = true; + } + } - if (meshes.empty()) - for (const ModelVolume* mv : mo->volumes) - meshes.push_back(&mv->mesh()); + if (meshes.empty()) { + for (const ModelVolume* mv : mo->volumes) { + meshes.emplace_back(&mv->mesh()); + trafos.emplace_back(mv->get_transformation()); + } + } - if (meshes != m_old_meshes) { + if (force_clipper_regeneration || meshes != m_old_meshes) { m_clippers.clear(); - for (const TriangleMesh* mesh : meshes) { - m_clippers.emplace_back(new MeshClipper); - m_clippers.back()->set_mesh(*mesh); + for (size_t i = 0; i < meshes.size(); ++i) { + m_clippers.emplace_back(new MeshClipper, trafos[i]); + m_clippers.back().first->set_mesh(*meshes[i]); } m_old_meshes = meshes; -// if (has_hollowed) -// m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior()); - m_active_inst_bb_radius = mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); } @@ -437,46 +375,27 @@ void ObjectClipper::render_cut() const { if (m_clp_ratio == 0.) return; + const SelectionInfo* sel_info = get_pool()->selection_info(); - int sel_instance_idx = sel_info->get_active_instance(); - if (sel_instance_idx < 0) - return; - const ModelObject* mo = sel_info->model_object(); - const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation(); - - size_t clipper_id = 0; - for (const ModelVolume* mv : mo->volumes) { - const Geometry::Transformation vol_trafo = mv->get_transformation(); - Geometry::Transformation trafo = inst_trafo * vol_trafo; + const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); + for (auto& clipper : m_clippers) { + Geometry::Transformation trafo = inst_trafo * clipper.second; trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - - auto& clipper = m_clippers[clipper_id]; - clipper->set_plane(*m_clp); - clipper->set_transformation(trafo); - clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); -#if ENABLE_LEGACY_OPENGL_REMOVAL - clipper->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }); - clipper->render_contour({ 1.f, 1.f, 1.f, 1.f}); -#else - glsafe(::glPushMatrix()); - glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); - clipper->render_cut(); - glsafe(::glColor3f(1.f, 1.f, 1.f)); - clipper->render_contour(); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - ++clipper_id; + clipper.first->set_plane(*m_clp); + clipper.first->set_transformation(trafo); + clipper.first->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + clipper.first->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }); } } bool ObjectClipper::is_projection_inside_cut(const Vec3d& point) const { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const std::unique_ptr& cl) { return cl->is_projection_inside_cut(point); }); + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const auto& cl) { return cl.first->is_projection_inside_cut(point); }); } bool ObjectClipper::has_valid_contour() const { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const std::unique_ptr& cl) { return cl->has_valid_contour(); }); + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); }); } void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) @@ -514,13 +433,13 @@ void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contou { m_hide_clipped = hide_clipped; for (auto& clipper : m_clippers) - clipper->set_behaviour(fill_cut, contour_width); + clipper.first->set_behaviour(fill_cut, contour_width); } void ObjectClipper::pass_mouse_click(const Vec3d& pt) { for (auto& clipper : m_clippers) - clipper->pass_mouse_click(pt); + clipper.first->pass_mouse_click(pt); } std::vector ObjectClipper::get_disabled_contours() const @@ -586,7 +505,6 @@ void SupportsClipper::render_cut() const { const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper(); if (ocl->get_position() == 0. - || ! get_pool()->instances_hider()->are_supports_shown() || ! m_clipper) return; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index d6c5801230..aa9493d9d3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -63,7 +63,6 @@ enum class CommonGizmosDataID { None = 0, SelectionInfo = 1 << 0, InstancesHider = 1 << 1, - HollowedMesh = 1 << 2, Raycaster = 1 << 3, ObjectClipper = 1 << 4, SupportsClipper = 1 << 5, @@ -187,8 +186,7 @@ public: CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } #endif // NDEBUG - void show_supports(bool show); - bool are_supports_shown() const { return m_show_supports; } + void set_hide_full_scene(bool hide); void render_cut() const; protected: @@ -196,42 +194,13 @@ protected: void on_release() override; private: - bool m_show_supports = false; + bool m_hide_full_scene{ false }; std::vector m_old_meshes; std::vector> m_clippers; }; -//class HollowedMesh : public CommonGizmosDataBase -//{ -//public: -// explicit HollowedMesh(CommonGizmosDataPool* cgdp) -// : CommonGizmosDataBase(cgdp) {} -//#ifndef NDEBUG -// CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } -//#endif // NDEBUG - -// const sla::DrainHoles &get_drainholes() const { return m_drainholes; } - -// const TriangleMesh* get_hollowed_mesh() const; -// const TriangleMesh* get_hollowed_interior() const; - -//protected: -// void on_update() override; -// void on_release() override; - -//private: -// std::unique_ptr m_hollowed_mesh_transformed; -// std::unique_ptr m_hollowed_interior_transformed; -// size_t m_old_hollowing_timestamp = 0; -// int m_print_object_idx = -1; -// int m_print_objects_count = 0; -// sla::DrainHoles m_drainholes; -//}; - - - class Raycaster : public CommonGizmosDataBase { public: @@ -251,6 +220,8 @@ protected: private: std::vector> m_raycasters; std::vector m_old_meshes; + // Used to store the sla mesh coming from the backend + TriangleMesh m_sla_mesh_cache; }; @@ -285,7 +256,9 @@ protected: private: std::vector m_old_meshes; - std::vector> m_clippers; + // Used to store the sla mesh coming from the backend + TriangleMesh m_sla_mesh_cache; + std::vector, Geometry::Transformation>> m_clippers; std::unique_ptr m_clp; double m_clp_ratio = 0.; double m_active_inst_bb_radius = 0.; diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index f309d6eaaa..9e726b7f4b 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -100,7 +100,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came }; #if ENABLE_RAYCAST_PICKING_DEBUG - m_last_hit.reset(); + const_cast*>(&m_last_hit)->reset(); #endif // ENABLE_RAYCAST_PICKING_DEBUG HitResult ret; @@ -142,7 +142,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came ret.raycaster_id = decode_id(ret.type, ret.raycaster_id); #if ENABLE_RAYCAST_PICKING_DEBUG - m_last_hit = ret; + *const_cast*>(&m_last_hit) = ret; #endif // ENABLE_RAYCAST_PICKING_DEBUG return ret; } From c4db736f6fedb645c5f649e8ec7bc066a6c7a127 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Nov 2022 09:22:47 +0100 Subject: [PATCH 12/75] Gizmo Hollow and SLA support - Disable imgui dialog and scene input until the proper geometry is not loaded --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 52 ++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 30 ++++++++--- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 4 +- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index d098d41f5d..e8889b39da 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -14,10 +14,11 @@ #include "libslic3r/Model.hpp" - namespace Slic3r { namespace GUI { +static const ColorRGBA DISABLED_COLOR = ColorRGBA::DARK_GRAY(); + GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { @@ -508,6 +509,7 @@ void GLGizmoHollow::delete_selected_points() bool GLGizmoHollow::on_mouse(const wxMouseEvent &mouse_event) { + if (!m_input_enabled) return true; if (mouse_event.Moving()) return false; if (use_grabbers(mouse_event)) return true; @@ -644,6 +646,16 @@ void GLGizmoHollow::update_hole_raycasters_for_picking_transform() } #endif // ENABLE_RAYCAST_PICKING +static int last_completed_step(const SLAPrint& sla) +{ + int step = -1; + for (int i = 0; i < (int)SLAPrintObjectStep::slaposCount; ++i) { + if (sla.is_step_done((SLAPrintObjectStep)i)) + ++step; + } + return step; +} + void GLGizmoHollow::update_volumes() { m_volumes.clear(); @@ -657,6 +669,8 @@ void GLGizmoHollow::update_volumes() if (po == nullptr) return; + m_input_enabled = false; + TriangleMesh backend_mesh = po->get_mesh_to_print(); if (!backend_mesh.empty()) { // The backend has generated a valid mesh. Use it @@ -666,8 +680,12 @@ void GLGizmoHollow::update_volumes() new_volume->model.init_from(backend_mesh); new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); new_volume->set_sla_shift_z(po->get_current_elevation()); - new_volume->selected = true; // to set the proper color new_volume->mesh_raycaster = std::make_unique(backend_mesh); + m_input_enabled = last_completed_step(*m_c->selection_info()->print_object()->print()) >= slaposAssembly; + if (m_input_enabled) + new_volume->selected = true; // to set the proper color + else + new_volume->set_color(DISABLED_COLOR); } if (m_volumes.volumes.empty()) { @@ -684,7 +702,7 @@ void GLGizmoHollow::update_volumes() new_volume->set_instance_transformation(v->get_instance_transformation()); new_volume->set_volume_transformation(v->get_volume_transformation()); new_volume->set_sla_shift_z(v->get_sla_shift_z()); - new_volume->selected = true; // to set the proper color + new_volume->set_color(DISABLED_COLOR); new_volume->mesh_raycaster = std::make_unique(mesh); } } @@ -784,6 +802,8 @@ RENDER_AGAIN: float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left}); window_width = std::max(window_width, button_preview_width); + m_imgui->disabled_begin(!m_input_enabled); + if (m_imgui->button(m_desc["preview"])) process_mesh(slaposDrillHoles); @@ -801,7 +821,10 @@ RENDER_AGAIN: } } - m_imgui->disabled_begin(! m_enable_hollowing); + m_imgui->disabled_end(); + + m_imgui->disabled_begin(!m_input_enabled || !m_enable_hollowing); + ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("offset")); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); @@ -844,7 +867,7 @@ RENDER_AGAIN: mo->config.set("hollowing_min_thickness", m_offset_stash); mo->config.set("hollowing_quality", m_quality_stash); mo->config.set("hollowing_closing_distance", m_closing_d_stash); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Hollowing parameter change")); } mo->config.set("hollowing_min_thickness", offset); mo->config.set("hollowing_quality", quality); @@ -867,12 +890,15 @@ RENDER_AGAIN: if (m_new_hole_radius * 2.f > diameter_upper_cap) m_new_hole_radius = diameter_upper_cap / 2.f; ImGui::AlignTextToFramePadding(); + + m_imgui->disabled_begin(!m_input_enabled); + m_imgui->text(m_desc.at("hole_diameter")); ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x); ImGui::PushItemWidth(window_width - diameter_slider_left); - float diam = 2.f * m_new_hole_radius; m_imgui->slider_float("##hole_diameter", &diam, 1.f, 25.f, "%.1f mm", 1.f, false); + // Let's clamp the value (which could have been entered by keyboard) to a larger range // than the slider. This allows entering off-scale values and still protects against //complete non-sense. @@ -883,9 +909,13 @@ RENDER_AGAIN: bool deactivated = m_imgui->get_last_slider_status().deactivated_after_edit; ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc["hole_depth"]); ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x); m_imgui->slider_float("##hole_depth", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false); + + m_imgui->disabled_end(); + // Same as above: m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f); @@ -921,24 +951,24 @@ RENDER_AGAIN: break; } } - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Change drainage hole diameter")); m_new_hole_radius = backup_rad; m_new_hole_height = backup_hei; mo->sla_drain_holes = new_holes; } } - m_imgui->disabled_begin(m_selection_empty); + m_imgui->disabled_begin(!m_input_enabled || m_selection_empty); remove_selected = m_imgui->button(m_desc.at("remove_selected")); m_imgui->disabled_end(); - m_imgui->disabled_begin(mo->sla_drain_holes.empty()); + m_imgui->disabled_begin(!m_input_enabled || mo->sla_drain_holes.empty()); remove_all = m_imgui->button(m_desc.at("remove_all")); m_imgui->disabled_end(); // Following is rendered in both editing and non-editing mode: - // m_imgui->text(""); ImGui::Separator(); + m_imgui->disabled_begin(!m_input_enabled); if (m_c->object_clipper()->get_position() == 0.f) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("clipping_of_view")); @@ -957,6 +987,8 @@ RENDER_AGAIN: if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position_by_ratio(clp_dist, true); + m_imgui->disabled_end(); + m_imgui->end(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index e7a324a1e1..96f8c65701 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -81,6 +81,7 @@ private: #endif // ENABLE_RAYCAST_PICKING GLVolumeCollection m_volumes; + bool m_input_enabled{ false }; float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 6.f; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index cc45a8e73a..291a8323a1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -30,6 +30,8 @@ static const double CONE_HEIGHT = 0.75; namespace Slic3r { namespace GUI { +static const ColorRGBA DISABLED_COLOR = ColorRGBA::DARK_GRAY(); + GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) {} @@ -748,8 +750,7 @@ RENDER_AGAIN: float win_h = ImGui::GetWindowHeight(); y = std::min(y, bottom_limit - win_h); ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); - if ((last_h != win_h) || (last_y != y)) - { + if (last_h != win_h || last_y != y) { // ask canvas for another frame to render the window in the correct position m_imgui->set_requires_extra_frame(); if (last_h != win_h) @@ -780,6 +781,7 @@ RENDER_AGAIN: if (m_new_point_head_diameter > diameter_upper_cap) m_new_point_head_diameter = diameter_upper_cap; ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("head_diameter")); ImGui::SameLine(diameter_slider_left); ImGui::PushItemWidth(window_width - diameter_slider_left); @@ -840,6 +842,8 @@ RENDER_AGAIN: } } else { // not in editing mode: + m_imgui->disabled_begin(!m_input_enabled); + ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("minimal_distance")); ImGui::SameLine(settings_sliders_left); @@ -889,7 +893,9 @@ RENDER_AGAIN: if (m_imgui->button(m_desc.at("manual_editing"))) switch_to_editing_mode(); - m_imgui->disabled_begin(m_normal_cache.empty()); + m_imgui->disabled_end(); + + m_imgui->disabled_begin(!m_input_enabled || m_normal_cache.empty()); remove_all = m_imgui->button(m_desc.at("remove_all")); m_imgui->disabled_end(); @@ -902,6 +908,7 @@ RENDER_AGAIN: // Following is rendered in both editing and non-editing mode: + m_imgui->disabled_begin(!m_input_enabled); ImGui::Separator(); if (m_c->object_clipper()->get_position() == 0.f) { ImGui::AlignTextToFramePadding(); @@ -921,7 +928,6 @@ RENDER_AGAIN: if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position_by_ratio(clp_dist, true); - if (m_imgui->button("?")) { wxGetApp().CallAfter([]() { SlaGizmoHelpDialog help_dlg; @@ -929,6 +935,8 @@ RENDER_AGAIN: }); } + m_imgui->disabled_end(); + m_imgui->end(); if (remove_selected || remove_all) { @@ -1221,7 +1229,9 @@ void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) cons }); } -bool GLGizmoSlaSupports::on_mouse(const wxMouseEvent &mouse_event){ +bool GLGizmoSlaSupports::on_mouse(const wxMouseEvent &mouse_event) +{ + if (!m_input_enabled) return true; if (mouse_event.Moving()) return false; if (!mouse_event.ShiftDown() && !mouse_event.AltDown() && use_grabbers(mouse_event)) return true; @@ -1445,6 +1455,8 @@ void GLGizmoSlaSupports::update_volumes() if (po == nullptr) return; + m_input_enabled = false; + TriangleMesh backend_mesh = po->get_mesh_to_print(); if (!backend_mesh.empty()) { // The backend has generated a valid mesh. Use it @@ -1454,8 +1466,12 @@ void GLGizmoSlaSupports::update_volumes() new_volume->model.init_from(backend_mesh); new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); new_volume->set_sla_shift_z(po->get_current_elevation()); - new_volume->selected = true; // to set the proper color new_volume->mesh_raycaster = std::make_unique(backend_mesh); + m_input_enabled = last_completed_step(*m_c->selection_info()->print_object()->print()) >= slaposDrillHoles; + if (m_input_enabled) + new_volume->selected = true; // to set the proper color + else + new_volume->set_color(DISABLED_COLOR); } if (m_volumes.volumes.empty()) { @@ -1472,7 +1488,7 @@ void GLGizmoSlaSupports::update_volumes() new_volume->set_instance_transformation(v->get_instance_transformation()); new_volume->set_volume_transformation(v->get_volume_transformation()); new_volume->set_sla_shift_z(v->get_sla_shift_z()); - new_volume->selected = true; // to set the proper color + new_volume->set_color(DISABLED_COLOR); new_volume->mesh_raycaster = std::make_unique(mesh); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 6729d6edac..bc5eede66c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -127,6 +127,7 @@ private: #endif // ENABLE_RAYCAST_PICKING GLVolumeCollection m_volumes; + bool m_input_enabled{ false }; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. @@ -161,8 +162,7 @@ private: protected: void on_set_state() override; - void on_set_hover_id() override - { + void on_set_hover_id() override { if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id) m_hover_id = -1; } From 28ffdcc391ce1902eea0a553a7bfeb266ef8c410 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Nov 2022 12:46:46 +0100 Subject: [PATCH 13/75] Disable CGAL booleans for now in sla pipeline Does not work yet as expected. --- src/libslic3r/SLAPrintSteps.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index a5873155c4..4cf420a29e 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -158,16 +158,22 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) { - MeshBoolean::cgal::CGALMeshPtr cgalptr; + // TODO: enable when this works reliably. Currently, perform_csgmesh_booleans + // can generate incorrect result despite not throwing any exception. +// MeshBoolean::cgal::CGALMeshPtr cgalptr; - try { - cgalptr = csg::perform_csgmesh_booleans(range(po.m_mesh_to_slice)); - } catch(...) {} +// try { +// cgalptr = csg::perform_csgmesh_booleans(range(po.m_mesh_to_slice)); +// } catch(...) { +// cgalptr = nullptr; +// } - if (cgalptr) { - po.m_preview_meshes[step] = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalptr); - } else - po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, step)}; +// if (cgalptr) { +// po.m_preview_meshes[step] = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalptr); +// } else +// po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, step)}; + + po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, step)}; for (size_t i = size_t(step) + 1; i < slaposCount; ++i) { From c28a00ae0413afcf9b42833f1759d870b9462006 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Nov 2022 13:34:31 +0100 Subject: [PATCH 14/75] Holes are were not drilled in the right orientation. This change seemingly fixes the issue. --- src/libslic3r/SLA/Hollowing.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 974e525638..a35ba511b1 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -118,9 +118,9 @@ indexed_triangle_set DrainHole::to_mesh() const { auto r = double(radius); auto h = double(height); - indexed_triangle_set hole = sla::cylinder(r, h, steps); + indexed_triangle_set hole = its_make_cylinder(r, h); //sla::cylinder(r, h, steps); Eigen::Quaternionf q; - q.setFromTwoVectors(Vec3f{0.f, 0.f, 1.f}, normal); + q.setFromTwoVectors(Vec3f::UnitZ(), normal); for(auto& p : hole.vertices) p = q * p + pos; return hole; @@ -692,24 +692,23 @@ DrainHoles transformed_drainhole_points(const ModelObject &mo, const Transform3d &trafo) { auto pts = mo.sla_drain_holes; - const Transform3d& vol_trafo = mo.volumes.front()->get_transformation().get_matrix(); - const Geometry::Transformation trans(trafo * vol_trafo); - const Transform3f& tr = trans.get_matrix().cast(); - const Vec3f sc = trans.get_scaling_factor().cast(); +// const Transform3d& vol_trafo = mo.volumes.front()->get_transformation().get_matrix(); + const Geometry::Transformation trans(trafo /** vol_trafo*/); + const Transform3d& tr = trans.get_matrix(); for (sla::DrainHole &hl : pts) { - hl.pos = tr * hl.pos; - hl.normal = tr * hl.normal - tr.translation(); + Vec3d pos = hl.pos.cast(); + Vec3d nrm = hl.normal.cast(); - // The normal scales as a covector (and we must also - // undo the damage already done). - hl.normal = Vec3f(hl.normal(0)/(sc(0)*sc(0)), - hl.normal(1)/(sc(1)*sc(1)), - hl.normal(2)/(sc(2)*sc(2))); + pos = tr * pos; + nrm = tr * nrm - tr.translation(); // Now shift the hole a bit above the object and make it deeper to // compensate for it. This is to avoid problems when the hole is placed // on (nearly) flat surface. - hl.pos -= hl.normal.normalized() * sla::HoleStickOutLength; + pos -= nrm.normalized() * sla::HoleStickOutLength; + + hl.pos = pos.cast(); + hl.normal = nrm.cast(); hl.height += sla::HoleStickOutLength; } From 602c48a116afd5901c089367c4c42d15af2faf8d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Nov 2022 12:44:54 +0100 Subject: [PATCH 15/75] Added new base class for SLA gizmos to remove duplicated code --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 170 ++---------------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 18 +- src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp | 171 ++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp | 59 ++++++ src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 179 ++----------------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 18 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 10 -- src/slic3r/GUI/Plater.hpp | 2 - 10 files changed, 259 insertions(+), 372 deletions(-) create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 4fa51c4868..ac103615aa 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -39,6 +39,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmosCommon.hpp GUI/Gizmos/GLGizmoBase.cpp GUI/Gizmos/GLGizmoBase.hpp + GUI/Gizmos/GLGizmoSlaBase.cpp + GUI/Gizmos/GLGizmoSlaBase.hpp GUI/Gizmos/GLGizmoEmboss.cpp GUI/Gizmos/GLGizmoEmboss.hpp GUI/Gizmos/GLGizmoMove.cpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index e8889b39da..633e61e56e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -1,6 +1,6 @@ +#include "libslic3r/libslic3r.h" #include "GLGizmoHollow.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include @@ -17,10 +17,8 @@ namespace Slic3r { namespace GUI { -static const ColorRGBA DISABLED_COLOR = ColorRGBA::DARK_GRAY(); - GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) + : GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposAssembly) { } @@ -58,7 +56,7 @@ void GLGizmoHollow::data_changed() const SLAPrintObject* po = m_c->selection_info()->print_object(); if (po != nullptr && po->get_mesh_to_print().empty()) - process_mesh(slaposAssembly); + reslice_until_step(slaposAssembly); update_volumes(); @@ -265,25 +263,6 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } -void GLGizmoHollow::render_volumes() -{ - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip"); - if (shader == nullptr) - return; - - shader->start_using(); - shader->set_uniform("emission_factor", 0.0f); - const Camera& camera = wxGetApp().plater()->get_camera(); - - ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane(); - clipping_plane.set_normal(-clipping_plane.get_normal()); - m_volumes.set_clipping_plane(clipping_plane.get_data()); - - m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); - shader->stop_using(); - -} - bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const { if (m_c->object_clipper()->get_position() == 0.) @@ -299,34 +278,6 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point); } - - -// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal -// Return false if no intersection was found, true otherwise. -bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) -{ - if (m_c->raycaster()->raycasters().size() != 1) - return false; - if (! m_c->raycaster()->raycaster()) - return false; - - // The raycaster query - Vec3f hit; - Vec3f normal; - if (m_c->raycaster()->raycaster()->unproject_on_mesh( - mouse_pos, - m_volumes.volumes.front()->world_matrix(), - wxGetApp().plater()->get_camera(), - hit, - normal, - m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) { - // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); - return true; - } - return false; -} - // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. // The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is // aware that the event was reacted to and stops trying to make different sense of it. If the gizmo @@ -509,7 +460,7 @@ void GLGizmoHollow::delete_selected_points() bool GLGizmoHollow::on_mouse(const wxMouseEvent &mouse_event) { - if (!m_input_enabled) return true; + if (!is_input_enabled()) return true; if (mouse_event.Moving()) return false; if (use_grabbers(mouse_event)) return true; @@ -572,13 +523,6 @@ bool GLGizmoHollow::on_mouse(const wxMouseEvent &mouse_event) return false; } -void GLGizmoHollow::process_mesh(SLAPrintObjectStep step, bool postpone_error_messages) -{ - wxGetApp().CallAfter([this, step, postpone_error_messages]() { - wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); - }); -} - #if ENABLE_RAYCAST_PICKING void GLGizmoHollow::register_hole_raycasters_for_picking() { @@ -604,22 +548,6 @@ void GLGizmoHollow::unregister_hole_raycasters_for_picking() m_hole_raycasters.clear(); } -void GLGizmoHollow::register_volume_raycasters_for_picking() -{ - for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { - const GLVolume* v = m_volumes.volumes[i]; - m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i, *v->mesh_raycaster, v->world_matrix())); - } -} - -void GLGizmoHollow::unregister_volume_raycasters_for_picking() -{ - for (size_t i = 0; i < m_volume_raycasters.size(); ++i) { - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i); - } - m_volume_raycasters.clear(); -} - void GLGizmoHollow::update_hole_raycasters_for_picking_transform() { const CommonGizmosDataObjects::SelectionInfo* info = m_c->selection_info(); @@ -646,71 +574,6 @@ void GLGizmoHollow::update_hole_raycasters_for_picking_transform() } #endif // ENABLE_RAYCAST_PICKING -static int last_completed_step(const SLAPrint& sla) -{ - int step = -1; - for (int i = 0; i < (int)SLAPrintObjectStep::slaposCount; ++i) { - if (sla.is_step_done((SLAPrintObjectStep)i)) - ++step; - } - return step; -} - -void GLGizmoHollow::update_volumes() -{ - m_volumes.clear(); - unregister_volume_raycasters_for_picking(); - - const ModelObject* mo = m_c->selection_info()->model_object(); - if (mo == nullptr) - return; - - const SLAPrintObject* po = m_c->selection_info()->print_object(); - if (po == nullptr) - return; - - m_input_enabled = false; - - TriangleMesh backend_mesh = po->get_mesh_to_print(); - if (!backend_mesh.empty()) { - // The backend has generated a valid mesh. Use it - backend_mesh.transform(po->trafo().inverse()); - m_volumes.volumes.emplace_back(new GLVolume()); - GLVolume* new_volume = m_volumes.volumes.back(); - new_volume->model.init_from(backend_mesh); - new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); - new_volume->set_sla_shift_z(po->get_current_elevation()); - new_volume->mesh_raycaster = std::make_unique(backend_mesh); - m_input_enabled = last_completed_step(*m_c->selection_info()->print_object()->print()) >= slaposAssembly; - if (m_input_enabled) - new_volume->selected = true; // to set the proper color - else - new_volume->set_color(DISABLED_COLOR); - } - - if (m_volumes.volumes.empty()) { - // No valid mesh found in the backend. Use the selection to duplicate the volumes - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList& idxs = selection.get_volume_idxs(); - for (unsigned int idx : idxs) { - const GLVolume* v = selection.get_volume(idx); - if (!v->is_modifier) { - m_volumes.volumes.emplace_back(new GLVolume()); - GLVolume* new_volume = m_volumes.volumes.back(); - const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh(); - new_volume->model.init_from(mesh); - new_volume->set_instance_transformation(v->get_instance_transformation()); - new_volume->set_volume_transformation(v->get_volume_transformation()); - new_volume->set_sla_shift_z(v->get_sla_shift_z()); - new_volume->set_color(DISABLED_COLOR); - new_volume->mesh_raycaster = std::make_unique(mesh); - } - } - } - - register_volume_raycasters_for_picking(); -} - std::vector> GLGizmoHollow::get_config_options(const std::vector& keys) const { @@ -802,10 +665,10 @@ RENDER_AGAIN: float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left}); window_width = std::max(window_width, button_preview_width); - m_imgui->disabled_begin(!m_input_enabled); + m_imgui->disabled_begin(!is_input_enabled()); if (m_imgui->button(m_desc["preview"])) - process_mesh(slaposDrillHoles); + reslice_until_step(slaposDrillHoles); bool config_changed = false; @@ -823,7 +686,7 @@ RENDER_AGAIN: m_imgui->disabled_end(); - m_imgui->disabled_begin(!m_input_enabled || !m_enable_hollowing); + m_imgui->disabled_begin(!is_input_enabled() || !m_enable_hollowing); ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("offset")); @@ -891,7 +754,7 @@ RENDER_AGAIN: m_new_hole_radius = diameter_upper_cap / 2.f; ImGui::AlignTextToFramePadding(); - m_imgui->disabled_begin(!m_input_enabled); + m_imgui->disabled_begin(!is_input_enabled()); m_imgui->text(m_desc.at("hole_diameter")); ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x); @@ -958,17 +821,17 @@ RENDER_AGAIN: } } - m_imgui->disabled_begin(!m_input_enabled || m_selection_empty); + m_imgui->disabled_begin(!is_input_enabled() || m_selection_empty); remove_selected = m_imgui->button(m_desc.at("remove_selected")); m_imgui->disabled_end(); - m_imgui->disabled_begin(!m_input_enabled || mo->sla_drain_holes.empty()); + m_imgui->disabled_begin(!is_input_enabled() || mo->sla_drain_holes.empty()); remove_all = m_imgui->button(m_desc.at("remove_all")); m_imgui->disabled_end(); // Following is rendered in both editing and non-editing mode: ImGui::Separator(); - m_imgui->disabled_begin(!m_input_enabled); + m_imgui->disabled_begin(!is_input_enabled()); if (m_c->object_clipper()->get_position() == 0.f) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("clipping_of_view")); @@ -1043,17 +906,6 @@ std::string GLGizmoHollow::on_get_name() const return _u8L("Hollow and drill"); } - -CommonGizmosDataID GLGizmoHollow::on_get_requirements() const -{ - return CommonGizmosDataID( - int(CommonGizmosDataID::SelectionInfo) - | int(CommonGizmosDataID::InstancesHider) - | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::ObjectClipper)); -} - - void GLGizmoHollow::on_set_state() { if (m_state == m_old_state) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 96f8c65701..c1664b458b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -1,9 +1,8 @@ #ifndef slic3r_GLGizmoHollow_hpp_ #define slic3r_GLGizmoHollow_hpp_ -#include "GLGizmoBase.hpp" +#include "GLGizmoSlaBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" -#include "slic3r/GUI/3DScene.hpp" #include #include @@ -21,11 +20,8 @@ namespace GUI { enum class SLAGizmoEventType : unsigned char; class Selection; -class GLGizmoHollow : public GLGizmoBase +class GLGizmoHollow : public GLGizmoSlaBase { -private: - bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); - public: GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); void data_changed() override; @@ -59,30 +55,21 @@ private: #else void render_points(const Selection& selection, bool picking = false); #endif // ENABLE_RAYCAST_PICKING - void render_volumes(); - void process_mesh(SLAPrintObjectStep step, bool postpone_error_messages = false); #if ENABLE_RAYCAST_PICKING void register_hole_raycasters_for_picking(); void unregister_hole_raycasters_for_picking(); - void register_volume_raycasters_for_picking(); - void unregister_volume_raycasters_for_picking(); void update_hole_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING - void update_volumes(); ObjectID m_old_mo_id = -1; #if ENABLE_RAYCAST_PICKING PickingModel m_cylinder; std::vector> m_hole_raycasters; - std::vector> m_volume_raycasters; #else GLModel m_cylinder; #endif // ENABLE_RAYCAST_PICKING - GLVolumeCollection m_volumes; - bool m_input_enabled{ false }; - float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 6.f; mutable std::vector m_selected; // which holes are currently selected @@ -129,7 +116,6 @@ protected: void on_stop_dragging() override; void on_dragging(const UpdateData &data) override; void on_render_input_window(float x, float y, float bottom_limit) override; - virtual CommonGizmosDataID on_get_requirements() const override; std::string on_get_name() const override; bool on_is_activable() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp new file mode 100644 index 0000000000..7db24bb115 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp @@ -0,0 +1,171 @@ +#include "libslic3r/libslic3r.h" +#include "GLGizmoSlaBase.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" + +namespace Slic3r { +namespace GUI { + +static const ColorRGBA DISABLED_COLOR = ColorRGBA::DARK_GRAY(); +#if ENABLE_RAYCAST_PICKING +static const int VOLUME_RAYCASTERS_BASE_ID = (int)SceneRaycaster::EIdBase::Gizmo; +#endif // ENABLE_RAYCAST_PICKING + +GLGizmoSlaBase::GLGizmoSlaBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, SLAPrintObjectStep min_step) +: GLGizmoBase(parent, icon_filename, sprite_id) +, m_min_sla_print_object_step((int)min_step) +{} + +void GLGizmoSlaBase::reslice_until_step(SLAPrintObjectStep step, bool postpone_error_messages) +{ + wxGetApp().CallAfter([this, step, postpone_error_messages]() { + wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); + }); +} + +CommonGizmosDataID GLGizmoSlaBase::on_get_requirements() const +{ + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::Raycaster) + | int(CommonGizmosDataID::ObjectClipper)); +} + +void GLGizmoSlaBase::update_volumes() +{ + m_volumes.clear(); + unregister_volume_raycasters_for_picking(); + + const ModelObject* mo = m_c->selection_info()->model_object(); + if (mo == nullptr) + return; + + const SLAPrintObject* po = m_c->selection_info()->print_object(); + if (po == nullptr) + return; + + m_input_enabled = false; + + TriangleMesh backend_mesh = po->get_mesh_to_print(); + if (!backend_mesh.empty()) { + // The backend has generated a valid mesh. Use it + backend_mesh.transform(po->trafo().inverse()); + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + new_volume->model.init_from(backend_mesh); + new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); + new_volume->set_sla_shift_z(po->get_current_elevation()); + new_volume->mesh_raycaster = std::make_unique(backend_mesh); + m_input_enabled = last_completed_step(*m_c->selection_info()->print_object()->print()) >= m_min_sla_print_object_step; + if (m_input_enabled) + new_volume->selected = true; // to set the proper color + else + new_volume->set_color(DISABLED_COLOR); + } + + if (m_volumes.volumes.empty()) { + // No valid mesh found in the backend. Use the selection to duplicate the volumes + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int idx : idxs) { + const GLVolume* v = selection.get_volume(idx); + if (!v->is_modifier) { + m_volumes.volumes.emplace_back(new GLVolume()); + GLVolume* new_volume = m_volumes.volumes.back(); + const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh(); + new_volume->model.init_from(mesh); + new_volume->set_instance_transformation(v->get_instance_transformation()); + new_volume->set_volume_transformation(v->get_volume_transformation()); + new_volume->set_sla_shift_z(v->get_sla_shift_z()); + new_volume->set_color(DISABLED_COLOR); + new_volume->mesh_raycaster = std::make_unique(mesh); + } + } + } + +#if ENABLE_RAYCAST_PICKING + register_volume_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING +} + +void GLGizmoSlaBase::render_volumes() +{ + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("emission_factor", 0.0f); + const Camera& camera = wxGetApp().plater()->get_camera(); + + ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane(); + clipping_plane.set_normal(-clipping_plane.get_normal()); + m_volumes.set_clipping_plane(clipping_plane.get_data()); + + m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); + shader->stop_using(); + +} + +#if ENABLE_RAYCAST_PICKING +void GLGizmoSlaBase::register_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { + const GLVolume* v = m_volumes.volumes[i]; + m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, VOLUME_RAYCASTERS_BASE_ID + (int)i, *v->mesh_raycaster, v->world_matrix())); + } +} + +void GLGizmoSlaBase::unregister_volume_raycasters_for_picking() +{ + for (size_t i = 0; i < m_volume_raycasters.size(); ++i) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, VOLUME_RAYCASTERS_BASE_ID + (int)i); + } + m_volume_raycasters.clear(); +} +#endif // ENABLE_RAYCAST_PICKING + +int GLGizmoSlaBase::last_completed_step(const SLAPrint& sla) +{ + int step = -1; + for (int i = 0; i < (int)SLAPrintObjectStep::slaposCount; ++i) { + if (sla.is_step_done((SLAPrintObjectStep)i)) + ++step; + } + return step; +} + +// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal +// Return false if no intersection was found, true otherwise. +bool GLGizmoSlaBase::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) +{ + if (m_c->raycaster()->raycasters().size() != 1) + return false; + if (!m_c->raycaster()->raycaster()) + return false; + if (m_volumes.volumes.empty()) + return false; + + // The raycaster query + Vec3f hit; + Vec3f normal; + if (m_c->raycaster()->raycaster()->unproject_on_mesh( + mouse_pos, + m_volumes.volumes.front()->world_matrix(), + wxGetApp().plater()->get_camera(), + hit, + normal, + m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) { + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); + return true; + } + return false; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp new file mode 100644 index 0000000000..ca87d75ac9 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp @@ -0,0 +1,59 @@ +#ifndef slic3r_GLGizmoSlaBase_hpp_ +#define slic3r_GLGizmoSlaBase_hpp_ + +#include "GLGizmoBase.hpp" +#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/SceneRaycaster.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/Point.hpp" + +#include +#include +#include + +namespace Slic3r { + +class SLAPrint; + +namespace GUI { + +class GLCanvas3D; + +class GLGizmoSlaBase : public GLGizmoBase +{ +public: + GLGizmoSlaBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, SLAPrintObjectStep min_step); + + void reslice_until_step(SLAPrintObjectStep step, bool postpone_error_messages = false); + +protected: + virtual CommonGizmosDataID on_get_requirements() const override; + + void update_volumes(); + void render_volumes(); + +#if ENABLE_RAYCAST_PICKING + void register_volume_raycasters_for_picking(); + void unregister_volume_raycasters_for_picking(); +#endif // ENABLE_RAYCAST_PICKING + + bool is_input_enabled() const { return m_input_enabled; } + int get_min_sla_print_object_step() const { return m_min_sla_print_object_step; } + + static int last_completed_step(const SLAPrint& sla); + + bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); + +private: + GLVolumeCollection m_volumes; + bool m_input_enabled{ false }; + int m_min_sla_print_object_step{ -1 }; +#if ENABLE_RAYCAST_PICKING + std::vector> m_volume_raycasters; +#endif // ENABLE_RAYCAST_PICKING +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoSlaBase_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 291a8323a1..2884d49698 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -1,8 +1,6 @@ +#include "libslic3r/libslic3r.h" // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoSlaSupports.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/Camera.hpp" -#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/Utils/UndoRedo.hpp" @@ -12,11 +10,9 @@ #include #include -#include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" -#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/MsgDialog.hpp" #include "libslic3r/PresetBundle.hpp" @@ -30,10 +26,8 @@ static const double CONE_HEIGHT = 0.75; namespace Slic3r { namespace GUI { -static const ColorRGBA DISABLED_COLOR = ColorRGBA::DARK_GRAY(); - GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) + : GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles) {} bool GLGizmoSlaSupports::on_init() @@ -62,16 +56,6 @@ bool GLGizmoSlaSupports::on_init() return true; } -static int last_completed_step(const SLAPrint& sla) -{ - int step = -1; - for (int i = 0; i < (int)SLAPrintObjectStep::slaposCount; ++i) { - if (sla.is_step_done((SLAPrintObjectStep)i)) - ++step; - } - return step; -} - void GLGizmoSlaSupports::data_changed() { if (! m_c->selection_info()) @@ -89,8 +73,9 @@ void GLGizmoSlaSupports::data_changed() if (mo) { m_c->instances_hider()->set_hide_full_scene(true); const SLAPrintObject* po = m_c->selection_info()->print_object(); - if (po != nullptr && last_completed_step(*po->print()) < (int)slaposDrillHoles) - process_mesh(slaposDrillHoles, false); + const int required_step = get_min_sla_print_object_step(); + if (po != nullptr && last_completed_step(*po->print()) < required_step) + reslice_until_step((SLAPrintObjectStep)required_step, false); update_volumes(); @@ -380,24 +365,6 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } -void GLGizmoSlaSupports::render_volumes() -{ - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light_clip"); - if (shader == nullptr) - return; - - shader->start_using(); - shader->set_uniform("emission_factor", 0.0f); - const Camera& camera = wxGetApp().plater()->get_camera(); - - ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane(); - clipping_plane.set_normal(-clipping_plane.get_normal()); - m_volumes.set_clipping_plane(clipping_plane.get_data()); - - m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix()); - shader->stop_using(); -} - bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const { if (m_c->object_clipper()->get_position() == 0.) @@ -413,33 +380,6 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point); } - - -// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal -// Return false if no intersection was found, true otherwise. -bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) -{ - if (!m_c->raycaster()->raycaster()) - return false; - - // The raycaster query - Vec3f hit; - Vec3f normal; - if (m_c->raycaster()->raycaster()->unproject_on_mesh( - mouse_pos, - m_volumes.volumes.front()->world_matrix(), - wxGetApp().plater()->get_camera(), - hit, - normal, - m_c->object_clipper()->get_position() != 0.0 ? m_c->object_clipper()->get_clipping_plane() : nullptr)) { - // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); - return true; - } - - return false; -} - // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. // The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is // aware that the event was reacted to and stops trying to make different sense of it. If the gizmo @@ -842,7 +782,7 @@ RENDER_AGAIN: } } else { // not in editing mode: - m_imgui->disabled_begin(!m_input_enabled); + m_imgui->disabled_begin(!is_input_enabled()); ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("minimal_distance")); @@ -895,7 +835,7 @@ RENDER_AGAIN: m_imgui->disabled_end(); - m_imgui->disabled_begin(!m_input_enabled || m_normal_cache.empty()); + m_imgui->disabled_begin(!is_input_enabled() || m_normal_cache.empty()); remove_all = m_imgui->button(m_desc.at("remove_all")); m_imgui->disabled_end(); @@ -908,7 +848,7 @@ RENDER_AGAIN: // Following is rendered in both editing and non-editing mode: - m_imgui->disabled_begin(!m_input_enabled); + m_imgui->disabled_begin(!is_input_enabled()); ImGui::Separator(); if (m_c->object_clipper()->get_position() == 0.f) { ImGui::AlignTextToFramePadding(); @@ -991,17 +931,6 @@ std::string GLGizmoSlaSupports::on_get_name() const return _u8L("SLA Support Points"); } -CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const -{ - return CommonGizmosDataID( - int(CommonGizmosDataID::SelectionInfo) - | int(CommonGizmosDataID::InstancesHider) - | int(CommonGizmosDataID::Raycaster) - | int(CommonGizmosDataID::ObjectClipper)); -} - - - void GLGizmoSlaSupports::ask_about_changes_call_after(std::function on_yes, std::function on_no) { wxGetApp().CallAfter([on_yes, on_no]() { @@ -1189,7 +1118,7 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() mo->sla_support_points.clear(); mo->sla_support_points = m_normal_cache; - reslice_SLA_supports(); + reslice_until_step(slaposPad); } } @@ -1221,17 +1150,9 @@ bool GLGizmoSlaSupports::has_backend_supports() const return false; } -void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) const -{ - wxGetApp().CallAfter([this, postpone_error_messages]() { - wxGetApp().plater()->reslice_SLA_supports( - *m_c->selection_info()->model_object(), postpone_error_messages); - }); -} - bool GLGizmoSlaSupports::on_mouse(const wxMouseEvent &mouse_event) { - if (!m_input_enabled) return true; + if (!is_input_enabled()) return true; if (mouse_event.Moving()) return false; if (!mouse_event.ShiftDown() && !mouse_event.AltDown() && use_grabbers(mouse_event)) return true; @@ -1324,7 +1245,7 @@ void GLGizmoSlaSupports::auto_generate() if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points")); - wxGetApp().CallAfter([this]() { reslice_SLA_supports(); }); + wxGetApp().CallAfter([this]() { reslice_until_step(slaposPad); }); mo->sla_points_status = sla::PointsStatus::Generating; } } @@ -1395,22 +1316,6 @@ void GLGizmoSlaSupports::unregister_point_raycasters_for_picking() m_point_raycasters.clear(); } -void GLGizmoSlaSupports::register_volume_raycasters_for_picking() -{ - for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { - const GLVolume* v = m_volumes.volumes[i]; - m_volume_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i, *v->mesh_raycaster, v->world_matrix())); - } -} - -void GLGizmoSlaSupports::unregister_volume_raycasters_for_picking() -{ - for (size_t i = 0; i < m_volume_raycasters.size(); ++i) { - m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, (int)SceneRaycaster::EIdBase::Gizmo + (int)i); - } - m_volume_raycasters.clear(); -} - void GLGizmoSlaSupports::update_point_raycasters_for_picking_transform() { if (m_editing_cache.empty()) @@ -1442,68 +1347,6 @@ void GLGizmoSlaSupports::update_point_raycasters_for_picking_transform() } #endif // ENABLE_RAYCAST_PICKING -void GLGizmoSlaSupports::update_volumes() -{ - m_volumes.clear(); - unregister_volume_raycasters_for_picking(); - - const ModelObject* mo = m_c->selection_info()->model_object(); - if (mo == nullptr) - return; - - const SLAPrintObject* po = m_c->selection_info()->print_object(); - if (po == nullptr) - return; - - m_input_enabled = false; - - TriangleMesh backend_mesh = po->get_mesh_to_print(); - if (!backend_mesh.empty()) { - // The backend has generated a valid mesh. Use it - backend_mesh.transform(po->trafo().inverse()); - m_volumes.volumes.emplace_back(new GLVolume()); - GLVolume* new_volume = m_volumes.volumes.back(); - new_volume->model.init_from(backend_mesh); - new_volume->set_instance_transformation(po->model_object()->instances[m_parent.get_selection().get_instance_idx()]->get_transformation()); - new_volume->set_sla_shift_z(po->get_current_elevation()); - new_volume->mesh_raycaster = std::make_unique(backend_mesh); - m_input_enabled = last_completed_step(*m_c->selection_info()->print_object()->print()) >= slaposDrillHoles; - if (m_input_enabled) - new_volume->selected = true; // to set the proper color - else - new_volume->set_color(DISABLED_COLOR); - } - - if (m_volumes.volumes.empty()) { - // No valid mesh found in the backend. Use the selection to duplicate the volumes - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList& idxs = selection.get_volume_idxs(); - for (unsigned int idx : idxs) { - const GLVolume* v = selection.get_volume(idx); - if (!v->is_modifier) { - m_volumes.volumes.emplace_back(new GLVolume()); - GLVolume* new_volume = m_volumes.volumes.back(); - const TriangleMesh& mesh = mo->volumes[v->volume_idx()]->mesh(); - new_volume->model.init_from(mesh); - new_volume->set_instance_transformation(v->get_instance_transformation()); - new_volume->set_volume_transformation(v->get_volume_transformation()); - new_volume->set_sla_shift_z(v->get_sla_shift_z()); - new_volume->set_color(DISABLED_COLOR); - new_volume->mesh_raycaster = std::make_unique(mesh); - } - } - } - - register_volume_raycasters_for_picking(); -} - -void GLGizmoSlaSupports::process_mesh(SLAPrintObjectStep step, bool postpone_error_messages) -{ - wxGetApp().CallAfter([this, step, postpone_error_messages]() { - wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); - }); -} - SlaGizmoHelpDialog::SlaGizmoHelpDialog() : wxDialog(nullptr, wxID_ANY, _L("SLA gizmo keyboard shortcuts"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index bc5eede66c..c10d225da0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -1,9 +1,8 @@ #ifndef slic3r_GLGizmoSlaSupports_hpp_ #define slic3r_GLGizmoSlaSupports_hpp_ -#include "GLGizmoBase.hpp" +#include "GLGizmoSlaBase.hpp" #include "slic3r/GUI/GLSelectionRectangle.hpp" -#include "slic3r/GUI/3DScene.hpp" #include "libslic3r/SLA/SupportPoint.hpp" #include "libslic3r/ObjectID.hpp" @@ -20,11 +19,9 @@ namespace GUI { class Selection; enum class SLAGizmoEventType : unsigned char; -class GLGizmoSlaSupports : public GLGizmoBase +class GLGizmoSlaSupports : public GLGizmoSlaBase { private: - bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); - static constexpr float RenderPointScale = 1.f; class CacheEntry { @@ -65,7 +62,6 @@ public: bool is_in_editing_mode() const override { return m_editing_mode; } bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); } bool has_backend_supports() const; - void reslice_SLA_supports(bool postpone_error_messages = false) const; bool wants_enter_leave_snapshots() const override { return true; } std::string get_gizmo_entering_text() const override { return _u8L("Entering SLA support points"); } @@ -93,17 +89,12 @@ private: #else void render_points(const Selection& selection, bool picking = false); #endif // ENABLE_RAYCAST_PICKING - void render_volumes(); bool unsaved_changes() const; #if ENABLE_RAYCAST_PICKING void register_point_raycasters_for_picking(); void unregister_point_raycasters_for_picking(); - void register_volume_raycasters_for_picking(); - void unregister_volume_raycasters_for_picking(); void update_point_raycasters_for_picking_transform(); #endif // ENABLE_RAYCAST_PICKING - void update_volumes(); - void process_mesh(SLAPrintObjectStep step, bool postpone_error_messages = false); bool m_lock_unique_islands = false; bool m_editing_mode = false; // Is editing mode active? @@ -120,15 +111,11 @@ private: PickingModel m_sphere; PickingModel m_cone; std::vector, std::shared_ptr>> m_point_raycasters; - std::vector> m_volume_raycasters; #else GLModel m_cone; GLModel m_sphere; #endif // ENABLE_RAYCAST_PICKING - GLVolumeCollection m_volumes; - bool m_input_enabled{ false }; - // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; @@ -174,7 +161,6 @@ protected: std::string on_get_name() const override; bool on_is_activable() const override; bool on_is_selectable() const override; - virtual CommonGizmosDataID on_get_requirements() const override; void on_load(cereal::BinaryInputArchive& ar) override; void on_save(cereal::BinaryOutputArchive& ar) const override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 37d1d3de4c..ad0e268f6f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -683,7 +683,7 @@ void GLGizmosManager::update_after_undo_redo(const UndoRedo::Snapshot& snapshot) m_serializing = false; if (m_current == SlaSupports && snapshot.snapshot_data.flags & UndoRedo::SnapshotData::RECALCULATE_SLA_SUPPORTS) - dynamic_cast(m_gizmos[SlaSupports].get())->reslice_SLA_supports(true); + dynamic_cast(m_gizmos[SlaSupports].get())->reslice_until_step(slaposPad, true); } #if ENABLE_LEGACY_OPENGL_REMOVAL diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d57963e258..9db47ffb04 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6370,16 +6370,6 @@ void Plater::reslice() p->preview->reload_print(!clean_gcode_toolpaths); } -void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages) -{ - reslice_SLA_until_step(slaposPad, object, postpone_error_messages); -} - -void Plater::reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages) -{ - reslice_SLA_until_step(slaposDrillHoles, object, postpone_error_messages); -} - void Plater::reslice_until_step_inner(int step, const ModelObject &object, bool postpone_error_messages) { //FIXME Don't reslice if export of G-code or sending to OctoPrint is running. diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 77a401f3f1..5818ecf567 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -266,8 +266,6 @@ public: void export_toolpaths_to_obj() const; void reslice(); void reslice_FFF_until_step(PrintObjectStep step, const ModelObject &object, bool postpone_error_messages = false); - void reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages = false); - void reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages = false); void reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages = false); void clear_before_change_mesh(int obj_idx); From 75a25b5ad7214e90e17838ae0e9215f124ece85d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 24 Nov 2022 12:26:22 +0100 Subject: [PATCH 16/75] Add missing includes to fix compilation on linux --- src/libslic3r/SLA/Hollowing.cpp | 2 ++ src/libslic3r/SlicesToTriangleMesh.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index a35ba511b1..2d3606c475 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include diff --git a/src/libslic3r/SlicesToTriangleMesh.cpp b/src/libslic3r/SlicesToTriangleMesh.cpp index 3ea64e8781..9e290d472b 100644 --- a/src/libslic3r/SlicesToTriangleMesh.cpp +++ b/src/libslic3r/SlicesToTriangleMesh.cpp @@ -9,6 +9,8 @@ #include #include +#include + namespace Slic3r { // Same as walls() but with identical higher and lower polygons. From 7831b6e6ccf31cf886e62a664479ec2e4cff3116 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Nov 2022 12:51:00 +0100 Subject: [PATCH 17/75] Fix compilation --- src/libslic3r/SLAPrintSteps.cpp | 4 ++-- src/slic3r/GUI/GUI_Factories.cpp | 4 ++-- src/slic3r/GUI/GUI_Factories.hpp | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 4cf420a29e..c8dcb9f3be 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -423,7 +423,7 @@ static void filter_support_points_by_modifiers( !is_enforced && enf_idx < enforcers[idx].size(); ++enf_idx) { - if (enforcers[idx][enf_idx].contains_b(sp2d)) + if (enforcers[idx][enf_idx].contains(sp2d)) is_enforced = true; } } @@ -434,7 +434,7 @@ static void filter_support_points_by_modifiers( !is_blocked && blk_idx < blockers[idx].size(); ++blk_idx) { - if (blockers[idx][blk_idx].contains_b(sp2d)) + if (blockers[idx][blk_idx].contains(sp2d)) is_blocked = true; } } diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 39cf44eb4e..a753220289 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -164,12 +164,12 @@ static const constexpr std::array, 5> ADD_ }}; // Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important -const std::vector> MenuFactory::TEXT_VOLUME_ICONS { +static const constexpr std::array, 3> TEXT_VOLUME_ICONS {{ // menu_item Name menu_item bitmap name {L("Add text"), "add_text_part"}, // ~ModelVolumeType::MODEL_PART {L("Add negative text"), "add_text_negative" }, // ~ModelVolumeType::NEGATIVE_VOLUME {L("Add text modifier"), "add_text_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER -}; +}}; static Plater* plater() { diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index e418cf6751..81798af9cc 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -33,8 +33,6 @@ struct SettingsFactory class MenuFactory { public: - static const std::vector> ADD_VOLUME_MENU_ITEMS; - static const std::vector> TEXT_VOLUME_ICONS; static std::vector get_volume_bitmaps(); static std::vector get_text_volume_bitmaps(); From 1e2b4929debaceda386e2f78bf1a2fb06fb3935a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Nov 2022 14:40:22 +0100 Subject: [PATCH 18/75] Prevent crash with disabled supports and enabled pad --- src/libslic3r/SLAPrintSteps.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index c8dcb9f3be..f617c3e69b 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -204,6 +204,9 @@ struct csg_inserter { void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po) { po.m_mesh_to_slice.clear(); + po.m_supportdata.reset(); + po.m_hollowing_data.reset(); + csg::model_to_csgmesh(*po.model_object(), po.trafo(), csg_inserter{po.m_mesh_to_slice, slaposAssembly}, csg::mpartsPositive | csg::mpartsNegative); @@ -599,6 +602,12 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // repeated) if(po.m_config.pad_enable.getBool()) { + if (!po.m_supportdata) + po.m_supportdata = + std::make_unique( + po.get_mesh_to_print() + ); + // Get the distilled pad configuration from the config // (Again, despite it was retrieved in the previous step. Note that // on a param change event, the previous step might not be executed From faf20a26503a6b30655ca520f77b8020fcea6f7a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 12 Dec 2022 15:37:36 +0100 Subject: [PATCH 19/75] Fixed crash while reloading scene when using sla printer with multipart objects --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1cb1ca4f31..23e974a558 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1944,9 +1944,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed for (GLVolume* volume : m_volumes.volumes) if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { - const SLAPrintObject *po = sla_print()->objects()[volume->object_idx()]; - float zoffs = po->get_current_elevation() / sla_print()->relative_correction().z(); - volume->set_sla_shift_z(zoffs); + const SLAPrintObject* po = sla_print()->get_print_object_by_model_object_id(volume->object_idx()); + if (po != nullptr) + volume->set_sla_shift_z(po->get_current_elevation() / sla_print()->relative_correction().z()); } } From 8511a17ad0cf1275d2b0506b0a78dc14cc84971c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 7 Dec 2022 13:29:41 +0100 Subject: [PATCH 20/75] Increase fidelity of openvdb previews and log duration of generating it --- src/libslic3r/SLAPrintSteps.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index f617c3e69b..7989e9044f 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -138,7 +138,7 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( bench.start(); // update preview mesh - double vscale = 1. / (2. * po.m_config.layer_height.getFloat()); + double vscale = 1. / po.m_config.layer_height.getFloat(); auto voxparams = csg::VoxelizeParams{} .voxel_scale(vscale) .exterior_bandwidth(1.f) @@ -150,7 +150,7 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( bench.stop(); - std::cout << "Preview gen took: " << bench.getElapsedSec() << std::endl; + BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec(); return m; } From a141a4c0bc9f4816dc34e584be34b23016a96bbe Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 7 Dec 2022 16:35:07 +0100 Subject: [PATCH 21/75] Improve cancellation of new sla backend --- src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp | 42 +++++--------------- src/libslic3r/OpenVDBUtils.cpp | 47 +++++++++++++++++------ src/libslic3r/OpenVDBUtils.hpp | 27 +++++++++++-- src/libslic3r/SLA/Hollowing.hpp | 24 +++++++++--- src/libslic3r/SLAPrint.cpp | 11 ++---- src/libslic3r/SLAPrint.hpp | 33 +++++++++++++--- src/libslic3r/SLAPrintSteps.cpp | 27 ++++++++----- src/libslic3r/SLAPrintSteps.hpp | 32 +++++++-------- 8 files changed, 151 insertions(+), 92 deletions(-) diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp index 9d72e3ea88..003fc2ca08 100644 --- a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -1,52 +1,27 @@ #ifndef VOXELIZECSGMESH_HPP #define VOXELIZECSGMESH_HPP +#include + #include "CSGMesh.hpp" #include "libslic3r/OpenVDBUtils.hpp" namespace Slic3r { namespace csg { -class VoxelizeParams { - float m_voxel_scale = 1.f; - float m_exterior_bandwidth = 3.0f; - float m_interior_bandwidth = 3.0f; - -public: - float voxel_scale() const noexcept { return m_voxel_scale; } - float exterior_bandwidth() const noexcept { return m_exterior_bandwidth; } - float interior_bandwidth() const noexcept { return m_interior_bandwidth; } - - VoxelizeParams &voxel_scale(float val) noexcept - { - m_voxel_scale = val; - return *this; - } - VoxelizeParams &exterior_bandwidth(float val) noexcept - { - m_exterior_bandwidth = val; - return *this; - } - VoxelizeParams &interior_bandwidth(float val) noexcept - { - m_interior_bandwidth = val; - return *this; - } -}; +using VoxelizeParams = MeshToGridParams; // This method can be overriden when a specific CSGPart type supports caching // of the voxel grid template -VoxelGridPtr get_voxelgrid(const CSGPartT &csgpart, const VoxelizeParams ¶ms) +VoxelGridPtr get_voxelgrid(const CSGPartT &csgpart, VoxelizeParams params) { const indexed_triangle_set *its = csg::get_mesh(csgpart); VoxelGridPtr ret; + params.trafo(params.trafo() * csg::get_transform(csgpart)); + if (its) - ret = mesh_to_grid(*csg::get_mesh(csgpart), - csg::get_transform(csgpart), - params.voxel_scale(), - params.exterior_bandwidth(), - params.interior_bandwidth()); + ret = mesh_to_grid(*csg::get_mesh(csgpart), params); return ret; } @@ -58,6 +33,9 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, VoxelGridPtr ret; for (auto &csgpart : csgrange) { + if (params.statusfn() && params.statusfn()(-1)) + break; + VoxelGridPtr partgrid = get_voxelgrid(csgpart, params); if (!ret && get_operation(csgpart) == CSGType::Union) { diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 8ea1100002..2a94d38e0a 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -74,11 +74,18 @@ public: : its{m}, trafo{tr} {} }; +struct Interrupter +{ + std::function statusfn; + + void start(const char* name = nullptr) { (void)name; } + void end() {} + + inline bool wasInterrupted(int percent = -1) { return statusfn(percent); } +}; + VoxelGridPtr mesh_to_grid(const indexed_triangle_set &mesh, - const Transform3f &tr, - float voxel_scale, - float exteriorBandWidth, - float interiorBandWidth) + const MeshToGridParams ¶ms) { // Might not be needed but this is now proven to be working openvdb::initialize(); @@ -92,14 +99,22 @@ VoxelGridPtr mesh_to_grid(const indexed_triangle_set &mesh, meshparts.erase(it, meshparts.end()); - Transform3d trafo = tr.cast(); - trafo.prescale(voxel_scale); + Transform3d trafo = params.trafo().cast(); + trafo.prescale(params.voxel_scale()); + + Interrupter interrupter{params.statusfn()}; openvdb::FloatGrid::Ptr grid; for (auto &m : meshparts) { auto subgrid = openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{m, trafo}, {}, - exteriorBandWidth, interiorBandWidth); + interrupter, + TriangleMeshDataAdapter{m, trafo}, + openvdb::math::Transform{}, + params.exterior_bandwidth(), + params.interior_bandwidth()); + + if (interrupter.wasInterrupted()) + break; if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid); @@ -107,16 +122,24 @@ VoxelGridPtr mesh_to_grid(const indexed_triangle_set &mesh, grid = std::move(subgrid); } + if (interrupter.wasInterrupted()) + return {}; + if (meshparts.empty()) { // Splitting failed, fall back to hollow the original mesh grid = openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{mesh, trafo}, {}, exteriorBandWidth, - interiorBandWidth); + interrupter, + TriangleMeshDataAdapter{mesh, trafo}, + openvdb::math::Transform{}, + params.exterior_bandwidth(), + params.interior_bandwidth()); } + if (interrupter.wasInterrupted()) + return {}; - grid->transform().preScale(1./voxel_scale); - grid->insertMeta("voxel_scale", openvdb::FloatMetadata(voxel_scale)); + grid->transform().preScale(1./params.voxel_scale()); + grid->insertMeta("voxel_scale", openvdb::FloatMetadata(params.voxel_scale())); VoxelGridPtr ret = make_voxelgrid(std::move(*grid)); diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index 959cd854d7..cb857020f7 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -23,6 +23,28 @@ float get_voxel_scale(const VoxelGrid &grid); VoxelGridPtr clone(const VoxelGrid &grid); +class MeshToGridParams { + Transform3f m_tr = Transform3f::Identity(); + float m_voxel_scale = 1.f; + float m_exteriorBandWidth = 3.0f; + float m_interiorBandWidth = 3.0f; + + std::function m_statusfn; + +public: + MeshToGridParams & trafo(const Transform3f &v) { m_tr = v; return *this; } + MeshToGridParams & voxel_scale(float v) { m_voxel_scale = v; return *this; } + MeshToGridParams & exterior_bandwidth(float v) { m_exteriorBandWidth = v; return *this; } + MeshToGridParams & interior_bandwidth(float v) { m_interiorBandWidth = v; return *this; } + MeshToGridParams & statusfn(std::function fn) { m_statusfn = fn; return *this; } + + const Transform3f& trafo() const noexcept { return m_tr; } + float voxel_scale() const noexcept { return m_voxel_scale; } + float exterior_bandwidth() const noexcept { return m_exteriorBandWidth; } + float interior_bandwidth() const noexcept { return m_interiorBandWidth; } + const std::function& statusfn() const noexcept { return m_statusfn; } +}; + // Here voxel_scale defines the scaling of voxels which affects the voxel count. // 1.0 value means a voxel for every unit cube. 2 means the model is scaled to // be 2x larger and the voxel count is increased by the increment in the scaled @@ -31,10 +53,7 @@ VoxelGridPtr clone(const VoxelGrid &grid); // The resulting grid will contain the voxel_scale in its metadata under the // "voxel_scale" key to be used in grid_to_mesh function. VoxelGridPtr mesh_to_grid(const indexed_triangle_set &mesh, - const Transform3f &tr = Transform3f::Identity(), - float voxel_scale = 1.f, - float exteriorBandWidth = 3.0f, - float interiorBandWidth = 3.0f); + const MeshToGridParams ¶ms = {}); indexed_triangle_set grid_to_mesh(const VoxelGrid &grid, double isovalue = 0.0, diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index 6ff4660bae..bcc26981e2 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -84,16 +84,24 @@ InteriorPtr generate_interior(const VoxelGrid &mesh, const JobController &ctl = {}); inline InteriorPtr generate_interior(const indexed_triangle_set &mesh, - const HollowingConfig &hc = {}, - const JobController &ctl = {}) + const HollowingConfig &hc = {}, + const JobController &ctl = {}) { auto voxel_scale = get_voxel_scale(its_volume(mesh), hc); - auto grid = mesh_to_grid(mesh, Transform3f::Identity(), voxel_scale, 1.f, 1.f); + auto statusfn = [&ctl](int){ return ctl.stopcondition && ctl.stopcondition(); }; + auto grid = mesh_to_grid(mesh, MeshToGridParams{} + .voxel_scale(voxel_scale) + .exterior_bandwidth(1.f) + .interior_bandwidth(1.f) + .statusfn(statusfn)); + + if (!grid || (ctl.stopcondition && ctl.stopcondition())) + return {}; if (its_is_splittable(mesh)) grid = redistance_grid(*grid, 0.0f, 6.f / voxel_scale, 6.f / voxel_scale); - return generate_interior(*grid, hc, ctl); + return grid ? generate_interior(*grid, hc, ctl) : InteriorPtr{}; } template @@ -109,17 +117,21 @@ InteriorPtr generate_interior(const Range &csgparts, auto params = csg::VoxelizeParams{} .voxel_scale(get_voxel_scale(mesh_vol, hc)) .exterior_bandwidth(1.f) - .interior_bandwidth(1.f); + .interior_bandwidth(1.f) + .statusfn([&ctl](int){ return ctl.stopcondition && ctl.stopcondition(); }); auto ptr = csg::voxelize_csgmesh(csgparts, params); + if (!ptr || (ctl.stopcondition && ctl.stopcondition())) + return {}; + if (csgparts.size() > 1 || its_is_splittable(*csg::get_mesh(*csgparts.begin()))) ptr = redistance_grid(*ptr, 0.0f, 6.f / params.voxel_scale(), 6.f / params.voxel_scale()); - return generate_interior(*ptr, hc, ctl); + return ptr ? generate_interior(*ptr, hc, ctl) : InteriorPtr{}; } // Will do the hollowing diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index b8000f3849..fdb264b5e3 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1160,19 +1160,16 @@ inline bool operator==(const VoxelizeParams &a, const VoxelizeParams &b) return h(a) == h(b); } -VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, const VoxelizeParams &p) +VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, VoxelizeParams p) { VoxelGridPtr &ret = part.gridcache[p]; if (!ret) { - ret = mesh_to_grid(*csg::get_mesh(part), - csg::get_transform(part), - p.voxel_scale(), - p.exterior_bandwidth(), - p.interior_bandwidth()); + p.trafo(csg::get_transform(part)); + ret = mesh_to_grid(*csg::get_mesh(part), p); } - return clone(*ret); + return ret ? clone(*ret) : nullptr; } } // namespace csg diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index ca74cb3914..37aeb7b1cc 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -13,6 +13,8 @@ #include "libslic3r/CSGMesh/CSGMesh.hpp" #include "libslic3r/OpenVDBUtils.hpp" +#include + namespace Slic3r { enum SLAPrintStep : unsigned int { @@ -51,11 +53,32 @@ namespace std { template<> struct hash { size_t operator() (const Slic3r::csg::VoxelizeParams &p) const { - std::string str = Slic3r::float_to_string_decimal_point(p.voxel_scale()); - str += Slic3r::float_to_string_decimal_point(p.exterior_bandwidth()); - str += Slic3r::float_to_string_decimal_point(p.interior_bandwidth()); + int64_t vs = Slic3r::scaled(p.voxel_scale()) >> 10; + int64_t eb = Slic3r::scaled(p.exterior_bandwidth()) >> 10; + int64_t ib = Slic3r::scaled(p.interior_bandwidth()) >> 10; - return std::hash{}(str); + size_t h = 0; + boost::hash_combine(h, vs); + boost::hash_combine(h, eb); + boost::hash_combine(h, ib); + + return h; + } +}; + +template<> struct equal_to { + size_t operator() (const Slic3r::csg::VoxelizeParams &p1, + const Slic3r::csg::VoxelizeParams &p2) const { + + int64_t vs1 = Slic3r::scaled(p1.voxel_scale()) >> 10; + int64_t eb1 = Slic3r::scaled(p1.exterior_bandwidth()) >> 10; + int64_t ib1 = Slic3r::scaled(p1.interior_bandwidth()) >> 10; + + int64_t vs2 = Slic3r::scaled(p2.voxel_scale()) >> 10; + int64_t eb2 = Slic3r::scaled(p2.exterior_bandwidth()) >> 10; + int64_t ib2 = Slic3r::scaled(p2.interior_bandwidth()) >> 10; + + return vs1 == vs2 && eb1 == eb2 && ib1 == ib2; } }; @@ -91,7 +114,7 @@ struct CSGPartForStep : public csg::CSGPart namespace csg { -VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, const VoxelizeParams &p); +VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, VoxelizeParams p); } // namespace csg diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 7989e9044f..6c26c6f7bd 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -41,14 +41,14 @@ namespace Slic3r { namespace { const std::array OBJ_STEP_LEVELS = { - 5, // slaposAssembly - 5, // slaposHollowing, - 10, // slaposDrillHoles - 10, // slaposObjectSlice, - 20, // slaposSupportPoints, - 10, // slaposSupportTree, - 10, // slaposPad, - 30, // slaposSliceSupports, + 13, // slaposAssembly + 13, // slaposHollowing, + 13, // slaposDrillHoles + 13, // slaposObjectSlice, + 13, // slaposSupportPoints, + 13, // slaposSupportTree, + 11, // slaposPad, + 11, // slaposSliceSupports, }; std::string OBJ_STEP_LABELS(size_t idx) @@ -144,13 +144,20 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( .exterior_bandwidth(1.f) .interior_bandwidth(1.f); + voxparams.statusfn([&po](int){ + return po.m_print->cancel_status() != CancelStatus::NOT_CANCELED; + }); + auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams); - indexed_triangle_set m = grid_to_mesh(*grid); + auto m = grid ? grid_to_mesh(*grid) : indexed_triangle_set{}; bench.stop(); - BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec(); + if (!m.empty()) + BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec(); + else + BOOST_LOG_TRIVIAL(error) << "Preview failed!"; return m; } diff --git a/src/libslic3r/SLAPrintSteps.hpp b/src/libslic3r/SLAPrintSteps.hpp index 3d63cbbadb..a1b3ef42d1 100644 --- a/src/libslic3r/SLAPrintSteps.hpp +++ b/src/libslic3r/SLAPrintSteps.hpp @@ -15,34 +15,34 @@ class SLAPrint::Steps private: SLAPrint *m_print = nullptr; std::mt19937 m_rng; - -public: + +public: // where the per object operations start and end - static const constexpr unsigned min_objstatus = 0; - static const constexpr unsigned max_objstatus = 50; - + static const constexpr unsigned min_objstatus = 0; + static const constexpr unsigned max_objstatus = 70; + private: const size_t objcount; - + // shortcut to initial layer height const double ilhd; const float ilh; const coord_t ilhs; - + // the coefficient that multiplies the per object status values which // are set up for <0, 100>. They need to be scaled into the whole process const double objectstep_scale; - + template void report_status(Args&&...args) { m_print->m_report_status(*m_print, std::forward(args)...); } - + double current_status() const { return m_print->m_report_status.status(); } void throw_if_canceled() const { m_print->throw_if_canceled(); } bool canceled() const { return m_print->canceled(); } void initialize_printer_input(); - + void apply_printer_corrections(SLAPrintObject &po, SliceOrigin o); void generate_preview(SLAPrintObject &po, SLAPrintObjectStep step); @@ -50,7 +50,7 @@ private: public: explicit Steps(SLAPrint *print); - + void mesh_assembly(SLAPrintObject &po); void hollow_model(SLAPrintObject &po); void drill_holes (SLAPrintObject &po); @@ -59,20 +59,20 @@ public: void support_tree(SLAPrintObject& po); void generate_pad(SLAPrintObject& po); void slice_supports(SLAPrintObject& po); - + void merge_slices_and_eval_stats(); void rasterize(); - + void execute(SLAPrintObjectStep step, SLAPrintObject &obj); void execute(SLAPrintStep step); - + static std::string label(SLAPrintObjectStep step); static std::string label(SLAPrintStep step); - + double progressrange(SLAPrintObjectStep step) const; double progressrange(SLAPrintStep step) const; }; -} +} // namespace Slic3r #endif // SLAPRINTSTEPS_HPP From 9dc091d1a8ab413a636c468aa619e2d6b2db5c01 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 8 Dec 2022 14:20:06 +0100 Subject: [PATCH 22/75] Fix redundant token after include --- src/libslic3r/SLAPrintSteps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 6c26c6f7bd..6ab0f6ec73 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -18,7 +18,7 @@ #include #include #include -#include > +#include #include #include #include From 2cf48683de2d4d36fd12667058dafd0614339f42 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 9 Dec 2022 17:23:31 +0100 Subject: [PATCH 23/75] enable split to parts in sla --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 871ed5e4f5..85a781e5ce 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4961,7 +4961,7 @@ bool Plater::priv::can_split_to_objects() const bool Plater::priv::can_split_to_volumes() const { - return (printer_technology != ptSLA) && q->can_split(false); + return q->can_split(false); } bool Plater::priv::can_arrange() const From 3a7af1c1de798afb0c95ec0238bfe00267e1bc10 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 12 Dec 2022 14:07:28 +0100 Subject: [PATCH 24/75] SLA Backend: skip voxelization if the original mesh is usable as preview --- src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp | 2 +- src/libslic3r/SLAPrintSteps.cpp | 31 ++++++++++------------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp index 003fc2ca08..f1fd739815 100644 --- a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -21,7 +21,7 @@ VoxelGridPtr get_voxelgrid(const CSGPartT &csgpart, VoxelizeParams params) params.trafo(params.trafo() * csg::get_transform(csgpart)); if (its) - ret = mesh_to_grid(*csg::get_mesh(csgpart), params); + ret = mesh_to_grid(*its, params); return ret; } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 6ab0f6ec73..177c4e9842 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -148,9 +148,17 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( return po.m_print->cancel_status() != CancelStatus::NOT_CANCELED; }); - auto grid = csg::voxelize_csgmesh(range(po.m_mesh_to_slice), voxparams); - - auto m = grid ? grid_to_mesh(*grid) : indexed_triangle_set{}; + auto r = range(po.m_mesh_to_slice); + auto m = indexed_triangle_set{}; + if (r.size() > 1) { + auto grid = csg::voxelize_csgmesh(r, voxparams); + m = grid ? grid_to_mesh(*grid) : indexed_triangle_set{}; + } else if (!r.empty()) { + m = *(csg::get_mesh(*r.begin())); + auto tr = csg::get_transform(*r.begin()); + for (auto &v : m.vertices) + v = tr * v; + } bench.stop(); @@ -165,21 +173,6 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) { - // TODO: enable when this works reliably. Currently, perform_csgmesh_booleans - // can generate incorrect result despite not throwing any exception. -// MeshBoolean::cgal::CGALMeshPtr cgalptr; - -// try { -// cgalptr = csg::perform_csgmesh_booleans(range(po.m_mesh_to_slice)); -// } catch(...) { -// cgalptr = nullptr; -// } - -// if (cgalptr) { -// po.m_preview_meshes[step] = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalptr); -// } else -// po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, step)}; - po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, step)}; for (size_t i = size_t(step) + 1; i < slaposCount; ++i) @@ -286,6 +279,8 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) // update preview mesh if (r.first != r.second) generate_preview(po, slaposDrillHoles); + else + po.m_preview_meshes[slaposDrillHoles] = po.get_mesh_to_print(); } template From 7b207aaf5c988c4b7a70f805d660178f1a9eb8eb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 12 Dec 2022 16:23:33 +0100 Subject: [PATCH 25/75] Add "Enforcers only" option into support combo box And also make it work --- src/libslic3r/Preset.cpp | 1 + src/libslic3r/PrintConfig.cpp | 7 +++++ src/libslic3r/PrintConfig.hpp | 3 ++ src/libslic3r/SLAPrint.cpp | 5 +-- src/libslic3r/SLAPrintSteps.cpp | 44 +++++++++++++++++---------- src/slic3r/GUI/ConfigManipulation.cpp | 1 + src/slic3r/GUI/Plater.cpp | 7 +++-- src/slic3r/GUI/Tab.cpp | 1 + 8 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index bd70ee739f..49b8c99a21 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -502,6 +502,7 @@ static std::vector s_Preset_sla_print_options { "support_max_bridges_on_pillar", "support_pillar_connection_mode", "support_buildplate_only", + "support_enforcers_only", "support_pillar_widening_factor", "support_base_diameter", "support_base_height", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index fe35161b04..b1a98d5ad1 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3661,6 +3661,13 @@ void PrintConfigDef::init_sla_params() def->mode = comSimple; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("support_enforcers_only", coBool); + def->label = L("Support only in enforced regions"); + def->category = L("Supports"); + def->tooltip = L("Only create support if it lies in a support enforcer."); + def->mode = comSimple; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("support_pillar_widening_factor", coFloat); def->label = L("Pillar widening factor"); def->category = L("Supports"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 3258de005b..d672966460 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -858,6 +858,9 @@ PRINT_CONFIG_CLASS_DEFINE( // Generate only ground facing supports ((ConfigOptionBool, support_buildplate_only)) + // Generate only ground facing supports + ((ConfigOptionBool, support_enforcers_only)) + // TODO: unimplemented at the moment. This coefficient will have an impact // when bridges and pillars are merged. The resulting pillar should be a bit // thicker than the ones merging into it. How much thicker? I don't know diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index fdb264b5e3..89c881ebf6 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -35,7 +35,7 @@ bool is_zero_elevation(const SLAPrintObjectConfig &c) sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) { sla::SupportTreeConfig scfg; - + scfg.enabled = c.supports_enable.getBool(); scfg.tree_type = c.support_tree_type.value; scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); @@ -58,7 +58,7 @@ sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) scfg.pillar_base_safety_distance_mm = c.support_base_safety_distance.getFloat() < EPSILON ? scfg.safety_distance_mm : c.support_base_safety_distance.getFloat(); - + scfg.max_bridges_on_pillar = unsigned(c.support_max_bridges_on_pillar.getInt()); return scfg; @@ -827,6 +827,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector &blockers, - const std::vector &enforcers, - const std::vector &slice_grid) + +struct SuppPtMask { + const std::vector &blockers; + const std::vector &enforcers; + bool enforcers_only = false; +}; + +static void filter_support_points_by_modifiers(sla::SupportPoints &pts, + const SuppPtMask &mask, + const std::vector &slice_grid) { assert((blockers.empty() || blockers.size() == slice_grid.size()) && (enforcers.empty() || enforcers.size() == slice_grid.size())); @@ -423,24 +428,30 @@ static void filter_support_points_by_modifiers( if (it != slice_grid.end()) { size_t idx = std::distance(slice_grid.begin(), it); bool is_enforced = false; - if (idx < enforcers.size()) { + if (idx < mask.enforcers.size()) { for (size_t enf_idx = 0; - !is_enforced && enf_idx < enforcers[idx].size(); + !is_enforced && enf_idx < mask.enforcers[idx].size(); ++enf_idx) { - if (enforcers[idx][enf_idx].contains(sp2d)) + if (mask.enforcers[idx][enf_idx].contains(sp2d)) is_enforced = true; } } bool is_blocked = false; - if (!is_enforced && idx < blockers.size()) { - for (size_t blk_idx = 0; - !is_blocked && blk_idx < blockers[idx].size(); - ++blk_idx) - { - if (blockers[idx][blk_idx].contains(sp2d)) - is_blocked = true; + if (!is_enforced) { + if (!mask.enforcers_only) { + if (idx < mask.blockers.size()) { + for (size_t blk_idx = 0; + !is_blocked && blk_idx < mask.blockers[idx].size(); + ++blk_idx) + { + if (mask.blockers[idx][blk_idx].contains(sp2d)) + is_blocked = true; + } + } + } else { + is_blocked = true; } } @@ -527,7 +538,8 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) return vol->is_support_enforcer(); }); - filter_support_points_by_modifiers(points, blockers, enforcers, po.m_model_height_levels); + SuppPtMask mask{blockers, enforcers, po.config().support_enforcers_only.getBool()}; + filter_support_points_by_modifiers(points, mask, po.m_model_height_levels); po.m_supportdata->input.pts = points; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 50dd4e743e..876228bd9b 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -374,6 +374,7 @@ void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) toggle_field("support_pillar_connection_mode", supports_en && is_default_tree); toggle_field("support_tree_type", supports_en); toggle_field("support_buildplate_only", supports_en); + toggle_field("support_enforcers_only", supports_en); toggle_field("support_base_diameter", supports_en); toggle_field("support_base_height", supports_en); toggle_field("support_base_safety_distance", supports_en); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 85a781e5ce..7d197e1e36 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -545,10 +545,15 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : const bool supports_enable = selection == _("None") ? false : true; new_conf.set_key_value("supports_enable", new ConfigOptionBool(supports_enable)); + new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(false)); + if (selection == _("Everywhere")) new_conf.set_key_value("support_buildplate_only", new ConfigOptionBool(false)); else if (selection == _("Support on build plate only")) new_conf.set_key_value("support_buildplate_only", new ConfigOptionBool(true)); + else if (selection == _("For support enforcers only")) { + new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(true)); + } } tab->load_config(new_conf); @@ -559,8 +564,6 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : ConfigOptionDef support_def_sla = support_def; support_def_sla.set_default_value(new ConfigOptionStrings{ "None" }); - assert(support_def_sla.enum_labels[2] == L("For support enforcers only")); - support_def_sla.enum_labels.erase(support_def_sla.enum_labels.begin() + 2); option = Option(support_def_sla, "support"); option.opt.full_width = true; line.append_option(option); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9515fa2b6b..d36c25d39f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4777,6 +4777,7 @@ void TabSLAPrint::build() optgroup->append_single_option_line("support_pillar_connection_mode"); optgroup->append_single_option_line("support_buildplate_only"); + optgroup->append_single_option_line("support_enforcers_only"); optgroup->append_single_option_line("support_pillar_widening_factor"); optgroup->append_single_option_line("support_base_diameter"); optgroup->append_single_option_line("support_base_height"); From 0dadde6ae3eb16a1a109b20f992a2d2ff2931f94 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 8 Dec 2022 11:20:05 +0100 Subject: [PATCH 26/75] MenuFactory: SLA specific: Fixed adding of the "Edit text" menu item --- src/slic3r/GUI/GUI_Factories.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index a753220289..53b15a3214 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -1018,16 +1018,19 @@ void MenuFactory::append_menu_item_edit_text(wxMenu *menu) wxString name = _L("Edit text"); auto can_edit_text = []() { - const auto& sel = plater()->get_selection(); - if (sel.volumes_count() != 1) return false; - auto cid = sel.get_volume(*sel.get_volume_idxs().begin()); - const ModelVolume* vol = plater()->canvas3D()->get_model() - ->objects[cid->object_idx()]->volumes[cid->volume_idx()]; - return vol->text_configuration.has_value(); + if (plater() != nullptr) { + const Selection& sel = plater()->get_selection(); + if (sel.volumes_count() == 1) { + const GLVolume* gl_vol = sel.get_first_volume(); + const ModelVolume* vol = plater()->model().objects[gl_vol->object_idx()]->volumes[gl_vol->volume_idx()]; + return vol->text_configuration.has_value(); + } + } + return false; }; - if (menu == &m_object_menu) { - auto menu_item_id = menu->FindItem(name); + if (menu != &m_text_part_menu) { + const int menu_item_id = menu->FindItem(name); if (menu_item_id != wxNOT_FOUND) menu->Destroy(menu_item_id); if (!can_edit_text()) From 9de057ddebb1ccda85b4f47349e3b744e4054e94 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 13 Dec 2022 13:23:59 +0100 Subject: [PATCH 27/75] Fix in sla mode: wrong part menu and split submenu --- src/slic3r/GUI/GUI_Factories.cpp | 20 ++++++++++++-------- src/slic3r/GUI/GUI_Factories.hpp | 1 + src/slic3r/GUI/Plater.cpp | 10 +++++----- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 53b15a3214..0250d2164b 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -1088,22 +1088,28 @@ void MenuFactory::create_common_object_menu(wxMenu* menu) append_menu_items_mirror(menu); } -void MenuFactory::create_object_menu() +void MenuFactory::append_menu_items_split(wxMenu *menu) { - create_common_object_menu(&m_object_menu); wxMenu* split_menu = new wxMenu(); if (!split_menu) return; append_menu_item(split_menu, wxID_ANY, _L("To objects"), _L("Split the selected object into individual objects"), - [](wxCommandEvent&) { plater()->split_object(); }, "split_object_SMALL", &m_object_menu, + [](wxCommandEvent&) { plater()->split_object(); }, "split_object_SMALL", menu, []() { return plater()->can_split(true); }, m_parent); append_menu_item(split_menu, wxID_ANY, _L("To parts"), _L("Split the selected object into individual parts"), - [](wxCommandEvent&) { plater()->split_volume(); }, "split_parts_SMALL", &m_object_menu, + [](wxCommandEvent&) { plater()->split_volume(); }, "split_parts_SMALL", menu, []() { return plater()->can_split(false); }, m_parent); - append_submenu(&m_object_menu, split_menu, wxID_ANY, _L("Split"), _L("Split the selected object"), "", + append_submenu(menu, split_menu, wxID_ANY, _L("Split"), _L("Split the selected object"), "", []() { return plater()->can_split(true); }, m_parent); +} + +void MenuFactory::create_object_menu() +{ + create_common_object_menu(&m_object_menu); + + append_menu_items_split(&m_object_menu); m_object_menu.AppendSeparator(); // "Height range Modifier" and "Add (volumes)" menu items will be added later in append_menu_items_add_volume() @@ -1112,9 +1118,7 @@ void MenuFactory::create_object_menu() void MenuFactory::create_sla_object_menu() { create_common_object_menu(&m_sla_object_menu); - append_menu_item(&m_sla_object_menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual objects"), - [](wxCommandEvent&) { plater()->split_object(); }, "split_object_SMALL", nullptr, - []() { return plater()->can_split(true); }, m_parent); + append_menu_items_split(&m_sla_object_menu); m_sla_object_menu.AppendSeparator(); append_menu_items_add_sla_volume(&m_sla_object_menu); diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 81798af9cc..34725d6c14 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -113,6 +113,7 @@ private: void append_menu_item_edit_text(wxMenu *menu); void append_menu_items_instance_manipulation(wxMenu *menu); void update_menu_items_instance_manipulation(MenuType type); + void append_menu_items_split(wxMenu *menu); }; }} diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7d197e1e36..9b179be663 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4484,9 +4484,9 @@ void Plater::priv::on_right_click(RBtnEvent& evt) // so this selection should be updated before menu creation wxGetApp().obj_list()->update_selections(); - if (printer_technology == ptSLA) - menu = menus.sla_object_menu(); - else { +// if (printer_technology == ptSLA) +// menu = menus.sla_object_menu(); +// else { const Selection& selection = get_selection(); // show "Object menu" for each one or several FullInstance instead of FullObject const bool is_some_full_instances = selection.is_single_full_instance() || @@ -4498,12 +4498,12 @@ void Plater::priv::on_right_click(RBtnEvent& evt) const bool is_part = selection.is_single_volume() || selection.is_single_modifier(); #endif // ENABLE_WORLD_COORDINATE if (is_some_full_instances) - menu = menus.object_menu(); + menu = printer_technology == ptSLA ? menus.sla_object_menu() : menus.object_menu(); else if (is_part) menu = selection.is_single_text() ? menus.text_part_menu() : menus.part_menu(); else menu = menus.multi_selection_menu(); - } +// } } if (q != nullptr && menu) { From 5561e21e7781675b68f3530c806ca7cf73a64834 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 13 Dec 2022 14:53:27 +0100 Subject: [PATCH 28/75] object export to stl will work the same way as in FFF TODO might make more sense to execute background processing up until the necessary step and then export backend data --- src/slic3r/GUI/Plater.cpp | 94 +++++---------------------------------- 1 file changed, 12 insertions(+), 82 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9b179be663..45f1012ef1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6112,92 +6112,22 @@ void Plater::export_stl_obj(bool extended, bool selection_only) }; TriangleMesh mesh; - if (p->printer_technology == ptFFF) { - if (selection_only) { - const ModelObject* model_object = p->model.objects[obj_idx]; - if (selection.get_mode() == Selection::Instance) - mesh = mesh_to_export(*model_object, (selection.is_single_full_object() && model_object->instances.size() > 1) ? -1 : selection.get_instance_idx()); - else { - const GLVolume* volume = selection.get_first_volume(); - mesh = model_object->volumes[volume->volume_idx()]->mesh(); - mesh.transform(volume->get_volume_transformation().get_matrix(), true); - } - - if (!selection.is_single_full_object() || model_object->instances.size() == 1) - mesh.translate(-model_object->origin_translation.cast()); - } + if (selection_only) { + const ModelObject* model_object = p->model.objects[obj_idx]; + if (selection.get_mode() == Selection::Instance) + mesh = mesh_to_export(*model_object, (selection.is_single_full_object() && model_object->instances.size() > 1) ? -1 : selection.get_instance_idx()); else { - for (const ModelObject* o : p->model.objects) { - mesh.merge(mesh_to_export(*o, -1)); - } + const GLVolume* volume = selection.get_first_volume(); + mesh = model_object->volumes[volume->volume_idx()]->mesh(); + mesh.transform(volume->get_volume_transformation().get_matrix(), true); } + + if (!selection.is_single_full_object() || model_object->instances.size() == 1) + mesh.translate(-model_object->origin_translation.cast()); } else { - // This is SLA mode, all objects have only one volume. - // However, we must have a look at the backend to load - // hollowed mesh and/or supports - - const PrintObjects& objects = p->sla_print.objects(); - for (const SLAPrintObject* object : objects) { - const ModelObject* model_object = object->model_object(); - if (selection_only) { - if (model_object->id() != p->model.objects[obj_idx]->id()) - continue; - } - const Transform3d mesh_trafo_inv = object->trafo().inverse(); - const bool is_left_handed = object->is_left_handed(); - - auto pad_mesh = extended? object->pad_mesh() : TriangleMesh{}; - pad_mesh = object->pad_mesh(); - pad_mesh.transform(mesh_trafo_inv); - - auto supports_mesh = extended ? object->support_mesh() : TriangleMesh{}; - supports_mesh.transform(mesh_trafo_inv); - - const std::vector& obj_instances = object->instances(); - for (const SLAPrintObject::Instance& obj_instance : obj_instances) { - auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), - [&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; }); - assert(it != model_object->instances.end()); - - if (it != model_object->instances.end()) { - const bool one_inst_only = selection_only && ! selection.is_single_full_object(); - - const int instance_idx = it - model_object->instances.begin(); - const Transform3d& inst_transform = one_inst_only - ? Transform3d::Identity() - : object->model_object()->instances[instance_idx]->get_transformation().get_matrix(); - - TriangleMesh inst_mesh; - - if (!pad_mesh.empty()) { - TriangleMesh inst_pad_mesh = pad_mesh; - inst_pad_mesh.transform(inst_transform, is_left_handed); - inst_mesh.merge(inst_pad_mesh); - } - - if (!supports_mesh.empty()) { - TriangleMesh inst_supports_mesh = supports_mesh; - inst_supports_mesh.transform(inst_transform, is_left_handed); - inst_mesh.merge(inst_supports_mesh); - } - - TriangleMesh inst_object_mesh = object->get_mesh_to_print(); - inst_object_mesh.transform(mesh_trafo_inv); - inst_object_mesh.transform(inst_transform, is_left_handed); - - inst_mesh.merge(inst_object_mesh); - - // ensure that the instance lays on the bed - inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z()); - - // merge instance with global mesh - mesh.merge(inst_mesh); - - if (one_inst_only) - break; - } - } + for (const ModelObject* o : p->model.objects) { + mesh.merge(mesh_to_export(*o, -1)); } } From 9143d9891a7974adceae0f6fe5abbf199f4da8c8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 13 Dec 2022 15:13:31 +0100 Subject: [PATCH 29/75] In SLA mode, upon export to stl, export preview mesh, if it exists --- src/slic3r/GUI/Plater.cpp | 77 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 45f1012ef1..f3eae56335 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6089,7 +6089,7 @@ void Plater::export_stl_obj(bool extended, bool selection_only) return; // Following lambda generates a combined mesh for export with normals pointing outwards. - auto mesh_to_export = [](const ModelObject& mo, int instance_id) { + auto mesh_to_export_fff = [](const ModelObject& mo, int instance_id) { TriangleMesh mesh; for (const ModelVolume* v : mo.volumes) if (v->is_model_part()) { @@ -6111,6 +6111,81 @@ void Plater::export_stl_obj(bool extended, bool selection_only) return mesh; }; + auto mesh_to_export_sla = [&, this](const ModelObject& mo, int instance_id) { + TriangleMesh mesh; + + const SLAPrintObject *object = this->p->sla_print.get_print_object_by_model_object_id(mo.id()); + + if (object->get_mesh_to_print().empty()) + mesh = mesh_to_export_fff(mo, instance_id); + else { + const Transform3d mesh_trafo_inv = object->trafo().inverse(); + const bool is_left_handed = object->is_left_handed(); + + auto pad_mesh = extended? object->pad_mesh() : TriangleMesh{}; + pad_mesh = object->pad_mesh(); + pad_mesh.transform(mesh_trafo_inv); + + auto supports_mesh = extended ? object->support_mesh() : TriangleMesh{}; + supports_mesh.transform(mesh_trafo_inv); + + const std::vector& obj_instances = object->instances(); + for (const SLAPrintObject::Instance& obj_instance : obj_instances) { + auto it = std::find_if(object->model_object()->instances.begin(), object->model_object()->instances.end(), + [&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; }); + assert(it != model_object->instances.end()); + + if (it != object->model_object()->instances.end()) { + const bool one_inst_only = selection_only && ! selection.is_single_full_object(); + + const int instance_idx = it - object->model_object()->instances.begin(); + const Transform3d& inst_transform = one_inst_only + ? Transform3d::Identity() + : object->model_object()->instances[instance_idx]->get_transformation().get_matrix(); + + TriangleMesh inst_mesh; + + if (!pad_mesh.empty()) { + TriangleMesh inst_pad_mesh = pad_mesh; + inst_pad_mesh.transform(inst_transform, is_left_handed); + inst_mesh.merge(inst_pad_mesh); + } + + if (!supports_mesh.empty()) { + TriangleMesh inst_supports_mesh = supports_mesh; + inst_supports_mesh.transform(inst_transform, is_left_handed); + inst_mesh.merge(inst_supports_mesh); + } + + TriangleMesh inst_object_mesh = object->get_mesh_to_print(); + inst_object_mesh.transform(mesh_trafo_inv); + inst_object_mesh.transform(inst_transform, is_left_handed); + + inst_mesh.merge(inst_object_mesh); + + // ensure that the instance lays on the bed + inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z()); + + // merge instance with global mesh + mesh.merge(inst_mesh); + + if (one_inst_only) + break; + } + } + } + + return mesh; + }; + + std::function + mesh_to_export; + + if (p->printer_technology == ptFFF ) + mesh_to_export = mesh_to_export_fff; + else + mesh_to_export = mesh_to_export_sla; + TriangleMesh mesh; if (selection_only) { const ModelObject* model_object = p->model.objects[obj_idx]; From f987caa63d60ef0e98ad376a7b424e0ad7054727 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 14 Dec 2022 09:55:09 +0100 Subject: [PATCH 30/75] Fix build in debug mode --- src/libslic3r/SLAPrintSteps.cpp | 4 ++-- src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index b3e47fe14f..630f6f85f4 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -415,8 +415,8 @@ static void filter_support_points_by_modifiers(sla::SupportPoints &pts, const SuppPtMask &mask, const std::vector &slice_grid) { - assert((blockers.empty() || blockers.size() == slice_grid.size()) && - (enforcers.empty() || enforcers.size() == slice_grid.size())); + assert((mask.blockers.empty() || mask.blockers.size() == slice_grid.size()) && + (mask.enforcers.empty() || mask.enforcers.size() == slice_grid.size())); auto new_pts = reserve_vector(pts.size()); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f3eae56335..734f6140ec 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6133,7 +6133,7 @@ void Plater::export_stl_obj(bool extended, bool selection_only) for (const SLAPrintObject::Instance& obj_instance : obj_instances) { auto it = std::find_if(object->model_object()->instances.begin(), object->model_object()->instances.end(), [&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; }); - assert(it != model_object->instances.end()); + assert(it != object->model_object()->instances.end()); if (it != object->model_object()->instances.end()) { const bool one_inst_only = selection_only && ! selection.is_single_full_object(); From a3403c51cf0955e334b98d12d5812a469caf6e65 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 14 Dec 2022 13:14:56 +0100 Subject: [PATCH 31/75] SLA mode: Updated object menu and "Change type" dialog + ObjectList: Suppress to add modifiers to the object --- src/slic3r/GUI/GUI_Factories.cpp | 100 +++++++----------------------- src/slic3r/GUI/GUI_Factories.hpp | 7 +-- src/slic3r/GUI/GUI_ObjectList.cpp | 63 ++++++++++++++----- src/slic3r/GUI/Plater.cpp | 1 - 4 files changed, 72 insertions(+), 99 deletions(-) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 0250d2164b..6dece9a224 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -532,8 +532,12 @@ void MenuFactory::append_menu_item_add_text(wxMenu* menu, ModelVolumeType type, } } -void MenuFactory::append_menu_items_add_volume(wxMenu* menu) +void MenuFactory::append_menu_items_add_volume(MenuType menu_type) { + wxMenu* menu = menu_type == mtObjectFFF ? &m_object_menu : menu_type == mtObjectSLA ? &m_sla_object_menu : nullptr; + if (!menu) + return; + // Update "add" items(delete old & create new) items popupmenu for (auto& item : ADD_VOLUME_MENU_ITEMS) { const wxString item_name = _(item.first); @@ -569,9 +573,11 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu) return; } - int type = 0; - for (auto& item : ADD_VOLUME_MENU_ITEMS) { - wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type++)); + for (size_t type = 0; type < ADD_VOLUME_MENU_ITEMS.size(); type++) { + auto& item = ADD_VOLUME_MENU_ITEMS[type]; + if (menu_type == mtObjectSLA && ModelVolumeType(type) == ModelVolumeType::PARAMETER_MODIFIER) + continue; + wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type)); append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, [type]() { bool can_add = type < size_t(ModelVolumeType::PARAMETER_MODIFIER) ? !obj_list()->is_selected_object_cut() : true; @@ -579,56 +585,8 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu) }, m_parent); } - append_menu_item_layers_editing(menu); -} - -void MenuFactory::append_menu_items_add_sla_volume(wxMenu *menu) -{ - // Update "add" items(delete old & create new) settings popupmenu - for (auto& item : ADD_VOLUME_MENU_ITEMS) { - const auto settings_id = menu->FindItem(_(item.first)); - if (settings_id != wxNOT_FOUND) - menu->Destroy(settings_id); - } - - const ConfigOptionMode mode = wxGetApp().get_mode(); - - if (mode == comAdvanced) { - append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].first), "", - [](wxCommandEvent&) { obj_list()->load_subobject(ModelVolumeType::MODEL_PART); }, - ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].second, nullptr, - []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); - } else { - auto& item = ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)]; - - wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::MODEL_PART); - append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, - []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); - } - - { - auto& item = ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::NEGATIVE_VOLUME)]; - - wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::NEGATIVE_VOLUME); - append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, - []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); - } - - { - auto& item = ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)]; - - wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::SUPPORT_ENFORCER); - append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, - []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); - } - - { - auto& item = ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)]; - - wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType::SUPPORT_BLOCKER); - append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, - []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); - } + if (menu_type == mtObjectFFF) + append_menu_item_layers_editing(menu); } wxMenuItem* MenuFactory::append_menu_item_layers_editing(wxMenu* menu) @@ -1086,6 +1044,9 @@ void MenuFactory::create_common_object_menu(wxMenu* menu) append_menu_item_fix_through_netfabb(menu); append_menu_item_simplify(menu); append_menu_items_mirror(menu); + + append_menu_items_split(menu); + menu->AppendSeparator(); } void MenuFactory::append_menu_items_split(wxMenu *menu) @@ -1105,26 +1066,6 @@ void MenuFactory::append_menu_items_split(wxMenu *menu) []() { return plater()->can_split(true); }, m_parent); } -void MenuFactory::create_object_menu() -{ - create_common_object_menu(&m_object_menu); - - append_menu_items_split(&m_object_menu); - m_object_menu.AppendSeparator(); - - // "Height range Modifier" and "Add (volumes)" menu items will be added later in append_menu_items_add_volume() -} - -void MenuFactory::create_sla_object_menu() -{ - create_common_object_menu(&m_sla_object_menu); - append_menu_items_split(&m_sla_object_menu); - - m_sla_object_menu.AppendSeparator(); - append_menu_items_add_sla_volume(&m_sla_object_menu); - m_sla_object_menu.AppendSeparator(); -} - void MenuFactory::append_immutable_part_menu_items(wxMenu* menu) { append_menu_items_mirror(menu); @@ -1184,8 +1125,8 @@ void MenuFactory::init(wxWindow* parent) m_parent = parent; create_default_menu(); - create_object_menu(); - create_sla_object_menu(); + create_common_object_menu(&m_object_menu); + create_common_object_menu(&m_sla_object_menu); create_part_menu(); create_text_part_menu(); create_instance_menu(); @@ -1194,7 +1135,7 @@ void MenuFactory::init(wxWindow* parent) void MenuFactory::update() { update_default_menu(); - update_object_menu(); + update_objects_menu(); } wxMenu* MenuFactory::default_menu() @@ -1331,9 +1272,10 @@ void MenuFactory::update_menu_items_instance_manipulation(MenuType type) } } -void MenuFactory::update_object_menu() +void MenuFactory::update_objects_menu() { - append_menu_items_add_volume(&m_object_menu); + append_menu_items_add_volume(mtObjectFFF); + append_menu_items_add_volume(mtObjectSLA); } void MenuFactory::update_default_menu() diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 34725d6c14..515311d0de 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -41,7 +41,7 @@ public: void init(wxWindow* parent); void update(); - void update_object_menu(); + void update_objects_menu(); void update_default_menu(); void sys_color_changed(); @@ -79,8 +79,6 @@ private: void create_default_menu(); void create_common_object_menu(wxMenu *menu); - void create_object_menu(); - void create_sla_object_menu(); void append_immutable_part_menu_items(wxMenu* menu); void append_mutable_part_menu_items(wxMenu* menu); void create_part_menu(); @@ -89,8 +87,7 @@ private: wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type); void append_menu_item_add_text(wxMenu* menu, ModelVolumeType type, bool is_submenu_item = true); - void append_menu_items_add_volume(wxMenu* menu); - void append_menu_items_add_sla_volume(wxMenu* menu); + void append_menu_items_add_volume(MenuType type); wxMenuItem* append_menu_item_layers_editing(wxMenu* menu); wxMenuItem* append_menu_item_settings(wxMenu* menu); wxMenuItem* append_menu_item_change_type(wxMenu* menu); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 8c15464100..a5ddcf94c5 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2992,7 +2992,8 @@ wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, st int volume_idx{ -1 }; for (const ModelVolume* volume : object->volumes) { ++volume_idx; - if (object->is_cut() && volume->is_cut_connector()) + if ((object->is_cut() && volume->is_cut_connector()) || + (printer_technology() == ptSLA && volume->type() == ModelVolumeType::PARAMETER_MODIFIER)) continue; const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, from_u8(volume->name), @@ -4262,17 +4263,32 @@ void ObjectList::change_part_type() } const bool is_cut_object = obj->is_cut(); - wxArrayString names; - if (!is_cut_object) - for (const wxString& type : { _L("Part"), _L("Negative Volume") }) - names.Add(type); - names.Add(_L("Modifier")); - if (!volume->text_configuration.has_value()) - for (const wxString& name : { _L("Support Blocker"), _L("Support Enforcer") }) + wxArrayString names; + std::vector types; + types.reserve(5); + if (!is_cut_object) { + for (const wxString& name : { _L("Part"), _L("Negative Volume") }) names.Add(name); + for (const ModelVolumeType type_id : { ModelVolumeType::MODEL_PART, ModelVolumeType::NEGATIVE_VOLUME }) + types.emplace_back(type_id); + } - const int type_shift = is_cut_object ? 2 : 0; - auto new_type = ModelVolumeType(type_shift + wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, int(type) - type_shift)); + if (printer_technology() != ptSLA) { + names.Add(_L("Modifier")); + types.emplace_back(ModelVolumeType::PARAMETER_MODIFIER); + } + + if (!volume->text_configuration.has_value()) { + for (const wxString& name : { _L("Support Blocker"), _L("Support Enforcer") }) + names.Add(name); + for (const ModelVolumeType type_id : { ModelVolumeType::SUPPORT_BLOCKER, ModelVolumeType::SUPPORT_ENFORCER }) + types.emplace_back(type_id); + } + + int selection = 0; + if (auto it = std::find(types.begin(), types.end(), type); it != types.end()) + selection = it - types.begin(); + const auto new_type = types[wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, selection)]; if (new_type == type || new_type == ModelVolumeType::INVALID) return; @@ -4357,8 +4373,9 @@ void ObjectList::update_object_list_by_printer_technology() m_objects_model->GetChildren(wxDataViewItem(nullptr), object_items); for (auto& object_item : object_items) { + const int obj_idx = m_objects_model->GetObjectIdByItem(object_item); // update custom supports info - update_info_items(m_objects_model->GetObjectIdByItem(object_item), &sel); + update_info_items(obj_idx, &sel); // Update Settings Item for object update_settings_item_and_selection(object_item, sel); @@ -4366,10 +4383,26 @@ void ObjectList::update_object_list_by_printer_technology() // Update settings for Volumes wxDataViewItemArray all_object_subitems; m_objects_model->GetChildren(object_item, all_object_subitems); + + bool was_selected_some_subitem = false; for (auto item : all_object_subitems) - if (m_objects_model->GetItemType(item) & itVolume) - // update settings for volume - update_settings_item_and_selection(item, sel); + if (m_objects_model->GetItemType(item) & itVolume) { + if (sel.Index(item) != wxNOT_FOUND) { + sel.Remove(item); + was_selected_some_subitem = true; + } + else if (const wxDataViewItem vol_settings_item = m_objects_model->GetSettingsItem(item); + sel.Index(vol_settings_item) != wxNOT_FOUND) { + sel.Remove(vol_settings_item); + was_selected_some_subitem = true; + break; + } + } + if (was_selected_some_subitem) + sel.Add(object_item); + + // Update volumes list in respect to the print mode + add_volumes_to_object_in_list(obj_idx); // Update Layers Items wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(object_item); @@ -4411,6 +4444,8 @@ void ObjectList::update_object_list_by_printer_technology() // restore selection: SetSelections(sel); m_prevent_canvas_selection_update = false; + + update_selections_on_canvas(); } void ObjectList::instances_to_separated_object(const int obj_idx, const std::set& inst_idxs) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 734f6140ec..8a9e934853 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1529,7 +1529,6 @@ void Sidebar::update_mode() p->object_list->unselect_objects(); p->object_list->update_selections(); -// p->object_list->update_object_menu(); Layout(); } From 3f7dcf744bde9da5f114779ed095b88343bf0e9e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 14 Dec 2022 18:19:04 +0100 Subject: [PATCH 32/75] wip on support points elevation bug --- src/slic3r/GUI/GLCanvas3D.cpp | 18 +++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 7 +++++++ src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 2 ++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 23e974a558..a119e09387 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1940,15 +1940,15 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } - if (printer_technology == ptSLA) { - // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed - for (GLVolume* volume : m_volumes.volumes) - if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { - const SLAPrintObject* po = sla_print()->get_print_object_by_model_object_id(volume->object_idx()); - if (po != nullptr) - volume->set_sla_shift_z(po->get_current_elevation() / sla_print()->relative_correction().z()); - } - } +// if (printer_technology == ptSLA) { +// // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed +// for (GLVolume* volume : m_volumes.volumes) +// if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { +// const SLAPrintObject* po = sla_print()->objects()[volume->object_idx()];//sla_print()->get_print_object_by_model_object_id(volume->object_idx()); +//// if (po != nullptr) +//// volume->set_sla_shift_z(po->get_current_elevation() / sla_print()->relative_correction().z()); +// } +// } if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) { // Should the wipe tower be visualized ? diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp index 7bcc01b719..9b5e9f4695 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.hpp @@ -42,6 +42,8 @@ protected: bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); + const GLVolumeCollection &volumes() const { return m_volumes; } + private: GLVolumeCollection m_volumes; bool m_input_enabled{ false }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 01d01f1b85..d41120b116 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -129,6 +129,13 @@ void SelectionInfo::on_release() m_model_volume = nullptr; } +ModelInstance *SelectionInfo::model_instance() const +{ + int inst_idx = get_active_instance(); + return int(m_model_object->instances.size()) < inst_idx ? + m_model_object->instances[get_active_instance()] : nullptr; +} + int SelectionInfo::get_active_instance() const { return get_pool()->get_canvas()->get_selection().get_instance_idx(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 2ae30a71bc..21e1bb73cf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -9,6 +9,7 @@ namespace Slic3r { class ModelObject; +class ModelInstance; class SLAPrintObject; class ModelVolume; @@ -160,6 +161,7 @@ public: const SLAPrintObject *print_object() const { return m_print_object; } // Returns a non-null pointer if the selection is a single volume ModelVolume* model_volume() const { return m_model_volume; } + ModelInstance *model_instance() const; int get_active_instance() const; float get_sla_shift() const { return m_z_shift; } From cf63c60c22b758210b83ac910bf68828e580d3c2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 14 Dec 2022 18:47:56 +0100 Subject: [PATCH 33/75] Support points trafo seems to be OK now --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 19 +++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 45013744ff..3629126075 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -148,8 +148,15 @@ void GLGizmoSlaSupports::render_points(const Selection& selection) shader->start_using(); ScopeGuard guard([shader]() { shader->stop_using(); }); - const GLVolume* vol = selection.get_first_volume(); - const Geometry::Transformation transformation(vol->world_matrix()); + auto *inst = m_c->selection_info()->model_instance(); + if (!inst) + return; + + double shift_z = m_c->selection_info()->print_object()->get_current_elevation(); + Transform3d trafo(inst->get_transformation().get_matrix()); + trafo.translate(Vec3d{0., 0., shift_z}); + const Geometry::Transformation transformation{trafo}; + #if ENABLE_WORLD_COORDINATE const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); #else @@ -197,7 +204,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection) // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. const Transform3d support_matrix = Geometry::translation_transform(support_point.pos.cast()) * instance_scaling_matrix_inverse; - if (vol->is_left_handed()) + if (transformation.is_left_handed()) glsafe(::glFrontFace(GL_CW)); // Matrices set, we can render the point mark now. @@ -210,7 +217,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection) Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); const Eigen::AngleAxisd aa(q); - const Transform3d model_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * + const Transform3d model_matrix = transformation.get_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * Geometry::translation_transform((CONE_HEIGHT + support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ()) * Geometry::rotation_transform({ double(PI), 0.0, 0.0 }) * Geometry::scale_transform({ CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT }); @@ -221,13 +228,13 @@ void GLGizmoSlaSupports::render_points(const Selection& selection) } const double radius = (double)support_point.head_front_radius * RenderPointScale; - const Transform3d model_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius); + const Transform3d model_matrix = transformation.get_matrix() * support_matrix * Geometry::scale_transform(radius); shader->set_uniform("view_model_matrix", view_matrix * model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); m_sphere.model.render(); - if (vol->is_left_handed()) + if (transformation.is_left_handed()) glsafe(::glFrontFace(GL_CCW)); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index d41120b116..f0734b445e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -132,7 +132,7 @@ void SelectionInfo::on_release() ModelInstance *SelectionInfo::model_instance() const { int inst_idx = get_active_instance(); - return int(m_model_object->instances.size()) < inst_idx ? + return inst_idx < int(m_model_object->instances.size()) ? m_model_object->instances[get_active_instance()] : nullptr; } From 45fffed309dac9c7b1641b6f31c0e1c03fe35d69 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 15 Dec 2022 09:50:11 +0100 Subject: [PATCH 34/75] Remove leftover commented code --- src/slic3r/GUI/GLCanvas3D.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a119e09387..2aa96369db 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1940,16 +1940,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } -// if (printer_technology == ptSLA) { -// // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed -// for (GLVolume* volume : m_volumes.volumes) -// if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { -// const SLAPrintObject* po = sla_print()->objects()[volume->object_idx()];//sla_print()->get_print_object_by_model_object_id(volume->object_idx()); -//// if (po != nullptr) -//// volume->set_sla_shift_z(po->get_current_elevation() / sla_print()->relative_correction().z()); -// } -// } - if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) { // Should the wipe tower be visualized ? unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); From 999c49c8e814c075ec4e82daed4bb314712c8495 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 15 Dec 2022 11:43:39 +0100 Subject: [PATCH 35/75] Remove leftover comments in SLAPrint --- src/libslic3r/SLAPrint.cpp | 51 -------------------------------------- src/libslic3r/SLAPrint.hpp | 9 ------- 2 files changed, 60 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 89c881ebf6..dc05148fc9 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -994,44 +994,6 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const return idx >= v.size() ? EMPTY_SLICE : v[idx]; } -//bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const -//{ -// switch (step) { -// case slaposAssembly: -// return true; -// case slaposObjectSlice: -// return ! this->m_mesh_from_slices.empty(); -// case slaposDrillHoles: -// return ! this->m_mesh_from_slices.empty(); -// case slaposSupportTree: -// return ! this->support_mesh().empty(); -// case slaposPad: -// return ! this->pad_mesh().empty(); -// default: -// return false; -// } -//} - -//TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const -//{ -// switch (step) { -// case slaposAssembly: -// return m_transformed_rmesh; -// case slaposObjectSlice: -// return this->m_mesh_from_slices; -// case slaposSupportTree: -// return this->support_mesh(); -// case slaposPad: -// return this->pad_mesh(); -// case slaposDrillHoles: -//// if (m_hollowing_data) -// return get_mesh_to_print(); -// [[fallthrough]]; -// default: -// return TriangleMesh(); -// } -//} - const TriangleMesh& SLAPrintObject::support_mesh() const { if (m_config.supports_enable.getBool() && @@ -1068,19 +1030,6 @@ const TriangleMesh &SLAPrintObject::get_mesh_to_print() const return *ret; } -//const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const -//{ -// if (m_hollowing_data && m_hollowing_data->interior && -// m_config.hollowing_enable.getBool()) -// return sla::get_mesh(*m_hollowing_data->interior); - -// return EMPTY_TRIANGLE_SET; -//} - -//const TriangleMesh &SLAPrintObject::transformed_mesh() const { -// return m_transformed_rmesh; -//} - sla::SupportPoints SLAPrintObject::transformed_support_points() const { assert(m_model_object != nullptr); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 37aeb7b1cc..6379575bf8 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -145,26 +145,17 @@ public: }; const std::vector& instances() const { return m_instances; } -// bool has_mesh(SLAPrintObjectStep step) const; -// TriangleMesh get_mesh(SLAPrintObjectStep step) const; - // Get a support mesh centered around origin in XY, and with zero rotation around Z applied. // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true. const TriangleMesh& support_mesh() const; // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied. // Support mesh is only valid if this->is_step_done(slaposPad) is true. const TriangleMesh& pad_mesh() const; - -// // Ready after this->is_step_done(slaposDrillHoles) is true -// const indexed_triangle_set &hollowed_interior_mesh() const; // Get the mesh that is going to be printed with all the modifications // like hollowing and drilled holes. const TriangleMesh & get_mesh_to_print() const; -// // This will return the transformed mesh which is cached -// const TriangleMesh& transformed_mesh() const; - sla::SupportPoints transformed_support_points() const; sla::DrainHoles transformed_drainhole_points() const; From 473c8a26a409fa66f786c6fea3c32e4295c0132d Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 15 Dec 2022 11:21:41 +0100 Subject: [PATCH 36/75] Suppress to switch to SLA-technology printer, if an object has a modifier(s) + WIP: ptSLA : Hide modifiers on 3DScene. Waiting for @enricoturri1966 review/reworking --- src/libslic3r/Model.cpp | 9 +++++++++ src/libslic3r/Model.hpp | 3 +++ src/slic3r/GUI/GLCanvas3D.cpp | 6 ++++++ src/slic3r/GUI/GUI_App.cpp | 9 +++++++++ src/slic3r/GUI/Selection.cpp | 32 ++++++++++++++++++++++++++++++-- src/slic3r/GUI/Tab.cpp | 4 ++-- 6 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 76668d0d2b..6572f7484f 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2586,6 +2586,15 @@ bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObjec [](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.mmu_segmentation_facets.timestamp_matches(mv_new.mmu_segmentation_facets); }); } +bool model_has_parameter_modifiers_in_objects(const Model &model) +{ + for (const auto& model_object : model.objects) + for (const auto& volume : model_object->volumes) + if (volume->is_modifier()) + return true; + return false; +} + bool model_has_multi_part_objects(const Model &model) { for (const ModelObject *model_object : model.objects) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 1cd6c34143..74d95b20fa 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -1388,6 +1388,9 @@ bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo // The function assumes that volumes list is synchronized. extern bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new); +// If the model has object(s) which contains a modofoer, then it is currently not supported by the SLA mode. +// Either the model cannot be loaded, or a SLA printer has to be activated. +bool model_has_parameter_modifiers_in_objects(const Model& model); // If the model has multi-part objects, then it is currently not supported by the SLA mode. // Either the model cannot be loaded, or a SLA printer has to be activated. bool model_has_multi_part_objects(const Model &model); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2aa96369db..ea6376463a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2049,6 +2049,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re raycaster->set_active(v->is_active); } + for (GLVolume* volume : m_volumes.volumes) + if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) { + if (volume->is_modifier && m_model->objects[volume->object_idx()]->volumes[volume->volume_idx()]->is_modifier()) + volume->is_active = printer_technology != ptSLA; + } + // refresh gizmo elements raycasters for picking GLGizmoBase* curr_gizmo = m_gizmos.get_current(); if (curr_gizmo != nullptr) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 161784d959..5f5d0384ab 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2884,6 +2884,14 @@ void GUI_App::open_web_page_localized(const std::string &http_address) // Because of we can't to print the multi-part objects with SLA technology. bool GUI_App::may_switch_to_SLA_preset(const wxString& caption) { + if (model_has_parameter_modifiers_in_objects(model())) { + show_info(nullptr, + _L("It's impossible to print object(s) which contains parameter modifiers with SLA technology.") + "\n\n" + + _L("Please check your object list before preset changing."), + caption); + return false; + } +/* if (model_has_multi_part_objects(model())) { show_info(nullptr, _L("It's impossible to print multi-part object(s) with SLA technology.") + "\n\n" + @@ -2898,6 +2906,7 @@ bool GUI_App::may_switch_to_SLA_preset(const wxString& caption) caption); return false; } +*/ return true; } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 31e09003c9..a70698b438 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -157,6 +157,11 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec return; const GLVolume* volume = (*m_volumes)[volume_idx]; + + if (wxGetApp().plater()->printer_technology() == ptSLA && volume->is_modifier && + m_model->objects[volume->object_idx()]->volumes[volume->volume_idx()]->is_modifier()) + return; + // wipe tower is already selected if (is_wipe_tower() && volume->is_wipe_tower) return; @@ -482,8 +487,14 @@ void Selection::instances_changed(const std::vector &instance_ids_select assert(m_valid); assert(m_mode == Instance); m_list.clear(); + + const PrinterTechnology pt = wxGetApp().plater()->printer_technology(); + for (unsigned int volume_idx = 0; volume_idx < (unsigned int)m_volumes->size(); ++ volume_idx) { const GLVolume *volume = (*m_volumes)[volume_idx]; + if (pt == ptSLA && volume->is_modifier && + m_model->objects[volume->object_idx()]->volumes[volume->volume_idx()]->is_modifier()) + continue; auto it = std::lower_bound(instance_ids_selected.begin(), instance_ids_selected.end(), volume->geometry_id.second); if (it != instance_ids_selected.end() && *it == volume->geometry_id.second) this->do_add_volume(volume_idx); @@ -1940,9 +1951,16 @@ std::vector Selection::get_volume_idxs_from_object(unsigned int ob { std::vector idxs; + const PrinterTechnology pt = wxGetApp().plater()->printer_technology(); + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { - if ((*m_volumes)[i]->object_idx() == (int)object_idx) + const GLVolume* v = (*m_volumes)[i]; + if (v->object_idx() == (int)object_idx) { + if (pt == ptSLA && v->is_modifier && + m_model->objects[object_idx]->volumes[v->volume_idx()]->is_modifier()) + continue; idxs.push_back(i); + } } return idxs; @@ -1952,8 +1970,13 @@ std::vector Selection::get_volume_idxs_from_instance(unsigned int { std::vector idxs; + const PrinterTechnology pt = wxGetApp().plater()->printer_technology(); + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; + if (pt == ptSLA && v->is_modifier && + m_model->objects[object_idx]->volumes[i]->is_modifier()) + continue; if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx) idxs.push_back(i); } @@ -2911,7 +2934,12 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const return false; unsigned int count = (unsigned int)std::count_if(m_list.begin(), m_list.end(), SameInstance(object_idx, volume->instance_idx(), *m_volumes)); - return count == (unsigned int)m_model->objects[object_idx]->volumes.size(); + + PrinterTechnology pt = wxGetApp().plater()->printer_technology(); + const ModelVolumePtrs& volumes = m_model->objects[object_idx]->volumes; + const unsigned int vol_cnt = (unsigned int)std::count_if(volumes.begin(), volumes.end(), [pt](const ModelVolume* volume) { return pt == ptFFF || !volume->is_modifier(); }); + + return count == vol_cnt; } void Selection::paste_volumes_from_clipboard() diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d36c25d39f..21529579b6 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3279,9 +3279,9 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, const PresetWithVendorProfile new_printer_preset_with_vendor_profile = m_presets->get_preset_with_vendor_profile(new_printer_preset); PrinterTechnology old_printer_technology = m_presets->get_edited_preset().printer_technology(); PrinterTechnology new_printer_technology = new_printer_preset.printer_technology(); - /*if (new_printer_technology == ptSLA && old_printer_technology == ptFFF && !wxGetApp().may_switch_to_SLA_preset(_L("New printer preset selected"))) + if (new_printer_technology == ptSLA && old_printer_technology == ptFFF && !wxGetApp().may_switch_to_SLA_preset(_L("New printer preset selected"))) canceled = true; - else */{ + else { struct PresetUpdate { Preset::Type tab_type; PresetCollection *presets; From fa2fef9283f79e9f6806340a9169244b249a8ed4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 15 Dec 2022 12:27:53 +0100 Subject: [PATCH 37/75] Optimize csg slicing with large number of positive parts --- src/libslic3r/CSGMesh/SliceCSGMesh.hpp | 36 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp index f8a7354204..76ca22ec45 100644 --- a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -4,6 +4,7 @@ #include "CSGMesh.hpp" #include "libslic3r/TriangleMeshSlicer.hpp" #include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/Execution/ExecutionTBB.hpp" namespace Slic3r { namespace csg { @@ -18,6 +19,8 @@ std::vector slice_csgmesh_ex( MeshSlicingParamsEx params_cpy = params; auto trafo = params.trafo; + auto nonempty_indices = reserve_vector(slicegrid.size()); + for (const auto &m : csg) { const indexed_triangle_set *its = csg::get_mesh(m); if (!its) @@ -30,13 +33,18 @@ std::vector slice_csgmesh_ex( assert(slices.size() == slicegrid.size()); + nonempty_indices.clear(); for (size_t i = 0; i < slicegrid.size(); ++i) { + if (get_operation(m) == CSGType::Intersection || !slices[i].empty()) + nonempty_indices.emplace_back(i); + } + + auto mergefn = [&m, &slices, &ret](size_t i){ switch(get_operation(m)) { case CSGType::Union: for (ExPolygon &expoly : slices[i]) ret[i].emplace_back(std::move(expoly)); - ret[i] = union_ex(ret[i]); break; case CSGType::Difference: ret[i] = diff_ex(ret[i], slices[i]); @@ -45,19 +53,25 @@ std::vector slice_csgmesh_ex( ret[i] = intersection_ex(ret[i], slices[i]); break; } - } + }; - for (ExPolygons &slice : ret) { - auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){ - return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON); - }); - - // Hopefully, ExPolygons are moved, not copied to new positions - // and that is cheap for expolygons - slice.erase(it, slice.end()); - } + execution::for_each(ex_tbb, + nonempty_indices.begin(), nonempty_indices.end(), + mergefn, + execution::max_concurrency(ex_tbb)); } + execution::for_each(ex_tbb, ret.begin(), ret.end(), [](ExPolygons &slice) { + auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){ + return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON); + }); + + // Hopefully, ExPolygons are moved, not copied to new positions + // and that is cheap for expolygons + slice.erase(it, slice.end()); + slice = union_ex(slice); + }, execution::max_concurrency(ex_tbb)); + return ret; } From 6323e27cdca4d3fabc2ee22d78dd7efb23adcadb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 15 Dec 2022 13:05:02 +0100 Subject: [PATCH 38/75] csg voxelization speedup with additional parallelism --- src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp index f1fd739815..0f2e8c898d 100644 --- a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -5,6 +5,7 @@ #include "CSGMesh.hpp" #include "libslic3r/OpenVDBUtils.hpp" +#include "libslic3r/Execution/ExecutionTBB.hpp" namespace Slic3r { namespace csg { @@ -32,11 +33,24 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, { VoxelGridPtr ret; + std::vector grids (csgrange.size()); + + execution::for_each(ex_tbb, size_t(0), csgrange.size(), [&](size_t csgidx) { + if (params.statusfn() && params.statusfn()(-1)) + return; + + auto it = csgrange.begin(); + std::advance(it, csgidx); + auto &csgpart = *it; + grids[csgidx] = get_voxelgrid(csgpart, params); + }, execution::max_concurrency(ex_tbb)); + + size_t csgidx = 0; for (auto &csgpart : csgrange) { if (params.statusfn() && params.statusfn()(-1)) break; - VoxelGridPtr partgrid = get_voxelgrid(csgpart, params); + auto &partgrid = grids[csgidx++]; if (!ret && get_operation(csgpart) == CSGType::Union) { ret = std::move(partgrid); From 7832d600ff095a41845be37afa3244bded4582ab Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 15 Dec 2022 13:21:57 +0100 Subject: [PATCH 39/75] CutGizmo: Allow to add connectors for SLA --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 29d578fba9..599a54b3f3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1627,12 +1627,10 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) if (auto oc = m_c->object_clipper(); oc && m_is_contour_changed) oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); - if (wxGetApp().plater()->printer_technology() == ptFFF) { - m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); - if (m_imgui->button(_L("Add/Edit connectors"))) - set_connectors_editing(true); - m_imgui->disabled_end(); - } + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); + if (m_imgui->button(_L("Add/Edit connectors"))) + set_connectors_editing(true); + m_imgui->disabled_end(); ImGui::Separator(); @@ -1755,7 +1753,7 @@ void GLGizmoCut3D::render_input_window_warning() const { if (m_is_contour_changed) return; - if (wxGetApp().plater()->printer_technology() == ptFFF && m_has_invalid_connector) { + if (m_has_invalid_connector) { wxString out = wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected") + ":"; if (m_info_stats.outside_cut_contour > size_t(0)) out += "\n - " + format_wxstr(_L_PLURAL("%1$d connector is out of cut contour", "%1$d connectors are out of cut contour", m_info_stats.outside_cut_contour), From fdf51c8a5e49b6ee403722eef83288feab71687a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 15 Dec 2022 13:50:19 +0100 Subject: [PATCH 40/75] Another fix for support points trafo --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 3629126075..b117a2bebb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -153,8 +153,8 @@ void GLGizmoSlaSupports::render_points(const Selection& selection) return; double shift_z = m_c->selection_info()->print_object()->get_current_elevation(); - Transform3d trafo(inst->get_transformation().get_matrix()); - trafo.translate(Vec3d{0., 0., shift_z}); + Transform3d trafo(inst->get_transformation().get_matrix() * inst->get_object()->volumes.front()->get_matrix()); + trafo.translation()(2) += shift_z; const Geometry::Transformation transformation{trafo}; #if ENABLE_WORLD_COORDINATE From b8b462df5ef772d679d9845017d6991d450f0606 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 15 Dec 2022 14:07:01 +0100 Subject: [PATCH 41/75] Fix trafo for drillholes --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index a209b51de7..ec6ea0a1ff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -117,11 +117,17 @@ void GLGizmoHollow::render_points(const Selection& selection) shader->start_using(); ScopeGuard guard([shader]() { shader->stop_using(); }); - const GLVolume* vol = selection.get_first_volume(); - const Transform3d trafo = vol->world_matrix(); + auto *inst = m_c->selection_info()->model_instance(); + if (!inst) + return; + + double shift_z = m_c->selection_info()->print_object()->get_current_elevation(); + Transform3d trafo(inst->get_transformation().get_matrix() * inst->get_object()->volumes.front()->get_matrix()); + trafo.translation()(2) += shift_z; + const Geometry::Transformation transformation{trafo}; #if ENABLE_WORLD_COORDINATE - const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_scaling_factor_matrix().inverse(); + const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); #else const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); #endif // ENABLE_WORLD_COORDINATE @@ -152,7 +158,7 @@ void GLGizmoHollow::render_points(const Selection& selection) // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. const Transform3d hole_matrix = Geometry::translation_transform(drain_hole.pos.cast()) * instance_scaling_matrix_inverse; - if (vol->is_left_handed()) + if (transformation.is_left_handed()) glsafe(::glFrontFace(GL_CW)); // Matrices set, we can render the point mark now. @@ -166,7 +172,7 @@ void GLGizmoHollow::render_points(const Selection& selection) shader->set_uniform("view_normal_matrix", view_normal_matrix); m_cylinder.model.render(); - if (vol->is_left_handed()) + if (transformation.is_left_handed()) glsafe(::glFrontFace(GL_CCW)); } } From 9606473c16f1e877f235c1519b9a4b30c2ce617b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 15 Dec 2022 16:06:56 +0100 Subject: [PATCH 42/75] Try to fix build on msvc (perl xs) --- src/libslic3r/Execution/ExecutionTBB.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Execution/ExecutionTBB.hpp b/src/libslic3r/Execution/ExecutionTBB.hpp index 2250b8e322..db4ee243fd 100644 --- a/src/libslic3r/Execution/ExecutionTBB.hpp +++ b/src/libslic3r/Execution/ExecutionTBB.hpp @@ -53,7 +53,7 @@ public: I to, const T &init, MergeFn &&mergefn, - AccessFn &&access, + AccessFn &&accessfn, size_t granularity = 1 ) { @@ -61,7 +61,7 @@ public: tbb::blocked_range{from, to, granularity}, init, [&](const auto &range, T subinit) { T acc = subinit; - loop_(range, [&](auto &i) { acc = mergefn(acc, access(i)); }); + loop_(range, [&](auto &i) { acc = mergefn(acc, accessfn(i)); }); return acc; }, std::forward(mergefn)); From 743d431574ff5083099f1e0b7bb46926f4fcbc10 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 16 Dec 2022 10:49:37 +0100 Subject: [PATCH 43/75] Remove redundant bb calculation in AABBTree --- src/libslic3r/AABBMesh.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libslic3r/AABBMesh.cpp b/src/libslic3r/AABBMesh.cpp index b6dc68aaea..ca7042c601 100644 --- a/src/libslic3r/AABBMesh.cpp +++ b/src/libslic3r/AABBMesh.cpp @@ -68,8 +68,6 @@ public: template void AABBMesh::init(const M &mesh, bool calculate_epsilon) { - BoundingBoxf3 bb = bounding_box(mesh); - // Build the AABB accelaration tree m_aabb->init(*m_tm, calculate_epsilon); } From e8faa71fcf36530ff218203fa872f23297f5eb5f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 19 Dec 2022 12:41:45 +0100 Subject: [PATCH 44/75] Decrease quality of preview meshes to improve processing time --- src/libslic3r/SLAPrintSteps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 630f6f85f4..ff827b5758 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -138,7 +138,7 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( bench.start(); // update preview mesh - double vscale = 1. / po.m_config.layer_height.getFloat(); + double vscale = 1. / (1.5 * po.m_config.layer_height.getFloat()); auto voxparams = csg::VoxelizeParams{} .voxel_scale(vscale) .exterior_bandwidth(1.f) From f997609db6607e77c4885592ff50e3aeb01b00d1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 19 Dec 2022 14:15:38 +0100 Subject: [PATCH 45/75] Fix stl export of object with unwanted pad --- src/slic3r/GUI/Plater.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8a9e934853..dbdd24d151 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6122,7 +6122,6 @@ void Plater::export_stl_obj(bool extended, bool selection_only) const bool is_left_handed = object->is_left_handed(); auto pad_mesh = extended? object->pad_mesh() : TriangleMesh{}; - pad_mesh = object->pad_mesh(); pad_mesh.transform(mesh_trafo_inv); auto supports_mesh = extended ? object->support_mesh() : TriangleMesh{}; From faf4d8e6b0d5d59468caef9c3bc075b58f7cc622 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 19 Dec 2022 18:49:53 +0100 Subject: [PATCH 46/75] Fix triangle orientation after openvdb mesh generation --- src/libslic3r/OpenVDBUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 2a94d38e0a..44a5b172ed 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -49,7 +49,7 @@ template VoxelGridPtr make_voxelgrid<>(); inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast(); } -inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } +inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[2]), int(v[1]), int(v[0])}; } class TriangleMeshDataAdapter { public: @@ -170,7 +170,7 @@ indexed_triangle_set grid_to_mesh(const VoxelGrid &vgrid, for (auto &v : triangles) ret.indices.emplace_back(to_vec3i(v)); for (auto &quad : quads) { ret.indices.emplace_back(quad(2), quad(1), quad(0)); - ret.indices.emplace_back(quad(0), quad(3), quad(2)); + ret.indices.emplace_back(quad(3), quad(2), quad(0)); } return ret; From 8470d4594de589e7b5eda0fd710c693ff99e2f40 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 19 Dec 2022 18:50:16 +0100 Subject: [PATCH 47/75] Fix crash in debug mode with empty preview --- src/slic3r/GUI/GLCanvas3D.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ea6376463a..419e5fbc96 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6648,14 +6648,17 @@ void GLCanvas3D::_load_sla_shells() for (const SLAPrintObject* obj : print->objects()) { unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); for (const SLAPrintObject::Instance& instance : obj->instances()) { - add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); - // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when - // through the update_volumes_colors_by_extruder() call. - m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); - if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty()) - add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true); - if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty()) - add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); + auto & m = obj->get_mesh_to_print(); + if (!m.empty()) { + add_volume(*obj, 0, instance, m, GLVolume::MODEL_COLOR[0], true); + // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when + // through the update_volumes_colors_by_extruder() call. + m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); + if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty()) + add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true); + if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty()) + add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); + } } double shift_z = obj->get_current_elevation(); for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { From 495a3f35573c2b723257230272727a36fc15dfc4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 20 Dec 2022 11:25:48 +0100 Subject: [PATCH 48/75] Fix unshifted cut slices in sla gizmos --- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index f0734b445e..ac3750871e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -119,7 +119,7 @@ void SelectionInfo::on_update() if (m_model_object) m_print_object = get_pool()->get_canvas()->sla_print()->get_print_object_by_model_object_id(m_model_object->id()); - m_z_shift = selection.get_first_volume()->get_sla_shift_z(); + m_z_shift = m_print_object ? m_print_object->get_current_elevation() : selection.get_first_volume()->get_sla_shift_z(); } } From d3a4edd37f3d494c8f894afe05bda667ae4decfd Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 20 Dec 2022 12:51:04 +0100 Subject: [PATCH 49/75] Skip voxelization for csg meshes with positive parts only. --- src/libslic3r/SLAPrintSteps.cpp | 47 ++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index ff827b5758..e92cc6c547 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -130,15 +130,49 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin } } +template bool is_all_positive(const Cont &csgmesh) +{ + bool is_all_pos = + std::all_of(csgmesh.begin(), + csgmesh.end(), + [](auto &part) { + return csg::get_operation(part) == csg::CSGType::Union; + }); + + return is_all_pos; +} + +template +static indexed_triangle_set csgmesh_merge_positive_parts(const Cont &csgmesh) +{ + indexed_triangle_set m; + for (auto &csgpart : csgmesh) { + auto op = csg::get_operation(csgpart); + const indexed_triangle_set * pmesh = csg::get_mesh(csgpart); + if (pmesh && op == csg::CSGType::Union) { + indexed_triangle_set mcpy = *pmesh; + its_transform(mcpy, csg::get_transform(csgpart)); + its_merge(m, mcpy); + } + } + + return m; +} + 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(); // update preview mesh - double vscale = 1. / (1.5 * po.m_config.layer_height.getFloat()); + double vscale = std::min(MaxPreviewVoxelScale, + 1. / po.m_config.layer_height.getFloat()); + auto voxparams = csg::VoxelizeParams{} .voxel_scale(vscale) .exterior_bandwidth(1.f) @@ -150,14 +184,13 @@ 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) { + if (r.size() > 1 && !is_all_positive(r)) { auto grid = csg::voxelize_csgmesh(r, voxparams); m = grid ? grid_to_mesh(*grid) : indexed_triangle_set{}; - } else if (!r.empty()) { - m = *(csg::get_mesh(*r.begin())); - auto tr = csg::get_transform(*r.begin()); - for (auto &v : m.vertices) - v = tr * v; +// float loss_less_max_error = 1e-6; +// its_quadric_edge_collapse(m, 0U, &loss_less_max_error); + } else { + m = csgmesh_merge_positive_parts(r); } bench.stop(); From 4fabd0b3072bb11f208b792b70f284be3dacc469 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 20 Dec 2022 12:51:55 +0100 Subject: [PATCH 50/75] Delete hollowing interior grid after the mesh is extracted It was needed for the triangle trimming but that is gone now --- src/libslic3r/SLAPrintSteps.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index e92cc6c547..215a3bdce1 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -277,7 +277,10 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); po.m_hollowing_data->interior = std::move(interior); - indexed_triangle_set &m = sla::get_mesh(*po.m_hollowing_data->interior); + indexed_triangle_set m = sla::get_mesh(*po.m_hollowing_data->interior); + + // Release the data, won't be needed anymore, takes huge amount of ram + po.m_hollowing_data->interior.reset(); if (!m.empty()) { // simplify mesh lossless @@ -291,8 +294,7 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) // Put the interior into the target mesh as a negative po.m_mesh_to_slice .emplace(slaposHollowing, - csg::CSGPart{&sla::get_mesh(*po.m_hollowing_data->interior), - csg::CSGType::Difference}); + csg::CSGPart{std::make_unique(std::move(m)), csg::CSGType::Difference}); generate_preview(po, slaposHollowing); } From 25ca46e3eb14b2349fba7b5c9980f99de94fe9ec Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 20 Dec 2022 17:38:46 +0100 Subject: [PATCH 51/75] Add support for csg operation stacking --- src/libslic3r/CSGMesh/CSGMesh.hpp | 12 +++ src/libslic3r/CSGMesh/ModelToCSGMesh.hpp | 41 ++++++-- src/libslic3r/CSGMesh/SliceCSGMesh.hpp | 117 ++++++++++++++++------ src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp | 49 +++++++-- src/libslic3r/SLAPrint.cpp | 2 +- src/libslic3r/SLAPrintSteps.cpp | 13 ++- 6 files changed, 185 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/CSGMesh/CSGMesh.hpp b/src/libslic3r/CSGMesh/CSGMesh.hpp index 4655f684a3..f30905066f 100644 --- a/src/libslic3r/CSGMesh/CSGMesh.hpp +++ b/src/libslic3r/CSGMesh/CSGMesh.hpp @@ -18,6 +18,7 @@ namespace Slic3r { namespace csg { // Supported CSG operation types enum class CSGType { Union, Difference, Intersection }; +enum class CSGStackOp { Push, Continue, Pop }; // Get the CSG operation of the part. Can be overriden for any type template CSGType get_operation(const CSGPartT &part) @@ -25,6 +26,11 @@ template CSGType get_operation(const CSGPartT &part) return part.operation; } +template CSGStackOp get_stack_operation(const CSGPartT &part) +{ + return part.stack_operation; +} + // Get the mesh for the part. Can be overriden for any type template const indexed_triangle_set *get_mesh(const CSGPartT &part) @@ -48,6 +54,11 @@ inline CSGType get_operation(const indexed_triangle_set &part) return CSGType::Union; } +inline CSGStackOp get_stack_operation(const indexed_triangle_set &part) +{ + return CSGStackOp::Continue; +} + inline const indexed_triangle_set * get_mesh(const indexed_triangle_set &part) { return ∂ @@ -63,6 +74,7 @@ struct CSGPart { AnyPtr its_ptr; Transform3f trafo; CSGType operation; + CSGStackOp stack_operation; CSGPart(AnyPtr ptr = {}, CSGType op = CSGType::Union, diff --git a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp index f7247ea706..3d1941294d 100644 --- a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp @@ -5,13 +5,15 @@ #include "libslic3r/Model.hpp" #include "libslic3r/SLA/Hollowing.hpp" +#include "libslic3r/MeshSplitImpl.hpp" namespace Slic3r { namespace csg { enum ModelParts { mpartsPositive = 1, mpartsNegative = 2, - mpartsDrillHoles = 4 + mpartsDrillHoles = 4, + mpartsDoSplits = 8 }; template @@ -22,20 +24,43 @@ void model_to_csgmesh(const ModelObject &mo, int parts_to_include = mpartsPositive ) { - bool do_positives = parts_to_include & mpartsPositive; - bool do_negatives = parts_to_include & mpartsNegative; + bool do_positives = parts_to_include & mpartsPositive; + bool do_negatives = parts_to_include & mpartsNegative; bool do_drillholes = parts_to_include & mpartsDrillHoles; + bool do_splits = parts_to_include & mpartsDoSplits; for (const ModelVolume *vol : mo.volumes) { if (vol && vol->mesh_ptr() && ((do_positives && vol->is_model_part()) || (do_negatives && vol->is_negative_volume()))) { - CSGPart part{&(vol->mesh().its), - vol->is_model_part() ? CSGType::Union : CSGType::Difference, - (trafo * vol->get_matrix()).cast()}; - *out = std::move(part); - ++out; + if (do_splits && its_is_splittable(vol->mesh().its)) { + CSGPart part_begin{{}, vol->is_model_part() ? CSGType::Union : CSGType::Difference}; + part_begin.stack_operation = CSGStackOp::Push; + *out = std::move(part_begin); + ++out; + + its_split(vol->mesh().its, SplitOutputFn{[&out, &vol, &trafo](indexed_triangle_set &&its) { + CSGPart part{std::make_unique(std::move(its)), + CSGType::Union, + (trafo * vol->get_matrix()).cast()}; + + *out = std::move(part); + ++out; + }}); + + CSGPart part_end{{}}; + part_end.stack_operation = CSGStackOp::Pop; + *out = std::move(part_end); + ++out; + } else { + CSGPart part{&(vol->mesh().its), + vol->is_model_part() ? CSGType::Union : CSGType::Difference, + (trafo * vol->get_matrix()).cast()}; + + *out = std::move(part); + ++out; + } } } diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp index 76ca22ec45..06302faa46 100644 --- a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -2,6 +2,9 @@ #define SLICECSGMESH_HPP #include "CSGMesh.hpp" + +#include + #include "libslic3r/TriangleMeshSlicer.hpp" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp" @@ -10,57 +13,107 @@ namespace Slic3r { namespace csg { template std::vector slice_csgmesh_ex( - const Range &csg, + const Range &csgrange, const std::vector &slicegrid, const MeshSlicingParamsEx ¶ms, const std::function &throw_on_cancel = [] {}) { - std::vector ret(slicegrid.size()); + struct Frame { CSGType op; std::vector slices; }; + + std::stack opstack{std::vector{}}; MeshSlicingParamsEx params_cpy = params; auto trafo = params.trafo; auto nonempty_indices = reserve_vector(slicegrid.size()); - for (const auto &m : csg) { - const indexed_triangle_set *its = csg::get_mesh(m); - if (!its) - continue; + if (!csgrange.empty() && csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) + opstack.push({CSGType::Union, std::vector(slicegrid.size())}); - params_cpy.trafo = trafo * csg::get_transform(m).template cast(); - std::vector slices = slice_mesh_ex(*its, - slicegrid, params_cpy, - throw_on_cancel); + for (const auto &csgpart : csgrange) { + const indexed_triangle_set *its = csg::get_mesh(csgpart); - assert(slices.size() == slicegrid.size()); + auto op = get_operation(csgpart); - nonempty_indices.clear(); - for (size_t i = 0; i < slicegrid.size(); ++i) { - if (get_operation(m) == CSGType::Intersection || !slices[i].empty()) - nonempty_indices.emplace_back(i); + if (get_stack_operation(csgpart) == CSGStackOp::Push) { + opstack.push({op, std::vector(slicegrid.size())}); + op = CSGType::Union; } - auto mergefn = [&m, &slices, &ret](size_t i){ - switch(get_operation(m)) { - case CSGType::Union: - for (ExPolygon &expoly : slices[i]) - ret[i].emplace_back(std::move(expoly)); + Frame *top = &opstack.top(); - break; - case CSGType::Difference: - ret[i] = diff_ex(ret[i], slices[i]); - break; - case CSGType::Intersection: - ret[i] = intersection_ex(ret[i], slices[i]); - break; + if (its) { + params_cpy.trafo = trafo * csg::get_transform(csgpart).template cast(); + std::vector slices = slice_mesh_ex(*its, + slicegrid, params_cpy, + throw_on_cancel); + + assert(slices.size() == slicegrid.size()); + + nonempty_indices.clear(); + for (size_t i = 0; i < slicegrid.size(); ++i) { + if (op == CSGType::Intersection || !slices[i].empty()) + nonempty_indices.emplace_back(i); } - }; - execution::for_each(ex_tbb, - nonempty_indices.begin(), nonempty_indices.end(), - mergefn, - execution::max_concurrency(ex_tbb)); + auto mergefn = [&csgpart, &slices, &top](size_t i){ + switch(get_operation(csgpart)) { + case CSGType::Union: + for (ExPolygon &expoly : slices[i]) + top->slices[i].emplace_back(std::move(expoly)); + + break; + case CSGType::Difference: + top->slices[i] = diff_ex(top->slices[i], slices[i]); + break; + case CSGType::Intersection: + top->slices[i] = intersection_ex(top->slices[i], slices[i]); + break; + } + }; + + execution::for_each(ex_tbb, + nonempty_indices.begin(), nonempty_indices.end(), + mergefn, + execution::max_concurrency(ex_tbb)); + } + + if (get_stack_operation(csgpart) == CSGStackOp::Pop) { + std::vector popslices = std::move(top->slices); + auto popop = opstack.top().op; + opstack.pop(); + std::vector &prev_slices = opstack.top().slices; + + nonempty_indices.clear(); + for (size_t i = 0; i < slicegrid.size(); ++i) { + if (popop == CSGType::Intersection || !popslices[i].empty()) + nonempty_indices.emplace_back(i); + } + + auto mergefn2 = [&popslices, &prev_slices, popop](size_t i){ + switch(popop) { + case CSGType::Union: + for (ExPolygon &expoly : popslices[i]) + prev_slices[i].emplace_back(std::move(expoly)); + + break; + case CSGType::Difference: + prev_slices[i] = diff_ex(prev_slices[i], popslices[i]); + break; + case CSGType::Intersection: + prev_slices[i] = intersection_ex(prev_slices[i], popslices[i]); + break; + } + }; + + execution::for_each(ex_tbb, + nonempty_indices.begin(), nonempty_indices.end(), + mergefn2, + execution::max_concurrency(ex_tbb)); + } } + std::vector ret = std::move(opstack.top().slices); + execution::for_each(ex_tbb, ret.begin(), ret.end(), [](ExPolygons &slice) { auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){ return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON); diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp index 0f2e8c898d..ac54cf9226 100644 --- a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -2,6 +2,7 @@ #define VOXELIZECSGMESH_HPP #include +#include #include "CSGMesh.hpp" #include "libslic3r/OpenVDBUtils.hpp" @@ -46,29 +47,65 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, }, execution::max_concurrency(ex_tbb)); size_t csgidx = 0; + 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({}); + for (auto &csgpart : csgrange) { if (params.statusfn() && params.statusfn()(-1)) break; auto &partgrid = grids[csgidx++]; - if (!ret && get_operation(csgpart) == CSGType::Union) { - ret = std::move(partgrid); - } else if (ret) { + auto op = get_operation(csgpart); + + if (get_stack_operation(csgpart) == CSGStackOp::Push) { + opstack.push({op, nullptr}); + op = CSGType::Union; + } + + Frame *top = &opstack.top(); + + if (!top->grid && op == CSGType::Union) { + top->grid = std::move(partgrid); + } else if (top->grid && partgrid) { switch (get_operation(csgpart)) { case CSGType::Union: - grid_union(*ret, *partgrid); + grid_union(*(top->grid), *partgrid); break; case CSGType::Difference: - grid_difference(*ret, *partgrid); + grid_difference(*(top->grid), *partgrid); break; case CSGType::Intersection: - grid_intersection(*ret, *partgrid); + grid_intersection(*(top->grid), *partgrid); + break; + } + } + + if (get_stack_operation(csgpart) == CSGStackOp::Pop) { + VoxelGridPtr popgrid = std::move(top->grid); + auto popop = opstack.top().op; + opstack.pop(); + VoxelGridPtr &grid = opstack.top().grid; + + switch (popop) { + case CSGType::Union: + grid_union(*grid, *popgrid); + break; + case CSGType::Difference: + grid_difference(*grid, *popgrid); + break; + case CSGType::Intersection: + grid_intersection(*grid, *popgrid); break; } } } + ret = std::move(opstack.top().grid); + return ret; } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index dc05148fc9..f37638fb47 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1114,7 +1114,7 @@ VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, VoxelizeParams p) { VoxelGridPtr &ret = part.gridcache[p]; - if (!ret) { + if (!ret && csg::get_mesh(part)) { p.trafo(csg::get_transform(part)); ret = mesh_to_grid(*csg::get_mesh(part), p); } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 215a3bdce1..623dee4e69 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -242,7 +242,7 @@ void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po) csg::model_to_csgmesh(*po.model_object(), po.trafo(), csg_inserter{po.m_mesh_to_slice, slaposAssembly}, - csg::mpartsPositive | csg::mpartsNegative); + csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits); generate_preview(po, slaposAssembly); } @@ -352,9 +352,18 @@ template BoundingBoxf3 csgmesh_positive_bb(const Cont &csg) bounding_box(*csg::get_mesh(*csg.begin()), csg::get_transform(*csg.begin())); + bool skip = false; for (const auto &m : csg) { - if (m.operation == csg::CSGType::Union) + 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) bb3d.merge(bounding_box(*csg::get_mesh(m), csg::get_transform(m))); + + if (stackop == csg::CSGStackOp::Pop) + skip = false; } return bb3d; From d75b951c39e8219b1f3deab8bf95a4719f8cabc3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 21 Dec 2022 13:53:33 +0100 Subject: [PATCH 52/75] FInish csg operation stacking in cgal side Broken slicing --- .../CSGMesh/PerformCSGMeshBooleans.hpp | 148 +++++++++++++++--- src/libslic3r/CSGMesh/SliceCSGMesh.hpp | 99 ++++++------ src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp | 49 +++--- src/libslic3r/SLAPrintSteps.cpp | 10 +- 4 files changed, 213 insertions(+), 93 deletions(-) diff --git a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp index da1ddce3f9..733cdd1fe2 100644 --- a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp +++ b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp @@ -1,8 +1,12 @@ #ifndef PERFORMCSGMESHBOOLEANS_HPP #define PERFORMCSGMESHBOOLEANS_HPP +#include +#include + #include "CSGMesh.hpp" +#include "libslic3r/Execution/ExecutionTBB.hpp" #include "libslic3r/MeshBoolean.hpp" namespace Slic3r { namespace csg { @@ -24,26 +28,134 @@ MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart) return ret; } -template -void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMesh &cgalm, - const Range &csgparts) +namespace detail_cgal { + +using MeshBoolean::cgal::CGALMeshPtr; + +inline void perform_csg(CSGType op, CGALMeshPtr &dst, CGALMeshPtr &src) { - for (auto &csgpart : csgparts) { - auto m = get_cgalmesh(csgpart); - if (m) { - switch (get_operation(csgpart)) { - case CSGType::Union: - MeshBoolean::cgal::plus(cgalm, *m); - break; - case CSGType::Difference: - MeshBoolean::cgal::minus(cgalm, *m); - break; - case CSGType::Intersection: - MeshBoolean::cgal::intersect(cgalm, *m); - break; - } + switch (op) { + case CSGType::Union: + MeshBoolean::cgal::plus(*dst, *src); + break; + case CSGType::Difference: + MeshBoolean::cgal::minus(*dst, *src); + break; + case CSGType::Intersection: + MeshBoolean::cgal::intersect(*dst, *src); + break; + } +} + +template +std::vector get_cgalptrs(Ex policy, const Range &csgrange) +{ + std::vector ret(csgrange.size()); + execution::for_each(policy, size_t(0), csgrange.size(), + [&csgrange, &ret](size_t i) { + auto it = csgrange.begin(); + std::advance(it, i); + auto &csgpart = *it; + ret[i] = get_cgalmesh(csgpart); + }); + + return ret; +} + +} // namespace detail + +template +void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMeshPtr &cgalm, + const Range &csgrange) +{ + using MeshBoolean::cgal::CGALMesh; + using MeshBoolean::cgal::CGALMeshPtr; + using namespace detail_cgal; + + struct Frame { CSGType op = CSGType::Union; CGALMeshPtr cgalptr; }; + + std::stack opstack{std::vector{}}; + + if (!csgrange.empty() && + csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) + opstack.push({}); + + std::vector cgalmeshes = get_cgalptrs(ex_tbb, csgrange); + + size_t csgidx = 0; + for (auto &csgpart : csgrange) { + + auto op = get_operation(csgpart); + CGALMeshPtr &cgalptr = cgalmeshes[csgidx++]; + + if (get_stack_operation(csgpart) == CSGStackOp::Push) { + opstack.push({op, nullptr}); + 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); + auto popop = opstack.top().op; + opstack.pop(); + CGALMeshPtr &dst = opstack.top().cgalptr; + perform_csg(popop, dst, src); } } + + cgalm = std::move(opstack.top().cgalptr); +} + +template +It check_csgmesh_booleans(const Range &csgrange, Visitor &&vfn) +{ + using namespace detail_cgal; + + std::vector cgalmeshes(csgrange.size()); + auto check_part = [&csgrange, &cgalmeshes](size_t i) + { + auto it = csgrange.begin(); + std::advance(it, i); + auto &csgpart = *it; + auto m = get_cgalmesh(csgpart); + + if (!m || MeshBoolean::cgal::empty(*m)) + return; + + if (!MeshBoolean::cgal::does_bound_a_volume(*m) || + MeshBoolean::cgal::does_self_intersect(*m)) + return; + + cgalmeshes[i] = std::move(m); + }; + execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part); + + It ret = csgrange.end(); + for (size_t i = 0; i < csgrange.size(); ++i) { + if (!cgalmeshes[i]) { + auto it = csgrange.begin(); + std::advance(it, i); + vfn(it); + + if (it == csgrange.end()) + ret = it; + } + } + + return csgrange.end(); +} + +template +It check_csgmesh_booleans(const Range &csgrange) +{ + return check_csgmesh_booleans(csgrange, [](auto &) {}); } template @@ -51,7 +163,7 @@ MeshBoolean::cgal::CGALMeshPtr perform_csgmesh_booleans(const Range &csgpart { auto ret = MeshBoolean::cgal::triangle_mesh_to_cgal(indexed_triangle_set{}); if (ret) - perform_csgmesh_booleans(*ret, csgparts); + perform_csgmesh_booleans(ret, csgparts); return ret; } diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp index 06302faa46..d9df97e956 100644 --- a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -11,6 +11,40 @@ namespace Slic3r { namespace csg { +namespace detail { + +void merge_slices(csg::CSGType op, size_t i, + std::vector &target, + std::vector &source) +{ + switch(op) { + case CSGType::Union: + for (ExPolygon &expoly : source[i]) + target[i].emplace_back(std::move(expoly)); + break; + case CSGType::Difference: + target[i] = diff_ex(target[i], source[i]); + break; + case CSGType::Intersection: + target[i] = intersection_ex(target[i], source[i]); + break; + } +} + +void collect_nonempty_indices(csg::CSGType op, + const std::vector &slicegrid, + const std::vector &slices, + std::vector indices) +{ + indices.clear(); + for (size_t i = 0; i < slicegrid.size(); ++i) { + if (op == CSGType::Intersection || !slices[i].empty()) + indices.emplace_back(i); + } +} + +} // namespace detail + template std::vector slice_csgmesh_ex( const Range &csgrange, @@ -18,6 +52,8 @@ std::vector slice_csgmesh_ex( const MeshSlicingParamsEx ¶ms, const std::function &throw_on_cancel = [] {}) { + using namespace detail; + struct Frame { CSGType op; std::vector slices; }; std::stack opstack{std::vector{}}; @@ -49,32 +85,13 @@ std::vector slice_csgmesh_ex( assert(slices.size() == slicegrid.size()); - nonempty_indices.clear(); - for (size_t i = 0; i < slicegrid.size(); ++i) { - if (op == CSGType::Intersection || !slices[i].empty()) - nonempty_indices.emplace_back(i); - } + collect_nonempty_indices(op, slicegrid, slices, nonempty_indices); - auto mergefn = [&csgpart, &slices, &top](size_t i){ - switch(get_operation(csgpart)) { - case CSGType::Union: - for (ExPolygon &expoly : slices[i]) - top->slices[i].emplace_back(std::move(expoly)); - - break; - case CSGType::Difference: - top->slices[i] = diff_ex(top->slices[i], slices[i]); - break; - case CSGType::Intersection: - top->slices[i] = intersection_ex(top->slices[i], slices[i]); - break; - } - }; - - execution::for_each(ex_tbb, - nonempty_indices.begin(), nonempty_indices.end(), - mergefn, - execution::max_concurrency(ex_tbb)); + execution::for_each( + ex_tbb, nonempty_indices.begin(), nonempty_indices.end(), + [op, &slices, &top](size_t i) { + merge_slices(op, i, top->slices, slices); + }, execution::max_concurrency(ex_tbb)); } if (get_stack_operation(csgpart) == CSGStackOp::Pop) { @@ -83,37 +100,19 @@ std::vector slice_csgmesh_ex( opstack.pop(); std::vector &prev_slices = opstack.top().slices; - nonempty_indices.clear(); - for (size_t i = 0; i < slicegrid.size(); ++i) { - if (popop == CSGType::Intersection || !popslices[i].empty()) - nonempty_indices.emplace_back(i); - } + collect_nonempty_indices(popop, slicegrid, popslices, nonempty_indices); - auto mergefn2 = [&popslices, &prev_slices, popop](size_t i){ - switch(popop) { - case CSGType::Union: - for (ExPolygon &expoly : popslices[i]) - prev_slices[i].emplace_back(std::move(expoly)); - - break; - case CSGType::Difference: - prev_slices[i] = diff_ex(prev_slices[i], popslices[i]); - break; - case CSGType::Intersection: - prev_slices[i] = intersection_ex(prev_slices[i], popslices[i]); - break; - } - }; - - execution::for_each(ex_tbb, - nonempty_indices.begin(), nonempty_indices.end(), - mergefn2, - execution::max_concurrency(ex_tbb)); + execution::for_each( + ex_tbb, nonempty_indices.begin(), nonempty_indices.end(), + [&popslices, &prev_slices, popop](size_t i) { + merge_slices(popop, i, prev_slices, popslices); + }, execution::max_concurrency(ex_tbb)); } } std::vector ret = std::move(opstack.top().slices); + // TODO: verify if this part can be omitted or not. execution::for_each(ex_tbb, ret.begin(), ret.end(), [](ExPolygons &slice) { auto it = std::remove_if(slice.begin(), slice.end(), [](const ExPolygon &p){ return p.area() < double(SCALED_EPSILON) * double(SCALED_EPSILON); diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp index ac54cf9226..43019ef856 100644 --- a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -28,10 +28,31 @@ VoxelGridPtr get_voxelgrid(const CSGPartT &csgpart, VoxelizeParams params) return ret; } +namespace detail { + +inline void perform_csg(CSGType op, VoxelGridPtr &dst, VoxelGridPtr &src) +{ + switch (op) { + case CSGType::Union: + grid_union(*dst, *src); + break; + case CSGType::Difference: + grid_difference(*dst, *src); + break; + case CSGType::Intersection: + grid_intersection(*dst, *src); + break; + } +} + +} // namespace detail + template VoxelGridPtr voxelize_csgmesh(const Range &csgrange, const VoxelizeParams ¶ms = {}) { + using namespace detail; + VoxelGridPtr ret; std::vector grids (csgrange.size()); @@ -50,7 +71,8 @@ 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) + if (!csgrange.empty() && + csg::get_stack_operation(*csgrange.begin()) != CSGStackOp::Push) opstack.push({}); for (auto &csgpart : csgrange) { @@ -71,17 +93,7 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, if (!top->grid && op == CSGType::Union) { top->grid = std::move(partgrid); } else if (top->grid && partgrid) { - switch (get_operation(csgpart)) { - case CSGType::Union: - grid_union(*(top->grid), *partgrid); - break; - case CSGType::Difference: - grid_difference(*(top->grid), *partgrid); - break; - case CSGType::Intersection: - grid_intersection(*(top->grid), *partgrid); - break; - } + perform_csg(get_operation(csgpart), top->grid, partgrid); } if (get_stack_operation(csgpart) == CSGStackOp::Pop) { @@ -89,18 +101,7 @@ VoxelGridPtr voxelize_csgmesh(const Range &csgrange, auto popop = opstack.top().op; opstack.pop(); VoxelGridPtr &grid = opstack.top().grid; - - switch (popop) { - case CSGType::Union: - grid_union(*grid, *popgrid); - break; - case CSGType::Difference: - grid_difference(*grid, *popgrid); - break; - case CSGType::Intersection: - grid_intersection(*grid, *popgrid); - break; - } + perform_csg(popop, grid, popgrid); } } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 623dee4e69..4e34ffbed1 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -206,7 +206,15 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) { - po.m_preview_meshes[step] = TriangleMesh{generate_preview_vdb(po, 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) { From 1efc8191a23248c17d33a25e9babd465ac20c3f7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 21 Dec 2022 18:09:31 +0100 Subject: [PATCH 53/75] 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) { From bfb1ec073d7ab4b498698823c40feb0b9704466d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 2 Jan 2023 10:29:55 +0100 Subject: [PATCH 54/75] Comments for CSG stack ops --- src/libslic3r/CSGMesh/CSGMesh.hpp | 18 ++++++++++++++++++ src/libslic3r/CSGMesh/ModelToCSGMesh.hpp | 16 +++++++++------- .../CSGMesh/PerformCSGMeshBooleans.hpp | 1 + 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/CSGMesh/CSGMesh.hpp b/src/libslic3r/CSGMesh/CSGMesh.hpp index f44119c929..00b60e4fc3 100644 --- a/src/libslic3r/CSGMesh/CSGMesh.hpp +++ b/src/libslic3r/CSGMesh/CSGMesh.hpp @@ -18,6 +18,23 @@ namespace Slic3r { namespace csg { // Supported CSG operation types enum class CSGType { Union, Difference, Intersection }; + +// A CSG part can instruct the processing to push the sub-result until a new +// csg part with a pop instruction appears. This can be used to implement +// parentheses in a CSG expression represented by the collection of csg parts. +// A CSG part can not contain another CSG collection, only a mesh, this is why +// its easier to do this stacking instead of recursion in the data definition. +// CSGStackOp::Continue means no stack operation required. +// When a CSG part contains a Push instruction, it is expected that the CSG +// operation it contains refers to the whole collection spanning to the nearest +// part with a Pop instruction. +// e.g.: +// { +// CUBE1: { mesh: cube, op: Union, stack op: Continue }, +// CUBE2: { mesh: cube, op: Difference, stack op: Push}, +// CUBE3: { mesh: cube, op: Union, stack op: Pop} +// } +// is a collection of csg parts representing the expression CUBE1 - (CUBE2 + CUBE3) enum class CSGStackOp { Push, Continue, Pop }; // Get the CSG operation of the part. Can be overriden for any type @@ -26,6 +43,7 @@ template CSGType get_operation(const CSGPartT &part) return part.operation; } +// Get the stack operation required by the CSG part. template CSGStackOp get_stack_operation(const CSGPartT &part) { return part.stack_operation; diff --git a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp index 3d1941294d..8a3773bfde 100644 --- a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp @@ -9,18 +9,20 @@ namespace Slic3r { namespace csg { +// Flags to select which parts to export from Model into a csg part collection. +// These flags can be chained with the | operator enum ModelParts { - mpartsPositive = 1, - mpartsNegative = 2, - mpartsDrillHoles = 4, - mpartsDoSplits = 8 + mpartsPositive = 1, // Include positive parts + mpartsNegative = 2, // Include negative parts + mpartsDrillHoles = 4, // Include drill holes + mpartsDoSplits = 8 // Split each splitable mesh and export as a union of csg parts }; template void model_to_csgmesh(const ModelObject &mo, - const Transform3d &trafo, - OutIt out, - // values of ModelParts ORed + const Transform3d &trafo, // Applies to all exported parts + OutIt out, // Output iterator + // values of ModelParts OR-ed int parts_to_include = mpartsPositive ) { diff --git a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp index 0a880c8fa2..f81a445416 100644 --- a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp +++ b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp @@ -82,6 +82,7 @@ std::vector get_cgalptrs(Ex policy, const Range &csgrange) } // namespace detail +// Process the sequence of CSG parts with CGAL. template void perform_csgmesh_booleans(MeshBoolean::cgal::CGALMeshPtr &cgalm, const Range &csgrange) From 159fc4e28e963db2f120b93f276c4528c9e32c78 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 2 Jan 2023 13:40:04 +0100 Subject: [PATCH 55/75] Minor improvements Fix bad function call exception with empty interrupter (OpenVDB) --- src/libslic3r/CSGMesh/ModelToCSGMesh.hpp | 3 +++ src/libslic3r/OpenVDBUtils.cpp | 2 +- src/libslic3r/SLAPrintSteps.cpp | 6 ++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp index 8a3773bfde..ecf9d8168b 100644 --- a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp @@ -43,6 +43,9 @@ void model_to_csgmesh(const ModelObject &mo, ++out; its_split(vol->mesh().its, SplitOutputFn{[&out, &vol, &trafo](indexed_triangle_set &&its) { + if (its.empty()) + return; + CSGPart part{std::make_unique(std::move(its)), CSGType::Union, (trafo * vol->get_matrix()).cast()}; diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 44a5b172ed..5d00fac0da 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -81,7 +81,7 @@ struct Interrupter void start(const char* name = nullptr) { (void)name; } void end() {} - inline bool wasInterrupted(int percent = -1) { return statusfn(percent); } + inline bool wasInterrupted(int percent = -1) { return statusfn && statusfn(percent); } }; VoxelGridPtr mesh_to_grid(const indexed_triangle_set &mesh, diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index c1b0356810..1c56b8946a 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -196,7 +196,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st auto r = range(po.m_mesh_to_slice); auto m = indexed_triangle_set{}; - if (r.size() == 1 || is_all_positive(r)) { + if (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); @@ -204,7 +204,9 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st 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!")); + L("Can't perform full mesh booleans! " + "Some parts of the print will be previewed with approximated meshes. " + "This does not affect the quality of slices or the physical print in any way.")); m = generate_preview_vdb(po, step); } From 39197ecd2dd32ae0e22cf9f19a00ea169798af24 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 2 Jan 2023 14:36:34 +0100 Subject: [PATCH 56/75] Caching of cgal data instead of voxel grid --- src/libslic3r/MeshBoolean.cpp | 5 +++++ src/libslic3r/MeshBoolean.hpp | 2 ++ src/libslic3r/SLAPrint.cpp | 18 +++++----------- src/libslic3r/SLAPrint.hpp | 40 +++-------------------------------- 4 files changed, 15 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 3373616a47..c7ebcbd19e 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -315,6 +315,11 @@ bool empty(const CGALMesh &mesh) return mesh.m.is_empty(); } +CGALMeshPtr clone(const CGALMesh &m) +{ + return CGALMeshPtr{new CGALMesh{m}}; +} + } // namespace cgal } // namespace MeshBoolean diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index 4210bfe869..e20425c1c7 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -28,6 +28,8 @@ struct CGALMesh; struct CGALMeshDeleter { void operator()(CGALMesh *ptr); }; using CGALMeshPtr = std::unique_ptr; +CGALMeshPtr clone(const CGALMesh &m); + CGALMeshPtr triangle_mesh_to_cgal( const std::vector &V, const std::vector &F); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index f37638fb47..0e1f8b4308 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1,5 +1,6 @@ #include "SLAPrint.hpp" #include "SLAPrintSteps.hpp" +#include "CSGMesh/PerformCSGMeshBooleans.hpp" #include "Geometry.hpp" #include "Thread.hpp" @@ -1104,22 +1105,13 @@ void SLAPrint::StatusReporter::operator()(SLAPrint & p, namespace csg { -inline bool operator==(const VoxelizeParams &a, const VoxelizeParams &b) +MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartForStep &part) { - std::hash h; - return h(a) == h(b); -} - -VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, VoxelizeParams p) -{ - VoxelGridPtr &ret = part.gridcache[p]; - - if (!ret && csg::get_mesh(part)) { - p.trafo(csg::get_transform(part)); - ret = mesh_to_grid(*csg::get_mesh(part), p); + if (!part.cgalcache && csg::get_mesh(part)) { + part.cgalcache = csg::get_cgalmesh(static_cast(part)); } - return ret ? clone(*ret) : nullptr; + return part.cgalcache? clone(*part.cgalcache) : nullptr; } } // namespace csg diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 6379575bf8..182c2f9111 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -11,6 +11,7 @@ #include "Format/SLAArchiveWriter.hpp" #include "GCode/ThumbnailData.hpp" #include "libslic3r/CSGMesh/CSGMesh.hpp" +#include "libslic3r/MeshBoolean.hpp" #include "libslic3r/OpenVDBUtils.hpp" #include @@ -49,41 +50,6 @@ enum SliceOrigin { soSupport, soModel }; } // namespace Slic3r -namespace std { - -template<> struct hash { - size_t operator() (const Slic3r::csg::VoxelizeParams &p) const { - int64_t vs = Slic3r::scaled(p.voxel_scale()) >> 10; - int64_t eb = Slic3r::scaled(p.exterior_bandwidth()) >> 10; - int64_t ib = Slic3r::scaled(p.interior_bandwidth()) >> 10; - - size_t h = 0; - boost::hash_combine(h, vs); - boost::hash_combine(h, eb); - boost::hash_combine(h, ib); - - return h; - } -}; - -template<> struct equal_to { - size_t operator() (const Slic3r::csg::VoxelizeParams &p1, - const Slic3r::csg::VoxelizeParams &p2) const { - - int64_t vs1 = Slic3r::scaled(p1.voxel_scale()) >> 10; - int64_t eb1 = Slic3r::scaled(p1.exterior_bandwidth()) >> 10; - int64_t ib1 = Slic3r::scaled(p1.interior_bandwidth()) >> 10; - - int64_t vs2 = Slic3r::scaled(p2.voxel_scale()) >> 10; - int64_t eb2 = Slic3r::scaled(p2.exterior_bandwidth()) >> 10; - int64_t ib2 = Slic3r::scaled(p2.interior_bandwidth()) >> 10; - - return vs1 == vs2 && eb1 == eb2 && ib1 == ib2; - } -}; - -} // namespace std - namespace Slic3r { // Each sla object step can hold a collection of csg operations on the @@ -95,7 +61,7 @@ namespace Slic3r { struct CSGPartForStep : public csg::CSGPart { SLAPrintObjectStep key; - mutable std::unordered_map gridcache; + mutable MeshBoolean::cgal::CGALMeshPtr cgalcache; CSGPartForStep(SLAPrintObjectStep k, CSGPart &&p = {}) : key{k}, CSGPart{std::move(p)} @@ -114,7 +80,7 @@ struct CSGPartForStep : public csg::CSGPart namespace csg { -VoxelGridPtr get_voxelgrid(const CSGPartForStep &part, VoxelizeParams p); +MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartForStep &part); } // namespace csg From 7c834de6abe7ce00ac113b63301b7a8845559a6d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 10 Jan 2023 09:42:53 +0100 Subject: [PATCH 57/75] Fix crash when selecting an object issue no. 11 --- src/slic3r/GUI/GLCanvas3D.cpp | 13 +++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 2 ++ src/slic3r/GUI/Selection.cpp | 4 ++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 419e5fbc96..fa94e6abf8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7055,5 +7055,18 @@ void GLCanvas3D::GizmoHighlighter::blink() invalidate(); } +const ModelVolume *get_model_volume(const GLVolume &v, const Model &model) +{ + const ModelVolume * ret = nullptr; + + if (model.objects.size() < v.object_idx()) { + const ModelObject *obj = model.objects[v.object_idx()]; + if (obj->volumes.size() < v.volume_idx()) + ret = obj->volumes[v.volume_idx()]; + } + + return ret; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 7b5a1084c1..198ecb1d81 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1046,6 +1046,8 @@ private: float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } }; +const ModelVolume * get_model_volume(const GLVolume &v, const Model &model); + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index a70698b438..42331f3a70 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1974,8 +1974,8 @@ std::vector Selection::get_volume_idxs_from_instance(unsigned int for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; - if (pt == ptSLA && v->is_modifier && - m_model->objects[object_idx]->volumes[i]->is_modifier()) + const ModelVolume *mv = get_model_volume(*v, *m_model); + if (pt == ptSLA && v->is_modifier && mv && mv->is_modifier()) continue; if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx) idxs.push_back(i); From b2ef76f4d0148065e096e2ccc904d8188d00a02a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 10 Jan 2023 14:39:03 +0100 Subject: [PATCH 58/75] Resurrect the old hollowing and hole drilling functions. Apply them if generic cgal fails and there are no negative volumes. --- src/libslic3r/SLA/Hollowing.cpp | 169 ++++++++++++++++++++++++++++++-- src/libslic3r/SLA/Hollowing.hpp | 23 +++++ src/libslic3r/SLAPrintSteps.cpp | 93 +++++++++++++++++- src/libslic3r/SLAPrintSteps.hpp | 1 - 4 files changed, 270 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 2d3606c475..31289e7baf 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -2,11 +2,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -14,6 +16,8 @@ #include #include +#include + #include #include @@ -291,6 +295,19 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags) mesh.merge(inter); } +void hollow_mesh(indexed_triangle_set &mesh, const Interior &interior, int flags) +{ + if (mesh.empty() || interior.mesh.empty()) return; + + if (flags & hfRemoveInsideTriangles && interior.gridptr) + remove_inside_triangles(mesh, interior); + + indexed_triangle_set interi = interior.mesh; + sla::swap_normals(interi); + + its_merge(mesh, interi); +} + // Get the distance of p to the interior's zero iso_surface. Interior should // have its zero isosurface positioned at offset + closing_distance inwards form // the model surface. @@ -378,14 +395,14 @@ void divide_triangle(const DivFace &face, Fn &&visitor) divide_triangle(child2, std::forward(visitor)); } -void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, +void remove_inside_triangles(indexed_triangle_set &mesh, const Interior &interior, const std::vector &exclude_mask) { enum TrPos { posInside, posTouch, posOutside }; - auto &faces = mesh.its.indices; - auto &vertices = mesh.its.vertices; - auto bb = mesh.bounding_box(); + auto &faces = mesh.indices; + auto &vertices = mesh.vertices; + auto bb = bounding_box(mesh); //mesh.bounding_box(); bool use_exclude_mask = faces.size() == exclude_mask.size(); auto is_excluded = [&exclude_mask, use_exclude_mask](size_t face_id) { @@ -421,8 +438,8 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, // or not. std::vector to_remove; - MeshMods(const TriangleMesh &mesh): - to_remove(mesh.its.indices.size(), false) {} + MeshMods(const indexed_triangle_set &mesh): + to_remove(mesh.indices.size(), false) {} // Number of triangles that need to be removed. size_t to_remove_cnt() const @@ -484,7 +501,8 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, const Vec3i &face = faces[face_idx]; // If the triangle is excluded, we need to keep it. - if (is_excluded(face_idx)) return; + if (is_excluded(face_idx)) + return; std::array pts = {vertices[face(0)], vertices[face(1)], vertices[face(2)]}; @@ -492,7 +510,8 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, BoundingBoxf3 facebb{pts.begin(), pts.end()}; // Face is certainly outside the cavity - if (!facebb.intersects(bb)) return; + if (!facebb.intersects(bb)) + return; DivFace df{face, pts, long(face_idx)}; @@ -525,10 +544,16 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, faces.swap(new_faces); new_faces = {}; - mesh = TriangleMesh{mesh.its}; +// mesh = TriangleMesh{mesh.its}; //FIXME do we want to repair the mesh? Are there duplicate vertices or flipped triangles? } +void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, + const std::vector &exclude_mask) +{ + remove_inside_triangles(mesh.its, interior, exclude_mask); +} + struct FaceHash { // A 64 bit number's max hex digits @@ -590,8 +615,10 @@ struct FaceHash { FaceHash (const indexed_triangle_set &its): facehash(its.indices.size()) { - for (const Vec3i &face : its.indices) + for (Vec3i face : its.indices) { + std::swap(face(0), face(2)); facehash.insert(facekey(face, its.vertices)); + } } bool find(const std::string &key) @@ -747,4 +774,126 @@ double get_voxel_scale(double mesh_volume, const HollowingConfig &hc) return voxel_scale; } +// The same as its_compactify_vertices, but returns a new mesh, doesn't touch +// the original +static indexed_triangle_set +remove_unconnected_vertices(const indexed_triangle_set &its) +{ + if (its.indices.empty()) {}; + + indexed_triangle_set M; + + std::vector vtransl(its.vertices.size(), -1); + int vcnt = 0; + for (auto &f : its.indices) { + + for (int i = 0; i < 3; ++i) + if (vtransl[size_t(f(i))] < 0) { + + M.vertices.emplace_back(its.vertices[size_t(f(i))]); + vtransl[size_t(f(i))] = vcnt++; + } + + std::array new_f = { + vtransl[size_t(f(0))], + vtransl[size_t(f(1))], + vtransl[size_t(f(2))] + }; + + M.indices.emplace_back(new_f[0], new_f[1], new_f[2]); + } + + return M; +} + +int hollow_mesh_and_drill(indexed_triangle_set &hollowed_mesh, + const Interior &interior, + const DrainHoles &drainholes, + std::function on_hole_fail) +{ + auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( + hollowed_mesh.vertices, + hollowed_mesh.indices + ); + + std::uniform_real_distribution dist(0., float(EPSILON)); + auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}, {}); + indexed_triangle_set part_to_drill = hollowed_mesh; + + std::mt19937 m_rng{std::random_device{}()}; + + for (size_t i = 0; i < drainholes.size(); ++i) { + sla::DrainHole holept = drainholes[i]; + + holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; + holept.normal.normalize(); + holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; + indexed_triangle_set m = holept.to_mesh(); + + part_to_drill.indices.clear(); + auto bb = bounding_box(m); + Eigen::AlignedBox ebb{bb.min.cast(), + bb.max.cast()}; + + AABBTreeIndirect::traverse( + tree, + AABBTreeIndirect::intersecting(ebb), + [&part_to_drill, &hollowed_mesh](const auto& node) + { + part_to_drill.indices.emplace_back(hollowed_mesh.indices[node.idx]); + // continue traversal + return true; + }); + + auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal( + remove_unconnected_vertices(part_to_drill)); + + if (MeshBoolean::cgal::does_self_intersect(*cgal_meshpart)) { + on_hole_fail(i); + continue; + } + + auto cgal_hole = MeshBoolean::cgal::triangle_mesh_to_cgal(m); + MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_hole); + } + + auto ret = static_cast(HollowMeshResult::Ok); + + if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) { + ret |= static_cast(HollowMeshResult::DrillingFailed); + } + + auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); + + if (!MeshBoolean::cgal::does_bound_a_volume(*hollowed_mesh_cgal)) { + ret |= static_cast(HollowMeshResult::FaultyMesh); + } + + if (!MeshBoolean::cgal::empty(*holes_mesh_cgal) + && !MeshBoolean::cgal::does_bound_a_volume(*holes_mesh_cgal)) { + ret |= static_cast(HollowMeshResult::FaultyHoles); + } + + // Don't even bother + if (ret & static_cast(HollowMeshResult::DrillingFailed)) + return ret; + + try { + if (!MeshBoolean::cgal::empty(*holes_mesh_cgal)) + MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); + + hollowed_mesh = + MeshBoolean::cgal::cgal_to_indexed_triangle_set(*hollowed_mesh_cgal); + + std::vector exclude_mask = + create_exclude_mask(hollowed_mesh, interior, drainholes); + + sla::remove_inside_triangles(hollowed_mesh, interior, exclude_mask); + } catch (const Slic3r::RuntimeError &) { + ret |= static_cast(HollowMeshResult::DrillingFailed); + } + + return ret; +} + }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index aa43b021bb..c21bdff4e3 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -159,9 +159,32 @@ void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags = 0); // Hollowing prepared in "interior", merge with original mesh void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags = 0); +// Will do the hollowing +void hollow_mesh(indexed_triangle_set &mesh, const HollowingConfig &cfg, int flags = 0); + +// Hollowing prepared in "interior", merge with original mesh +void hollow_mesh(indexed_triangle_set &mesh, const Interior &interior, int flags = 0); + +enum class HollowMeshResult { + Ok = 0, + FaultyMesh = 1, + FaultyHoles = 2, + DrillingFailed = 4 +}; + +// Return HollowMeshResult codes OR-ed. +int hollow_mesh_and_drill( + indexed_triangle_set &mesh, + const Interior& interior, + const DrainHoles &holes, + std::function on_hole_fail = [](size_t){}); + void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, const std::vector &exclude_mask = {}); +void remove_inside_triangles(indexed_triangle_set &mesh, const Interior &interior, + const std::vector &exclude_mask = {}); + sla::DrainHoles transformed_drainhole_points(const ModelObject &mo, const Transform3d &trafo); diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 1c56b8946a..c7567528b4 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -87,7 +87,6 @@ std::string PRINT_STEP_LABELS(size_t idx) SLAPrint::Steps::Steps(SLAPrint *print) : m_print{print} - , m_rng{std::random_device{}()} , objcount{m_print->m_objects.size()} , ilhd{m_print->m_material_config.initial_layer_height.getFloat()} , ilh{float(ilhd)} @@ -187,6 +186,12 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( return m; } +inline auto parts_to_slice(const std::multiset &parts, + SLAPrintObjectStep step) +{ + auto r = parts.equal_range(step); + return Range{r.first, r.second}; +} void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) { @@ -196,13 +201,91 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st auto r = range(po.m_mesh_to_slice); auto m = indexed_triangle_set{}; + + bool handled = false; + if (is_all_positive(r)) { m = csgmesh_merge_positive_parts(r); + handled = true; } else if (csg::check_csgmesh_booleans(r) == r.end()) { auto cgalmeshptr = csg::perform_csgmesh_booleans(r); - if (cgalmeshptr) + if (cgalmeshptr) { m = MeshBoolean::cgal::cgal_to_indexed_triangle_set(*cgalmeshptr); + handled = true; + } } else { + // Normal cgal processing failed. If there are no negative volumes, + // the hollowing can be tried with the old algorithm which didn't handled volumes. + // If that fails for any of the drillholes, the voxelization fallback is + // used. + + bool is_pure_model = is_all_positive(parts_to_slice(po.m_mesh_to_slice, slaposAssembly)); + bool can_hollow = po.m_hollowing_data && po.m_hollowing_data->interior && + !sla::get_mesh(*po.m_hollowing_data->interior).empty(); + + + bool hole_fail = false; + if (step == slaposHollowing && is_pure_model) { + if (can_hollow) { + m = csgmesh_merge_positive_parts(r); + sla::hollow_mesh(m, *po.m_hollowing_data->interior, + sla::hfRemoveInsideTriangles); + } + + handled = true; + } else if (step == slaposDrillHoles && is_pure_model) { + if (po.m_model_object->sla_drain_holes.empty()) { + m = po.m_preview_meshes[slaposHollowing].its; + handled = true; + } else if (can_hollow) { + m = csgmesh_merge_positive_parts(r); + sla::hollow_mesh(m, *po.m_hollowing_data->interior); + sla::DrainHoles drainholes = po.transformed_drainhole_points(); + + auto ret = sla::hollow_mesh_and_drill( + m, *po.m_hollowing_data->interior, drainholes, + [/*&po, &drainholes, */&hole_fail](size_t i) + { + hole_fail = /*drainholes[i].failed = + po.model_object()->sla_drain_holes[i].failed =*/ true; + }); + + if (ret & static_cast(sla::HollowMeshResult::FaultyMesh)) { + po.active_step_add_warning( + PrintStateBase::WarningLevel::NON_CRITICAL, + L("Mesh to be hollowed is not suitable for hollowing (does not " + "bound a volume).")); + } + + if (ret & static_cast(sla::HollowMeshResult::FaultyHoles)) { + po.active_step_add_warning( + PrintStateBase::WarningLevel::NON_CRITICAL, + L("Unable to drill the current configuration of holes into the " + "model.")); + } + + handled = true; + + if (ret & static_cast(sla::HollowMeshResult::DrillingFailed)) { + po.active_step_add_warning( + PrintStateBase::WarningLevel::NON_CRITICAL, L( + "Drilling holes into the mesh failed. " + "This is usually caused by broken model. Try to fix it first.")); + + handled = false; + } + + if (hole_fail) { + po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, + L("Failed to drill some holes into the model")); + + handled = false; + } + } + } + } + + if (!handled) { // Last resort to voxelization. po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, L("Can't perform full mesh booleans! " "Some parts of the print will be previewed with approximated meshes. " @@ -290,9 +373,6 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) indexed_triangle_set m = sla::get_mesh(*po.m_hollowing_data->interior); - // Release the data, won't be needed anymore, takes huge amount of ram - po.m_hollowing_data->interior.reset(); - if (!m.empty()) { // simplify mesh lossless float loss_less_max_error = 2*std::numeric_limits::epsilon(); @@ -327,6 +407,9 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) generate_preview(po, slaposDrillHoles); else po.m_preview_meshes[slaposDrillHoles] = po.get_mesh_to_print(); + + // Release the data, won't be needed anymore, takes huge amount of ram + po.m_hollowing_data->interior.reset(); } template diff --git a/src/libslic3r/SLAPrintSteps.hpp b/src/libslic3r/SLAPrintSteps.hpp index a1b3ef42d1..32d10a424c 100644 --- a/src/libslic3r/SLAPrintSteps.hpp +++ b/src/libslic3r/SLAPrintSteps.hpp @@ -14,7 +14,6 @@ class SLAPrint::Steps { private: SLAPrint *m_print = nullptr; - std::mt19937 m_rng; public: // where the per object operations start and end From 0989a6f5df4df1495b04cb7031c6a1c8c5de7b69 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 10 Jan 2023 16:12:54 +0100 Subject: [PATCH 59/75] Fix crash --- src/libslic3r/SLAPrintSteps.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index c7567528b4..88b7926d1e 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -409,7 +409,8 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) po.m_preview_meshes[slaposDrillHoles] = po.get_mesh_to_print(); // Release the data, won't be needed anymore, takes huge amount of ram - po.m_hollowing_data->interior.reset(); + if (po.m_hollowing_data && po.m_hollowing_data->interior) + po.m_hollowing_data->interior.reset(); } template From 897e5673c94a4ba9b4587eebadf82cca187425d1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 10 Jan 2023 16:13:13 +0100 Subject: [PATCH 60/75] Export interface for csgparts in SLAPrintObject --- src/libslic3r/SLAPrint.hpp | 17 +++++++++++++---- src/libslic3r/SLAPrintSteps.cpp | 9 +-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 182c2f9111..0cf796d07c 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -88,6 +88,7 @@ class SLAPrintObject : public _SLAPrintObjectBase { private: // Prevents erroneous use by other classes. using Inherited = _SLAPrintObjectBase; + using CSGContainer = std::multiset; public: @@ -122,6 +123,17 @@ public: // like hollowing and drilled holes. const TriangleMesh & get_mesh_to_print() const; + const Range get_parts_to_slice() const + { + return range(m_mesh_to_slice); + } + + const Range get_parts_to_slice(SLAPrintObjectStep step) const + { + auto r = m_mesh_to_slice.equal_range(step); + return {r.first, r.second}; + } + sla::SupportPoints transformed_support_points() const; sla::DrainHoles transformed_drainhole_points() const; @@ -331,9 +343,6 @@ private: std::vector m_model_height_levels; - // Caching the transformed (m_trafo) raw mesh of the object -// TriangleMesh m_transformed_rmesh; - struct SupportData { sla::SupportableMesh input; // the input @@ -362,7 +371,7 @@ private: std::unique_ptr m_supportdata; // Holds CSG operations for the printed object, prioritized by print steps. - std::multiset m_mesh_to_slice; + CSGContainer m_mesh_to_slice; // Holds the preview of the object to be printed (as it will look like with // all its holes and cavities, negatives and positive volumes unified. diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 88b7926d1e..f0e2e5db08 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -186,13 +186,6 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb( return m; } -inline auto parts_to_slice(const std::multiset &parts, - SLAPrintObjectStep step) -{ - auto r = parts.equal_range(step); - return Range{r.first, r.second}; -} - void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep step) { Benchmark bench; @@ -219,7 +212,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st // If that fails for any of the drillholes, the voxelization fallback is // used. - bool is_pure_model = is_all_positive(parts_to_slice(po.m_mesh_to_slice, slaposAssembly)); + bool is_pure_model = is_all_positive(po.get_parts_to_slice(slaposAssembly)); bool can_hollow = po.m_hollowing_data && po.m_hollowing_data->interior && !sla::get_mesh(*po.m_hollowing_data->interior).empty(); From a4e50f8219884e7ac963e35e092dc41a76b71e72 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 10 Jan 2023 17:45:48 +0100 Subject: [PATCH 61/75] Fix gizmo cut previews When using legacy hole drilling algorithm --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/CSGMesh/CSGMesh.hpp | 23 ----- src/libslic3r/CSGMesh/CSGMeshCopy.hpp | 69 ++++++++++++++ src/libslic3r/CSGMesh/SliceCSGMesh.hpp | 4 +- src/libslic3r/CSGMesh/TriangleMeshAdapter.hpp | 95 +++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 33 ++++--- src/slic3r/GUI/MeshUtils.cpp | 42 ++++++-- src/slic3r/GUI/MeshUtils.hpp | 29 +++++- 8 files changed, 246 insertions(+), 51 deletions(-) create mode 100644 src/libslic3r/CSGMesh/CSGMeshCopy.hpp create mode 100644 src/libslic3r/CSGMesh/TriangleMeshAdapter.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 4bb4fee9e7..da561d411a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -44,6 +44,8 @@ set(SLIC3R_SOURCES CSGMesh/ModelToCSGMesh.hpp CSGMesh/PerformCSGMeshBooleans.hpp CSGMesh/VoxelizeCSGMesh.hpp + CSGMesh/TriangleMeshAdapter.hpp + CSGMesh/CSGMeshCopy.hpp EdgeGrid.cpp EdgeGrid.hpp ElephantFootCompensation.cpp diff --git a/src/libslic3r/CSGMesh/CSGMesh.hpp b/src/libslic3r/CSGMesh/CSGMesh.hpp index 00b60e4fc3..ef555a38b3 100644 --- a/src/libslic3r/CSGMesh/CSGMesh.hpp +++ b/src/libslic3r/CSGMesh/CSGMesh.hpp @@ -64,29 +64,6 @@ Transform3f get_transform(const CSGPartT &part) return part.trafo; } -// Provide default overloads for indexed_triangle_set to be usable as a plain -// CSGPart with an implicit union operation - -inline CSGType get_operation(const indexed_triangle_set &part) -{ - return CSGType::Union; -} - -inline CSGStackOp get_stack_operation(const indexed_triangle_set &part) -{ - return CSGStackOp::Continue; -} - -inline const indexed_triangle_set * get_mesh(const indexed_triangle_set &part) -{ - return ∂ -} - -inline Transform3f get_transform(const indexed_triangle_set &part) -{ - return Transform3f::Identity(); -} - // Default implementation struct CSGPart { AnyPtr its_ptr; diff --git a/src/libslic3r/CSGMesh/CSGMeshCopy.hpp b/src/libslic3r/CSGMesh/CSGMeshCopy.hpp new file mode 100644 index 0000000000..b296ecbfa7 --- /dev/null +++ b/src/libslic3r/CSGMesh/CSGMeshCopy.hpp @@ -0,0 +1,69 @@ +#ifndef CSGMESHCOPY_HPP +#define CSGMESHCOPY_HPP + +#include "CSGMesh.hpp" + +namespace Slic3r { namespace csg { + +// Copy a csg range but for the meshes, only copy the pointers. +template +void copy_csgrange_shallow(const Range &csgrange, OutIt out) +{ + for (const auto &part : csgrange) { + CSGPart cpy{AnyPtr{get_mesh(part)}, + get_operation(part), + get_transform(part)}; + + cpy.stack_operation = get_stack_operation(part); + + *out = std::move(cpy); + ++out; + } +} + +// Copy the csg range, allocating new meshes +template +void copy_csgrange_deep(const Range &csgrange, OutIt out) +{ + for (const auto &part : csgrange) { + + CSGPart cpy{{}, get_operation(part), get_transform(part)}; + + if (auto meshptr = get_mesh(part)) { + cpy.its_ptr = std::make_unique(*meshptr); + } + + cpy.stack_operation = get_stack_operation(part); + + *out = std::move(cpy); + ++out; + } +} + +template +bool is_same(const Range &A, const Range &B) +{ + bool ret = true; + + size_t s = A.size(); + + if (B.size() != s) + ret = false; + + size_t i = 0; + auto itA = A.begin(); + auto itB = B.begin(); + for (; ret && i < s; ++itA, ++itB, ++i) { + ret = ret && + get_mesh(*itA) == get_mesh(*itB) && + get_operation(*itA) == get_operation(*itB) && + get_stack_operation(*itA) == get_stack_operation(*itB) && + get_transform(*itA).isApprox(get_transform(*itB)); + } + + return ret; +} + +}} // namespace Slic3r::csg + +#endif // CSGCOPY_HPP diff --git a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp index 041acedb0e..9d7b9a077d 100644 --- a/src/libslic3r/CSGMesh/SliceCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/SliceCSGMesh.hpp @@ -13,7 +13,7 @@ namespace Slic3r { namespace csg { namespace detail { -void merge_slices(csg::CSGType op, size_t i, +inline void merge_slices(csg::CSGType op, size_t i, std::vector &target, std::vector &source) { @@ -31,7 +31,7 @@ void merge_slices(csg::CSGType op, size_t i, } } -void collect_nonempty_indices(csg::CSGType op, +inline void collect_nonempty_indices(csg::CSGType op, const std::vector &slicegrid, const std::vector &slices, std::vector &indices) diff --git a/src/libslic3r/CSGMesh/TriangleMeshAdapter.hpp b/src/libslic3r/CSGMesh/TriangleMeshAdapter.hpp new file mode 100644 index 0000000000..81b05b0463 --- /dev/null +++ b/src/libslic3r/CSGMesh/TriangleMeshAdapter.hpp @@ -0,0 +1,95 @@ +#ifndef TRIANGLEMESHADAPTER_HPP +#define TRIANGLEMESHADAPTER_HPP + +#include "CSGMesh.hpp" + +#include "libslic3r/TriangleMesh.hpp" + +namespace Slic3r { namespace csg { + +// Provide default overloads for indexed_triangle_set to be usable as a plain +// CSGPart with an implicit union operation + +inline CSGType get_operation(const indexed_triangle_set &part) +{ + return CSGType::Union; +} + +inline CSGStackOp get_stack_operation(const indexed_triangle_set &part) +{ + return CSGStackOp::Continue; +} + +inline const indexed_triangle_set * get_mesh(const indexed_triangle_set &part) +{ + return ∂ +} + +inline Transform3f get_transform(const indexed_triangle_set &part) +{ + return Transform3f::Identity(); +} + +inline CSGType get_operation(const indexed_triangle_set *const part) +{ + return CSGType::Union; +} + +inline CSGStackOp get_stack_operation(const indexed_triangle_set *const part) +{ + return CSGStackOp::Continue; +} + +inline const indexed_triangle_set * get_mesh(const indexed_triangle_set *const part) +{ + return part; +} + +inline Transform3f get_transform(const indexed_triangle_set *const part) +{ + return Transform3f::Identity(); +} + +inline CSGType get_operation(const TriangleMesh &part) +{ + return CSGType::Union; +} + +inline CSGStackOp get_stack_operation(const TriangleMesh &part) +{ + return CSGStackOp::Continue; +} + +inline const indexed_triangle_set * get_mesh(const TriangleMesh &part) +{ + return &part.its; +} + +inline Transform3f get_transform(const TriangleMesh &part) +{ + return Transform3f::Identity(); +} + +inline CSGType get_operation(const TriangleMesh * const part) +{ + return CSGType::Union; +} + +inline CSGStackOp get_stack_operation(const TriangleMesh * const part) +{ + return CSGStackOp::Continue; +} + +inline const indexed_triangle_set * get_mesh(const TriangleMesh * const part) +{ + return &part->its; +} + +inline Transform3f get_transform(const TriangleMesh * const part) +{ + return Transform3f::Identity(); +} + +}} // namespace Slic3r::csg + +#endif // TRIANGLEMESHADAPTER_HPP diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index ac3750871e..705a13b902 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -172,7 +172,7 @@ void InstancesHider::on_update() for (const TriangleMesh* mesh : meshes) { m_clippers.emplace_back(new MeshClipper); m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); - m_clippers.back()->set_mesh(*mesh); + m_clippers.back()->set_mesh(mesh->its); } m_old_meshes = meshes; } @@ -299,8 +299,9 @@ std::vector Raycaster::raycasters() const return mrcs; } +} // namespace GUI - +namespace GUI { void ObjectClipper::on_update() @@ -313,33 +314,39 @@ void ObjectClipper::on_update() std::vector meshes; std::vector trafos; bool force_clipper_regeneration = false; + + std::unique_ptr mc; + Geometry::Transformation mc_tr; if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) { // For sla printers we use the mesh generated by the backend const SLAPrintObject* po = get_pool()->selection_info()->print_object(); assert(po != nullptr); - m_sla_mesh_cache = po->get_mesh_to_print(); - if (!m_sla_mesh_cache.empty()) { - m_sla_mesh_cache.transform(po->trafo().inverse()); - meshes.emplace_back(&m_sla_mesh_cache); - trafos.emplace_back(Geometry::Transformation()); - force_clipper_regeneration = true; + auto partstoslice = po->get_parts_to_slice(); + if (! partstoslice.empty()) { + mc = std::make_unique(); + mc->set_mesh(partstoslice); + mc_tr = Geometry::Transformation{po->trafo().inverse().cast()}; } } - if (meshes.empty()) { + if (!mc && meshes.empty()) { for (const ModelVolume* mv : mo->volumes) { meshes.emplace_back(&mv->mesh()); trafos.emplace_back(mv->get_transformation()); } } - if (force_clipper_regeneration || meshes != m_old_meshes) { + if (mc || force_clipper_regeneration || meshes != m_old_meshes) { m_clippers.clear(); for (size_t i = 0; i < meshes.size(); ++i) { m_clippers.emplace_back(new MeshClipper, trafos[i]); - m_clippers.back().first->set_mesh(*meshes[i]); + m_clippers.back().first->set_mesh(meshes[i]->its); + } + m_old_meshes = std::move(meshes); + + if (mc) { + m_clippers.emplace_back(std::move(mc), mc_tr); } - m_old_meshes = meshes; m_active_inst_bb_radius = mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); @@ -470,7 +477,7 @@ void SupportsClipper::on_update() // The timestamp has changed. m_clipper.reset(new MeshClipper); // The mesh should already have the shared vertices calculated. - m_clipper->set_mesh(print_object->support_mesh()); + m_clipper->set_mesh(print_object->support_mesh().its); m_old_timestamp = timestamp; } } diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 39e463a19b..faa970c4a2 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -5,11 +5,13 @@ #include "libslic3r/TriangleMeshSlicer.hpp" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/CSGMesh/SliceCSGMesh.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/Camera.hpp" + #include #include @@ -49,22 +51,38 @@ void MeshClipper::set_limiting_plane(const ClippingPlane& plane) -void MeshClipper::set_mesh(const TriangleMesh& mesh) +void MeshClipper::set_mesh(const indexed_triangle_set& mesh) { - if (m_mesh != &mesh) { + if (m_mesh.get() != &mesh) { m_mesh = &mesh; m_result.reset(); } } -void MeshClipper::set_negative_mesh(const TriangleMesh& mesh) +void MeshClipper::set_mesh(AnyPtr &&ptr) { - if (m_negative_mesh != &mesh) { + if (m_mesh.get() != ptr.get()) { + m_mesh = std::move(ptr); + m_result.reset(); + } +} + +void MeshClipper::set_negative_mesh(const indexed_triangle_set& mesh) +{ + if (m_negative_mesh.get() != &mesh) { m_negative_mesh = &mesh; m_result.reset(); } } +void MeshClipper::set_negative_mesh(AnyPtr &&ptr) +{ + if (m_negative_mesh.get() != ptr.get()) { + m_negative_mesh = std::move(ptr); + m_result.reset(); + } +} + void MeshClipper::set_transformation(const Geometry::Transformation& trafo) @@ -172,13 +190,21 @@ void MeshClipper::recalculate_triangles() MeshSlicingParams slicing_params; slicing_params.trafo.rotate(Eigen::Quaternion::FromTwoVectors(up, Vec3d::UnitZ())); - ExPolygons expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params)); + ExPolygons expolys; - if (m_negative_mesh && !m_negative_mesh->empty()) { - const ExPolygons neg_expolys = union_ex(slice_mesh(m_negative_mesh->its, height_mesh, slicing_params)); - expolys = diff_ex(expolys, neg_expolys); + if (m_csgmesh.empty()) { + if (m_mesh) + expolys = union_ex(slice_mesh(*m_mesh, height_mesh, slicing_params)); + + if (m_negative_mesh && !m_negative_mesh->empty()) { + const ExPolygons neg_expolys = union_ex(slice_mesh(*m_negative_mesh, height_mesh, slicing_params)); + expolys = diff_ex(expolys, neg_expolys); + } + } else { + expolys = std::move(csg::slice_csgmesh_ex(range(m_csgmesh), {height_mesh}, MeshSlicingParamsEx{slicing_params}).front()); } + // Triangulate and rotate the cut into world coords: Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), up); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index b7afbbb89a..c9ba916ade 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -5,6 +5,8 @@ #include "libslic3r/Geometry.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/AABBMesh.hpp" +#include "libslic3r/CSGMesh/TriangleMeshAdapter.hpp" +#include "libslic3r/CSGMesh/CSGMeshCopy.hpp" #include "admesh/stl.h" #include "slic3r/GUI/GLModel.hpp" @@ -14,7 +16,6 @@ #include namespace Slic3r { - namespace GUI { struct Camera; @@ -88,9 +89,25 @@ public: // Which mesh to cut. MeshClipper remembers const * to it, caller // must make sure that it stays valid. - void set_mesh(const TriangleMesh& mesh); + void set_mesh(const indexed_triangle_set& mesh); + void set_mesh(AnyPtr &&ptr); - void set_negative_mesh(const TriangleMesh &mesh); + void set_negative_mesh(const indexed_triangle_set &mesh); + void set_negative_mesh(AnyPtr &&ptr); + + template + void set_mesh(const Range &csgrange, bool copy_meshes = false) + { + if (! csg::is_same(range(m_csgmesh), csgrange)) { + m_csgmesh.clear(); + if (copy_meshes) + csg::copy_csgrange_deep(csgrange, std::back_inserter(m_csgmesh)); + else + csg::copy_csgrange_shallow(csgrange, std::back_inserter(m_csgmesh)); + + m_result.reset(); + } + } // Inform the MeshClipper about the transformation that transforms the mesh // into world coordinates. @@ -110,8 +127,10 @@ private: void recalculate_triangles(); Geometry::Transformation m_trafo; - const TriangleMesh* m_mesh = nullptr; - const TriangleMesh* m_negative_mesh = nullptr; + AnyPtr m_mesh; + AnyPtr m_negative_mesh; + std::vector m_csgmesh; + ClippingPlane m_plane; ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); From 440df505b47086e30272547b429b2bd75db99334 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 11 Jan 2023 18:24:44 +0100 Subject: [PATCH 62/75] SLA backend thread-safety improvements - Put AnyPtr into separate header, it deserves one - Add means to handle shared pointers inside AnyPtr - Use shared pointers in sla csg collection for meshes not owned by Model - Add method to get the last completed step in PrintObjectBase - Make SLAPrintObject::get_parts_to_slice() safe to call from UI thread against background thread activity. --- src/libslic3r/AnyPtr.hpp | 130 +++++++++++++++++++++++ src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/CSGMesh/CSGMesh.hpp | 2 +- src/libslic3r/CSGMesh/CSGMeshCopy.hpp | 15 ++- src/libslic3r/CSGMesh/ModelToCSGMesh.hpp | 8 +- src/libslic3r/MTUtils.hpp | 83 --------------- src/libslic3r/PrintBase.hpp | 15 +++ src/libslic3r/SLAPrint.cpp | 37 ++++++- src/libslic3r/SLAPrint.hpp | 20 ++-- src/libslic3r/SLAPrintSteps.cpp | 14 ++- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 2 +- 11 files changed, 218 insertions(+), 109 deletions(-) create mode 100644 src/libslic3r/AnyPtr.hpp diff --git a/src/libslic3r/AnyPtr.hpp b/src/libslic3r/AnyPtr.hpp new file mode 100644 index 0000000000..c40d10093e --- /dev/null +++ b/src/libslic3r/AnyPtr.hpp @@ -0,0 +1,130 @@ +#ifndef ANYPTR_HPP +#define ANYPTR_HPP + +#include +#include +#include + +namespace Slic3r { + +// A general purpose pointer holder that can hold any type of smart pointer +// or raw pointer which can own or not own any object they point to. +// In case a raw pointer is stored, it is not destructed so ownership is +// assumed to be foreign. +// +// The stored pointer is not checked for being null when dereferenced. +// +// This is a movable only object due to the fact that it can possibly hold +// a unique_ptr which a non-copy. +template +class AnyPtr { + enum { RawPtr, UPtr, ShPtr, WkPtr }; + + boost::variant, std::shared_ptr, std::weak_ptr> ptr; + + template static T *get_ptr(Self &&s) + { + switch (s.ptr.which()) { + case RawPtr: return boost::get(s.ptr); + case UPtr: return boost::get>(s.ptr).get(); + case ShPtr: return boost::get>(s.ptr).get(); + case WkPtr: { + auto shptr = boost::get>(s.ptr).lock(); + return shptr.get(); + } + } + + return nullptr; + } + +public: + template>> + AnyPtr(TT *p = nullptr) : ptr{p} + {} + template>> + AnyPtr(std::unique_ptr p) : ptr{std::unique_ptr(std::move(p))} + {} + template>> + AnyPtr(std::shared_ptr p) : ptr{std::shared_ptr(std::move(p))} + {} + template>> + AnyPtr(std::weak_ptr p) : ptr{std::weak_ptr(std::move(p))} + {} + + ~AnyPtr() = default; + + AnyPtr(AnyPtr &&other) noexcept : ptr{std::move(other.ptr)} {} + AnyPtr(const AnyPtr &other) = delete; + + AnyPtr &operator=(AnyPtr &&other) noexcept { ptr = std::move(other.ptr); return *this; } + AnyPtr &operator=(const AnyPtr &other) = delete; + + template>> + AnyPtr &operator=(TT *p) { ptr = p; return *this; } + + template>> + AnyPtr &operator=(std::unique_ptr p) { ptr = std::move(p); return *this; } + + template>> + AnyPtr &operator=(std::shared_ptr p) { ptr = p; return *this; } + + template>> + AnyPtr &operator=(std::weak_ptr p) { ptr = std::move(p); return *this; } + + const T &operator*() const { return *get_ptr(*this); } + T &operator*() { return *get_ptr(*this); } + + T *operator->() { return get_ptr(*this); } + const T *operator->() const { return get_ptr(*this); } + + T *get() { return get_ptr(*this); } + const T *get() const { return get_ptr(*this); } + + operator bool() const + { + switch (ptr.which()) { + case RawPtr: return bool(boost::get(ptr)); + case UPtr: return bool(boost::get>(ptr)); + case ShPtr: return bool(boost::get>(ptr)); + case WkPtr: { + auto shptr = boost::get>(ptr).lock(); + return bool(shptr); + } + } + + return false; + } + + // If the stored pointer is a shared or weak pointer, returns a reference + // counted copy. Empty shared pointer is returned otherwise. + std::shared_ptr get_shared_cpy() const + { + std::shared_ptr ret; + + switch (ptr.which()) { + case ShPtr: ret = boost::get>(ptr); break; + case WkPtr: ret = boost::get>(ptr).lock(); break; + default: + ; + } + + return ret; + } + + // If the underlying pointer is unique, convert to shared pointer + void convert_unique_to_shared() + { + if (ptr.which() == UPtr) + ptr = std::shared_ptr{std::move(boost::get>(ptr))}; + } + + // Returns true if the data is owned by this AnyPtr instance + bool is_owned() const noexcept + { + return ptr.which() == UPtr || ptr.which() == ShPtr; + } +}; + +} // namespace Slic3r + +#endif // ANYPTR_HPP diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index da561d411a..e9aeb68e27 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -22,6 +22,7 @@ set(SLIC3R_SOURCES AABBTreeLines.hpp AABBMesh.hpp AABBMesh.cpp + AnyPtr.hpp BoundingBox.cpp BoundingBox.hpp BridgeDetector.cpp diff --git a/src/libslic3r/CSGMesh/CSGMesh.hpp b/src/libslic3r/CSGMesh/CSGMesh.hpp index ef555a38b3..d14ed76595 100644 --- a/src/libslic3r/CSGMesh/CSGMesh.hpp +++ b/src/libslic3r/CSGMesh/CSGMesh.hpp @@ -1,7 +1,7 @@ #ifndef CSGMESH_HPP #define CSGMESH_HPP -#include // for AnyPtr +#include #include namespace Slic3r { namespace csg { diff --git a/src/libslic3r/CSGMesh/CSGMeshCopy.hpp b/src/libslic3r/CSGMesh/CSGMeshCopy.hpp index b296ecbfa7..78800f9bbb 100644 --- a/src/libslic3r/CSGMesh/CSGMeshCopy.hpp +++ b/src/libslic3r/CSGMesh/CSGMeshCopy.hpp @@ -5,17 +5,28 @@ namespace Slic3r { namespace csg { -// Copy a csg range but for the meshes, only copy the pointers. +// Copy a csg range but for the meshes, only copy the pointers. If the copy +// is made from a CSGPart compatible object, and the pointer is a shared one, +// it will be copied with reference counting. template void copy_csgrange_shallow(const Range &csgrange, OutIt out) { for (const auto &part : csgrange) { - CSGPart cpy{AnyPtr{get_mesh(part)}, + CSGPart cpy{{}, get_operation(part), get_transform(part)}; cpy.stack_operation = get_stack_operation(part); + if constexpr (std::is_convertible_v) { + if (auto shptr = part.its_ptr.get_shared_cpy()) { + cpy.its_ptr = shptr; + } + } + + if (!cpy.its_ptr) + cpy.its_ptr = AnyPtr{get_mesh(part)}; + *out = std::move(cpy); ++out; } diff --git a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp index ecf9d8168b..9e413594ed 100644 --- a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp @@ -12,10 +12,10 @@ namespace Slic3r { namespace csg { // Flags to select which parts to export from Model into a csg part collection. // These flags can be chained with the | operator enum ModelParts { - mpartsPositive = 1, // Include positive parts - mpartsNegative = 2, // Include negative parts - mpartsDrillHoles = 4, // Include drill holes - mpartsDoSplits = 8 // Split each splitable mesh and export as a union of csg parts + mpartsPositive = 1, // Include positive parts + mpartsNegative = 2, // Include negative parts + mpartsDrillHoles = 4, // Include drill holes + mpartsDoSplits = 8, // Split each splitable mesh and export as a union of csg parts }; template diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 74c44aa5e5..ab99ea5f68 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -8,7 +8,6 @@ #include #include #include -#include #include "libslic3r.h" @@ -137,88 +136,6 @@ inline std::vector> grid(const T &start, return vals; } -// A general purpose pointer holder that can hold any type of smart pointer -// or raw pointer which can own or not own any object they point to. -// In case a raw pointer is stored, it is not destructed so ownership is -// assumed to be foreign. -// -// The stored pointer is not checked for being null when dereferenced. -// -// This is a movable only object due to the fact that it can possibly hold -// a unique_ptr which a non-copy. -template -class AnyPtr { - enum { RawPtr, UPtr, ShPtr, WkPtr }; - - boost::variant, std::shared_ptr, std::weak_ptr> ptr; - - template static T *get_ptr(Self &&s) - { - switch (s.ptr.which()) { - case RawPtr: return boost::get(s.ptr); - case UPtr: return boost::get>(s.ptr).get(); - case ShPtr: return boost::get>(s.ptr).get(); - case WkPtr: { - auto shptr = boost::get>(s.ptr).lock(); - return shptr.get(); - } - } - - return nullptr; - } - -public: - template>> - AnyPtr(TT *p = nullptr) : ptr{p} - {} - template>> - AnyPtr(std::unique_ptr p) : ptr{std::unique_ptr(std::move(p))} - {} - template>> - AnyPtr(std::shared_ptr p) : ptr{std::shared_ptr(std::move(p))} - {} - template>> - AnyPtr(std::weak_ptr p) : ptr{std::weak_ptr(std::move(p))} - {} - - ~AnyPtr() = default; - - AnyPtr(AnyPtr &&other) noexcept : ptr{std::move(other.ptr)} {} - AnyPtr(const AnyPtr &other) = delete; - - AnyPtr &operator=(AnyPtr &&other) noexcept { ptr = std::move(other.ptr); return *this; } - AnyPtr &operator=(const AnyPtr &other) = delete; - - AnyPtr &operator=(T *p) { ptr = p; return *this; } - AnyPtr &operator=(std::unique_ptr p) { ptr = std::move(p); return *this; } - AnyPtr &operator=(std::shared_ptr p) { ptr = p; return *this; } - AnyPtr &operator=(std::weak_ptr p) { ptr = std::move(p); return *this; } - - const T &operator*() const { return *get_ptr(*this); } - T &operator*() { return *get_ptr(*this); } - - T *operator->() { return get_ptr(*this); } - const T *operator->() const { return get_ptr(*this); } - - T *get() { return get_ptr(*this); } - const T *get() const { return get_ptr(*this); } - - operator bool() const - { - switch (ptr.which()) { - case RawPtr: return bool(boost::get(ptr)); - case UPtr: return bool(boost::get>(ptr)); - case ShPtr: return bool(boost::get>(ptr)); - case WkPtr: { - auto shptr = boost::get>(ptr).lock(); - return bool(shptr); - } - } - - return false; - } -}; - } // namespace Slic3r #endif // MTUTILS_HPP diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 13796abbad..08268c04b9 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -690,6 +690,21 @@ public: PrintStateBase::StateWithTimeStamp step_state_with_timestamp(PrintObjectStepEnum step) const { return m_state.state_with_timestamp(step, PrintObjectBase::state_mutex(m_print)); } PrintStateBase::StateWithWarnings step_state_with_warnings(PrintObjectStepEnum step) const { return m_state.state_with_warnings(step, PrintObjectBase::state_mutex(m_print)); } + auto last_completed_step() const + { + static_assert(COUNT > 0, "Step count should be > 0"); + auto s = int(COUNT) - 1; + + std::lock_guard lk(state_mutex(m_print)); + while (s >= 0 && ! is_step_done_unguarded(PrintObjectStepEnum(s))) + --s; + + if (s < 0) + s = COUNT; + + return PrintObjectStepEnum(s); + } + protected: PrintObjectBaseWithState(PrintType *print, ModelObject *model_object) : PrintObjectBase(model_object), m_print(print) {} diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 0e1f8b4308..79cc2605ff 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1,5 +1,6 @@ #include "SLAPrint.hpp" #include "SLAPrintSteps.hpp" +#include "CSGMesh/CSGMeshCopy.hpp" #include "CSGMesh/PerformCSGMeshBooleans.hpp" #include "Geometry.hpp" @@ -1017,12 +1018,16 @@ const TriangleMesh &SLAPrintObject::get_mesh_to_print() const { const TriangleMesh *ret = nullptr; - int s = SLAPrintObjectStep::slaposCount; + int s = last_completed_step(); - while (s > 0 && !ret) { - --s; - if (is_step_done(SLAPrintObjectStep(s)) && !m_preview_meshes[s].empty()) + if (s == slaposCount) + ret = &EMPTY_MESH; + + while (s >= 0 && !ret) { + if (!m_preview_meshes[s].empty()) ret = &m_preview_meshes[s]; + + --s; } if (!ret) @@ -1031,6 +1036,30 @@ const TriangleMesh &SLAPrintObject::get_mesh_to_print() const return *ret; } +std::vector SLAPrintObject::get_parts_to_slice() const +{ + return get_parts_to_slice(slaposCount); +} + +std::vector +SLAPrintObject::get_parts_to_slice(SLAPrintObjectStep untilstep) const +{ + auto laststep = last_completed_step(); + SLAPrintObjectStep s = std::min(untilstep, laststep); + + if (s == slaposCount) + return {}; + + std::vector ret; + + for (int step = 0; step < s; ++step) { + auto r = m_mesh_to_slice.equal_range(SLAPrintObjectStep(step)); + csg::copy_csgrange_shallow(Range{r.first, r.second}, std::back_inserter(ret)); + } + + return ret; +} + sla::SupportPoints SLAPrintObject::transformed_support_points() const { assert(m_model_object != nullptr); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 0cf796d07c..e6a4286eaa 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -123,16 +123,9 @@ public: // like hollowing and drilled holes. const TriangleMesh & get_mesh_to_print() const; - const Range get_parts_to_slice() const - { - return range(m_mesh_to_slice); - } + std::vector get_parts_to_slice() const; - const Range get_parts_to_slice(SLAPrintObjectStep step) const - { - auto r = m_mesh_to_slice.equal_range(step); - return {r.first, r.second}; - } + std::vector get_parts_to_slice(SLAPrintObjectStep step) const; sla::SupportPoints transformed_support_points() const; sla::DrainHoles transformed_drainhole_points() const; @@ -373,6 +366,15 @@ private: // Holds CSG operations for the printed object, prioritized by print steps. CSGContainer m_mesh_to_slice; + auto mesh_to_slice(SLAPrintObjectStep s) const + { + auto r = m_mesh_to_slice.equal_range(s); + + return Range{r.first, r.second}; + } + + auto mesh_to_slice() const { return range(m_mesh_to_slice); } + // Holds the preview of the object to be printed (as it will look like with // all its holes and cavities, negatives and positive volumes unified. // Essentially this should be a m_mesh_to_slice after the CSG operations diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index f0e2e5db08..f3824f7515 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -212,7 +212,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st // If that fails for any of the drillholes, the voxelization fallback is // used. - bool is_pure_model = is_all_positive(po.get_parts_to_slice(slaposAssembly)); + bool is_pure_model = is_all_positive(po.mesh_to_slice(slaposAssembly)); bool can_hollow = po.m_hollowing_data && po.m_hollowing_data->interior && !sla::get_mesh(*po.m_hollowing_data->interior).empty(); @@ -317,7 +317,11 @@ struct csg_inserter { SLAPrintObjectStep key; csg_inserter &operator*() { return *this; } - void operator=(csg::CSGPart &&part) { m.emplace(key, std::move(part)); } + void operator=(csg::CSGPart &&part) + { + part.its_ptr.convert_unique_to_shared(); + m.emplace(key, std::move(part)); + } csg_inserter& operator++() { return *this; } }; @@ -356,7 +360,7 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) ctl.cancelfn = [this]() { throw_if_canceled(); }; sla::InteriorPtr interior = - generate_interior(range(po.m_mesh_to_slice), hlwcfg, ctl); + generate_interior(po.mesh_to_slice(), hlwcfg, ctl); if (!interior || sla::get_mesh(*interior).empty()) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; @@ -378,7 +382,7 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) // Put the interior into the target mesh as a negative po.m_mesh_to_slice .emplace(slaposHollowing, - csg::CSGPart{std::make_unique(std::move(m)), csg::CSGType::Difference}); + csg::CSGPart{std::make_shared(std::move(m)), csg::CSGType::Difference}); generate_preview(po, slaposHollowing); } @@ -518,7 +522,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) auto thr = [this]() { m_print->throw_if_canceled(); }; auto &slice_grid = po.m_model_height_levels; - po.m_model_slices = slice_csgmesh_ex(range(po.m_mesh_to_slice), slice_grid, params, thr); + po.m_model_slices = slice_csgmesh_ex(po.mesh_to_slice(), slice_grid, params, thr); auto mit = slindex_it; for (size_t id = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 705a13b902..62f7eb5fe3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -324,7 +324,7 @@ void ObjectClipper::on_update() auto partstoslice = po->get_parts_to_slice(); if (! partstoslice.empty()) { mc = std::make_unique(); - mc->set_mesh(partstoslice); + mc->set_mesh(range(partstoslice)); mc_tr = Geometry::Transformation{po->trafo().inverse().cast()}; } } From bb82ce90c95bb038298d3f6a1340a0e862c498aa Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 12 Jan 2023 19:18:33 +0100 Subject: [PATCH 63/75] Don't allow part type change to modifier type in SLA mode --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index be04d804d7..13b1c52baa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1707,17 +1707,19 @@ void GLGizmoEmboss::draw_model_type() } } - ImGui::SameLine(); - if (type == modifier) { - draw_icon(IconType::modifier, IconState::hovered); - } else { - if(draw_button(IconType::modifier, is_last_solid_part)) - new_type = modifier; - if (ImGui::IsItemHovered()) { - if(is_last_solid_part) - ImGui::SetTooltip("%s", _u8L("You can't change a type of the last solid part of the object.").c_str()); - else if (type != modifier) - ImGui::SetTooltip("%s", _u8L("Click to change part type into modifier.").c_str()); + if (wxGetApp().plater()->printer_technology() != ptSLA) { + ImGui::SameLine(); + if (type == modifier) { + draw_icon(IconType::modifier, IconState::hovered); + } else { + if(draw_button(IconType::modifier, is_last_solid_part)) + new_type = modifier; + if (ImGui::IsItemHovered()) { + if(is_last_solid_part) + ImGui::SetTooltip("%s", _u8L("You can't change a type of the last solid part of the object.").c_str()); + else if (type != modifier) + ImGui::SetTooltip("%s", _u8L("Click to change part type into modifier.").c_str()); + } } } From cf4f07c2205ce5553f8d4db36db589f56d1627d4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 12 Jan 2023 19:38:24 +0100 Subject: [PATCH 64/75] Use shared pointers for SLA preview data To be able to survive a sudden cancellation and subsequent cleanup in the background thread --- src/libslic3r/SLAPrint.cpp | 19 ++++----------- src/libslic3r/SLAPrint.hpp | 4 ++-- src/libslic3r/SLAPrintSteps.cpp | 30 ++++++++++++++++-------- src/slic3r/GUI/GLCanvas3D.cpp | 14 +++++------ src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp | 6 ++++- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 5 +++- src/slic3r/GUI/Plater.cpp | 8 +++++-- 8 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 79cc2605ff..80857e34a5 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1014,26 +1014,15 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const return EMPTY_MESH; } -const TriangleMesh &SLAPrintObject::get_mesh_to_print() const +const std::shared_ptr & +SLAPrintObject::get_mesh_to_print() const { - const TriangleMesh *ret = nullptr; - int s = last_completed_step(); - if (s == slaposCount) - ret = &EMPTY_MESH; - - while (s >= 0 && !ret) { - if (!m_preview_meshes[s].empty()) - ret = &m_preview_meshes[s]; - + while (s > 0 && ! m_preview_meshes[s]) --s; - } - if (!ret) - ret = &EMPTY_MESH; - - return *ret; + return m_preview_meshes[s]; } std::vector SLAPrintObject::get_parts_to_slice() const diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index e6a4286eaa..6d35f013d8 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -121,7 +121,7 @@ public: // Get the mesh that is going to be printed with all the modifications // like hollowing and drilled holes. - const TriangleMesh & get_mesh_to_print() const; + const std::shared_ptr& get_mesh_to_print() const; std::vector get_parts_to_slice() const; @@ -379,7 +379,7 @@ private: // all its holes and cavities, negatives and positive volumes unified. // Essentially this should be a m_mesh_to_slice after the CSG operations // or an approximation of that. - std::array m_preview_meshes; + std::array, SLAPrintObjectStep::slaposCount + 1> m_preview_meshes; class HollowingData { diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index f3824f7515..2f7243a9ca 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -228,7 +228,11 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st handled = true; } else if (step == slaposDrillHoles && is_pure_model) { if (po.m_model_object->sla_drain_holes.empty()) { - m = po.m_preview_meshes[slaposHollowing].its; + // Get the last printable preview + auto &meshp = po.get_mesh_to_print(); + if (meshp) + m = *(meshp); + handled = true; } else if (can_hollow) { m = csgmesh_merge_positive_parts(r); @@ -286,7 +290,11 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st m = generate_preview_vdb(po, step); } - po.m_preview_meshes[step] = TriangleMesh{std::move(m)}; + assert(po.m_preview_meshes[step].empty()); + + if (!m.empty()) + po.m_preview_meshes[step] = + std::make_shared(std::move(m)); for (size_t i = size_t(step) + 1; i < slaposCount; ++i) { @@ -604,11 +612,12 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // If supports are disabled, we can skip the model scan. if(!po.m_config.supports_enable.getBool()) return; - if (!po.m_supportdata) + if (!po.m_supportdata) { + auto &meshp = po.get_mesh_to_print(); + assert(meshp); po.m_supportdata = - std::make_unique( - po.get_mesh_to_print() - ); + std::make_unique(*meshp); + } po.m_supportdata->input.zoffset = csgmesh_positive_bb(po.m_mesh_to_slice) .min.z(); @@ -750,11 +759,12 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // repeated) if(po.m_config.pad_enable.getBool()) { - if (!po.m_supportdata) + if (!po.m_supportdata) { + auto &meshp = po.get_mesh_to_print(); + assert(meshp); po.m_supportdata = - std::make_unique( - po.get_mesh_to_print() - ); + std::make_unique(*meshp); + } // Get the distilled pad configuration from the config // (Again, despite it was retrieved in the previous step. Note that diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fa94e6abf8..c9eb100600 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6628,7 +6628,7 @@ void GLCanvas3D::_load_sla_shells() return; auto add_volume = [this](const SLAPrintObject &object, int volume_id, const SLAPrintObject::Instance& instance, - const TriangleMesh& mesh, const ColorRGBA& color, bool outside_printer_detection_enabled) { + const indexed_triangle_set& mesh, const ColorRGBA& color, bool outside_printer_detection_enabled) { m_volumes.volumes.emplace_back(new GLVolume(color)); GLVolume& v = *m_volumes.volumes.back(); #if ENABLE_SMOOTH_NORMALS @@ -6641,22 +6641,22 @@ void GLCanvas3D::_load_sla_shells() v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0)); v.set_instance_rotation({ 0.0, 0.0, (double)instance.rotation }); v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.); - v.set_convex_hull(mesh.convex_hull_3d()); + v.set_convex_hull(TriangleMesh{its_convex_hull(mesh)}); }; // adds objects' volumes for (const SLAPrintObject* obj : print->objects()) { unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); for (const SLAPrintObject::Instance& instance : obj->instances()) { - auto & m = obj->get_mesh_to_print(); - if (!m.empty()) { - add_volume(*obj, 0, instance, m, GLVolume::MODEL_COLOR[0], true); + std::shared_ptr m = obj->get_mesh_to_print(); + if (m && !m->empty()) { + add_volume(*obj, 0, instance, *m, GLVolume::MODEL_COLOR[0], true); // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when // through the update_volumes_colors_by_extruder() call. m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); - if (auto &tree_mesh = obj->support_mesh(); !tree_mesh.empty()) + if (auto &tree_mesh = obj->support_mesh().its; !tree_mesh.empty()) add_volume(*obj, -int(slaposSupportTree), instance, tree_mesh, GLVolume::SLA_SUPPORT_COLOR, true); - if (auto &pad_mesh = obj->pad_mesh(); !pad_mesh.empty()) + if (auto &pad_mesh = obj->pad_mesh().its; !pad_mesh.empty()) add_volume(*obj, -int(slaposPad), instance, pad_mesh, GLVolume::SLA_PAD_COLOR, false); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index ec6ea0a1ff..5bd2d98fdf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -55,7 +55,8 @@ void GLGizmoHollow::data_changed() } const SLAPrintObject* po = m_c->selection_info()->print_object(); - if (po != nullptr && po->get_mesh_to_print().empty()) + std::shared_ptr preview_mesh_ptr = po->get_mesh_to_print(); + if (po != nullptr && (!preview_mesh_ptr || preview_mesh_ptr->empty())) reslice_until_step(slaposAssembly); update_volumes(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp index 9e94b9604b..a9876bed1c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp @@ -48,7 +48,11 @@ void GLGizmoSlaBase::update_volumes() m_input_enabled = false; - TriangleMesh backend_mesh = po->get_mesh_to_print(); + TriangleMesh backend_mesh; + std::shared_ptr preview_mesh_ptr = po->get_mesh_to_print(); + if (preview_mesh_ptr) + backend_mesh = TriangleMesh{*preview_mesh_ptr}; + if (!backend_mesh.empty()) { // The backend has generated a valid mesh. Use it backend_mesh.transform(po->trafo().inverse()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 62f7eb5fe3..b33b0b2022 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -261,7 +261,10 @@ void Raycaster::on_update() // For sla printers we use the mesh generated by the backend const SLAPrintObject* po = get_pool()->selection_info()->print_object(); assert(po != nullptr); - m_sla_mesh_cache = po->get_mesh_to_print(); + std::shared_ptr preview_mesh_ptr = po->get_mesh_to_print(); + if (preview_mesh_ptr) + m_sla_mesh_cache = TriangleMesh{*preview_mesh_ptr}; + if (!m_sla_mesh_cache.empty()) { m_sla_mesh_cache.transform(po->trafo().inverse()); meshes.emplace_back(&m_sla_mesh_cache); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index dbdd24d151..a0ed276afc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6115,7 +6115,7 @@ void Plater::export_stl_obj(bool extended, bool selection_only) const SLAPrintObject *object = this->p->sla_print.get_print_object_by_model_object_id(mo.id()); - if (object->get_mesh_to_print().empty()) + if (auto m = object->get_mesh_to_print(); !m || m->empty()) mesh = mesh_to_export_fff(mo, instance_id); else { const Transform3d mesh_trafo_inv = object->trafo().inverse(); @@ -6155,7 +6155,11 @@ void Plater::export_stl_obj(bool extended, bool selection_only) inst_mesh.merge(inst_supports_mesh); } - TriangleMesh inst_object_mesh = object->get_mesh_to_print(); + std::shared_ptr m = object->get_mesh_to_print(); + TriangleMesh inst_object_mesh; + if (m) + inst_object_mesh = TriangleMesh{*m}; + inst_object_mesh.transform(mesh_trafo_inv); inst_object_mesh.transform(inst_transform, is_left_handed); From 7858b5d3cd9ea1148a1da9ac5a87eb6ea98a7b53 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 13 Jan 2023 09:45:20 +0100 Subject: [PATCH 65/75] Fix the missing merge option from multi-selection menu in SLA --- src/slic3r/GUI/GUI_ObjectList.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a5ddcf94c5..9d0311e630 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2595,9 +2595,6 @@ void ObjectList::delete_all_connectors_for_object(int obj_idx) bool ObjectList::can_merge_to_multipart_object() const { - if (printer_technology() == ptSLA || has_selected_cut_object()) - return false; - wxDataViewItemArray sels; GetSelections(sels); if (sels.IsEmpty()) From 53e358f32c50e5524316d5d82da788a0463a4de7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Jan 2023 09:59:41 +0100 Subject: [PATCH 66/75] Fix crash when cancelling part type change dialog --- src/slic3r/GUI/GUI_ObjectList.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 8a5d6f139a..0b03ecabe2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -4301,7 +4301,10 @@ void ObjectList::change_part_type() int selection = 0; if (auto it = std::find(types.begin(), types.end(), type); it != types.end()) selection = it - types.begin(); - const auto new_type = types[wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, selection)]; + + auto choice = wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, selection); + const auto new_type = choice >= 0 ? types[choice] : ModelVolumeType::INVALID; + if (new_type == type || new_type == ModelVolumeType::INVALID) return; From 7e74f781c62a0441e3d15f8709d4aa15d12195b4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Jan 2023 13:41:07 +0100 Subject: [PATCH 67/75] Minor fixes to sla print steps --- src/libslic3r/SLAPrintSteps.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 2f7243a9ca..f2496091d4 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -205,6 +205,8 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st if (cgalmeshptr) { m = MeshBoolean::cgal::cgal_to_indexed_triangle_set(*cgalmeshptr); handled = true; + } else { + BOOST_LOG_TRIVIAL(warning) << "CSG mesh is not egligible for proper CGAL booleans!"; } } else { // Normal cgal processing failed. If there are no negative volumes, @@ -290,7 +292,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st m = generate_preview_vdb(po, step); } - assert(po.m_preview_meshes[step].empty()); + assert(!po.m_preview_meshes[step]); if (!m.empty()) po.m_preview_meshes[step] = @@ -376,7 +378,7 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); po.m_hollowing_data->interior = std::move(interior); - indexed_triangle_set m = sla::get_mesh(*po.m_hollowing_data->interior); + indexed_triangle_set &m = sla::get_mesh(*po.m_hollowing_data->interior); if (!m.empty()) { // simplify mesh lossless @@ -390,7 +392,8 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) // Put the interior into the target mesh as a negative po.m_mesh_to_slice .emplace(slaposHollowing, - csg::CSGPart{std::make_shared(std::move(m)), csg::CSGType::Difference}); + csg::CSGPart{std::make_shared(m), + csg::CSGType::Difference}); generate_preview(po, slaposHollowing); } From 7d6c2cad0a6411c4be5a9322e9c19115e957ba33 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Jan 2023 19:39:30 +0100 Subject: [PATCH 68/75] Hotfix for hollowing issues --- src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp | 6 +++++- src/libslic3r/OpenVDBUtils.cpp | 5 +++++ src/libslic3r/OpenVDBUtils.hpp | 2 ++ src/libslic3r/SLA/Hollowing.cpp | 17 ++++++++-------- src/libslic3r/SLA/Hollowing.hpp | 24 +++++++++++------------ src/libslic3r/SLAPrint.cpp | 2 +- 6 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp index c76187723d..f64d17b9a4 100644 --- a/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/VoxelizeCSGMesh.hpp @@ -37,7 +37,11 @@ inline void perform_csg(CSGType op, VoxelGridPtr &dst, VoxelGridPtr &src) switch (op) { case CSGType::Union: - grid_union(*dst, *src); + if (is_grid_empty(*dst) && !is_grid_empty(*src)) + dst = clone(*src); + else + grid_union(*dst, *src); + break; case CSGType::Difference: grid_difference(*dst, *src); diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 5d00fac0da..21409445fc 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -300,4 +300,9 @@ void rescale_grid(VoxelGrid &grid, float scale) // grid.grid.setTransform(tr); } +bool is_grid_empty(const VoxelGrid &grid) +{ + return grid.grid.empty(); +} + } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index cb857020f7..d4996854c2 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -77,6 +77,8 @@ void grid_union(VoxelGrid &grid, VoxelGrid &arg); void grid_difference(VoxelGrid &grid, VoxelGrid &arg); void grid_intersection(VoxelGrid &grid, VoxelGrid &arg); +bool is_grid_empty(const VoxelGrid &grid); + } // namespace Slic3r #endif // OPENVDBUTILS_HPP diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 31289e7baf..634cde4695 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -75,11 +75,12 @@ InteriorPtr generate_interior(const VoxelGrid &vgrid, const HollowingConfig &hc, const JobController &ctl) { - double offset = hc.min_thickness; - double D = hc.closing_distance; - float in_range = 1.1f * float(offset + D); - auto narrowb = 3.f / get_voxel_scale(vgrid); - float out_range = narrowb; + double voxsc = get_voxel_scale(vgrid); + double offset = hc.min_thickness; // world units + double D = hc.closing_distance; // world units + float in_range = 1.1f * float(offset + D); // world units + float out_range = 1.f / voxsc; // world units + auto narrowb = 1.f; // voxel units (voxel count) if (ctl.stopcondition()) return {}; else ctl.statuscb(0, L("Hollowing")); @@ -91,12 +92,12 @@ InteriorPtr generate_interior(const VoxelGrid &vgrid, double iso_surface = D; if (D > EPSILON) { - in_range = narrowb; - gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, in_range); + gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, narrowb); - gridptr = dilate_grid(*gridptr, std::ceil(iso_surface), 0.f); + gridptr = dilate_grid(*gridptr, 1.1 * std::ceil(iso_surface), 0.f); out_range = iso_surface; + in_range = narrowb / voxsc; } else { iso_surface = -offset; } diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index c21bdff4e3..b6c07aff5a 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -91,15 +91,15 @@ inline InteriorPtr generate_interior(const indexed_triangle_set &mesh, auto statusfn = [&ctl](int){ return ctl.stopcondition && ctl.stopcondition(); }; auto grid = mesh_to_grid(mesh, MeshToGridParams{} .voxel_scale(voxel_scale) - .exterior_bandwidth(1.f) - .interior_bandwidth(1.f) + .exterior_bandwidth(3.f) + .interior_bandwidth(3.f) .statusfn(statusfn)); if (!grid || (ctl.stopcondition && ctl.stopcondition())) return {}; - if (its_is_splittable(mesh)) - grid = redistance_grid(*grid, 0.0f, 6.f / voxel_scale, 6.f / voxel_scale); +// if (its_is_splittable(mesh)) + grid = redistance_grid(*grid, 0.0f, 3.f, 3.f); return grid ? generate_interior(*grid, hc, ctl) : InteriorPtr{}; } @@ -132,11 +132,12 @@ InteriorPtr generate_interior(const Range &csgparts, const JobController &ctl = {}) { double mesh_vol = csgmesh_positive_maxvolume(csgparts); + double voxsc = get_voxel_scale(mesh_vol, hc); auto params = csg::VoxelizeParams{} - .voxel_scale(get_voxel_scale(mesh_vol, hc)) - .exterior_bandwidth(1.f) - .interior_bandwidth(1.f) + .voxel_scale(voxsc) + .exterior_bandwidth(3.f) + .interior_bandwidth(3.f) .statusfn([&ctl](int){ return ctl.stopcondition && ctl.stopcondition(); }); auto ptr = csg::voxelize_csgmesh(csgparts, params); @@ -144,11 +145,10 @@ InteriorPtr generate_interior(const Range &csgparts, if (!ptr || (ctl.stopcondition && ctl.stopcondition())) return {}; - if (csgparts.size() > 1 || its_is_splittable(*csg::get_mesh(*csgparts.begin()))) - ptr = redistance_grid(*ptr, - 0.0f, - 6.f / params.voxel_scale(), - 6.f / params.voxel_scale()); + // TODO: figure out issues without the redistance +// if (csgparts.size() > 1 || its_is_splittable(*csg::get_mesh(*csgparts.begin()))) + + ptr = redistance_grid(*ptr, 0.0f, 3.f, 3.f); return ptr ? generate_interior(*ptr, hc, ctl) : InteriorPtr{}; } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 80857e34a5..03c0df4ef1 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -13,7 +13,7 @@ #include #include -// #define SLAPRINT_DO_BENCHMARK + #define SLAPRINT_DO_BENCHMARK #ifdef SLAPRINT_DO_BENCHMARK #include From 0fa9bcc63b6daee4c58d73bb726e3311c78ea66b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Jan 2023 19:47:35 +0100 Subject: [PATCH 69/75] Fix crash with fff mmu --- src/slic3r/GUI/GLCanvas3D.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6b8499ddec..291cc57ece 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7100,9 +7100,12 @@ const ModelVolume *get_model_volume(const GLVolume &v, const Model &model) const ModelVolume * ret = nullptr; if (model.objects.size() < v.object_idx()) { - const ModelObject *obj = model.objects[v.object_idx()]; - if (obj->volumes.size() < v.volume_idx()) - ret = obj->volumes[v.volume_idx()]; + if (v.object_idx() < model.objects.size()) { + const ModelObject *obj = model.objects[v.object_idx()]; + if (v.volume_idx() < obj->volumes.size()) { + ret = obj->volumes[v.volume_idx()]; + } + } } return ret; From 9ee71ddd925b1f7ac2a3ca7daffb72f09d347a0e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Jan 2023 11:23:18 +0100 Subject: [PATCH 70/75] WIP fixing trafos --- src/libslic3r/SLA/SupportPoint.hpp | 9 ++++++++- src/libslic3r/SLA/SupportPointGenerator.cpp | 12 ++++++++++++ src/libslic3r/SLAPrint.cpp | 16 ++++++---------- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 4 ++-- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/SLA/SupportPoint.hpp b/src/libslic3r/SLA/SupportPoint.hpp index 71849a3643..cf6dbabbaf 100644 --- a/src/libslic3r/SLA/SupportPoint.hpp +++ b/src/libslic3r/SLA/SupportPoint.hpp @@ -3,7 +3,11 @@ #include -namespace Slic3r { namespace sla { +namespace Slic3r { + +class ModelObject; + +namespace sla { // An enum to keep track of where the current points on the ModelObject came from. enum class PointsStatus { @@ -62,6 +66,9 @@ struct SupportPoint using SupportPoints = std::vector; +SupportPoints transformed_support_points(const ModelObject &mo, + const Transform3d &trafo); + }} // namespace Slic3r::sla #endif // SUPPORTPOINT_HPP diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 6d159b37b6..193333bc6a 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -661,5 +661,17 @@ void SupportPointGenerator::output_expolygons(const ExPolygons& expolys, const s } #endif +SupportPoints transformed_support_points(const ModelObject &mo, + const Transform3d &trafo) +{ + auto spts = mo.sla_support_points; + Transform3f tr = trafo.cast(); + for (sla::SupportPoint& suppt : spts) { + suppt.pos = tr * suppt.pos; + } + + return spts; +} + } // namespace sla } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 03c0df4ef1..71d6e889dd 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1051,20 +1051,16 @@ SLAPrintObject::get_parts_to_slice(SLAPrintObjectStep untilstep) const sla::SupportPoints SLAPrintObject::transformed_support_points() const { - assert(m_model_object != nullptr); - auto spts = m_model_object->sla_support_points; - const Transform3d& vol_trafo = m_model_object->volumes.front()->get_transformation().get_matrix(); - const Transform3f& tr = (trafo() * vol_trafo).cast(); - for (sla::SupportPoint& suppt : spts) { - suppt.pos = tr * suppt.pos; - } - - return spts; + assert(model_object()); + + return sla::transformed_support_points(*model_object(), trafo()); } sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const { - return sla::transformed_drainhole_points(*this->model_object(), trafo()); + assert(model_object()); + + return sla::transformed_drainhole_points(*model_object(), trafo()); } DynamicConfig SLAPrintStatistics::config() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 5bd2d98fdf..31252d319e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -123,7 +123,7 @@ void GLGizmoHollow::render_points(const Selection& selection) return; double shift_z = m_c->selection_info()->print_object()->get_current_elevation(); - Transform3d trafo(inst->get_transformation().get_matrix() * inst->get_object()->volumes.front()->get_matrix()); + Transform3d trafo(inst->get_transformation().get_matrix()); trafo.translation()(2) += shift_z; const Geometry::Transformation transformation{trafo}; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b117a2bebb..301bbcdbe1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -153,7 +153,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection) return; double shift_z = m_c->selection_info()->print_object()->get_current_elevation(); - Transform3d trafo(inst->get_transformation().get_matrix() * inst->get_object()->volumes.front()->get_matrix()); + Transform3d trafo = inst->get_transformation().get_matrix(); trafo.translation()(2) += shift_z; const Geometry::Transformation transformation{trafo}; @@ -1084,7 +1084,7 @@ void GLGizmoSlaSupports::get_data_from_backend() if (po->model_object()->id() == mo->id()) { m_normal_cache.clear(); const std::vector& points = po->get_support_points(); - auto mat = (po->trafo() * po->model_object()->volumes.front()->get_transformation().get_matrix()).inverse().cast(); + auto mat = po->trafo().inverse().cast(); for (unsigned int i=0; i Date: Tue, 17 Jan 2023 13:13:09 +0100 Subject: [PATCH 71/75] Fixing sla support point and drillhole transformations May brake compatibility with 3mf files storing these objects from from previous Slic3r versions --- src/libslic3r/SLAPrintSteps.cpp | 2 -- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 13 +++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp | 13 ++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 15 ++++++++++++--- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index f2496091d4..8a71f2da78 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -292,8 +292,6 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st m = generate_preview_vdb(po, step); } - assert(!po.m_preview_meshes[step]); - if (!m.empty()) po.m_preview_meshes[step] = std::make_shared(std::move(m)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 31252d319e..fc54622e84 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -463,7 +463,16 @@ void GLGizmoHollow::update_hole_raycasters_for_picking_transform() assert(!m_hole_raycasters.empty()); const GLVolume* vol = m_parent.get_selection().get_first_volume(); - const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_scaling_factor_matrix().inverse(); + Geometry::Transformation transformation(vol->get_instance_transformation()); + + auto *inst = m_c->selection_info()->model_instance(); + if (inst && m_c->selection_info() && m_c->selection_info()->print_object()) { + double shift_z = m_c->selection_info()->print_object()->get_current_elevation(); + auto trafo = inst->get_transformation().get_matrix(); + trafo.translation()(2) += shift_z; + transformation.set_matrix(trafo); + } + const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); for (size_t i = 0; i < drain_holes.size(); ++i) { const sla::DrainHole& drain_hole = drain_holes[i]; @@ -471,7 +480,7 @@ void GLGizmoHollow::update_hole_raycasters_for_picking_transform() Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); const Eigen::AngleAxisd aa(q); - const Transform3d matrix = vol->world_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) * + const Transform3d matrix = transformation.get_matrix() * hole_matrix * Transform3d(aa.toRotationMatrix()) * Geometry::translation_transform(-drain_hole.height * Vec3d::UnitZ()) * Geometry::scale_transform(Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength)); m_hole_raycasters[i]->set_transform(matrix); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp index a9876bed1c..444e3bf466 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp @@ -148,12 +148,23 @@ bool GLGizmoSlaBase::unproject_on_mesh(const Vec2d& mouse_pos, std::pairselection_info()->model_instance(); + if (!inst) + return false; + + Transform3d trafo = m_volumes.volumes.front()->world_matrix(); + if (m_c->selection_info() && m_c->selection_info()->print_object()) { + double shift_z = m_c->selection_info()->print_object()->get_current_elevation(); + trafo = inst->get_transformation().get_matrix(); + trafo.translation()(2) += shift_z; + } + // The raycaster query Vec3f hit; Vec3f normal; if (m_c->raycaster()->raycaster()->unproject_on_mesh( mouse_pos, - m_volumes.volumes.front()->world_matrix(), + trafo/*m_volumes.volumes.front()->world_matrix()*/, wxGetApp().plater()->get_camera(), hit, normal, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 301bbcdbe1..e7ea945e36 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -1184,7 +1184,16 @@ void GLGizmoSlaSupports::update_point_raycasters_for_picking_transform() assert(!m_point_raycasters.empty()); const GLVolume* vol = m_parent.get_selection().get_first_volume(); - const Geometry::Transformation transformation(vol->world_matrix()); + Geometry::Transformation transformation(vol->world_matrix()); + + auto *inst = m_c->selection_info()->model_instance(); + if (inst && m_c->selection_info() && m_c->selection_info()->print_object()) { + double shift_z = m_c->selection_info()->print_object()->get_current_elevation(); + auto trafo = inst->get_transformation().get_matrix(); + trafo.translation()(2) += shift_z; + transformation.set_matrix(trafo); + } + const Transform3d instance_scaling_matrix_inverse = transformation.get_scaling_factor_matrix().inverse(); for (size_t i = 0; i < m_editing_cache.size(); ++i) { const Transform3d support_matrix = Geometry::translation_transform(m_editing_cache[i].support_point.pos.cast()) * instance_scaling_matrix_inverse; @@ -1195,13 +1204,13 @@ void GLGizmoSlaSupports::update_point_raycasters_for_picking_transform() Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); const Eigen::AngleAxisd aa(q); - const Transform3d cone_matrix = vol->world_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * + const Transform3d cone_matrix = transformation.get_matrix() * support_matrix * Transform3d(aa.toRotationMatrix()) * Geometry::assemble_transform((CONE_HEIGHT + m_editing_cache[i].support_point.head_front_radius * RenderPointScale) * Vec3d::UnitZ(), Vec3d(PI, 0.0, 0.0), Vec3d(CONE_RADIUS, CONE_RADIUS, CONE_HEIGHT)); m_point_raycasters[i].second->set_transform(cone_matrix); const double radius = (double)m_editing_cache[i].support_point.head_front_radius * RenderPointScale; - const Transform3d sphere_matrix = vol->world_matrix() * support_matrix * Geometry::scale_transform(radius); + const Transform3d sphere_matrix = transformation.get_matrix() * support_matrix * Geometry::scale_transform(radius); m_point_raycasters[i].first->set_transform(sphere_matrix); } } From 53287002b30faeee43509e7a3ba486c31d269d65 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Jan 2023 17:18:29 +0100 Subject: [PATCH 72/75] Fix crash when changing hollowing thickness --- src/libslic3r/SLAPrintSteps.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 8a71f2da78..269195bc01 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -349,6 +349,7 @@ void SLAPrint::Steps::mesh_assembly(SLAPrintObject &po) void SLAPrint::Steps::hollow_model(SLAPrintObject &po) { po.m_hollowing_data.reset(); + po.m_supportdata.reset(); clear_csg(po.m_mesh_to_slice, slaposDrillHoles); clear_csg(po.m_mesh_to_slice, slaposHollowing); @@ -400,6 +401,7 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) // Drill holes into the hollowed/original mesh. void SLAPrint::Steps::drill_holes(SLAPrintObject &po) { + po.m_supportdata.reset(); clear_csg(po.m_mesh_to_slice, slaposDrillHoles); csg::model_to_csgmesh(*po.model_object(), po.trafo(), From b403ba10c126c45ff8c38ab69fb62455081507ef Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Jan 2023 11:20:22 +0100 Subject: [PATCH 73/75] Fix mirroring inside volumes trafos Fix leftover after successive slicing runs --- src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp | 3 ++- src/libslic3r/SLAPrintSteps.cpp | 15 ++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp index f81a445416..aabe9a2de3 100644 --- a/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp +++ b/src/libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp @@ -26,7 +26,8 @@ MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart) MeshBoolean::cgal::CGALMeshPtr ret; indexed_triangle_set m = *its; - its_transform(m, get_transform(csgpart)); + auto tr = get_transform(csgpart); + its_transform(m, get_transform(csgpart), true); try { ret = MeshBoolean::cgal::triangle_mesh_to_cgal(m); diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 269195bc01..92260deecb 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -150,7 +150,7 @@ static indexed_triangle_set csgmesh_merge_positive_parts(const Cont &csgmesh) const indexed_triangle_set * pmesh = csg::get_mesh(csgpart); if (pmesh && op == csg::CSGType::Union) { indexed_triangle_set mcpy = *pmesh; - its_transform(mcpy, csg::get_transform(csgpart)); + its_transform(mcpy, csg::get_transform(csgpart), true); its_merge(m, mcpy); } } @@ -292,9 +292,8 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st m = generate_preview_vdb(po, step); } - if (!m.empty()) - po.m_preview_meshes[step] = - std::make_shared(std::move(m)); + po.m_preview_meshes[step] = + std::make_shared(std::move(m)); for (size_t i = size_t(step) + 1; i < slaposCount; ++i) { @@ -408,13 +407,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) csg_inserter{po.m_mesh_to_slice, slaposDrillHoles}, csg::mpartsDrillHoles); - auto r = po.m_mesh_to_slice.equal_range(slaposDrillHoles); - - // update preview mesh - if (r.first != r.second) - generate_preview(po, slaposDrillHoles); - else - po.m_preview_meshes[slaposDrillHoles] = po.get_mesh_to_print(); + generate_preview(po, slaposDrillHoles); // Release the data, won't be needed anymore, takes huge amount of ram if (po.m_hollowing_data && po.m_hollowing_data->interior) From ee15fe62382a08842f2c105d25c069a19d971354 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Jan 2023 15:15:01 +0100 Subject: [PATCH 74/75] Fix crash with cut gizmo --- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index b33b0b2022..a8dc16c558 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -259,9 +259,11 @@ void Raycaster::on_update() bool force_raycaster_regeneration = false; if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) { // For sla printers we use the mesh generated by the backend + std::shared_ptr preview_mesh_ptr; const SLAPrintObject* po = get_pool()->selection_info()->print_object(); - assert(po != nullptr); - std::shared_ptr preview_mesh_ptr = po->get_mesh_to_print(); + if (po) + preview_mesh_ptr = po->get_mesh_to_print(); + if (preview_mesh_ptr) m_sla_mesh_cache = TriangleMesh{*preview_mesh_ptr}; @@ -323,12 +325,13 @@ void ObjectClipper::on_update() if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) { // For sla printers we use the mesh generated by the backend const SLAPrintObject* po = get_pool()->selection_info()->print_object(); - assert(po != nullptr); - auto partstoslice = po->get_parts_to_slice(); - if (! partstoslice.empty()) { - mc = std::make_unique(); - mc->set_mesh(range(partstoslice)); - mc_tr = Geometry::Transformation{po->trafo().inverse().cast()}; + if (po) { + auto partstoslice = po->get_parts_to_slice(); + if (! partstoslice.empty()) { + mc = std::make_unique(); + mc->set_mesh(range(partstoslice)); + mc_tr = Geometry::Transformation{po->trafo().inverse().cast()}; + } } } From ce2659141afbecb3ae757fea9a32c140937682ee Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Jan 2023 16:56:33 +0100 Subject: [PATCH 75/75] Fix sidebar support combobox in SLA --- src/slic3r/GUI/Plater.cpp | 8 ++++++-- src/slic3r/GUI/Tab.cpp | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 673c3892f4..69a848496d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -555,10 +555,14 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : std::string treetype = get_sla_suptree_prefix(new_conf); - if (selection == _("Everywhere")) + if (selection == _("Everywhere")) { new_conf.set_key_value(treetype + "support_buildplate_only", new ConfigOptionBool(false)); - else if (selection == _("Support on build plate only")) + new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(false)); + } + else if (selection == _("Support on build plate only")) { new_conf.set_key_value(treetype + "support_buildplate_only", new ConfigOptionBool(true)); + new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(false)); + } else if (selection == _("For support enforcers only")) { new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(true)); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index e519a52e36..6e43afc3cf 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1046,7 +1046,7 @@ static wxString support_combo_value_for_config(const DynamicPrintConfig &config, return ! config.opt_bool(support) ? _("None") : - (is_fff && !config.opt_bool("support_material_auto")) ? + ((is_fff && !config.opt_bool("support_material_auto")) || (!is_fff && config.opt_bool("support_enforcers_only"))) ? _("For support enforcers only") : (config.opt_bool(buildplate_only) ? _("Support on build plate only") : _("Everywhere")); @@ -1085,7 +1085,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (is_fff ? (opt_key == "support_material" || opt_key == "support_material_auto" || opt_key == "support_material_buildplate_only") : - (opt_key == "supports_enable" || opt_key == "support_tree_type" || opt_key == get_sla_suptree_prefix(*m_config) + "support_buildplate_only")) + (opt_key == "supports_enable" || opt_key == "support_tree_type" || opt_key == get_sla_suptree_prefix(*m_config) + "support_buildplate_only" || opt_key == "support_enforcers_only")) og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff)); if (! is_fff && (opt_key == "pad_enable" || opt_key == "pad_around_object"))