/* 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(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(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 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(area->spacedata.first); if (sl->spacetype == SPACE_SPREADSHEET) { SpaceSpreadsheet &sspreadsheet = *reinterpret_cast(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(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(viewer_path.path.first); if (elem->type != VIEWER_PATH_ELEM_TYPE_ID) { return nullptr; } ID *id = reinterpret_cast(elem)->id; if (id == nullptr) { return nullptr; } if (GS(id->name) != ID_OB) { return nullptr; } return reinterpret_cast(id); } std::optional parse_geometry_nodes_viewer( const ViewerPath &viewer_path) { const Vector elems_vec = viewer_path.path; if (elems_vec.size() < 3) { /* Need at least the object, modifier and viewer node name. */ return std::nullopt; } Span 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(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(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(modifier_elem).modifier_name; if (modifier_name == nullptr) { return std::nullopt; } remaining_elems = remaining_elems.drop_front(1); Vector 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(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(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(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(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(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 screens = {active_screen}; if (ELEM(active_screen->state, SCREENMAXIMIZED, SCREENFULL)) { const ScrArea *area = static_cast(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(area->spacedata.first); if (sl == nullptr) { continue; } if (sl->spacetype != SPACE_NODE) { continue; } const SpaceNode &snode = *reinterpret_cast(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 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 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