[Bf-blender-cvs] [4d5c08b9387] master: Geometry Nodes: Add Signed Output to Edge Angle Node

Johnny Matthews noreply at git.blender.org
Tue Jan 18 16:21:21 CET 2022


Commit: 4d5c08b9387c5dcb60b7ea84b0d7be57b829649a
Author: Johnny Matthews
Date:   Tue Jan 18 09:20:31 2022 -0600
Branches: master
https://developer.blender.org/rB4d5c08b9387c5dcb60b7ea84b0d7be57b829649a

Geometry Nodes: Add Signed Output to Edge Angle Node

Adds a second output to the edge angle node that shows the signed angle
between the two faces, where Convex angles are positive and Concave angles
are negative. This calculation is slower than the unsigned angle, so it
was best to leave both for times where the unsigned angle will suffice.

Differential Revision: https://developer.blender.org/D13796

===================================================================

M	source/blender/blenloader/intern/versioning_300.c
M	source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc

===================================================================

diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index f36285c9ecf..78ec2276af7 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -2544,11 +2544,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
       }
     }
 
-    /* Rename geometry socket on "String to Curves" node and "Transfer Attribute" node. */
+    /* Rename sockets on multiple nodes */
     LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
       if (ntree->type == NTREE_GEOMETRY) {
         version_node_output_socket_name(
             ntree, GEO_NODE_STRING_TO_CURVES, "Curves", "Curve Instances");
+        version_node_output_socket_name(
+            ntree, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Angle", "Unsigned Angle");
         version_node_input_socket_name(ntree, GEO_NODE_TRANSFER_ATTRIBUTE, "Target", "Source");
       }
     }
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
index 2d16c60ba86..4b6ed7b77b7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
@@ -25,11 +25,18 @@ namespace blender::nodes::node_geo_input_mesh_edge_angle_cc {
 
 static void node_declare(NodeDeclarationBuilder &b)
 {
-  b.add_output<decl::Float>(N_("Angle"))
+  b.add_output<decl::Float>(N_("Unsigned Angle"))
       .field_source()
       .description(
-          "The angle in radians between two faces where they meet at an edge. Flat edges and "
-          "Non-manifold edges have an angle of zero");
+          "The shortest angle in radians between two faces where they meet at an edge. Flat edges "
+          "and Non-manifold edges have an angle of zero. Computing this value is faster than the "
+          "signed angle");
+  b.add_output<decl::Float>(N_("Signed Angle"))
+      .field_source()
+      .description(
+          "The signed angle in radians between two faces where they meet at an edge. Flat edges "
+          "and Non-manifold edges have an angle of zero. Concave angles are positive and convex "
+          "angles are negative. Computing this value is slower than the unsigned angle");
 }
 
 struct EdgeMapEntry {
@@ -38,9 +45,31 @@ struct EdgeMapEntry {
   int face_index_2;
 };
 
+static Array<EdgeMapEntry> create_edge_map(const Span<MPoly> polys,
+                                           const Span<MLoop> loops,
+                                           const int total_edges)
+{
+  Array<EdgeMapEntry> edge_map(total_edges, {0, 0, 0});
+
+  for (const int i_poly : polys.index_range()) {
+    const MPoly &mpoly = polys[i_poly];
+    for (const MLoop &loop : loops.slice(mpoly.loopstart, mpoly.totloop)) {
+      EdgeMapEntry &entry = edge_map[loop.e];
+      if (entry.face_count == 0) {
+        entry.face_index_1 = i_poly;
+      }
+      else if (entry.face_count == 1) {
+        entry.face_index_2 = i_poly;
+      }
+      entry.face_count++;
+    }
+  }
+  return edge_map;
+}
+
 class AngleFieldInput final : public GeometryFieldInput {
  public:
-  AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Angle Field")
+  AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
   {
     category_ = Category::Generated;
   }
@@ -61,34 +90,18 @@ class AngleFieldInput final : public GeometryFieldInput {
 
     Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
     Span<MLoop> loops{mesh->mloop, mesh->totloop};
-    Array<EdgeMapEntry> edge_map(mesh->totedge, {0, 0, 0});
-
-    for (const int i_poly : polys.index_range()) {
-      const MPoly &mpoly = polys[i_poly];
-      for (const MLoop &loop : loops.slice(mpoly.loopstart, mpoly.totloop)) {
-        EdgeMapEntry &entry = edge_map[loop.e];
-        if (entry.face_count == 0) {
-          entry.face_index_1 = i_poly;
-        }
-        else if (entry.face_count == 1) {
-          entry.face_index_2 = i_poly;
-        }
-        entry.face_count++;
-      }
-    }
+    Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
 
     auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
-      if (edge_map[i].face_count == 2) {
-        const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
-        const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
-        float3 normal_1, normal_2;
-        BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1);
-        BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2);
-        return angle_normalized_v3v3(normal_1, normal_2);
-      }
-      else {
+      if (edge_map[i].face_count != 2) {
         return 0.0f;
       }
+      const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
+      const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
+      float3 normal_1, normal_2;
+      BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1);
+      BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2);
+      return angle_normalized_v3v3(normal_1, normal_2);
     };
 
     VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
