Blender V4.3
node_geo_index_switch.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
6
7#include "UI_interface.hh"
8#include "UI_resources.hh"
9
11#include "NOD_rna_define.hh"
12#include "NOD_socket.hh"
15
16#include "RNA_enum_types.hh"
17#include "RNA_prototypes.hh"
18
19#include "BLO_read_write.hh"
20
22
24
26
28{
29 const bNode *node = b.node_or_null();
30 if (!node) {
31 return;
32 }
33 const NodeIndexSwitch &storage = node_storage(*node);
34 const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.data_type);
35 const bool supports_fields = socket_type_supports_fields(data_type);
36
37 const Span<IndexSwitchItem> items = storage.items_span();
38 auto &index = b.add_input<decl::Int>("Index").min(0).max(std::max<int>(0, items.size() - 1));
39 if (supports_fields) {
40 index.supports_field();
41 }
42
43 for (const int i : items.index_range()) {
44 const std::string identifier = IndexSwitchItemsAccessor::socket_identifier_for_item(items[i]);
45 auto &input = b.add_input(data_type, std::to_string(i), std::move(identifier));
46 if (supports_fields) {
47 input.supports_field();
48 }
49 /* Labels are ugly in combination with data-block pickers and are usually disabled. */
50 input.hide_label(ELEM(data_type, SOCK_OBJECT, SOCK_IMAGE, SOCK_COLLECTION, SOCK_MATERIAL));
51 }
52
53 auto &output = b.add_output(data_type, "Output");
54 if (supports_fields) {
55 output.dependent_field().reference_pass_all();
56 }
57 else if (data_type == SOCK_GEOMETRY) {
58 output.propagate_all();
59 }
60
61 b.add_input<decl::Extend>("", "__extend__");
62}
63
64static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
65{
66 uiItemR(layout, ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
67}
68
69static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
70{
71 bNode &node = *static_cast<bNode *>(ptr->data);
72 NodeIndexSwitch &storage = node_storage(node);
73 if (uiLayout *panel = uiLayoutPanel(C, layout, "index_switch_items", false, TIP_("Items"))) {
74 uiItemO(panel, IFACE_("Add Item"), ICON_ADD, "node.index_switch_item_add");
75 uiLayout *col = uiLayoutColumn(panel, false);
76 for (const int i : IndexRange(storage.items_num)) {
77 uiLayout *row = uiLayoutRow(col, false);
78 uiItemL(row, node.input_socket(i + 1).name, ICON_NONE);
79 uiItemIntO(row, "", ICON_REMOVE, "node.index_switch_item_remove", "index", i);
80 }
81 }
82}
83
85{
86 socket_items::ops::add_item<IndexSwitchItemsAccessor>(ot, "Add Item", __func__, "Add bake item");
87}
88
90{
92 ot, "Remove Item", __func__, "Remove an item from the index switch");
93}
94
100
101static void node_init(bNodeTree * /*tree*/, bNode *node)
102{
103 NodeIndexSwitch *data = MEM_cnew<NodeIndexSwitch>(__func__);
104 data->data_type = SOCK_GEOMETRY;
105 data->next_identifier = 0;
106
107 BLI_assert(data->items == nullptr);
108 const int default_items_num = 2;
109 data->items = MEM_cnew_array<IndexSwitchItem>(default_items_num, __func__);
110 for (const int i : IndexRange(default_items_num)) {
111 data->items[i].identifier = data->next_identifier++;
112 }
113 data->items_num = default_items_num;
114
115 node->storage = data;
116}
117
119{
120 if (params.in_out() == SOCK_OUT) {
121 params.add_item(IFACE_("Output"), [](LinkSearchOpParams &params) {
122 bNode &node = params.add_node("GeometryNodeIndexSwitch");
123 node_storage(node).data_type = params.socket.type;
124 params.update_and_connect_available_socket(node, "Output");
125 });
126 }
127 else {
128 const eNodeSocketDatatype other_type = eNodeSocketDatatype(params.other_socket().type);
129 if (params.node_tree().typeinfo->validate_link(other_type, SOCK_INT)) {
130 params.add_item(IFACE_("Index"), [](LinkSearchOpParams &params) {
131 bNode &node = params.add_node("GeometryNodeIndexSwitch");
132 params.update_and_connect_available_socket(node, "Index");
133 });
134 }
135 }
136}
137
138constexpr int value_inputs_start = 1;
139
141 mf::Signature signature_;
142 Array<std::string> debug_names_;
143
144 public:
145 IndexSwitchFunction(const CPPType &type, const int items_num)
146 {
147 mf::SignatureBuilder builder{"Index Switch", signature_};
148 builder.single_input<int>("Index");
149 debug_names_.reinitialize(items_num);
150 for (const int i : IndexRange(items_num)) {
151 debug_names_[i] = std::to_string(i);
152 builder.single_input(debug_names_[i].c_str(), type);
153 }
154 builder.single_output("Output", type);
155 this->set_signature(&signature_);
156 }
157
158 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
159 {
160 const int inputs_num = signature_.params.size() - 2;
161 const VArray<int> indices = params.readonly_single_input<int>(0, "Index");
162
163 GMutableSpan output = params.uninitialized_single_output(
164 signature_.params.index_range().last(), "Output");
165 const CPPType &type = output.type();
166
167 if (const std::optional<int> i = indices.get_if_single()) {
168 if (IndexRange(inputs_num).contains(*i)) {
169 const GVArray inputs = params.readonly_single_input(value_inputs_start + *i);
170 inputs.materialize_to_uninitialized(mask, output.data());
171 }
172 else {
173 type.fill_construct_indices(type.default_value(), output.data(), mask);
174 }
175 return;
176 }
177
178 /* Use one extra mask at the end for invalid indices. */
179 const int invalid_index = inputs_num;
180 IndexMaskMemory memory;
181 Array<IndexMask> masks(inputs_num + 1);
183 mask,
184 memory,
185 [&](const int64_t i) {
186 const int index = indices[i];
187 return IndexRange(inputs_num).contains(index) ? index : invalid_index;
188 },
189 masks);
190
191 for (const int i : IndexRange(inputs_num)) {
192 if (!masks[i].is_empty()) {
193 const GVArray inputs = params.readonly_single_input(value_inputs_start + i);
194 inputs.materialize_to_uninitialized(masks[i], output.data());
195 }
196 }
197
198 type.fill_construct_indices(type.default_value(), output.data(), masks[invalid_index]);
199 }
200
202 {
203 ExecutionHints hints;
204 hints.allocates_array = true;
205 return hints;
206 }
207};
208
210 private:
211 const bNode &node_;
212 bool can_be_field_ = false;
213 const CPPType *field_base_type_;
214
215 public:
218 : node_(node)
219 {
220 const NodeIndexSwitch &storage = node_storage(node);
221 const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.data_type);
222 const bNodeSocket &index_socket = node.input_socket(0);
223 const bNodeSocket &output_socket = node.output_socket(0);
224 const CPPType &cpp_type = *output_socket.typeinfo->geometry_nodes_cpp_type;
225
226 debug_name_ = node.name;
227 can_be_field_ = socket_type_supports_fields(data_type);
228 field_base_type_ = output_socket.typeinfo->base_cpp_type;
229
230 MutableSpan<int> lf_index_by_bsocket = lf_graph_info.mapping.lf_index_by_bsocket;
231
232 lf_index_by_bsocket[index_socket.index_in_tree()] = inputs_.append_and_get_index_as(
233 "Index", CPPType::get<SocketValueVariant>(), lf::ValueUsage::Used);
234 lf_index_by_bsocket[output_socket.index_in_tree()] = outputs_.append_and_get_index_as(
235 "Value", cpp_type);
236
237 for (const int i : storage.items_span().index_range()) {
238 const bNodeSocket &input = node.input_socket(value_inputs_start + i);
239 lf_index_by_bsocket[input.index_in_tree()] = inputs_.append_and_get_index_as(
240 input.identifier, cpp_type, lf::ValueUsage::Maybe);
241 }
242 }
243
244 void execute_impl(lf::Params &params, const lf::Context & /*context*/) const override
245 {
246 SocketValueVariant index_variant = params.get_input<SocketValueVariant>(0);
247 if (index_variant.is_context_dependent_field() && can_be_field_) {
248 this->execute_field(index_variant.get<Field<int>>(), params);
249 }
250 else {
251 this->execute_single(index_variant.get<int>(), params);
252 }
253 }
254
255 int values_num() const
256 {
257 return inputs_.size() - value_inputs_start;
258 }
259
260 void execute_single(const int index, lf::Params &params) const
261 {
262 const int values_num = this->values_num();
263 for (const int i : IndexRange(values_num)) {
264 if (i != index) {
265 params.set_input_unused(value_inputs_start + i);
266 }
267 }
268
269 /* Check for an invalid index. */
270 if (!IndexRange(values_num).contains(index)) {
272 return;
273 }
274
275 /* Request input and try again if unavailable. */
276 void *value_to_forward = params.try_get_input_data_ptr_or_request(index + value_inputs_start);
277 if (value_to_forward == nullptr) {
278 return;
279 }
280
281 const CPPType &type = *outputs_[0].type;
282 void *output_ptr = params.get_output_data_ptr(0);
283 type.move_construct(value_to_forward, output_ptr);
284 params.output_set(0);
285 }
286
288 {
289 const int values_num = this->values_num();
291 for (const int i : IndexRange(values_num)) {
292 input_values[i] = params.try_get_input_data_ptr_or_request<SocketValueVariant>(
294 }
295 if (input_values.as_span().contains(nullptr)) {
296 /* Try again when inputs are available. */
297 return;
298 }
299
300 Vector<GField> input_fields({std::move(index)});
301 for (const int i : IndexRange(values_num)) {
302 input_fields.append(input_values[i]->extract<GField>());
303 }
304
305 std::unique_ptr<mf::MultiFunction> switch_fn = std::make_unique<IndexSwitchFunction>(
306 *field_base_type_, values_num);
307 GField output_field(FieldOperation::Create(std::move(switch_fn), std::move(input_fields)));
308
309 void *output_ptr = params.get_output_data_ptr(0);
310 new (output_ptr) SocketValueVariant(std::move(output_field));
311 params.output_set(0);
312 }
313};
314
315static void node_rna(StructRNA *srna)
316{
318 srna,
319 "data_type",
320 "Data Type",
321 "",
325 [](bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free) {
326 *r_free = true;
328 [](const EnumPropertyItem &item) -> bool {
329 return ELEM(item.value,
331 SOCK_INT,
337 SOCK_RGBA,
343 SOCK_MENU);
344 });
345 });
346}
347
348static void node_free_storage(bNode *node)
349{
351 MEM_freeN(node->storage);
352}
353
354static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
355{
356 const NodeIndexSwitch &src_storage = node_storage(*src_node);
357 auto *dst_storage = MEM_cnew<NodeIndexSwitch>(__func__, src_storage);
358 dst_node->storage = dst_storage;
359
361}
362
363static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
364{
366 *ntree, *node, *node, *link);
367}
368
369static void register_node()
370{
371 static blender::bke::bNodeType ntype;
372
373 geo_node_type_base(&ntype, GEO_NODE_INDEX_SWITCH, "Index Switch", NODE_CLASS_CONVERTER);
374 ntype.declare = node_declare;
375 ntype.initfunc = node_init;
383
384 node_rna(ntype.rna_ext.srna);
385}
387
388} // namespace blender::nodes::node_geo_index_switch_cc
389
390namespace blender::nodes {
391
392std::unique_ptr<LazyFunction> get_index_switch_node_lazy_function(
393 const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
394{
395 using namespace node_geo_index_switch_cc;
396 BLI_assert(node.type == GEO_NODE_INDEX_SWITCH);
397 return std::make_unique<LazyFunctionForIndexSwitchNode>(node, lf_graph_info);
398}
399
400StructRNA *IndexSwitchItemsAccessor::item_srna = &RNA_IndexSwitchItem;
402
404{
405 const auto &storage = *static_cast<const NodeIndexSwitch *>(node.storage);
406 BLO_write_struct_array(writer, IndexSwitchItem, storage.items_num, storage.items);
407}
408
410{
411 auto &storage = *static_cast<NodeIndexSwitch *>(node.storage);
412 BLO_read_struct_array(reader, IndexSwitchItem, storage.items_num, &storage.items);
413}
414
415} // namespace blender::nodes
416
417blender::Span<IndexSwitchItem> NodeIndexSwitch::items_span() const
418{
419 return blender::Span<IndexSwitchItem>(items, items_num);
420}
421
422blender::MutableSpan<IndexSwitchItem> NodeIndexSwitch::items_span()
423{
424 return blender::MutableSpan<IndexSwitchItem>(items, items_num);
425}
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:410
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define GEO_NODE_INDEX_SWITCH
Definition BKE_node.hh:1348
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ELEM(...)
#define BLO_write_struct_array(writer, struct_name, array_size, data_ptr)
#define BLO_read_struct_array(reader, struct_name, array_size, ptr_p)
#define TIP_(msgid)
#define IFACE_(msgid)
@ SOCK_OUT
eNodeSocketDatatype
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_MATERIAL
@ SOCK_MATRIX
@ SOCK_FLOAT
@ SOCK_IMAGE
@ SOCK_COLLECTION
@ SOCK_GEOMETRY
@ SOCK_ROTATION
@ SOCK_OBJECT
@ SOCK_STRING
@ SOCK_RGBA
@ SOCK_MENU
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
void uiItemIntO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
void uiItemL(uiLayout *layout, const char *name, int icon)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
#define UI_ITEM_NONE
PanelLayout uiLayoutPanel(const bContext *C, uiLayout *layout, const char *idname, bool default_closed)
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
__forceinline float extract(const int4 &b)
Definition binning.cpp:27
Span< T > as_span() const
Definition BLI_array.hh:232
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
static const CPPType & get()
constexpr bool contains(int64_t value) const
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
void append(const T &value)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
void set_signature(const Signature *signature)
static void from_groups(const IndexMask &universe, IndexMaskMemory &memory, Fn &&get_group_index, MutableSpan< IndexMask > r_masks)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
LazyFunctionForIndexSwitchNode(const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
void execute_impl(lf::Params &params, const lf::Context &) const override
local_group_size(16, 16) .push_constant(Type b
uint col
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float4 mask(const int4 mask, const float4 a)
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
static void node_init(bNodeTree *, bNode *node)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void NODE_OT_index_switch_item_add(wmOperatorType *ot)
static void NODE_OT_index_switch_item_remove(wmOperatorType *ot)
static void node_declare(NodeDeclarationBuilder &b)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
void remove_item_by_index(wmOperatorType *ot, const char *name, const char *idname, const char *description)
void add_item(wmOperatorType *ot, const char *name, const char *idname, const char *description)
void copy_array(const bNode &src_node, bNode &dst_node)
bool try_add_item_via_any_extend_socket(bNodeTree &ntree, bNode &extend_node, bNode &storage_node, bNodeLink &link, const std::optional< StringRef > socket_identifier=std::nullopt)
std::unique_ptr< LazyFunction > get_index_switch_node_lazy_function(const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
bool socket_type_supports_fields(const eNodeSocketDatatype socket_type)
void set_default_remaining_node_outputs(lf::Params &params, const bNode &node)
const EnumPropertyItem * enum_items_filter(const EnumPropertyItem *original_item_array, FunctionRef< bool(const EnumPropertyItem &item)> fn)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
const EnumPropertyItem rna_enum_node_socket_data_type_items[]
#define min(a, b)
Definition sort.c:32
__int64 int64_t
Definition stdint.h:89
StructRNA * srna
Definition RNA_types.hh:780
void * data
Definition RNA_types.hh:42
bNodeSocketTypeHandle * typeinfo
void * storage
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:240
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:363
bool(* insert_link)(bNodeTree *ntree, bNode *node, bNodeLink *link)
Definition BKE_node.hh:309
NodeDeclareFunction declare
Definition BKE_node.hh:347
void(* register_operators)()
Definition BKE_node.hh:392
static void blend_write(BlendWriter *writer, const bNode &node)
static std::string socket_identifier_for_item(const IndexSwitchItem &item)
static void blend_read_data(BlendDataReader *reader, bNode &node)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))