Blender V5.0
node_geo_mesh_topology_corners_of_edge.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 "DNA_mesh_types.h"
6
7#include "BKE_mesh_mapping.hh"
8
9#include "BLI_array_utils.hh"
10
11#include "node_geometry_util.hh"
12
14
16{
17 b.add_input<decl::Int>("Edge Index")
18 .implicit_field(NODE_DEFAULT_INPUT_INDEX_FIELD)
19 .description("The edge to retrieve data from. Defaults to the edge from the context")
20 .structure_type(StructureType::Field);
21 b.add_input<decl::Float>("Weights").supports_field().hide_value().description(
22 "Values that sort the corners attached to the edge");
23 b.add_input<decl::Int>("Sort Index")
24 .min(0)
25 .supports_field()
26 .description("Which of the sorted corners to output");
27 b.add_output<decl::Int>("Corner Index")
28 .field_source_reference_all()
30 "A corner of the input edge in its face's winding order, chosen by the sort index");
31 b.add_output<decl::Int>("Total").field_source().reference_pass({0}).description(
32 "The number of faces or corners connected to each edge");
33}
34
36 const Field<int> edge_index_;
37 const Field<int> sort_index_;
38 const Field<float> sort_weight_;
39
40 public:
41 CornersOfEdgeInput(Field<int> edge_index, Field<int> sort_index, Field<float> sort_weight)
42 : bke::MeshFieldInput(CPPType::get<int>(), "Corner of Edge"),
43 edge_index_(std::move(edge_index)),
44 sort_index_(std::move(sort_index)),
45 sort_weight_(std::move(sort_weight))
46 {
48 }
49
51 const AttrDomain domain,
52 const IndexMask &mask) const final
53 {
54 const IndexRange edge_range(mesh.edges_num);
55 Array<int> map_offsets;
56 Array<int> map_indices;
57 const Span<int> corner_edges = mesh.corner_edges();
59 mesh.corner_edges(), mesh.edges_num, map_offsets, map_indices);
60
61 const bke::MeshFieldContext context{mesh, domain};
62 fn::FieldEvaluator evaluator{context, &mask};
63 evaluator.add(edge_index_);
64 evaluator.add(sort_index_);
65 evaluator.evaluate();
66 const VArray<int> edge_indices = evaluator.get_evaluated<int>(0);
67 const VArray<int> indices_in_sort = evaluator.get_evaluated<int>(1);
68
69 const bke::MeshFieldContext corner_context{mesh, AttrDomain::Corner};
70 fn::FieldEvaluator corner_evaluator{corner_context, corner_edges.size()};
71 corner_evaluator.add(sort_weight_);
72 corner_evaluator.evaluate();
73 const VArray<float> all_sort_weights = corner_evaluator.get_evaluated<float>(0);
74 const bool use_sorting = !all_sort_weights.is_single();
75
76 Array<int> corner_of_edge(mask.min_array_size());
77 mask.foreach_segment(GrainSize(1024), [&](const IndexMaskSegment segment) {
78 /* Reuse arrays to avoid allocation. */
79 Array<int64_t> corner_indices;
80 Array<float> sort_weights;
81 Array<int> sort_indices;
82
83 for (const int selection_i : segment) {
84 const int edge_i = edge_indices[selection_i];
85 const int index_in_sort = indices_in_sort[selection_i];
86 if (!edge_range.contains(edge_i)) {
87 corner_of_edge[selection_i] = 0;
88 continue;
89 }
90
91 const Span<int> corners = edge_to_corner_map[edge_i];
92 if (corners.is_empty()) {
93 corner_of_edge[selection_i] = 0;
94 continue;
95 }
96
97 const int index_in_sort_wrapped = mod_i(index_in_sort, corners.size());
98 if (use_sorting) {
99 /* Retrieve a compressed array of weights for each edge. */
100 sort_weights.reinitialize(corners.size());
101 IndexMaskMemory memory;
102 all_sort_weights.materialize_compressed(IndexMask::from_indices<int>(corners, memory),
103 sort_weights.as_mutable_span());
104
105 /* Sort a separate array of compressed indices corresponding to the compressed weights.
106 * This allows using `materialize_compressed` to avoid virtual function call overhead
107 * when accessing values in the sort weights. However, it means a separate array of
108 * indices within the compressed array is necessary for sorting. */
109 sort_indices.reinitialize(corners.size());
111 std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) {
112 return sort_weights[a] < sort_weights[b];
113 });
114 corner_of_edge[selection_i] = corners[sort_indices[index_in_sort_wrapped]];
115 }
116 else {
117 corner_of_edge[selection_i] = corners[index_in_sort_wrapped];
118 }
119 }
120 });
121
122 return VArray<int>::from_container(std::move(corner_of_edge));
123 }
124
125 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
126 {
127 edge_index_.node().for_each_field_input_recursive(fn);
128 sort_index_.node().for_each_field_input_recursive(fn);
129 sort_weight_.node().for_each_field_input_recursive(fn);
130 }
131
132 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const final
133 {
134 return AttrDomain::Edge;
135 }
136};
137
139 public:
140 CornersOfEdgeCountInput() : bke::MeshFieldInput(CPPType::get<int>(), "Edge Corner Count")
141 {
143 }
144
146 const AttrDomain domain,
147 const IndexMask & /*mask*/) const final
148 {
149 if (domain != AttrDomain::Edge) {
150 return {};
151 }
152 Array<int> counts(mesh.edges_num, 0);
153 array_utils::count_indices(mesh.corner_edges(), counts);
154 return VArray<int>::from_container(std::move(counts));
155 }
156
158 {
159 return 2345897985577;
160 }
161
162 bool is_equal_to(const fn::FieldNode &other) const final
163 {
164 return dynamic_cast<const CornersOfEdgeCountInput *>(&other) != nullptr;
165 }
166
167 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const final
168 {
169 return AttrDomain::Edge;
170 }
171};
172
174{
175 const Field<int> edge_index = params.extract_input<Field<int>>("Edge Index");
176 if (params.output_is_required("Total")) {
177 params.set_output("Total",
178 Field<int>(std::make_shared<bke::EvaluateAtIndexInput>(
179 edge_index,
180 Field<int>(std::make_shared<CornersOfEdgeCountInput>()),
181 AttrDomain::Edge)));
182 }
183 if (params.output_is_required("Corner Index")) {
184 params.set_output("Corner Index",
185 Field<int>(std::make_shared<CornersOfEdgeInput>(
186 edge_index,
187 params.extract_input<Field<int>>("Sort Index"),
188 params.extract_input<Field<float>>("Weights"))));
189 }
190}
191
192static void node_register()
193{
194 static blender::bke::bNodeType ntype;
195 geo_node_type_base(&ntype, "GeometryNodeCornersOfEdge", GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_EDGE);
196 ntype.ui_name = "Corners of Edge";
197 ntype.ui_description = "Retrieve face corners connected to edges";
198 ntype.enum_name_legacy = "CORNERS_OF_EDGE";
199 ntype.nclass = NODE_CLASS_INPUT;
201 ntype.declare = node_declare;
203}
205
206} // namespace blender::nodes::node_geo_mesh_topology_corners_of_edge_cc
#define NODE_CLASS_INPUT
Definition BKE_node.hh:447
#define GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_EDGE
#define final(a, b, c)
Definition BLI_hash.h:19
MINLINE int mod_i(int i, int n)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
unsigned long long int uint64_t
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T * end() const
Definition BLI_array.hh:325
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
const T * begin() const
Definition BLI_array.hh:321
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
constexpr bool contains(int64_t value) const
constexpr int64_t size() const
Definition BLI_span.hh:252
void materialize_compressed(const IndexMask &mask, MutableSpan< T > r_span) const
static VArray from_container(ContainerT container)
FieldInput(const CPPType &type, std::string debug_name="")
Definition field.cc:677
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
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 &mask) const final
CornersOfEdgeInput(Field< int > edge_index, Field< int > sort_index, Field< float > sort_weight)
void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const override
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void count_indices(Span< int > indices, MutableSpan< int > counts)
void fill_index_range(MutableSpan< T > span, const T start=0)
GroupedSpan< int > build_edge_to_corner_map(Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
#define min(a, b)
Definition sort.cc:36
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362