Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/space_node/node_relationships.cc
| Show All 21 Lines | |||||
| #include "BKE_node_runtime.hh" | #include "BKE_node_runtime.hh" | ||||
| #include "BKE_node_tree_update.h" | #include "BKE_node_tree_update.h" | ||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "ED_node.h" /* own include */ | #include "ED_node.h" /* own include */ | ||||
| #include "ED_render.h" | #include "ED_render.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_space_api.h" | #include "ED_space_api.h" | ||||
| #include "ED_spreadsheet.h" | |||||
| #include "ED_util.h" | #include "ED_util.h" | ||||
| #include "ED_viewer_path.hh" | |||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "RNA_prototypes.h" | #include "RNA_prototypes.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| ▲ Show 20 Lines • Show All 450 Lines • ▼ Show 20 Lines | static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree, | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| static bool is_viewer_node(const bNode &node) | static bool is_viewer_node(const bNode &node) | ||||
| { | { | ||||
| return ELEM(node.type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER); | 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) | static bool is_viewer_socket_in_viewer(const bNodeSocket &socket) | ||||
| { | { | ||||
| const bNode &node = socket.owner_node(); | const bNode &node = socket.owner_node(); | ||||
| BLI_assert(is_viewer_node(node)); | BLI_assert(is_viewer_node(node)); | ||||
| if (node.typeinfo->type == GEO_NODE_VIEWER) { | if (node.typeinfo->type == GEO_NODE_VIEWER) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| return socket.index() == 0; | 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 (is_viewer_node(socket.owner_node())) { | ||||
| if (&target_socket->owner_node() != &viewer_node) { | return is_viewer_socket_in_viewer(socket); | ||||
| continue; | |||||
| } | |||||
| if (!target_socket->is_available()) { | |||||
| continue; | |||||
| } | |||||
| if (is_viewer_socket_in_viewer(*target_socket)) { | |||||
| return true; | |||||
| } | |||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static int get_default_viewer_type(const bContext *C) | static int get_default_viewer_type(const bContext *C) | ||||
| { | { | ||||
| SpaceNode *snode = CTX_wm_space_node(C); | SpaceNode *snode = CTX_wm_space_node(C); | ||||
| return ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER; | return ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER; | ||||
| } | } | ||||
| static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node) | static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node) | ||||
| { | { | ||||
| LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) { | LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) { | ||||
| if (link->tonode == &viewer_node) { | if (link->tonode == &viewer_node) { | ||||
| if (link->tosock->flag & SOCK_UNAVAIL) { | if (link->tosock->flag & SOCK_UNAVAIL) { | ||||
| nodeRemLink(&btree, link); | nodeRemLink(&btree, link); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| 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); | int last_linked_socket_index = -1; | ||||
| for (bNodeSocket *socket : node_to_view.output_sockets()) { | |||||
| /* Check if there is already an active viewer node that should be used. */ | if (!socket_can_be_viewed(*socket)) { | ||||
| for (const bNode *viewer_node : viewer_nodes) { | continue; | ||||
| if (viewer_node->flag & NODE_DO_OUTPUT) { | |||||
| return viewer_node; | |||||
| } | |||||
| } | } | ||||
| for (bNodeLink *link : socket->directly_linked_links()) { | |||||
| /* If no active but non-active viewers exist, make one active. */ | bNodeSocket &target_socket = *link->tosock; | ||||
| if (!viewer_nodes.is_empty()) { | bNode &target_node = *link->tonode; | ||||
| const_cast<bNode *>(viewer_nodes[0])->flag |= NODE_DO_OUTPUT; | if (is_viewer_socket(target_socket)) { | ||||
| return viewer_nodes[0]; | 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; | |||||
| } | } | ||||
| return nullptr; | last_linked_socket_index = socket->index(); | ||||
| } | } | ||||
| 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; | |||||
| } | } | ||||
| } | } | ||||
| const bNodeSocket *last_socket_linked_to_viewer = nullptr; | if (last_linked_socket_index == -1) { | ||||
| if (active_viewer_node != nullptr) { | /* Returnt he first socket that can be viewed. */ | ||||
| for (const bNodeSocket *output_socket : node_to_view.output_sockets()) { | for (bNodeSocket *socket : node_to_view.output_sockets()) { | ||||
| if (!socket_can_be_viewed(*output_socket)) { | if (socket_can_be_viewed(*socket)) { | ||||
| continue; | return socket; | ||||
| } | |||||
| if (is_linked_to_viewer(*output_socket, *active_viewer_node)) { | |||||
| last_socket_linked_to_viewer = output_socket; | |||||
| } | |||||
| } | |||||
| } | |||||
| 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; | |||||
| } | } | ||||
| } | } | ||||
| return nullptr; | |||||
| } | } | ||||
| else { | |||||
| /* Pick the next socket to be linked to the viewer. */ | /* Pick the next socket to be linked to the viewer. */ | ||||
| const int tot_outputs = node_to_view.output_sockets().size(); | const int tot_outputs = node_to_view.output_sockets().size(); | ||||
| for (const int offset : IndexRange(1, tot_outputs - 1)) { | for (const int offset : IndexRange(1, tot_outputs)) { | ||||
| const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs; | const int index = (last_linked_socket_index + offset) % tot_outputs; | ||||
| const bNodeSocket &output_socket = node_to_view.output_socket(index); | bNodeSocket &output_socket = node_to_view.output_socket(index); | ||||
| if (!socket_can_be_viewed(output_socket)) { | if (!socket_can_be_viewed(output_socket)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (is_linked_to_viewer(output_socket, *active_viewer_node)) { | 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; | continue; | ||||
| } | } | ||||
| return &output_socket; | if (link->is_muted()) { | ||||
| continue; | |||||
| } | |||||
| if (!(target_node.flag & NODE_DO_OUTPUT)) { | |||||
| continue; | |||||
| } | |||||
| is_currently_viewed = true; | |||||
| break; | |||||
| } | } | ||||
| if (is_currently_viewed) { | |||||
| continue; | |||||
| } | |||||
| return &output_socket; | |||||
| } | } | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| static int link_socket_to_viewer(const bContext &C, | static void finalize_viewer_link(const bContext &C, | ||||
| bNode *viewer_bnode, | SpaceNode &snode, | ||||
| bNode &viewer_node, | |||||
| bNodeLink &viewer_link) | |||||
| { | |||||
| 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, | bNode &bnode_to_view, | ||||
| bNodeSocket &bsocket_to_view) | bNodeSocket &bsocket_to_view) | ||||
| { | { | ||||
| SpaceNode &snode = *CTX_wm_space_node(&C); | bNode *viewer_node = nullptr; | ||||
| bNodeTree &btree = *snode.edittree; | /* 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) { | /* Try to reactivate existing viewer connection. */ | ||||
| /* Create a new viewer node if none exists. */ | 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 int viewer_type = get_default_viewer_type(&C); | ||||
| const float2 location{bsocket_to_view.locx / UI_DPI_FAC + 100, | const float2 location{bsocket_to_view.locx / UI_DPI_FAC + 100, | ||||
| bsocket_to_view.locy / UI_DPI_FAC}; | bsocket_to_view.locy / UI_DPI_FAC}; | ||||
| viewer_bnode = add_static_node(C, viewer_type, location); | viewer_node = add_static_node(C, viewer_type, location); | ||||
| if (viewer_bnode == nullptr) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| } | } | ||||
| 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) { | if (viewer_bsocket == nullptr) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| bNodeLink *viewer_link = nullptr; | |||||
| bNodeLink *link_to_change = nullptr; | LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) { | ||||
| LISTBASE_FOREACH (bNodeLink *, link, &btree.links) { | |||||
| if (link->tosock == viewer_bsocket) { | if (link->tosock == viewer_bsocket) { | ||||
| link_to_change = link; | viewer_link = link; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (viewer_link == nullptr) { | |||||
| if (link_to_change == nullptr) { | viewer_link = nodeAddLink( | ||||
| nodeAddLink(&btree, &bnode_to_view, &bsocket_to_view, viewer_bnode, viewer_bsocket); | &btree, &bnode_to_view, &bsocket_to_view, viewer_node, viewer_bsocket); | ||||
| } | } | ||||
| else { | else { | ||||
| link_to_change->fromnode = &bnode_to_view; | viewer_link->fromnode = &bnode_to_view; | ||||
| link_to_change->fromsock = &bsocket_to_view; | viewer_link->fromsock = &bsocket_to_view; | ||||
| BKE_ntree_update_tag_link_changed(&btree); | BKE_ntree_update_tag_link_changed(&btree); | ||||
| } | } | ||||
| finalize_viewer_link(C, snode, *viewer_node, *viewer_link); | |||||
| remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode); | return OPERATOR_CANCELLED; | ||||
| 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; | |||||
| } | } | ||||
| 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); | SpaceNode &snode = *CTX_wm_space_node(&C); | ||||
| bNodeTree *btree = snode.edittree; | bNodeTree *btree = snode.edittree; | ||||
| btree->ensure_topology_cache(); | 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) { | if (bsocket_to_view == nullptr) { | ||||
| return OPERATOR_FINISHED; | bsocket_to_view = determine_socket_to_view(bnode_to_view); | ||||
| } | |||||
| if (bsocket_to_view == nullptr) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | } | ||||
| return link_socket_to_viewer(C, active_viewer_bnode, bnode_to_view, *bsocket_to_view); | |||||
| return view_socket(C, snode, *btree, bnode_to_view, *bsocket_to_view); | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| } // namespace viewer_linking | } // namespace viewer_linking | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Link to Viewer Node Operator | /** \name Link to Viewer Node Operator | ||||
| * \{ */ | * \{ */ | ||||
| static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) | static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| SpaceNode &snode = *CTX_wm_space_node(C); | SpaceNode &snode = *CTX_wm_space_node(C); | ||||
| bNode *node = nodeGetActive(snode.edittree); | bNode *node = nodeGetActive(snode.edittree); | ||||
| if (!node) { | if (!node) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); | 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; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); | ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 1,781 Lines • Show Last 20 Lines | |||||