Geometry Nodes: add Inspection Index to Repeat Zone

Previously, it was only possible to inspect the data from the first iteration. That
applied to both, the viewer node as well as socket inspection. Now, there is a
new `Inspection Index` setting in the zone properties. It specifies which iteration
should be used by the inspection features.

In theory we could support features like counting the index from the end, but
that can be done separately as well, as it likely requires more UI.

Pull Request: https://projects.blender.org/blender/blender/pulls/112818
This commit is contained in:
Jacques Lucke
2023-09-27 11:09:39 +02:00
parent bc7034d9fd
commit c8cc169d6f
13 changed files with 102 additions and 30 deletions

View File

@@ -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):

View File

@@ -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

View File

@@ -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<const ViewerPathElem *>(a->path.first);
const ViewerPathElem *elem_b = static_cast<const ViewerPathElem *>(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<const RepeatZoneViewerPathElem *>(a);
const auto *b_elem = reinterpret_cast<const RepeatZoneViewerPathElem *>(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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}();

View File

@@ -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<const NodeGeometryRepeatOutput *>(
zone->output_node->storage);
compute_context_builder.push<bke::RepeatZoneComputeContext>(*zone->output_node,
repeat_iteration);
storage.inspection_index);
break;
}
}

View File

@@ -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<NodeGeometryRepeatOutput *>(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<ViewerPathElem *>(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<const ViewerNodeViewerPathElem *>(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<wmWindowManager *>(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)

View File

@@ -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<NodeRepeatItem> items_span() const;

View File

@@ -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)

View File

@@ -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;

View File

@@ -1629,6 +1629,9 @@ class LazyFunctionForRepeatZone : public LazyFunction {
void execute_impl(lf::Params &params, const lf::Context &context) const override
{
auto &user_data = *static_cast<GeoNodesLFUserData *>(context.user_data);
auto &local_user_data = *static_cast<GeoNodesLFLocalUserData *>(context.local_user_data);
const NodeGeometryRepeatOutput &node_storage = *static_cast<const NodeGeometryRepeatOutput *>(
repeat_output_bnode_.storage);
RepeatEvalStorage &eval_storage = *static_cast<RepeatEvalStorage *>(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 &params,
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<int>(
0, params.get_input<ValueOrField<int>>(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;

View File

@@ -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<bke::RepeatZoneComputeContext>(*zone.output_node, iteration);
const auto &storage = *static_cast<const NodeGeometryRepeatOutput *>(
zone.output_node->storage);
compute_context_builder.push<bke::RepeatZoneComputeContext>(*zone.output_node,
storage.inspection_index);
break;
}
}