diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp index d7ce26bb2..1bb1943ef 100644 --- a/src/libslic3r/SLA/SLABoilerPlate.hpp +++ b/src/libslic3r/SLA/SLABoilerPlate.hpp @@ -12,25 +12,35 @@ #include "SLASpatIndex.hpp" namespace Slic3r { + +typedef Eigen::Matrix Vec4i; + namespace sla { /// Intermediate struct for a 3D mesh struct Contour3D { Pointf3s points; - std::vector indices; + std::vector faces3; + std::vector faces4; Contour3D& merge(const Contour3D& ctr) { - auto s3 = coord_t(points.size()); - auto s = indices.size(); + auto N = coord_t(points.size()); + auto N_f3 = faces3.size(); + auto N_f4 = faces4.size(); points.insert(points.end(), ctr.points.begin(), ctr.points.end()); - indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end()); + faces3.insert(faces3.end(), ctr.faces3.begin(), ctr.faces3.end()); + faces4.insert(faces4.end(), ctr.faces4.begin(), ctr.faces4.end()); - for(size_t n = s; n < indices.size(); n++) { - auto& idx = indices[n]; idx.x() += s3; idx.y() += s3; idx.z() += s3; + for(size_t n = N_f3; n < faces3.size(); n++) { + auto& idx = faces3[n]; idx.x() += N; idx.y() += N; idx.z() += N; } + for(size_t n = N_f4; n < faces4.size(); n++) { + auto& idx = faces4[n]; for (int k = 0; k < 4; k++) idx(k) += N; + } + return *this; } @@ -38,10 +48,10 @@ struct Contour3D { { const size_t offs = points.size(); points.insert(points.end(), triangles.begin(), triangles.end()); - indices.reserve(indices.size() + points.size() / 3); + faces3.reserve(faces3.size() + points.size() / 3); for(int i = int(offs); i < int(points.size()); i += 3) - indices.emplace_back(i, i + 1, i + 2); + faces3.emplace_back(i, i + 1, i + 2); return *this; } @@ -53,10 +63,16 @@ struct Contour3D { stream << "v " << p.transpose() << "\n"; } - for(auto& f : indices) { + for(auto& f : faces3) { stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n"; } + + for(auto& f : faces4) { + stream << "f " << (f + Vec4i(1, 1, 1, 1)).transpose() << "\n"; + } } + + bool empty() const { return points.empty() || (faces4.empty() && faces3.empty()); } }; using ClusterEl = std::vector; @@ -82,19 +98,45 @@ ClusteredPoints cluster( // Calculate the normals for the selected points (from 'points' set) on the // mesh. This will call squared distance for each point. PointSet normals(const PointSet& points, - const EigenMesh3D& mesh, + const EigenMesh3D& convert_mesh, double eps = 0.05, // min distance from edges std::function throw_on_cancel = [](){}, const std::vector& selected_points = {}); /// Mesh from an existing contour. -inline TriangleMesh mesh(const Contour3D& ctour) { - return {ctour.points, ctour.indices}; +inline TriangleMesh convert_mesh(const Contour3D& ctour) { + return {ctour.points, ctour.faces3}; } /// Mesh from an evaporating 3D contour -inline TriangleMesh mesh(Contour3D&& ctour) { - return {std::move(ctour.points), std::move(ctour.indices)}; +inline TriangleMesh convert_mesh(Contour3D&& ctour) { + return {std::move(ctour.points), std::move(ctour.faces3)}; +} + +inline Contour3D convert_mesh(const TriangleMesh &trmesh) { + Contour3D ret; + ret.points.reserve(trmesh.its.vertices.size()); + ret.faces3.reserve(trmesh.its.indices.size()); + + for (auto &v : trmesh.its.vertices) + ret.points.emplace_back(v.cast()); + + std::copy(trmesh.its.indices.begin(), trmesh.its.indices.end(), + std::back_inserter(ret.faces3)); + + return ret; +} + +inline Contour3D convert_mesh(TriangleMesh &&trmesh) { + Contour3D ret; + ret.points.reserve(trmesh.its.vertices.size()); + + for (auto &v : trmesh.its.vertices) + ret.points.emplace_back(v.cast()); + + ret.faces3.swap(trmesh.its.indices); + + return ret; } } diff --git a/src/libslic3r/SLA/SLAPad.cpp b/src/libslic3r/SLA/SLAPad.cpp index 7cd9eb4e4..d0e802d84 100644 --- a/src/libslic3r/SLA/SLAPad.cpp +++ b/src/libslic3r/SLA/SLAPad.cpp @@ -69,7 +69,7 @@ Contour3D walls( // Shorthand for the vertex arrays auto& upts = upper.points, &lpts = lower.points; - auto& rpts = ret.points; auto& ind = ret.indices; + auto& rpts = ret.points; auto& ind = ret.faces3; // If the Z levels are flipped, or the offset difference is negative, we // will interpret that as the triangles normals should be inverted. @@ -677,7 +677,7 @@ void create_pad(const ExPolygons &sup_blueprint, ThrowOnCancel thr) { Contour3D t = create_pad_geometry(sup_blueprint, model_blueprint, cfg, thr); - out.merge(mesh(std::move(t))); + out.merge(convert_mesh(std::move(t))); } std::string PadConfig::validate() const diff --git a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp b/src/libslic3r/SLA/SLASupportTreeBuilder.cpp index 2e0310ed8..0c276738a 100644 --- a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SLASupportTreeBuilder.cpp @@ -12,7 +12,7 @@ Contour3D sphere(double rho, Portion portion, double fa) { if(rho <= 1e-6 && rho >= -1e-6) return ret; auto& vertices = ret.points; - auto& facets = ret.indices; + auto& facets = ret.faces3; // Algorithm: // Add points one-by-one to the sphere grid and form facets using relative @@ -102,7 +102,7 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) auto steps = int(ssteps); auto& points = ret.points; - auto& indices = ret.indices; + auto& indices = ret.faces3; points.reserve(2*ssteps); double a = 2*PI/steps; @@ -211,8 +211,8 @@ Head::Head(double r_big_mm, coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; - mesh.indices.emplace_back(i1s1, i2s1, i2s2); - mesh.indices.emplace_back(i1s1, i2s2, i1s2); + mesh.faces3.emplace_back(i1s1, i2s1, i2s2); + mesh.faces3.emplace_back(i1s1, i2s2, i1s2); } auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); @@ -220,8 +220,8 @@ Head::Head(double r_big_mm, auto i1s2 = coord_t(s1.points.size()); auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; - mesh.indices.emplace_back(i2s2, i2s1, i1s1); - mesh.indices.emplace_back(i1s2, i2s2, i1s1); + mesh.faces3.emplace_back(i2s2, i2s1, i1s1); + mesh.faces3.emplace_back(i1s2, i2s2, i1s1); // To simplify further processing, we translate the mesh so that the // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) @@ -240,7 +240,7 @@ Pillar::Pillar(const Vec3d &jp, const Vec3d &endp, double radius, size_t st): // move the data. Contour3D body = cylinder(radius, height, st, endp); mesh.points.swap(body.points); - mesh.indices.swap(body.indices); + mesh.faces3.swap(body.faces3); } } @@ -275,7 +275,7 @@ Pillar &Pillar::add_base(double baseheight, double radius) base.points.emplace_back(endpt); base.points.emplace_back(ep); - auto& indices = base.indices; + auto& indices = base.faces3; auto hcenter = int(base.points.size() - 1); auto lcenter = int(base.points.size() - 2); auto offs = int(steps); @@ -466,7 +466,7 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh() const return m_meshcache; } - m_meshcache = mesh(merged); + m_meshcache = convert_mesh(merged); // The mesh will be passed by const-pointer to TriangleMeshSlicer, // which will need this. diff --git a/tests/hollowing/CMakeLists.txt b/tests/hollowing/CMakeLists.txt index 79c12baef..9679a1b77 100644 --- a/tests/hollowing/CMakeLists.txt +++ b/tests/hollowing/CMakeLists.txt @@ -1,5 +1,5 @@ if(TARGET OpenVDB::openvdb) - add_executable(hollowing_tests hollowing_tests.cpp) + add_executable(hollowing_tests hollowing_test_main.cpp hollowing_tests.cpp openvdb_utils.cpp openvdb_utils.hpp) #find_package(GTest REQUIRED) #target_link_libraries(hollowing_tests libslic3r OpenVDB::openvdb GTest::GTest GTest::Main) diff --git a/tests/hollowing/hollowing_test_main.cpp b/tests/hollowing/hollowing_test_main.cpp new file mode 100644 index 000000000..b2aa80259 --- /dev/null +++ b/tests/hollowing/hollowing_test_main.cpp @@ -0,0 +1 @@ +#include diff --git a/tests/hollowing/hollowing_tests.cpp b/tests/hollowing/hollowing_tests.cpp index 846f7e831..a7b2874eb 100644 --- a/tests/hollowing/hollowing_tests.cpp +++ b/tests/hollowing/hollowing_tests.cpp @@ -1,10 +1,8 @@ #include -#include +#include +#include -#include -#include -#include -#include +#include "openvdb_utils.hpp" #include "libslic3r/Format/OBJ.hpp" #if defined(WIN32) || defined(_WIN32) @@ -13,22 +11,6 @@ #define PATH_SEPARATOR R"(/)" #endif -class TriangleMeshDataAdapter { -public: - Slic3r::TriangleMesh mesh; - - size_t polygonCount() const { return mesh.its.indices.size(); } - size_t pointCount() const { return mesh.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 - void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const { - auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v))); - Slic3r::Vec3d p = mesh.its.vertices[vidx].cast(); - pos = {double(p.x()), double(p.y()), p.z()}; - } -}; - static Slic3r::TriangleMesh load_model(const std::string &obj_filename) { Slic3r::TriangleMesh mesh; @@ -38,24 +20,21 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) } TEST_CASE("Load object", "[Hollowing]") { - TriangleMeshDataAdapter mesh{load_model("20mm_cube.obj")}; - auto ptr = openvdb::tools::meshToVolume(mesh, {}); + Slic3r::TriangleMesh mesh = load_model("20mm_cube.obj"); + + Slic3r::sla::Contour3D imesh = Slic3r::sla::convert_mesh(mesh); + auto ptr = Slic3r::meshToVolume(imesh, {}); REQUIRE(ptr); - std::vector points; - std::vector quad_indices; - std::vector triangle_indices; + Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, -1., 0.0, true); - openvdb::tools::volumeToMesh(*ptr, points, triangle_indices, quad_indices, 0.0, 1.0, true); + REQUIRE(!omesh.empty()); - std::cout << "Triangle count: " << triangle_indices.size() << std::endl; - std::cout << "Quad count: " << quad_indices.size() << std::endl; - std::cout << "Point count: " << points.size() << " vs " << mesh.mesh.its.vertices.size() << std::endl; + std::fstream outfile{"out.obj", std::ios::out}; + omesh.to_obj(outfile); + + imesh.merge(omesh); + std::fstream merged_outfile("merged_out.obj", std::ios::out); + imesh.to_obj(merged_outfile); } - -//int main(int argc, char **argv) -//{ -// ::testing::InitGoogleTest(&argc, argv); -// return RUN_ALL_TESTS(); -//} diff --git a/tests/hollowing/openvdb_utils.cpp b/tests/hollowing/openvdb_utils.cpp new file mode 100644 index 000000000..38e96a885 --- /dev/null +++ b/tests/hollowing/openvdb_utils.cpp @@ -0,0 +1,93 @@ +#include "openvdb_utils.hpp" + +namespace Slic3r { + +class TriangleMeshDataAdapter { +public: + const TriangleMesh &mesh; + + size_t polygonCount() const { return mesh.its.indices.size(); } + size_t pointCount() const { return mesh.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 + void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const; +}; + +class Contour3DDataAdapter { +public: + const sla::Contour3D &mesh; + + size_t polygonCount() const { return mesh.faces3.size() + mesh.faces4.size(); } + size_t pointCount() const { return mesh.points.size(); } + size_t vertexCount(size_t n) const { return n < mesh.faces3.size() ? 3 : 4; } + + // Return position pos in local grid index space for polygon n and vertex v + void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const; +}; + +void TriangleMeshDataAdapter::getIndexSpacePoint(size_t n, + size_t v, + openvdb::Vec3d &pos) const +{ + auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v))); + Slic3r::Vec3d p = mesh.its.vertices[vidx].cast(); + pos = {p.x(), p.y(), p.z()}; +} + +void Contour3DDataAdapter::getIndexSpacePoint(size_t n, + size_t v, + openvdb::Vec3d &pos) const +{ + size_t vidx = 0; + if (n < mesh.faces3.size()) vidx = size_t(mesh.faces3[n](Eigen::Index(v))); + else vidx = size_t(mesh.faces4[n - mesh.faces3.size()](Eigen::Index(v))); + + Slic3r::Vec3d p = mesh.points[vidx]; + pos = {p.x(), p.y(), p.z()}; +} + +openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, + const openvdb::math::Transform &tr) +{ + return openvdb::tools::meshToVolume( + TriangleMeshDataAdapter{mesh}, tr); +} + +openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, + const openvdb::math::Transform &tr) +{ + return openvdb::tools::meshToVolume( + Contour3DDataAdapter{mesh}, tr); +} + +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 Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; } + +sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) +{ + std::vector points; + std::vector triangles; + std::vector quads; + + openvdb::tools::volumeToMesh(grid, points, triangles, quads, isovalue, + adaptivity, relaxDisorientedTriangles); + + sla::Contour3D ret; + ret.points.reserve(points.size()); + ret.faces3.reserve(triangles.size()); + ret.faces4.reserve(quads.size()); + + for (auto &v : points) ret.points.emplace_back(to_vec3d(v)); + for (auto &v : triangles) ret.faces3.emplace_back(to_vec3i(v)); + for (auto &v : quads) ret.faces4.emplace_back(to_vec4i(v)); + + return ret; +} + +} // namespace Slic3r diff --git a/tests/hollowing/openvdb_utils.hpp b/tests/hollowing/openvdb_utils.hpp new file mode 100644 index 000000000..a2c02c078 --- /dev/null +++ b/tests/hollowing/openvdb_utils.hpp @@ -0,0 +1,25 @@ +#ifndef OPENVDB_UTILS_HPP +#define OPENVDB_UTILS_HPP + +#include +#include +#include +#include +#include + +namespace Slic3r { + +openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, + const openvdb::math::Transform &tr); + +openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, + const openvdb::math::Transform &tr); + +sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue = 0.0, + double adaptivity = 0.0, + bool relaxDisorientedTriangles = true); + +} // namespace Slic3r + +#endif // OPENVDB_UTILS_HPP