|
1 | 1 | #include <noether_tpp/mesh_modifiers/normals_from_mesh_faces_modifier.h> |
2 | 2 | #include <noether_tpp/utils.h> |
3 | 3 |
|
4 | | -#include <pcl/point_types.h> |
| 4 | +#include <Eigen/Dense> |
5 | 5 | #include <pcl/common/io.h> |
6 | 6 | #include <pcl/conversions.h> |
| 7 | +#include <pcl/point_cloud.h> |
| 8 | +#include <pcl/point_types.h> |
| 9 | +#include <pcl/PolygonMesh.h> |
| 10 | +#include <vector> |
7 | 11 | #include <yaml-cpp/yaml.h> |
8 | 12 |
|
9 | 13 | namespace noether |
10 | 14 | { |
11 | | -Eigen::Vector3f computeFaceNormal(const pcl::PolygonMesh& mesh, |
12 | | - const TriangleMesh& tri_mesh, |
13 | | - const TriangleMesh::FaceIndex& face_idx) |
14 | | -{ |
15 | | - using VAFC = TriangleMesh::VertexAroundFaceCirculator; |
16 | | - |
17 | | - // Get the vertices of this triangle |
18 | | - VAFC v_circ = tri_mesh.getVertexAroundFaceCirculator(face_idx); |
19 | | - |
20 | | - const TriangleMesh::VertexIndex v1_idx = v_circ++.getTargetIndex(); |
21 | | - const TriangleMesh::VertexIndex v2_idx = v_circ++.getTargetIndex(); |
22 | | - const TriangleMesh::VertexIndex v3_idx = v_circ++.getTargetIndex(); |
23 | | - |
24 | | - // Check the validity of the vertices |
25 | | - if (!v1_idx.isValid() || !v2_idx.isValid() || !v3_idx.isValid()) |
26 | | - return Eigen::Vector3f::Constant(std::numeric_limits<float>::quiet_NaN()); |
27 | | - |
28 | | - const Eigen::Vector3f v1 = getPoint(mesh.cloud, v1_idx.get()); |
29 | | - const Eigen::Vector3f v2 = getPoint(mesh.cloud, v2_idx.get()); |
30 | | - const Eigen::Vector3f v3 = getPoint(mesh.cloud, v3_idx.get()); |
31 | | - |
32 | | - // Get the edges v1 -> v2 and v1 -> v3 |
33 | | - const Eigen::Vector3f edge_12 = v2 - v1; |
34 | | - const Eigen::Vector3f edge_13 = v3 - v1; |
35 | | - |
36 | | - // Compute the face normal as the cross product between edge v1 -> v2 and edge v1 -> v3 |
37 | | - return edge_12.cross(edge_13).normalized(); |
38 | | -} |
39 | | - |
40 | 15 | std::vector<pcl::PolygonMesh> NormalsFromMeshFacesMeshModifier::modify(const pcl::PolygonMesh& mesh) const |
41 | 16 | { |
42 | | - // Create a triangle mesh representation of the input mesh |
43 | | - TriangleMesh tri_mesh = createTriangleMesh(mesh); |
44 | | - |
45 | | - pcl::PointCloud<pcl::Normal> normals_cloud; |
46 | | - normals_cloud.reserve(mesh.cloud.height * mesh.cloud.width); |
| 17 | + // Create a point cloud for the normals and extract its Eigen matrix mapping |
| 18 | + pcl::PointCloud<pcl::Normal> normals_cloud(mesh.cloud.width, mesh.cloud.height); |
| 19 | + auto normals_map = normals_cloud.getMatrixXfMap(); |
47 | 20 |
|
48 | | - // For each vertex, circulate the faces around and average the face normals |
49 | | - for (std::size_t i = 0; i < tri_mesh.getVertexDataCloud().size(); ++i) |
| 21 | + // Iterate across the polygons, calculating their normals and accumulating the normals onto all adjacent vertices |
| 22 | + for (const auto& p : mesh.polygons) |
50 | 23 | { |
51 | | - Eigen::Vector3f avg_face_normal = Eigen::Vector3f::Zero(); |
52 | | - int n_faces = 0; |
| 24 | + // Compute the normal |
| 25 | + const Eigen::Vector3f normal = getFaceNormal(mesh, p); |
53 | 26 |
|
54 | | - using FAVC = TriangleMesh::FaceAroundVertexCirculator; |
55 | | - FAVC circ = tri_mesh.getFaceAroundVertexCirculator(TriangleMesh::VertexIndex(i)); |
56 | | - |
57 | | - FAVC circ_end = circ; |
58 | | - do |
59 | | - { |
60 | | - if (n_faces > mesh.polygons.size()) |
61 | | - throw std::runtime_error("Vertex " + std::to_string(i) + |
62 | | - " appears to participate in more faces than exist in the mesh; this mesh likely " |
63 | | - "contains degenerate half-edges."); |
| 27 | + // Add this normal to the accumulators for this face's valid vertices |
| 28 | + for (const std::size_t v_idx : p.vertices) |
| 29 | + normals_map.col(v_idx).head<3>() += normal; |
| 30 | + } |
64 | 31 |
|
65 | | - if (circ.isValid()) |
66 | | - { |
67 | | - TriangleMesh::FaceIndex face_idx = circ.getTargetIndex(); |
68 | | - if (face_idx.isValid()) |
69 | | - { |
70 | | - avg_face_normal += computeFaceNormal(mesh, tri_mesh, face_idx); |
71 | | - ++n_faces; |
72 | | - } |
73 | | - } |
74 | | - } while (++circ != circ_end); |
| 32 | + // Normalize the normals |
| 33 | + normals_map.colwise().normalize(); |
75 | 34 |
|
76 | | - pcl::Normal normal; |
77 | | - normal.getNormalVector3fMap() = (avg_face_normal / n_faces).normalized(); |
78 | | - normals_cloud.push_back(normal); |
79 | | - } |
| 35 | + // Replace nan values with 0 |
| 36 | + normals_map.unaryExpr([](float v) { return std::isfinite(v) ? v : 0.0; }); |
80 | 37 |
|
81 | 38 | // Convert to message type |
82 | | - pcl::PCLPointCloud2 normals; |
83 | | - pcl::toPCLPointCloud2(normals_cloud, normals); |
| 39 | + pcl::PCLPointCloud2 normals_pc2; |
| 40 | + pcl::toPCLPointCloud2(normals_cloud, normals_pc2); |
84 | 41 |
|
85 | | - // Copy the mesh |
| 42 | + // Create output mesh |
86 | 43 | pcl::PolygonMesh output = mesh; |
87 | 44 |
|
88 | 45 | // Concatenate the normals with the nominal mesh information |
89 | | - if (!pcl::concatenateFields(mesh.cloud, normals, output.cloud)) |
| 46 | + if (!pcl::concatenateFields(mesh.cloud, normals_pc2, output.cloud)) |
90 | 47 | throw std::runtime_error("Failed to concatenate normals into mesh vertex cloud"); |
91 | 48 |
|
92 | 49 | return { output }; |
|
0 commit comments