Blender V4.5
node_geo_foreach_geometry_element.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BLI_string_utf8.h"
8
9#include "BLO_read_write.hh"
10
11#include "RNA_access.hh"
12#include "RNA_prototypes.hh"
13
20
21#include "UI_interface.hh"
22#include "UI_resources.hh"
23
24#include "BKE_library.hh"
25#include "BKE_screen.hh"
26
27#include "WM_api.hh"
28
30
32static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_node_ptr)
33{
34 bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(current_node_ptr->owner_id);
35 bNode *current_node = static_cast<bNode *>(current_node_ptr->data);
36
37 const bke::bNodeTreeZones *zones = ntree.zones();
38 if (!zones) {
39 return;
40 }
41 const bke::bNodeTreeZone *zone = zones->get_zone_by_node(current_node->identifier);
42 if (!zone) {
43 return;
44 }
45 if (!zone->output_node_id) {
46 return;
47 }
48 const bool is_zone_input_node = current_node->type_legacy ==
50 bNode &output_node = const_cast<bNode &>(*zone->output_node());
52 current_node_ptr->owner_id, &RNA_Node, &output_node);
53 auto &storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(output_node.storage);
54
55 if (is_zone_input_node) {
56 if (uiLayout *panel = layout->panel(C, "input", false, IFACE_("Input Fields"))) {
58 C, panel, ntree, output_node);
60 ntree, output_node, [&](PointerRNA *item_ptr) {
61 uiLayoutSetPropSep(panel, true);
62 uiLayoutSetPropDecorate(panel, false);
63 panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
64 });
65 }
66 }
67 else {
68 if (uiLayout *panel = layout->panel(C, "main_items", false, IFACE_("Main Geometry"))) {
70 C, panel, ntree, output_node);
72 ntree, output_node, [&](PointerRNA *item_ptr) {
73 uiLayoutSetPropSep(panel, true);
74 uiLayoutSetPropDecorate(panel, false);
75 panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
76 });
77 }
78 if (uiLayout *panel = layout->panel(
79 C, "generation_items", false, IFACE_("Generated Geometry")))
80 {
84 ntree, output_node, [&](PointerRNA *item_ptr) {
86 storage.generation_items.items[storage.generation_items.active_index];
87 uiLayoutSetPropSep(panel, true);
88 uiLayoutSetPropDecorate(panel, false);
89 panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
90 if (active_item.socket_type != SOCK_GEOMETRY) {
91 panel->prop(item_ptr, "domain", UI_ITEM_NONE, std::nullopt, ICON_NONE);
92 }
93 });
94 }
95 }
96
97 layout->prop(&output_node_ptr, "inspection_index", UI_ITEM_NONE, std::nullopt, ICON_NONE);
98}
99
100namespace input_node {
101
103
105{
106 b.use_custom_socket_order();
107 b.allow_any_socket_order();
108 const bNode *node = b.node_or_null();
109 const bNodeTree *tree = b.tree_or_null();
110
111 b.add_default_layout();
112
113 if (!node || !tree) {
114 return;
115 }
116
117 const NodeGeometryForeachGeometryElementInput &storage = node_storage(*node);
118 const bNode *output_node = tree->node_by_id(storage.output_node_id);
119 const auto &output_storage = output_node ?
120 static_cast<const NodeGeometryForeachGeometryElementOutput *>(
121 output_node->storage) :
122 nullptr;
123
124 b.add_output<decl::Int>("Index").description(
125 "Index of the element in the source geometry. Note that the same index can occur more than "
126 "once when iterating over multiple components at once");
127
128 b.add_output<decl::Geometry>("Element")
129 .description(
130 "Single-element geometry for the current iteration. Note that it can be quite "
131 "inefficient to split up large geometries into many small geometries")
132 .propagate_all()
133 .available(output_storage && AttrDomain(output_storage->domain) != AttrDomain::Corner);
134
135 b.add_input<decl::Geometry>("Geometry").description("Geometry whose elements are iterated over");
136
137 b.add_input<decl::Bool>("Selection")
138 .default_value(true)
139 .hide_value()
140 .field_on_all()
141 .description("Selection on the iteration domain");
142
143 if (output_storage) {
144 for (const int i : IndexRange(output_storage->input_items.items_num)) {
145 const NodeForeachGeometryElementInputItem &item = output_storage->input_items.items[i];
146 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
147 const StringRef name = item.name ? item.name : "";
148 const std::string identifier =
150 b.add_input(socket_type, name, identifier)
151 .socket_name_ptr(
153 .description("Field that is evaluated on the iteration domain")
154 .field_on_all();
155 b.add_output(socket_type, name, identifier)
156 .align_with_previous()
157 .description("Evaluated field value for the current element");
158 }
159 }
160
161 b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
162 b.add_output<decl::Extend>("", "__extend__")
163 .structure_type(StructureType::Dynamic)
164 .align_with_previous();
165}
166
167static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
168{
169 bNodeTree &tree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
170 bNode &node = *static_cast<bNode *>(ptr->data);
171 const NodeGeometryForeachGeometryElementInput &storage = node_storage(node);
172 bNode *output_node = tree.node_by_id(storage.output_node_id);
173
174 PointerRNA output_node_ptr = RNA_pointer_create_discrete(ptr->owner_id, &RNA_Node, output_node);
175 layout->prop(&output_node_ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
176}
177
178static void node_init(bNodeTree * /*tree*/, bNode *node)
179{
182 /* Needs to be initialized for the node to work. */
183 data->output_node_id = 0;
184 node->storage = data;
185}
186
187static void node_label(const bNodeTree * /*ntree*/,
188 const bNode * /*node*/,
189 char *label,
190 const int label_maxncpy)
191{
192 BLI_strncpy_utf8(label, IFACE_("For Each Element"), label_maxncpy);
193}
194
195static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
196{
197 bNode *output_node = ntree->node_by_id(node_storage(*node).output_node_id);
198 if (!output_node) {
199 return true;
200 }
203}
204
205static void node_register()
206{
207 static blender::bke::bNodeType ntype;
209 &ntype, "GeometryNodeForeachGeometryElementInput", GEO_NODE_FOREACH_GEOMETRY_ELEMENT_INPUT);
210 ntype.ui_name = "For Each Geometry Element Input";
211 ntype.enum_name_legacy = "FOREACH_GEOMETRY_ELEMENT_INPUT";
213 ntype.initfunc = node_init;
214 ntype.declare = node_declare;
217 ntype.labelfunc = node_label;
219 ntype.gather_link_search_ops = nullptr;
220 ntype.no_muting = true;
222 "NodeGeometryForeachGeometryElementInput",
226}
228
229} // namespace input_node
230
231namespace output_node {
232
234
236{
237 b.use_custom_socket_order();
238 b.allow_any_socket_order();
239
240 b.add_output<decl::Geometry>("Geometry")
241 .description(
242 "The original input geometry with potentially new attributes that are output by the "
243 "zone");
244
245 const bNode *node = b.node_or_null();
246 const bNodeTree *tree = b.tree_or_null();
247 if (node && tree) {
248 const NodeGeometryForeachGeometryElementOutput &storage = node_storage(*node);
249 for (const int i : IndexRange(storage.main_items.items_num)) {
251 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
252 const StringRef name = item.name ? item.name : "";
254 item);
255 b.add_input(socket_type, name, identifier)
256 .socket_name_ptr(
258 .description(
259 "Attribute value that will be stored for the current element on the main geometry");
260 b.add_output(socket_type, name, identifier)
261 .align_with_previous()
262 .field_on({0})
263 .description("Attribute on the geometry above");
264 }
265 b.add_input<decl::Extend>("", "__extend__main");
266 b.add_output<decl::Extend>("", "__extend__main").align_with_previous();
267
268 auto &panel = b.add_panel("Generated");
269
270 int previous_output_geometry_index = -1;
271 int previous_input_geometry_index = -1;
272 for (const int i : IndexRange(storage.generation_items.items_num)) {
274 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
275 if (socket_type == SOCK_GEOMETRY && i > 0) {
276 panel.add_separator();
277 }
278 const StringRef name = item.name ? item.name : "";
279 std::string identifier =
281 auto &input_decl = panel.add_input(socket_type, name, identifier)
282 .socket_name_ptr(
283 &tree->id,
285 &item,
286 "name");
287 auto &output_decl = panel.add_output(socket_type, name, identifier).align_with_previous();
288 if (socket_type == SOCK_GEOMETRY) {
289 previous_input_geometry_index = input_decl.index();
290 previous_output_geometry_index = output_decl.index();
291
292 input_decl.description(
293 "Geometry generated in the current iteration. Will be joined with geometries from all "
294 "other iterations");
295 output_decl.description("Result of joining generated geometries from each iteration");
296 }
297 else {
298 if (previous_output_geometry_index > 0) {
299 input_decl.description("Field that will be stored as attribute on the geometry above");
300 input_decl.field_on({previous_input_geometry_index});
301 output_decl.field_on({previous_output_geometry_index});
302 }
303 output_decl.description("Attribute on the geometry above");
304 }
305 }
306 panel.add_input<decl::Extend>("", "__extend__generation");
307 panel.add_output<decl::Extend>("", "__extend__generation").align_with_previous();
308 }
309}
310
311static void node_init(bNodeTree * /*tree*/, bNode *node)
312{
315
317 1, __func__);
318 NodeForeachGeometryElementGenerationItem &item = data->generation_items.items[0];
319 item.name = BLI_strdup(DATA_("Geometry"));
321 item.identifier = data->generation_items.next_identifier++;
322 data->generation_items.items_num = 1;
323
324 node->storage = data;
325}
326
334
335static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
336{
337 const NodeGeometryForeachGeometryElementOutput &src_storage = node_storage(*src_node);
339 src_storage);
340 dst_node->storage = dst_storage;
341
345}
346
347static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
348{
350 *ntree, *node, *node, *link, "__extend__main"))
351 {
352 return false;
353 }
355 ForeachGeometryElementGenerationItemsAccessor>(*ntree, *node, *node, *link);
356}
357
364
366{
367 const NodeGeometryForeachGeometryElementOutput &storage = node_storage(params.node);
368 if (storage.generation_items.items_num > 0) {
371 row.text = RPT_("Missing Geometry");
372 row.tooltip = TIP_("Each output field has to correspond to a geometry that is above it");
373 row.icon = ICON_ERROR;
374 params.rows.append(std::move(row));
375 }
376 }
377}
378
379static std::pair<bNode *, bNode *> add_foreach_zone(LinkSearchOpParams &params)
380{
381 bNode &input_node = params.add_node("GeometryNodeForeachGeometryElementInput");
382 bNode &output_node = params.add_node("GeometryNodeForeachGeometryElementOutput");
383 output_node.location[0] = 300;
384
385 auto &input_storage = *static_cast<NodeGeometryForeachGeometryElementInput *>(
386 input_node.storage);
387 input_storage.output_node_id = output_node.identifier;
388
389 return {&input_node, &output_node};
390}
391
393{
394 const bNodeSocket &other_socket = params.other_socket();
395 const eNodeSocketDatatype type = eNodeSocketDatatype(other_socket.type);
396 if (type != SOCK_GEOMETRY) {
397 return;
398 }
399 if (other_socket.in_out == SOCK_OUT) {
400 params.add_item_full_name(IFACE_("For Each Element"), [](LinkSearchOpParams &params) {
402 params.update_and_connect_available_socket(*input_node, "Geometry");
403 });
404 }
405 else {
406 params.add_item_full_name(
407 IFACE_("For Each Element " UI_MENU_ARROW_SEP " Main"), [](LinkSearchOpParams &params) {
410 params.update_and_connect_available_socket(*output_node, "Geometry");
411 });
412
413 params.add_item_full_name(IFACE_("For Each Element " UI_MENU_ARROW_SEP " Generated"),
416 params.node_tree.ensure_topology_cache();
417 bke::node_add_link(params.node_tree,
419 output_node->output_socket(2),
420 params.node,
421 params.socket);
422 });
423 }
424}
425
432
439
440static void node_register()
441{
442 static blender::bke::bNodeType ntype;
443 geo_node_type_base(&ntype,
444 "GeometryNodeForeachGeometryElementOutput",
446 ntype.ui_name = "For Each Geometry Element Output";
447 ntype.enum_name_legacy = "FOREACH_GEOMETRY_ELEMENT_OUTPUT";
449 ntype.initfunc = node_init;
450 ntype.declare = node_declare;
457 ntype.no_muting = true;
461 ntype, "NodeGeometryForeachGeometryElementOutput", node_free_storage, node_copy_storage);
463}
465
466} // namespace output_node
467
468} // namespace blender::nodes::node_geo_foreach_geometry_element_cc
469
470namespace blender::nodes {
471
473 &RNA_ForeachGeometryElementInputItem;
474
480
486
488 &RNA_ForeachGeometryElementMainItem;
489
491 const ItemT &item)
492{
493 BLO_write_string(writer, item.name);
494}
495
501
503 &RNA_ForeachGeometryElementGenerationItem;
504
510
516
517} // namespace blender::nodes
#define NODE_CLASS_INTERFACE
Definition BKE_node.hh:445
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_INPUT
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_OUTPUT
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5351
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define RPT_(msgid)
#define TIP_(msgid)
#define IFACE_(msgid)
#define DATA_(msgid)
@ SOCK_OUT
eNodeSocketDatatype
@ SOCK_GEOMETRY
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define C
Definition RandGen.cpp:29
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
BMesh const char void * data
std::optional< int > output_node_id
const bNode * output_node() const
const bNodeTreeZone * get_zone_by_node(const int32_t node_id) const
KDTree_3d * tree
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
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:4087
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_label(const bNodeTree *, const bNode *, char *label, const int label_maxncpy)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
static void node_copy_storage(bNodeTree *, bNode *dst_node, const bNode *src_node)
static void node_blend_write(const bNodeTree &, const bNode &node, BlendWriter &writer)
static std::pair< bNode *, bNode * > add_foreach_zone(LinkSearchOpParams &params)
static void node_blend_read(bNodeTree &, bNode &node, BlendDataReader &reader)
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_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)
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)
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
#define UI_MENU_ARROW_SEP
NodeForeachGeometryElementGenerationItem * items
NodeForeachGeometryElementMainItem * items
NodeForeachGeometryElementMainItems main_items
NodeForeachGeometryElementGenerationItems generation_items
ID * owner_id
Definition RNA_types.hh:51
void * data
Definition RNA_types.hh:53
int16_t type_legacy
void * storage
int32_t identifier
Defines a node type.
Definition BKE_node.hh:226
NodeBlendWriteFunction blend_write_storage_content
Definition BKE_node.hh:383
NodeBlendDataReadFunction blend_data_read_storage_content
Definition BKE_node.hh:384
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
void(* labelfunc)(const bNodeTree *ntree, const bNode *node, char *label, int label_maxncpy)
Definition BKE_node.hh:258
void(* draw_buttons_ex)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:249
NodeExtraInfoFunction get_extra_info
Definition BKE_node.hh:374
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
static void blend_read_data_item(BlendDataReader *reader, ItemT &item)
static void blend_write_item(BlendWriter *writer, const ItemT &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