Blender V4.3
node_geo_input_mesh_edge_angle.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_math_vector.h"
6
7#include "BKE_mesh.hh"
8
10
12
14{
15 b.add_output<decl::Float>("Unsigned Angle")
16 .field_source()
18 "The shortest angle in radians between two faces where they meet at an edge. Flat edges "
19 "and Non-manifold edges have an angle of zero. Computing this value is faster than the "
20 "signed angle");
21 b.add_output<decl::Float>("Signed Angle")
22 .field_source()
24 "The signed angle in radians between two faces where they meet at an edge. Flat edges "
25 "and Non-manifold edges have an angle of zero. Concave angles are positive and convex "
26 "angles are negative. Computing this value is slower than the unsigned angle");
27}
28
34
36 const Span<int> corner_edges,
37 const int total_edges)
38{
39 Array<EdgeMapEntry> edge_map(total_edges, {0, 0, 0});
40
41 for (const int i_face : faces.index_range()) {
42 for (const int edge : corner_edges.slice(faces[i_face])) {
43 EdgeMapEntry &entry = edge_map[edge];
44 if (entry.face_count == 0) {
45 entry.face_index_1 = i_face;
46 }
47 else if (entry.face_count == 1) {
48 entry.face_index_2 = i_face;
49 }
50 entry.face_count++;
51 }
52 }
53 return edge_map;
54}
55
57 public:
58 AngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
59 {
61 }
62
64 const AttrDomain domain,
65 const IndexMask & /*mask*/) const final
66 {
67 const Span<float3> positions = mesh.vert_positions();
68 const OffsetIndices faces = mesh.faces();
69 const Span<int> corner_verts = mesh.corner_verts();
70 const Span<int> corner_edges = mesh.corner_edges();
71 Array<EdgeMapEntry> edge_map = create_edge_map(faces, corner_edges, mesh.edges_num);
72
73 auto angle_fn =
74 [edge_map = std::move(edge_map), positions, faces, corner_verts](const int i) -> float {
75 if (edge_map[i].face_count != 2) {
76 return 0.0f;
77 }
78 const IndexRange face_1 = faces[edge_map[i].face_index_1];
79 const IndexRange face_2 = faces[edge_map[i].face_index_2];
80 const float3 normal_1 = bke::mesh::face_normal_calc(positions, corner_verts.slice(face_1));
81 const float3 normal_2 = bke::mesh::face_normal_calc(positions, corner_verts.slice(face_2));
82 return angle_normalized_v3v3(normal_1, normal_2);
83 };
84
85 VArray<float> angles = VArray<float>::ForFunc(mesh.edges_num, angle_fn);
86 return mesh.attributes().adapt_domain<float>(std::move(angles), AttrDomain::Edge, domain);
87 }
88
89 uint64_t hash() const override
90 {
91 /* Some random constant hash. */
92 return 32426725235;
93 }
94
95 bool is_equal_to(const fn::FieldNode &other) const override
96 {
97 return dynamic_cast<const AngleFieldInput *>(&other) != nullptr;
98 }
99
100 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const override
101 {
102 return AttrDomain::Edge;
103 }
104};
105
107 public:
108 SignedAngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Signed Angle Field")
109 {
111 }
112
114 const AttrDomain domain,
115 const IndexMask & /*mask*/) const final
116 {
117 const Span<float3> positions = mesh.vert_positions();
118 const Span<int2> edges = mesh.edges();
119 const OffsetIndices faces = mesh.faces();
120 const Span<int> corner_verts = mesh.corner_verts();
121 const Span<int> corner_edges = mesh.corner_edges();
122 Array<EdgeMapEntry> edge_map = create_edge_map(faces, corner_edges, mesh.edges_num);
123
124 auto angle_fn = [edge_map = std::move(edge_map), positions, edges, faces, corner_verts](
125 const int i) -> float {
126 if (edge_map[i].face_count != 2) {
127 return 0.0f;
128 }
129 const IndexRange face_1 = faces[edge_map[i].face_index_1];
130 const IndexRange face_2 = faces[edge_map[i].face_index_2];
131
132 /* Find the normals of the 2 faces. */
133 const float3 face_1_normal = bke::mesh::face_normal_calc(positions,
134 corner_verts.slice(face_1));
135 const float3 face_2_normal = bke::mesh::face_normal_calc(positions,
136 corner_verts.slice(face_2));
137
138 /* Find the centerpoint of the axis edge */
139 const float3 edge_centerpoint = (positions[edges[i][0]] + positions[edges[i][1]]) * 0.5f;
140
141 /* Get the centerpoint of face 2 and subtract the edge centerpoint to get a tangent
142 * normal for face 2. */
143 const float3 face_center_2 = bke::mesh::face_center_calc(positions,
144 corner_verts.slice(face_2));
145 const float3 face_2_tangent = math::normalize(face_center_2 - edge_centerpoint);
146 const float concavity = math::dot(face_1_normal, face_2_tangent);
147
148 /* Get the unsigned angle between the two faces */
149 const float angle = angle_normalized_v3v3(face_1_normal, face_2_normal);
150
151 if (angle == 0.0f || angle == 2.0f * M_PI || concavity < 0) {
152 return angle;
153 }
154 return -angle;
155 };
156
157 VArray<float> angles = VArray<float>::ForFunc(mesh.edges_num, angle_fn);
158 return mesh.attributes().adapt_domain<float>(std::move(angles), AttrDomain::Edge, domain);
159 }
160
161 uint64_t hash() const override
162 {
163 /* Some random constant hash. */
164 return 68465416863;
165 }
166
167 bool is_equal_to(const fn::FieldNode &other) const override
168 {
169 return dynamic_cast<const SignedAngleFieldInput *>(&other) != nullptr;
170 }
171
172 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const override
173 {
174 return AttrDomain::Edge;
175 }
176};
177
179{
180 if (params.output_is_required("Unsigned Angle")) {
181 Field<float> angle_field{std::make_shared<AngleFieldInput>()};
182 params.set_output("Unsigned Angle", std::move(angle_field));
183 }
184 if (params.output_is_required("Signed Angle")) {
185 Field<float> angle_field{std::make_shared<SignedAngleFieldInput>()};
186 params.set_output("Signed Angle", std::move(angle_field));
187 }
188}
189
190static void node_register()
191{
192 static blender::bke::bNodeType ntype;
193 geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Edge Angle", NODE_CLASS_INPUT);
194 ntype.declare = node_declare;
197}
199
200} // namespace blender::nodes::node_geo_input_mesh_edge_angle_cc
#define NODE_CLASS_INPUT
Definition BKE_node.hh:404
#define M_PI
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
#define NOD_REGISTER_NODE(REGISTER_FUNC)
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
static VArray ForFunc(const int64_t size, GetFunc get_func)
std::optional< AttrDomain > preferred_domain(const Mesh &) const override
GVArray get_varray_for_context(const Mesh &mesh, const AttrDomain domain, const IndexMask &) const final
GVArray get_varray_for_context(const Mesh &mesh, const AttrDomain domain, const IndexMask &) const final
local_group_size(16, 16) .push_constant(Type b
draw_view in_light_buf[] float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static char faces[256]
float3 face_normal_calc(Span< float3 > vert_positions, Span< int > face_verts)
float3 face_center_calc(Span< float3 > vert_positions, Span< int > face_verts)
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
static Array< EdgeMapEntry > create_edge_map(const OffsetIndices< int > faces, const Span< int > corner_edges, const int total_edges)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
unsigned __int64 uint64_t
Definition stdint.h:90
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
NodeDeclareFunction declare
Definition BKE_node.hh:347