diff options
-rw-r--r-- | source/blender/blenloader/intern/versioning_300.c | 4 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc | 154 |
2 files changed, 127 insertions, 31 deletions
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 |