Skip to content

Commit 90f08d9

Browse files
DavidMerzJrmarip8
andauthored
Refactor NormalsFromMeshFacesModifier (#334)
* Refactor NormalsFromMeshFacesModifier * Apply Clang Format * Simplified; used existing methods for extracting face normals --------- Co-authored-by: Michael Ripperger <[email protected]>
1 parent b641849 commit 90f08d9

File tree

2 files changed

+29
-68
lines changed

2 files changed

+29
-68
lines changed

noether_tpp/include/noether_tpp/mesh_modifiers/normals_from_mesh_faces_modifier.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ namespace noether
88
/**
99
* @ingroup mesh_modifiers
1010
* @brief MeshModifier that assigns vertex normals using mesh faces
11-
* @details
11+
* @details The normal of each vertex is calculated as the average of the normals of all faces
12+
* which include that vertex. This function handles supra-triangular polygonal faces, but ignores
13+
* 'faces' that include less than three vertices. Well-behaved on meshes with holes. Note that a
14+
* mesh with inconsistent face normals will generate inconsistent vertex normals. Note that a
15+
* non-manifold mesh may produce numerically unstable results.
1216
*/
1317
class NormalsFromMeshFacesMeshModifier : public MeshModifier
1418
{

noether_tpp/src/mesh_modifiers/normals_from_mesh_faces_modifier.cpp

Lines changed: 24 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,49 @@
11
#include <noether_tpp/mesh_modifiers/normals_from_mesh_faces_modifier.h>
22
#include <noether_tpp/utils.h>
33

4-
#include <pcl/point_types.h>
4+
#include <Eigen/Dense>
55
#include <pcl/common/io.h>
66
#include <pcl/conversions.h>
7+
#include <pcl/point_cloud.h>
8+
#include <pcl/point_types.h>
9+
#include <pcl/PolygonMesh.h>
10+
#include <vector>
711
#include <yaml-cpp/yaml.h>
812

913
namespace noether
1014
{
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-
4015
std::vector<pcl::PolygonMesh> NormalsFromMeshFacesMeshModifier::modify(const pcl::PolygonMesh& mesh) const
4116
{
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();
4720

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)
5023
{
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);
5326

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+
}
6431

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();
7534

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; });
8037

8138
// 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);
8441

85-
// Copy the mesh
42+
// Create output mesh
8643
pcl::PolygonMesh output = mesh;
8744

8845
// 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))
9047
throw std::runtime_error("Failed to concatenate normals into mesh vertex cloud");
9148

9249
return { output };

0 commit comments

Comments
 (0)