Blender V4.5
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"
16
17#include "RNA_enum_types.hh"
18#include "RNA_prototypes.hh"
19
20#include "BLO_read_write.hh"
21
23
25
27
29{
30 const bNode *node = b.node_or_null();
31 if (!node) {
32 return;
33 }
34 const NodeIndexSwitch &storage = node_storage(*node);
35 const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.data_type);
36 const bool supports_fields = socket_type_supports_fields(data_type);
37
38 const Span<IndexSwitchItem> items = storage.items_span();
39 auto &index = b.add_input<decl::Int>("Index").min(0).max(std::max<int>(0, items.size() - 1));
40 if (supports_fields) {
41 index.supports_field();
42 }
43
44 for (const int i : items.index_range()) {
45 const std::string identifier = IndexSwitchItemsAccessor::socket_identifier_for_item(items[i]);
46 auto &input = b.add_input(data_type, std::to_string(i), std::move(identifier));
47 if (supports_fields) {
48 input.supports_field();
49 }
50 /* Labels are ugly in combination with data-block pickers and are usually disabled. */
52 }
53
54 auto &output = b.add_output(data_type, "Output");
55 if (supports_fields) {
56 output.dependent_field().reference_pass_all();
57 }
58 else if (data_type == SOCK_GEOMETRY) {
59 output.propagate_all();
60 }
61
62 b.add_input<decl::Extend>("", "__extend__");
63}
64
65static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
66{
67 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
68}
69
71{
72 bNode &node = *static_cast<bNode *>(ptr->data);
73 NodeIndexSwitch &storage = node_storage(node);
74 if (uiLayout *panel = layout->panel(C, "index_switch_items", false, IFACE_("Items"))) {
75 panel->op("node.index_switch_item_add", IFACE_("Add Item"), ICON_ADD);
76 uiLayout *col = &panel->column(false);
77 for (const int i : IndexRange(storage.items_num)) {
78 uiLayout *row = &col->row(false);
79 row->label(node.input_socket(i + 1).name, ICON_NONE);
80 PointerRNA op_ptr = row->op("node.index_switch_item_remove", "", ICON_REMOVE);
81 RNA_int_set(&op_ptr, "index", i);
82 }
83 }
84}
85
87{
88 socket_items::ops::add_item<IndexSwitchItemsAccessor>(ot, "Add Item", __func__, "Add bake item");
89}
90
92{
94 ot, "Remove Item", __func__, "Remove an item from the index switch");
95}
96
102
103static void node_init(bNodeTree * /*tree*/, bNode *node)
104{
106 data->data_type = SOCK_GEOMETRY;
107 data->next_identifier = 0;
108
109 BLI_assert(data->items == nullptr);
110 const int default_items_num = 2;
111 data->items = MEM_calloc_arrayN<IndexSwitchItem>(default_items_num, __func__);
112 for (const int i : IndexRange(default_items_num)) {
113 data->items[i].identifier = data->next_identifier++;
114 }
115 data->items_num = default_items_num;
116
117 node->storage = data;
118}
119
121{
122 if (params.in_out() == SOCK_OUT) {
123 params.add_item(IFACE_("Output"), [](LinkSearchOpParams &params) {
124 bNode &node = params.add_node("GeometryNodeIndexSwitch");
125 node_storage(node).data_type = params.socket.type;
126 params.update_and_connect_available_socket(node, "Output");
127 });
128 }
129 else {
130 const eNodeSocketDatatype other_type = eNodeSocketDatatype(params.other_socket().type);
131 if (params.node_tree().typeinfo->validate_link(other_type, SOCK_INT)) {
132 params.add_item(IFACE_("Index"), [](LinkSearchOpParams &params) {
133 bNode &node = params.add_node("GeometryNodeIndexSwitch");
134 params.update_and_connect_available_socket(node, "Index");
135 });
136 }
137 }
138}
139
140constexpr int value_inputs_start = 1;
141
142class IndexSwitchFunction : public mf::MultiFunction {
143 mf::Signature signature_;
144 Array<std::string> debug_names_;
145
146 public:
147 IndexSwitchFunction(const CPPType &type, const int items_num)
148 {
149 mf::SignatureBuilder builder{"Index Switch", signature_};
150 builder.single_input<int>("Index");
151 debug_names_.reinitialize(items_num);
152 for (const int i : IndexRange(items_num)) {
153 debug_names_[i] = std::to_string(i);
154 builder.single_input(debug_names_[i].c_str(), type);
155 }
156 builder.single_output("Output", type);
157 this->set_signature(&signature_);
158 }
159
160 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
161 {
162 const int inputs_num = signature_.params.size() - 2;
163 const VArray<int> indices = params.readonly_single_input<int>(0, "Index");
164
165 GMutableSpan output = params.uninitialized_single_output(
166 signature_.params.index_range().last(), "Output");
167 const CPPType &type = output.type();
168
169 if (const std::optional<int> i = indices.get_if_single()) {
170 if (IndexRange(inputs_num).contains(*i)) {
171 const GVArray inputs = params.readonly_single_input(value_inputs_start + *i);
172 inputs.materialize_to_uninitialized(mask, output.data());
173 }
174 else {
175 type.fill_construct_indices(type.default_value(), output.data(), mask);
176 }
177 return;
178 }
179
180 /* Use one extra mask at the end for invalid indices. */
181 const int invalid_index = inputs_num;
182 IndexMaskMemory memory;
183 Array<IndexMask> masks(inputs_num + 1);
185 mask,
186 memory,
187 [&](const int64_t i) {
188 const int index = indices[i];
189 return IndexRange(inputs_num).contains(index) ? index : invalid_index;
190 },
191 masks);
192
193 for (const int i : IndexRange(inputs_num)) {
194 if (!masks[i].is_empty()) {
195 const GVArray inputs = params.readonly_single_input(value_inputs_start + i);
196 inputs.materialize_to_uninitialized(masks[i], output.data());
197 }
198 }
199
200 type.fill_construct_indices(type.default_value(), output.data(), masks[invalid_index]);
201 }
202
204 {
205 ExecutionHints hints;
206 hints.allocates_array = true;
207 return hints;
208 }
209};
210
212 private:
213 const bNode &node_;
214 bool can_be_field_ = false;
215 const CPPType *field_base_type_;
216
217 public:
220 : node_(node)
221 {
222 const NodeIndexSwitch &storage = node_storage(node);
223 const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.data_type);
224 const bNodeSocket &index_socket = node.input_socket(0);
225 const bNodeSocket &output_socket = node.output_socket(0);
226 const CPPType &cpp_type = *output_socket.typeinfo->geometry_nodes_cpp_type;
227
228 debug_name_ = node.name;
229 can_be_field_ = socket_type_supports_fields(data_type);
230 field_base_type_ = output_socket.typeinfo->base_cpp_type;
231
232 MutableSpan<int> lf_index_by_bsocket = lf_graph_info.mapping.lf_index_by_bsocket;
233
234 lf_index_by_bsocket[index_socket.index_in_tree()] = inputs_.append_and_get_index_as(
236 lf_index_by_bsocket[output_socket.index_in_tree()] = outputs_.append_and_get_index_as(
237 "Value", cpp_type);
238
239 for (const int i : storage.items_span().index_range()) {
240 const bNodeSocket &input = node.input_socket(value_inputs_start + i);
241 lf_index_by_bsocket[input.index_in_tree()] = inputs_.append_and_get_index_as(
242 input.identifier, cpp_type, lf::ValueUsage::Maybe);
243 }
244 }
245
246 void execute_impl(lf::Params &params, const lf::Context & /*context*/) const override
247 {
248 SocketValueVariant index_variant = params.get_input<SocketValueVariant>(0);
249 if (index_variant.is_context_dependent_field() && can_be_field_) {
250 this->execute_field(index_variant.get<Field<int>>(), params);
251 }
252 else {
253 this->execute_single(index_variant.get<int>(), params);
254 }
255 }
256
257 int values_num() const
258 {
259 return inputs_.size() - value_inputs_start;
260 }
261
262 void execute_single(const int index, lf::Params &params) const
263 {
264 const int values_num = this->values_num();
265 for (const int i : IndexRange(values_num)) {
266 if (i != index) {
267 params.set_input_unused(value_inputs_start + i);
268 }
269 }
270
271 /* Check for an invalid index. */
272 if (!IndexRange(values_num).contains(index)) {
274 return;
275 }
276
277 /* Request input and try again if unavailable. */
278 void *value_to_forward = params.try_get_input_data_ptr_or_request(index + value_inputs_start);
279 if (value_to_forward == nullptr) {
280 return;
281 }
282
283 const CPPType &type = *outputs_[0].type;
284 void *output_ptr = params.get_output_data_ptr(0);
285 type.move_construct(value_to_forward, output_ptr);
286 params.output_set(0);
287 }
288
290 {
291 const int values_num = this->values_num();
293 for (const int i : IndexRange(values_num)) {
294 input_values[i] = params.try_get_input_data_ptr_or_request<SocketValueVariant>(
296 }
297 if (input_values.as_span().contains(nullptr)) {
298 /* Try again when inputs are available. */
299 return;
300 }
301
302 Vector<GField> input_fields({std::move(index)});
303 for (const int i : IndexRange(values_num)) {
304 input_fields.append(input_values[i]->extract<GField>());
305 }
306
307 std::unique_ptr<mf::MultiFunction> switch_fn = std::make_unique<IndexSwitchFunction>(
308 *field_base_type_, values_num);
309 GField output_field(FieldOperation::Create(std::move(switch_fn), std::move(input_fields)));
310
311 void *output_ptr = params.get_output_data_ptr(0);
312 new (output_ptr) SocketValueVariant(std::move(output_field));
313 params.output_set(0);
314 }
315};
316
317static void node_rna(StructRNA *srna)
318{
320 srna,
321 "data_type",
322 "Data Type",
323 "",
327 [](bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free) {
328 *r_free = true;
330 [](const EnumPropertyItem &item) -> bool {
331 if (!U.experimental.use_bundle_and_closure_nodes) {
332 if (ELEM(item.value, SOCK_BUNDLE, SOCK_CLOSURE)) {
333 return false;
334 }
335 }
336 return ELEM(item.value,
338 SOCK_INT,
344 SOCK_RGBA,
350 SOCK_MENU,
353 });
354 });
355}
356
362
363static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
364{
365 const NodeIndexSwitch &src_storage = node_storage(*src_node);
366 auto *dst_storage = MEM_dupallocN<NodeIndexSwitch>(__func__, src_storage);
367 dst_node->storage = dst_storage;
368
370}
371
372static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
373{
375 *ntree, *node, *node, *link);
376}
377
378static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
379{
381}
382
383static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
384{
386}
387
389 const bNode &node,
390 const bNodeSocket & /*output_socket*/)
391{
392 const NodeIndexSwitch &src_storage = node_storage(node);
393 if (src_storage.items_num == 0) {
394 return nullptr;
395 }
396 /* Default to the 0 input. */
397 return &node.input_socket(1);
398}
399
400static void register_node()
401{
402 static blender::bke::bNodeType ntype;
403
404 geo_node_type_base(&ntype, "GeometryNodeIndexSwitch", GEO_NODE_INDEX_SWITCH);
405 ntype.ui_name = "Index Switch";
406 ntype.ui_description = "Choose between an arbitrary number of values with an index";
407 ntype.enum_name_legacy = "INDEX_SWITCH";
409 ntype.declare = node_declare;
410 ntype.initfunc = node_init;
422
423 node_rna(ntype.rna_ext.srna);
424}
426
427} // namespace blender::nodes::node_geo_index_switch_cc
428
429namespace blender::nodes {
430
431std::unique_ptr<LazyFunction> get_index_switch_node_lazy_function(
432 const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
433{
434 using namespace node_geo_index_switch_cc;
436 return std::make_unique<LazyFunctionForIndexSwitchNode>(node, lf_graph_info);
437}
438
439StructRNA *IndexSwitchItemsAccessor::item_srna = &RNA_IndexSwitchItem;
440
442{
443}
444
448
449} // namespace blender::nodes
450
451blender::Span<IndexSwitchItem> NodeIndexSwitch::items_span() const
452{
453 return blender::Span<IndexSwitchItem>(items, items_num);
454}
455
456blender::MutableSpan<IndexSwitchItem> NodeIndexSwitch::items_span()
457{
458 return blender::MutableSpan<IndexSwitchItem>(items, items_num);
459}
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:439
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define GEO_NODE_INDEX_SWITCH
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
#define IFACE_(msgid)
@ SOCK_OUT
eNodeSocketDatatype
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_CLOSURE
@ SOCK_BOOLEAN
@ SOCK_MATERIAL
@ SOCK_MATRIX
@ SOCK_FLOAT
@ SOCK_IMAGE
@ SOCK_COLLECTION
@ SOCK_BUNDLE
@ SOCK_GEOMETRY
@ SOCK_ROTATION
@ SOCK_OBJECT
@ SOCK_STRING
@ SOCK_RGBA
@ SOCK_MENU
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
__forceinline float extract(const int4 &b)
Definition binning.cpp:27
#define U
BMesh const char void * data
long long int int64_t
Span< T > as_span() const
Definition BLI_array.hh:232
static const CPPType & get()
void fill_construct_indices(const void *value, void *dst, const IndexMask &mask) const
const void * default_value() const
void move_construct(void *src, void *dst) const
static void from_groups(const IndexMask &universe, IndexMaskMemory &memory, Fn &&get_group_index, MutableSpan< IndexMask > r_masks)
constexpr bool contains(int64_t value) const
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
void set_signature(const Signature *signature)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
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
static ushort indices[]
uint col
#define input
#define output
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5603
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
static void node_init(bNodeTree *, bNode *node)
static void node_blend_read(bNodeTree &, bNode &node, BlendDataReader &reader)
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 void node_blend_write(const bNodeTree &, const bNode &node, BlendWriter &writer)
static const bNodeSocket * node_internally_linked_input(const bNodeTree &, const bNode &node, const bNodeSocket &)
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 blend_write(BlendWriter *writer, const bNode &node)
void blend_read_data(BlendDataReader *reader, bNode &node)
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, std::string idname, const std::optional< int16_t > legacy_type)
static blender::bke::bNodeSocketTemplate inputs[]
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
const EnumPropertyItem rna_enum_node_socket_data_type_items[]
#define min(a, b)
Definition sort.cc:36
StructRNA * srna
Definition RNA_types.hh:909
bNodeSocketTypeHandle * typeinfo
char name[64]
int16_t type_legacy
void * storage
Defines a node type.
Definition BKE_node.hh:226
NodeInternallyLinkedInputFunction internally_linked_input
Definition BKE_node.hh:377
NodeBlendWriteFunction blend_write_storage_content
Definition BKE_node.hh:383
std::string ui_description
Definition BKE_node.hh:232
NodeBlendDataReadFunction blend_data_read_storage_content
Definition BKE_node.hh:384
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:249
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:371
bool(* insert_link)(bNodeTree *ntree, bNode *node, bNodeLink *link)
Definition BKE_node.hh:321
NodeDeclareFunction declare
Definition BKE_node.hh:355
void(* register_operators)()
Definition BKE_node.hh:410
bool ignore_inferred_input_socket_visibility
Definition BKE_node.hh:415
static std::string socket_identifier_for_item(const IndexSwitchItem &item)
static void blend_read_data_item(BlendDataReader *reader, ItemT &item)
static void blend_write_item(BlendWriter *writer, const ItemT &item)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
void label(blender::StringRef name, int icon)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4227
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))