Geometry Nodes: add Inspection Index to Repeat Zone #112818
|
@ -1142,6 +1142,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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -9089,6 +9089,14 @@ static void def_geo_repeat_output(StructRNA *srna)
|
|||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<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 ¶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<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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
An
enum class
would be much prettier here ;)