Blender V5.0
node_geo_combine_bundle.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "NOD_geo_bundle.hh"
12#include "NOD_sync_sockets.hh"
13
14#include "BKE_idprop.hh"
15
16#include "BLO_read_write.hh"
17
19
22
24
26
28{
29 const bNodeTree *tree = b.tree_or_null();
30 const bNode *node = b.node_or_null();
31 if (tree && node) {
32 const NodeCombineBundle &storage = node_storage(*node);
33 for (const int i : IndexRange(storage.items_num)) {
34 const NodeCombineBundleItem &item = storage.items[i];
35 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
36 const StringRef name = item.name ? item.name : "";
37 const std::string identifier = CombineBundleItemsAccessor::socket_identifier_for_item(item);
38 auto &decl = b.add_input(socket_type, name, identifier)
39 .socket_name_ptr(
41 .supports_field();
43 decl.structure_type(StructureType(item.structure_type));
44 }
45 else {
46 decl.structure_type(StructureType::Dynamic);
47 }
48 }
49 }
50 b.add_input<decl::Extend>("", "__extend__");
51 b.add_output<decl::Bundle>("Bundle").propagate_all().reference_pass_all();
52}
53
54static void node_init(bNodeTree * /*tree*/, bNode *node)
55{
56 auto *storage = MEM_callocN<NodeCombineBundle>(__func__);
57 node->storage = storage;
58}
59
60static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
61{
62 const NodeCombineBundle &src_storage = node_storage(*src_node);
63 auto *dst_storage = MEM_dupallocN<NodeCombineBundle>(__func__, src_storage);
64 dst_node->storage = dst_storage;
65
67}
68
74
76{
77 if (params.C && params.link.fromnode == &params.node && params.link.tosock->type == SOCK_BUNDLE)
78 {
79 const NodeCombineBundle &storage = node_storage(params.node);
80 if (storage.items_num == 0) {
82 if (snode && snode->edittree == &params.ntree) {
83 sync_sockets_combine_bundle(*snode, params.node, nullptr, params.link.tosock);
84 }
85 }
86 return true;
87 }
89 params.ntree, params.node, params.node, params.link);
90}
91
92static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *node_ptr)
93{
94 bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(node_ptr->owner_id);
95 bNode &node = *static_cast<bNode *>(node_ptr->data);
96
97 layout->use_property_split_set(true);
98 layout->use_property_decorate_set(false);
99
100 layout->op("node.sockets_sync", IFACE_("Sync"), ICON_FILE_REFRESH);
101 layout->prop(node_ptr, "define_signature", UI_ITEM_NONE, std::nullopt, ICON_NONE);
102 if (uiLayout *panel = layout->panel(C, "bundle_items", false, IFACE_("Bundle Items"))) {
104 C, panel, ntree, node);
106 ntree, node, [&](PointerRNA *item_ptr) {
107 const auto &item = *item_ptr->data_as<NodeCombineBundleItem>();
108 panel->use_property_split_set(true);
109 panel->use_property_decorate_set(false);
110 panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, IFACE_("Type"), ICON_NONE);
111 if (!socket_type_always_single(eNodeSocketDatatype(item.socket_type))) {
112 panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, IFACE_("Shape"), ICON_NONE);
113 }
114 });
115 }
116}
117
122
124{
125 const bNode &node = params.node();
126 const NodeCombineBundle &storage = node_storage(node);
127
128 BundlePtr bundle_ptr = Bundle::create();
129 BLI_assert(bundle_ptr->is_mutable());
130 Bundle &bundle = const_cast<Bundle &>(*bundle_ptr);
131
132 for (const int i : IndexRange(storage.items_num)) {
133 const NodeCombineBundleItem &item = storage.items[i];
135 if (!stype || !stype->geometry_nodes_default_value) {
136 continue;
137 }
138 const StringRef name = item.name;
139 if (name.is_empty()) {
140 continue;
141 }
143 node.input_socket(i).identifier);
144 bundle.add(name, BundleItemSocketValue{stype, std::move(value)});
145 }
146
147 params.set_output("Bundle", std::move(bundle_ptr));
148}
149
151{
152 const bNodeSocket &other_socket = params.other_socket();
153 if (other_socket.in_out == SOCK_OUT) {
155 params.node_tree().type))
156 {
157 return;
158 }
159 params.add_item(IFACE_("Item"), [](LinkSearchOpParams &params) {
160 bNode &node = params.add_node("NodeCombineBundle");
161 const auto *item =
163 params.node_tree, node, params.socket.typeinfo->type, params.socket.name);
164 params.update_and_connect_available_socket(node, item->name);
165 });
166 }
167 else {
168 if (other_socket.type != SOCK_BUNDLE) {
169 return;
170 }
171 params.add_item(IFACE_("Bundle"), [](LinkSearchOpParams &params) {
172 bNode &node = params.add_node("NodeCombineBundle");
173 params.connect_available_socket(node, "Bundle");
174
175 SpaceNode &snode = *CTX_wm_space_node(&params.C);
176 sync_sockets_combine_bundle(snode, node, nullptr);
177 });
178 }
179}
180
181static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
182{
184}
185
186static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
187{
189}
190
191static void node_register()
192{
193 static blender::bke::bNodeType ntype;
194
195 sh_geo_node_type_base(&ntype, "NodeCombineBundle", NODE_COMBINE_BUNDLE);
196 ntype.ui_name = "Combine Bundle";
197 ntype.ui_description = "Combine multiple socket values into one.";
199 ntype.declare = node_declare;
200 ntype.initfunc = node_init;
208 bke::node_type_storage(ntype, "NodeCombineBundle", node_free_storage, node_copy_storage);
210}
212
213} // namespace blender::nodes::node_geo_combine_bundle_cc
214
215namespace blender::nodes {
216
217StructRNA *CombineBundleItemsAccessor::item_srna = &RNA_NodeCombineBundleItem;
218
220{
221 BLO_write_string(writer, item.name);
222}
223
228
230{
231 std::string result;
232 if (name.is_empty()) {
233 return result;
234 }
235 /* Disallow certain characters so that we can use them to e.g. build a bundle path or
236 * expressions referencing multiple bundle items. We might not need all of them in the future,
237 * but better reserve them now while we still can. */
238 constexpr StringRefNull forbidden_chars_str = "/*&|\"^~!,{}()+$#@[];:?<>.-%\\=";
239 const Span<char> forbidden_chars = forbidden_chars_str;
240 for (const char c : name) {
241 if (forbidden_chars.contains(c)) {
242 result += '_';
243 }
244 else {
245 result += c;
246 }
247 }
248 if (!result.empty()) {
249 /* Disallow leading spaces. */
250 if (std::isspace(result[0])) {
251 result[0] = '_';
252 }
253 /* Disallow trailing spaces. */
254 const int last_index = result.size() - 1;
255 if (std::isspace(result[last_index])) {
256 result[last_index] = '_';
257 }
258 }
259 return result;
260}
261
262} // namespace blender::nodes
SpaceNode * CTX_wm_space_node(const bContext *C)
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:453
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_COMBINE_BUNDLE
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5828
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define IFACE_(msgid)
@ NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO
@ SOCK_OUT
eNodeSocketDatatype
@ SOCK_BUNDLE
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
bool add(StringRef key, const BundleItemValue &value)
KDTree_3d * tree
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
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
bNodeSocketType * node_socket_type_find_static(int type, int subtype=0)
Definition node.cc:2471
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:5414
static bool node_insert_link(bke::NodeInsertLinkParams &params)
static void node_blend_write(const bNodeTree &, const bNode &node, BlendWriter &writer)
static void node_init(bNodeTree *, bNode *node)
static void node_geo_exec(GeoNodeExecParams params)
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void node_blend_read(bNodeTree &, bNode &node, BlendDataReader &reader)
static void node_declare(NodeDeclarationBuilder &b)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *node_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)
void blend_read_data(BlendDataReader *reader, bNode &node)
void copy_array(const bNode &src_node, bNode &dst_node)
Accessor::ItemT * add_item_with_socket_type_and_name(bNodeTree &ntree, bNode &node, const eNodeSocketDatatype socket_type, const char *name, std::optional< int > dimensions=std::nullopt)
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, typename Accessor::ItemT **r_new_item=nullptr)
ImplicitSharingPtr< Bundle > BundlePtr
void sync_sockets_combine_bundle(SpaceNode &snode, bNode &combine_bundle_node, ReportList *reports, const bNodeSocket *src_bundle_socket)
bool socket_type_always_single(const eNodeSocketDatatype socket_type)
void sh_geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
const char * name
NodeCombineBundleItem * items
T * data_as() const
Definition RNA_types.hh:124
ID * owner_id
Definition RNA_types.hh:51
void * data
Definition RNA_types.hh:53
struct bNodeTree * edittree
bNodeSocketTypeHandle * typeinfo
char name[64]
void * storage
Defines a socket type.
Definition BKE_node.hh:158
const SocketValueVariant * geometry_nodes_default_value
Definition BKE_node.hh:211
Defines a node type.
Definition BKE_node.hh:238
NodeBlendWriteFunction blend_write_storage_content
Definition BKE_node.hh:390
std::string ui_description
Definition BKE_node.hh:244
NodeBlendDataReadFunction blend_data_read_storage_content
Definition BKE_node.hh:391
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:261
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
bool(* insert_link)(NodeInsertLinkParams &params)
Definition BKE_node.hh:333
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
NodeDeclareFunction declare
Definition BKE_node.hh:362
void(* register_operators)()
Definition BKE_node.hh:417
static void blend_write_item(BlendWriter *writer, const ItemT &item)
static void blend_read_data_item(BlendDataReader *reader, ItemT &item)
static bool supports_socket_type(const eNodeSocketDatatype socket_type, const int ntree_type)
static std::string validate_name(const StringRef name)
static std::string socket_identifier_for_item(const ItemT &item)
void use_property_decorate_set(bool is_sep)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void use_property_split_set(bool value)
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