Blender  V3.3
node_geo_input_mesh_edge_angle.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "DNA_mesh_types.h"
4 #include "DNA_meshdata_types.h"
5 
6 #include "BKE_mesh.h"
7 
8 #include "node_geometry_util.hh"
9 
11 
13 {
14  b.add_output<decl::Float>(N_("Unsigned Angle"))
15  .field_source()
16  .description(
17  "The shortest angle in radians between two faces where they meet at an edge. Flat edges "
18  "and Non-manifold edges have an angle of zero. Computing this value is faster than the "
19  "signed angle");
20  b.add_output<decl::Float>(N_("Signed Angle"))
21  .field_source()
22  .description(
23  "The signed angle in radians between two faces where they meet at an edge. Flat edges "
24  "and Non-manifold edges have an angle of zero. Concave angles are positive and convex "
25  "angles are negative. Computing this value is slower than the unsigned angle");
26 }
27 
28 struct EdgeMapEntry {
32 };
33 
35  const Span<MLoop> loops,
36  const int total_edges)
37 {
38  Array<EdgeMapEntry> edge_map(total_edges, {0, 0, 0});
39 
40  for (const int i_poly : polys.index_range()) {
41  const MPoly &mpoly = polys[i_poly];
42  for (const MLoop &loop : loops.slice(mpoly.loopstart, mpoly.totloop)) {
43  EdgeMapEntry &entry = edge_map[loop.e];
44  if (entry.face_count == 0) {
45  entry.face_index_1 = i_poly;
46  }
47  else if (entry.face_count == 1) {
48  entry.face_index_2 = i_poly;
49  }
50  entry.face_count++;
51  }
52  }
53  return edge_map;
54 }
55 
57  public:
58  AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
59  {
61  }
62 
64  const eAttrDomain domain,
65  IndexMask UNUSED(mask)) const final
66  {
67  if (component.type() != GEO_COMPONENT_TYPE_MESH) {
68  return {};
69  }
70 
71  const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
72  const Mesh *mesh = mesh_component.get_for_read();
73  if (mesh == nullptr) {
74  return {};
75  }
76 
77  Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
78  Span<MLoop> loops{mesh->mloop, mesh->totloop};
79  Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
80 
81  auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
82  if (edge_map[i].face_count != 2) {
83  return 0.0f;
84  }
85  const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
86  const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
87  float3 normal_1, normal_2;
88  BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1);
89  BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2);
90  return angle_normalized_v3v3(normal_1, normal_2);
91  };
92 
93  VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
94  return component.attributes()->adapt_domain<float>(
95  std::move(angles), ATTR_DOMAIN_EDGE, domain);
96  }
97 
98  uint64_t hash() const override
99  {
100  /* Some random constant hash. */
101  return 32426725235;
102  }
103 
104  bool is_equal_to(const fn::FieldNode &other) const override
105  {
106  return dynamic_cast<const AngleFieldInput *>(&other) != nullptr;
107  }
108 };
109 
111  public:
112  SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field")
113  {
115  }
116 
118  const eAttrDomain domain,
119  IndexMask UNUSED(mask)) const final
120  {
121  if (component.type() != GEO_COMPONENT_TYPE_MESH) {
122  return {};
123  }
124 
125  const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
126  const Mesh *mesh = mesh_component.get_for_read();
127  if (mesh == nullptr) {
128  return {};
129  }
130 
131  Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
132  Span<MLoop> loops{mesh->mloop, mesh->totloop};
133  Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
134 
135  auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
136  if (edge_map[i].face_count != 2) {
137  return 0.0f;
138  }
139  const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
140  const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
141 
142  /* Find the normals of the 2 polys. */
143  float3 poly_1_normal, poly_2_normal;
144  BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, poly_1_normal);
145  BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_2_normal);
146 
147  /* Find the centerpoint of the axis edge */
148  const float3 edge_centerpoint = (float3(mesh->mvert[mesh->medge[i].v1].co) +
149  float3(mesh->mvert[mesh->medge[i].v2].co)) *
150  0.5f;
151 
152  /* Get the centerpoint of poly 2 and subtract the edge centerpoint to get a tangent
153  * normal for poly 2. */
154  float3 poly_center_2;
155  BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_center_2);
156  const float3 poly_2_tangent = math::normalize(poly_center_2 - edge_centerpoint);
157  const float concavity = math::dot(poly_1_normal, poly_2_tangent);
158 
159  /* Get the unsigned angle between the two polys */
160  const float angle = angle_normalized_v3v3(poly_1_normal, poly_2_normal);
161 
162  if (angle == 0.0f || angle == 2.0f * M_PI || concavity < 0) {
163  return angle;
164  }
165  return -angle;
166  };
167 
168  VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
169  return component.attributes()->adapt_domain<float>(
170  std::move(angles), ATTR_DOMAIN_EDGE, domain);
171  }
172 
173  uint64_t hash() const override
174  {
175  /* Some random constant hash. */
176  return 68465416863;
177  }
178 
179  bool is_equal_to(const fn::FieldNode &other) const override
180  {
181  return dynamic_cast<const SignedAngleFieldInput *>(&other) != nullptr;
182  }
183 };
184 
186 {
187  if (params.output_is_required("Unsigned Angle")) {
188  Field<float> angle_field{std::make_shared<AngleFieldInput>()};
189  params.set_output("Unsigned Angle", std::move(angle_field));
190  }
191  if (params.output_is_required("Signed Angle")) {
192  Field<float> angle_field{std::make_shared<SignedAngleFieldInput>()};
193  params.set_output("Signed Angle", std::move(angle_field));
194  }
195 }
196 
197 } // namespace blender::nodes::node_geo_input_mesh_edge_angle_cc
198 
200 {
202 
203  static bNodeType ntype;
207  nodeRegisterType(&ntype);
208 }
typedef float(TangentPoint)[2]
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_EDGE
Definition: BKE_attribute.h:28
@ GEO_COMPONENT_TYPE_MESH
void BKE_mesh_calc_poly_center(const struct MPoly *mpoly, const struct MLoop *loopstart, const struct MVert *mvarray, float r_cent[3])
void BKE_mesh_calc_poly_normal(const struct MPoly *mpoly, const struct MLoop *loopstart, const struct MVert *mvarray, float r_no[3])
#define GEO_NODE_INPUT_MESH_EDGE_ANGLE
Definition: BKE_node.h:1488
#define NODE_CLASS_INPUT
Definition: BKE_node.h:345
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define final(a, b, c)
Definition: BLI_hash.h:21
#define M_PI
Definition: BLI_math_base.h:20
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:445
#define UNUSED(x)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:108
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
const Mesh * get_for_read() const
constexpr Span slice(int64_t start, int64_t size) const
Definition: BLI_span.hh:142
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
static VArray ForFunc(const int64_t size, GetFunc get_func)
GVArray get_varray_for_context(const GeometryComponent &component, const eAttrDomain domain, IndexMask UNUSED(mask)) const final
GVArray get_varray_for_context(const GeometryComponent &component, const eAttrDomain domain, IndexMask UNUSED(mask)) const final
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
T dot(const vec_base< T, Size > &a, const vec_base< T, Size > &b)
vec_base< T, Size > normalize(const vec_base< T, Size > &v)
static Array< EdgeMapEntry > create_edge_map(const Span< MPoly > polys, const Span< MLoop > loops, const int total_edges)
vec_base< float, 3 > float3
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void register_node_type_geo_input_mesh_edge_angle()
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
unsigned __int64 uint64_t
Definition: stdint.h:90
unsigned int v1
unsigned int v2
float co[3]
struct MEdge * medge
struct MVert * mvert
int totedge
struct MLoop * mloop
int totpoly
int totloop
struct MPoly * mpoly
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
NodeDeclareFunction declare
Definition: BKE_node.h:324
#define N_(msgid)