Lagrange
compute_vertex_normal.h
1/*
2 * Copyright 2017 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13#include <cassert>
14#include <exception>
15#include <iostream>
16
17#include <lagrange/Mesh.h>
18#include <lagrange/MeshTrait.h>
19#include <lagrange/NormalWeightingType.h>
20#include <lagrange/common.h>
21#include <lagrange/compute_triangle_normal.h>
22#include <lagrange/internal/doublearea.h>
23#include <lagrange/internal/internal_angles.h>
24#include <lagrange/legacy/inline.h>
25
26namespace lagrange {
27
28LAGRANGE_LEGACY_INLINE
29namespace legacy {
30
34enum PerVertexNormalsWeightingType : char {
36 PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM = 0,
37
39 PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA = 1,
40
42 PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE = 2,
43
45 PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT = 3,
46
48 NUM_PER_VERTEX_NORMALS_WEIGHTING_TYPE = 4
49};
50
51
52template <typename MeshType>
54 MeshType& mesh,
55 const PerVertexNormalsWeightingType weighting = PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE,
56 bool recompute_facet_normals = false)
57{
58 static_assert(MeshTrait<MeshType>::is_mesh(), "Input type is not Mesh");
59 using Index = IndexOf<MeshType>;
60 using Scalar = ScalarOf<MeshType>;
61
62 if (mesh.get_vertex_per_facet() != 3) {
63 throw std::runtime_error("Input mesh is not triangle mesh.");
64 }
65
66 if (!mesh.has_facet_attribute("normal") || recompute_facet_normals) {
67 compute_triangle_normal(mesh);
68 la_runtime_assert(mesh.has_facet_attribute("normal"));
69 }
70
71 using AttributeArray = typename MeshType::AttributeArray;
72 const auto& vertices = mesh.get_vertices();
73 const auto& facets = mesh.get_facets();
74 const auto& facet_normals = mesh.get_facet_attribute("normal");
75 AttributeArray vertex_normals(vertices.rows(), 3);
76 vertex_normals.setZero();
77
78 {
79 // Reimplementation of igl::per_vertex_normals, except we use a parallel sum over the
80 // vertices if mesh edge data is available.
81 AttributeArray weights(facets.rows(), 3);
82
83 switch (weighting) {
84 case PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM: weights.setConstant(Scalar(1)); break;
85 case PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT:
86 case PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA: {
87 Eigen::Matrix<Scalar, Eigen::Dynamic, 1> areas;
88 lagrange::internal::doublearea(vertices, facets, areas);
89 weights = areas.replicate(1, 3);
90 break;
91 }
92 case PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE:
93 lagrange::internal::internal_angles(vertices, facets, weights);
94 break;
95 default: assert(false && "Unknown weighting type");
96 }
97
98 // Weight vector may contain NaNs, so we zero them out
99 weights = weights.array().isFinite().select(weights, Scalar(0));
100
101 if (mesh.is_edge_data_initialized()) {
102 // Parallel version, iterating over vertices
103 tbb::parallel_for(Index(0), mesh.get_num_vertices(), [&](Index v) {
104 mesh.foreach_corners_around_vertex(v, [&](const Index c) {
105 const Index f = c / 3;
106 const Index lv = c % 3;
107 vertex_normals.row(v) += weights(f, lv) * facet_normals.row(f);
108 });
109 });
110 } else {
111 // Loop over faces
112 for (Index f = 0; f < static_cast<Index>(facets.rows()); ++f) {
113 // Throw normal at each corner
114 for (Index lv = 0; lv < 3; ++lv) {
115 const Index v = facets(f, lv);
116 vertex_normals.row(v) += weights(f, lv) * facet_normals.row(f);
117 }
118 }
119 }
120 // Take average via normalization.
121 //
122 // Note: For some reason stableNormalize() is not yet available as a vectorwise operation,
123 // so we cannot simply call `vertex_normals.rowwise().stableNormalize()` and call it a day.
124 tbb::parallel_for(Index(0), mesh.get_num_vertices(), [&](Index v) {
125 vertex_normals.row(v).stableNormalize();
126 });
127 }
128
129 mesh.add_vertex_attribute("normal");
130 mesh.import_vertex_attribute("normal", vertex_normals);
131}
132
133} // namespace legacy
134} // namespace lagrange
Definition: Mesh.h:48
AttributeId compute_vertex_normal(SurfaceMesh< Scalar, Index > &mesh, VertexNormalOptions options={})
Compute per-vertex normals based on specified weighting type.
Definition: compute_vertex_normal.cpp:34
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
void internal_angles(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets, Eigen::PlainObjectBase< DerivedK > &angles)
Compute internal angles for a triangle mesh.
Definition: internal_angles.h:39
Main namespace for Lagrange.
Definition: AABBIGL.h:30