Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2022-09-28 18:54:59 +0300
committerJacques Lucke <jacques@blender.org>2022-09-28 18:54:59 +0300
commitc55d38f00b8c0e6ae8bda9cc66614afe28fb3fc9 (patch)
tree0fc280eec3e2d0655197afda823de1062f51ea05 /source/blender/editors
parent2312915b9620808d29e9b20529684800638d5a2a (diff)
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d viewport (instead of just in the spreadsheet). The "viewer geometry" bypasses the group output. So it is not necessary to change the final output of the node group to be able to see the intermediate geometry. **Activation and deactivation of a viewer node** * A viewer node is activated by clicking on it. * Ctrl+shift+click on any node/socket connects it to the viewer and makes it active. * Ctrl+shift+click in empty space deactivates the active viewer. * When the active viewer is not visible anymore (e.g. another object is selected, or the current node group is exit), it is deactivated. * Clicking on the icon in the header of the Viewer node toggles whether its active or not. **Pinning** * The spreadsheet still allows pinning the active viewer as before. When pinned, the spreadsheet still references the viewer node even when it becomes inactive. * The viewport does not support pinning at the moment. It always shows the active viewer. **Attribute** * When a field is linked to the second input of the viewer node it is displayed as an overlay in the viewport. * When possible the correct domain for the attribute is determined automatically. This does not work in all cases. It falls back to the face corner domain on meshes and the point domain on curves. When necessary, the domain can be picked manually. * The spreadsheet now only shows the "Viewer" column for the domain that is selected in the Viewer node. * Instance attributes are visualized as a constant color per instance. **Viewport Options** * The attribute overlay opacity can be controlled with the "Viewer Node" setting in the overlays popover. * A viewport can be configured not to show intermediate viewer-geometry by disabling the "Viewer Node" option in the "View" menu. **Implementation Details** * The "spreadsheet context path" was generalized to a "viewer path" that is used in more places now. * The viewer node itself determines the attribute domain, evaluates the field and stores the result in a `.viewer` attribute. * A new "viewer attribute' overlay displays the data from the `.viewer` attribute. * The ground truth for the active viewer node is stored in the workspace now. Node editors, spreadsheets and viewports retrieve the active viewer from there unless they are pinned. * The depsgraph object iterator has a new "viewer path" setting. When set, the viewed geometry of the corresponding object is part of the iterator instead of the final evaluated geometry. * To support the instance attribute overlay `DupliObject` was extended to contain the information necessary for drawing the overlay. * The ctrl+shift+click operator has been refactored so that it can make existing links to viewers active again. * The auto-domain-detection in the Viewer node works by checking the "preferred domain" for every field input. If there is not exactly one preferred domain, the fallback is used. Known limitations: * Loose edges of meshes don't have the attribute overlay. This could be added separately if necessary. * Some attributes are hard to visualize as a color directly. For example, the values might have to be normalized or some should be drawn as arrays. For now, we encourage users to build node groups that generate appropriate viewer-geometry. We might include some of that functionality in future versions. Support for displaying attribute values as text in the viewport is planned as well. * There seems to be an issue with the attribute overlay for pointclouds on nvidia gpus, to be investigated. Differential Revision: https://developer.blender.org/D15954
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/include/ED_screen.h2
-rw-r--r--source/blender/editors/include/ED_spreadsheet.h22
-rw-r--r--source/blender/editors/include/ED_viewer_path.hh69
-rw-r--r--source/blender/editors/screen/CMakeLists.txt1
-rw-r--r--source/blender/editors/screen/workspace_listen.cc37
-rw-r--r--source/blender/editors/space_node/node_draw.cc54
-rw-r--r--source/blender/editors/space_node/node_edit.cc44
-rw-r--r--source/blender/editors/space_node/node_intern.hh1
-rw-r--r--source/blender/editors/space_node/node_ops.cc2
-rw-r--r--source/blender/editors/space_node/node_relationships.cc237
-rw-r--r--source/blender/editors/space_node/node_select.cc20
-rw-r--r--source/blender/editors/space_node/space_node.cc3
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc213
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.cc591
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.hh13
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc73
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.cc23
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.cc23
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/ed_viewer_path.cc362
22 files changed, 879 insertions, 916 deletions
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 144fa4e0b93..e1fd53ccdee 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -469,6 +469,8 @@ bool ED_workspace_layout_cycle(struct WorkSpace *workspace, short direction, str
void ED_workspace_status_text(struct bContext *C, const char *str);
+void ED_workspace_do_listen(struct bContext *C, const struct wmNotifier *note);
+
/* anim */
/**
* Results in fully updated anim system.
diff --git a/source/blender/editors/include/ED_spreadsheet.h b/source/blender/editors/include/ED_spreadsheet.h
index da13e6d3636..736da367a44 100644
--- a/source/blender/editors/include/ED_spreadsheet.h
+++ b/source/blender/editors/include/ED_spreadsheet.h
@@ -7,7 +7,6 @@ struct Main;
struct Object;
struct SpaceNode;
struct SpaceSpreadsheet;
-struct SpreadsheetContext;
struct bContext;
struct bNode;
@@ -15,29 +14,8 @@ struct bNode;
extern "C" {
#endif
-struct SpreadsheetContext *ED_spreadsheet_context_new(int type);
-void ED_spreadsheet_context_free(struct SpreadsheetContext *context);
-void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet);
-bool ED_spreadsheet_context_path_update_tag(struct SpaceSpreadsheet *sspreadsheet);
-uint64_t ED_spreadsheet_context_path_hash(const struct SpaceSpreadsheet *sspreadsheet);
-
struct ID *ED_spreadsheet_get_current_id(const struct SpaceSpreadsheet *sspreadsheet);
-void ED_spreadsheet_context_path_set_geometry_node(struct SpaceSpreadsheet *sspreadsheet,
- struct SpaceNode *snode,
- struct bNode *node);
-void ED_spreadsheet_context_paths_set_geometry_node(struct Main *bmain,
- struct SpaceNode *snode,
- struct bNode *node);
-void ED_spreadsheet_context_path_set_evaluated_object(struct SpaceSpreadsheet *sspreadsheet,
- struct Object *object);
-
-void ED_spreadsheet_context_path_guess(const struct bContext *C,
- struct SpaceSpreadsheet *sspreadsheet);
-bool ED_spreadsheet_context_path_is_active(const struct bContext *C,
- struct SpaceSpreadsheet *sspreadsheet);
-bool ED_spreadsheet_context_path_exists(struct Main *bmain, struct SpaceSpreadsheet *sspreadsheet);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_viewer_path.hh b/source/blender/editors/include/ED_viewer_path.hh
new file mode 100644
index 00000000000..957dfddb8af
--- /dev/null
+++ b/source/blender/editors/include/ED_viewer_path.hh
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <optional>
+
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "BKE_viewer_path.h"
+
+struct Main;
+struct SpaceNode;
+struct bNode;
+struct bContext;
+struct Object;
+
+namespace blender::ed::viewer_path {
+
+/**
+ * Activates the given node in the context provided by the editor. This indirectly updates all
+ * non-pinned viewer paths in other editors (spreadsheet and 3d view).
+ */
+void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node);
+
+/**
+ * Returns the object referenced by the viewer path. This only returns something if the viewer path
+ * *only* contains the object and nothing more.
+ */
+Object *parse_object_only(const ViewerPath &viewer_path);
+
+/**
+ * Represents a parsed #ViewerPath for easier consumption.
+ */
+struct ViewerPathForGeometryNodesViewer {
+ Object *object;
+ blender::StringRefNull modifier_name;
+ blender::Vector<blender::StringRefNull> group_node_names;
+ blender::StringRefNull viewer_node_name;
+};
+
+/**
+ * Parses a #ViewerPath into a #ViewerPathForGeometryNodesViewer or returns none if that does not
+ * work.
+ */
+std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer(
+ const ViewerPath &viewer_path);
+
+/**
+ * Finds the node referenced by the #ViewerPath within the provided editor. If no node is
+ * referenced, null is returned. When two different editors show the same node group but in a
+ * different context, it's possible that the same node is active in one editor but not the other.
+ */
+bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode);
+
+/**
+ * Checks if the node referenced by the viewer path and its entire context still exists. The node
+ * does not have to be visible for this to return true.
+ */
+bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed_viewer_path);
+
+/**
+ * Checks if the node referenced by the viewer and its entire context is still active, i.e. some
+ * editor is showing it.
+ */
+bool is_active_geometry_nodes_viewer(const bContext &C,
+ const ViewerPathForGeometryNodesViewer &parsed_viewer_path);
+
+} // namespace blender::ed::viewer_path
diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt
index 119758f3335..bde73402277 100644
--- a/source/blender/editors/screen/CMakeLists.txt
+++ b/source/blender/editors/screen/CMakeLists.txt
@@ -34,6 +34,7 @@ set(SRC
screendump.c
workspace_edit.c
workspace_layout_edit.c
+ workspace_listen.cc
screen_intern.h
)
diff --git a/source/blender/editors/screen/workspace_listen.cc b/source/blender/editors/screen/workspace_listen.cc
new file mode 100644
index 00000000000..84326007d66
--- /dev/null
+++ b/source/blender/editors/screen/workspace_listen.cc
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_context.h"
+
+#include "ED_screen.h"
+#include "ED_viewer_path.hh"
+
+#include "WM_api.h"
+
+/**
+ * Checks if the viewer path stored in the workspace is still active and resets it if not.
+ * The viewer path stored in the workspace is the ground truth for other editors, so it should be
+ * updated before other editors look at it.
+ */
+static void validate_viewer_paths(bContext &C, WorkSpace &workspace)
+{
+ if (BLI_listbase_is_empty(&workspace.viewer_path.path)) {
+ return;
+ }
+
+ const std::optional<blender::ed::viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
+ blender::ed::viewer_path::parse_geometry_nodes_viewer(workspace.viewer_path);
+ if (parsed_path.has_value() &&
+ blender::ed::viewer_path::is_active_geometry_nodes_viewer(C, *parsed_path)) {
+ /* The current viewer path is still valid and active. */
+ return;
+ }
+ /* Reset inactive viewer path. */
+ BKE_viewer_path_clear(&workspace.viewer_path);
+ WM_event_add_notifier(&C, NC_VIEWER_PATH, nullptr);
+}
+
+void ED_workspace_do_listen(bContext *C, const wmNotifier * /*note*/)
+{
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ validate_viewer_paths(*C, *workspace);
+}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 8f72d292740..29dad103dc3 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -60,6 +60,7 @@
#include "ED_node.h"
#include "ED_screen.h"
#include "ED_space_api.h"
+#include "ED_viewer_path.hh"
#include "UI_interface.hh"
#include "UI_resources.h"
@@ -95,6 +96,14 @@ extern void ui_draw_dropshadow(
*/
struct TreeDrawContext {
/**
+ * Whether a viewer node is active in geometry nodes can not be determined by a flag on the node
+ * alone. That's because if the node group with the viewer is used multiple times, it's only
+ * active in one of these cases.
+ * The active node is cached here to avoid doing the more expensive check for every viewer node
+ * in the tree.
+ */
+ const bNode *active_geometry_nodes_viewer = nullptr;
+ /**
* Geometry nodes logs various data during execution. The logged data that corresponds to the
* currently drawn node tree can be retrieved from the log below.
*/
@@ -639,15 +648,19 @@ static void node_update_hidden(bNode &node, uiBlock &block)
node.totr.ymax);
}
-static int node_get_colorid(const bNode &node)
+static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
{
const int nclass = (node.typeinfo->ui_class == nullptr) ? node.typeinfo->nclass :
node.typeinfo->ui_class(&node);
switch (nclass) {
case NODE_CLASS_INPUT:
return TH_NODE_INPUT;
- case NODE_CLASS_OUTPUT:
+ case NODE_CLASS_OUTPUT: {
+ if (node.type == GEO_NODE_VIEWER) {
+ return &node == tree_draw_ctx.active_geometry_nodes_viewer ? TH_NODE_OUTPUT : TH_NODE;
+ }
return (node.flag & NODE_DO_OUTPUT) ? TH_NODE_OUTPUT : TH_NODE;
+ }
case NODE_CLASS_CONVERTER:
return TH_NODE_CONVERTER;
case NODE_CLASS_OP_COLOR:
@@ -2055,7 +2068,7 @@ static void node_draw_basis(const bContext &C,
const rctf &rct = node.totr;
float color[4];
- int color_id = node_get_colorid(node);
+ int color_id = node_get_colorid(tree_draw_ctx, node);
GPU_line_width(1.0f);
@@ -2153,6 +2166,29 @@ static void node_draw_basis(const bContext &C,
"");
UI_block_emboss_set(&block, UI_EMBOSS);
}
+ if (node.type == GEO_NODE_VIEWER) {
+ const bool is_active = &node == tree_draw_ctx.active_geometry_nodes_viewer;
+ iconofs -= iconbutw;
+ UI_block_emboss_set(&block, UI_EMBOSS_NONE);
+ uiBut *but = uiDefIconBut(&block,
+ UI_BTYPE_BUT,
+ 0,
+ is_active ? ICON_HIDE_OFF : ICON_HIDE_ON,
+ iconofs,
+ rct.ymax - NODE_DY,
+ iconbutw,
+ UI_UNIT_Y,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ /* Selection implicitly activates the node. */
+ const char *operator_idname = is_active ? "NODE_OT_deactivate_viewer" : "NODE_OT_select";
+ UI_but_func_set(but, node_toggle_button_cb, &node, (void *)operator_idname);
+ UI_block_emboss_set(&block, UI_EMBOSS);
+ }
node_add_error_message_button(tree_draw_ctx, node, block, rct, iconofs);
@@ -2341,7 +2377,7 @@ static void node_draw_hidden(const bContext &C,
float scale;
UI_view2d_scale_get(&v2d, &scale, nullptr);
- const int color_id = node_get_colorid(node);
+ const int color_id = node_get_colorid(tree_draw_ctx, node);
node_draw_extra_info_panel(tree_draw_ctx, snode, node, block);
@@ -2698,7 +2734,8 @@ static void node_update_nodetree(const bContext &C,
}
}
-static void frame_node_draw_label(const bNodeTree &ntree,
+static void frame_node_draw_label(TreeDrawContext &tree_draw_ctx,
+ const bNodeTree &ntree,
const bNode &node,
const SpaceNode &snode)
{
@@ -2717,7 +2754,7 @@ static void frame_node_draw_label(const bNodeTree &ntree,
BLF_size(fontid, MIN2(24.0f, font_size) * U.dpi_fac);
/* title color */
- int color_id = node_get_colorid(node);
+ int color_id = node_get_colorid(tree_draw_ctx, node);
uchar color[3];
UI_GetThemeColorBlendShade3ubv(TH_TEXT, color_id, 0.4f, 10, color);
BLF_color3ubv(fontid, color);
@@ -2831,7 +2868,7 @@ static void frame_node_draw(const bContext &C,
}
/* label and text */
- frame_node_draw_label(ntree, node, snode);
+ frame_node_draw_label(tree_draw_ctx, ntree, node, snode);
node_draw_extra_info_panel(tree_draw_ctx, snode, node, block);
@@ -3036,6 +3073,9 @@ static void draw_nodetree(const bContext &C,
tree_draw_ctx.geo_tree_log->ensure_node_warnings();
tree_draw_ctx.geo_tree_log->ensure_node_run_time();
}
+ WorkSpace *workspace = CTX_wm_workspace(&C);
+ tree_draw_ctx.active_geometry_nodes_viewer = viewer_path::find_geometry_nodes_viewer(
+ workspace->viewer_path, *snode);
}
node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks);
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index e446d5421e7..8135369f271 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -43,7 +43,7 @@
#include "ED_render.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
-#include "ED_spreadsheet.h"
+#include "ED_viewer_path.hh"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -821,8 +821,8 @@ void ED_node_set_active(
}
}
node->flag |= NODE_DO_OUTPUT;
- ED_spreadsheet_context_paths_set_geometry_node(bmain, snode, node);
}
+ blender::ed::viewer_path::activate_geometry_node(*bmain, *snode, *node);
}
}
}
@@ -1700,6 +1700,46 @@ void NODE_OT_preview_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+static int node_deactivate_viewer_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode &snode = *CTX_wm_space_node(C);
+ WorkSpace &workspace = *CTX_wm_workspace(C);
+
+ bNode *active_viewer = viewer_path::find_geometry_nodes_viewer(workspace.viewer_path, snode);
+
+ LISTBASE_FOREACH (bNode *, node, &snode.edittree->nodes) {
+ if (node->type != GEO_NODE_VIEWER) {
+ continue;
+ }
+ if (!(node->flag & SELECT)) {
+ continue;
+ }
+ if (node == active_viewer) {
+ node->flag &= ~NODE_DO_OUTPUT;
+ BKE_ntree_update_tag_node_property(snode.edittree, node);
+ }
+ }
+
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_deactivate_viewer(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Deactivate Viewer Node";
+ ot->description = "Deactivate selected viewer node in geometry nodes";
+ ot->idname = __func__;
+
+ /* callbacks */
+ ot->exec = node_deactivate_viewer_exec;
+ ot->poll = ED_operator_node_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 70ac0e48d01..50c03489027 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -322,6 +322,7 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot);
void NODE_OT_preview_toggle(wmOperatorType *ot);
void NODE_OT_options_toggle(wmOperatorType *ot);
void NODE_OT_node_copy_color(wmOperatorType *ot);
+void NODE_OT_deactivate_viewer(wmOperatorType *ot);
void NODE_OT_read_viewlayers(wmOperatorType *ot);
void NODE_OT_render_changed(wmOperatorType *ot);
diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc
index f02c019359d..6c52dae5b86 100644
--- a/source/blender/editors/space_node/node_ops.cc
+++ b/source/blender/editors/space_node/node_ops.cc
@@ -44,6 +44,7 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_options_toggle);
WM_operatortype_append(NODE_OT_hide_socket_toggle);
WM_operatortype_append(NODE_OT_node_copy_color);
+ WM_operatortype_append(NODE_OT_deactivate_viewer);
WM_operatortype_append(NODE_OT_duplicate);
WM_operatortype_append(NODE_OT_delete);
@@ -135,6 +136,7 @@ void ED_operatormacros_node()
mot = WM_operatortype_macro_define(ot, "NODE_OT_select");
RNA_boolean_set(mot->ptr, "extend", false);
RNA_boolean_set(mot->ptr, "socket_select", true);
+ RNA_boolean_set(mot->ptr, "clear_viewer", true);
WM_operatortype_macro_define(ot, "NODE_OT_link_viewer");
ot = WM_operatortype_append_macro("NODE_OT_translate_attach",
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index f597bf20b55..84cef798907 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -27,8 +27,8 @@
#include "ED_render.h"
#include "ED_screen.h"
#include "ED_space_api.h"
-#include "ED_spreadsheet.h"
#include "ED_util.h"
+#include "ED_viewer_path.hh"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -495,17 +495,6 @@ static bool is_viewer_node(const bNode &node)
return ELEM(node.type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER);
}
-static Vector<const bNode *> find_viewer_nodes(const bNodeTree &tree)
-{
- Vector<const bNode *> viewer_nodes;
- for (const bNode *node : tree.all_nodes()) {
- if (is_viewer_node(*node)) {
- viewer_nodes.append(node);
- }
- }
- return viewer_nodes;
-}
-
static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
{
const bNode &node = socket.owner_node();
@@ -516,18 +505,10 @@ static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
return socket.index() == 0;
}
-static bool is_linked_to_viewer(const bNodeSocket &socket, const bNode &viewer_node)
+static bool is_viewer_socket(const bNodeSocket &socket)
{
- for (const bNodeSocket *target_socket : socket.directly_linked_sockets()) {
- if (&target_socket->owner_node() != &viewer_node) {
- continue;
- }
- if (!target_socket->is_available()) {
- continue;
- }
- if (is_viewer_socket_in_viewer(*target_socket)) {
- return true;
- }
+ if (is_viewer_node(socket.owner_node())) {
+ return is_viewer_socket_in_viewer(socket);
}
return false;
}
@@ -549,137 +530,165 @@ static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &
}
}
-static const bNode *get_existing_viewer(const bNodeTree &tree)
+static bNodeSocket *determine_socket_to_view(bNode &node_to_view)
{
- Vector<const bNode *> viewer_nodes = find_viewer_nodes(tree);
-
- /* Check if there is already an active viewer node that should be used. */
- for (const bNode *viewer_node : viewer_nodes) {
- if (viewer_node->flag & NODE_DO_OUTPUT) {
- return viewer_node;
+ int last_linked_socket_index = -1;
+ for (bNodeSocket *socket : node_to_view.output_sockets()) {
+ if (!socket_can_be_viewed(*socket)) {
+ continue;
}
- }
-
- /* If no active but non-active viewers exist, make one active. */
- if (!viewer_nodes.is_empty()) {
- const_cast<bNode *>(viewer_nodes[0])->flag |= NODE_DO_OUTPUT;
- return viewer_nodes[0];
- }
- return nullptr;
-}
-
-static const bNodeSocket *find_output_socket_to_be_viewed(const bNode *active_viewer_node,
- const bNode &node_to_view)
-{
- /* Check if any of the output sockets is selected, which is the case when the user just clicked
- * on the socket. */
- for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
- if (output_socket->flag & SELECT) {
- return output_socket;
+ for (bNodeLink *link : socket->directly_linked_links()) {
+ bNodeSocket &target_socket = *link->tosock;
+ bNode &target_node = *link->tonode;
+ if (is_viewer_socket(target_socket)) {
+ if (link->is_muted() || !(target_node.flag & NODE_DO_OUTPUT)) {
+ /* This socket is linked to a deactivated viewer, the viewer should be activated. */
+ return socket;
+ }
+ last_linked_socket_index = socket->index();
+ }
}
}
- const bNodeSocket *last_socket_linked_to_viewer = nullptr;
- if (active_viewer_node != nullptr) {
- for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
- if (!socket_can_be_viewed(*output_socket)) {
- continue;
- }
- if (is_linked_to_viewer(*output_socket, *active_viewer_node)) {
- last_socket_linked_to_viewer = output_socket;
+ if (last_linked_socket_index == -1) {
+ /* Returnt he first socket that can be viewed. */
+ for (bNodeSocket *socket : node_to_view.output_sockets()) {
+ if (socket_can_be_viewed(*socket)) {
+ return socket;
}
}
+ return nullptr;
}
- if (last_socket_linked_to_viewer == nullptr) {
- /* If no output is connected to a viewer, use the first output that can be viewed. */
- for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
- if (socket_can_be_viewed(*output_socket)) {
- return output_socket;
- }
+
+ /* Pick the next socket to be linked to the viewer. */
+ const int tot_outputs = node_to_view.output_sockets().size();
+ for (const int offset : IndexRange(1, tot_outputs)) {
+ const int index = (last_linked_socket_index + offset) % tot_outputs;
+ bNodeSocket &output_socket = node_to_view.output_socket(index);
+ if (!socket_can_be_viewed(output_socket)) {
+ continue;
}
- }
- else {
- /* Pick the next socket to be linked to the viewer. */
- const int tot_outputs = node_to_view.output_sockets().size();
- for (const int offset : IndexRange(1, tot_outputs - 1)) {
- const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs;
- const bNodeSocket &output_socket = node_to_view.output_socket(index);
- if (!socket_can_be_viewed(output_socket)) {
+ bool is_currently_viewed = false;
+ for (const bNodeLink *link : output_socket.directly_linked_links()) {
+ bNodeSocket &target_socket = *link->tosock;
+ bNode &target_node = *link->tonode;
+ if (!is_viewer_socket(target_socket)) {
continue;
}
- if (is_linked_to_viewer(output_socket, *active_viewer_node)) {
+ if (link->is_muted()) {
continue;
}
- return &output_socket;
+ if (!(target_node.flag & NODE_DO_OUTPUT)) {
+ continue;
+ }
+ is_currently_viewed = true;
+ break;
}
+ if (is_currently_viewed) {
+ continue;
+ }
+ return &output_socket;
}
return nullptr;
}
-static int link_socket_to_viewer(const bContext &C,
- bNode *viewer_bnode,
- bNode &bnode_to_view,
- bNodeSocket &bsocket_to_view)
+static void finalize_viewer_link(const bContext &C,
+ SpaceNode &snode,
+ bNode &viewer_node,
+ bNodeLink &viewer_link)
{
- SpaceNode &snode = *CTX_wm_space_node(&C);
- bNodeTree &btree = *snode.edittree;
+ Main *bmain = CTX_data_main(&C);
+ remove_links_to_unavailable_viewer_sockets(*snode.edittree, viewer_node);
+ viewer_link.flag &= ~NODE_LINK_MUTED;
+ viewer_node.flag &= ~NODE_MUTED;
+ viewer_node.flag |= NODE_DO_OUTPUT;
+ if (snode.edittree->type == NTREE_GEOMETRY) {
+ viewer_path::activate_geometry_node(*bmain, snode, viewer_node);
+ }
+ ED_node_tree_propagate_change(&C, bmain, snode.edittree);
+}
+
+static int view_socket(const bContext &C,
+ SpaceNode &snode,
+ bNodeTree &btree,
+ bNode &bnode_to_view,
+ bNodeSocket &bsocket_to_view)
+{
+ bNode *viewer_node = nullptr;
+ /* Try to find a viewer that is already active. */
+ LISTBASE_FOREACH (bNode *, node, &btree.nodes) {
+ if (is_viewer_node(*node)) {
+ if (node->flag & NODE_DO_OUTPUT) {
+ viewer_node = node;
+ break;
+ }
+ }
+ }
- if (viewer_bnode == nullptr) {
- /* Create a new viewer node if none exists. */
+ /* Try to reactivate existing viewer connection. */
+ for (bNodeLink *link : bsocket_to_view.directly_linked_links()) {
+ bNodeSocket &target_socket = *link->tosock;
+ bNode &target_node = *link->tonode;
+ if (is_viewer_socket(target_socket) && ELEM(viewer_node, nullptr, &target_node)) {
+ finalize_viewer_link(C, snode, target_node, *link);
+ return OPERATOR_FINISHED;
+ }
+ }
+
+ if (viewer_node == nullptr) {
+ LISTBASE_FOREACH (bNode *, node, &btree.nodes) {
+ if (is_viewer_node(*node)) {
+ viewer_node = node;
+ break;
+ }
+ }
+ }
+ if (viewer_node == nullptr) {
const int viewer_type = get_default_viewer_type(&C);
const float2 location{bsocket_to_view.locx / UI_DPI_FAC + 100,
bsocket_to_view.locy / UI_DPI_FAC};
- viewer_bnode = add_static_node(C, viewer_type, location);
- if (viewer_bnode == nullptr) {
- return OPERATOR_CANCELLED;
- }
+ viewer_node = add_static_node(C, viewer_type, location);
}
- bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_bnode, bsocket_to_view);
+ bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_node, bsocket_to_view);
if (viewer_bsocket == nullptr) {
return OPERATOR_CANCELLED;
}
-
- bNodeLink *link_to_change = nullptr;
- LISTBASE_FOREACH (bNodeLink *, link, &btree.links) {
+ bNodeLink *viewer_link = nullptr;
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
if (link->tosock == viewer_bsocket) {
- link_to_change = link;
+ viewer_link = link;
break;
}
}
-
- if (link_to_change == nullptr) {
- nodeAddLink(&btree, &bnode_to_view, &bsocket_to_view, viewer_bnode, viewer_bsocket);
+ if (viewer_link == nullptr) {
+ viewer_link = nodeAddLink(
+ &btree, &bnode_to_view, &bsocket_to_view, viewer_node, viewer_bsocket);
}
else {
- link_to_change->fromnode = &bnode_to_view;
- link_to_change->fromsock = &bsocket_to_view;
+ viewer_link->fromnode = &bnode_to_view;
+ viewer_link->fromsock = &bsocket_to_view;
BKE_ntree_update_tag_link_changed(&btree);
}
-
- remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode);
-
- if (btree.type == NTREE_GEOMETRY) {
- ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(&C), &snode, viewer_bnode);
- }
-
- ED_node_tree_propagate_change(&C, CTX_data_main(&C), &btree);
- return OPERATOR_FINISHED;
+ finalize_viewer_link(C, snode, *viewer_node, *viewer_link);
+ return OPERATOR_CANCELLED;
}
-static int node_link_viewer(const bContext &C, bNode &bnode_to_view)
+static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *bsocket_to_view)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree *btree = snode.edittree;
btree->ensure_topology_cache();
- bNode *active_viewer_bnode = const_cast<bNode *>(get_existing_viewer(*btree));
- bNodeSocket *bsocket_to_view = const_cast<bNodeSocket *>(
- find_output_socket_to_be_viewed(active_viewer_bnode, bnode_to_view));
if (bsocket_to_view == nullptr) {
- return OPERATOR_FINISHED;
+ bsocket_to_view = determine_socket_to_view(bnode_to_view);
}
- return link_socket_to_viewer(C, active_viewer_bnode, bnode_to_view, *bsocket_to_view);
+
+ if (bsocket_to_view == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return view_socket(C, snode, *btree, bnode_to_view, *bsocket_to_view);
}
/** \} */
@@ -701,7 +710,15 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- if (viewer_linking::node_link_viewer(*C, *node) == OPERATOR_CANCELLED) {
+ bNodeSocket *socket_to_view = nullptr;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ if (socket->flag & SELECT) {
+ socket_to_view = socket;
+ break;
+ }
+ }
+
+ if (viewer_linking::node_link_viewer(*C, *node, socket_to_view) == OPERATOR_CANCELLED) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc
index 1b47316ebd0..c11ae323115 100644
--- a/source/blender/editors/space_node/node_select.cc
+++ b/source/blender/editors/space_node/node_select.cc
@@ -23,13 +23,14 @@
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
+#include "BKE_node_tree_update.h"
#include "BKE_workspace.h"
#include "ED_node.h" /* own include */
#include "ED_screen.h"
#include "ED_select_utils.h"
-#include "ED_spreadsheet.h"
#include "ED_view3d.h"
+#include "ED_viewer_path.hh"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -644,6 +645,15 @@ static bool node_mouse_select(bContext *C,
}
}
+ if (RNA_boolean_get(op->ptr, "clear_viewer")) {
+ if (node == nullptr) {
+ /* Disable existing active viewer. */
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ BKE_viewer_path_clear(&workspace->viewer_path);
+ WM_event_add_notifier(C, NC_VIEWER_PATH, nullptr);
+ }
+ }
+
if (!(changed || found)) {
return false;
}
@@ -655,7 +665,7 @@ static bool node_mouse_select(bContext *C,
ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed);
}
else if (node != nullptr && node->type == GEO_NODE_VIEWER) {
- ED_spreadsheet_context_paths_set_geometry_node(&bmain, &snode, node);
+ viewer_path::activate_geometry_node(bmain, snode, *node);
}
ED_node_set_active_viewer_key(&snode);
node_sort(*snode.edittree);
@@ -731,6 +741,12 @@ void NODE_OT_select(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", "");
+
+ RNA_def_boolean(ot->srna,
+ "clear_viewer",
+ false,
+ "Clear Viewer",
+ "Deactivate geometry nodes viewer when clicking in empty space");
}
/** \} */
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index ea551b7488a..3e5cbf88e15 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -823,6 +823,9 @@ static void node_region_listener(const wmRegionListenerParams *params)
ED_region_tag_redraw(region);
}
break;
+ case NC_VIEWER_PATH:
+ ED_region_tag_redraw(region);
+ break;
}
}
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index a551e096b48..08032ddbaeb 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -27,7 +27,6 @@ set(SRC
space_spreadsheet.cc
spreadsheet_cache.cc
spreadsheet_column.cc
- spreadsheet_context.cc
spreadsheet_data_source.cc
spreadsheet_data_source_geometry.cc
spreadsheet_dataset_draw.cc
@@ -41,7 +40,6 @@ set(SRC
spreadsheet_cache.hh
spreadsheet_column.hh
spreadsheet_column_values.hh
- spreadsheet_context.hh
spreadsheet_data_source.hh
spreadsheet_data_source_geometry.hh
spreadsheet_dataset_draw.hh
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index d5fa0145fe5..ee43519e260 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -10,6 +10,7 @@
#include "ED_screen.h"
#include "ED_space_api.h"
#include "ED_spreadsheet.h"
+#include "ED_viewer_path.hh"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -34,7 +35,6 @@
#include "BLF_api.h"
-#include "spreadsheet_context.hh"
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_dataset_draw.hh"
#include "spreadsheet_intern.hh"
@@ -107,9 +107,7 @@ static void spreadsheet_free(SpaceLink *sl)
LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) {
spreadsheet_column_free(column);
}
- LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) {
- spreadsheet_context_free(context);
- }
+ BKE_viewer_path_clear(&sspreadsheet->viewer_path);
}
static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area)
@@ -143,11 +141,7 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
BLI_addtail(&sspreadsheet_new->columns, new_column);
}
- BLI_listbase_clear(&sspreadsheet_new->context_path);
- LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, src_context, &sspreadsheet_old->context_path) {
- SpreadsheetContext *new_context = spreadsheet_context_copy(src_context);
- BLI_addtail(&sspreadsheet_new->context_path, new_context);
- }
+ BKE_viewer_path_copy(&sspreadsheet_new->viewer_path, &sspreadsheet_old->viewer_path);
return (SpaceLink *)sspreadsheet_new;
}
@@ -163,19 +157,7 @@ static void spreadsheet_id_remap(ScrArea *UNUSED(area),
const IDRemapper *mappings)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink;
- LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
- if (context->type != SPREADSHEET_CONTEXT_OBJECT) {
- continue;
- }
- SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
-
- if (object_context->object != nullptr && GS(object_context->object->id.name) != ID_OB) {
- object_context->object = nullptr;
- continue;
- }
-
- BKE_id_remapper_apply(mappings, ((ID **)&object_context->object), ID_REMAP_APPLY_DEFAULT);
- }
+ BKE_viewer_path_id_remap(&sspreadsheet->viewer_path, mappings);
}
static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
@@ -201,54 +183,105 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
ID *ED_spreadsheet_get_current_id(const struct SpaceSpreadsheet *sspreadsheet)
{
- if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ if (BLI_listbase_is_empty(&sspreadsheet->viewer_path.path)) {
return nullptr;
}
- SpreadsheetContext *root_context = (SpreadsheetContext *)sspreadsheet->context_path.first;
- if (root_context->type != SPREADSHEET_CONTEXT_OBJECT) {
+ ViewerPathElem *root_context = static_cast<ViewerPathElem *>(
+ sspreadsheet->viewer_path.path.first);
+ if (root_context->type != VIEWER_PATH_ELEM_TYPE_ID) {
return nullptr;
}
- SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)root_context;
- return (ID *)object_context->object;
+ IDViewerPathElem *id_elem = reinterpret_cast<IDViewerPathElem *>(root_context);
+ return id_elem->id;
}
-/* Check if the pinned context still exists. If it doesn't try to find a new context. */
-static void update_pinned_context_path_if_outdated(const bContext *C)
+static void view_active_object(const bContext *C, SpaceSpreadsheet *sspreadsheet)
{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- Main *bmain = CTX_data_main(C);
- if (!ED_spreadsheet_context_path_exists(bmain, sspreadsheet)) {
- ED_spreadsheet_context_path_guess(C, sspreadsheet);
- if (ED_spreadsheet_context_path_update_tag(sspreadsheet)) {
- ED_area_tag_redraw(CTX_wm_area(C));
- }
- }
-
- if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
- /* Don't pin empty context_path, that could be annoying. */
- sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED;
+ BKE_viewer_path_clear(&sspreadsheet->viewer_path);
+ Object *ob = CTX_data_active_object(C);
+ if (ob == nullptr) {
+ return;
}
+ IDViewerPathElem *id_elem = BKE_viewer_path_elem_new_id();
+ id_elem->id = &ob->id;
+ BLI_addtail(&sspreadsheet->viewer_path.path, id_elem);
+ ED_area_tag_redraw(CTX_wm_area(C));
}
-static void update_context_path_from_context(const bContext *C)
+static void spreadsheet_update_context(const bContext *C)
{
+ using blender::ed::viewer_path::ViewerPathForGeometryNodesViewer;
+
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (!ED_spreadsheet_context_path_is_active(C, sspreadsheet)) {
- ED_spreadsheet_context_path_guess(C, sspreadsheet);
- if (ED_spreadsheet_context_path_update_tag(sspreadsheet)) {
- ED_area_tag_redraw(CTX_wm_area(C));
+ Object *active_object = CTX_data_active_object(C);
+ Object *context_object = blender::ed::viewer_path::parse_object_only(sspreadsheet->viewer_path);
+ switch (eSpaceSpreadsheet_ObjectEvalState(sspreadsheet->object_eval_state)) {
+ case SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL:
+ case SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED: {
+ if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
+ if (context_object == nullptr) {
+ /* Object is not available anymore, so clear the pinning. */
+ sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED;
+ }
+ else {
+ /* The object is still pinned, do nothing. */
+ break;
+ }
+ }
+ else {
+ if (active_object != context_object) {
+ /* The active object has changed, so view the new active object. */
+ view_active_object(C, sspreadsheet);
+ }
+ else {
+ /* Nothing changed. */
+ break;
+ }
+ }
+ break;
}
- }
-}
+ case SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE: {
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
+ const std::optional<ViewerPathForGeometryNodesViewer> parsed_path =
+ blender::ed::viewer_path::parse_geometry_nodes_viewer(sspreadsheet->viewer_path);
+ if (parsed_path.has_value()) {
+ if (blender::ed::viewer_path::exists_geometry_nodes_viewer(*parsed_path)) {
+ /* The pinned path is still valid, do nothing. */
+ break;
+ }
+ else {
+ /* The pinned path does not exist anymore, clear pinning. */
+ sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED;
+ }
+ }
+ else {
+ /* Unknown pinned path, clear pinning. */
+ sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED;
+ }
+ }
+ /* Now try to update the viewer path from the workspace. */
+ const std::optional<ViewerPathForGeometryNodesViewer> workspace_parsed_path =
+ blender::ed::viewer_path::parse_geometry_nodes_viewer(workspace->viewer_path);
+ if (workspace_parsed_path.has_value()) {
+ if (BKE_viewer_path_equal(&sspreadsheet->viewer_path, &workspace->viewer_path)) {
+ /* Nothing changed. */
+ break;
+ }
+ else {
+ /* Update the viewer path from the workspace. */
+ BKE_viewer_path_clear(&sspreadsheet->viewer_path);
+ BKE_viewer_path_copy(&sspreadsheet->viewer_path, &workspace->viewer_path);
+ }
+ }
+ else {
+ /* No active viewer node, change back to showing evaluated active object. */
+ sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
+ view_active_object(C, sspreadsheet);
+ }
-void spreadsheet_update_context_path(const bContext *C)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
- update_pinned_context_path_if_outdated(C);
- }
- else {
- update_context_path_from_context(C);
+ break;
+ }
}
}
@@ -390,7 +423,7 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
sspreadsheet->runtime->cache.set_all_unused();
- spreadsheet_update_context_path(C);
+ spreadsheet_update_context(C);
std::unique_ptr<DataSource> data_source = get_data_source(C);
if (!data_source) {
@@ -439,6 +472,7 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param
{
ARegion *region = params->region;
const wmNotifier *wmn = params->notifier;
+ SpaceSpreadsheet *sspreadsheet = static_cast<SpaceSpreadsheet *>(params->area->spacedata.first);
switch (wmn->category) {
case NC_SCENE: {
@@ -467,6 +501,12 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param
ED_region_tag_redraw(region);
break;
}
+ case NC_VIEWER_PATH: {
+ if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
+ ED_region_tag_redraw(region);
+ }
+ break;
+ }
}
}
@@ -477,7 +517,7 @@ static void spreadsheet_header_region_init(wmWindowManager *UNUSED(wm), ARegion
static void spreadsheet_header_region_draw(const bContext *C, ARegion *region)
{
- spreadsheet_update_context_path(C);
+ spreadsheet_update_context(C);
ED_region_header(C, region);
}
@@ -489,6 +529,7 @@ static void spreadsheet_header_region_listener(const wmRegionListenerParams *par
{
ARegion *region = params->region;
const wmNotifier *wmn = params->notifier;
+ SpaceSpreadsheet *sspreadsheet = static_cast<SpaceSpreadsheet *>(params->area->spacedata.first);
switch (wmn->category) {
case NC_SCENE: {
@@ -515,6 +556,12 @@ static void spreadsheet_header_region_listener(const wmRegionListenerParams *par
ED_region_tag_redraw(region);
break;
}
+ case NC_VIEWER_PATH: {
+ if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
+ ED_region_tag_redraw(region);
+ }
+ break;
+ }
}
}
@@ -593,7 +640,7 @@ static void spreadsheet_dataset_region_listener(const wmRegionListenerParams *pa
static void spreadsheet_dataset_region_draw(const bContext *C, ARegion *region)
{
- spreadsheet_update_context_path(C);
+ spreadsheet_update_context(C);
ED_region_panels(C, region);
}
@@ -634,34 +681,13 @@ static void spreadsheet_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
BLO_read_data_address(reader, &column->display_name);
}
- BLO_read_list(reader, &sspreadsheet->context_path);
- LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
- switch (context->type) {
- case SPREADSHEET_CONTEXT_NODE: {
- SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context;
- BLO_read_data_address(reader, &node_context->node_name);
- break;
- }
- case SPREADSHEET_CONTEXT_MODIFIER: {
- SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context;
- BLO_read_data_address(reader, &modifier_context->modifier_name);
- break;
- }
- case SPREADSHEET_CONTEXT_OBJECT: {
- break;
- }
- }
- }
+ BKE_viewer_path_blend_read_data(reader, &sspreadsheet->viewer_path);
}
static void spreadsheet_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
- if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
- BLO_read_id_address(reader, parent_id->lib, &((SpreadsheetContextObject *)context)->object);
- }
- }
+ BKE_viewer_path_blend_read_lib(reader, parent_id->lib, &sspreadsheet->viewer_path);
}
static void spreadsheet_blend_write(BlendWriter *writer, SpaceLink *sl)
@@ -683,27 +709,8 @@ static void spreadsheet_blend_write(BlendWriter *writer, SpaceLink *sl)
* This would ideally be cleared here. */
BLO_write_string(writer, column->display_name);
}
- LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
- switch (context->type) {
- case SPREADSHEET_CONTEXT_OBJECT: {
- SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
- BLO_write_struct(writer, SpreadsheetContextObject, object_context);
- break;
- }
- case SPREADSHEET_CONTEXT_MODIFIER: {
- SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context;
- BLO_write_struct(writer, SpreadsheetContextModifier, modifier_context);
- BLO_write_string(writer, modifier_context->modifier_name);
- break;
- }
- case SPREADSHEET_CONTEXT_NODE: {
- SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context;
- BLO_write_struct(writer, SpreadsheetContextNode, node_context);
- BLO_write_string(writer, node_context->node_name);
- break;
- }
- }
- }
+
+ BKE_viewer_path_blend_write(writer, &sspreadsheet->viewer_path);
}
void ED_spacetype_spreadsheet()
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
deleted file mode 100644
index ec9fa72edb1..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
+++ /dev/null
@@ -1,591 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_hash.h"
-#include "BLI_hash.hh"
-#include "BLI_hash_mm2a.h"
-#include "BLI_listbase.h"
-#include "BLI_string.h"
-#include "BLI_utildefines.h"
-#include "BLI_vector.hh"
-
-#include "ED_screen.h"
-#include "ED_spreadsheet.h"
-
-#include "DEG_depsgraph.h"
-
-#include "BKE_context.h"
-#include "BKE_main.h"
-#include "BKE_modifier.h"
-#include "BKE_node.h"
-#include "BKE_object.h"
-#include "BKE_workspace.h"
-
-#include "DNA_modifier_types.h"
-#include "DNA_windowmanager_types.h"
-
-#include "spreadsheet_context.hh"
-
-using blender::IndexRange;
-using blender::Span;
-using blender::StringRef;
-using blender::Vector;
-
-namespace blender::ed::spreadsheet {
-
-static SpreadsheetContextObject *spreadsheet_context_object_new()
-{
- SpreadsheetContextObject *context = MEM_cnew<SpreadsheetContextObject>(__func__);
- context->base.type = SPREADSHEET_CONTEXT_OBJECT;
- return context;
-}
-
-static SpreadsheetContextObject *spreadsheet_context_object_copy(
- const SpreadsheetContextObject *src_context)
-{
- SpreadsheetContextObject *new_context = spreadsheet_context_object_new();
- new_context->object = src_context->object;
- return new_context;
-}
-
-static void spreadsheet_context_object_hash(const SpreadsheetContextObject *context,
- BLI_HashMurmur2A *mm2)
-{
- BLI_hash_mm2a_add(mm2, (const uchar *)&context->object, sizeof(Object *));
-}
-
-static void spreadsheet_context_object_free(SpreadsheetContextObject *context)
-{
- MEM_freeN(context);
-}
-
-static SpreadsheetContextModifier *spreadsheet_context_modifier_new()
-{
- SpreadsheetContextModifier *context = MEM_cnew<SpreadsheetContextModifier>(__func__);
- context->base.type = SPREADSHEET_CONTEXT_MODIFIER;
- return context;
-}
-
-static SpreadsheetContextModifier *spreadsheet_context_modifier_copy(
- const SpreadsheetContextModifier *src_context)
-{
- SpreadsheetContextModifier *new_context = spreadsheet_context_modifier_new();
- if (src_context->modifier_name) {
- new_context->modifier_name = BLI_strdup(src_context->modifier_name);
- }
- return new_context;
-}
-
-static void spreadsheet_context_modifier_hash(const SpreadsheetContextModifier *context,
- BLI_HashMurmur2A *mm2)
-{
- if (context->modifier_name) {
- BLI_hash_mm2a_add(mm2, (const uchar *)context->modifier_name, strlen(context->modifier_name));
- }
-}
-
-static void spreadsheet_context_modifier_free(SpreadsheetContextModifier *context)
-{
- if (context->modifier_name) {
- MEM_freeN(context->modifier_name);
- }
- MEM_freeN(context);
-}
-
-static SpreadsheetContextNode *spreadsheet_context_node_new()
-{
- SpreadsheetContextNode *context = MEM_cnew<SpreadsheetContextNode>(__func__);
- context->base.type = SPREADSHEET_CONTEXT_NODE;
- return context;
-}
-
-static SpreadsheetContextNode *spreadsheet_context_node_copy(
- const SpreadsheetContextNode *src_context)
-{
- SpreadsheetContextNode *new_context = spreadsheet_context_node_new();
- if (src_context->node_name) {
- new_context->node_name = BLI_strdup(src_context->node_name);
- }
- return new_context;
-}
-
-static void spreadsheet_context_node_hash(const SpreadsheetContextNode *context,
- BLI_HashMurmur2A *mm2)
-{
- if (context->node_name) {
- BLI_hash_mm2a_add(mm2, (const uchar *)context->node_name, strlen(context->node_name));
- }
-}
-
-static void spreadsheet_context_node_free(SpreadsheetContextNode *context)
-{
- if (context->node_name) {
- MEM_freeN(context->node_name);
- }
- MEM_freeN(context);
-}
-
-SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type)
-{
- switch (type) {
- case SPREADSHEET_CONTEXT_OBJECT: {
- return (SpreadsheetContext *)spreadsheet_context_object_new();
- }
- case SPREADSHEET_CONTEXT_MODIFIER: {
- return (SpreadsheetContext *)spreadsheet_context_modifier_new();
- }
- case SPREADSHEET_CONTEXT_NODE: {
- return (SpreadsheetContext *)spreadsheet_context_node_new();
- }
- }
- BLI_assert_unreachable();
- return nullptr;
-}
-
-SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context)
-{
- switch (old_context->type) {
- case SPREADSHEET_CONTEXT_OBJECT: {
- return (SpreadsheetContext *)spreadsheet_context_object_copy(
- (const SpreadsheetContextObject *)old_context);
- }
- case SPREADSHEET_CONTEXT_MODIFIER: {
- return (SpreadsheetContext *)spreadsheet_context_modifier_copy(
- (const SpreadsheetContextModifier *)old_context);
- }
- case SPREADSHEET_CONTEXT_NODE: {
- return (SpreadsheetContext *)spreadsheet_context_node_copy(
- (const SpreadsheetContextNode *)old_context);
- }
- }
- BLI_assert_unreachable();
- return nullptr;
-}
-
-static void spreadsheet_context_hash(const SpreadsheetContext *context, BLI_HashMurmur2A *mm2)
-{
- BLI_hash_mm2a_add_int(mm2, context->type);
- switch (context->type) {
- case SPREADSHEET_CONTEXT_OBJECT: {
- spreadsheet_context_object_hash((const SpreadsheetContextObject *)context, mm2);
- break;
- }
- case SPREADSHEET_CONTEXT_MODIFIER: {
- spreadsheet_context_modifier_hash((const SpreadsheetContextModifier *)context, mm2);
- break;
- }
- case SPREADSHEET_CONTEXT_NODE: {
- spreadsheet_context_node_hash((const SpreadsheetContextNode *)context, mm2);
- break;
- }
- }
-}
-
-void spreadsheet_context_free(SpreadsheetContext *context)
-{
- switch (context->type) {
- case SPREADSHEET_CONTEXT_OBJECT: {
- return spreadsheet_context_object_free((SpreadsheetContextObject *)context);
- }
- case SPREADSHEET_CONTEXT_MODIFIER: {
- return spreadsheet_context_modifier_free((SpreadsheetContextModifier *)context);
- }
- case SPREADSHEET_CONTEXT_NODE: {
- return spreadsheet_context_node_free((SpreadsheetContextNode *)context);
- }
- }
- BLI_assert_unreachable();
-}
-
-/**
- * Tag any data relevant to the spreadsheet's context for recalculation in order to collect
- * information to display in the editor, which may be cached during evaluation.
- * \return True when any data has been tagged for update.
- */
-static bool spreadsheet_context_update_tag(SpaceSpreadsheet *sspreadsheet)
-{
- using namespace blender;
- Vector<const SpreadsheetContext *> context_path = sspreadsheet->context_path;
- if (context_path.is_empty()) {
- return false;
- }
- if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
- return false;
- }
- SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
- Object *object = object_context->object;
- if (object == nullptr) {
- return false;
- }
- if (context_path.size() == 1) {
- /* No need to reevaluate, when the final or original object is viewed. */
- return false;
- }
-
- DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
- return true;
-}
-
-} // namespace blender::ed::spreadsheet
-
-SpreadsheetContext *ED_spreadsheet_context_new(int type)
-{
- return blender::ed::spreadsheet::spreadsheet_context_new((eSpaceSpreadsheet_ContextType)type);
-}
-
-void ED_spreadsheet_context_free(struct SpreadsheetContext *context)
-{
- blender::ed::spreadsheet::spreadsheet_context_free(context);
-}
-
-void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet)
-{
- LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) {
- ED_spreadsheet_context_free(context);
- }
- BLI_listbase_clear(&sspreadsheet->context_path);
-}
-
-bool ED_spreadsheet_context_path_update_tag(SpaceSpreadsheet *sspreadsheet)
-{
- return blender::ed::spreadsheet::spreadsheet_context_update_tag(sspreadsheet);
-}
-
-uint64_t ED_spreadsheet_context_path_hash(const SpaceSpreadsheet *sspreadsheet)
-{
- BLI_HashMurmur2A mm2;
- BLI_hash_mm2a_init(&mm2, 1234);
- LISTBASE_FOREACH (const SpreadsheetContext *, context, &sspreadsheet->context_path) {
- blender::ed::spreadsheet::spreadsheet_context_hash(context, &mm2);
- }
- return BLI_hash_mm2a_end(&mm2);
-}
-
-void ED_spreadsheet_context_path_set_geometry_node(struct SpaceSpreadsheet *sspreadsheet,
- struct SpaceNode *snode,
- struct bNode *node)
-{
- using namespace blender::ed::spreadsheet;
-
- Object *object = (Object *)snode->id;
- /* Try to find the modifier the node tree belongs to. */
- ModifierData *modifier = BKE_object_active_modifier(object);
- if (modifier && modifier->type != eModifierType_Nodes) {
- modifier = nullptr;
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- if (md->type == eModifierType_Nodes) {
- NodesModifierData *nmd = (NodesModifierData *)md;
- if (nmd->node_group == snode->nodetree) {
- modifier = md;
- break;
- }
- }
- }
- }
- if (modifier == nullptr) {
- return;
- }
-
- ED_spreadsheet_context_path_clear(sspreadsheet);
-
- {
- SpreadsheetContextObject *context = spreadsheet_context_object_new();
- context->object = object;
- BLI_addtail(&sspreadsheet->context_path, context);
- }
- {
- SpreadsheetContextModifier *context = spreadsheet_context_modifier_new();
- context->modifier_name = BLI_strdup(modifier->name);
- BLI_addtail(&sspreadsheet->context_path, context);
- }
- {
- int i;
- LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
- if (i == 0) {
- continue;
- }
- SpreadsheetContextNode *context = spreadsheet_context_node_new();
- context->node_name = BLI_strdup(path->node_name);
- BLI_addtail(&sspreadsheet->context_path, context);
- }
- }
- {
- SpreadsheetContextNode *context = spreadsheet_context_node_new();
- context->node_name = BLI_strdup(node->name);
- BLI_addtail(&sspreadsheet->context_path, context);
- }
-
- sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE;
-}
-
-void ED_spreadsheet_context_paths_set_geometry_node(Main *bmain, SpaceNode *snode, bNode *node)
-{
- wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
- if (wm == nullptr) {
- return;
- }
- LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
- bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- SpaceLink *sl = (SpaceLink *)area->spacedata.first;
- if (sl->spacetype == SPACE_SPREADSHEET) {
- SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- if ((sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) == 0) {
- const uint64_t context_hash_before = ED_spreadsheet_context_path_hash(sspreadsheet);
- ED_spreadsheet_context_path_set_geometry_node(sspreadsheet, snode, node);
- const uint64_t context_hash_after = ED_spreadsheet_context_path_hash(sspreadsheet);
- if (context_hash_before != context_hash_after) {
- ED_spreadsheet_context_path_update_tag(sspreadsheet);
- }
- ED_area_tag_redraw(area);
- }
- }
- }
- }
-}
-
-void ED_spreadsheet_context_path_set_evaluated_object(SpaceSpreadsheet *sspreadsheet,
- Object *object)
-{
- using namespace blender::ed::spreadsheet;
- ED_spreadsheet_context_path_clear(sspreadsheet);
-
- SpreadsheetContextObject *context = spreadsheet_context_object_new();
- context->object = object;
- BLI_addtail(&sspreadsheet->context_path, context);
-}
-
-static bScreen *find_screen_to_search_for_context(wmWindow *window,
- SpaceSpreadsheet *current_space)
-{
- bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
- if (ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL)) {
- /* If the spreadsheet is maximized, try to find the context in the unmaximized screen. */
- ScrArea *main_area = (ScrArea *)screen->areabase.first;
- SpaceLink *sl = (SpaceLink *)main_area->spacedata.first;
- if (sl == (SpaceLink *)current_space) {
- return main_area->full;
- }
- }
- return screen;
-}
-
-void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspreadsheet)
-{
- ED_spreadsheet_context_path_clear(sspreadsheet);
-
- Main *bmain = CTX_data_main(C);
- wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
- if (wm == nullptr) {
- return;
- }
-
- if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
- LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
- bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet);
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- SpaceLink *sl = (SpaceLink *)area->spacedata.first;
- if (sl == nullptr) {
- continue;
- }
- if (sl->spacetype == SPACE_NODE) {
- SpaceNode *snode = (SpaceNode *)sl;
- if (snode->edittree != nullptr) {
- if (snode->edittree->type == NTREE_GEOMETRY) {
- LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
- if (node->type == GEO_NODE_VIEWER) {
- if (node->flag & NODE_DO_OUTPUT) {
- ED_spreadsheet_context_path_set_geometry_node(sspreadsheet, snode, node);
- return;
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- Object *active_object = CTX_data_active_object(C);
- if (active_object != nullptr) {
- ED_spreadsheet_context_path_set_evaluated_object(sspreadsheet, active_object);
- return;
- }
-}
-
-bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet *sspreadsheet)
-{
- Main *bmain = CTX_data_main(C);
- wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
- if (wm == nullptr) {
- return false;
- }
- Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path;
- if (context_path.is_empty()) {
- return false;
- }
- if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
- return false;
- }
- Object *object = ((SpreadsheetContextObject *)context_path[0])->object;
- if (object == nullptr) {
- return false;
- }
- if (context_path.size() == 1) {
- if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
- return false;
- }
- Object *active_object = CTX_data_active_object(C);
- return object == active_object;
- }
- if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
- return false;
- }
- if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
- return false;
- }
- const char *modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name;
- const ModifierData *modifier = BKE_modifiers_findby_name(object, modifier_name);
- if (modifier == nullptr) {
- return false;
- }
- const bool modifier_is_active = modifier->flag & eModifierFlag_Active;
- if (modifier->type != eModifierType_Nodes) {
- return false;
- }
- bNodeTree *root_node_tree = ((NodesModifierData *)modifier)->node_group;
- if (root_node_tree == nullptr) {
- return false;
- }
- const Span<SpreadsheetContext *> node_context_path = context_path.as_span().drop_front(2);
- if (node_context_path.is_empty()) {
- return false;
- }
-
- LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
- bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet);
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- SpaceLink *sl = (SpaceLink *)area->spacedata.first;
- if (sl == nullptr) {
- continue;
- }
- if (sl->spacetype != SPACE_NODE) {
- continue;
- }
- SpaceNode *snode = (SpaceNode *)sl;
- if (snode->nodetree != root_node_tree) {
- continue;
- }
- if (!modifier_is_active) {
- if (!(snode->flag & SNODE_PIN)) {
- /* Node tree has to be pinned when the modifier is not active. */
- continue;
- }
- }
- if (snode->id != &object->id) {
- continue;
- }
- Vector<bNodeTreePath *> tree_path = snode->treepath;
- if (node_context_path.size() != tree_path.size()) {
- continue;
- }
- int valid_count = 0;
- for (const int i : IndexRange(tree_path.size() - 1)) {
- if (node_context_path[i]->type != SPREADSHEET_CONTEXT_NODE) {
- break;
- }
- SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)node_context_path[i];
- if (!STREQ(node_context->node_name, tree_path[i + 1]->node_name)) {
- break;
- }
- valid_count++;
- }
- if (valid_count != tree_path.size() - 1) {
- continue;
- }
- SpreadsheetContext *last_context = node_context_path.last();
- if (last_context->type != SPREADSHEET_CONTEXT_NODE) {
- return false;
- }
- const char *node_name = ((SpreadsheetContextNode *)last_context)->node_name;
- bNode *node = nodeFindNodebyName(snode->edittree, node_name);
- if (node == nullptr) {
- return false;
- }
- if (node->type != GEO_NODE_VIEWER) {
- return false;
- }
- if (!(node->flag & NODE_DO_OUTPUT)) {
- return false;
- }
- return true;
- }
- }
- return false;
-}
-
-bool ED_spreadsheet_context_path_exists(Main *UNUSED(bmain), SpaceSpreadsheet *sspreadsheet)
-{
- Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path;
- if (context_path.is_empty()) {
- return false;
- }
- if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
- return false;
- }
- Object *object = ((SpreadsheetContextObject *)context_path[0])->object;
- if (object == nullptr) {
- return false;
- }
- if (context_path.size() == 1) {
- return true;
- }
- if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
- return false;
- }
- const char *modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name;
- const ModifierData *modifier = BKE_modifiers_findby_name(object, modifier_name);
- if (modifier == nullptr) {
- return false;
- }
- if (modifier->type != eModifierType_Nodes) {
- return false;
- }
- bNodeTree *root_node_tree = ((NodesModifierData *)modifier)->node_group;
- if (root_node_tree == nullptr) {
- return false;
- }
- const Span<SpreadsheetContext *> node_context_path = context_path.as_span().drop_front(2);
- if (node_context_path.is_empty()) {
- return false;
- }
- bNodeTree *node_tree = root_node_tree;
- for (const int i : node_context_path.index_range()) {
- if (node_context_path[i]->type != SPREADSHEET_CONTEXT_NODE) {
- return false;
- }
- const char *node_name = ((SpreadsheetContextNode *)node_context_path[i])->node_name;
- bNode *node = nodeFindNodebyName(node_tree, node_name);
- if (node == nullptr) {
- return false;
- }
- if (node->type == GEO_NODE_VIEWER) {
- if (i == node_context_path.index_range().last()) {
- return true;
- }
- return false;
- }
- if (node->id != nullptr) {
- if (GS(node->id->name) != ID_NT) {
- return false;
- }
- node_tree = (bNodeTree *)node->id;
- }
- else {
- return false;
- }
- }
- return false;
-}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.hh b/source/blender/editors/space_spreadsheet/spreadsheet_context.hh
deleted file mode 100644
index 758ae392894..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_context.hh
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-#include "DNA_space_types.h"
-
-namespace blender::ed::spreadsheet {
-
-SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type);
-SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context);
-void spreadsheet_context_free(SpreadsheetContext *context);
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index fd2ac4d39a1..8af12590b0f 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -98,7 +98,8 @@ void GeometryDataSource::foreach_default_column_ids(
}
SpreadsheetColumnID column_id;
column_id.name = (char *)attribute_id.name().data();
- fn(column_id, false);
+ const bool is_front = attribute_id.name() == ".viewer";
+ fn(column_id, is_front);
return true;
});
@@ -228,7 +229,12 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
return {};
}
- return std::make_unique<ColumnValues>(column_id.name, std::move(varray));
+ StringRefNull column_display_name = column_id.name;
+ if (column_display_name == ".viewer") {
+ column_display_name = "Viewer";
+ }
+
+ return std::make_unique<ColumnValues>(column_display_name, std::move(varray));
}
int GeometryDataSource::tot_rows() const
@@ -463,7 +469,7 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
}
else {
- if (BLI_listbase_count(&sspreadsheet->context_path) == 1) {
+ if (BLI_listbase_count(&sspreadsheet->viewer_path.path) == 1) {
/* Use final evaluated object. */
if (object_eval->runtime.geometry_set_eval != nullptr) {
geometry_set = *object_eval->runtime.geometry_set_eval;
@@ -471,8 +477,8 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
}
else {
if (const ViewerNodeLog *viewer_log =
- nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_spreadsheet(
- *sspreadsheet)) {
+ nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_path(
+ sspreadsheet->viewer_path)) {
geometry_set = viewer_log->geometry;
}
}
@@ -481,25 +487,6 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
return geometry_set;
}
-static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet,
- Map<std::string, GField> &r_fields)
-{
- if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
- return;
- }
- if (BLI_listbase_count(&sspreadsheet->context_path) <= 1) {
- /* No viewer is currently referenced by the context path. */
- return;
- }
- if (const ViewerNodeLog *viewer_log =
- nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_spreadsheet(
- *sspreadsheet)) {
- if (viewer_log->field) {
- r_fields.add("Viewer", viewer_log->field);
- }
- }
-}
-
class GeometryComponentCacheKey : public SpreadsheetCache::Key {
public:
/* Use the pointer to the geometry component as a key to detect when the geometry changed. */
@@ -531,38 +518,6 @@ class GeometryComponentCacheValue : public SpreadsheetCache::Value {
Map<std::pair<eAttrDomain, GField>, GArray<>> arrays;
};
-static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
- const GeometryComponent &component,
- ExtraColumns &r_extra_columns)
-{
- Map<std::string, GField> fields_to_show;
- find_fields_to_evaluate(sspreadsheet, fields_to_show);
-
- GeometryComponentCacheValue &cache =
- sspreadsheet->runtime->cache.lookup_or_add<GeometryComponentCacheValue>(
- std::make_unique<GeometryComponentCacheKey>(component));
-
- const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain;
- const int domain_num = component.attribute_domain_size(domain);
- for (const auto item : fields_to_show.items()) {
- const StringRef name = item.key;
- const GField &field = item.value;
-
- /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */
- GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
- GArray<> evaluated_array(field.cpp_type(), domain_num);
-
- bke::GeometryFieldContext field_context{component, domain};
- fn::FieldEvaluator field_evaluator{field_context, domain_num};
- field_evaluator.add_with_destination(field, evaluated_array);
- field_evaluator.evaluate();
- return evaluated_array;
- });
-
- r_extra_columns.add(name, evaluated_array.as_span());
- }
-}
-
std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
@@ -574,15 +529,11 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
return {};
}
- const GeometryComponent &component = *geometry_set.get_component_for_read(component_type);
- ExtraColumns extra_columns;
- add_fields_as_extra_columns(sspreadsheet, component, extra_columns);
-
if (component_type == GEO_COMPONENT_TYPE_VOLUME) {
return std::make_unique<VolumeDataSource>(std::move(geometry_set));
}
return std::make_unique<GeometryDataSource>(
- object_eval, std::move(geometry_set), component_type, domain, std::move(extra_columns));
+ object_eval, std::move(geometry_set), component_type, domain);
}
} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
index 71bc4768949..478b3372427 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -54,7 +54,7 @@ class GeometryDataSource : public DataSource {
GeometrySet geometry_set,
const GeometryComponentType component_type,
const eAttrDomain domain,
- ExtraColumns extra_columns)
+ ExtraColumns extra_columns = {})
: object_eval_(object_eval),
geometry_set_(std::move(geometry_set)),
component_(geometry_set_.get_component_for_read(component_type)),
diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc
index 7398a109b19..345958d86ec 100644
--- a/source/blender/editors/space_view3d/space_view3d.cc
+++ b/source/blender/editors/space_view3d/space_view3d.cc
@@ -44,6 +44,7 @@
#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
+#include "BKE_viewer_path.h"
#include "BKE_workspace.h"
#include "ED_object.h"
@@ -53,6 +54,7 @@
#include "ED_space_api.h"
#include "ED_transform.h"
#include "ED_undo.h"
+#include "ED_viewer_path.hh"
#include "GPU_matrix.h"
@@ -322,6 +324,8 @@ static void view3d_free(SpaceLink *sl)
IDP_FreeProperty(vd->shading.prop);
vd->shading.prop = nullptr;
}
+
+ BKE_viewer_path_clear(&vd->viewer_path);
}
/* spacetype; init callback */
@@ -360,6 +364,8 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl)
v3dn->shading.prop = IDP_CopyProperty(v3do->shading.prop);
}
+ BKE_viewer_path_copy(&v3dn->viewer_path, &v3do->viewer_path);
+
/* copy or clear inside new stuff */
return (SpaceLink *)v3dn;
@@ -1319,6 +1325,16 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params)
/* In case the region displays workspace settings. */
ED_region_tag_redraw(region);
break;
+ case NC_VIEWER_PATH: {
+ if (v3d->flag2 & V3D_SHOW_VIEWER) {
+ ViewLayer *view_layer = WM_window_get_active_view_layer(window);
+ if (Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer)) {
+ ED_render_view3d_update(depsgraph, window, area, true);
+ }
+ ED_region_tag_redraw(region);
+ }
+ break;
+ }
}
}
@@ -1980,6 +1996,7 @@ static void view3d_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRema
/* Object centers in local-view aren't used, see: T52663 */
view3d_id_remap_v3d(area, slink, view3d->localvd, mappings, true);
}
+ BKE_viewer_path_id_remap(&view3d->viewer_path, mappings);
}
static void view3d_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
@@ -2003,6 +2020,8 @@ static void view3d_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
BKE_screen_view3d_shading_blend_read_data(reader, &v3d->shading);
BKE_screen_view3d_do_versions_250(v3d, &sl->regionbase);
+
+ BKE_viewer_path_blend_read_data(reader, &v3d->viewer_path);
}
static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
@@ -2015,6 +2034,8 @@ static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLi
if (v3d->localvd) {
BLO_read_id_address(reader, parent_id->lib, &v3d->localvd->camera);
}
+
+ BKE_viewer_path_blend_read_lib(reader, parent_id->lib, &v3d->viewer_path);
}
static void view3d_blend_write(BlendWriter *writer, SpaceLink *sl)
@@ -2027,6 +2048,8 @@ static void view3d_blend_write(BlendWriter *writer, SpaceLink *sl)
}
BKE_screen_view3d_shading_blend_write(writer, &v3d->shading);
+
+ BKE_viewer_path_blend_write(writer, &v3d->viewer_path);
}
void ED_spacetype_view3d(void)
diff --git a/source/blender/editors/space_view3d/view3d_draw.cc b/source/blender/editors/space_view3d/view3d_draw.cc
index 9686609fa65..bc8800120f8 100644
--- a/source/blender/editors/space_view3d/view3d_draw.cc
+++ b/source/blender/editors/space_view3d/view3d_draw.cc
@@ -55,6 +55,7 @@
#include "ED_screen_types.h"
#include "ED_transform.h"
#include "ED_view3d_offscreen.h"
+#include "ED_viewer_path.hh"
#include "DEG_depsgraph_query.h"
@@ -1294,7 +1295,7 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y
* frame-number, collection, object name, bone name (if available), marker name (if available).
*/
static void draw_selected_name(
- Scene *scene, ViewLayer *view_layer, Object *ob, int xoffset, int *yoffset)
+ const View3D *v3d, Scene *scene, ViewLayer *view_layer, Object *ob, int xoffset, int *yoffset)
{
const int cfra = scene->r.cfra;
const char *msg_pin = " (Pinned)";
@@ -1409,6 +1410,12 @@ static void draw_selected_name(
s += sprintf(s, " <%s>", markern);
}
+ if (v3d->flag2 & V3D_SHOW_VIEWER) {
+ if (!BLI_listbase_is_empty(&v3d->viewer_path.path)) {
+ s += sprintf(s, IFACE_(" (Viewer)"));
+ }
+ }
+
BLF_enable(font_id, BLF_SHADOW);
BLF_shadow(font_id, 5, float4{0.0f, 0.0f, 0.0f, 1.0f});
BLF_shadow_offset(font_id, 1, -1);
@@ -1502,7 +1509,7 @@ void view3d_draw_region_info(const bContext *C, ARegion *region)
if (U.uiflag & USER_DRAWVIEWINFO) {
BKE_view_layer_synced_ensure(scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
- draw_selected_name(scene, view_layer, ob, xoffset, &yoffset);
+ draw_selected_name(v3d, scene, view_layer, ob, xoffset, &yoffset);
}
if (v3d->gridflag & (V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_Z)) {
@@ -1558,11 +1565,23 @@ RenderEngineType *ED_view3d_engine_type(const Scene *scene, int drawtype)
return type;
}
+static void view3d_update_viewer_path(const bContext *C)
+{
+ View3D *v3d = CTX_wm_view3d(C);
+ WorkSpace *workspace = CTX_wm_workspace(C);
+ /* Always use viewer path from workspace, pinning is not supported currently. */
+ if (!BKE_viewer_path_equal(&v3d->viewer_path, &workspace->viewer_path)) {
+ BKE_viewer_path_clear(&v3d->viewer_path);
+ BKE_viewer_path_copy(&v3d->viewer_path, &workspace->viewer_path);
+ }
+}
+
void view3d_main_region_draw(const bContext *C, ARegion *region)
{
Main *bmain = CTX_data_main(C);
View3D *v3d = CTX_wm_view3d(C);
+ view3d_update_viewer_path(C);
view3d_draw_view(C, region);
DRW_cache_free_old_subdiv();
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index a9e6adc6e60..164e594b37c 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -28,6 +28,7 @@ set(SRC
ed_util.c
ed_util_imbuf.c
ed_util_ops.cc
+ ed_viewer_path.cc
gizmo_utils.c
numinput.c
select_utils.c
diff --git a/source/blender/editors/util/ed_viewer_path.cc b/source/blender/editors/util/ed_viewer_path.cc
new file mode 100644
index 00000000000..5c03367cba8
--- /dev/null
+++ b/source/blender/editors/util/ed_viewer_path.cc
@@ -0,0 +1,362 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "ED_viewer_path.hh"
+#include "ED_screen.h"
+
+#include "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_node_runtime.hh"
+#include "BKE_workspace.h"
+
+#include "BLI_listbase.h"
+#include "BLI_vector.hh"
+
+#include "DNA_modifier_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+
+namespace blender::ed::viewer_path {
+
+static void viewer_path_for_geometry_node(const SpaceNode &snode,
+ const bNode &node,
+ ViewerPath &r_dst)
+{
+ BKE_viewer_path_init(&r_dst);
+
+ Object *ob = reinterpret_cast<Object *>(snode.id);
+ IDViewerPathElem *id_elem = BKE_viewer_path_elem_new_id();
+ id_elem->id = &ob->id;
+ BLI_addtail(&r_dst.path, id_elem);
+
+ NodesModifierData *modifier = nullptr;
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type != eModifierType_Nodes) {
+ continue;
+ }
+ NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
+ if (nmd->node_group != snode.nodetree) {
+ continue;
+ }
+ if (snode.flag & SNODE_PIN) {
+ /* If the node group is pinned, use the first matching modifier. This can be improved by
+ * storing the modifier name in the node editor when the context is pinned. */
+ modifier = nmd;
+ break;
+ }
+ if (md->flag & eModifierFlag_Active) {
+ modifier = nmd;
+ }
+ }
+
+ ModifierViewerPathElem *modifier_elem = BKE_viewer_path_elem_new_modifier();
+ modifier_elem->modifier_name = BLI_strdup(modifier->modifier.name);
+ BLI_addtail(&r_dst.path, modifier_elem);
+
+ Vector<const bNodeTreePath *, 16> tree_path = snode.treepath;
+ for (const bNodeTreePath *tree_path_elem : tree_path.as_span().drop_front(1)) {
+ NodeViewerPathElem *node_elem = BKE_viewer_path_elem_new_node();
+ node_elem->node_name = BLI_strdup(tree_path_elem->node_name);
+ BLI_addtail(&r_dst.path, node_elem);
+ }
+
+ NodeViewerPathElem *viewer_node_elem = BKE_viewer_path_elem_new_node();
+ viewer_node_elem->node_name = BLI_strdup(node.name);
+ BLI_addtail(&r_dst.path, viewer_node_elem);
+}
+
+void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
+{
+ wmWindowManager *wm = (wmWindowManager *)bmain.wm.first;
+ if (wm == nullptr) {
+ return;
+ }
+ LISTBASE_FOREACH (bNode *, iter_node, &snode.edittree->nodes) {
+ if (iter_node->type == GEO_NODE_VIEWER) {
+ SET_FLAG_FROM_TEST(iter_node->flag, iter_node == &node, NODE_DO_OUTPUT);
+ }
+ }
+ ViewerPath new_viewer_path{};
+ BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&new_viewer_path); });
+ viewer_path_for_geometry_node(snode, node, new_viewer_path);
+
+ bool found_view3d_with_enabled_viewer = false;
+ View3D *any_view3d_without_viewer = nullptr;
+ LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
+ WorkSpace *workspace = BKE_workspace_active_get(window->workspace_hook);
+ bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ SpaceSpreadsheet &sspreadsheet = *reinterpret_cast<SpaceSpreadsheet *>(sl);
+ if (!(sspreadsheet.flag & SPREADSHEET_FLAG_PINNED)) {
+ sspreadsheet.object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE;
+ }
+ }
+ else if (sl->spacetype == SPACE_VIEW3D) {
+ View3D &v3d = *reinterpret_cast<View3D *>(sl);
+ if (v3d.flag2 & V3D_SHOW_VIEWER) {
+ found_view3d_with_enabled_viewer = true;
+ }
+ else {
+ any_view3d_without_viewer = &v3d;
+ }
+ }
+ }
+
+ /* Enable viewer in one viewport if it is disable in all of them. */
+ if (!found_view3d_with_enabled_viewer && any_view3d_without_viewer != nullptr) {
+ any_view3d_without_viewer->flag2 |= V3D_SHOW_VIEWER;
+ }
+
+ BKE_viewer_path_clear(&workspace->viewer_path);
+ BKE_viewer_path_copy(&workspace->viewer_path, &new_viewer_path);
+
+ /* Make sure the viewed data becomes available. */
+ DEG_id_tag_update(snode.id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_VIEWER_PATH, nullptr);
+ }
+}
+
+Object *parse_object_only(const ViewerPath &viewer_path)
+{
+ if (BLI_listbase_count(&viewer_path.path) != 1) {
+ return nullptr;
+ }
+ const ViewerPathElem *elem = static_cast<ViewerPathElem *>(viewer_path.path.first);
+ if (elem->type != VIEWER_PATH_ELEM_TYPE_ID) {
+ return nullptr;
+ }
+ ID *id = reinterpret_cast<const IDViewerPathElem *>(elem)->id;
+ if (id == nullptr) {
+ return nullptr;
+ }
+ if (GS(id->name) != ID_OB) {
+ return nullptr;
+ }
+ return reinterpret_cast<Object *>(id);
+}
+
+std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer(
+ const ViewerPath &viewer_path)
+{
+ const Vector<const ViewerPathElem *, 16> elems_vec = viewer_path.path;
+ if (elems_vec.size() < 3) {
+ /* Need at least the object, modifier and viewer node name. */
+ return std::nullopt;
+ }
+ Span<const ViewerPathElem *> remaining_elems = elems_vec;
+ const ViewerPathElem &id_elem = *remaining_elems[0];
+ if (id_elem.type != VIEWER_PATH_ELEM_TYPE_ID) {
+ return std::nullopt;
+ }
+ ID *root_id = reinterpret_cast<const IDViewerPathElem &>(id_elem).id;
+ if (root_id == nullptr) {
+ return std::nullopt;
+ }
+ if (GS(root_id->name) != ID_OB) {
+ return std::nullopt;
+ }
+ Object *root_ob = reinterpret_cast<Object *>(root_id);
+ remaining_elems = remaining_elems.drop_front(1);
+ const ViewerPathElem &modifier_elem = *remaining_elems[0];
+ if (modifier_elem.type != VIEWER_PATH_ELEM_TYPE_MODIFIER) {
+ return std::nullopt;
+ }
+ const char *modifier_name =
+ reinterpret_cast<const ModifierViewerPathElem &>(modifier_elem).modifier_name;
+ if (modifier_name == nullptr) {
+ return std::nullopt;
+ }
+ remaining_elems = remaining_elems.drop_front(1);
+ Vector<StringRefNull> node_names;
+ for (const ViewerPathElem *elem : remaining_elems) {
+ if (elem->type != VIEWER_PATH_ELEM_TYPE_NODE) {
+ return std::nullopt;
+ }
+ const char *node_name = reinterpret_cast<const NodeViewerPathElem *>(elem)->node_name;
+ if (node_name == nullptr) {
+ return std::nullopt;
+ }
+ node_names.append(node_name);
+ }
+ const StringRefNull viewer_node_name = node_names.pop_last();
+ return ViewerPathForGeometryNodesViewer{root_ob, modifier_name, node_names, viewer_node_name};
+}
+
+bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed_viewer_path)
+{
+ const NodesModifierData *modifier = nullptr;
+ LISTBASE_FOREACH (const ModifierData *, md, &parsed_viewer_path.object->modifiers) {
+ if (md->type != eModifierType_Nodes) {
+ continue;
+ }
+ if (md->name != parsed_viewer_path.modifier_name) {
+ continue;
+ }
+ modifier = reinterpret_cast<const NodesModifierData *>(md);
+ break;
+ }
+ if (modifier == nullptr) {
+ return false;
+ }
+ if (modifier->node_group == nullptr) {
+ return false;
+ }
+ const bNodeTree *ngroup = modifier->node_group;
+ ngroup->ensure_topology_cache();
+ for (const StringRefNull group_node_name : parsed_viewer_path.group_node_names) {
+ const bNode *group_node = nullptr;
+ for (const bNode *node : ngroup->group_nodes()) {
+ if (node->name != group_node_name) {
+ continue;
+ }
+ group_node = node;
+ break;
+ }
+ if (group_node == nullptr) {
+ return false;
+ }
+ if (group_node->id == nullptr) {
+ return false;
+ }
+ ngroup = reinterpret_cast<const bNodeTree *>(group_node->id);
+ }
+ const bNode *viewer_node = nullptr;
+ for (const bNode *node : ngroup->nodes_by_type("GeometryNodeViewer")) {
+ if (node->name != parsed_viewer_path.viewer_node_name) {
+ continue;
+ }
+ viewer_node = node;
+ break;
+ }
+ if (viewer_node == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+bool is_active_geometry_nodes_viewer(const bContext &C,
+ const ViewerPathForGeometryNodesViewer &parsed_viewer_path)
+{
+ const NodesModifierData *modifier = nullptr;
+ LISTBASE_FOREACH (const ModifierData *, md, &parsed_viewer_path.object->modifiers) {
+ if (md->name != parsed_viewer_path.modifier_name) {
+ continue;
+ }
+ if (md->type != eModifierType_Nodes) {
+ return false;
+ }
+ modifier = reinterpret_cast<const NodesModifierData *>(md);
+ break;
+ }
+ if (modifier == nullptr) {
+ return false;
+ }
+ if (modifier->node_group == nullptr) {
+ return false;
+ }
+ const bool modifier_is_active = modifier->modifier.flag & eModifierFlag_Active;
+
+ const Main *bmain = CTX_data_main(&C);
+ const wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
+ if (wm == nullptr) {
+ return false;
+ }
+ LISTBASE_FOREACH (const wmWindow *, window, &wm->windows) {
+ const bScreen *active_screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ Vector<const bScreen *> screens = {active_screen};
+ if (ELEM(active_screen->state, SCREENMAXIMIZED, SCREENFULL)) {
+ const ScrArea *area = static_cast<ScrArea *>(active_screen->areabase.first);
+ screens.append(area->full);
+ }
+ for (const bScreen *screen : screens) {
+ LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) {
+ const SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
+ if (sl == nullptr) {
+ continue;
+ }
+ if (sl->spacetype != SPACE_NODE) {
+ continue;
+ }
+ const SpaceNode &snode = *reinterpret_cast<const SpaceNode *>(sl);
+ if (!modifier_is_active) {
+ if (!(snode.flag & SNODE_PIN)) {
+ /* Node tree has to be pinned when the modifier is not active. */
+ continue;
+ }
+ }
+ if (snode.id != &parsed_viewer_path.object->id) {
+ continue;
+ }
+ if (snode.nodetree != modifier->node_group) {
+ continue;
+ }
+ Vector<const bNodeTreePath *, 16> tree_path = snode.treepath;
+ if (tree_path.size() != parsed_viewer_path.group_node_names.size() + 1) {
+ continue;
+ }
+ bool valid_path = true;
+ for (const int i : parsed_viewer_path.group_node_names.index_range()) {
+ if (parsed_viewer_path.group_node_names[i] != tree_path[i + 1]->node_name) {
+ valid_path = false;
+ break;
+ }
+ }
+ if (!valid_path) {
+ continue;
+ }
+ const bNodeTree *ngroup = snode.edittree;
+ ngroup->ensure_topology_cache();
+ const bNode *viewer_node = nullptr;
+ for (const bNode *node : ngroup->nodes_by_type("GeometryNodeViewer")) {
+ if (node->name != parsed_viewer_path.viewer_node_name) {
+ continue;
+ }
+ viewer_node = node;
+ }
+ if (viewer_node == nullptr) {
+ continue;
+ }
+ if (!(viewer_node->flag & NODE_DO_OUTPUT)) {
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode)
+{
+ const std::optional<ViewerPathForGeometryNodesViewer> parsed_viewer_path =
+ parse_geometry_nodes_viewer(viewer_path);
+ if (!parsed_viewer_path.has_value()) {
+ return nullptr;
+ }
+
+ snode.edittree->ensure_topology_cache();
+ bNode *possible_viewer = nullptr;
+ for (bNode *node : snode.edittree->nodes_by_type("GeometryNodeViewer")) {
+ if (node->name == parsed_viewer_path->viewer_node_name) {
+ possible_viewer = node;
+ break;
+ }
+ }
+ if (possible_viewer == nullptr) {
+ return nullptr;
+ }
+ ViewerPath tmp_viewer_path;
+ BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&tmp_viewer_path); });
+ viewer_path_for_geometry_node(snode, *possible_viewer, tmp_viewer_path);
+
+ if (BKE_viewer_path_equal(&viewer_path, &tmp_viewer_path)) {
+ return possible_viewer;
+ }
+ return nullptr;
+}
+
+} // namespace blender::ed::viewer_path