diff options
Diffstat (limited to 'source/blender/editors/util/ed_viewer_path.cc')
-rw-r--r-- | source/blender/editors/util/ed_viewer_path.cc | 362 |
1 files changed, 362 insertions, 0 deletions
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 |