diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 41bb64c0a..69b649f5c 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -54,14 +54,16 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh, { openvdb::initialize(); - TriangleMeshPtrs meshparts_raw = mesh.split(); - auto meshparts = reserve_vector>(meshparts_raw.size()); - for (auto *p : meshparts_raw) - meshparts.emplace_back(p); + std::vector meshparts; + its_split(mesh, std::back_inserter(meshparts)); + +// TriangleMeshPtrs meshparts_raw = mesh.split(); +// auto meshparts = reserve_vector>(meshparts_raw.size()); +// for (auto *p : meshparts_raw) +// meshparts.emplace_back(p); auto it = std::remove_if(meshparts.begin(), meshparts.end(), [](auto &m) { - m->require_shared_vertices(); - return m->volume() < EPSILON; + return its_volume(m) < EPSILON; }); meshparts.erase(it, meshparts.end()); @@ -69,7 +71,7 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh, openvdb::FloatGrid::Ptr grid; for (auto &m : meshparts) { auto subgrid = openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{*m, voxel_scale}, tr, exteriorBandWidth, + TriangleMeshDataAdapter{m, voxel_scale}, tr, exteriorBandWidth, interiorBandWidth, flags); if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid); @@ -91,11 +93,10 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh, return grid; } -template -sla::Contour3D _volumeToMesh(const Grid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) +indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) { openvdb::initialize(); @@ -111,36 +112,20 @@ sla::Contour3D _volumeToMesh(const Grid &grid, scale = grid.template metaValue("voxel_scale"); } catch (...) { } - sla::Contour3D ret; - ret.points.reserve(points.size()); - ret.faces3.reserve(triangles.size()); - ret.faces4.reserve(quads.size()); + indexed_triangle_set ret; + ret.vertices.reserve(points.size()); + ret.indices.reserve(triangles.size() + quads.size() * 2); - for (auto &v : points) ret.points.emplace_back(to_vec3d(v) / scale); - for (auto &v : triangles) ret.faces3.emplace_back(to_vec3i(v)); - for (auto &v : quads) ret.faces4.emplace_back(to_vec4i(v)); + 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)); + } return ret; } -TriangleMesh grid_to_mesh(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) -{ - return to_triangle_mesh( - _volumeToMesh(grid, isovalue, adaptivity, relaxDisorientedTriangles)); -} - -sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) -{ - return _volumeToMesh(grid, isovalue, adaptivity, - relaxDisorientedTriangles); -} - openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, double iso, double er, diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index 92530dc54..c101ddc11 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -35,11 +35,6 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set & mesh, float interiorBandWidth = 3.0f, int flags = 0); -sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles = true); - indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid, double isovalue = 0.0, double adaptivity = 0.0, diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 151ceba11..03d2fc721 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -77,7 +77,7 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh, if (ctl.stopcondition()) return {}; else ctl.statuscb(0, L("Hollowing")); - auto gridptr = mesh_to_grid(mesh, {}, voxel_scale, out_range, in_range); + auto gridptr = mesh_to_grid(mesh.its, {}, voxel_scale, out_range, in_range); assert(gridptr); @@ -136,19 +136,15 @@ InteriorPtr generate_interior(const TriangleMesh & mesh, if (interior && !interior->mesh.empty()) { - // This flips the normals to be outward facing... - interior->mesh.require_shared_vertices(); - indexed_triangle_set its = std::move(interior->mesh.its); + // flip normals back... + swap_normals(interior->mesh); + Slic3r::simplify_mesh(interior->mesh); - Slic3r::simplify_mesh(its); + its_compactify_vertices(interior->mesh); + its_merge_vertices(interior->mesh); // flip normals back... - for (stl_triangle_vertex_indices &ind : its.indices) - std::swap(ind(0), ind(2)); - - interior->mesh = Slic3r::TriangleMesh{its}; - interior->mesh.repaired = true; - interior->mesh.require_shared_vertices(); + swap_normals(interior->mesh); } return interior; @@ -325,7 +321,7 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags) if (flags & hfRemoveInsideTriangles && interior.gridptr) remove_inside_triangles(mesh, interior); - mesh.merge(interior.mesh); + mesh.merge(TriangleMesh{interior.mesh}); mesh.require_shared_vertices(); } diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index ffad480c3..3516509dd 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -99,6 +99,12 @@ void cut_drainholes(std::vector & obj_slices, const sla::DrainHoles & holes, std::function thr); +inline void swap_normals(indexed_triangle_set &its) +{ + for (auto &face : its.indices) + std::swap(face(0), face(2)); +} + } // namespace sla } // namespace Slic3r diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index acd091ca2..8d995b59e 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -480,7 +480,6 @@ void pad_blueprint(const indexed_triangle_set &mesh, { if (mesh.empty()) return; - assert(mesh.has_shared_vertices()); std::vector out = slice_mesh_ex(mesh, heights, thrfn); size_t count = 0; diff --git a/src/libslic3r/SLA/SupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp index 3d246b5cc..d9f8958a2 100644 --- a/src/libslic3r/SLA/SupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -45,7 +45,6 @@ std::vector SupportTree::slice(const std::vector &grid, if (!sup_mesh.empty()) { slices.emplace_back(); - assert(sup_mesh.has_shared_vertices()); slices.back() = slice_mesh_ex(sup_mesh, grid, cr, ctl().cancelfn); } @@ -59,7 +58,6 @@ std::vector SupportTree::slice(const std::vector &grid, auto padgrid = reserve_vector(size_t(cap > 0 ? cap : 0)); std::copy(grid.begin(), maxzit, std::back_inserter(padgrid)); - assert(pad_mesh.has_shared_vertices()); slices.back() = slice_mesh_ex(pad_mesh, padgrid, cr, ctl().cancelfn); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index f0c223e95..27423793e 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1067,6 +1067,7 @@ Vec3d SLAPrint::relative_correction() const namespace { // dummy empty static containers for return values in some methods const std::vector EMPTY_SLICES; const TriangleMesh EMPTY_MESH; +const indexed_triangle_set EMPTY_TRIANGLE_SET; const ExPolygons EMPTY_SLICE; const std::vector EMPTY_SUPPORT_POINTS; } @@ -1143,13 +1144,13 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const return EMPTY_MESH; } -const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const +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_MESH; + return EMPTY_TRIANGLE_SET; } const TriangleMesh &SLAPrintObject::transformed_mesh() const { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 223254c60..e11926c7e 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -79,8 +79,8 @@ public: const TriangleMesh& pad_mesh() const; // Ready after this->is_step_done(slaposDrillHoles) is true - const TriangleMesh& hollowed_interior_mesh() const; - + 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 { diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 8287e8461..a640d5eac 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -194,7 +194,7 @@ static std::vector create_exclude_mask( const sla::Interior &interior, const std::vector &holes) { - FaceHash interior_hash{sla::get_mesh(interior).its}; + FaceHash interior_hash{sla::get_mesh(interior)}; std::vector exclude_mask(its.indices.size(), false); @@ -489,11 +489,11 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) nullptr; if (interior && ! sla::get_mesh(*interior).empty()) { - TriangleMesh interiormesh = sla::get_mesh(*interior); - interiormesh.repaired = false; - interiormesh.repair(true); + 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.its, slice_grid, params, thr); + + std::vector interior_slices = slice_mesh_ex(interiormesh, slice_grid, closing_r, thr); sla::ccr::for_each(size_t(0), interior_slices.size(), [&po, &interior_slices] (size_t i) { diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 39eb68735..b29771357 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1161,4 +1161,67 @@ void its_merge(indexed_triangle_set &A, const Pointf3s &triangles) its_merge(A, trianglesf); } +float its_volume(const indexed_triangle_set &its) +{ + if (its.empty()) return 0.; + + // Choose a point, any point as the reference. + auto p0 = its.vertices.front(); + float volume = 0.f; + for (size_t i = 0; i < its.indices.size(); ++ i) { + // Do dot product to get distance from point to plane. + its_triangle triangle = its_triangle_vertices(its, i); + Vec3f U = triangle[1] - triangle[0]; + Vec3f V = triangle[2] - triangle[0]; + Vec3f C = U.cross(V); + Vec3f normal = C.normalized(); + float area = 0.5 * C.norm(); + float height = normal.dot(triangle[0] - p0); + volume += (area * height) / 3.0f; + } + + return volume; } + +PartMap::PartMap(const indexed_triangle_set & its, + const std::vector> &vfidx) + : count(0), face_part_indices(its.indices.size(), UNVISITED) +{ + auto next_face_idx = [this](size_t start) { + size_t i = start; + while (face_part_indices[i++] >= 0); + return i; + }; + + size_t face_idx = 0; + size_t part_idx = 0; + + do { + face_idx = next_face_idx(face_idx); + } while(split_recurse(its, vfidx, face_idx, part_idx++)); + + count = size_t(part_idx - 1); +} + +bool PartMap::split_recurse(const indexed_triangle_set & its, + const std::vector> &vfidx, + size_t fi, + size_t part_idx) +{ + if (face_part_indices[fi] >= 0) + return false; + + face_part_indices[fi] = part_idx; + const auto &face = its.indices[fi]; + + for (size_t v = 0; v < 3; ++v) { + auto vi = face(v); + const std::vector neigh_faces = vfidx[vi]; + for (size_t neigh_face : neigh_faces) + split_recurse(its, vfidx, neigh_face, part_idx); + } + + return true; +} + +} // namespace Slic3r diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index c2882fbbf..f31e4dcef 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -136,6 +136,77 @@ int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit = // Remove vertices, which none of the faces references. Return number of freed vertices. int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true); +// Used by its_split to map each face of a mesh to a part index. Can be used +// to query the number of parts in a mesh. +struct PartMap +{ + static constexpr int UNVISITED = -1; + + size_t count; + std::vector face_part_indices; + + PartMap(const indexed_triangle_set & its, + const std::vector> &vfidx); + + explicit PartMap(const indexed_triangle_set &its) + : PartMap(its, create_vertex_faces_index(its)) + {} + +private: + + bool split_recurse(const indexed_triangle_set & its, + const std::vector> &vfidx, + size_t fi, + size_t part_idx); +}; + +template +void its_split(const indexed_triangle_set &its, + const PartMap & partmap, + OutputIt out_it) +{ + std::vector meshes(partmap.count); + + std::vector vidx_conv(its.vertices.size() * meshes.size(), + PartMap::UNVISITED); + + auto &parts = partmap.face_part_indices; + + for (size_t fi = 0; fi < parts.size(); ++fi) { + int pi = parts[fi]; + + if (pi < 0) continue; + + indexed_triangle_set &part_its = meshes[size_t(pi)]; + const auto & face = its.indices[fi]; + size_t conv_begin = (pi * its.vertices.size()); + Vec3i new_face; + for (size_t v = 0; v < 3; ++v) { + auto vi = face(v); + size_t conv_idx = conv_begin + vi; + + if (vidx_conv[conv_idx] == PartMap::UNVISITED) { + vidx_conv[conv_idx] = part_its.vertices.size(); + part_its.vertices.emplace_back(its.vertices[size_t(vi)]); + } + + new_face(v) = vidx_conv[conv_idx]; + } + + part_its.indices.emplace_back(new_face); + } + + for (indexed_triangle_set &part_its : meshes) + out_it = std::move(part_its); +} + +template +void its_split(const indexed_triangle_set & its, + OutputIt out_it) +{ + its_split(its, PartMap{its}, out_it); +} + // Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors. void its_shrink_to_fit(indexed_triangle_set &its); @@ -164,6 +235,8 @@ inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its, return (tri[1] - tri[0]).cross(tri[2] - tri[0]); } +float its_volume(const indexed_triangle_set &its); + void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B); void its_merge(indexed_triangle_set &A, const std::vector &triangles); void its_merge(indexed_triangle_set &A, const Pointf3s &triangles); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index acb85d539..425e11f73 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -208,7 +208,7 @@ void HollowedMesh::on_update() m_drainholes = print_object->model_object()->sla_drain_holes; m_old_hollowing_timestamp = timestamp; - const TriangleMesh &interior = print_object->hollowed_interior_mesh(); + const indexed_triangle_set &interior = print_object->hollowed_interior_mesh(); if (!interior.empty()) { m_hollowed_interior_transformed = std::make_unique(interior); m_hollowed_interior_transformed->repaired = false; diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 1f5ca3845..5662d8b0d 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -20,3 +20,23 @@ TEST_CASE("Hollow two overlapping spheres") { sphere1.WriteOBJFile("twospheres.obj"); } +TEST_CASE("Split its") { + using namespace Slic3r; + + TriangleMesh sphere1 = make_sphere(10., 2 * PI / 20.), sphere2 = sphere1; + + sphere1.translate(-5.f, 0.f, 0.f); + sphere2.translate( 5.f, 0.f, 0.f); + + sphere1.merge(sphere2); + sphere1.require_shared_vertices(); + + std::vector parts; + its_split(sphere1.its, std::back_inserter(parts)); + + size_t part_idx = 0; + for (auto &part_its : parts) { + its_write_obj(part_its, (std::string("part_its") + std::to_string(part_idx++) + ".obj").c_str()); + } +} + diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 1bb8b2324..be9bf9741 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -94,7 +94,7 @@ void test_supports(const std::string &obj_filename, if (hollowingcfg.enabled) { sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg); REQUIRE(interior); - mesh.merge(sla::get_mesh(*interior)); + mesh.merge(TriangleMesh{sla::get_mesh(*interior)}); mesh.require_shared_vertices(); }