@@ -108,10 +121,91 @@ class AngleFieldInput final : public GeometryFieldInput {
   }
 };
 
+class SignedAngleFieldInput final : public GeometryFieldInput {
+ public:
+  SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field")
+  {
+    category_ = Category::Generated;
+  }
+
+  GVArray get_varray_for_context(const GeometryComponent &component,
+                                 const AttributeDomain domain,
+                                 IndexMask UNUSED(mask)) const final
+  {
+    if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+      return {};
+    }
+
+    const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+    const Mesh *mesh = mesh_component.get_for_read();
+    if (mesh == nullptr) {
+      return {};
+    }
+
+    Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
+    Span<MLoop> loops{mesh->mloop, mesh->totloop};
+    Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
+
+    auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
+      if (edge_map[i].face_count != 2) {
+        return 0.0f;
+      }
+      const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
+      const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
+
+      /* Find the normals of the 2 polys. */
+      float3 poly_1_normal, poly_2_normal;
+      BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, poly_1_normal);
+      BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_2_normal);
+
+      /* Find the centerpoint of the axis edge */
+      const float3 edge_centerpoint = (float3(mesh->mvert[mesh->medge[i].v1].co) +
+                                       float3(mesh->mvert[mesh->medge[i].v2].co)) *
+                                      0.5f;
+
+      /* Get the centerpoint of poly 2 and subtract the edge centerpoint to get a tangent
+       * normal for poly 2. */
+      float3 poly_center_2;
+      BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_center_2);
+      const float3 poly_2_tangent = math::normalize(poly_center_2 - edge_centerpoint);
+      const float concavity = math::dot(poly_1_normal, poly_2_tangent);
+
+      /* Get the unsigned angle between the two polys */
+      const float angle = angle_normalized_v3v3(poly_1_normal, poly_2_normal);
+
+      if (angle == 0.0f || angle == 2.0f * M_PI || concavity < 0) {
+        return angle;
+      }
+      return -angle;
+    };
+
+    VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
+    return component.attribute_try_adapt_domain<float>(
+        std::move(angles), ATTR_DOMAIN_EDGE, domain);
+  }
+
+  uint64_t hash() const override
+  {
+    /* Some random constant hash. */
+    return 68465416863;
+  }
+
+  bool is_equal_to(const fn::FieldNode &other) const override
+  {
+    return dynamic_cast<const SignedAngleFieldInput *>(&other) != nullptr;
+  }
+};
+
 static void node_geo_exec(GeoNodeExecParams params)
 {
-  Field<float> angle_field{std::make_shared<AngleFieldInput>()};
-  params.set_output("Angle", std::move(angle_field));
+  if (params.output_is_required("Unsigned Angle")) {
+    Field<float> angle_field{std::make_shared<AngleFieldInput>()};
+    params.set_output("Unsigned Angle", std::move(angle_field));
+  }
+  if (params.output_is_required("Signed Angle")) {
+    Field<float> angle_field{std::make_shared<SignedAngleFieldInput>()};
+    params.set_output("Signed Angle", std::move(angle_field));
+  }
 }
 
 }  // namespace blender::nodes::node_geo_input_mesh_edge_angle_cc



More information about the Bf-blender-cvs mailing list