From ad8f940400a64649afe319404ed0c66207250a24 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 16 Sep 2023 14:48:01 +0200 Subject: [PATCH 01/38] initial for-each zone boilerplate --- .../datafiles/userdef/userdef_default_theme.c | 1 + scripts/startup/bl_operators/node.py | 19 ++++- scripts/startup/bl_ui/node_add_menu.py | 5 ++ .../startup/bl_ui/node_add_menu_geometry.py | 1 + source/blender/blenkernel/BKE_node.h | 2 + .../blender/blenkernel/intern/node_runtime.cc | 17 +++++ .../blenkernel/intern/node_tree_zones.cc | 21 +++++- .../blenloader/intern/versioning_userdef.cc | 1 + .../blender/editors/include/UI_resources.hh | 1 + source/blender/editors/interface/resources.cc | 3 + .../blender/editors/space_node/node_draw.cc | 9 ++- .../blender/editors/space_node/node_edit.cc | 13 ++++ .../blender/editors/space_node/node_group.cc | 40 ++++++++++ .../blender/editors/space_node/node_select.cc | 11 +++ source/blender/makesdna/DNA_node_types.h | 5 ++ source/blender/makesdna/DNA_userdef_types.h | 2 +- .../blender/makesrna/intern/rna_nodetree.cc | 60 +++++++++++++++ source/blender/makesrna/intern/rna_userdef.cc | 6 ++ source/blender/nodes/NOD_geometry.hh | 3 + source/blender/nodes/NOD_static_types.h | 2 + source/blender/nodes/geometry/CMakeLists.txt | 2 + .../geometry/nodes/node_geo_foreach_input.cc | 75 +++++++++++++++++++ .../geometry/nodes/node_geo_foreach_output.cc | 36 +++++++++ .../intern/geometry_nodes_lazy_function.cc | 9 +++ 24 files changed, 337 insertions(+), 7 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 81283c4d128..83ef621ba4c 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -868,6 +868,7 @@ const bTheme U_theme_default = { .nodeclass_attribute = RGBA(0x001566ff), .node_zone_simulation = RGBA(0x66416233), .node_zone_repeat = RGBA(0x76512f33), + .node_zone_foreach = RGBA(0x2f517633), .movie = RGBA(0x0f0f0fcc), .gp_vertex_size = 3, .gp_vertex = RGBA(0x97979700), diff --git a/scripts/startup/bl_operators/node.py b/scripts/startup/bl_operators/node.py index 4c7f15e96db..4613ac03826 100644 --- a/scripts/startup/bl_operators/node.py +++ b/scripts/startup/bl_operators/node.py @@ -182,9 +182,12 @@ class NodeAddZoneOperator(NodeAddOperator): # Connect geometry sockets by default. # Get the sockets by their types, because the name is not guaranteed due to i18n. - from_socket = next(s for s in input_node.outputs if s.type == 'GEOMETRY') - to_socket = next(s for s in output_node.inputs if s.type == 'GEOMETRY') - tree.links.new(to_socket, from_socket) + try: + from_socket = next(s for s in input_node.outputs if s.type == 'GEOMETRY') + to_socket = next(s for s in output_node.inputs if s.type == 'GEOMETRY') + tree.links.new(to_socket, from_socket) + except: + pass return {'FINISHED'} @@ -209,6 +212,15 @@ class NODE_OT_add_repeat_zone(NodeAddZoneOperator, Operator): output_node_type = "GeometryNodeRepeatOutput" +class NODE_OT_add_foreach_zone(NodeAddZoneOperator, Operator): + """Add a for-each zone that allows executing nodes in parallel a dynamic number of times""" + bl_idname = "node.add_foreach_zone" + bl_label = "Add For-Each Zone" + bl_options = {'REGISTER', 'UNDO'} + + input_node_type = "GeometryNodeForEachInput" + output_node_type = "GeometryNodeForEachOutput" + class NODE_OT_collapse_hide_unused_toggle(Operator): """Toggle collapsed nodes and hide unused sockets""" bl_idname = "node.collapse_hide_unused_toggle" @@ -372,6 +384,7 @@ classes = ( NODE_OT_add_node, NODE_OT_add_simulation_zone, NODE_OT_add_repeat_zone, + NODE_OT_add_foreach_zone, NODE_OT_collapse_hide_unused_toggle, NODE_OT_interface_item_new, NODE_OT_interface_item_duplicate, diff --git a/scripts/startup/bl_ui/node_add_menu.py b/scripts/startup/bl_ui/node_add_menu.py index ae23abd92fb..9dd28073b8b 100644 --- a/scripts/startup/bl_ui/node_add_menu.py +++ b/scripts/startup/bl_ui/node_add_menu.py @@ -74,6 +74,11 @@ def add_repeat_zone(layout, label): props.use_transform = True return props +def add_foreach_zone(layout, label): + props = layout.operator("node.add_foreach_zone", text=label, text_ctxt=i18n_contexts.default) + props.use_transform = True + return props + class NODE_MT_category_layout(Menu): bl_idname = "NODE_MT_category_layout" diff --git a/scripts/startup/bl_ui/node_add_menu_geometry.py b/scripts/startup/bl_ui/node_add_menu_geometry.py index 718a57dd001..da2fe5a4d06 100644 --- a/scripts/startup/bl_ui/node_add_menu_geometry.py +++ b/scripts/startup/bl_ui/node_add_menu_geometry.py @@ -542,6 +542,7 @@ class NODE_MT_category_GEO_UTILITIES(Menu): layout.menu("NODE_MT_category_GEO_UTILITIES_ROTATION") layout.separator() node_add_menu.add_node_type(layout, "FunctionNodeRandomValue") + node_add_menu.add_foreach_zone(layout, label="For-Each Zone") node_add_menu.add_repeat_zone(layout, label="Repeat Zone") node_add_menu.add_node_type(layout, "GeometryNodeSwitch") node_add_menu.draw_assets_for_catalog(layout, self.bl_label) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 0be2c403966..03df317be88 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1306,6 +1306,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define GEO_NODE_TOOL_SET_FACE_SET 2113 #define GEO_NODE_POINTS_TO_CURVES 2114 #define GEO_NODE_INPUT_EDGE_SMOOTH 2115 +#define GEO_NODE_FOR_EACH_INPUT 2116 +#define GEO_NODE_FOR_EACH_OUTPUT 2117 /** \} */ diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index db70c332c67..f63d09e8494 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -307,6 +307,17 @@ static Vector get_implicit_origin_nodes(const bNodeTree &ntree, b } } } + if (node.type == GEO_NODE_FOR_EACH_OUTPUT) { + for (const bNode *foreach_input_node : + ntree.runtime->nodes_by_type.lookup(nodeTypeFind("GeometryNodeForEachInput"))) + { + const auto &storage = *static_cast( + foreach_input_node->storage); + if (storage.output_node_id == node.identifier) { + origin_nodes.append(foreach_input_node); + } + } + } return origin_nodes; } @@ -325,6 +336,12 @@ static Vector get_implicit_target_nodes(const bNodeTree &ntree, b target_nodes.append(repeat_output_node); } } + if (node.type == GEO_NODE_FOR_EACH_INPUT) { + const auto &storage = *static_cast(node.storage); + if (const bNode *foreach_output_node = ntree.node_by_id(storage.output_node_id)) { + target_nodes.append(foreach_output_node); + } + } return target_nodes; } diff --git a/source/blender/blenkernel/intern/node_tree_zones.cc b/source/blender/blenkernel/intern/node_tree_zones.cc index ec88a254adb..23b0208d232 100644 --- a/source/blender/blenkernel/intern/node_tree_zones.cc +++ b/source/blender/blenkernel/intern/node_tree_zones.cc @@ -36,6 +36,7 @@ static Vector> find_zone_nodes( Vector zone_output_nodes; zone_output_nodes.extend(tree.nodes_by_type("GeometryNodeSimulationOutput")); zone_output_nodes.extend(tree.nodes_by_type("GeometryNodeRepeatOutput")); + zone_output_nodes.extend(tree.nodes_by_type("GeometryNodeForEachOutput")); for (const bNode *node : zone_output_nodes) { auto zone = std::make_unique(); zone->owner = &owner; @@ -62,6 +63,16 @@ static Vector> find_zone_nodes( } } } + for (const bNode *node : tree.nodes_by_type("GeometryNodeForEachInput")) { + const auto &storage = *static_cast(node->storage); + if (const bNode *foreach_output_node = tree.node_by_id(storage.output_node_id)) { + if (bNodeTreeZone *zone = r_zone_by_inout_node.lookup_default(foreach_output_node, nullptr)) + { + zone->input_node = node; + r_zone_by_inout_node.add(node, zone); + } + } + } return zones; } @@ -257,13 +268,19 @@ static std::unique_ptr discover_tree_zones(const bNodeTree &tree depend_on_output_flags |= depend_on_output_flag_array[from_node_i]; } } - if (ELEM(node->type, GEO_NODE_SIMULATION_INPUT, GEO_NODE_REPEAT_INPUT)) { + if (ELEM( + node->type, GEO_NODE_SIMULATION_INPUT, GEO_NODE_REPEAT_INPUT, GEO_NODE_FOR_EACH_INPUT)) + { if (const bNodeTreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) { /* Now entering a zone, so set the corresponding bit. */ depend_on_input_flags[zone->index].set(); } } - else if (ELEM(node->type, GEO_NODE_SIMULATION_OUTPUT, GEO_NODE_REPEAT_OUTPUT)) { + else if (ELEM(node->type, + GEO_NODE_SIMULATION_OUTPUT, + GEO_NODE_REPEAT_OUTPUT, + GEO_NODE_FOR_EACH_OUTPUT)) + { if (const bNodeTreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) { /* The output is implicitly linked to the input, so also propagate the bits from there. */ if (const bNode *zone_input_node = zone->input_node) { diff --git a/source/blender/blenloader/intern/versioning_userdef.cc b/source/blender/blenloader/intern/versioning_userdef.cc index 8d0531bc888..4767a065df2 100644 --- a/source/blender/blenloader/intern/versioning_userdef.cc +++ b/source/blender/blenloader/intern/versioning_userdef.cc @@ -125,6 +125,7 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) /* Keep this block, even when empty. */ FROM_DEFAULT_V4_UCHAR(space_sequencer.transition); FROM_DEFAULT_V4_UCHAR(tui.wcol_list_item.inner_sel); + FROM_DEFAULT_V4_UCHAR(space_node.node_zone_foreach); } #undef FROM_DEFAULT_V4_UCHAR diff --git a/source/blender/editors/include/UI_resources.hh b/source/blender/editors/include/UI_resources.hh index 19950c546da..ba130d6d947 100644 --- a/source/blender/editors/include/UI_resources.hh +++ b/source/blender/editors/include/UI_resources.hh @@ -190,6 +190,7 @@ enum ThemeColorID { TH_NODE_ZONE_SIMULATION, TH_NODE_ZONE_REPEAT, + TH_NODE_ZONE_FOR_EACH, TH_SIMULATED_FRAMES, TH_CONSOLE_OUTPUT, diff --git a/source/blender/editors/interface/resources.cc b/source/blender/editors/interface/resources.cc index d9c4b0e25b1..2f9795cb45e 100644 --- a/source/blender/editors/interface/resources.cc +++ b/source/blender/editors/interface/resources.cc @@ -666,6 +666,9 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_NODE_ZONE_REPEAT: cp = ts->node_zone_repeat; break; + case TH_NODE_ZONE_FOR_EACH: + cp = ts->node_zone_foreach; + break; case TH_SIMULATED_FRAMES: cp = ts->simulated_frames; break; diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index cbe2c0f9e64..c2ce072e238 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2980,6 +2980,10 @@ static void node_draw_basis(const bContext &C, UI_GetThemeColor4fv(TH_NODE_ZONE_REPEAT, color_outline); color_outline[3] = 1.0f; } + else if (ELEM(node.type, GEO_NODE_FOR_EACH_INPUT, GEO_NODE_FOR_EACH_OUTPUT)) { + UI_GetThemeColor4fv(TH_NODE_ZONE_FOR_EACH, color_outline); + color_outline[3] = 1.0f; + } else { UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline); } @@ -3719,7 +3723,10 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/, if (node->type == GEO_NODE_SIMULATION_OUTPUT) { return TH_NODE_ZONE_SIMULATION; } - return TH_NODE_ZONE_REPEAT; + if (node->type == GEO_NODE_REPEAT_OUTPUT) { + return TH_NODE_ZONE_REPEAT; + } + return TH_NODE_ZONE_FOR_EACH; }; const uint pos = GPU_vertformat_attr_add( diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 661369d284d..244278c6170 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1324,6 +1324,19 @@ void remap_node_pairing(bNodeTree &dst_tree, const Map & } break; } + case GEO_NODE_FOR_EACH_INPUT: { + NodeGeometryForEachInput *data = static_cast( + dst_node->storage); + if (const bNode *output_node = dst_output_node_map.lookup_default(data->output_node_id, + nullptr)) { + data->output_node_id = output_node->identifier; + } + else { + data->output_node_id = 0; + blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node); + } + break; + } } } } diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index dbb44fc706f..1a5c20cdfc1 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -168,6 +168,19 @@ static void remap_pairing(bNodeTree &dst_tree, continue; } + data->output_node_id = identifier_map.lookup_default(data->output_node_id, 0); + if (data->output_node_id == 0) { + blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node); + } + break; + } + case GEO_NODE_FOR_EACH_INPUT: { + NodeGeometryForEachInput *data = static_cast( + dst_node->storage); + if (data->output_node_id == 0) { + continue; + } + data->output_node_id = identifier_map.lookup_default(data->output_node_id, 0); if (data->output_node_id == 0) { blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node); @@ -843,6 +856,33 @@ static bool node_group_make_test_selected(bNodeTree &ntree, } } } + for (bNode *input_node : ntree.nodes_by_type("GeometryNodeForEachInput")) { + const NodeGeometryForEachInput &input_data = *static_cast( + input_node->storage); + + if (bNode *output_node = ntree.node_by_id(input_data.output_node_id)) { + const bool input_selected = nodes_to_group.contains(input_node); + const bool output_selected = nodes_to_group.contains(output_node); + if (input_selected && !output_selected) { + BKE_reportf( + &reports, + RPT_WARNING, + "Can not add for-each input node '%s' to a group without its paired output '%s'", + input_node->name, + output_node->name); + return false; + } + if (output_selected && !input_selected) { + BKE_reportf( + &reports, + RPT_WARNING, + "Can not add for-each output node '%s' to a group without its paired input '%s'", + output_node->name, + input_node->name); + return false; + } + } + } return true; } diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 387f65e83fc..4c6b8d05af1 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -337,6 +337,17 @@ void node_select_paired(bNodeTree &node_tree) } } } + for (bNode *input_node : node_tree.nodes_by_type("GeometryNodeForEachInput")) { + const auto *storage = static_cast(input_node->storage); + if (bNode *output_node = node_tree.node_by_id(storage->output_node_id)) { + if (input_node->flag & NODE_SELECT) { + output_node->flag |= NODE_SELECT; + } + if (output_node->flag & NODE_SELECT) { + input_node->flag |= NODE_SELECT; + } + } + } } VectorSet get_selected_nodes(bNodeTree &node_tree) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 4919f607cdc..315bb0ca4c3 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1835,6 +1835,11 @@ typedef struct NodeGeometryRepeatOutput { #endif } NodeGeometryRepeatOutput; +typedef struct NodeGeometryForEachInput { + /** bNode.identifier of the corresponding output node. */ + int output_node_id; +} NodeGeometryForEachInput; + typedef struct NodeGeometryDistributePointsInVolume { /** #GeometryNodePointDistributeVolumeMode. */ uint8_t mode; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index d3064c1b736..38a7172c677 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -349,7 +349,7 @@ typedef struct ThemeSpace { unsigned char node_zone_simulation[4]; unsigned char node_zone_repeat[4]; - unsigned char _pad9[4]; + unsigned char node_zone_foreach[4]; unsigned char simulated_frames[4]; /** For sequence editor. */ diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index e4c0db78bff..c6af4b7b7ba 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -3247,6 +3247,16 @@ static PointerRNA rna_NodeGeometryRepeatInput_paired_output_get(PointerRNA *ptr) return r_ptr; } +static PointerRNA rna_NodeGeometryForEachInput_paired_output_get(PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + bNode *node = static_cast(ptr->data); + NodeGeometryForEachInput *storage = static_cast(node->storage); + bNode *output_node = ntree->node_by_id(storage->output_node_id); + PointerRNA r_ptr = RNA_pointer_create(&ntree->id, &RNA_Node, output_node); + return r_ptr; +} + static bool rna_GeometryNodeSimulationInput_pair_with_output( ID *id, bNode *node, bContext *C, ReportList *reports, bNode *output_node) { @@ -3289,6 +3299,27 @@ static bool rna_GeometryNodeRepeatInput_pair_with_output( return true; } +static bool rna_GeometryNodeForEachInput_pair_with_output( + ID *id, bNode *node, bContext *C, ReportList *reports, bNode *output_node) +{ + bNodeTree *ntree = (bNodeTree *)id; + + if (!NOD_geometry_foreach_input_pair_with_output(ntree, node, output_node)) { + BKE_reportf(reports, + RPT_ERROR, + "Failed to pair for-each input node %s with output node %s", + node->name, + output_node->name); + return false; + } + + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + + return true; +} + static NodeSimulationItem *rna_NodeGeometrySimulationOutput_items_new( ID *id, bNode *node, Main *bmain, ReportList *reports, int socket_type, const char *name) { @@ -8905,6 +8936,35 @@ static void def_geo_repeat_input(StructRNA *srna) RNA_def_function_return(func, parm); } +static void def_geo_foreach_input(StructRNA *srna) +{ + PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_struct_sdna_from(srna, "NodeGeometryForEachInput", "storage"); + + prop = RNA_def_property(srna, "paired_output", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "Node"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs( + prop, "rna_NodeGeometryForEachInput_paired_output_get", nullptr, nullptr, nullptr); + RNA_def_property_ui_text( + prop, "Paired Output", "For-each output node that this input node is paired with"); + + func = RNA_def_function( + srna, "pair_with_output", "rna_GeometryNodeForEachInput_pair_with_output"); + RNA_def_function_ui_description(func, "Pair a for-each input node with an output node."); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS | FUNC_USE_CONTEXT); + parm = RNA_def_pointer( + func, "output_node", "GeometryNode", "Output Node", "For-each output node to pair with"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + /* return value */ + parm = RNA_def_boolean( + func, "result", false, "Result", "True if pairing the node was successful"); + RNA_def_function_return(func, parm); +} + static void rna_def_simulation_state_item(BlenderRNA *brna) { PropertyRNA *prop; diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index 75e6a1e4875..b8336708b9d 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -3194,6 +3194,12 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Repeat Zone", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "foreach_zone", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, nullptr, "node_zone_foreach"); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "For-Each Zone", ""); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); } static void rna_def_userdef_theme_space_buts(BlenderRNA *brna) diff --git a/source/blender/nodes/NOD_geometry.hh b/source/blender/nodes/NOD_geometry.hh index 4541d8c7f3a..08d3fedd06d 100644 --- a/source/blender/nodes/NOD_geometry.hh +++ b/source/blender/nodes/NOD_geometry.hh @@ -28,6 +28,9 @@ bool NOD_geometry_simulation_input_pair_with_output(const bNodeTree *node_tree, bool NOD_geometry_repeat_input_pair_with_output(const bNodeTree *node_tree, bNode *repeat_input_node, const bNode *repeat_output_node); +bool NOD_geometry_foreach_input_pair_with_output(const bNodeTree *node_tree, + bNode *foreach_input_node, + const bNode *foreach_output_node); /** \} */ diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 47a83a58ca6..7312cf4d6df 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -331,6 +331,8 @@ DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, 0, "EXTRUDE_MESH", ExtrudeMesh, "Ex DefNode(GeometryNode, GEO_NODE_FILL_CURVE, 0, "FILL_CURVE", FillCurve, "Fill Curve", "Generate a mesh on the XY plane with faces on the inside of input curves") DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, 0, "FILLET_CURVE", FilletCurve, "Fillet Curve", "Round corners by generating circular arcs on each control point") DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "Reverse the order of the vertices and edges of selected faces, flipping their normal direction") +DefNode(GeometryNode, GEO_NODE_FOR_EACH_INPUT, def_geo_foreach_input, "FOREACH_INPUT", ForEachInput, "For-Each Input", "") +DefNode(GeometryNode, GEO_NODE_FOR_EACH_OUTPUT, 0, "FOREACH_OUTPUT", ForEachOutput, "For-Each Output", "") DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "Convert each input geometry into an instance, which can be much faster than the Join Geometry node when the inputs are large") DefNode(GeometryNode, GEO_NODE_IMAGE_INFO, 0, "IMAGE_INFO", ImageInfo, "Image Info", "Retrieve information about an image") DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture") diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index fb1e83f55c0..bd70ce57e95 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -76,6 +76,8 @@ set(SRC nodes/node_geo_evaluate_on_domain.cc nodes/node_geo_extrude_mesh.cc nodes/node_geo_flip_faces.cc + nodes/node_geo_foreach_input.cc + nodes/node_geo_foreach_output.cc nodes/node_geo_geometry_to_instance.cc nodes/node_geo_image.cc nodes/node_geo_image_info.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc new file mode 100644 index 00000000000..cbf47f4284c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc @@ -0,0 +1,75 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_compute_contexts.hh" +#include "BKE_scene.h" + +#include "DEG_depsgraph_query.h" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +#include "NOD_geometry.hh" +#include "NOD_socket.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_foreach_input_cc { + +NODE_STORAGE_FUNCS(NodeGeometryForEachInput); + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Amount").min(0).default_value(1); + b.add_output("Index"); +} + +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + NodeGeometryForEachInput *data = MEM_cnew(__func__); + /* Needs to be initialized for the node to work. */ + data->output_node_id = 0; + node->storage = data; +} + +static void node_register() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_INPUT, "For-Each Input", NODE_CLASS_INTERFACE); + ntype.initfunc = node_init; + ntype.declare = node_declare; + ntype.gather_link_search_ops = nullptr; + node_type_storage( + &ntype, "NodeGeometryForEachInput", node_free_standard_storage, node_copy_standard_storage); + nodeRegisterType(&ntype); +} +NOD_REGISTER_NODE(node_register) + +} // namespace blender::nodes::node_geo_foreach_input_cc + +bool NOD_geometry_foreach_input_pair_with_output(const bNodeTree *node_tree, + bNode *foreach_input_node, + const bNode *foreach_output_node) +{ + namespace file_ns = blender::nodes::node_geo_foreach_input_cc; + + BLI_assert(foreach_input_node->type == GEO_NODE_FOR_EACH_INPUT); + if (foreach_output_node->type != GEO_NODE_FOR_EACH_OUTPUT) { + return false; + } + + /* Allow only one input paired to an output. */ + for (const bNode *other_input_node : node_tree->nodes_by_type("GeometryNodeForEachInput")) { + if (other_input_node != foreach_input_node) { + const NodeGeometryForEachInput &other_storage = file_ns::node_storage(*other_input_node); + if (other_storage.output_node_id == foreach_output_node->identifier) { + return false; + } + } + } + + NodeGeometryForEachInput &storage = file_ns::node_storage(*foreach_input_node); + storage.output_node_id = foreach_output_node->identifier; + return true; +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc new file mode 100644 index 00000000000..4f0ec550e70 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc @@ -0,0 +1,36 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_compute_contexts.hh" +#include "BKE_scene.h" + +#include "DEG_depsgraph_query.h" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +#include "NOD_geometry.hh" +#include "NOD_socket.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_foreach_output_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_output("Geometry"); +} + +static void node_register() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_OUTPUT, "For-Each Output", NODE_CLASS_INTERFACE); + ntype.declare = node_declare; + ntype.gather_link_search_ops = nullptr; + nodeRegisterType(&ntype); +} +NOD_REGISTER_NODE(node_register) + +} // namespace blender::nodes::node_geo_foreach_output_cc diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 800934415fa..05090adfbbd 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1853,6 +1853,11 @@ struct GeometryNodesLazyFunctionGraphBuilder { this->build_repeat_zone_function(zone); break; } + case GEO_NODE_FOR_EACH_OUTPUT: { + /* TODO */ + BLI_assert_unreachable(); + break; + } default: { BLI_assert_unreachable(); break; @@ -3865,6 +3870,10 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr /* Simulations and repeats need input and output nodes. */ return nullptr; } + if (zone->output_node->type == GEO_NODE_FOR_EACH_OUTPUT) { + /* Not yet supported. */ + return nullptr; + } } if (const ID *id_orig = DEG_get_original_id(const_cast(&btree.id))) { if (id_orig->tag & LIB_TAG_MISSING) { -- 2.30.2 From a77eaf533b6408ff38cc0d70d3615f44eb893432 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 20 Sep 2023 14:53:15 +0200 Subject: [PATCH 02/38] fixes after merge --- .../blender/blenkernel/intern/node_runtime.cc | 17 ----------------- .../blenkernel/intern/node_tree_zones.cc | 10 ---------- source/blender/editors/space_node/node_draw.cc | 4 ---- source/blender/editors/space_node/node_edit.cc | 13 ------------- source/blender/editors/space_node/node_group.cc | 13 ------------- 5 files changed, 57 deletions(-) diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index 6f072dc0920..e4a6b9e0b07 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -291,17 +291,6 @@ static Vector get_implicit_origin_nodes(const bNodeTree &ntree, b origin_nodes.append(input_node); } } - if (node.type == GEO_NODE_FOR_EACH_OUTPUT) { - for (const bNode *foreach_input_node : - ntree.runtime->nodes_by_type.lookup(nodeTypeFind("GeometryNodeForEachInput"))) - { - const auto &storage = *static_cast( - foreach_input_node->storage); - if (storage.output_node_id == node.identifier) { - origin_nodes.append(foreach_input_node); - } - } - } return origin_nodes; } @@ -314,12 +303,6 @@ static Vector get_implicit_target_nodes(const bNodeTree &ntree, b target_nodes.append(output_node); } } - if (node.type == GEO_NODE_FOR_EACH_INPUT) { - const auto &storage = *static_cast(node.storage); - if (const bNode *foreach_output_node = ntree.node_by_id(storage.output_node_id)) { - target_nodes.append(foreach_output_node); - } - } return target_nodes; } diff --git a/source/blender/blenkernel/intern/node_tree_zones.cc b/source/blender/blenkernel/intern/node_tree_zones.cc index bb6b0224a77..8ee302e2d38 100644 --- a/source/blender/blenkernel/intern/node_tree_zones.cc +++ b/source/blender/blenkernel/intern/node_tree_zones.cc @@ -57,16 +57,6 @@ static Vector> find_zone_nodes( } } } - for (const bNode *node : tree.nodes_by_type("GeometryNodeForEachInput")) { - const auto &storage = *static_cast(node->storage); - if (const bNode *foreach_output_node = tree.node_by_id(storage.output_node_id)) { - if (bNodeTreeZone *zone = r_zone_by_inout_node.lookup_default(foreach_output_node, nullptr)) - { - zone->input_node = node; - r_zone_by_inout_node.add(node, zone); - } - } - } return zones; } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 7b3103ffdf9..9c8dba2a3c5 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2985,10 +2985,6 @@ static void node_draw_basis(const bContext &C, UI_GetThemeColor4fv(zone_type->theme_id, color_outline); color_outline[3] = 1.0f; } - else if (ELEM(node.type, GEO_NODE_FOR_EACH_INPUT, GEO_NODE_FOR_EACH_OUTPUT)) { - UI_GetThemeColor4fv(TH_NODE_ZONE_FOR_EACH, color_outline); - color_outline[3] = 1.0f; - } else { UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline); } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 08ad65e5213..3534f97d92b 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1308,19 +1308,6 @@ void remap_node_pairing(bNodeTree &dst_tree, const Map & output_node_id = 0; blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node); } - case GEO_NODE_FOR_EACH_INPUT: { - NodeGeometryForEachInput *data = static_cast( - dst_node->storage); - if (const bNode *output_node = dst_output_node_map.lookup_default(data->output_node_id, - nullptr)) { - data->output_node_id = output_node->identifier; - } - else { - data->output_node_id = 0; - blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node); - } - break; - } } } } diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index c004425e0f8..81500478fa3 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -158,19 +158,6 @@ static void remap_pairing(bNodeTree &dst_tree, if (output_node_id == 0) { blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node); } - case GEO_NODE_FOR_EACH_INPUT: { - NodeGeometryForEachInput *data = static_cast( - dst_node->storage); - if (data->output_node_id == 0) { - continue; - } - - data->output_node_id = identifier_map.lookup_default(data->output_node_id, 0); - if (data->output_node_id == 0) { - blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node); - } - break; - } } } } -- 2.30.2 From 0d6c636d1bc07f231f3c05bc56eb31aeca608ac8 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 20 Sep 2023 14:59:18 +0200 Subject: [PATCH 03/38] register foreach zone --- source/blender/nodes/intern/node_register.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source/blender/nodes/intern/node_register.cc b/source/blender/nodes/intern/node_register.cc index 406ffc34b89..1451ece077b 100644 --- a/source/blender/nodes/intern/node_register.cc +++ b/source/blender/nodes/intern/node_register.cc @@ -88,12 +88,32 @@ class RepeatZoneType : public blender::bke::bNodeZoneType { } }; +class ForEachZoneType : public blender::bke::bNodeZoneType { + public: + ForEachZoneType() + { + this->input_idname = "GeometryNodeForEachInput"; + this->output_idname = "GeometryNodeForEachOutput"; + this->input_type = GEO_NODE_FOR_EACH_INPUT; + this->output_type = GEO_NODE_FOR_EACH_OUTPUT; + this->theme_id = TH_NODE_ZONE_FOR_EACH; + } + + const int &get_corresponding_output_id(const bNode &input_bnode) const override + { + BLI_assert(input_bnode.type == this->input_type); + return static_cast(input_bnode.storage)->output_node_id; + } +}; + static void register_zone_types() { static SimulationZoneType simulation_zone_type; static RepeatZoneType repeat_zone_type; + static ForEachZoneType foreach_zone_type; blender::bke::register_node_zone_type(simulation_zone_type); blender::bke::register_node_zone_type(repeat_zone_type); + blender::bke::register_node_zone_type(foreach_zone_type); } void register_nodes() -- 2.30.2 From a72876ecc8ca2c50ad5e6245dc858a20a57ac8df Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 20 Sep 2023 18:07:25 +0200 Subject: [PATCH 04/38] fix after merge --- .../geometry/nodes/node_geo_foreach_input.cc | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc index cbf47f4284c..ec9614942d7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc @@ -47,29 +47,3 @@ static void node_register() NOD_REGISTER_NODE(node_register) } // namespace blender::nodes::node_geo_foreach_input_cc - -bool NOD_geometry_foreach_input_pair_with_output(const bNodeTree *node_tree, - bNode *foreach_input_node, - const bNode *foreach_output_node) -{ - namespace file_ns = blender::nodes::node_geo_foreach_input_cc; - - BLI_assert(foreach_input_node->type == GEO_NODE_FOR_EACH_INPUT); - if (foreach_output_node->type != GEO_NODE_FOR_EACH_OUTPUT) { - return false; - } - - /* Allow only one input paired to an output. */ - for (const bNode *other_input_node : node_tree->nodes_by_type("GeometryNodeForEachInput")) { - if (other_input_node != foreach_input_node) { - const NodeGeometryForEachInput &other_storage = file_ns::node_storage(*other_input_node); - if (other_storage.output_node_id == foreach_output_node->identifier) { - return false; - } - } - } - - NodeGeometryForEachInput &storage = file_ns::node_storage(*foreach_input_node); - storage.output_node_id = foreach_output_node->identifier; - return true; -} -- 2.30.2 From 9bf4ccf4142a6671b01e3ffe6dac0c43c33ae0bf Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 20 Sep 2023 18:11:42 +0200 Subject: [PATCH 05/38] cleanup --- .../blender/editors/space_node/node_group.cc | 27 ------------------- .../blender/editors/space_node/node_select.cc | 11 -------- 2 files changed, 38 deletions(-) diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index fef7c943c2e..81fe054a761 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -798,33 +798,6 @@ static bool node_group_make_test_selected(bNodeTree &ntree, } } } - for (bNode *input_node : ntree.nodes_by_type("GeometryNodeForEachInput")) { - const NodeGeometryForEachInput &input_data = *static_cast( - input_node->storage); - - if (bNode *output_node = ntree.node_by_id(input_data.output_node_id)) { - const bool input_selected = nodes_to_group.contains(input_node); - const bool output_selected = nodes_to_group.contains(output_node); - if (input_selected && !output_selected) { - BKE_reportf( - &reports, - RPT_WARNING, - "Can not add for-each input node '%s' to a group without its paired output '%s'", - input_node->name, - output_node->name); - return false; - } - if (output_selected && !input_selected) { - BKE_reportf( - &reports, - RPT_WARNING, - "Can not add for-each output node '%s' to a group without its paired input '%s'", - output_node->name, - input_node->name); - return false; - } - } - } return true; } diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index dd799fedd7e..91afeab3540 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -327,17 +327,6 @@ void node_select_paired(bNodeTree &node_tree) } } } - for (bNode *input_node : node_tree.nodes_by_type("GeometryNodeForEachInput")) { - const auto *storage = static_cast(input_node->storage); - if (bNode *output_node = node_tree.node_by_id(storage->output_node_id)) { - if (input_node->flag & NODE_SELECT) { - output_node->flag |= NODE_SELECT; - } - if (output_node->flag & NODE_SELECT) { - input_node->flag |= NODE_SELECT; - } - } - } } VectorSet get_selected_nodes(bNodeTree &node_tree) -- 2.30.2 From b740914b72121d9afc29bd0dc27f3b3169ce707f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 20 Sep 2023 18:26:23 +0200 Subject: [PATCH 06/38] cleanup --- .../blender/makesrna/intern/rna_nodetree.cc | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index a31aff20265..c1ac484572f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -8872,29 +8872,9 @@ static void def_geo_repeat_input(StructRNA *srna) static void def_geo_foreach_input(StructRNA *srna) { - PropertyRNA *prop; - FunctionRNA *func; - PropertyRNA *parm; - RNA_def_struct_sdna_from(srna, "NodeGeometryForEachInput", "storage"); - prop = RNA_def_property(srna, "paired_output", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "Node"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_pointer_funcs(prop, "rna_Node_paired_output_get", nullptr, nullptr, nullptr); - RNA_def_property_ui_text( - prop, "Paired Output", "For-each output node that this input node is paired with"); - - func = RNA_def_function(srna, "pair_with_output", "rna_Node_pair_with_output"); - RNA_def_function_ui_description(func, "Pair a for-each input node with an output node."); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS | FUNC_USE_CONTEXT); - parm = RNA_def_pointer( - func, "output_node", "GeometryNode", "Output Node", "For-each output node to pair with"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - /* return value */ - parm = RNA_def_boolean( - func, "result", false, "Result", "True if pairing the node was successful"); - RNA_def_function_return(func, parm); + def_common_zone_input(srna); } static void rna_def_simulation_state_item(BlenderRNA *brna) -- 2.30.2 From 28158631ddb8f75a1e96eebb159b6fe5b6874f57 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 30 Sep 2023 20:36:04 +0200 Subject: [PATCH 07/38] fix after merge --- source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc index ec9614942d7..6f2bf6acfe6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc @@ -5,7 +5,7 @@ #include "BKE_compute_contexts.hh" #include "BKE_scene.h" -#include "DEG_depsgraph_query.h" +#include "DEG_depsgraph_query.hh" #include "UI_interface.hh" #include "UI_resources.hh" diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc index 4f0ec550e70..7c4f24501e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc @@ -5,7 +5,7 @@ #include "BKE_compute_contexts.hh" #include "BKE_scene.h" -#include "DEG_depsgraph_query.h" +#include "DEG_depsgraph_query.hh" #include "UI_interface.hh" #include "UI_resources.hh" -- 2.30.2 From 15111f80d0cd1bccb46ed3225bacb197f698cc3d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 30 Sep 2023 21:33:09 +0200 Subject: [PATCH 08/38] generalize zone code --- .../intern/geometry_nodes_lazy_function.cc | 197 ++++++++++++------ 1 file changed, 137 insertions(+), 60 deletions(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 03bda8932b2..d7511a4363b 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1547,6 +1547,67 @@ class ParamsForRepeatZoneGraph : public lf::Params { } }; +static void build_interface_for_zone_function(const bNodeTreeZone &zone, + const ZoneBodyFunction &body_fn, + ZoneBuildInfo &r_zone_info, + Vector &r_inputs, + Vector &r_outputs) +{ + for (const bNodeSocket *socket : zone.input_node->input_sockets()) { + if (socket->typeinfo->geometry_nodes_cpp_type == nullptr) { + /* Skip "empty" sockets. */ + continue; + } + r_inputs.append_as( + socket->name, *socket->typeinfo->geometry_nodes_cpp_type, lf::ValueUsage::Maybe); + } + r_zone_info.indices.inputs.main = r_inputs.index_range(); + + for (const bNodeLink *link : zone.border_links) { + r_inputs.append_as(link->fromsock->name, + *link->tosock->typeinfo->geometry_nodes_cpp_type, + lf::ValueUsage::Maybe); + } + r_zone_info.indices.inputs.border_links = r_inputs.index_range().take_back( + zone.border_links.size()); + + for (const bNodeSocket *socket : zone.output_node->output_sockets()) { + if (socket->typeinfo->geometry_nodes_cpp_type == nullptr) { + continue; + } + r_inputs.append_as("Usage", CPPType::get(), lf::ValueUsage::Maybe); + r_outputs.append_as(socket->name, *socket->typeinfo->geometry_nodes_cpp_type); + } + r_zone_info.indices.outputs.main = r_outputs.index_range(); + r_zone_info.indices.inputs.output_usages = r_inputs.index_range().take_back( + r_zone_info.indices.outputs.main.size()); + + for ([[maybe_unused]] const bNodeSocket *socket : zone.input_node->input_sockets()) { + if (socket->typeinfo->geometry_nodes_cpp_type == nullptr) { + continue; + } + r_outputs.append_as("Usage", CPPType::get()); + } + r_zone_info.indices.outputs.input_usages = r_outputs.index_range().take_back( + r_zone_info.indices.inputs.main.size()); + for ([[maybe_unused]] const bNodeLink *link : zone.border_links) { + r_outputs.append_as("Border Link Usage", CPPType::get()); + } + r_zone_info.indices.outputs.border_link_usages = r_outputs.index_range().take_back( + zone.border_links.size()); + + for (const auto item : body_fn.indices.inputs.attributes_by_field_source_index.items()) { + const int index = r_inputs.append_and_get_index_as( + "Attribute Set", CPPType::get(), lf::ValueUsage::Maybe); + r_zone_info.indices.inputs.attributes_by_field_source_index.add_new(item.key, index); + } + for (const auto item : body_fn.indices.inputs.attributes_by_caller_propagation_index.items()) { + const int index = r_inputs.append_and_get_index_as( + "Attribute Set", CPPType::get(), lf::ValueUsage::Maybe); + r_zone_info.indices.inputs.attributes_by_caller_propagation_index.add_new(item.key, index); + } +} + class LazyFunctionForRepeatZone : public LazyFunction { private: const bNodeTreeZone &zone_; @@ -1564,53 +1625,10 @@ class LazyFunctionForRepeatZone : public LazyFunction { body_fn_(body_fn) { debug_name_ = "Repeat Zone"; + build_interface_for_zone_function(zone, body_fn, zone_info, inputs_, outputs_); - inputs_.append_as("Iterations", CPPType::get>(), lf::ValueUsage::Used); - for (const bNodeSocket *socket : zone.input_node->input_sockets().drop_front(1).drop_back(1)) { - inputs_.append_as( - socket->name, *socket->typeinfo->geometry_nodes_cpp_type, lf::ValueUsage::Maybe); - } - zone_info.indices.inputs.main = inputs_.index_range(); - - for (const bNodeLink *link : zone.border_links) { - inputs_.append_as(link->fromsock->name, - *link->tosock->typeinfo->geometry_nodes_cpp_type, - lf::ValueUsage::Maybe); - } - zone_info.indices.inputs.border_links = inputs_.index_range().take_back( - zone.border_links.size()); - - for (const bNodeSocket *socket : zone.output_node->output_sockets().drop_back(1)) { - inputs_.append_as("Usage", CPPType::get(), lf::ValueUsage::Maybe); - outputs_.append_as(socket->name, *socket->typeinfo->geometry_nodes_cpp_type); - } - zone_info.indices.inputs.output_usages = inputs_.index_range().take_back( - zone.output_node->output_sockets().drop_back(1).size()); - zone_info.indices.outputs.main = outputs_.index_range(); - - for ([[maybe_unused]] const bNodeSocket *socket : - zone.input_node->input_sockets().drop_back(1)) { - outputs_.append_as("Usage", CPPType::get()); - } - zone_info.indices.outputs.input_usages = outputs_.index_range().take_back( - zone.input_node->input_sockets().drop_back(1).size()); - for ([[maybe_unused]] const bNodeLink *link : zone.border_links) { - outputs_.append_as("Border Link Usage", CPPType::get()); - } - zone_info.indices.outputs.border_link_usages = outputs_.index_range().take_back( - zone.border_links.size()); - - for (const auto item : body_fn_.indices.inputs.attributes_by_field_source_index.items()) { - const int index = inputs_.append_and_get_index_as( - "Attribute Set", CPPType::get(), lf::ValueUsage::Maybe); - zone_info.indices.inputs.attributes_by_field_source_index.add_new(item.key, index); - } - for (const auto item : body_fn_.indices.inputs.attributes_by_caller_propagation_index.items()) - { - const int index = inputs_.append_and_get_index_as( - "Attribute Set", CPPType::get(), lf::ValueUsage::Maybe); - zone_info.indices.inputs.attributes_by_caller_propagation_index.add_new(item.key, index); - } + /* Iterations input is always used. */ + inputs_[zone_info.indices.inputs.main[0]].usage = lf::ValueUsage::Used; } void *init_storage(LinearAllocator<> &allocator) const override @@ -1865,6 +1883,48 @@ class LazyFunctionForRepeatZone : public LazyFunction { } }; +class LazyFunctionForForeachZone : public LazyFunction { + private: + const bNodeTreeZone &zone_; + const ZoneBuildInfo &zone_info_; + const ZoneBodyFunction &body_fn_; + + public: + LazyFunctionForForeachZone(const bNodeTreeZone &zone, + ZoneBuildInfo &zone_info, + const ZoneBodyFunction &body_fn) + : zone_(zone), zone_info_(zone_info), body_fn_(body_fn) + { + debug_name_ = "For-Each Zone"; + build_interface_for_zone_function(zone, body_fn, zone_info, inputs_, outputs_); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + params.set_default_remaining_outputs(); + } + + std::string input_name(const int i) const override + { + if (zone_info_.indices.inputs.output_usages.contains(i)) { + const bNodeSocket &bsocket = zone_.output_node->output_socket( + i - zone_info_.indices.inputs.output_usages.first()); + return "Usage: " + StringRef(bsocket.name); + } + return inputs_[i].debug_name; + } + + std::string output_name(const int i) const override + { + if (zone_info_.indices.outputs.input_usages.contains(i)) { + const bNodeSocket &bsocket = zone_.input_node->input_socket( + i - zone_info_.indices.outputs.input_usages.first()); + return "Usage: " + StringRef(bsocket.name); + } + return outputs_[i].debug_name; + } +}; + /** * Logs intermediate values from the lazy-function graph evaluation into #GeoModifierLog based on * the mapping between the lazy-function graph and the corresponding #bNodeTree. @@ -2123,8 +2183,7 @@ struct GeometryNodesLazyFunctionBuilder { break; } case GEO_NODE_FOR_EACH_OUTPUT: { - /* TODO */ - BLI_assert_unreachable(); + this->build_foreach_zone_function(zone); break; } default: { @@ -2321,6 +2380,14 @@ struct GeometryNodesLazyFunctionBuilder { zone_info.lazy_function = &zone_fn; } + void build_foreach_zone_function(const bNodeTreeZone &zone) + { + ZoneBuildInfo &zone_info = zone_build_infos_[zone.index]; + ZoneBodyFunction &body_fn = this->build_zone_body_function(zone); + auto &zone_fn = scope_.construct(zone, zone_info, body_fn); + zone_info.lazy_function = &zone_fn; + } + /** * Build a lazy-function for the "body" of a zone, i.e. for all the nodes within the zone. */ @@ -2337,7 +2404,11 @@ struct GeometryNodesLazyFunctionBuilder { Vector lf_main_inputs; Vector lf_main_input_usages; - for (const bNodeSocket *bsocket : zone.input_node->output_sockets().drop_back(1)) { + for (const bNodeSocket *bsocket : zone.input_node->output_sockets()) { + if (bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) { + /* Skip empty sockets. */ + continue; + } lf::GraphInputSocket &lf_input = lf_body_graph.add_input( *bsocket->typeinfo->geometry_nodes_cpp_type, bsocket->identifier); lf::GraphOutputSocket &lf_input_usage = lf_body_graph.add_output( @@ -2357,7 +2428,11 @@ struct GeometryNodesLazyFunctionBuilder { Vector lf_main_outputs; Vector lf_main_output_usages; - for (const bNodeSocket *bsocket : zone.output_node->input_sockets().drop_back(1)) { + for (const bNodeSocket *bsocket : zone.output_node->input_sockets()) { + if (bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) { + /* Skip empty sockets. */ + continue; + } lf::GraphOutputSocket &lf_output = lf_body_graph.add_output( *bsocket->typeinfo->geometry_nodes_cpp_type, bsocket->identifier); lf::GraphInputSocket &lf_output_usage = lf_body_graph.add_input( @@ -2390,10 +2465,14 @@ struct GeometryNodesLazyFunctionBuilder { this->insert_nodes_and_zones(zone.child_nodes, zone.child_zones, graph_params); this->build_output_socket_usages(*zone.input_node, graph_params); - for (const int i : zone.input_node->output_sockets().drop_back(1).index_range()) { - const bNodeSocket &bsocket = zone.input_node->output_socket(i); - lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(&bsocket, nullptr); - lf::GraphOutputSocket &lf_usage_output = *lf_main_input_usages[i]; + int lf_input_index = 0; + for (const bNodeSocket *bsocket : zone.input_node->output_sockets()) { + if (bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) { + /* Skip empty socket. */ + continue; + } + lf::OutputSocket *lf_usage = graph_params.usage_by_bsocket.lookup_default(bsocket, nullptr); + lf::GraphOutputSocket &lf_usage_output = *lf_main_input_usages[lf_input_index]; if (lf_usage) { lf_body_graph.add_link(*lf_usage, lf_usage_output); } @@ -2401,6 +2480,7 @@ struct GeometryNodesLazyFunctionBuilder { static const bool static_false = false; lf_usage_output.set_default_value(&static_false); } + lf_input_index++; } for (const auto item : graph_params.lf_output_by_bsocket.items()) { @@ -2447,7 +2527,7 @@ struct GeometryNodesLazyFunctionBuilder { body_fn.function = &scope_.construct( lf_body_graph, lf_body_inputs, lf_body_outputs, &logger, &side_effect_provider, nullptr); - // std::cout << "\n\n" << lf_body_graph.to_dot() << "\n\n"; + std::cout << "\n\n" << lf_body_graph.to_dot() << "\n\n"; return body_fn; } @@ -2605,7 +2685,7 @@ struct GeometryNodesLazyFunctionBuilder { this->fix_link_cycles(lf_graph, graph_params.socket_usage_inputs); - // std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; + std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; lf_graph.update_node_indices(); lf_graph_info_->num_inline_nodes_approximate += lf_graph.nodes().size(); @@ -2951,6 +3031,7 @@ struct GeometryNodesLazyFunctionBuilder { *child_zone_info.lazy_function); mapping_->zone_node_map.add_new(&child_zone, &child_zone_node); + /* TODO: Handle "empty" sockets that are not at the end. */ for (const int i : child_zone_info.indices.inputs.main.index_range()) { const bNodeSocket &bsocket = child_zone.input_node->input_socket(i); lf::InputSocket &lf_input_socket = child_zone_node.input( @@ -4103,10 +4184,6 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr /* Simulations and repeats need input and output nodes. */ return nullptr; } - if (zone->output_node->type == GEO_NODE_FOR_EACH_OUTPUT) { - /* Not yet supported. */ - return nullptr; - } } if (const ID *id_orig = DEG_get_original_id(const_cast(&btree.id))) { if (id_orig->tag & LIB_TAG_MISSING) { -- 2.30.2 From 1216f4b42e3b23023d021ba6bc27827f8734817d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 30 Sep 2023 21:57:28 +0200 Subject: [PATCH 09/38] initial working evaluation --- source/blender/geometry/CMakeLists.txt | 2 + .../blender/geometry/GEO_join_geometries.hh | 15 ++ .../geometry/intern/join_geometries.cc | 203 ++++++++++++++++++ .../nodes/geometry/node_geometry_util.hh | 1 - .../geometry/nodes/node_geo_join_geometry.cc | 182 +--------------- .../intern/geometry_nodes_lazy_function.cc | 17 ++ 6 files changed, 239 insertions(+), 181 deletions(-) create mode 100644 source/blender/geometry/GEO_join_geometries.hh create mode 100644 source/blender/geometry/intern/join_geometries.cc diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index eda7767b8ab..4ccc6c0ce16 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRC intern/add_curves_on_mesh.cc intern/curve_constraints.cc intern/fillet_curves.cc + intern/join_geometries.cc intern/mesh_copy_selection.cc intern/mesh_merge_by_distance.cc intern/mesh_primitive_cuboid.cc @@ -43,6 +44,7 @@ set(SRC GEO_add_curves_on_mesh.hh GEO_curve_constraints.hh GEO_fillet_curves.hh + GEO_join_geometries.hh GEO_mesh_copy_selection.hh GEO_mesh_merge_by_distance.hh GEO_mesh_primitive_cuboid.hh diff --git a/source/blender/geometry/GEO_join_geometries.hh b/source/blender/geometry/GEO_join_geometries.hh new file mode 100644 index 00000000000..73991740489 --- /dev/null +++ b/source/blender/geometry/GEO_join_geometries.hh @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_anonymous_attribute_id.hh" +#include "BKE_geometry_set.hh" + +namespace blender::geometry { + +bke::GeometrySet join_geometries(Span geometries, + const bke::AnonymousAttributePropagationInfo &propagation_info); + +} diff --git a/source/blender/geometry/intern/join_geometries.cc b/source/blender/geometry/intern/join_geometries.cc new file mode 100644 index 00000000000..5c7565f2003 --- /dev/null +++ b/source/blender/geometry/intern/join_geometries.cc @@ -0,0 +1,203 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "GEO_join_geometries.hh" +#include "GEO_realize_instances.hh" + +#include "BKE_instances.hh" + +namespace blender::geometry { + +using bke::AttributeIDRef; +using bke::AttributeMetaData; +using bke::GeometryComponent; +using bke::GeometrySet; + +template +static Array to_base_components(Span components) +{ + return components; +} + +static Map get_final_attribute_info( + Span components, Span ignored_attributes) +{ + Map info; + + for (const GeometryComponent *component : components) { + component->attributes()->for_all( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (ignored_attributes.contains(attribute_id.name())) { + return true; + } + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } + info.add_or_modify( + attribute_id, + [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, + [&](AttributeMetaData *meta_data_final) { + meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity( + {meta_data_final->data_type, meta_data.data_type}); + meta_data_final->domain = blender::bke::attribute_domain_highest_priority( + {meta_data_final->domain, meta_data.domain}); + }); + return true; + }); + } + + return info; +} + +static void fill_new_attribute(Span src_components, + const AttributeIDRef &attribute_id, + const eCustomDataType data_type, + const eAttrDomain domain, + GMutableSpan dst_span) +{ + const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type); + BLI_assert(cpp_type != nullptr); + + int offset = 0; + for (const GeometryComponent *component : src_components) { + const int domain_num = component->attribute_domain_size(domain); + if (domain_num == 0) { + continue; + } + GVArray read_attribute = *component->attributes()->lookup_or_default( + attribute_id, domain, data_type, nullptr); + + GVArraySpan src_span{read_attribute}; + const void *src_buffer = src_span.data(); + void *dst_buffer = dst_span[offset]; + cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num); + + offset += domain_num; + } +} + +static void join_attributes(Span src_components, + GeometryComponent &result, + Span ignored_attributes = {}) +{ + const Map info = get_final_attribute_info(src_components, + ignored_attributes); + + for (const MapItem item : info.items()) { + const AttributeIDRef attribute_id = item.key; + const AttributeMetaData &meta_data = item.value; + + bke::GSpanAttributeWriter write_attribute = + result.attributes_for_write()->lookup_or_add_for_write_only_span( + attribute_id, meta_data.domain, meta_data.data_type); + if (!write_attribute) { + continue; + } + fill_new_attribute( + src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span); + write_attribute.finish(); + } +} + +static void join_components(Span src_components, + GeometrySet &result) +{ + std::unique_ptr dst_instances = std::make_unique(); + + int tot_instances = 0; + for (const bke::InstancesComponent *src_component : src_components) { + tot_instances += src_component->get()->instances_num(); + } + dst_instances->reserve(tot_instances); + + for (const bke::InstancesComponent *src_component : src_components) { + const bke::Instances &src_instances = *src_component->get(); + + Span src_references = src_instances.references(); + Array handle_map(src_references.size()); + for (const int src_handle : src_references.index_range()) { + handle_map[src_handle] = dst_instances->add_reference(src_references[src_handle]); + } + + Span src_transforms = src_instances.transforms(); + Span src_reference_handles = src_instances.reference_handles(); + + for (const int i : src_transforms.index_range()) { + const int src_handle = src_reference_handles[i]; + const int dst_handle = handle_map[src_handle]; + const float4x4 &transform = src_transforms[i]; + dst_instances->add_instance(dst_handle, transform); + } + } + + result.replace_instances(dst_instances.release()); + bke::InstancesComponent &dst_component = + result.get_component_for_write(); + join_attributes(to_base_components(src_components), dst_component, {"position"}); +} + +static void join_components(Span /*src_components*/, + GeometrySet & /*result*/) +{ + /* Not yet supported. Joining volume grids with the same name requires resampling of at least one + * of the grids. The cell size of the resulting volume has to be determined somehow. */ +} + +template +static void join_component_type(Span src_geometry_sets, + GeometrySet &result, + const bke::AnonymousAttributePropagationInfo &propagation_info) +{ + Vector components; + for (const GeometrySet &geometry_set : src_geometry_sets) { + const Component *component = geometry_set.get_component(); + if (component != nullptr && !component->is_empty()) { + components.append(component); + } + } + + if (components.size() == 0) { + return; + } + if (components.size() == 1) { + result.add(*components[0]); + return; + } + + if constexpr (is_same_any_v) { + join_components(components, result); + } + else { + std::unique_ptr instances = std::make_unique(); + for (const Component *component : components) { + GeometrySet tmp_geo; + tmp_geo.add(*component); + const int handle = instances->add_reference(bke::InstanceReference{tmp_geo}); + instances->add_instance(handle, float4x4::identity()); + } + + RealizeInstancesOptions options; + options.keep_original_ids = true; + options.realize_instance_attributes = false; + options.propagation_info = propagation_info; + GeometrySet joined_components = geometry::realize_instances( + GeometrySet::from_instances(instances.release()), options); + result.add(joined_components.get_component_for_write()); + } +} + +GeometrySet join_geometries(Span geometries, + const bke::AnonymousAttributePropagationInfo &propagation_info) +{ + GeometrySet result; + join_component_type(geometries, result, propagation_info); + join_component_type(geometries, result, propagation_info); + join_component_type(geometries, result, propagation_info); + join_component_type(geometries, result, propagation_info); + join_component_type(geometries, result, propagation_info); + join_component_type(geometries, result, propagation_info); + return result; +} + +} // namespace blender::geometry diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 3269cf71647..488e1bf0108 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -20,7 +20,6 @@ #endif struct BVHTreeFromMesh; -struct GeometrySet; namespace blender::nodes { class GatherAddNodeSearchParams; class GatherLinkSearchOpParams; diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 8706ec40977..5a404647a7f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -2,7 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#include "GEO_realize_instances.hh" +#include "GEO_join_geometries.hh" #include "BKE_instances.hh" @@ -16,177 +16,6 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output("Geometry").propagate_all(); } -template -static Array to_base_components(Span components) -{ - return components; -} - -static Map get_final_attribute_info( - Span components, Span ignored_attributes) -{ - Map info; - - for (const GeometryComponent *component : components) { - component->attributes()->for_all( - [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (ignored_attributes.contains(attribute_id.name())) { - return true; - } - if (meta_data.data_type == CD_PROP_STRING) { - return true; - } - info.add_or_modify( - attribute_id, - [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, - [&](AttributeMetaData *meta_data_final) { - meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity( - {meta_data_final->data_type, meta_data.data_type}); - meta_data_final->domain = blender::bke::attribute_domain_highest_priority( - {meta_data_final->domain, meta_data.domain}); - }); - return true; - }); - } - - return info; -} - -static void fill_new_attribute(Span src_components, - const AttributeIDRef &attribute_id, - const eCustomDataType data_type, - const eAttrDomain domain, - GMutableSpan dst_span) -{ - const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - - int offset = 0; - for (const GeometryComponent *component : src_components) { - const int domain_num = component->attribute_domain_size(domain); - if (domain_num == 0) { - continue; - } - GVArray read_attribute = *component->attributes()->lookup_or_default( - attribute_id, domain, data_type, nullptr); - - GVArraySpan src_span{read_attribute}; - const void *src_buffer = src_span.data(); - void *dst_buffer = dst_span[offset]; - cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num); - - offset += domain_num; - } -} - -static void join_attributes(Span src_components, - GeometryComponent &result, - Span ignored_attributes = {}) -{ - const Map info = get_final_attribute_info(src_components, - ignored_attributes); - - for (const MapItem item : info.items()) { - const AttributeIDRef attribute_id = item.key; - const AttributeMetaData &meta_data = item.value; - - GSpanAttributeWriter write_attribute = - result.attributes_for_write()->lookup_or_add_for_write_only_span( - attribute_id, meta_data.domain, meta_data.data_type); - if (!write_attribute) { - continue; - } - fill_new_attribute( - src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span); - write_attribute.finish(); - } -} - -static void join_components(Span src_components, GeometrySet &result) -{ - std::unique_ptr dst_instances = std::make_unique(); - - int tot_instances = 0; - for (const InstancesComponent *src_component : src_components) { - tot_instances += src_component->get()->instances_num(); - } - dst_instances->reserve(tot_instances); - - for (const InstancesComponent *src_component : src_components) { - const bke::Instances &src_instances = *src_component->get(); - - Span src_references = src_instances.references(); - Array handle_map(src_references.size()); - for (const int src_handle : src_references.index_range()) { - handle_map[src_handle] = dst_instances->add_reference(src_references[src_handle]); - } - - Span src_transforms = src_instances.transforms(); - Span src_reference_handles = src_instances.reference_handles(); - - for (const int i : src_transforms.index_range()) { - const int src_handle = src_reference_handles[i]; - const int dst_handle = handle_map[src_handle]; - const float4x4 &transform = src_transforms[i]; - dst_instances->add_instance(dst_handle, transform); - } - } - - result.replace_instances(dst_instances.release()); - InstancesComponent &dst_component = result.get_component_for_write(); - join_attributes(to_base_components(src_components), dst_component, {"position"}); -} - -static void join_components(Span /*src_components*/, - GeometrySet & /*result*/) -{ - /* Not yet supported. Joining volume grids with the same name requires resampling of at least one - * of the grids. The cell size of the resulting volume has to be determined somehow. */ -} - -template -static void join_component_type(Span src_geometry_sets, - GeometrySet &result, - const AnonymousAttributePropagationInfo &propagation_info) -{ - Vector components; - for (const GeometrySet &geometry_set : src_geometry_sets) { - const Component *component = geometry_set.get_component(); - if (component != nullptr && !component->is_empty()) { - components.append(component); - } - } - - if (components.size() == 0) { - return; - } - if (components.size() == 1) { - result.add(*components[0]); - return; - } - - if constexpr (is_same_any_v) { - join_components(components, result); - } - else { - std::unique_ptr instances = std::make_unique(); - for (const Component *component : components) { - GeometrySet tmp_geo; - tmp_geo.add(*component); - const int handle = instances->add_reference(bke::InstanceReference{tmp_geo}); - instances->add_instance(handle, float4x4::identity()); - } - - geometry::RealizeInstancesOptions options; - options.keep_original_ids = true; - options.realize_instance_attributes = false; - options.propagation_info = propagation_info; - GeometrySet joined_components = geometry::realize_instances( - GeometrySet::from_instances(instances.release()), options); - result.add(joined_components.get_component_for_write()); - } -} - static void node_geo_exec(GeoNodeExecParams params) { Vector geometry_sets = params.extract_input>("Geometry"); @@ -198,14 +27,7 @@ static void node_geo_exec(GeoNodeExecParams params) GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry); } - GeometrySet geometry_set_result; - join_component_type(geometry_sets, geometry_set_result, propagation_info); - join_component_type(geometry_sets, geometry_set_result, propagation_info); - join_component_type(geometry_sets, geometry_set_result, propagation_info); - join_component_type(geometry_sets, geometry_set_result, propagation_info); - join_component_type(geometry_sets, geometry_set_result, propagation_info); - join_component_type( - geometry_sets, geometry_set_result, propagation_info); + GeometrySet geometry_set_result = geometry::join_geometries(geometry_sets, propagation_info); params.set_output("Geometry", std::move(geometry_set_result)); } diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index d7511a4363b..fcdbf1c133a 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -36,6 +36,8 @@ #include "DNA_ID.h" +#include "GEO_join_geometries.hh" + #include "BKE_compute_contexts.hh" #include "BKE_geometry_set.hh" #include "BKE_node_tree_anonymous_attributes.hh" @@ -1897,10 +1899,25 @@ class LazyFunctionForForeachZone : public LazyFunction { { debug_name_ = "For-Each Zone"; build_interface_for_zone_function(zone, body_fn, zone_info, inputs_, outputs_); + /* The Amount input is always used. */ + inputs_[zone_info.indices.inputs.main[0]].usage = lf::ValueUsage::Used; } void execute_impl(lf::Params ¶ms, const lf::Context &context) const override { + const int amount = params.get_input(zone_info_.indices.inputs.main[0]); + Array output_values(amount, NoInitialization{}); + for (const int i : IndexRange(amount)) { + GeometrySet &output_geometry = output_values[i]; + bool usage_index; + lf::execute_lazy_function_eagerly(*body_fn_.function, + context.user_data, + context.local_user_data, + std::make_tuple(ValueOrField(i), true), + std::make_tuple(&usage_index, &output_geometry)); + } + GeometrySet reduced_geometry = geometry::join_geometries(output_values, {}); + params.set_output(zone_info_.indices.outputs.main[0], std::move(reduced_geometry)); params.set_default_remaining_outputs(); } -- 2.30.2 From 336d983b261d18bf8b791225b63f070c0f723675 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 30 Sep 2023 21:58:28 +0200 Subject: [PATCH 10/38] disable debug prints --- source/blender/nodes/intern/geometry_nodes_lazy_function.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index fcdbf1c133a..6c120ec8aa8 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -2544,7 +2544,7 @@ struct GeometryNodesLazyFunctionBuilder { body_fn.function = &scope_.construct( lf_body_graph, lf_body_inputs, lf_body_outputs, &logger, &side_effect_provider, nullptr); - std::cout << "\n\n" << lf_body_graph.to_dot() << "\n\n"; + // std::cout << "\n\n" << lf_body_graph.to_dot() << "\n\n"; return body_fn; } @@ -2702,7 +2702,7 @@ struct GeometryNodesLazyFunctionBuilder { this->fix_link_cycles(lf_graph, graph_params.socket_usage_inputs); - std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; + // std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; lf_graph.update_node_indices(); lf_graph_info_->num_inline_nodes_approximate += lf_graph.nodes().size(); -- 2.30.2 From 61d2f29923dc96af7bad83eaa62bba27f569b074 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 30 Sep 2023 22:10:26 +0200 Subject: [PATCH 11/38] fix thread safety --- .../intern/geometry_nodes_lazy_function.cc | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 6c120ec8aa8..a9b136ae636 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1905,17 +1905,24 @@ class LazyFunctionForForeachZone : public LazyFunction { void execute_impl(lf::Params ¶ms, const lf::Context &context) const override { + auto &user_data = *static_cast(context.user_data); + // auto &local_user_data = *static_cast(context.local_user_data); + const int amount = params.get_input(zone_info_.indices.inputs.main[0]); Array output_values(amount, NoInitialization{}); - for (const int i : IndexRange(amount)) { - GeometrySet &output_geometry = output_values[i]; - bool usage_index; - lf::execute_lazy_function_eagerly(*body_fn_.function, - context.user_data, - context.local_user_data, - std::make_tuple(ValueOrField(i), true), - std::make_tuple(&usage_index, &output_geometry)); - } + threading::parallel_for(IndexRange(amount), 32, [&](const IndexRange range) { + for (const int i : range) { + GeometrySet &output_geometry = output_values[i]; + bool usage_index; + + GeoNodesLFLocalUserData body_local_user_data{user_data}; + lf::execute_lazy_function_eagerly(*body_fn_.function, + context.user_data, + &body_local_user_data, + std::make_tuple(ValueOrField(i), true), + std::make_tuple(&usage_index, &output_geometry)); + } + }); GeometrySet reduced_geometry = geometry::join_geometries(output_values, {}); params.set_output(zone_info_.indices.outputs.main[0], std::move(reduced_geometry)); params.set_default_remaining_outputs(); -- 2.30.2 From a6192380e496d999a764c78259e2bbe018c5fdfe Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 30 Sep 2023 22:55:46 +0200 Subject: [PATCH 12/38] add compute context and viewer path --- scripts/startup/bl_ui/space_spreadsheet.py | 2 + .../blenkernel/BKE_compute_contexts.hh | 25 +++++++++++++ source/blender/blenkernel/BKE_viewer_path.h | 2 + .../blenkernel/intern/compute_contexts.cc | 29 +++++++++++++++ .../blender/blenkernel/intern/viewer_path.cc | 37 +++++++++++++++++-- source/blender/editors/util/ed_viewer_path.cc | 22 ++++++++++- .../blender/makesdna/DNA_viewer_path_types.h | 8 ++++ source/blender/makesrna/intern/rna_space.cc | 15 ++++++++ source/blender/modifiers/intern/MOD_nodes.cc | 23 ++++++++++++ .../nodes/NOD_geometry_nodes_lazy_function.hh | 1 + .../intern/geometry_nodes_lazy_function.cc | 13 ++++++- .../nodes/intern/geometry_nodes_log.cc | 6 +++ 12 files changed, 177 insertions(+), 6 deletions(-) diff --git a/scripts/startup/bl_ui/space_spreadsheet.py b/scripts/startup/bl_ui/space_spreadsheet.py index f027e22e053..dc2a1cc52ae 100644 --- a/scripts/startup/bl_ui/space_spreadsheet.py +++ b/scripts/startup/bl_ui/space_spreadsheet.py @@ -93,6 +93,8 @@ class SPREADSHEET_HT_header(bpy.types.Header): layout.label(text="Simulation Zone") elif ctx.type == 'REPEAT_ZONE': layout.label(text="Repeat Zone") + elif ctx.type == 'FOREACH_ZONE': + layout.label(text='For-Each Zone') elif ctx.type == 'VIEWER_NODE': layout.label(text=ctx.ui_name) diff --git a/source/blender/blenkernel/BKE_compute_contexts.hh b/source/blender/blenkernel/BKE_compute_contexts.hh index a8c2db2ab06..4baa524432c 100644 --- a/source/blender/blenkernel/BKE_compute_contexts.hh +++ b/source/blender/blenkernel/BKE_compute_contexts.hh @@ -103,4 +103,29 @@ class RepeatZoneComputeContext : public ComputeContext { void print_current_in_line(std::ostream &stream) const override; }; +class ForEachZoneComputeContext : public ComputeContext { + private: + static constexpr const char *s_static_type = "FOREACH_ZONE"; + + int32_t output_node_id_; + int index_; + + public: + ForEachZoneComputeContext(const ComputeContext *parent, int32_t output_node_id, int index); + ForEachZoneComputeContext(const ComputeContext *parent, const bNode &node, int index); + + int32_t output_node_id() const + { + return output_node_id_; + } + + int index() const + { + return index_; + } + + private: + void print_current_in_line(std::ostream &stream) const override; +}; + } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_viewer_path.h b/source/blender/blenkernel/BKE_viewer_path.h index aa78593c81e..5be366a75a5 100644 --- a/source/blender/blenkernel/BKE_viewer_path.h +++ b/source/blender/blenkernel/BKE_viewer_path.h @@ -37,6 +37,7 @@ extern "C" { enum ViewerPathEqualFlag { VIEWER_PATH_EQUAL_FLAG_IGNORE_REPEAT_ITERATION = (1 << 0), + VIEWER_PATH_EQUAL_FLAG_IGNORE_FOREACH_INDEX = (1 << 1), }; void BKE_viewer_path_init(ViewerPath *viewer_path); @@ -57,6 +58,7 @@ GroupNodeViewerPathElem *BKE_viewer_path_elem_new_group_node(void); SimulationZoneViewerPathElem *BKE_viewer_path_elem_new_simulation_zone(void); ViewerNodeViewerPathElem *BKE_viewer_path_elem_new_viewer_node(void); RepeatZoneViewerPathElem *BKE_viewer_path_elem_new_repeat_zone(void); +ForEachZoneViewerPathElem *BKE_viewer_path_elem_new_foreach_zone(void); ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src); bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b, diff --git a/source/blender/blenkernel/intern/compute_contexts.cc b/source/blender/blenkernel/intern/compute_contexts.cc index ac47233b535..9796df74e38 100644 --- a/source/blender/blenkernel/intern/compute_contexts.cc +++ b/source/blender/blenkernel/intern/compute_contexts.cc @@ -119,4 +119,33 @@ void RepeatZoneComputeContext::print_current_in_line(std::ostream &stream) const stream << "Repeat Zone ID: " << output_node_id_; } +ForEachZoneComputeContext::ForEachZoneComputeContext(const ComputeContext *parent, + const int32_t output_node_id, + const int index) + : ComputeContext(s_static_type, parent), output_node_id_(output_node_id), index_(index) +{ + /* Mix static type and node id into a single buffer so that only a single call to #mix_in is + * necessary. */ + const int type_size = strlen(s_static_type); + const int buffer_size = type_size + 1 + sizeof(int32_t) + sizeof(int); + DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8); + char *buffer = static_cast(buffer_owner.buffer()); + memcpy(buffer, s_static_type, type_size + 1); + memcpy(buffer + type_size + 1, &output_node_id_, sizeof(int32_t)); + memcpy(buffer + type_size + 1 + sizeof(int32_t), &index_, sizeof(int)); + hash_.mix_in(buffer, buffer_size); +} + +ForEachZoneComputeContext::ForEachZoneComputeContext(const ComputeContext *parent, + const bNode &node, + const int index) + : ForEachZoneComputeContext(parent, node.identifier, index) +{ +} + +void ForEachZoneComputeContext::print_current_in_line(std::ostream &stream) const +{ + stream << "Repeat Zone ID: " << output_node_id_; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/viewer_path.cc b/source/blender/blenkernel/intern/viewer_path.cc index 9fbf8304370..84ed578ffc3 100644 --- a/source/blender/blenkernel/intern/viewer_path.cc +++ b/source/blender/blenkernel/intern/viewer_path.cc @@ -95,6 +95,11 @@ void BKE_viewer_path_blend_write(BlendWriter *writer, const ViewerPath *viewer_p BLO_write_struct(writer, RepeatZoneViewerPathElem, typed_elem); break; } + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { + const auto *typed_elem = reinterpret_cast(elem); + BLO_write_struct(writer, ForEachZoneViewerPathElem, typed_elem); + break; + } } BLO_write_string(writer, elem->ui_name); } @@ -110,6 +115,7 @@ void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: case VIEWER_PATH_ELEM_TYPE_ID: { break; } @@ -135,7 +141,8 @@ void BKE_viewer_path_foreach_id(LibraryForeachIDData *data, ViewerPath *viewer_p case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: - case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: { + case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { break; } } @@ -155,7 +162,8 @@ void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const IDRemapper *mapping case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: - case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: { + case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { break; } } @@ -190,6 +198,9 @@ ViewerPathElem *BKE_viewer_path_elem_new(const ViewerPathElemType type) case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: { return &make_elem(type)->base; } + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { + return &make_elem(type)->base; + } } BLI_assert_unreachable(); return nullptr; @@ -230,6 +241,12 @@ RepeatZoneViewerPathElem *BKE_viewer_path_elem_new_repeat_zone() BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE)); } +ForEachZoneViewerPathElem *BKE_viewer_path_elem_new_foreach_zone() +{ + return reinterpret_cast( + BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE)); +} + ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src) { ViewerPathElem *dst = BKE_viewer_path_elem_new(ViewerPathElemType(src->type)); @@ -276,6 +293,12 @@ ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src) new_elem->iteration = old_elem->iteration; break; } + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { + const auto *old_elem = reinterpret_cast(src); + auto *new_elem = reinterpret_cast(dst); + new_elem->foreach_output_node_id = old_elem->foreach_output_node_id; + new_elem->index = old_elem->index; + } } return dst; } @@ -320,6 +343,13 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, ((flag & VIEWER_PATH_EQUAL_FLAG_IGNORE_REPEAT_ITERATION) != 0 || a_elem->iteration == b_elem->iteration); } + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { + const auto *a_elem = reinterpret_cast(a); + const auto *b_elem = reinterpret_cast(b); + return a_elem->foreach_output_node_id == b_elem->foreach_output_node_id && + ((flag & VIEWER_PATH_EQUAL_FLAG_IGNORE_FOREACH_INDEX) != 0 || + a_elem->index == b_elem->index); + } } return false; } @@ -331,7 +361,8 @@ void BKE_viewer_path_elem_free(ViewerPathElem *elem) case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: - case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: { + case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { break; } case VIEWER_PATH_ELEM_TYPE_MODIFIER: { diff --git a/source/blender/editors/util/ed_viewer_path.cc b/source/blender/editors/util/ed_viewer_path.cc index f7b47a68781..31be4e52eea 100644 --- a/source/blender/editors/util/ed_viewer_path.cc +++ b/source/blender/editors/util/ed_viewer_path.cc @@ -43,6 +43,12 @@ static ViewerPathElem *viewer_path_elem_for_zone(const bNodeTreeZone &zone) node_elem->iteration = storage.inspection_index; return &node_elem->base; } + case GEO_NODE_FOR_EACH_OUTPUT: { + ForEachZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_foreach_zone(); + node_elem->foreach_output_node_id = zone.output_node->identifier; + node_elem->index = 0; + return &node_elem->base; + } } BLI_assert_unreachable(); return nullptr; @@ -252,7 +258,8 @@ std::optional parse_geometry_nodes_viewer( if (!ELEM(elem->type, VIEWER_PATH_ELEM_TYPE_GROUP_NODE, VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE, - VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE)) + VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE, + VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE)) { return std::nullopt; } @@ -319,6 +326,19 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed zone = next_zone; break; } + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { + const auto &typed_elem = *reinterpret_cast(path_elem); + const bNodeTreeZone *next_zone = tree_zones->get_zone_by_node( + typed_elem.foreach_output_node_id); + if (next_zone == nullptr) { + return false; + } + if (next_zone->parent_zone != zone) { + return false; + } + zone = next_zone; + break; + } case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { const auto &typed_elem = *reinterpret_cast(path_elem); const bNode *group_node = ngroup->node_by_id(typed_elem.node_id); diff --git a/source/blender/makesdna/DNA_viewer_path_types.h b/source/blender/makesdna/DNA_viewer_path_types.h index 0c942962bb8..9e4111f18f6 100644 --- a/source/blender/makesdna/DNA_viewer_path_types.h +++ b/source/blender/makesdna/DNA_viewer_path_types.h @@ -16,6 +16,7 @@ typedef enum ViewerPathElemType { VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE = 3, VIEWER_PATH_ELEM_TYPE_VIEWER_NODE = 4, VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE = 5, + VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE = 6, } ViewerPathElemType; typedef struct ViewerPathElem { @@ -56,6 +57,13 @@ typedef struct RepeatZoneViewerPathElem { int iteration; } RepeatZoneViewerPathElem; +typedef struct ForEachZoneViewerPathElem { + ViewerPathElem base; + + int foreach_output_node_id; + int index; +} ForEachZoneViewerPathElem; + typedef struct ViewerNodeViewerPathElem { ViewerPathElem base; diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index cb22f9bcbfc..efe8a738d8e 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -3348,6 +3348,8 @@ static StructRNA *rna_viewer_path_elem_refine(PointerRNA *ptr) return &RNA_ViewerNodeViewerPathElem; case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: return &RNA_RepeatZoneViewerPathElem; + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: + return &RNA_ForEachZoneViewerPathElem; } BLI_assert_unreachable(); return nullptr; @@ -8090,6 +8092,7 @@ static const EnumPropertyItem viewer_path_elem_type_items[] = { {VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE, "SIMULATION_ZONE", ICON_NONE, "Simulation Zone", ""}, {VIEWER_PATH_ELEM_TYPE_VIEWER_NODE, "VIEWER_NODE", ICON_NONE, "Viewer Node", ""}, {VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE, "REPEAT_ZONE", ICON_NONE, "Repeat", ""}, + {VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE, "FOREACH_ZONE", ICON_NONE, "For-Each Zone", ""}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -8168,6 +8171,17 @@ static void rna_def_repeat_zone_viewer_path_elem(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Repeat Output Node ID", ""); } +static void rna_def_foreach_zone_viewer_path_elem(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ForEachZoneViewerPathElem", "ViewerPathElem"); + + prop = RNA_def_property(srna, "foreach_output_node_id", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, "For-Each Output Node ID", ""); +} + static void rna_def_viewer_node_viewer_path_elem(BlenderRNA *brna) { StructRNA *srna; @@ -8190,6 +8204,7 @@ static void rna_def_viewer_path(BlenderRNA *brna) rna_def_group_node_viewer_path_elem(brna); rna_def_simulation_zone_viewer_path_elem(brna); rna_def_repeat_zone_viewer_path_elem(brna); + rna_def_foreach_zone_viewer_path_elem(brna); rna_def_viewer_node_viewer_path_elem(brna); srna = RNA_def_struct(brna, "ViewerPath", nullptr); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index cc22850362c..8d576140ac0 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -599,6 +599,29 @@ static void find_side_effect_nodes_for_viewer_path( zone = next_zone; break; } + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { + const auto &typed_elem = *reinterpret_cast(elem); + const bke::bNodeTreeZone *next_zone = tree_zones->get_zone_by_node( + typed_elem.foreach_output_node_id); + if (next_zone == nullptr) { + return; + } + if (next_zone->parent_zone != zone) { + return; + } + const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default( + next_zone, nullptr); + if (lf_zone_node == nullptr) { + return; + } + local_side_effect_nodes.nodes_by_context.add(compute_context_builder.hash(), lf_zone_node); + local_side_effect_nodes.indices_by_foreach_zone.add( + {compute_context_builder.hash(), typed_elem.foreach_output_node_id}, typed_elem.index); + compute_context_builder.push(*next_zone->output_node, + typed_elem.index); + zone = next_zone; + break; + } case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: { const auto &typed_elem = *reinterpret_cast(elem); const bNode *node = group->node_by_id(typed_elem.node_id); diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index ef468717b7c..747d04c94b2 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -135,6 +135,7 @@ struct GeoNodesSideEffectNodes { * repeat output node. */ MultiValueMap, int> iterations_by_repeat_zone; + MultiValueMap, int> indices_by_foreach_zone; }; /** diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index a9b136ae636..fa7e7569c37 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1915,9 +1915,18 @@ class LazyFunctionForForeachZone : public LazyFunction { GeometrySet &output_geometry = output_values[i]; bool usage_index; - GeoNodesLFLocalUserData body_local_user_data{user_data}; + const bke::ForEachZoneComputeContext body_compute_context{ + user_data.compute_context, *zone_.output_node, i}; + GeoNodesLFUserData body_user_data = user_data; + body_user_data.compute_context = &body_compute_context; + if (user_data.modifier_data && user_data.modifier_data->socket_log_contexts) { + body_user_data.log_socket_values = + user_data.modifier_data->socket_log_contexts->contains(body_compute_context.hash()); + } + + GeoNodesLFLocalUserData body_local_user_data{body_user_data}; lf::execute_lazy_function_eagerly(*body_fn_.function, - context.user_data, + &body_user_data, &body_local_user_data, std::make_tuple(ValueOrField(i), true), std::make_tuple(&usage_index, &output_geometry)); diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index 126e1b8dd07..1667abc2228 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -596,6 +596,12 @@ const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_path(const ViewerP typed_elem.repeat_output_node_id, typed_elem.iteration); break; } + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { + const auto &typed_elem = *reinterpret_cast(elem); + compute_context_builder.push( + typed_elem.foreach_output_node_id, typed_elem.index); + break; + } default: { BLI_assert_unreachable(); break; -- 2.30.2 From 18ee56bfa4164543ba05755a827893df87d66280 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 11:43:56 +0200 Subject: [PATCH 13/38] fixes after merge --- source/blender/editors/util/ed_viewer_path.cc | 6 +++++ source/blender/modifiers/intern/MOD_nodes.cc | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/source/blender/editors/util/ed_viewer_path.cc b/source/blender/editors/util/ed_viewer_path.cc index a54229b3216..8605937e4ce 100644 --- a/source/blender/editors/util/ed_viewer_path.cc +++ b/source/blender/editors/util/ed_viewer_path.cc @@ -504,6 +504,12 @@ bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snod elem.iteration); return true; } + case VIEWER_PATH_ELEM_TYPE_FOREACH_ZONE: { + const auto &elem = reinterpret_cast(elem_generic); + compute_context_builder.push(elem.foreach_output_node_id, + elem.index); + return true; + } } return false; } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 5161c58a4a4..5563de44e7b 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -606,6 +606,28 @@ static void try_add_side_effect_node(const ComputeContext &final_compute_context compute_context->iteration()); current_zone = repeat_zone; } + else if (const auto *compute_context = dynamic_cast( + compute_context_generic)) + { + const bke::bNodeTreeZone *foreach_zone = current_zones->get_zone_by_node( + compute_context->output_node_id()); + if (foreach_zone == nullptr) { + return; + } + if (foreach_zone->parent_zone != current_zone) { + return; + } + const lf::FunctionNode *lf_zone_node = lf_graph_info->mapping.zone_node_map.lookup_default( + foreach_zone, nullptr); + if (lf_zone_node == nullptr) { + return; + } + local_side_effect_nodes.nodes_by_context.add(parent_compute_context_hash, lf_zone_node); + local_side_effect_nodes.indices_by_foreach_zone.add( + {parent_compute_context_hash, compute_context->output_node_id()}, + compute_context->index()); + current_zone = foreach_zone; + } else if (const auto *compute_context = dynamic_cast( compute_context_generic)) { -- 2.30.2 From fa4c4090d0d487fb9c78442a4d9aa3dd4de3f460 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 13:19:20 +0200 Subject: [PATCH 14/38] add initial output storage --- source/blender/blenkernel/intern/node.cc | 7 ++++++ source/blender/makesdna/DNA_node_types.h | 4 ++++ .../blender/makesrna/intern/rna_nodetree.cc | 7 ++++++ source/blender/nodes/NOD_static_types.h | 2 +- .../geometry/nodes/node_geo_foreach_output.cc | 23 +++++++++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 18d3bbfbe75..92b2cf38a57 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -788,6 +788,9 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) if (node->type == GEO_NODE_REPEAT_OUTPUT) { blender::nodes::RepeatItemsAccessor::blend_write(writer, *node); } + if (node->type == GEO_NODE_FOR_EACH_OUTPUT) { + /* TODO */ + } } LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { @@ -985,6 +988,10 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) blender::nodes::RepeatItemsAccessor::blend_read_data(reader, *node); break; } + case GEO_NODE_FOR_EACH_OUTPUT: { + /* TODO */ + break; + } default: break; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 10c77724ed8..c369fcbee4a 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1841,6 +1841,10 @@ typedef struct NodeGeometryForEachInput { int output_node_id; } NodeGeometryForEachInput; +typedef struct NodeGeometryForEachOutput { + int next_input_identifier; +} NodeGeometryForEachOutput; + typedef struct NodeGeometryDistributePointsInVolume { /** #GeometryNodePointDistributeVolumeMode. */ uint8_t mode; diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 95e465c44db..7d18767b565 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -8963,6 +8963,13 @@ static void def_geo_repeat_output(StructRNA *srna) RNA_def_property_update(prop, NC_NODE, "rna_Node_update"); } +static void def_geo_foreach_output(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryForEachOutput", "storage"); +} + static void def_geo_curve_handle_type_selection(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index b4e0f3ed05a..d614389f8dc 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -332,7 +332,7 @@ DefNode(GeometryNode, GEO_NODE_FILL_CURVE, 0, "FILL_CURVE", FillCurve, "Fill Cur DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, 0, "FILLET_CURVE", FilletCurve, "Fillet Curve", "Round corners by generating circular arcs on each control point") DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "Reverse the order of the vertices and edges of selected faces, flipping their normal direction") DefNode(GeometryNode, GEO_NODE_FOR_EACH_INPUT, def_geo_foreach_input, "FOREACH_INPUT", ForEachInput, "For-Each Input", "") -DefNode(GeometryNode, GEO_NODE_FOR_EACH_OUTPUT, 0, "FOREACH_OUTPUT", ForEachOutput, "For-Each Output", "") +DefNode(GeometryNode, GEO_NODE_FOR_EACH_OUTPUT, def_geo_foreach_output, "FOREACH_OUTPUT", ForEachOutput, "For-Each Output", "") DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "Convert each input geometry into an instance, which can be much faster than the Join Geometry node when the inputs are large") DefNode(GeometryNode, GEO_NODE_IMAGE_INFO, 0, "IMAGE_INFO", ImageInfo, "Image Info", "Retrieve information about an image") DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture") diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc index 7c4f24501e7..3609f1b4cad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc @@ -17,18 +17,41 @@ namespace blender::nodes::node_geo_foreach_output_cc { +NODE_STORAGE_FUNCS(NodeGeometryForEachOutput); + static void node_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_output("Geometry"); } +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + NodeGeometryForEachOutput *data = MEM_cnew(__func__); + + node->storage = data; +} + +static void node_free_storage(bNode *node) +{ + MEM_freeN(node->storage); +} + +static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node) +{ + const NodeGeometryForEachOutput &src_storage = node_storage(*src_node); + auto *dst_storage = MEM_new(__func__, src_storage); + dst_node->storage = dst_storage; +} + static void node_register() { static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_OUTPUT, "For-Each Output", NODE_CLASS_INTERFACE); + ntype.initfunc = node_init; ntype.declare = node_declare; ntype.gather_link_search_ops = nullptr; + node_type_storage(&ntype, "NodeGeometryForEachOutput", node_free_storage, node_copy_storage); nodeRegisterType(&ntype); } NOD_REGISTER_NODE(node_register) -- 2.30.2 From 8751d8b6b36313541411b54156c16ede8b5f0241 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 14:00:30 +0200 Subject: [PATCH 15/38] add dna/rna for for each socket items --- source/blender/blenkernel/intern/node.cc | 6 +- source/blender/makesdna/DNA_node_types.h | 55 +++++++++- .../blender/makesrna/intern/rna_nodetree.cc | 73 +++++++++++-- source/blender/nodes/NOD_zone_socket_items.hh | 103 ++++++++++++++++++ .../geometry/nodes/node_geo_foreach_output.cc | 6 + .../nodes/intern/node_zone_socket_items.cc | 44 ++++++++ 6 files changed, 277 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 92b2cf38a57..9312e53180a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -789,7 +789,8 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) blender::nodes::RepeatItemsAccessor::blend_write(writer, *node); } if (node->type == GEO_NODE_FOR_EACH_OUTPUT) { - /* TODO */ + blender::nodes::ForEachInputItemsAccessor::blend_write(writer, *node); + blender::nodes::ForEachOutputItemsAccessor::blend_write(writer, *node); } } @@ -989,7 +990,8 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) break; } case GEO_NODE_FOR_EACH_OUTPUT: { - /* TODO */ + blender::nodes::ForEachInputItemsAccessor::blend_read_data(reader, *node); + blender::nodes::ForEachOutputItemsAccessor::blend_read_data(reader, *node); break; } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index c369fcbee4a..a26b0692256 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1836,13 +1836,66 @@ typedef struct NodeGeometryRepeatOutput { #endif } NodeGeometryRepeatOutput; +typedef struct NodeForEachInputItem { + char *name; + /** #eNodeSocketDatatype. */ + short socket_type; + char _pad[2]; + /** + * Generated unique identifier for sockets which stays the same even when the item order or + * names change. + */ + int identifier; +} NodeForEachInputItem; + +typedef struct NodeForEachOutputItem { + char *name; + /** #eNodeSocketDatatype. */ + short socket_type; + char _pad[2]; + /** + * Generated unique identifier for sockets which stays the same even when the item order or + * names change. + */ + int identifier; +} NodeForEachOutputItem; + typedef struct NodeGeometryForEachInput { /** bNode.identifier of the corresponding output node. */ int output_node_id; } NodeGeometryForEachInput; typedef struct NodeGeometryForEachOutput { - int next_input_identifier; + NodeForEachInputItem *input_items; + int input_items_num; + int input_active_index; + int input_next_identifier; + char _pad1[4]; + + NodeForEachOutputItem *output_items; + int output_items_num; + int output_active_index; + int output_next_identifier; + char _pad2[4]; + +#ifdef __cplusplus + blender::Span input_items_span() const + { + return {this->input_items, this->input_items_num}; + } + blender::MutableSpan input_items_span() + { + return {this->input_items, this->input_items_num}; + } + blender::Span output_items_span() const + { + return {this->output_items, this->output_items_num}; + } + blender::MutableSpan output_items_span() + { + return {this->output_items, this->output_items_num}; + } +#endif } NodeGeometryForEachOutput; typedef struct NodeGeometryDistributePointsInVolume { diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 7d18767b565..119d62ba059 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -608,6 +608,8 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = { # include "DNA_scene_types.h" # include "WM_api.hh" +using blender::nodes::ForEachInputItemsAccessor; +using blender::nodes::ForEachOutputItemsAccessor; using blender::nodes::RepeatItemsAccessor; using blender::nodes::SimulationItemsAccessor; @@ -8737,17 +8739,17 @@ static void rna_def_node_item_array_socket_item_common(StructRNA *srna, const ch static blender::LinearAllocator<> allocator; PropertyRNA *prop; - char name_set_func[64]; + char name_set_func[128]; SNPRINTF(name_set_func, "rna_Node_ItemArray_item_name_set<%s>", accessor); - char item_update_func[64]; + char item_update_func[128]; SNPRINTF(item_update_func, "rna_Node_ItemArray_item_update<%s>", accessor); const char *item_update_func_ptr = allocator.copy_string(item_update_func).c_str(); - char socket_type_itemf[64]; + char socket_type_itemf[128]; SNPRINTF(socket_type_itemf, "rna_Node_ItemArray_socket_type_itemf<%s>", accessor); - char color_get_func[64]; + char color_get_func[128]; SNPRINTF(color_get_func, "rna_Node_ItemArray_item_color_get<%s>", accessor); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); @@ -8782,11 +8784,11 @@ static void rna_def_node_item_array_common_functions(StructRNA *srna, PropertyRNA *parm; FunctionRNA *func; - char remove_call[64]; + char remove_call[128]; SNPRINTF(remove_call, "rna_Node_ItemArray_remove<%s>", accessor_name); - char clear_call[64]; + char clear_call[128]; SNPRINTF(clear_call, "rna_Node_ItemArray_clear<%s>", accessor_name); - char move_call[64]; + char move_call[128]; SNPRINTF(move_call, "rna_Node_ItemArray_move<%s>", accessor_name); func = RNA_def_function(srna, "remove", allocator.copy_string(remove_call).c_str()); @@ -8963,11 +8965,64 @@ static void def_geo_repeat_output(StructRNA *srna) RNA_def_property_update(prop, NC_NODE, "rna_Node_update"); } +static void rna_def_foreach_input_item(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "ForEachInputItem", nullptr); + RNA_def_struct_ui_text(srna, "For-Each Input Item", ""); + RNA_def_struct_sdna(srna, "NodeForEachInputItem"); + + rna_def_node_item_array_socket_item_common(srna, "ForEachInputItemsAccessor"); +} + +static void rna_def_foreach_output_item(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "ForEachOutputItem", nullptr); + RNA_def_struct_ui_text(srna, "For-Each Output Item", ""); + RNA_def_struct_sdna(srna, "NodeForEachOutputItem"); + + rna_def_node_item_array_socket_item_common(srna, "ForEachOutputItemsAccessor"); +} + +static void rna_def_geo_foreach_input_items(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "NodeGeometryForEachInputItems", nullptr); + RNA_def_struct_sdna(srna, "bNode"); + RNA_def_struct_ui_text(srna, "Input Items", "Collection of for-each input items"); + + rna_def_node_item_array_new_with_socket_and_name( + srna, "ForEachInputItem", "ForEachInputItemsAccessor"); + rna_def_node_item_array_common_functions(srna, "ForEachInputItem", "ForEachInputItemsAccessor"); +} + +static void rna_def_geo_foreach_output_items(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "NodeGeometryForEachOutputItems", nullptr); + RNA_def_struct_sdna(srna, "bNode"); + RNA_def_struct_ui_text(srna, "Output Items", "Collection of for-each output items"); + + rna_def_node_item_array_new_with_socket_and_name( + srna, "ForEachOutputItem", "ForEachOutputItemsAccessor"); + rna_def_node_item_array_common_functions( + srna, "ForEachOutputItem", "ForEachOutputItemsAccessor"); +} + static void def_geo_foreach_output(StructRNA *srna) { PropertyRNA *prop; RNA_def_struct_sdna_from(srna, "NodeGeometryForEachOutput", "storage"); + + prop = RNA_def_property(srna, "input_items", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, nullptr, "input_items", "input_items_num"); + RNA_def_property_struct_type(prop, "ForEachInputItem"); + RNA_def_property_ui_text(prop, "Input Items", ""); + RNA_def_property_srna(prop, "NodeGeometryForEachInputItems"); + + prop = RNA_def_property(srna, "output_items", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, nullptr, "output_items", "output_items_num"); + RNA_def_property_struct_type(prop, "ForEachOutputItem"); + RNA_def_property_ui_text(prop, "OutputItems", ""); + RNA_def_property_srna(prop, "NodeGeometryForEachOutputItems"); } static void def_geo_curve_handle_type_selection(StructRNA *srna) @@ -10292,6 +10347,8 @@ void RNA_def_nodetree(BlenderRNA *brna) rna_def_simulation_state_item(brna); rna_def_repeat_item(brna); + rna_def_foreach_input_item(brna); + rna_def_foreach_output_item(brna); # define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \ { \ @@ -10344,6 +10401,8 @@ void RNA_def_nodetree(BlenderRNA *brna) rna_def_cmp_output_file_slot_layer(brna); rna_def_geo_simulation_output_items(brna); rna_def_geo_repeat_output_items(brna); + rna_def_geo_foreach_input_items(brna); + rna_def_geo_foreach_output_items(brna); rna_def_node_instance_hash(brna); } diff --git a/source/blender/nodes/NOD_zone_socket_items.hh b/source/blender/nodes/NOD_zone_socket_items.hh index 72e4703fe9d..e46840bde38 100644 --- a/source/blender/nodes/NOD_zone_socket_items.hh +++ b/source/blender/nodes/NOD_zone_socket_items.hh @@ -141,4 +141,107 @@ struct RepeatItemsAccessor { } }; +struct ForEachInputItemsAccessor { + using ItemT = NodeForEachInputItem; + static StructRNA *item_srna; + /* This refers to the node that stores the item array. */ + static int node_type; + static constexpr const char *node_idname = "GeometryNodesForEachOutput"; + + static socket_items::SocketItemsRef get_items_from_node(bNode &node) + { + auto *storage = static_cast(node.storage); + return {&storage->input_items, &storage->input_items_num, &storage->input_active_index}; + } + static void copy_item(const NodeForEachInputItem &src, NodeForEachInputItem &dst) + { + dst = src; + dst.name = BLI_strdup_null(dst.name); + } + static void destruct_item(NodeForEachInputItem *item) + { + MEM_SAFE_FREE(item->name); + } + static void blend_write(BlendWriter *, const bNode &node); + static void blend_read_data(BlendDataReader *reader, bNode &node); + static short *get_socket_type(NodeForEachInputItem &item) + { + return &item.socket_type; + } + static char **get_name(NodeForEachInputItem &item) + { + return &item.name; + } + static bool supports_socket_type(const eNodeSocketDatatype /*socket_type*/) + { + /* TODO */ + return true; + } + static void init_with_socket_type_and_name(bNode &node, + NodeForEachInputItem &item, + const eNodeSocketDatatype socket_type, + const char *name) + { + auto *storage = static_cast(node.storage); + item.socket_type = socket_type; + item.identifier = storage->input_next_identifier++; + socket_items::set_item_name_and_make_unique(node, item, name); + } + static std::string socket_identifier_for_item(const NodeForEachInputItem &item) + { + return "Item_" + std::to_string(item.identifier); + } +}; + +struct ForEachOutputItemsAccessor { + using ItemT = NodeForEachOutputItem; + static StructRNA *item_srna; + static int node_type; + static constexpr const char *node_idname = "GeometryNodesForEachOutput"; + + static socket_items::SocketItemsRef get_items_from_node(bNode &node) + { + auto *storage = static_cast(node.storage); + return {&storage->output_items, &storage->output_items_num, &storage->output_active_index}; + } + static void copy_item(const NodeForEachOutputItem &src, NodeForEachOutputItem &dst) + { + dst = src; + dst.name = BLI_strdup_null(dst.name); + } + static void destruct_item(NodeForEachOutputItem *item) + { + MEM_SAFE_FREE(item->name); + } + static void blend_write(BlendWriter *, const bNode &node); + static void blend_read_data(BlendDataReader *reader, bNode &node); + static short *get_socket_type(NodeForEachOutputItem &item) + { + return &item.socket_type; + } + static char **get_name(NodeForEachOutputItem &item) + { + return &item.name; + } + static bool supports_socket_type(const eNodeSocketDatatype /*socket_type*/) + { + /* TODO */ + return true; + } + static void init_with_socket_type_and_name(bNode &node, + NodeForEachOutputItem &item, + const eNodeSocketDatatype socket_type, + const char *name) + { + auto *storage = static_cast(node.storage); + item.socket_type = socket_type; + item.identifier = storage->output_next_identifier++; + socket_items::set_item_name_and_make_unique(node, item, name); + } + static std::string socket_identifier_for_item(const NodeForEachOutputItem &item) + { + return "Item_" + std::to_string(item.identifier); + } +}; + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc index 3609f1b4cad..675b10e1d74 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc @@ -12,6 +12,7 @@ #include "NOD_geometry.hh" #include "NOD_socket.hh" +#include "NOD_zone_socket_items.hh" #include "node_geometry_util.hh" @@ -34,6 +35,8 @@ static void node_init(bNodeTree * /*tree*/, bNode *node) static void node_free_storage(bNode *node) { + socket_items::destruct_array(*node); + socket_items::destruct_array(*node); MEM_freeN(node->storage); } @@ -42,6 +45,9 @@ static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const b const NodeGeometryForEachOutput &src_storage = node_storage(*src_node); auto *dst_storage = MEM_new(__func__, src_storage); dst_node->storage = dst_storage; + + socket_items::copy_array(*src_node, *dst_node); + socket_items::copy_array(*src_node, *dst_node); } static void node_register() diff --git a/source/blender/nodes/intern/node_zone_socket_items.cc b/source/blender/nodes/intern/node_zone_socket_items.cc index 03c6b104ca1..9df8726e8b0 100644 --- a/source/blender/nodes/intern/node_zone_socket_items.cc +++ b/source/blender/nodes/intern/node_zone_socket_items.cc @@ -56,4 +56,48 @@ void RepeatItemsAccessor::blend_read_data(BlendDataReader *reader, bNode &node) } } +StructRNA *ForEachInputItemsAccessor::item_srna = &RNA_ForEachInputItem; +int ForEachInputItemsAccessor::node_type = GEO_NODE_FOR_EACH_OUTPUT; + +void ForEachInputItemsAccessor::blend_write(BlendWriter *writer, const bNode &node) +{ + const auto &storage = *static_cast(node.storage); + BLO_write_struct_array( + writer, NodeForEachInputItem, storage.input_items_num, storage.input_items); + for (const NodeForEachInputItem &item : Span(storage.input_items, storage.input_items_num)) { + BLO_write_string(writer, item.name); + } +} + +void ForEachInputItemsAccessor::blend_read_data(BlendDataReader *reader, bNode &node) +{ + auto &storage = *static_cast(node.storage); + BLO_read_data_address(reader, &storage.input_items); + for (const NodeForEachInputItem &item : Span(storage.input_items, storage.input_items_num)) { + BLO_read_data_address(reader, &item.name); + } +} + +StructRNA *ForEachOutputItemsAccessor::item_srna = &RNA_ForEachOutputItem; +int ForEachOutputItemsAccessor::node_type = GEO_NODE_REPEAT_OUTPUT; + +void ForEachOutputItemsAccessor::blend_write(BlendWriter *writer, const bNode &node) +{ + const auto &storage = *static_cast(node.storage); + BLO_write_struct_array( + writer, NodeForEachOutputItem, storage.output_items_num, storage.output_items); + for (const NodeForEachOutputItem &item : Span(storage.output_items, storage.output_items_num)) { + BLO_write_string(writer, item.name); + } +} + +void ForEachOutputItemsAccessor::blend_read_data(BlendDataReader *reader, bNode &node) +{ + auto &storage = *static_cast(node.storage); + BLO_read_data_address(reader, &storage.output_items); + for (const NodeForEachOutputItem &item : Span(storage.output_items, storage.output_items_num)) { + BLO_read_data_address(reader, &item.name); + } +} + } // namespace blender::nodes -- 2.30.2 From 6364e1735381260111b05b831fe49b92125f8dfd Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 14:16:45 +0200 Subject: [PATCH 16/38] support dynamic sockets on For-each output node --- .../geometry/nodes/node_geo_foreach_output.cc | 52 +++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc index 675b10e1d74..6d976d5244f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc @@ -20,10 +20,47 @@ namespace blender::nodes::node_geo_foreach_output_cc { NODE_STORAGE_FUNCS(NodeGeometryForEachOutput); -static void node_declare(NodeDeclarationBuilder &b) +static void node_declare_dynamic(const bNodeTree & /*node_tree*/, + const bNode &node, + NodeDeclaration &r_declaration) { - b.add_input("Geometry"); - b.add_output("Geometry"); + const NodeGeometryForEachOutput &storage = node_storage(node); + for (const int i : IndexRange(storage.output_items_num)) { + const NodeForEachOutputItem &item = storage.output_items[i]; + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); + { + SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); + BLI_assert(decl); + decl->name = StringRef(item.name); + decl->identifier = ForEachOutputItemsAccessor::socket_identifier_for_item(item); + decl->in_out = SOCK_IN; + if (socket_type_supports_fields(socket_type)) { + decl->input_field_type = InputSocketFieldType::IsSupported; + } + r_declaration.inputs.append(decl.get()); + r_declaration.items.append(std::move(decl)); + } + { + SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); + BLI_assert(decl); + decl->name = StringRef(item.name); + decl->identifier = ForEachOutputItemsAccessor::socket_identifier_for_item(item); + decl->in_out = SOCK_OUT; + if (socket_type_supports_fields(socket_type)) { + decl->output_field_dependency = OutputFieldDependency::ForFieldSource(); + } + r_declaration.outputs.append(decl.get()); + r_declaration.items.append(std::move(decl)); + } + } + + SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN); + r_declaration.inputs.append(input_extend_decl.get()); + r_declaration.items.append(std::move(input_extend_decl)); + + SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT); + r_declaration.outputs.append(output_extend_decl.get()); + r_declaration.items.append(std::move(output_extend_decl)); } static void node_init(bNodeTree * /*tree*/, bNode *node) @@ -50,13 +87,20 @@ static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const b socket_items::copy_array(*src_node, *dst_node); } +static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link) +{ + return socket_items::try_add_item_via_any_extend_socket( + *ntree, *node, *node, *link); +} + static void node_register() { static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_OUTPUT, "For-Each Output", NODE_CLASS_INTERFACE); ntype.initfunc = node_init; - ntype.declare = node_declare; + ntype.declare_dynamic = node_declare_dynamic; ntype.gather_link_search_ops = nullptr; + ntype.insert_link = node_insert_link; node_type_storage(&ntype, "NodeGeometryForEachOutput", node_free_storage, node_copy_storage); nodeRegisterType(&ntype); } -- 2.30.2 From ebbd3fcdd5b6874c5a6de0dd084e3965e1bb5b11 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 14:42:55 +0200 Subject: [PATCH 17/38] support dynamic sockets on input --- .../geometry/nodes/node_geo_foreach_input.cc | 67 +++++++++++++++++-- .../geometry/nodes/node_geo_foreach_output.cc | 5 +- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc index 6f2bf6acfe6..7f1625f2bb4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc @@ -12,6 +12,7 @@ #include "NOD_geometry.hh" #include "NOD_socket.hh" +#include "NOD_zone_socket_items.hh" #include "node_geometry_util.hh" @@ -19,10 +20,56 @@ namespace blender::nodes::node_geo_foreach_input_cc { NODE_STORAGE_FUNCS(NodeGeometryForEachInput); -static void node_declare(NodeDeclarationBuilder &b) +static void node_declare_dynamic(const bNodeTree &tree, + const bNode &node, + NodeDeclaration &r_declaration) { - b.add_input("Amount").min(0).default_value(1); - b.add_output("Index"); + const NodeGeometryForEachInput &input_storage = node_storage(node); + const bNode *output_node = tree.node_by_id(input_storage.output_node_id); + if (output_node == nullptr) { + r_declaration.skip_updating_sockets = true; + return; + } + { + NodeDeclarationBuilder b{r_declaration}; + b.add_input("Amount").min(0).default_value(1); + b.add_output("Index"); + } + const auto &output_storage = *static_cast( + output_node->storage); + for (const int i : IndexRange(output_storage.input_items_num)) { + const NodeForEachInputItem &item = output_storage.input_items[i]; + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); + const std::string identifier = ForEachInputItemsAccessor::socket_identifier_for_item(item); + { + SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); + BLI_assert(decl); + decl->name = StringRef(item.name); + decl->identifier = identifier; + decl->in_out = SOCK_IN; + if (socket_type_supports_fields(socket_type)) { + decl->input_field_type = InputSocketFieldType::IsSupported; + } + r_declaration.inputs.append(decl.get()); + r_declaration.items.append(std::move(decl)); + } + { + SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); + BLI_assert(decl); + decl->name = StringRef(item.name); + decl->identifier = identifier; + decl->in_out = SOCK_OUT; + r_declaration.outputs.append(decl.get()); + r_declaration.items.append(std::move(decl)); + } + } + SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN); + r_declaration.inputs.append(input_extend_decl.get()); + r_declaration.items.append(std::move(input_extend_decl)); + + SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT); + r_declaration.outputs.append(output_extend_decl.get()); + r_declaration.items.append(std::move(output_extend_decl)); } static void node_init(bNodeTree * /*tree*/, bNode *node) @@ -33,13 +80,25 @@ static void node_init(bNodeTree * /*tree*/, bNode *node) node->storage = data; } +static bool node_insert_link(bNodeTree *tree, bNode *node, bNodeLink *link) +{ + const NodeGeometryForEachInput &input_storage = node_storage(*node); + bNode *output_node = tree->node_by_id(input_storage.output_node_id); + if (output_node == nullptr) { + return true; + } + return socket_items::try_add_item_via_any_extend_socket( + *tree, *node, *output_node, *link); +} + static void node_register() { static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_FOR_EACH_INPUT, "For-Each Input", NODE_CLASS_INTERFACE); ntype.initfunc = node_init; - ntype.declare = node_declare; + ntype.declare_dynamic = node_declare_dynamic; ntype.gather_link_search_ops = nullptr; + ntype.insert_link = node_insert_link; node_type_storage( &ntype, "NodeGeometryForEachInput", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc index 6d976d5244f..0b3442c708c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc @@ -28,11 +28,12 @@ static void node_declare_dynamic(const bNodeTree & /*node_tree*/, for (const int i : IndexRange(storage.output_items_num)) { const NodeForEachOutputItem &item = storage.output_items[i]; const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); + const std::string identifier = ForEachOutputItemsAccessor::socket_identifier_for_item(item); { SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); BLI_assert(decl); decl->name = StringRef(item.name); - decl->identifier = ForEachOutputItemsAccessor::socket_identifier_for_item(item); + decl->identifier = identifier; decl->in_out = SOCK_IN; if (socket_type_supports_fields(socket_type)) { decl->input_field_type = InputSocketFieldType::IsSupported; @@ -44,7 +45,7 @@ static void node_declare_dynamic(const bNodeTree & /*node_tree*/, SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); BLI_assert(decl); decl->name = StringRef(item.name); - decl->identifier = ForEachOutputItemsAccessor::socket_identifier_for_item(item); + decl->identifier = identifier; decl->in_out = SOCK_OUT; if (socket_type_supports_fields(socket_type)) { decl->output_field_dependency = OutputFieldDependency::ForFieldSource(); -- 2.30.2 From daea6ff0efd1bf9bd5f2950993f9c31e9d3b1ae7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 15:10:48 +0200 Subject: [PATCH 18/38] add initial ui lists for for-each items --- scripts/startup/bl_ui/space_node.py | 137 ++++++++++++++++-- .../blender/makesrna/intern/rna_nodetree.cc | 36 +++++ source/blender/nodes/NOD_zone_socket_items.hh | 4 +- 3 files changed, 160 insertions(+), 17 deletions(-) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 5606f2a95f2..1e2c676eeb3 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -991,16 +991,18 @@ class NODE_PT_node_tree_properties(Panel): col.prop(group, "is_tool") +def draw_socket_item_in_list(uilist, layout, item, icon): + if uilist.layout_type in {'DEFAULT', 'COMPACT'}: + row = layout.row(align=True) + row.template_node_socket(color=item.color) + row.prop(item, "name", text="", emboss=False, icon_value=icon) + elif uilist.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.template_node_socket(color=item.color) + class NODE_UL_simulation_zone_items(bpy.types.UIList): def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index): - if self.layout_type in {'DEFAULT', 'COMPACT'}: - row = layout.row(align=True) - - row.template_node_socket(color=item.color) - row.prop(item, "name", text="", emboss=False, icon_value=icon) - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.template_node_socket(color=item.color) + draw_socket_item_in_list(self, layout, item, icon) class NODE_PT_simulation_zone_items(Panel): @@ -1072,13 +1074,7 @@ class NODE_PT_simulation_zone_items(Panel): class NODE_UL_repeat_zone_items(bpy.types.UIList): def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): - if self.layout_type in {'DEFAULT', 'COMPACT'}: - row = layout.row(align=True) - row.template_node_socket(color=item.color) - row.prop(item, "name", text="", emboss=False, icon_value=icon) - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.template_node_socket(color=item.color) + draw_socket_item_in_list(self, layout, item, icon) class NODE_PT_repeat_zone_items(Panel): @@ -1146,6 +1142,114 @@ class NODE_PT_repeat_zone_items(Panel): layout.prop(output_node, "inspection_index") +class NODE_UL_foreach_zone_input_items(bpy.types.UIList): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + draw_socket_item_in_list(self, layout, item, icon) + + +class NODE_UL_foreach_zone_output_items(bpy.types.UIList): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + draw_socket_item_in_list(self, layout, item, icon) + + +class NODE_PT_foreach_zone_items(Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'UI' + bl_category = "Node" + bl_label = "For-Each" + + input_node_type = 'GeometryNodeForEachInput' + output_node_type = 'GeometryNodeForEachOutput' + + @classmethod + def get_output_node(cls, context): + node = context.active_node + if node.bl_idname == cls.input_node_type: + return node.paired_output + if node.bl_idname == cls.output_node_type: + return node + return None + + @classmethod + def poll(cls, context): + snode = context.space_data + if snode is None: + return False + node = context.active_node + if node is None or node.bl_idname not in (cls.input_node_type, cls.output_node_type): + return False + if cls.get_output_node(context) is None: + return False + return True + + def draw(self, context): + layout = self.layout + output_node = self.get_output_node(context) + self.draw_input_items(layout, output_node) + self.draw_output_items(layout, output_node) + + def draw_input_items(self, layout, output_node): + split = layout.row() + split.template_list( + "NODE_UL_foreach_zone_input_items", + "", + output_node, + "input_items", + output_node, + "input_active_index") + + # ops_col = split.column() + + # add_remove_col = ops_col.column(align=True) + # add_remove_col.operator("node.repeat_zone_item_add", icon='ADD', text="") + # add_remove_col.operator("node.repeat_zone_item_remove", icon='REMOVE', text="") + + # ops_col.separator() + + # up_down_col = ops_col.column(align=True) + # props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_UP', text="") + # props.direction = 'UP' + # props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_DOWN', text="") + # props.direction = 'DOWN' + + # active_item = output_node.active_item + # if active_item is not None: + # layout.use_property_split = True + # layout.use_property_decorate = False + # layout.prop(active_item, "socket_type") + + def draw_output_items(self, layout, output_node): + split = layout.row() + split.template_list( + "NODE_UL_foreach_zone_output_items", + "", + output_node, + "output_items", + output_node, + "output_active_index") + + # ops_col = split.column() + + # add_remove_col = ops_col.column(align=True) + # add_remove_col.operator("node.repeat_zone_item_add", icon='ADD', text="") + # add_remove_col.operator("node.repeat_zone_item_remove", icon='REMOVE', text="") + + # ops_col.separator() + + # up_down_col = ops_col.column(align=True) + # props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_UP', text="") + # props.direction = 'UP' + # props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_DOWN', text="") + # props.direction = 'DOWN' + + # active_item = output_node.active_item + # if active_item is not None: + # layout.use_property_split = True + # layout.use_property_decorate = False + # layout.prop(active_item, "socket_type") + + + # Grease Pencil properties class NODE_PT_annotation(AnnotationDataPanel, Panel): bl_space_type = 'NODE_EDITOR' @@ -1215,6 +1319,9 @@ classes = ( NODE_PT_simulation_zone_items, NODE_UL_repeat_zone_items, NODE_PT_repeat_zone_items, + NODE_UL_foreach_zone_input_items, + NODE_UL_foreach_zone_output_items, + NODE_PT_foreach_zone_items, NODE_PT_active_node_properties, node_panel(EEVEE_MATERIAL_PT_settings), diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 119d62ba059..6f629c680c6 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -9018,11 +9018,47 @@ static void def_geo_foreach_output(StructRNA *srna) RNA_def_property_ui_text(prop, "Input Items", ""); RNA_def_property_srna(prop, "NodeGeometryForEachInputItems"); + prop = RNA_def_property(srna, "input_active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, nullptr, "input_active_index"); + RNA_def_property_ui_text(prop, "Active Input Item Index", "Index of the active item"); + RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_NODE, nullptr); + + prop = RNA_def_property(srna, "input_active_item", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ForEachInputItem"); + RNA_def_property_pointer_funcs(prop, + "rna_Node_ItemArray_active_get", + "rna_Node_ItemArray_active_set", + nullptr, + nullptr); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NO_DEG_UPDATE); + RNA_def_property_ui_text(prop, "Active Item Index", "Index of the active item"); + RNA_def_property_update(prop, NC_NODE, nullptr); + prop = RNA_def_property(srna, "output_items", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "output_items", "output_items_num"); RNA_def_property_struct_type(prop, "ForEachOutputItem"); RNA_def_property_ui_text(prop, "OutputItems", ""); RNA_def_property_srna(prop, "NodeGeometryForEachOutputItems"); + + prop = RNA_def_property(srna, "output_active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, nullptr, "output_active_index"); + RNA_def_property_ui_text(prop, "Active Output Item Index", "Index of the active item"); + RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_NODE, nullptr); + + prop = RNA_def_property(srna, "output_active_item", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ForEachOutputItem"); + RNA_def_property_pointer_funcs(prop, + "rna_Node_ItemArray_active_get", + "rna_Node_ItemArray_active_set", + nullptr, + nullptr); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NO_DEG_UPDATE); + RNA_def_property_ui_text(prop, "Active Item Index", "Index of the active item"); + RNA_def_property_update(prop, NC_NODE, nullptr); } static void def_geo_curve_handle_type_selection(StructRNA *srna) diff --git a/source/blender/nodes/NOD_zone_socket_items.hh b/source/blender/nodes/NOD_zone_socket_items.hh index e46840bde38..89e5b5978b0 100644 --- a/source/blender/nodes/NOD_zone_socket_items.hh +++ b/source/blender/nodes/NOD_zone_socket_items.hh @@ -146,7 +146,7 @@ struct ForEachInputItemsAccessor { static StructRNA *item_srna; /* This refers to the node that stores the item array. */ static int node_type; - static constexpr const char *node_idname = "GeometryNodesForEachOutput"; + static constexpr const char *node_idname = "GeometryNodeForEachOutput"; static socket_items::SocketItemsRef get_items_from_node(bNode &node) { @@ -197,7 +197,7 @@ struct ForEachOutputItemsAccessor { using ItemT = NodeForEachOutputItem; static StructRNA *item_srna; static int node_type; - static constexpr const char *node_idname = "GeometryNodesForEachOutput"; + static constexpr const char *node_idname = "GeometryNodeForEachOutput"; static socket_items::SocketItemsRef get_items_from_node(bNode &node) { -- 2.30.2 From 32d891d725516d2ec6b8b1771fdce75b39c345dd Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 15:59:09 +0200 Subject: [PATCH 19/38] improve ui for for-each inputs/outputs --- .../startup/bl_operators/geometry_nodes.py | 61 +++++++++++++++++++ scripts/startup/bl_ui/space_node.py | 60 +++++++++--------- .../blender/nodes/intern/node_declaration.cc | 1 + 3 files changed, 92 insertions(+), 30 deletions(-) diff --git a/scripts/startup/bl_operators/geometry_nodes.py b/scripts/startup/bl_operators/geometry_nodes.py index ce63ec72851..58417b35de0 100644 --- a/scripts/startup/bl_operators/geometry_nodes.py +++ b/scripts/startup/bl_operators/geometry_nodes.py @@ -457,6 +457,61 @@ class RepeatZoneItemMoveOperator(RepeatZoneOperator, ZoneMoveItemOperator, Opera bl_options = {'REGISTER', 'UNDO'} +class ForEachZoneOperator(ZoneOperator): + input_node_type = 'GeometryNodeForEachInput' + output_node_type = 'GeometryNodeForEachOutput' + + +class ForEachInputOperator(ForEachZoneOperator): + items_name = "input_items" + active_index_name = "input_active_index" + + +class ForEachInputItemAddOperator(ForEachInputOperator, ZoneItemAddOperator, Operator): + """Add an item to the for-each input node""" + bl_idname = "node.foreach_zone_input_item_add" + bl_label = "Add For-Each Input" + bl_options = {'REGISTER', 'UNDO'} + +class ForEachInputItemRemoveOperator(ForEachInputOperator, ZoneItemRemoveOperator, Operator): + """Remove an item from the for-each input node""" + bl_idname = "node.foreach_zone_input_item_remove" + bl_label = "Remove For-Each Input" + bl_options = {'REGISTER', 'UNDO'} + + +class ForEachInputItemMoveOperator(ForEachInputOperator, ZoneMoveItemOperator, Operator): + """Move an item up or down in the list""" + bl_idname = "node.foreach_zone_input_item_move" + bl_label = "Move For-Each Input" + bl_options = {'REGISTER', 'UNDO'} + + +class ForEachOutputOperator(ForEachZoneOperator): + items_name = "output_items" + active_index_name = "output_active_index" + + +class ForEachOutputItemAddOperator(ForEachOutputOperator, ZoneItemAddOperator, Operator): + """Add an item to the for-each output node""" + bl_idname = "node.foreach_zone_output_item_add" + bl_label = "Add For-Each Output" + bl_options = {'REGISTER', 'UNDO'} + +class ForEachOutputItemRemoveOperator(ForEachOutputOperator, ZoneItemRemoveOperator, Operator): + """Remove an item from the for-each output node""" + bl_idname = "node.foreach_zone_output_item_remove" + bl_label = "Remove For-Each Output" + bl_options = {'REGISTER', 'UNDO'} + + +class ForEachOutputItemMoveOperator(ForEachOutputOperator, ZoneMoveItemOperator, Operator): + """Move an item up or down in the list""" + bl_idname = "node.foreach_zone_output_item_move" + bl_label = "Move For-Each Output" + bl_options = {'REGISTER', 'UNDO'} + + classes = ( NewGeometryNodesModifier, NewGeometryNodeTreeAssign, @@ -468,4 +523,10 @@ classes = ( RepeatZoneItemAddOperator, RepeatZoneItemRemoveOperator, RepeatZoneItemMoveOperator, + ForEachInputItemAddOperator, + ForEachInputItemRemoveOperator, + ForEachInputItemMoveOperator, + ForEachOutputItemAddOperator, + ForEachOutputItemRemoveOperator, + ForEachOutputItemMoveOperator, ) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 1e2c676eeb3..f226964adb1 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -1198,25 +1198,25 @@ class NODE_PT_foreach_zone_items(Panel): output_node, "input_active_index") - # ops_col = split.column() + ops_col = split.column() - # add_remove_col = ops_col.column(align=True) - # add_remove_col.operator("node.repeat_zone_item_add", icon='ADD', text="") - # add_remove_col.operator("node.repeat_zone_item_remove", icon='REMOVE', text="") + add_remove_col = ops_col.column(align=True) + add_remove_col.operator("node.foreach_zone_input_item_add", icon='ADD', text="") + add_remove_col.operator("node.foreach_zone_input_item_remove", icon='REMOVE', text="") - # ops_col.separator() + ops_col.separator() - # up_down_col = ops_col.column(align=True) - # props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_UP', text="") - # props.direction = 'UP' - # props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_DOWN', text="") - # props.direction = 'DOWN' + up_down_col = ops_col.column(align=True) + props = up_down_col.operator("node.foreach_zone_input_item_move", icon='TRIA_UP', text="") + props.direction = 'UP' + props = up_down_col.operator("node.foreach_zone_input_item_move", icon='TRIA_DOWN', text="") + props.direction = 'DOWN' - # active_item = output_node.active_item - # if active_item is not None: - # layout.use_property_split = True - # layout.use_property_decorate = False - # layout.prop(active_item, "socket_type") + active_item = output_node.input_active_item + if active_item is not None: + layout.use_property_split = True + layout.use_property_decorate = False + layout.prop(active_item, "socket_type") def draw_output_items(self, layout, output_node): split = layout.row() @@ -1228,25 +1228,25 @@ class NODE_PT_foreach_zone_items(Panel): output_node, "output_active_index") - # ops_col = split.column() + ops_col = split.column() - # add_remove_col = ops_col.column(align=True) - # add_remove_col.operator("node.repeat_zone_item_add", icon='ADD', text="") - # add_remove_col.operator("node.repeat_zone_item_remove", icon='REMOVE', text="") + add_remove_col = ops_col.column(align=True) + add_remove_col.operator("node.foreach_zone_output_item_add", icon='ADD', text="") + add_remove_col.operator("node.foreach_zone_output_item_remove", icon='REMOVE', text="") - # ops_col.separator() + ops_col.separator() - # up_down_col = ops_col.column(align=True) - # props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_UP', text="") - # props.direction = 'UP' - # props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_DOWN', text="") - # props.direction = 'DOWN' + up_down_col = ops_col.column(align=True) + props = up_down_col.operator("node.foreach_zone_output_item_move", icon='TRIA_UP', text="") + props.direction = 'UP' + props = up_down_col.operator("node.foreach_zone_output_item_move", icon='TRIA_DOWN', text="") + props.direction = 'DOWN' - # active_item = output_node.active_item - # if active_item is not None: - # layout.use_property_split = True - # layout.use_property_decorate = False - # layout.prop(active_item, "socket_type") + active_item = output_node.output_active_item + if active_item is not None: + layout.use_property_split = True + layout.use_property_decorate = False + layout.prop(active_item, "socket_type") diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index bb5040de91e..3a7177f8cab 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -29,6 +29,7 @@ void build_node_declaration_dynamic(const bNodeTree &node_tree, r_declaration.items.clear(); r_declaration.inputs.clear(); r_declaration.outputs.clear(); + r_declaration.skip_updating_sockets = false; node.typeinfo->declare_dynamic(node_tree, node, r_declaration); } -- 2.30.2 From f64486b3e216c9ca44575f70c4829de8ffff9f5c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 16:19:37 +0200 Subject: [PATCH 20/38] add initial for-each mode --- source/blender/makesdna/DNA_node_types.h | 11 +++++++++- .../blender/makesrna/intern/rna_nodetree.cc | 19 ++++++++++++++++++ .../geometry/nodes/node_geo_foreach_input.cc | 20 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index a26b0692256..497eeb2361e 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1876,7 +1876,10 @@ typedef struct NodeGeometryForEachOutput { int output_items_num; int output_active_index; int output_next_identifier; - char _pad2[4]; + + /** #GeometryNodeForEachMode. */ + uint8_t mode; + char _pad2[3]; #ifdef __cplusplus blender::Span input_items_span() const @@ -2804,3 +2807,9 @@ typedef enum NodeCombSepColorMode { NODE_COMBSEP_COLOR_HSV = 1, NODE_COMBSEP_COLOR_HSL = 2, } NodeCombSepColorMode; + +typedef enum GeometryNodeForEachMode { + GEO_NODE_FOR_EACH_MODE_INDEX = 0, + GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT = 1, + GEO_NODE_FOR_EACH_MODE_INSTANCE = 2, +} GeometryNodeForEachMode; diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 6f629c680c6..7824252ea69 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -9010,8 +9010,27 @@ static void def_geo_foreach_output(StructRNA *srna) { PropertyRNA *prop; + static const EnumPropertyItem mode_items[] = { + {GEO_NODE_FOR_EACH_MODE_INDEX, "INDEX", 0, "Index", "Do something for each index"}, + {GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT, + "GEOMETRY_ELEMENT", + 0, + "Geometry Element", + "Do something for each element in a geometry on a specific domain"}, + {GEO_NODE_FOR_EACH_MODE_INSTANCE, + "INSTANCE", + 0, + "Instance", + "Do something for each unique top level instance"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + RNA_def_struct_sdna_from(srna, "NodeGeometryForEachOutput", "storage"); + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + /* TODO: Handle update. */ + prop = RNA_def_property(srna, "input_items", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "input_items", "input_items_num"); RNA_def_property_struct_type(prop, "ForEachInputItem"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc index 7f1625f2bb4..e61e72d3a42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc @@ -14,6 +14,9 @@ #include "NOD_socket.hh" #include "NOD_zone_socket_items.hh" +#include "RNA_access.hh" +#include "RNA_prototypes.h" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_foreach_input_cc { @@ -91,6 +94,22 @@ static bool node_insert_link(bNodeTree *tree, bNode *node, bNodeLink *link) *tree, *node, *output_node, *link); } +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + bNodeTree &tree = *reinterpret_cast(ptr->owner_id); + bNode &input_node = *static_cast(ptr->data); + const NodeGeometryForEachInput &input_storage = node_storage(input_node); + bNode *output_node = tree.node_by_id(input_storage.output_node_id); + if (output_node == nullptr) { + return; + } + PointerRNA output_node_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Node, output_node); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, &output_node_ptr, "mode", UI_ITEM_NONE, IFACE_("Iterate over"), ICON_NONE); +} + static void node_register() { static bNodeType ntype; @@ -99,6 +118,7 @@ static void node_register() ntype.declare_dynamic = node_declare_dynamic; ntype.gather_link_search_ops = nullptr; ntype.insert_link = node_insert_link; + ntype.draw_buttons = node_layout; node_type_storage( &ntype, "NodeGeometryForEachInput", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); -- 2.30.2 From b45bdc5f8a362330a0f9073ad83be509a0507fa2 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 18:18:51 +0200 Subject: [PATCH 21/38] depend on node to find supported socket types --- .../blender/makesrna/intern/rna_nodetree.cc | 14 ++++++++++---- source/blender/nodes/NOD_socket_items.hh | 4 ++-- source/blender/nodes/NOD_zone_socket_items.hh | 10 ++++++---- .../nodes/geometry/node_geometry_util.hh | 6 ++++-- .../geometry/nodes/node_geo_repeat_input.cc | 2 +- .../geometry/nodes/node_geo_repeat_output.cc | 19 ++++++++++++------- .../nodes/node_geo_simulation_input.cc | 3 ++- .../nodes/node_geo_simulation_output.cc | 15 ++++++++++----- 8 files changed, 47 insertions(+), 26 deletions(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 7824252ea69..230980099bc 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -3269,14 +3269,20 @@ static void rna_Node_ItemArray_item_update(Main *bmain, Scene * /*scene*/, Point template static const EnumPropertyItem *rna_Node_ItemArray_socket_type_itemf(bContext * /*C*/, - PointerRNA * /*ptr*/, + PointerRNA *ptr, PropertyRNA * /*prop*/, bool *r_free) { + using ItemT = typename Accessor::ItemT; + bNodeTree &ntree = *reinterpret_cast(ptr->owner_id); + ItemT &item = *static_cast(ptr->data); + const bNode *node = blender::nodes::socket_items::find_node_by_item(ntree, item); + BLI_assert(node != nullptr); + *r_free = true; return itemf_function_check( - rna_enum_node_socket_data_type_items, [](const EnumPropertyItem *item) { - return Accessor::supports_socket_type(eNodeSocketDatatype(item->value)); + rna_enum_node_socket_data_type_items, [&](const EnumPropertyItem *item) { + return Accessor::supports_socket_type(*node, eNodeSocketDatatype(item->value)); }); } @@ -3305,7 +3311,7 @@ typename Accessor::ItemT *rna_Node_ItemArray_new_with_socket_and_name( ID *id, bNode *node, Main *bmain, ReportList *reports, int socket_type, const char *name) { using ItemT = typename Accessor::ItemT; - if (!Accessor::supports_socket_type(eNodeSocketDatatype(socket_type))) { + if (!Accessor::supports_socket_type(*node, eNodeSocketDatatype(socket_type))) { BKE_report(reports, RPT_ERROR, "Unable to create item with this socket type"); return nullptr; } diff --git a/source/blender/nodes/NOD_socket_items.hh b/source/blender/nodes/NOD_socket_items.hh index a2ecb7a228f..b1f69dab712 100644 --- a/source/blender/nodes/NOD_socket_items.hh +++ b/source/blender/nodes/NOD_socket_items.hh @@ -217,7 +217,7 @@ inline typename Accessor::ItemT *add_item_with_socket_and_name( bNode &node, const eNodeSocketDatatype socket_type, const char *name) { using ItemT = typename Accessor::ItemT; - BLI_assert(Accessor::supports_socket_type(socket_type)); + BLI_assert(Accessor::supports_socket_type(node, socket_type)); SocketItemsRef array = Accessor::get_items_from_node(node); @@ -263,7 +263,7 @@ template return false; } const eNodeSocketDatatype socket_type = eNodeSocketDatatype(src_socket->type); - if (!Accessor::supports_socket_type(socket_type)) { + if (!Accessor::supports_socket_type(storage_node, socket_type)) { return false; } const ItemT *item = add_item_with_socket_and_name( diff --git a/source/blender/nodes/NOD_zone_socket_items.hh b/source/blender/nodes/NOD_zone_socket_items.hh index 89e5b5978b0..00d2b0cd95d 100644 --- a/source/blender/nodes/NOD_zone_socket_items.hh +++ b/source/blender/nodes/NOD_zone_socket_items.hh @@ -47,7 +47,7 @@ struct SimulationItemsAccessor { { return &item.name; } - static bool supports_socket_type(const eNodeSocketDatatype socket_type) + static bool supports_socket_type(const bNode & /*node*/, const eNodeSocketDatatype socket_type) { return ELEM(socket_type, SOCK_FLOAT, @@ -109,7 +109,7 @@ struct RepeatItemsAccessor { { return &item.name; } - static bool supports_socket_type(const eNodeSocketDatatype socket_type) + static bool supports_socket_type(const bNode & /*node*/, const eNodeSocketDatatype socket_type) { return ELEM(socket_type, SOCK_FLOAT, @@ -172,7 +172,8 @@ struct ForEachInputItemsAccessor { { return &item.name; } - static bool supports_socket_type(const eNodeSocketDatatype /*socket_type*/) + static bool supports_socket_type(const bNode & /*node*/, + const eNodeSocketDatatype /*socket_type*/) { /* TODO */ return true; @@ -223,7 +224,8 @@ struct ForEachOutputItemsAccessor { { return &item.name; } - static bool supports_socket_type(const eNodeSocketDatatype /*socket_type*/) + static bool supports_socket_type(const bNode & /*node*/, + const eNodeSocketDatatype /*socket_type*/) { /* TODO */ return true; diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 488e1bf0108..da1449dc945 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -100,7 +100,8 @@ class EvaluateAtIndexInput final : public bke::GeometryFieldInput { std::string socket_identifier_for_simulation_item(const NodeSimulationItem &item); -void socket_declarations_for_simulation_items(Span items, +void socket_declarations_for_simulation_items(const bNode &output_node, + Span items, NodeDeclaration &r_declaration); const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type); const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item); @@ -125,7 +126,8 @@ void copy_with_checked_indices(const GVArray &src, const IndexMask &mask, GMutableSpan dst); -void socket_declarations_for_repeat_items(const Span items, +void socket_declarations_for_repeat_items(const bNode &output_node, + const Span items, NodeDeclaration &r_declaration); namespace enums { diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc index c4282e533c9..16ebc1e5a07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc @@ -32,7 +32,7 @@ static void node_declare_dynamic(const bNodeTree &tree, if (output_node != nullptr) { const NodeGeometryRepeatOutput &output_storage = *static_cast(output_node->storage); - socket_declarations_for_repeat_items(output_storage.items_span(), r_declaration); + socket_declarations_for_repeat_items(*output_node, output_storage.items_span(), r_declaration); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc index 302a488a8ea..d716057ec58 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc @@ -23,11 +23,14 @@ namespace blender::nodes { static std::unique_ptr socket_declaration_for_repeat_item( - const NodeRepeatItem &item, const eNodeSocketInOut in_out, const int corresponding_input = -1) + const bNode &output_node, + const NodeRepeatItem &item, + const eNodeSocketInOut in_out, + const int corresponding_input = -1) { const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); - BLI_assert(RepeatItemsAccessor::supports_socket_type(socket_type)); - + BLI_assert(RepeatItemsAccessor::supports_socket_type(output_node, socket_type)); + std::unique_ptr decl = make_declaration_for_socket_type(socket_type); BLI_assert(decl); @@ -47,17 +50,19 @@ static std::unique_ptr socket_declaration_for_repeat_item( return decl; } -void socket_declarations_for_repeat_items(const Span items, +void socket_declarations_for_repeat_items(const bNode &output_node, + const Span items, NodeDeclaration &r_declaration) { for (const int i : items.index_range()) { const NodeRepeatItem &item = items[i]; - SocketDeclarationPtr input_decl = socket_declaration_for_repeat_item(item, SOCK_IN); + SocketDeclarationPtr input_decl = socket_declaration_for_repeat_item( + output_node, item, SOCK_IN); r_declaration.inputs.append(input_decl.get()); r_declaration.items.append(std::move(input_decl)); SocketDeclarationPtr output_decl = socket_declaration_for_repeat_item( - item, SOCK_OUT, r_declaration.inputs.size() - 1); + output_node, item, SOCK_OUT, r_declaration.inputs.size() - 1); r_declaration.outputs.append(output_decl.get()); r_declaration.items.append(std::move(output_decl)); } @@ -79,7 +84,7 @@ static void node_declare_dynamic(const bNodeTree & /*node_tree*/, NodeDeclaration &r_declaration) { const NodeGeometryRepeatOutput &storage = node_storage(node); - socket_declarations_for_repeat_items(storage.items_span(), r_declaration); + socket_declarations_for_repeat_items(node, storage.items_span(), r_declaration); } static void node_init(bNodeTree * /*tree*/, bNode *node) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index e13a24d7235..318f7ee777d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -200,7 +200,8 @@ static void node_declare_dynamic(const bNodeTree &node_tree, const NodeGeometrySimulationOutput &storage = *static_cast( output_node->storage); - socket_declarations_for_simulation_items({storage.items, storage.items_num}, r_declaration); + socket_declarations_for_simulation_items( + *output_node, {storage.items, storage.items_num}, r_declaration); } static void node_init(bNodeTree * /*tree*/, bNode *node) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 0741c2f70b3..995c9bea3d3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -56,12 +56,14 @@ std::string socket_identifier_for_simulation_item(const NodeSimulationItem &item } static std::unique_ptr socket_declaration_for_simulation_item( + const bNode &output_node, const NodeSimulationItem &item, const eNodeSocketInOut in_out, const int corresponding_input = -1) { const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); - BLI_assert(SimulationItemsAccessor::supports_socket_type(socket_type)); + BLI_assert(SimulationItemsAccessor::supports_socket_type(output_node, socket_type)); + UNUSED_VARS_NDEBUG(output_node); std::unique_ptr decl = make_declaration_for_socket_type(socket_type); BLI_assert(decl); @@ -82,15 +84,17 @@ static std::unique_ptr socket_declaration_for_simulation_item return decl; } -void socket_declarations_for_simulation_items(const Span items, +void socket_declarations_for_simulation_items(const bNode &output_node, + const Span items, NodeDeclaration &r_declaration) { const int inputs_offset = r_declaration.inputs.size(); for (const int i : items.index_range()) { const NodeSimulationItem &item = items[i]; - SocketDeclarationPtr input_decl = socket_declaration_for_simulation_item(item, SOCK_IN); + SocketDeclarationPtr input_decl = socket_declaration_for_simulation_item( + output_node, item, SOCK_IN); SocketDeclarationPtr output_decl = socket_declaration_for_simulation_item( - item, SOCK_OUT, inputs_offset + i); + output_node, item, SOCK_OUT, inputs_offset + i); r_declaration.inputs.append(input_decl.get()); r_declaration.items.append(std::move(input_decl)); r_declaration.outputs.append(output_decl.get()); @@ -742,7 +746,8 @@ static void node_declare_dynamic(const bNodeTree & /*node_tree*/, r_declaration.inputs.append(skip_decl.get()); r_declaration.items.append(std::move(skip_decl)); } - socket_declarations_for_simulation_items({storage.items, storage.items_num}, r_declaration); + socket_declarations_for_simulation_items( + node, {storage.items, storage.items_num}, r_declaration); } static void node_init(bNodeTree * /*tree*/, bNode *node) -- 2.30.2 From e14ce00b7000054caf98ae2703d29d2a6e5e1ee9 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 18:46:35 +0200 Subject: [PATCH 22/38] remove unsupported sockets when switching mode --- .../startup/bl_operators/geometry_nodes.py | 2 + .../blender/makesrna/intern/rna_nodetree.cc | 15 +++++- source/blender/nodes/NOD_socket_items.hh | 50 +++++++++++++++++++ source/blender/nodes/NOD_zone_socket_items.hh | 32 +++++++++--- .../geometry/nodes/node_geo_repeat_output.cc | 1 + 5 files changed, 91 insertions(+), 9 deletions(-) diff --git a/scripts/startup/bl_operators/geometry_nodes.py b/scripts/startup/bl_operators/geometry_nodes.py index 58417b35de0..8d16e458c9e 100644 --- a/scripts/startup/bl_operators/geometry_nodes.py +++ b/scripts/startup/bl_operators/geometry_nodes.py @@ -473,6 +473,8 @@ class ForEachInputItemAddOperator(ForEachInputOperator, ZoneItemAddOperator, Ope bl_label = "Add For-Each Input" bl_options = {'REGISTER', 'UNDO'} + default_socket_type = 'FLOAT' + class ForEachInputItemRemoveOperator(ForEachInputOperator, ZoneItemRemoveOperator, Operator): """Remove an item from the for-each input node""" bl_idname = "node.foreach_zone_input_item_remove" diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 230980099bc..a5c04fb3a2c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -3326,6 +3326,18 @@ typename Accessor::ItemT *rna_Node_ItemArray_new_with_socket_and_name( return new_item; } +static void rna_ForEachOutputNode_mode_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) +{ + bNodeTree &ntree = *reinterpret_cast(ptr->owner_id); + bNode &node = *static_cast(ptr->data); + + blender::nodes::socket_items::remove_unsupported_socket_types(node); + blender::nodes::socket_items::remove_unsupported_socket_types(node); + + BKE_ntree_update_tag_node_property(&ntree, &node); + ED_node_tree_propagate_change(nullptr, bmain, &ntree); +} + /* ******** Node Socket Types ******** */ static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter) @@ -9035,7 +9047,8 @@ static void def_geo_foreach_output(StructRNA *srna) prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, mode_items); - /* TODO: Handle update. */ + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_NODE, "rna_ForEachOutputNode_mode_update"); prop = RNA_def_property(srna, "input_items", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "input_items", "input_items_num"); diff --git a/source/blender/nodes/NOD_socket_items.hh b/source/blender/nodes/NOD_socket_items.hh index b1f69dab712..caa46e34ea2 100644 --- a/source/blender/nodes/NOD_socket_items.hh +++ b/source/blender/nodes/NOD_socket_items.hh @@ -137,6 +137,56 @@ inline void move_item(T *items, const int items_num, const int from_index, const } } +/** + * Low level utility to remove all socket items for which the predicate is true. + */ +template +inline void remove_if(T **items, + int *items_num, + int *active_index, + void (*destruct_item)(T *), + const FunctionRef predicate) +{ + static_assert(std::is_trivial_v); + T *old_items = *items; + const int old_items_num = *items_num; + Vector indices_to_keep; + for (const int i : IndexRange(old_items_num)) { + T &item = old_items[i]; + if (predicate(item)) { + destruct_item(&item); + } + else { + indices_to_keep.append(i); + } + } + const int new_items_num = indices_to_keep.size(); + if (old_items_num == new_items_num) { + return; + } + T *new_items = MEM_cnew_array(new_items_num, __func__); + for (const int i : IndexRange(new_items_num)) { + new_items[i] = old_items[indices_to_keep[i]]; + } + MEM_SAFE_FREE(old_items); + + *items = new_items; + *items_num = new_items_num; + *active_index = std::max(0, std::min(*active_index, new_items_num - 1)); +} + +template inline void remove_unsupported_socket_types(bNode &node) +{ + using ItemT = typename Accessor::ItemT; + SocketItemsRef ref = Accessor::get_items_from_node(node); + remove_if( + ref.items, ref.items_num, ref.active_index, Accessor::destruct_item, [&](const ItemT &item) { + const eNodeSocketDatatype socket_type = eNodeSocketDatatype( + *Accessor::get_socket_type(const_cast(item))); + return !Accessor::supports_socket_type(node, socket_type); + }); +} + /** * Destruct all the items and the free the array itself. */ diff --git a/source/blender/nodes/NOD_zone_socket_items.hh b/source/blender/nodes/NOD_zone_socket_items.hh index 00d2b0cd95d..371dd939dd3 100644 --- a/source/blender/nodes/NOD_zone_socket_items.hh +++ b/source/blender/nodes/NOD_zone_socket_items.hh @@ -172,11 +172,19 @@ struct ForEachInputItemsAccessor { { return &item.name; } - static bool supports_socket_type(const bNode & /*node*/, - const eNodeSocketDatatype /*socket_type*/) + static bool supports_socket_type(const bNode &node, const eNodeSocketDatatype socket_type) { - /* TODO */ - return true; + auto *storage = static_cast(node.storage); + switch (GeometryNodeForEachMode(storage->mode)) { + case GEO_NODE_FOR_EACH_MODE_INDEX: + case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT: { + return socket_type_supports_fields(socket_type); + } + case GEO_NODE_FOR_EACH_MODE_INSTANCE: { + return false; + } + } + return false; } static void init_with_socket_type_and_name(bNode &node, NodeForEachInputItem &item, @@ -224,11 +232,19 @@ struct ForEachOutputItemsAccessor { { return &item.name; } - static bool supports_socket_type(const bNode & /*node*/, - const eNodeSocketDatatype /*socket_type*/) + static bool supports_socket_type(const bNode &node, const eNodeSocketDatatype socket_type) { - /* TODO */ - return true; + auto *storage = static_cast(node.storage); + switch (GeometryNodeForEachMode(storage->mode)) { + case GEO_NODE_FOR_EACH_MODE_INDEX: + case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT: { + return socket_type == SOCK_GEOMETRY || socket_type_supports_fields(socket_type); + } + case GEO_NODE_FOR_EACH_MODE_INSTANCE: { + return false; + } + } + return false; } static void init_with_socket_type_and_name(bNode &node, NodeForEachOutputItem &item, diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc index d716057ec58..74a1898175f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc @@ -30,6 +30,7 @@ static std::unique_ptr socket_declaration_for_repeat_item( { const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); BLI_assert(RepeatItemsAccessor::supports_socket_type(output_node, socket_type)); + UNUSED_VARS_NDEBUG(output_node); std::unique_ptr decl = make_declaration_for_socket_type(socket_type); BLI_assert(decl); -- 2.30.2 From 78614fba9f31c41a5fec0c45fc703f736025fcb3 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 18:53:20 +0200 Subject: [PATCH 23/38] add default element --- source/blender/nodes/NOD_zone_socket_items.hh | 6 ++---- .../nodes/geometry/nodes/node_geo_foreach_output.cc | 8 ++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/NOD_zone_socket_items.hh b/source/blender/nodes/NOD_zone_socket_items.hh index 371dd939dd3..15a9f3b3d57 100644 --- a/source/blender/nodes/NOD_zone_socket_items.hh +++ b/source/blender/nodes/NOD_zone_socket_items.hh @@ -237,11 +237,9 @@ struct ForEachOutputItemsAccessor { auto *storage = static_cast(node.storage); switch (GeometryNodeForEachMode(storage->mode)) { case GEO_NODE_FOR_EACH_MODE_INDEX: - case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT: { - return socket_type == SOCK_GEOMETRY || socket_type_supports_fields(socket_type); - } + case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT: case GEO_NODE_FOR_EACH_MODE_INSTANCE: { - return false; + return socket_type == SOCK_GEOMETRY || socket_type_supports_fields(socket_type); } } return false; diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc index 0b3442c708c..b21d766f512 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc @@ -68,6 +68,14 @@ static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryForEachOutput *data = MEM_cnew(__func__); + data->output_items = MEM_cnew_array(1, __func__); + data->output_items_num = 1; + + NodeForEachOutputItem &item = data->output_items[0]; + item.name = BLI_strdup(DATA_("Geometry")); + item.socket_type = SOCK_GEOMETRY; + item.identifier = data->output_next_identifier++; + node->storage = data; } -- 2.30.2 From 50a11a67104ab094d57e7376fc5e451f619baf0b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 19:01:05 +0200 Subject: [PATCH 24/38] add standard inputs depending on mode --- .../geometry/nodes/node_geo_foreach_input.cc | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc index e61e72d3a42..a4c543c39fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc @@ -33,13 +33,37 @@ static void node_declare_dynamic(const bNodeTree &tree, r_declaration.skip_updating_sockets = true; return; } - { - NodeDeclarationBuilder b{r_declaration}; - b.add_input("Amount").min(0).default_value(1); - b.add_output("Index"); - } const auto &output_storage = *static_cast( output_node->storage); + const GeometryNodeForEachMode mode = GeometryNodeForEachMode(output_storage.mode); + { + /* Add standard inputs. */ + NodeDeclarationBuilder b{r_declaration}; + switch (mode) { + case GEO_NODE_FOR_EACH_MODE_INDEX: { + b.add_input("Amount").min(0).default_value(1); + b.add_output("Index"); + break; + } + case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT: { + b.add_input("Geometry"); + b.add_input("Selection").supports_field().default_value(true).hide_value(true); + b.add_output("Index"); + b.add_output("Element"); + break; + } + case GEO_NODE_FOR_EACH_MODE_INSTANCE: { + b.add_input("Instances"); + b.add_output("Geometry"); + break; + } + } + } + if (mode == GEO_NODE_FOR_EACH_MODE_INSTANCE) { + /* Other inputs are not allowed in this mode. */ + return; + } + /* Add dynamic sockets. */ for (const int i : IndexRange(output_storage.input_items_num)) { const NodeForEachInputItem &item = output_storage.input_items[i]; const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); @@ -107,7 +131,7 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, &output_node_ptr, "mode", UI_ITEM_NONE, IFACE_("Iterate over"), ICON_NONE); + uiItemR(layout, &output_node_ptr, "mode", UI_ITEM_NONE, "", ICON_NONE); } static void node_register() -- 2.30.2 From ebf1840c6122b051ec0688c2432eb92ecba274c5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 4 Oct 2023 19:15:03 +0200 Subject: [PATCH 25/38] add domain --- source/blender/makesdna/DNA_node_types.h | 4 +++- source/blender/makesrna/intern/rna_nodetree.cc | 5 +++++ .../nodes/geometry/nodes/node_geo_foreach_input.cc | 12 ++++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 497eeb2361e..275bc2f11a6 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1879,7 +1879,9 @@ typedef struct NodeGeometryForEachOutput { /** #GeometryNodeForEachMode. */ uint8_t mode; - char _pad2[3]; + /** #eAttrDomain. */ + uint8_t domain; + char _pad2[2]; #ifdef __cplusplus blender::Span input_items_span() const diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index a5c04fb3a2c..6e48cf2504d 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -9050,6 +9050,11 @@ static void def_geo_foreach_output(StructRNA *srna) RNA_def_property_ui_text(prop, "Mode", ""); RNA_def_property_update(prop, NC_NODE, "rna_ForEachOutputNode_mode_update"); + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_ui_text(prop, "Domain", ""); + RNA_def_property_update(prop, NC_NODE, "rna_Node_update"); + prop = RNA_def_property(srna, "input_items", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "input_items", "input_items_num"); RNA_def_property_struct_type(prop, "ForEachInputItem"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc index a4c543c39fe..70c30ddbf60 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc @@ -49,7 +49,9 @@ static void node_declare_dynamic(const bNodeTree &tree, b.add_input("Geometry"); b.add_input("Selection").supports_field().default_value(true).hide_value(true); b.add_output("Index"); - b.add_output("Element"); + if (output_storage.domain != ATTR_DOMAIN_CORNER) { + b.add_output("Element"); + } break; } case GEO_NODE_FOR_EACH_MODE_INSTANCE: { @@ -127,11 +129,17 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) if (output_node == nullptr) { return; } + const auto &output_storage = *static_cast( + output_node->storage); PointerRNA output_node_ptr = RNA_pointer_create(ptr->owner_id, &RNA_Node, output_node); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, &output_node_ptr, "mode", UI_ITEM_NONE, "", ICON_NONE); + uiLayout *col = uiLayoutColumn(layout, false); + uiItemR(col, &output_node_ptr, "mode", UI_ITEM_NONE, "", ICON_NONE); + if (output_storage.mode == GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT) { + uiItemR(col, &output_node_ptr, "domain", UI_ITEM_NONE, "Domain", ICON_NONE); + } } static void node_register() -- 2.30.2 From b73b6030931cc86f11bc4ad2c51b19c4dfed0709 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Oct 2023 18:09:20 +0200 Subject: [PATCH 26/38] propagate updates from input to output zone nodes --- .../blenkernel/intern/node_tree_update.cc | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index 0af746ce420..e7358bd08b9 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -984,15 +984,30 @@ class NodeTreeMainUpdater { } } else { - bool all_available_inputs_computed = true; + Vector src_sockets; for (const bNodeSocket *input_socket : node.input_sockets()) { if (input_socket->is_available()) { - if (!hash_by_socket_id[input_socket->index_in_tree()].has_value()) { - sockets_to_check.push(input_socket); - all_available_inputs_computed = false; + src_sockets.append(input_socket); + } + } + if (all_zone_output_node_types().contains(node.type)) { + bool all_zone_inputs_computed = true; + const bNodeZoneType &zone_type = *zone_type_by_node_type(node.type); + if (const bNode *zone_input_node = zone_type.get_corresponding_input(tree, node)) { + for (const bNodeSocket *input_socket : node.input_sockets()) { + if (input_socket->is_available()) { + src_sockets.append(input_socket); + } } } } + bool all_available_inputs_computed = true; + for (const bNodeSocket *socket : src_sockets) { + if (!hash_by_socket_id[socket->index_in_tree()].has_value()) { + sockets_to_check.push(socket); + all_available_inputs_computed = false; + } + } if (!all_available_inputs_computed) { continue; } @@ -1015,10 +1030,10 @@ class NodeTreeMainUpdater { } else { socket_hash = get_socket_ptr_hash(socket); - for (const bNodeSocket *input_socket : node.input_sockets()) { - if (input_socket->is_available()) { - const uint32_t input_socket_hash = *hash_by_socket_id[input_socket->index_in_tree()]; - socket_hash = noise::hash(socket_hash, input_socket_hash); + for (const bNodeSocket *src_socket : src_sockets) { + if (src_socket->is_available()) { + const uint32_t src_socket_hash = *hash_by_socket_id[src_socket->index_in_tree()]; + socket_hash = noise::hash(socket_hash, src_socket_hash); } } @@ -1096,6 +1111,21 @@ class NodeTreeMainUpdater { } } } + if (all_zone_output_node_types().contains(node.type)) { + const bNodeZoneType &zone_type = *zone_type_by_node_type(node.type); + if (const bNode *zone_input_node = zone_type.get_corresponding_input(tree, node)) { + if (zone_input_node->runtime->changed_flag != NTREE_CHANGED_NOTHING) { + return true; + } + for (const bNodeSocket *input_socket : zone_input_node->input_sockets()) { + bool &pushed = pushed_by_socket_id[input_socket->index_in_tree()]; + if (!pushed) { + sockets_to_check.push(input_socket); + pushed = true; + } + } + } + } /* The Normal node has a special case, because the value stored in the first output socket * is used as input in the node. */ if (node.type == SH_NODE_NORMAL && socket.index() == 1) { -- 2.30.2 From 172ed57c075b6f59ab95faa53f6be65298670d97 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Oct 2023 18:09:32 +0200 Subject: [PATCH 27/38] remove old eval code --- .../intern/geometry_nodes_lazy_function.cc | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 1056d1b6d02..dc6d4c04115 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1908,32 +1908,9 @@ class LazyFunctionForForeachZone : public LazyFunction { auto &user_data = *static_cast(context.user_data); // auto &local_user_data = *static_cast(context.local_user_data); - const int amount = params.get_input(zone_info_.indices.inputs.main[0]); - Array output_values(amount, NoInitialization{}); - threading::parallel_for(IndexRange(amount), 32, [&](const IndexRange range) { - for (const int i : range) { - GeometrySet &output_geometry = output_values[i]; - bool usage_index; + const int amount = params.get_input>(0).as_value(); + std::cout << amount << "\n"; - const bke::ForEachZoneComputeContext body_compute_context{ - user_data.compute_context, *zone_.output_node, i}; - GeoNodesLFUserData body_user_data = user_data; - body_user_data.compute_context = &body_compute_context; - if (user_data.modifier_data && user_data.modifier_data->socket_log_contexts) { - body_user_data.log_socket_values = - user_data.modifier_data->socket_log_contexts->contains(body_compute_context.hash()); - } - - GeoNodesLFLocalUserData body_local_user_data{body_user_data}; - lf::execute_lazy_function_eagerly(*body_fn_.function, - &body_user_data, - &body_local_user_data, - std::make_tuple(ValueOrField(i), true), - std::make_tuple(&usage_index, &output_geometry)); - } - }); - GeometrySet reduced_geometry = geometry::join_geometries(output_values, {}); - params.set_output(zone_info_.indices.outputs.main[0], std::move(reduced_geometry)); params.set_default_remaining_outputs(); } -- 2.30.2 From 1d46f7490deb6582494c1d99e06e2b8a614e6b70 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Oct 2023 18:25:36 +0200 Subject: [PATCH 28/38] initial graph generation --- .../intern/geometry_nodes_lazy_function.cc | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index dc6d4c04115..e3909b5bf53 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1885,6 +1885,12 @@ class LazyFunctionForRepeatZone : public LazyFunction { } }; +struct ForEachEvalStorage { + LinearAllocator<> allocator; + lf::Graph graph; + std::optional graph_executor; +}; + class LazyFunctionForForeachZone : public LazyFunction { private: const bNodeTreeZone &zone_; @@ -1903,15 +1909,52 @@ class LazyFunctionForForeachZone : public LazyFunction { inputs_[zone_info.indices.inputs.main[0]].usage = lf::ValueUsage::Used; } + void *init_storage(LinearAllocator<> &allocator) const override + { + return allocator.construct().release(); + } + void destruct_storage(void *storage) const override + { + ForEachEvalStorage *s = static_cast(storage); + std::destroy_at(s); + } + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override { auto &user_data = *static_cast(context.user_data); - // auto &local_user_data = *static_cast(context.local_user_data); + auto &local_user_data = *static_cast(context.local_user_data); + + const auto &node_storage = *static_cast( + zone_.output_node->storage); + ForEachEvalStorage &eval_storage = *static_cast(context.storage); + + if (!eval_storage.graph_executor) { + this->initialize_execution_graph( + params, eval_storage, node_storage, user_data, local_user_data); + } + + params.set_default_remaining_outputs(); + } + + void initialize_execution_graph(lf::Params ¶ms, + ForEachEvalStorage &eval_storage, + const NodeGeometryForEachOutput &node_storage, + GeoNodesLFUserData &user_data, + GeoNodesLFLocalUserData &local_user_data) const + { + UNUSED_VARS(params, eval_storage, node_storage, user_data, local_user_data); const int amount = params.get_input>(0).as_value(); std::cout << amount << "\n"; - params.set_default_remaining_outputs(); + lf::Graph &lf_graph = eval_storage.graph; + + for ([[maybe_unused]] const int i : IndexRange(amount)) { + lf::FunctionNode &lf_node = lf_graph.add_function(*body_fn_.function); + UNUSED_VARS(lf_node); + } + + std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; } std::string input_name(const int i) const override -- 2.30.2 From 820156cfc8b416089427745295bc569b1c7515a6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Oct 2023 18:39:55 +0200 Subject: [PATCH 29/38] fix --- source/blender/blenkernel/intern/node_tree_update.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index e7358bd08b9..a7bfcf2b36e 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -991,10 +991,9 @@ class NodeTreeMainUpdater { } } if (all_zone_output_node_types().contains(node.type)) { - bool all_zone_inputs_computed = true; const bNodeZoneType &zone_type = *zone_type_by_node_type(node.type); if (const bNode *zone_input_node = zone_type.get_corresponding_input(tree, node)) { - for (const bNodeSocket *input_socket : node.input_sockets()) { + for (const bNodeSocket *input_socket : zone_input_node->input_sockets()) { if (input_socket->is_available()) { src_sockets.append(input_socket); } -- 2.30.2 From 99822b5089a8fb319dca7c2eecb2628ac8c7cebb Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Oct 2023 19:15:16 +0200 Subject: [PATCH 30/38] handle index input --- .../intern/geometry_nodes_lazy_function.cc | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 3a2641ea2ed..64fe0e60ed9 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1885,10 +1885,30 @@ class LazyFunctionForRepeatZone : public LazyFunction { } }; +class LazyFunctionForIndexInput : public lf::LazyFunction { + private: + const int amount_; + + public: + LazyFunctionForIndexInput(const int amount) : amount_(amount) + { + const CPPType &type = CPPType::get>(); + outputs_.resize(amount, lf::Output("Index", type)); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + for (const int i : IndexRange(amount_)) { + params.set_output(i, ValueOrField(i)); + } + } +}; + struct ForEachEvalStorage { LinearAllocator<> allocator; lf::Graph graph; std::optional graph_executor; + std::optional index_input_fn; }; class LazyFunctionForForeachZone : public LazyFunction { @@ -1949,9 +1969,29 @@ class LazyFunctionForForeachZone : public LazyFunction { lf::Graph &lf_graph = eval_storage.graph; + Vector lf_graph_inputs; + Vector lf_graph_outputs; + + for (const int i : inputs_.index_range()) { + const lf::Input &input = inputs_[i]; + lf_graph_inputs.append(&lf_graph.add_input(*input.type, input.debug_name)); + } + for (const int i : outputs_.index_range()) { + const lf::Output &output = outputs_[i]; + lf_graph_outputs.append(&lf_graph.add_output(*output.type, output.debug_name)); + } + + VectorSet lf_body_nodes; for ([[maybe_unused]] const int i : IndexRange(amount)) { lf::FunctionNode &lf_node = lf_graph.add_function(*body_fn_.function); - UNUSED_VARS(lf_node); + lf_body_nodes.add_new(&lf_node); + } + + eval_storage.index_input_fn.emplace(amount); + lf::FunctionNode &lf_index_input_node = lf_graph.add_function(*eval_storage.index_input_fn); + + for (const int i : IndexRange(amount)) { + lf_graph.add_link(lf_index_input_node.output(i), lf_body_nodes[i]->input(0)); } std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; -- 2.30.2 From 6f766c86388813b5d7a61eec273c6e87730e6027 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Oct 2023 19:19:50 +0200 Subject: [PATCH 31/38] progress with graph --- .../blender/nodes/intern/geometry_nodes_lazy_function.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 64fe0e60ed9..0cc11a8db19 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1909,6 +1909,7 @@ struct ForEachEvalStorage { lf::Graph graph; std::optional graph_executor; std::optional index_input_fn; + std::optional or_fn; }; class LazyFunctionForForeachZone : public LazyFunction { @@ -1990,8 +1991,14 @@ class LazyFunctionForForeachZone : public LazyFunction { eval_storage.index_input_fn.emplace(amount); lf::FunctionNode &lf_index_input_node = lf_graph.add_function(*eval_storage.index_input_fn); + eval_storage.or_fn.emplace(amount); + lf::FunctionNode &lf_or_node = lf_graph.add_function(*eval_storage.or_fn); + for (const int i : IndexRange(amount)) { - lf_graph.add_link(lf_index_input_node.output(i), lf_body_nodes[i]->input(0)); + lf::FunctionNode &lf_body_node = *lf_body_nodes[i]; + lf_graph.add_link(lf_index_input_node.output(i), lf_body_node.input(0)); + lf_graph.add_link(*lf_graph_inputs[1], lf_body_node.input(1)); + lf_graph.add_link(lf_body_node.output(0), lf_or_node.input(i)); } std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; -- 2.30.2 From a0feb929614004adf104fd946ca81bc7442fbd81 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Oct 2023 19:29:21 +0200 Subject: [PATCH 32/38] continue with graph --- .../intern/geometry_nodes_lazy_function.cc | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 0cc11a8db19..f86a3d77206 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1896,7 +1896,7 @@ class LazyFunctionForIndexInput : public lf::LazyFunction { outputs_.resize(amount, lf::Output("Index", type)); } - void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override { for (const int i : IndexRange(amount_)) { params.set_output(i, ValueOrField(i)); @@ -1904,12 +1904,39 @@ class LazyFunctionForIndexInput : public lf::LazyFunction { } }; +class LazyFunctionForReduce : public lf::LazyFunction { + private: + const int amount_; + + public: + LazyFunctionForReduce(const int amount) : amount_(amount) + { + debug_name_ = "Reduce"; + const CPPType &geo_cpp_type = CPPType::get(); + for ([[maybe_unused]] const int i : IndexRange(amount)) { + inputs_.append_as("Geometry", geo_cpp_type); + } + outputs_.append_as("Geometry", geo_cpp_type); + } + + void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override + { + Vector geometries(amount_); + for (const int i : IndexRange(amount_)) { + geometries[i] = params.extract_input(i); + } + GeometrySet joined_geometries = geometry::join_geometries(geometries, {}); + params.set_output(0, std::move(joined_geometries)); + } +}; + struct ForEachEvalStorage { LinearAllocator<> allocator; lf::Graph graph; std::optional graph_executor; std::optional index_input_fn; std::optional or_fn; + std::optional reduce_fn; }; class LazyFunctionForForeachZone : public LazyFunction { @@ -1994,13 +2021,20 @@ class LazyFunctionForForeachZone : public LazyFunction { eval_storage.or_fn.emplace(amount); lf::FunctionNode &lf_or_node = lf_graph.add_function(*eval_storage.or_fn); + eval_storage.reduce_fn.emplace(amount); + lf::FunctionNode &lf_reduce_node = lf_graph.add_function(*eval_storage.reduce_fn); + for (const int i : IndexRange(amount)) { lf::FunctionNode &lf_body_node = *lf_body_nodes[i]; lf_graph.add_link(lf_index_input_node.output(i), lf_body_node.input(0)); lf_graph.add_link(*lf_graph_inputs[1], lf_body_node.input(1)); lf_graph.add_link(lf_body_node.output(0), lf_or_node.input(i)); + lf_graph.add_link(lf_body_node.output(1), lf_reduce_node.input(i)); } + lf_graph.add_link(lf_reduce_node.output(0), *lf_graph_outputs[0]); + lf_graph.add_link(lf_or_node.output(0), *lf_graph_outputs[1]); + std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; } -- 2.30.2 From a57e4d271b17f4ec2aa9595196d971bbec474343 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 6 Oct 2023 23:29:35 +0200 Subject: [PATCH 33/38] initial graph based evaluation --- .../functions/FN_lazy_function_graph.hh | 13 ++++ .../intern/geometry_nodes_lazy_function.cc | 66 +++++++++++++++---- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/source/blender/functions/FN_lazy_function_graph.hh b/source/blender/functions/FN_lazy_function_graph.hh index 52d0eba1280..6c5090adb63 100644 --- a/source/blender/functions/FN_lazy_function_graph.hh +++ b/source/blender/functions/FN_lazy_function_graph.hh @@ -239,6 +239,9 @@ class Graph : NonCopyable, NonMovable { Span function_nodes() const; Span function_nodes(); + Span graph_inputs(); + Span graph_outputs(); + Span graph_inputs() const; Span graph_outputs() const; @@ -506,6 +509,16 @@ inline Span Graph::function_nodes() return nodes_.as_span().drop_front(2).cast(); } +inline Span Graph::graph_inputs() +{ + return graph_inputs_; +} + +inline Span Graph::graph_outputs() +{ + return graph_outputs_; +} + inline Span Graph::graph_inputs() const { return graph_inputs_; diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 75ca369855e..2ac3ad88336 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1593,7 +1593,7 @@ class LazyFunctionForRepeatZone : public LazyFunction { RepeatEvalStorage &eval_storage = *static_cast(context.storage); const int iterations_usage_index = zone_info_.indices.outputs.input_usages[0]; - if (params.output_was_set(iterations_usage_index)) { + if (!params.output_was_set(iterations_usage_index)) { /* The iterations input is always used. */ params.set_output(iterations_usage_index, true); } @@ -1878,6 +1878,10 @@ struct ForEachEvalStorage { std::optional index_input_fn; std::optional or_fn; std::optional reduce_fn; + void *graph_executor_storage = nullptr; + Vector input_index_map; + Vector output_index_map; + bool multi_threading_enabled = false; }; class LazyFunctionForForeachZone : public LazyFunction { @@ -1905,6 +1909,9 @@ class LazyFunctionForForeachZone : public LazyFunction { void destruct_storage(void *storage) const override { ForEachEvalStorage *s = static_cast(storage); + if (s->graph_executor_storage) { + s->graph_executor->destruct_storage(s->graph_executor_storage); + } std::destroy_at(s); } @@ -1917,12 +1924,24 @@ class LazyFunctionForForeachZone : public LazyFunction { zone_.output_node->storage); ForEachEvalStorage &eval_storage = *static_cast(context.storage); + const int iteration_usage_index = zone_info_.indices.outputs.input_usages[0]; + if (!params.output_was_set(iteration_usage_index)) { + params.set_output(iteration_usage_index, true); + } + if (!eval_storage.graph_executor) { this->initialize_execution_graph( params, eval_storage, node_storage, user_data, local_user_data); } - params.set_default_remaining_outputs(); + lf::RemappedParams eval_graph_params{*eval_storage.graph_executor, + params, + eval_storage.input_index_map, + eval_storage.output_index_map, + eval_storage.multi_threading_enabled}; + lf::Context eval_graph_context{ + eval_storage.graph_executor_storage, context.user_data, context.local_user_data}; + eval_storage.graph_executor->execute(eval_graph_params, eval_graph_context); } void initialize_execution_graph(lf::Params ¶ms, @@ -1934,20 +1953,16 @@ class LazyFunctionForForeachZone : public LazyFunction { UNUSED_VARS(params, eval_storage, node_storage, user_data, local_user_data); const int amount = params.get_input>(0).as_value(); - std::cout << amount << "\n"; lf::Graph &lf_graph = eval_storage.graph; - Vector lf_graph_inputs; - Vector lf_graph_outputs; - for (const int i : inputs_.index_range()) { const lf::Input &input = inputs_[i]; - lf_graph_inputs.append(&lf_graph.add_input(*input.type, input.debug_name)); + lf_graph.add_input(*input.type, input.debug_name); } for (const int i : outputs_.index_range()) { const lf::Output &output = outputs_[i]; - lf_graph_outputs.append(&lf_graph.add_output(*output.type, output.debug_name)); + lf_graph.add_output(*output.type, output.debug_name); } VectorSet lf_body_nodes; @@ -1968,15 +1983,42 @@ class LazyFunctionForForeachZone : public LazyFunction { for (const int i : IndexRange(amount)) { lf::FunctionNode &lf_body_node = *lf_body_nodes[i]; lf_graph.add_link(lf_index_input_node.output(i), lf_body_node.input(0)); - lf_graph.add_link(*lf_graph_inputs[1], lf_body_node.input(1)); + lf_graph.add_link(*lf_graph.graph_inputs()[1], lf_body_node.input(1)); lf_graph.add_link(lf_body_node.output(0), lf_or_node.input(i)); lf_graph.add_link(lf_body_node.output(1), lf_reduce_node.input(i)); } - lf_graph.add_link(lf_reduce_node.output(0), *lf_graph_outputs[0]); - lf_graph.add_link(lf_or_node.output(0), *lf_graph_outputs[1]); + lf_graph.add_link(lf_reduce_node.output(0), *lf_graph.graph_outputs()[0]); - std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; + eval_storage.input_index_map.reinitialize(inputs_.size()); + array_utils::fill_index_range(eval_storage.input_index_map); + eval_storage.output_index_map.reinitialize(outputs_.size()); + array_utils::fill_index_range(eval_storage.output_index_map); + + eval_storage.input_index_map.remove(zone_info_.indices.inputs.main[0]); + eval_storage.output_index_map.remove(zone_info_.indices.outputs.input_usages[0]); + + Vector lf_graph_inputs; + Vector lf_graph_outputs; + for (const int i : eval_storage.input_index_map) { + lf_graph_inputs.append(lf_graph.graph_inputs()[i]); + } + for (const int i : eval_storage.output_index_map) { + lf_graph_outputs.append(lf_graph.graph_outputs()[i]); + } + + lf_graph.update_node_indices(); + + eval_storage.graph_executor.emplace(lf_graph, + std::move(lf_graph_inputs), + std::move(lf_graph_outputs), + nullptr, + nullptr, + nullptr); + eval_storage.graph_executor_storage = eval_storage.graph_executor->init_storage( + eval_storage.allocator); + + // std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; } std::string input_name(const int i) const override -- 2.30.2 From c89be12a2191f389f75a2cba688c88ebc39faeaa Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 7 Oct 2023 00:52:59 +0200 Subject: [PATCH 34/38] fix instance reference hashing --- source/blender/blenkernel/BKE_geometry_set.hh | 10 ++++++++++ source/blender/blenkernel/BKE_instances.hh | 9 --------- source/blender/blenkernel/intern/instances.cc | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 97650b9f6be..4c794675a49 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -400,6 +400,16 @@ struct GeometrySet { void replace_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + uint64_t hash() const + { + return get_default_hash(Span(components_)); + } + + friend bool operator==(const GeometrySet &a, const GeometrySet &b) + { + return Span(a.components_) == Span(b.components_); + } + private: /** * Retrieve the pointer to a component without creating it if it does not exist, diff --git a/source/blender/blenkernel/BKE_instances.hh b/source/blender/blenkernel/BKE_instances.hh index 44b30b2ce41..81c319932b7 100644 --- a/source/blender/blenkernel/BKE_instances.hh +++ b/source/blender/blenkernel/BKE_instances.hh @@ -259,15 +259,6 @@ inline const CustomDataAttributes &Instances::custom_data_attributes() const return attributes_; } -inline uint64_t InstanceReference::hash() const -{ - return blender::get_default_hash_2(data_, geometry_set_.get()); -} - -inline bool operator==(const InstanceReference &a, const InstanceReference &b) -{ - return a.data_ == b.data_ && a.geometry_set_.get() == b.geometry_set_.get(); -} /** \} */ diff --git a/source/blender/blenkernel/intern/instances.cc b/source/blender/blenkernel/intern/instances.cc index 05d6391c6b0..6c44151546d 100644 --- a/source/blender/blenkernel/intern/instances.cc +++ b/source/blender/blenkernel/intern/instances.cc @@ -35,6 +35,22 @@ bool InstanceReference::owns_direct_data() const return geometry_set_->owns_direct_data(); } +uint64_t InstanceReference::hash() const +{ + if (geometry_set_) { + return get_default_hash(*geometry_set_); + } + return get_default_hash(data_); +} + +bool operator==(const InstanceReference &a, const InstanceReference &b) +{ + if (a.geometry_set_ && b.geometry_set_) { + return *a.geometry_set_ == *b.geometry_set_; + } + return a.type_ == b.type_ && a.data_ == b.data_; +} + Instances::Instances(const Instances &other) : references_(other.references_), reference_handles_(other.reference_handles_), -- 2.30.2 From 29418f954c165a945e30d98f69944a992eb20c77 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 7 Oct 2023 00:53:24 +0200 Subject: [PATCH 35/38] automatically start threading when there are many nodes scheduled --- .../intern/lazy_function_graph_executor.cc | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/source/blender/functions/intern/lazy_function_graph_executor.cc b/source/blender/functions/intern/lazy_function_graph_executor.cc index 69780881f16..3cb62fc54b1 100644 --- a/source/blender/functions/intern/lazy_function_graph_executor.cc +++ b/source/blender/functions/intern/lazy_function_graph_executor.cc @@ -250,6 +250,22 @@ struct ScheduledNodes { { return this->priority_.is_empty() && this->normal_.is_empty(); } + + int64_t nodes_num() const + { + return priority_.size() + normal_.size(); + } + + void split_into(ScheduledNodes &other) + { + BLI_assert(this != &other); + const int64_t priority_split = priority_.size() / 2; + const int64_t normal_split = normal_.size() / 2; + other.priority_.extend(priority_.as_span().drop_front(priority_split)); + other.normal_.extend(normal_.as_span().drop_front(normal_split)); + priority_.resize(priority_split); + normal_.resize(normal_split); + } }; struct CurrentTask { @@ -794,6 +810,14 @@ class Executor { current_task.has_scheduled_nodes.store(false, std::memory_order_relaxed); } this->run_node_task(*node, current_task, local_data); + + if (current_task.scheduled_nodes.nodes_num() > 128) { + if (this->try_enable_multi_threading()) { + ScheduledNodes *split_nodes = MEM_new(__func__); + current_task.scheduled_nodes.split_into(*split_nodes); + this->push_to_task_pool(split_nodes); + } + } } } @@ -1229,18 +1253,24 @@ class Executor { /** * Allow other threads to steal all the nodes that are currently scheduled on this thread. */ - void move_scheduled_nodes_to_task_pool(CurrentTask ¤t_task) + void push_all_scheduled_nodes_to_task_pool(CurrentTask ¤t_task) { BLI_assert(this->use_multi_threading()); ScheduledNodes *scheduled_nodes = MEM_new(__func__); { std::lock_guard lock{current_task.mutex}; if (current_task.scheduled_nodes.is_empty()) { + MEM_delete(scheduled_nodes); return; } *scheduled_nodes = std::move(current_task.scheduled_nodes); current_task.has_scheduled_nodes.store(false, std::memory_order_relaxed); } + this->push_to_task_pool(scheduled_nodes); + } + + void push_to_task_pool(ScheduledNodes *scheduled_nodes) + { /* All nodes are pushed as a single task in the pool. This avoids unnecessary threading * overhead when the nodes are fast to compute. */ BLI_task_pool_push( @@ -1410,7 +1440,7 @@ inline void Executor::execute_node(const FunctionNode &node, if (!this->try_enable_multi_threading()) { return; } - this->move_scheduled_nodes_to_task_pool(current_task); + this->push_all_scheduled_nodes_to_task_pool(current_task); }; lazy_threading::HintReceiver blocking_hint_receiver{blocking_hint_fn}; -- 2.30.2 From d1feaee9f8884320fb46b799935402d307a1e749 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 8 Oct 2023 15:42:04 +0200 Subject: [PATCH 36/38] fix after merge --- .../geometry/nodes/node_geo_foreach_input.cc | 40 ++++------------- .../geometry/nodes/node_geo_foreach_output.cc | 43 +++++-------------- 2 files changed, 20 insertions(+), 63 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc index 70c30ddbf60..eb7a4693f34 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_input.cc @@ -25,12 +25,11 @@ NODE_STORAGE_FUNCS(NodeGeometryForEachInput); static void node_declare_dynamic(const bNodeTree &tree, const bNode &node, - NodeDeclaration &r_declaration) + NodeDeclarationBuilder &b) { const NodeGeometryForEachInput &input_storage = node_storage(node); const bNode *output_node = tree.node_by_id(input_storage.output_node_id); if (output_node == nullptr) { - r_declaration.skip_updating_sockets = true; return; } const auto &output_storage = *static_cast( @@ -38,7 +37,6 @@ static void node_declare_dynamic(const bNodeTree &tree, const GeometryNodeForEachMode mode = GeometryNodeForEachMode(output_storage.mode); { /* Add standard inputs. */ - NodeDeclarationBuilder b{r_declaration}; switch (mode) { case GEO_NODE_FOR_EACH_MODE_INDEX: { b.add_input("Amount").min(0).default_value(1); @@ -47,7 +45,7 @@ static void node_declare_dynamic(const bNodeTree &tree, } case GEO_NODE_FOR_EACH_MODE_GEOMETRY_ELEMENT: { b.add_input("Geometry"); - b.add_input("Selection").supports_field().default_value(true).hide_value(true); + b.add_input("Selection").default_value(true).hide_value(true).supports_field(); b.add_output("Index"); if (output_storage.domain != ATTR_DOMAIN_CORNER) { b.add_output("Element"); @@ -69,36 +67,16 @@ static void node_declare_dynamic(const bNodeTree &tree, for (const int i : IndexRange(output_storage.input_items_num)) { const NodeForEachInputItem &item = output_storage.input_items[i]; const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); + const StringRef name = item.name; const std::string identifier = ForEachInputItemsAccessor::socket_identifier_for_item(item); - { - SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); - BLI_assert(decl); - decl->name = StringRef(item.name); - decl->identifier = identifier; - decl->in_out = SOCK_IN; - if (socket_type_supports_fields(socket_type)) { - decl->input_field_type = InputSocketFieldType::IsSupported; - } - r_declaration.inputs.append(decl.get()); - r_declaration.items.append(std::move(decl)); - } - { - SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); - BLI_assert(decl); - decl->name = StringRef(item.name); - decl->identifier = identifier; - decl->in_out = SOCK_OUT; - r_declaration.outputs.append(decl.get()); - r_declaration.items.append(std::move(decl)); + auto &input_decl = b.add_input(socket_type, name, identifier); + b.add_output(socket_type, name, identifier); + if (socket_type_supports_fields(socket_type)) { + input_decl.supports_field(); } } - SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN); - r_declaration.inputs.append(input_extend_decl.get()); - r_declaration.items.append(std::move(input_extend_decl)); - - SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT); - r_declaration.outputs.append(output_extend_decl.get()); - r_declaration.items.append(std::move(output_extend_decl)); + b.add_input("", "__extend__"); + b.add_output("", "__extend__"); } static void node_init(bNodeTree * /*tree*/, bNode *node) diff --git a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc index b21d766f512..935f80b587d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_foreach_output.cc @@ -22,46 +22,25 @@ NODE_STORAGE_FUNCS(NodeGeometryForEachOutput); static void node_declare_dynamic(const bNodeTree & /*node_tree*/, const bNode &node, - NodeDeclaration &r_declaration) + NodeDeclarationBuilder &b) { const NodeGeometryForEachOutput &storage = node_storage(node); for (const int i : IndexRange(storage.output_items_num)) { const NodeForEachOutputItem &item = storage.output_items[i]; const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); const std::string identifier = ForEachOutputItemsAccessor::socket_identifier_for_item(item); - { - SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); - BLI_assert(decl); - decl->name = StringRef(item.name); - decl->identifier = identifier; - decl->in_out = SOCK_IN; - if (socket_type_supports_fields(socket_type)) { - decl->input_field_type = InputSocketFieldType::IsSupported; - } - r_declaration.inputs.append(decl.get()); - r_declaration.items.append(std::move(decl)); - } - { - SocketDeclarationPtr decl = make_declaration_for_socket_type(socket_type); - BLI_assert(decl); - decl->name = StringRef(item.name); - decl->identifier = identifier; - decl->in_out = SOCK_OUT; - if (socket_type_supports_fields(socket_type)) { - decl->output_field_dependency = OutputFieldDependency::ForFieldSource(); - } - r_declaration.outputs.append(decl.get()); - r_declaration.items.append(std::move(decl)); + const StringRef name = item.name; + + auto &input_decl = b.add_input(socket_type, name, identifier); + auto &output_decl = b.add_output(socket_type, name, identifier); + + if (socket_type_supports_fields(socket_type)) { + input_decl.supports_field(); + output_decl.field_on_all(); } } - - SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN); - r_declaration.inputs.append(input_extend_decl.get()); - r_declaration.items.append(std::move(input_extend_decl)); - - SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT); - r_declaration.outputs.append(output_extend_decl.get()); - r_declaration.items.append(std::move(output_extend_decl)); + b.add_input("", "__extend__"); + b.add_output("", "__extend__"); } static void node_init(bNodeTree * /*tree*/, bNode *node) -- 2.30.2 From 3674a96755a2335dac726f05dd09fcdb3d983a26 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 8 Oct 2023 15:57:43 +0200 Subject: [PATCH 37/38] improve naming --- .../intern/geometry_nodes_lazy_function.cc | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 2ac3ad88336..58def063d5c 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1826,12 +1826,12 @@ class LazyFunctionForRepeatZone : public LazyFunction { } }; -class LazyFunctionForIndexInput : public lf::LazyFunction { +class LazyFunctionForForEachSplit : public lf::LazyFunction { private: const int amount_; public: - LazyFunctionForIndexInput(const int amount) : amount_(amount) + LazyFunctionForForEachSplit(const int amount) : amount_(amount) { const CPPType &type = CPPType::get>(); outputs_.resize(amount, lf::Output("Index", type)); @@ -1845,12 +1845,12 @@ class LazyFunctionForIndexInput : public lf::LazyFunction { } }; -class LazyFunctionForReduce : public lf::LazyFunction { +class LazyFunctionForForEachReduce : public lf::LazyFunction { private: const int amount_; public: - LazyFunctionForReduce(const int amount) : amount_(amount) + LazyFunctionForForEachReduce(const int amount) : amount_(amount) { debug_name_ = "Reduce"; const CPPType &geo_cpp_type = CPPType::get(); @@ -1875,9 +1875,9 @@ struct ForEachEvalStorage { LinearAllocator<> allocator; lf::Graph graph; std::optional graph_executor; - std::optional index_input_fn; + std::optional split_fn; + std::optional reduce_fn; std::optional or_fn; - std::optional reduce_fn; void *graph_executor_storage = nullptr; Vector input_index_map; Vector output_index_map; @@ -1971,18 +1971,18 @@ class LazyFunctionForForeachZone : public LazyFunction { lf_body_nodes.add_new(&lf_node); } - eval_storage.index_input_fn.emplace(amount); - lf::FunctionNode &lf_index_input_node = lf_graph.add_function(*eval_storage.index_input_fn); - - eval_storage.or_fn.emplace(amount); - lf::FunctionNode &lf_or_node = lf_graph.add_function(*eval_storage.or_fn); + eval_storage.split_fn.emplace(amount); + lf::FunctionNode &lf_split_node = lf_graph.add_function(*eval_storage.split_fn); eval_storage.reduce_fn.emplace(amount); lf::FunctionNode &lf_reduce_node = lf_graph.add_function(*eval_storage.reduce_fn); + eval_storage.or_fn.emplace(amount); + lf::FunctionNode &lf_or_node = lf_graph.add_function(*eval_storage.or_fn); + for (const int i : IndexRange(amount)) { lf::FunctionNode &lf_body_node = *lf_body_nodes[i]; - lf_graph.add_link(lf_index_input_node.output(i), lf_body_node.input(0)); + lf_graph.add_link(lf_split_node.output(i), lf_body_node.input(0)); lf_graph.add_link(*lf_graph.graph_inputs()[1], lf_body_node.input(1)); lf_graph.add_link(lf_body_node.output(0), lf_or_node.input(i)); lf_graph.add_link(lf_body_node.output(1), lf_reduce_node.input(i)); -- 2.30.2 From 34224f0efa076aec0781af7ebf2d2ea2664640d6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 8 Oct 2023 16:52:40 +0200 Subject: [PATCH 38/38] initial border link support --- .../intern/geometry_nodes_lazy_function.cc | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 58def063d5c..25f22be6a1f 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1833,6 +1833,7 @@ class LazyFunctionForForEachSplit : public lf::LazyFunction { public: LazyFunctionForForEachSplit(const int amount) : amount_(amount) { + debug_name_ = "For-Each Split"; const CPPType &type = CPPType::get>(); outputs_.resize(amount, lf::Output("Index", type)); } @@ -1852,7 +1853,7 @@ class LazyFunctionForForEachReduce : public lf::LazyFunction { public: LazyFunctionForForEachReduce(const int amount) : amount_(amount) { - debug_name_ = "Reduce"; + debug_name_ = "For-Each Reduce"; const CPPType &geo_cpp_type = CPPType::get(); for ([[maybe_unused]] const int i : IndexRange(amount)) { inputs_.append_as("Geometry", geo_cpp_type); @@ -1953,6 +1954,8 @@ class LazyFunctionForForeachZone : public LazyFunction { UNUSED_VARS(params, eval_storage, node_storage, user_data, local_user_data); const int amount = params.get_input>(0).as_value(); + const int main_outputs_num = node_storage.output_items_num; + const int border_links_num = zone_.border_links.size(); lf::Graph &lf_graph = eval_storage.graph; @@ -1977,15 +1980,31 @@ class LazyFunctionForForeachZone : public LazyFunction { eval_storage.reduce_fn.emplace(amount); lf::FunctionNode &lf_reduce_node = lf_graph.add_function(*eval_storage.reduce_fn); - eval_storage.or_fn.emplace(amount); - lf::FunctionNode &lf_or_node = lf_graph.add_function(*eval_storage.or_fn); + for (const int node_i : IndexRange(amount)) { + lf::FunctionNode &lf_body_node = *lf_body_nodes[node_i]; + lf_graph.add_link(lf_split_node.output(node_i), + lf_body_node.input(body_fn_.indices.inputs.main[0])); + lf_graph.add_link(lf_body_node.output(body_fn_.indices.outputs.main[0]), + lf_reduce_node.input(node_i)); - for (const int i : IndexRange(amount)) { - lf::FunctionNode &lf_body_node = *lf_body_nodes[i]; - lf_graph.add_link(lf_split_node.output(i), lf_body_node.input(0)); - lf_graph.add_link(*lf_graph.graph_inputs()[1], lf_body_node.input(1)); - lf_graph.add_link(lf_body_node.output(0), lf_or_node.input(i)); - lf_graph.add_link(lf_body_node.output(1), lf_reduce_node.input(i)); + for (const int main_output_i : IndexRange(main_outputs_num)) { + lf_graph.add_link( + *lf_graph.graph_inputs()[zone_info_.indices.inputs.output_usages[main_output_i]], + lf_body_node.input(body_fn_.indices.inputs.output_usages[main_output_i])); + } + for (const int border_link_i : IndexRange(border_links_num)) { + lf_graph.add_link( + *lf_graph.graph_inputs()[zone_info_.indices.inputs.border_links[border_link_i]], + lf_body_node.input(body_fn_.indices.inputs.border_links[border_link_i])); + } + } + + static bool static_true = true; + for (const int i : zone_info_.indices.outputs.border_link_usages) { + lf_graph.graph_outputs()[i]->set_default_value(&static_true); + } + for (const int i : zone_info_.indices.outputs.input_usages) { + lf_graph.graph_outputs()[i]->set_default_value(&static_true); } lf_graph.add_link(lf_reduce_node.output(0), *lf_graph.graph_outputs()[0]); @@ -2018,7 +2037,7 @@ class LazyFunctionForForeachZone : public LazyFunction { eval_storage.graph_executor_storage = eval_storage.graph_executor->init_storage( eval_storage.allocator); - // std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; + std::cout << "\n\n" << lf_graph.to_dot() << "\n\n"; } std::string input_name(const int i) const override -- 2.30.2