diff --git a/CHANGELOG.md b/CHANGELOG.md index ba4497f10bc..fc2ea4804a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ - Fix alpha shape reconstruction if alpha too small for point scale (PR #6998) - Fix render to depth image on Apple Retina displays (PR #7001) - Fix infinite loop in segment_plane if num_points < ransac_n (PR #7032) +- Copy UV coordinates from a reference mesh #6740 ## 0.13 diff --git a/cpp/open3d/geometry/TriangleMesh.cpp b/cpp/open3d/geometry/TriangleMesh.cpp index 390557f762b..99515ef1724 100644 --- a/cpp/open3d/geometry/TriangleMesh.cpp +++ b/cpp/open3d/geometry/TriangleMesh.cpp @@ -159,6 +159,82 @@ TriangleMesh &TriangleMesh::ComputeAdjacencyList() { return *this; } +TriangleMesh TriangleMesh::InterpolateTextureCoordinatesFrom( + const TriangleMesh &input_mesh) { + KDTreeFlann kdtree(input_mesh); + + // Custom equality function for Eigen::Vector2d + struct Vector2dEqual { + bool operator()(const Eigen::Vector2d &v1, + const Eigen::Vector2d &v2) const { + return v1.isApprox(v2); + } + }; + using vector2d_set = + std::unordered_set, + Vector2dEqual>; + + // Maps vertex index to set of uvs in input mesh + std::unordered_map vertex_to_uvs; + for (size_t i = 0; i < input_mesh.triangles_.size(); i++) { + const Eigen::Vector3i &triangle = input_mesh.triangles_[i]; + for (int j = 0; j < 3; j++) { + int vertex_idx = triangle[j]; + auto uv = input_mesh.triangle_uvs_[i * 3 + j]; + if (vertex_to_uvs[vertex_idx].find(uv) == + vertex_to_uvs[vertex_idx].end()) { + vertex_to_uvs[vertex_idx].insert(uv); + } + } + } + + auto distance = [](const Eigen::Vector2d &v1, const Eigen::Vector2d &v2) { + return (v1 - v2).norm(); + }; + + // Gets the uv coordinates combination that are closest to each other (one + // uv coordinate from each set), given 3 uv sets corresponding to each + // vertex of a triangle + auto get_best_uvs = [&distance](const vector2d_set &uvs1, + const vector2d_set &uvs2, + const vector2d_set &uvs3) { + std::vector best_uvs{Eigen::Vector2d(0, 0), + Eigen::Vector2d(0, 0), + Eigen::Vector2d(0, 0)}; + if (uvs1.size() == 0 || uvs2.size() == 0 || uvs3.size() == 0) + return best_uvs; + + double minimum_distance = std::numeric_limits::max(); + for (auto &uv1 : uvs1) { + for (auto &uv2 : uvs2) { + for (auto &uv3 : uvs3) { + auto aggregate_distance = distance(uv1, uv2) + + distance(uv2, uv3) + + distance(uv3, uv1); + if (aggregate_distance < minimum_distance) { + minimum_distance = aggregate_distance; + best_uvs = {uv1, uv2, uv3}; + } + } + } + } + return best_uvs; + }; + + // Writes the uvs to the triangle mesh + for (const auto &triangle : triangles_) { + auto best_uvs = get_best_uvs(vertex_to_uvs[triangle(0)], + vertex_to_uvs[triangle(1)], + vertex_to_uvs[triangle(2)]); + for (auto &uv : best_uvs) { + triangle_uvs_.push_back(uv); + } + } + + return *this; +} + std::shared_ptr TriangleMesh::FilterSharpen( int number_of_iterations, double strength, FilterScope scope) const { bool filter_vertex = diff --git a/cpp/open3d/geometry/TriangleMesh.h b/cpp/open3d/geometry/TriangleMesh.h index 1da782fd9b6..79942dc06d4 100644 --- a/cpp/open3d/geometry/TriangleMesh.h +++ b/cpp/open3d/geometry/TriangleMesh.h @@ -149,6 +149,14 @@ class TriangleMesh : public MeshBase { /// This function might help to close triangle soups. TriangleMesh &MergeCloseVertices(double eps); + /// \brief Function to interpolate UVs from input mesh to this mesh. + /// UVs are interpolated from the UVs of the closest vertices + /// in the input mesh. + /// + /// \param input_mesh The input mesh from which UVs are interpolated. + TriangleMesh InterpolateTextureCoordinatesFrom( + const TriangleMesh &input_mesh); + /// \brief Function to sharpen triangle mesh. /// /// The output value (\f$v_o\f$) is the input value (\f$v_i\f$) plus diff --git a/cpp/pybind/geometry/trianglemesh.cpp b/cpp/pybind/geometry/trianglemesh.cpp index 4c046d5871b..3586ba658b1 100644 --- a/cpp/pybind/geometry/trianglemesh.cpp +++ b/cpp/pybind/geometry/trianglemesh.cpp @@ -103,6 +103,12 @@ void pybind_trianglemesh_definitions(py::module &m) { "function might help to " "close triangle soups.", "eps"_a) + .def("interpolate_texture_coordinates_from", + &TriangleMesh::InterpolateTextureCoordinatesFrom, + "Function to interpolate texture coordinates from another " + "triangle mesh. Texture is interpolated from the UVs of the " + "closest vertices in the input mesh", + "input_mesh"_a) .def("filter_sharpen", &TriangleMesh::FilterSharpen, "Function to sharpen triangle mesh. The output value " "(:math:`v_o`) is the input value (:math:`v_i`) plus strength "