Blender V4.5
node_geo_menu_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 "DNA_node_types.h"
8
10
11#include "UI_interface.hh"
12#include "UI_resources.hh"
13
15#include "NOD_rna_define.hh"
16#include "NOD_socket.hh"
21
22#include "BLO_read_write.hh"
23
24#include "RNA_enum_types.hh"
25#include "RNA_prototypes.hh"
26
27#include "BKE_screen.hh"
28
29#include "WM_api.hh"
30
32
34
36{
37 if (!U.experimental.use_bundle_and_closure_nodes) {
38 if (ELEM(data_type, SOCK_BUNDLE, SOCK_CLOSURE)) {
39 return false;
40 }
41 }
42 return ELEM(data_type,
58 SOCK_MENU);
59}
60
62{
63 const bNodeTree *ntree = b.tree_or_null();
64 const bNode *node = b.node_or_null();
65 if (node == nullptr) {
66 return;
67 }
68 const NodeMenuSwitch &storage = node_storage(*node);
69 const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.data_type);
70 const bool supports_fields = socket_type_supports_fields(data_type);
71
72 auto &menu = b.add_input<decl::Menu>("Menu");
73 if (supports_fields) {
74 menu.supports_field();
75 }
76
77 for (const NodeEnumItem &enum_item : storage.enum_definition.items()) {
78 const std::string identifier = MenuSwitchItemsAccessor::socket_identifier_for_item(enum_item);
79 auto &input = b.add_input(data_type, enum_item.name, std::move(identifier))
80 .socket_name_ptr(
81 &ntree->id, MenuSwitchItemsAccessor::item_srna, &enum_item, "name");
82 ;
83 if (supports_fields) {
84 input.supports_field();
85 }
86 /* Labels are ugly in combination with data-block pickers and are usually disabled. */
88 }
89
90 auto &output = b.add_output(data_type, "Output");
91 if (supports_fields) {
92 output.dependent_field().reference_pass_all();
93 }
94 else if (data_type == SOCK_GEOMETRY) {
95 output.propagate_all();
96 }
97
98 b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
99}
100
101static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
102{
103 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
104}
105
106static void node_init(bNodeTree * /*tree*/, bNode *node)
107{
109 data->data_type = SOCK_GEOMETRY;
110 data->enum_definition.next_identifier = 0;
111 data->enum_definition.items_array = nullptr;
112 data->enum_definition.items_num = 0;
113 node->storage = data;
114
117}
118
124
125static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
126{
127 const NodeMenuSwitch &src_storage = node_storage(*src_node);
128 NodeMenuSwitch *dst_storage = MEM_dupallocN<NodeMenuSwitch>(__func__, src_storage);
129 dst_node->storage = dst_storage;
130
132}
133
135{
136 const eNodeSocketDatatype data_type = eNodeSocketDatatype(params.other_socket().type);
137 if (params.in_out() == SOCK_IN) {
138 if (data_type == SOCK_MENU) {
139 params.add_item(IFACE_("Menu"), [](LinkSearchOpParams &params) {
140 bNode &node = params.add_node("GeometryNodeMenuSwitch");
141 params.update_and_connect_available_socket(node, "Menu");
142 });
143 }
144 }
145 else {
146 if (data_type != SOCK_MENU) {
147 params.add_item(IFACE_("Output"), [](LinkSearchOpParams &params) {
148 bNode &node = params.add_node("GeometryNodeMenuSwitch");
149 node_storage(node).data_type = params.socket.type;
150 params.update_and_connect_available_socket(node, "Output");
151 });
152 }
153 }
154}
155
160class MenuSwitchFn : public mf::MultiFunction {
161 const NodeEnumDefinition &enum_def_;
162 const CPPType &type_;
163 mf::Signature signature_;
164
165 public:
166 MenuSwitchFn(const NodeEnumDefinition &enum_def, const CPPType &type)
167 : enum_def_(enum_def), type_(type)
168 {
169 mf::SignatureBuilder builder{"Menu Switch", signature_};
170 builder.single_input<int>("Menu");
171 for (const NodeEnumItem &enum_item : enum_def.items()) {
172 builder.single_input(enum_item.name, type);
173 }
174 builder.single_output("Output", type);
175
176 this->set_signature(&signature_);
177 }
178
179 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
180 {
181 const int value_inputs_start = 1;
182 const int inputs_num = enum_def_.items_num;
183 const VArray<int> values = params.readonly_single_input<int>(0, "Menu");
184 /* Use one extra mask at the end for invalid indices. */
185 const int invalid_index = inputs_num;
186
187 GMutableSpan output = params.uninitialized_single_output(
188 signature_.params.index_range().last(), "Output");
189
190 auto find_item_index = [&](const int value) -> int {
191 for (const int i : enum_def_.items().index_range()) {
192 const NodeEnumItem &item = enum_def_.items()[i];
193 if (item.identifier == value) {
194 return i;
195 }
196 }
197 return invalid_index;
198 };
199
200 if (const std::optional<int> value = values.get_if_single()) {
201 const int index = find_item_index(*value);
202 if (index < inputs_num) {
203 const GVArray inputs = params.readonly_single_input(value_inputs_start + index);
204 inputs.materialize_to_uninitialized(mask, output.data());
205 }
206 else {
207 type_.fill_construct_indices(type_.default_value(), output.data(), mask);
208 }
209 return;
210 }
211
212 IndexMaskMemory memory;
213 Array<IndexMask> masks(inputs_num + 1);
215 mask, memory, [&](const int64_t i) { return find_item_index(values[i]); }, masks);
216
217 for (const int i : IndexRange(inputs_num)) {
218 if (!masks[i].is_empty()) {
219 const GVArray inputs = params.readonly_single_input(value_inputs_start + i);
220 inputs.materialize_to_uninitialized(masks[i], output.data());
221 }
222 }
223
224 type_.fill_construct_indices(type_.default_value(), output.data(), masks[invalid_index]);
225 }
226};
227
229 private:
230 const bNode &node_;
231 bool can_be_field_ = false;
232 const NodeEnumDefinition &enum_def_;
233 const CPPType *cpp_type_;
234 const CPPType *field_base_type_;
235
236 public:
239 : node_(node), enum_def_(node_storage(node).enum_definition)
240 {
241 const NodeMenuSwitch &storage = node_storage(node);
242 const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.data_type);
243 can_be_field_ = socket_type_supports_fields(data_type);
244 const bke::bNodeSocketType *socket_type = bke::node_socket_type_find_static(data_type);
245 BLI_assert(socket_type != nullptr);
246 cpp_type_ = socket_type->geometry_nodes_cpp_type;
247 field_base_type_ = socket_type->base_cpp_type;
248
249 MutableSpan<int> lf_index_by_bsocket = lf_graph_info.mapping.lf_index_by_bsocket;
250 debug_name_ = node.name;
251 lf_index_by_bsocket[node.input_socket(0).index_in_tree()] = inputs_.append_and_get_index_as(
253 for (const int i : enum_def_.items().index_range()) {
254 const NodeEnumItem &enum_item = enum_def_.items()[i];
255 lf_index_by_bsocket[node.input_socket(i + 1).index_in_tree()] =
256 inputs_.append_and_get_index_as(enum_item.name, *cpp_type_, lf::ValueUsage::Maybe);
257 }
258 lf_index_by_bsocket[node.output_socket(0).index_in_tree()] = outputs_.append_and_get_index_as(
259 "Value", *cpp_type_);
260 }
261
262 void execute_impl(lf::Params &params, const lf::Context & /*context*/) const override
263 {
264 SocketValueVariant condition_variant = params.get_input<SocketValueVariant>(0);
265 if (condition_variant.is_context_dependent_field() && can_be_field_) {
266 this->execute_field(condition_variant.get<Field<int>>(), params);
267 }
268 else {
269 this->execute_single(condition_variant.get<int>(), params);
270 }
271 }
272
273 void execute_single(const int condition, lf::Params &params) const
274 {
275 for (const int i : IndexRange(enum_def_.items_num)) {
276 const NodeEnumItem &enum_item = enum_def_.items_array[i];
277 const int input_index = i + 1;
278 if (enum_item.identifier == condition) {
279 void *value_to_forward = params.try_get_input_data_ptr_or_request(input_index);
280 if (value_to_forward == nullptr) {
281 /* Try again when the value is available. */
282 return;
283 }
284
285 void *output_ptr = params.get_output_data_ptr(0);
286 cpp_type_->move_construct(value_to_forward, output_ptr);
287 params.output_set(0);
288 }
289 else {
290 params.set_input_unused(input_index);
291 }
292 }
293 /* No guarantee that the switch input matches any enum,
294 * set default outputs to ensure valid state. */
296 }
297
299 {
300 /* When the condition is a non-constant field, we need all inputs. */
301 const int values_num = this->enum_def_.items_num;
302 Array<SocketValueVariant *, 8> input_values(values_num);
303 for (const int i : IndexRange(values_num)) {
304 const int input_index = i + 1;
305 input_values[i] = params.try_get_input_data_ptr_or_request<SocketValueVariant>(input_index);
306 }
307 if (input_values.as_span().contains(nullptr)) {
308 /* Try again when inputs are available. */
309 return;
310 }
311
312 Vector<GField> item_fields(enum_def_.items_num + 1);
313 item_fields[0] = std::move(condition);
314 for (const int i : IndexRange(enum_def_.items_num)) {
315 item_fields[i + 1] = input_values[i]->extract<GField>();
316 }
317 std::unique_ptr<MultiFunction> multi_function = std::make_unique<MenuSwitchFn>(
318 enum_def_, *field_base_type_);
319 GField output_field{FieldOperation::Create(std::move(multi_function), std::move(item_fields))};
320
321 void *output_ptr = params.get_output_data_ptr(0);
322 new (output_ptr) SocketValueVariant(std::move(output_field));
323 params.output_set(0);
324 }
325};
326
333 const NodeEnumDefinition &enum_def_;
334
335 public:
337 : enum_def_(node_storage(node).enum_definition)
338 {
339 debug_name_ = "Menu Switch Socket Usage";
340 inputs_.append_as("Condition", CPPType::get<SocketValueVariant>());
341 for (const int i : IndexRange(enum_def_.items_num)) {
342 const NodeEnumItem &enum_item = enum_def_.items()[i];
343 outputs_.append_as(enum_item.name, CPPType::get<bool>());
344 }
345 }
346
347 void execute_impl(lf::Params &params, const lf::Context & /*context*/) const override
348 {
349 const SocketValueVariant &condition_variant = params.get_input<SocketValueVariant>(0);
350 if (condition_variant.is_context_dependent_field()) {
351 for (const int i : IndexRange(enum_def_.items_num)) {
352 params.set_output(i, true);
353 }
354 }
355 else {
356 const int32_t value = condition_variant.get<int>();
357 for (const int i : IndexRange(enum_def_.items_num)) {
358 const NodeEnumItem &enum_item = enum_def_.items()[i];
359 params.set_output(i, value == enum_item.identifier);
360 }
361 }
362 }
363};
364
366{
367 bNodeTree &tree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
368 bNode &node = *static_cast<bNode *>(ptr->data);
369
370 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
371
372 if (uiLayout *panel = layout->panel(C, "menu_switch_items", false, IFACE_("Menu Items"))) {
374 C, panel, tree, node);
376 tree, node, [&](PointerRNA *item_ptr) {
377 uiLayoutSetPropSep(panel, true);
378 uiLayoutSetPropDecorate(panel, false);
379 panel->prop(item_ptr, "description", UI_ITEM_NONE, std::nullopt, ICON_NONE);
380 });
381 }
382}
383
388
389static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
390{
392 *ntree, *node, *node, *link);
393}
394
395static void node_blend_write(const bNodeTree & /*ntree*/, const bNode &node, BlendWriter &writer)
396{
398}
399
400static void node_blend_read(bNodeTree & /*ntree*/, bNode &node, BlendDataReader &reader)
401{
403}
404
406 const bNode &node,
407 const bNodeSocket & /*output_socket*/)
408{
409 const NodeMenuSwitch &storage = node_storage(node);
410 if (storage.enum_definition.items_num == 0) {
411 return nullptr;
412 }
413 /* Default to the first enum item input. */
414 return &node.input_socket(1);
415}
416
417static void node_rna(StructRNA *srna)
418{
420 srna,
421 "data_type",
422 "Data Type",
423 "",
427 [](bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free) {
428 *r_free = true;
429 return enum_items_filter(
432 });
433 });
434}
435
436static void register_node()
437{
438 static blender::bke::bNodeType ntype;
439
440 geo_node_type_base(&ntype, "GeometryNodeMenuSwitch", GEO_NODE_MENU_SWITCH);
441 ntype.ui_name = "Menu Switch";
442 ntype.ui_description = "Select from multiple inputs by name";
443 ntype.enum_name_legacy = "MENU_SWITCH";
445 ntype.declare = node_declare;
446 ntype.initfunc = node_init;
458
459 node_rna(ntype.rna_ext.srna);
460}
461NOD_REGISTER_NODE(register_node)
462
463} // namespace blender::nodes::node_geo_menu_switch_cc
464
465namespace blender::nodes {
466
467std::unique_ptr<LazyFunction> get_menu_switch_node_lazy_function(
468 const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
469{
470 using namespace node_geo_menu_switch_cc;
472 return std::make_unique<LazyFunctionForMenuSwitchNode>(node, lf_graph_info);
473}
474
475std::unique_ptr<LazyFunction> get_menu_switch_node_socket_usage_lazy_function(const bNode &node)
476{
477 using namespace node_geo_menu_switch_cc;
479 return std::make_unique<LazyFunctionForMenuSwitchSocketUsage>(node);
480}
481
483
485{
486 BLO_write_string(writer, item.name);
487 BLO_write_string(writer, item.description);
488}
489
491{
492 BLO_read_string(reader, &item.name);
493 BLO_read_string(reader, &item.description);
494}
495
496} // namespace blender::nodes
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:439
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define GEO_NODE_MENU_SWITCH
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5351
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define IFACE_(msgid)
@ SOCK_IN
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
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
#define U
BMesh const char void * data
long long int int64_t
static void from_groups(const IndexMask &universe, IndexMaskMemory &memory, Fn &&get_group_index, MutableSpan< IndexMask > r_masks)
Span< T > as_span() const
Definition BLI_array.hh:232
static const CPPType & get()
std::optional< T > get_if_single() const
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
void execute_field(Field< int > condition, lf::Params &params) const
void execute_impl(lf::Params &params, const lf::Context &) const override
LazyFunctionForMenuSwitchNode(const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
void execute_single(const int condition, lf::Params &params) const
void execute_impl(lf::Params &params, const lf::Context &) const override
MenuSwitchFn(const NodeEnumDefinition &enum_def, const CPPType &type)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
KDTree_3d * tree
#define input
#define this
#define output
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
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
bNodeSocketType * node_socket_type_find_static(int type, int subtype=0)
Definition node.cc:2803
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_blend_write(const bNodeTree &, const bNode &node, BlendWriter &writer)
static void node_init(bNodeTree *, bNode *node)
static void node_blend_read(bNodeTree &, bNode &node, BlendDataReader &reader)
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_declare(blender::nodes::NodeDeclarationBuilder &b)
static bool is_supported_socket_type(const eNodeSocketDatatype data_type)
static const bNodeSocket * node_internally_linked_input(const bNodeTree &, const bNode &node, const bNodeSocket &)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void draw_items_list_with_operators(const bContext *C, uiLayout *layout, const bNodeTree &tree, const bNode &node)
static void draw_active_item_props(const bNodeTree &tree, const bNode &node, const FunctionRef< void(PointerRNA *item_ptr)> draw_item)
void blend_write(BlendWriter *writer, const bNode &node)
Accessor::ItemT * add_item_with_name(bNode &node, const char *name)
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)
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)
std::unique_ptr< LazyFunction > get_menu_switch_node_socket_usage_lazy_function(const bNode &node)
bool socket_type_supports_fields(const eNodeSocketDatatype socket_type)
void set_default_remaining_node_outputs(lf::Params &params, const bNode &node)
std::unique_ptr< LazyFunction > get_menu_switch_node_lazy_function(const bNode &node, GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
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[]
const EnumPropertyItem rna_enum_node_socket_data_type_items[]
StructRNA * srna
Definition RNA_types.hh:909
NodeEnumDefinition enum_definition
char name[64]
int16_t type_legacy
void * storage
Defines a socket type.
Definition BKE_node.hh:152
const blender::CPPType * geometry_nodes_cpp_type
Definition BKE_node.hh:203
const blender::CPPType * base_cpp_type
Definition BKE_node.hh:199
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 NodeEnumItem &item)
static void blend_write_item(BlendWriter *writer, const ItemT &item)
static void blend_read_data_item(BlendDataReader *reader, ItemT &item)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
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:4226