Blender V4.3
node_geo_mesh_face_group_boundaries.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 <atomic>
6
7#include "BKE_mesh.hh"
8
10
12
14{
15 b.add_input<decl::Int>("Face Group ID", "Face Set")
16 .default_value(0)
17 .hide_value()
18 .supports_field()
19 .description(
20 "An identifier for the group of each face. All contiguous faces with the "
21 "same value are in the same region");
22 b.add_output<decl::Bool>("Boundary Edges")
23 .field_source_reference_all()
24 .description("The edges that lie on the boundaries between the different face groups");
25}
26
28 private:
29 const Field<int> face_set_;
30
31 public:
33 : bke::MeshFieldInput(CPPType::get<bool>(), "Face Group Boundaries"), face_set_(face_set)
34 {
36 }
37
39 const AttrDomain domain,
40 const IndexMask & /*mask*/) const final
41 {
42 const bke::MeshFieldContext face_context{mesh, AttrDomain::Face};
43 FieldEvaluator face_evaluator{face_context, mesh.faces_num};
44 face_evaluator.add(face_set_);
45 face_evaluator.evaluate();
46 const VArray<int> faces_group_id = face_evaluator.get_evaluated<int>(0);
47 if (faces_group_id.is_single()) {
48 return {};
49 }
50
51 Array<bool> boundary(mesh.edges_num, false);
52
53 Array<std::atomic<int>> edge_states(mesh.edges_num);
54 /* State is index of face or one of invalid values: */
55 static constexpr int no_face_yet = -1;
56 static constexpr int is_boundary = -2;
57
58 threading::parallel_for(edge_states.index_range(), 4096, [&](const IndexRange range) {
59 for (std::atomic<int> &v : edge_states.as_mutable_span().slice(range)) {
60 v.store(no_face_yet, std::memory_order_relaxed);
61 }
62 });
63
64 const GroupedSpan<int> face_edges(mesh.face_offsets(), mesh.corner_edges());
65 threading::parallel_for(face_edges.index_range(), 2048, [&](const IndexRange range) {
66 for (const int face_i : range) {
67 const int group_id = faces_group_id[face_i];
68 for (const int edge_i : face_edges[face_i]) {
69 std::atomic<int> &edge_state = edge_states[edge_i];
70 while (true) {
71 int edge_state_value = edge_state.load(std::memory_order_relaxed);
72 switch (edge_state_value) {
73 case is_boundary:
74 break;
75 case no_face_yet: {
76 if (edge_state.compare_exchange_weak(edge_state_value,
77 face_i,
78 std::memory_order_relaxed,
79 std::memory_order_relaxed))
80 {
81 break;
82 }
83 continue;
84 }
85 default: {
86 if (faces_group_id[edge_state_value] == group_id) {
87 break;
88 }
89 if (edge_state.compare_exchange_weak(
90 edge_state_value, is_boundary, std::memory_order_release))
91 {
92 boundary[edge_i] = true;
93 break;
94 }
95 continue;
96 }
97 }
98 break;
99 }
100 }
101 }
102 });
103 return mesh.attributes().adapt_domain<bool>(
104 VArray<bool>::ForContainer(std::move(boundary)), AttrDomain::Edge, domain);
105 }
106
107 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
108 {
109 face_set_.node().for_each_field_input_recursive(fn);
110 }
111
112 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const override
113 {
114 return AttrDomain::Edge;
115 }
116};
117
119{
120 const Field<int> face_set_field = params.extract_input<Field<int>>("Face Set");
121 Field<bool> face_set_boundaries{std::make_shared<BoundaryFieldInput>(face_set_field)};
122 params.set_output("Boundary Edges", std::move(face_set_boundaries));
123}
124
125static void node_register()
126{
127 static blender::bke::bNodeType ntype;
129 &ntype, GEO_NODE_MESH_FACE_GROUP_BOUNDARIES, "Face Group Boundaries", NODE_CLASS_INPUT);
130 bke::node_type_size_preset(&ntype, bke::eNodeSizePreset::Middle);
131 ntype.declare = node_declare;
134}
135NOD_REGISTER_NODE(node_register)
136
137} // namespace blender::nodes::node_geo_mesh_face_group_boundaries_cc
#define NODE_CLASS_INPUT
Definition BKE_node.hh:404
#define NOD_REGISTER_NODE(REGISTER_FUNC)
IndexRange index_range() const
Definition BLI_array.hh:349
static VArray ForContainer(ContainerT container)
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
virtual void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
Definition field.cc:587
const FieldNode & node() const
Definition FN_field.hh:137
GVArray get_varray_for_context(const Mesh &mesh, const AttrDomain domain, const IndexMask &) const final
void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const override
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
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