Blender V4.5
node_geo_mesh_topology_corners_of_vertex.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 "BLI_array_utils.hh"
8
10
12
14{
15 b.add_input<decl::Int>("Vertex Index")
16 .implicit_field(NODE_DEFAULT_INPUT_INDEX_FIELD)
17 .description("The vertex to retrieve data from. Defaults to the vertex from the context");
18 b.add_input<decl::Float>("Weights").supports_field().hide_value().description(
19 "Values used to sort corners attached to the vertex. Uses indices by default");
20 b.add_input<decl::Int>("Sort Index")
21 .min(0)
22 .supports_field()
23 .description("Which of the sorted corners to output");
24 b.add_output<decl::Int>("Corner Index")
25 .field_source_reference_all()
26 .description("A corner connected to the face, chosen by the sort index");
27 b.add_output<decl::Int>("Total").field_source().reference_pass({0}).description(
28 "The number of faces or corners connected to each vertex");
29}
30
32 const Field<int> vert_index_;
33 const Field<int> sort_index_;
34 const Field<float> sort_weight_;
35
36 public:
37 CornersOfVertInput(Field<int> vert_index, Field<int> sort_index, Field<float> sort_weight)
38 : bke::MeshFieldInput(CPPType::get<int>(), "Corner of Vertex"),
39 vert_index_(std::move(vert_index)),
40 sort_index_(std::move(sort_index)),
41 sort_weight_(std::move(sort_weight))
42 {
44 }
45
47 const AttrDomain domain,
48 const IndexMask &mask) const final
49 {
50 const IndexRange vert_range(mesh.verts_num);
51 const GroupedSpan<int> vert_to_corner_map = mesh.vert_to_corner_map();
52
53 const bke::MeshFieldContext context{mesh, domain};
54 fn::FieldEvaluator evaluator{context, &mask};
55 evaluator.add(vert_index_);
56 evaluator.add(sort_index_);
57 evaluator.evaluate();
58 const VArray<int> vert_indices = evaluator.get_evaluated<int>(0);
59 const VArray<int> indices_in_sort = evaluator.get_evaluated<int>(1);
60
61 const bke::MeshFieldContext corner_context{mesh, AttrDomain::Corner};
62 fn::FieldEvaluator corner_evaluator{corner_context, mesh.corners_num};
63 corner_evaluator.add(sort_weight_);
64 corner_evaluator.evaluate();
65 const VArray<float> all_sort_weights = corner_evaluator.get_evaluated<float>(0);
66 const bool use_sorting = !all_sort_weights.is_single();
67
68 Array<int> corner_of_vertex(mask.min_array_size());
69 mask.foreach_segment(GrainSize(1024), [&](const IndexMaskSegment segment) {
70 /* Reuse arrays to avoid allocation. */
71 Array<float> sort_weights;
72 Array<int> sort_indices;
73
74 for (const int selection_i : segment) {
75 const int vert_i = vert_indices[selection_i];
76 const int index_in_sort = indices_in_sort[selection_i];
77 if (!vert_range.contains(vert_i)) {
78 corner_of_vertex[selection_i] = 0;
79 continue;
80 }
81
82 const Span<int> corners = vert_to_corner_map[vert_i];
83 if (corners.is_empty()) {
84 corner_of_vertex[selection_i] = 0;
85 continue;
86 }
87
88 const int index_in_sort_wrapped = mod_i(index_in_sort, corners.size());
89 if (use_sorting) {
90 /* Retrieve a compressed array of weights for each edge. */
91 sort_weights.reinitialize(corners.size());
92 IndexMaskMemory memory;
93 all_sort_weights.materialize_compressed(IndexMask::from_indices<int>(corners, memory),
94 sort_weights.as_mutable_span());
95
96 /* Sort a separate array of compressed indices corresponding to the compressed weights.
97 * This allows using `materialize_compressed` to avoid virtual function call overhead
98 * when accessing values in the sort weights. However, it means a separate array of
99 * indices within the compressed array is necessary for sorting. */
100 sort_indices.reinitialize(corners.size());
102 std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) {
103 return sort_weights[a] < sort_weights[b];
104 });
105 corner_of_vertex[selection_i] = corners[sort_indices[index_in_sort_wrapped]];
106 }
107 else {
108 corner_of_vertex[selection_i] = corners[index_in_sort_wrapped];
109 }
110 }
111 });
112
113 return VArray<int>::ForContainer(std::move(corner_of_vertex));
114 }
115
116 void for_each_field_input_recursive(FunctionRef<void(const FieldInput &)> fn) const override
117 {
118 vert_index_.node().for_each_field_input_recursive(fn);
119 sort_index_.node().for_each_field_input_recursive(fn);
120 sort_weight_.node().for_each_field_input_recursive(fn);
121 }
122
124 {
125 return 3541871368173645;
126 }
127
128 bool is_equal_to(const fn::FieldNode &other) const final
129 {
130 if (const auto *typed = dynamic_cast<const CornersOfVertInput *>(&other)) {
131 return typed->vert_index_ == vert_index_ && typed->sort_index_ == sort_index_ &&
132 typed->sort_weight_ == sort_weight_;
133 }
134 return false;
135 }
136
137 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const final
138 {
139 return AttrDomain::Point;
140 }
141};
142
144 public:
145 CornersOfVertCountInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Corner Count")
146 {
148 }
149
151 const AttrDomain domain,
152 const IndexMask & /*mask*/) const final
153 {
154 if (domain != AttrDomain::Point) {
155 return {};
156 }
157 Array<int> counts(mesh.verts_num, 0);
158 array_utils::count_indices(mesh.corner_verts(), counts);
159 return VArray<int>::ForContainer(std::move(counts));
160 }
161
163 {
164 return 253098745374645;
165 }
166
167 bool is_equal_to(const fn::FieldNode &other) const final
168 {
169 return dynamic_cast<const CornersOfVertCountInput *>(&other) != nullptr;
170 }
171
172 std::optional<AttrDomain> preferred_domain(const Mesh & /*mesh*/) const final
173 {
174 return AttrDomain::Point;
175 }
176};
177
179{
180 const Field<int> vert_index = params.extract_input<Field<int>>("Vertex Index");
181 if (params.output_is_required("Total")) {
182 params.set_output("Total",
183 Field<int>(std::make_shared<bke::EvaluateAtIndexInput>(
184 vert_index,
185 Field<int>(std::make_shared<CornersOfVertCountInput>()),
186 AttrDomain::Point)));
187 }
188 if (params.output_is_required("Corner Index")) {
189 params.set_output("Corner Index",
190 Field<int>(std::make_shared<CornersOfVertInput>(
191 vert_index,
192 params.extract_input<Field<int>>("Sort Index"),
193 params.extract_input<Field<float>>("Weights"))));
194 }
195}
196
197static void node_register()
198{
199 static blender::bke::bNodeType ntype;
201 &ntype, "GeometryNodeCornersOfVertex", GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_VERTEX);
202 ntype.ui_name = "Corners of Vertex";
203 ntype.ui_description = "Retrieve face corners connected to vertices";
204 ntype.enum_name_legacy = "CORNERS_OF_VERTEX";
205 ntype.nclass = NODE_CLASS_INPUT;
207 ntype.declare = node_declare;
209}
211
212} // namespace blender::nodes::node_geo_mesh_topology_corners_of_vertex_cc
#define NODE_CLASS_INPUT
Definition BKE_node.hh:433
#define GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_VERTEX
#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:237
const T * end() const
Definition BLI_array.hh:314
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:398
const T * begin() const
Definition BLI_array.hh:310
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
constexpr bool contains(int64_t value) const
void materialize_compressed(const IndexMask &mask, MutableSpan< T > r_span) const
static VArray ForContainer(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
CornersOfVertInput(Field< int > vert_index, Field< int > sort_index, Field< float > sort_weight)
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)
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
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:226
std::string ui_description
Definition BKE_node.hh:232
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
NodeDeclareFunction declare
Definition BKE_node.hh:355