diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 8c816a2605e..db3031b8333 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -1140,6 +1140,8 @@ class NODE_PT_repeat_zone_items(Panel): layout.use_property_decorate = False layout.prop(active_item, "socket_type") + layout.prop(output_node, "inspection_index") + # Grease Pencil properties class NODE_PT_annotation(AnnotationDataPanel, Panel): diff --git a/source/blender/blenkernel/BKE_viewer_path.h b/source/blender/blenkernel/BKE_viewer_path.h index fa871166ece..aa78593c81e 100644 --- a/source/blender/blenkernel/BKE_viewer_path.h +++ b/source/blender/blenkernel/BKE_viewer_path.h @@ -35,10 +35,16 @@ struct IDRemapper; extern "C" { #endif +enum ViewerPathEqualFlag { + VIEWER_PATH_EQUAL_FLAG_IGNORE_REPEAT_ITERATION = (1 << 0), +}; + void BKE_viewer_path_init(ViewerPath *viewer_path); void BKE_viewer_path_clear(ViewerPath *viewer_path); void BKE_viewer_path_copy(ViewerPath *dst, const ViewerPath *src); -bool BKE_viewer_path_equal(const ViewerPath *a, const ViewerPath *b); +bool BKE_viewer_path_equal(const ViewerPath *a, + const ViewerPath *b, + ViewerPathEqualFlag flag = ViewerPathEqualFlag(0)); void BKE_viewer_path_blend_write(struct BlendWriter *writer, const ViewerPath *viewer_path); void BKE_viewer_path_blend_read_data(struct BlendDataReader *reader, ViewerPath *viewer_path); void BKE_viewer_path_foreach_id(struct LibraryForeachIDData *data, ViewerPath *viewer_path); @@ -52,7 +58,9 @@ 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); ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src); -bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b); +bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, + const ViewerPathElem *b, + ViewerPathEqualFlag flag = ViewerPathEqualFlag(0)); void BKE_viewer_path_elem_free(ViewerPathElem *elem); #ifdef __cplusplus diff --git a/source/blender/blenkernel/intern/viewer_path.cc b/source/blender/blenkernel/intern/viewer_path.cc index 2c3783631ad..9fbf8304370 100644 --- a/source/blender/blenkernel/intern/viewer_path.cc +++ b/source/blender/blenkernel/intern/viewer_path.cc @@ -40,13 +40,15 @@ void BKE_viewer_path_copy(ViewerPath *dst, const ViewerPath *src) } } -bool BKE_viewer_path_equal(const ViewerPath *a, const ViewerPath *b) +bool BKE_viewer_path_equal(const ViewerPath *a, + const ViewerPath *b, + const ViewerPathEqualFlag flag) { const ViewerPathElem *elem_a = static_cast(a->path.first); const ViewerPathElem *elem_b = static_cast(b->path.first); while (elem_a != nullptr && elem_b != nullptr) { - if (!BKE_viewer_path_elem_equal(elem_a, elem_b)) { + if (!BKE_viewer_path_elem_equal(elem_a, elem_b, flag)) { return false; } elem_a = elem_a->next; @@ -278,7 +280,9 @@ ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src) return dst; } -bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b) +bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, + const ViewerPathElem *b, + const ViewerPathEqualFlag flag) { if (a->type != b->type) { return false; @@ -313,7 +317,8 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a, const ViewerPathElem *b const auto *a_elem = reinterpret_cast(a); const auto *b_elem = reinterpret_cast(b); return a_elem->repeat_output_node_id == b_elem->repeat_output_node_id && - a_elem->iteration == b_elem->iteration; + ((flag & VIEWER_PATH_EQUAL_FLAG_IGNORE_REPEAT_ITERATION) != 0 || + a_elem->iteration == b_elem->iteration); } } return false; diff --git a/source/blender/editors/include/ED_viewer_path.hh b/source/blender/editors/include/ED_viewer_path.hh index b8e6b49ac41..c068c03bdc4 100644 --- a/source/blender/editors/include/ED_viewer_path.hh +++ b/source/blender/editors/include/ED_viewer_path.hh @@ -62,10 +62,18 @@ bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snod */ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed_viewer_path); +enum class UpdateActiveGeometryNodesViewerResult { + StillActive, + Updated, + NotActive, +}; + /** * Checks if the node referenced by the viewer and its entire context is still active, i.e. some - * editor is showing it. + * editor is showing it. If not, the viewer path might be updated in minor ways (like changing the + * repeat zone iteration). */ -bool is_active_geometry_nodes_viewer(const bContext &C, const ViewerPath &viewer_path); +UpdateActiveGeometryNodesViewerResult update_active_geometry_nodes_viewer(const bContext &C, + ViewerPath &viewer_path); } // namespace blender::ed::viewer_path diff --git a/source/blender/editors/screen/workspace_listen.cc b/source/blender/editors/screen/workspace_listen.cc index 8da2e451649..08cf10b5a9a 100644 --- a/source/blender/editors/screen/workspace_listen.cc +++ b/source/blender/editors/screen/workspace_listen.cc @@ -20,12 +20,20 @@ static void validate_viewer_paths(bContext &C, WorkSpace &workspace) return; } - if (blender::ed::viewer_path::is_active_geometry_nodes_viewer(C, workspace.viewer_path)) { - /* The current viewer path is still valid and active. */ - return; + using namespace blender::ed::viewer_path; + + const UpdateActiveGeometryNodesViewerResult result = update_active_geometry_nodes_viewer( + C, workspace.viewer_path); + switch (result) { + case UpdateActiveGeometryNodesViewerResult::StillActive: + return; + case UpdateActiveGeometryNodesViewerResult::Updated: + break; + case UpdateActiveGeometryNodesViewerResult::NotActive: + BKE_viewer_path_clear(&workspace.viewer_path); + break; } - /* Reset inactive viewer path. */ - BKE_viewer_path_clear(&workspace.viewer_path); + WM_event_add_notifier(&C, NC_VIEWER_PATH, nullptr); } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index d1ba61e8dd9..9d87121572a 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2192,6 +2192,9 @@ static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx, return nullptr; } const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier); + if (zone && ELEM(&node, zone->input_node, zone->output_node)) { + zone = zone->parent_zone; + } return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr); }(); diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 4354baa81ec..ee413e8a6a0 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -363,10 +363,10 @@ bool push_compute_context_for_tree_path(const SpaceNode &snode, break; } case GEO_NODE_REPEAT_OUTPUT: { - /* Only show data from the first iteration for now. */ - const int repeat_iteration = 0; + const auto &storage = *static_cast( + zone->output_node->storage); compute_context_builder.push(*zone->output_node, - repeat_iteration); + storage.inspection_index); break; } } diff --git a/source/blender/editors/util/ed_viewer_path.cc b/source/blender/editors/util/ed_viewer_path.cc index 9db5bcbeb78..f7b47a68781 100644 --- a/source/blender/editors/util/ed_viewer_path.cc +++ b/source/blender/editors/util/ed_viewer_path.cc @@ -16,6 +16,7 @@ #include "BLI_vector.hh" #include "DNA_modifier_types.h" +#include "DNA_node_types.h" #include "DNA_windowmanager_types.h" #include "DEG_depsgraph.hh" @@ -36,9 +37,10 @@ static ViewerPathElem *viewer_path_elem_for_zone(const bNodeTreeZone &zone) return &node_elem->base; } case GEO_NODE_REPEAT_OUTPUT: { + const auto &storage = *static_cast(zone.output_node->storage); RepeatZoneViewerPathElem *node_elem = BKE_viewer_path_elem_new_repeat_zone(); node_elem->repeat_output_node_id = zone.output_node->identifier; - node_elem->iteration = 0; + node_elem->iteration = storage.inspection_index; return &node_elem->base; } } @@ -354,14 +356,15 @@ bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed return true; } -bool is_active_geometry_nodes_viewer(const bContext &C, const ViewerPath &viewer_path) +UpdateActiveGeometryNodesViewerResult update_active_geometry_nodes_viewer(const bContext &C, + ViewerPath &viewer_path) { if (BLI_listbase_is_empty(&viewer_path.path)) { - return false; + return UpdateActiveGeometryNodesViewerResult::NotActive; } const ViewerPathElem *last_elem = static_cast(viewer_path.path.last); if (last_elem->type != VIEWER_PATH_ELEM_TYPE_VIEWER_NODE) { - return false; + return UpdateActiveGeometryNodesViewerResult::NotActive; } const int32_t viewer_node_id = reinterpret_cast(last_elem)->node_id; @@ -369,7 +372,7 @@ bool is_active_geometry_nodes_viewer(const bContext &C, const ViewerPath &viewer const Main *bmain = CTX_data_main(&C); const wmWindowManager *wm = static_cast(bmain->wm.first); if (wm == nullptr) { - return false; + return UpdateActiveGeometryNodesViewerResult::NotActive; } LISTBASE_FOREACH (const wmWindow *, window, &wm->windows) { const bScreen *active_screen = BKE_workspace_active_screen_get(window->workspace_hook); @@ -405,14 +408,22 @@ bool is_active_geometry_nodes_viewer(const bContext &C, const ViewerPath &viewer ViewerPath tmp_viewer_path{}; BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&tmp_viewer_path); }); viewer_path_for_geometry_node(snode, *viewer_node, tmp_viewer_path); - if (!BKE_viewer_path_equal(&viewer_path, &tmp_viewer_path)) { + if (!BKE_viewer_path_equal( + &viewer_path, &tmp_viewer_path, VIEWER_PATH_EQUAL_FLAG_IGNORE_REPEAT_ITERATION)) + { continue; } - return true; + if (!BKE_viewer_path_equal(&viewer_path, &tmp_viewer_path)) { + std::swap(viewer_path, tmp_viewer_path); + /* Make sure the viewed data becomes available. */ + DEG_id_tag_update(snode.id, ID_RECALC_GEOMETRY); + return UpdateActiveGeometryNodesViewerResult::Updated; + } + return UpdateActiveGeometryNodesViewerResult::StillActive; } } } - return false; + return UpdateActiveGeometryNodesViewerResult::NotActive; } bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 409f330dd33..fb99ee15e70 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1830,7 +1830,7 @@ typedef struct NodeGeometryRepeatOutput { int active_index; /** Identifier to give to the next repeat item. */ int next_identifier; - char _pad[4]; + int inspection_index; #ifdef __cplusplus blender::Span items_span() const; diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 4ba3b8b7ccb..b3302239045 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -9091,6 +9091,14 @@ static void def_geo_repeat_output(StructRNA *srna) 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, "inspection_index", PROP_INT, PROP_NONE); + RNA_def_property_ui_range(prop, 0, INT32_MAX, 1, -1); + RNA_def_property_ui_text(prop, + "Inspection Index", + "Iteration index that is used by inspection features like the viewer " + "node or socket inspection"); + RNA_def_property_update(prop, NC_NODE, "rna_Node_update"); } static void def_geo_curve_handle_type_selection(StructRNA *srna) 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 b641502bc72..b5038bd441a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc @@ -161,6 +161,7 @@ static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const b dst_storage->items_num = src_storage.items_num; dst_storage->active_index = src_storage.active_index; dst_storage->next_identifier = src_storage.next_identifier; + dst_storage->inspection_index = src_storage.inspection_index; for (const int i : IndexRange(src_storage.items_num)) { if (char *name = src_storage.items[i].name) { dst_storage->items[i].identifier = src_storage.items[i].identifier; diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index cfd92a4a401..92f98cfd947 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1629,6 +1629,9 @@ class LazyFunctionForRepeatZone : 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 NodeGeometryRepeatOutput &node_storage = *static_cast( repeat_output_bnode_.storage); RepeatEvalStorage &eval_storage = *static_cast(context.storage); @@ -1641,7 +1644,8 @@ class LazyFunctionForRepeatZone : public LazyFunction { if (!eval_storage.graph_executor) { /* Create the execution graph in the first evaluation. */ - this->initialize_execution_graph(params, eval_storage, node_storage); + this->initialize_execution_graph( + params, eval_storage, node_storage, user_data, local_user_data); } /* Execute the graph for the repeat zone. */ @@ -1661,7 +1665,9 @@ class LazyFunctionForRepeatZone : public LazyFunction { */ void initialize_execution_graph(lf::Params ¶ms, RepeatEvalStorage &eval_storage, - const NodeGeometryRepeatOutput &node_storage) const + const NodeGeometryRepeatOutput &node_storage, + GeoNodesLFUserData &user_data, + GeoNodesLFLocalUserData &local_user_data) const { const int num_repeat_items = node_storage.items_num; const int num_border_links = body_fn_.indices.inputs.border_links.size(); @@ -1669,6 +1675,17 @@ class LazyFunctionForRepeatZone : public LazyFunction { /* Number of iterations to evaluate. */ const int iterations = std::max( 0, params.get_input>(zone_info_.indices.inputs.main[0]).as_value()); + + /* Show a warning when the inspection index is out of range. */ + if (node_storage.inspection_index < 0 || node_storage.inspection_index >= iterations) { + if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger( + user_data)) { + tree_logger->node_warnings.append( + {repeat_output_bnode_.identifier, + {NodeWarningType::Info, N_("Inspection index is out of range")}}); + } + } + /* Take iterations input into account. */ const int main_inputs_offset = 1; diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index c9dbe317517..126e1b8dd07 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -492,9 +492,10 @@ static void find_tree_zone_hash_recursive( break; } case GEO_NODE_REPEAT_OUTPUT: { - /* Only show data from the first iteration for now. */ - const int iteration = 0; - compute_context_builder.push(*zone.output_node, iteration); + const auto &storage = *static_cast( + zone.output_node->storage); + compute_context_builder.push(*zone.output_node, + storage.inspection_index); break; } }