Geometry Nodes: sync bundle/closure nodes on first link

When using link-drag-search to create bundle or closure nodes, the newly created
nodes are already synced automatically. Now this automatic syncing also happens
when an empty node is first linked. If there are any sockets already, the
automatic syncing does not happen as it can be unintentional. In this case the
user can just click the sync icon in the node header to update the sockets.

Pull Request: https://projects.blender.org/blender/blender/pulls/143744
This commit is contained in:
Jacques Lucke
2025-07-31 20:49:28 +02:00
parent 95422b95ee
commit 9788f9f62a
9 changed files with 123 additions and 42 deletions

View File

@@ -226,6 +226,8 @@ struct NodeInsertLinkParams {
bNodeTree &ntree;
bNode &node;
bNodeLink &link;
/** Optional context to allow for more advanced link insertion functionality. */
bContext *C = nullptr;
};
/**

View File

@@ -286,7 +286,8 @@ static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, in
return nullptr;
}
static bool snode_autoconnect_input(SpaceNode &snode,
static bool snode_autoconnect_input(bContext &C,
SpaceNode &snode,
bNode *node_fr,
bNodeSocket *sock_fr,
bNode *node_to,
@@ -302,14 +303,14 @@ static bool snode_autoconnect_input(SpaceNode &snode,
bNodeLink &link = bke::node_add_link(*ntree, *node_fr, *sock_fr, *node_to, *sock_to);
if (link.fromnode->typeinfo->insert_link) {
bke::NodeInsertLinkParams params{*ntree, *link.fromnode, link};
bke::NodeInsertLinkParams params{*ntree, *link.fromnode, link, &C};
if (!link.fromnode->typeinfo->insert_link(params)) {
bke::node_remove_link(ntree, link);
return false;
}
}
if (link.tonode->typeinfo->insert_link) {
bke::NodeInsertLinkParams params{*ntree, *link.tonode, link};
bke::NodeInsertLinkParams params{*ntree, *link.tonode, link, &C};
if (!link.tonode->typeinfo->insert_link(params)) {
bke::node_remove_link(ntree, link);
return false;
@@ -365,7 +366,10 @@ void update_multi_input_indices_for_removed_links(bNode &node)
}
}
static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace)
static void snode_autoconnect(bContext &C,
SpaceNode &snode,
const bool allow_multiple,
const bool replace)
{
bNodeTree *ntree = snode.edittree;
Vector<bNode *> sorted_nodes = get_selected_nodes(*ntree).extract_vector();
@@ -401,7 +405,7 @@ static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const
continue;
}
if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
if (snode_autoconnect_input(C, snode, node_fr, sock_fr, node_to, sock_to, replace)) {
// numlinks++;
}
}
@@ -425,7 +429,7 @@ static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const
continue;
}
if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
if (snode_autoconnect_input(C, snode, node_fr, sock_fr, node_to, sock_to, replace)) {
// numlinks++;
break;
}
@@ -1260,14 +1264,14 @@ static void add_dragged_links_to_tree(bContext &C, bNodeLinkDrag &nldrag)
bNodeLink *new_link = MEM_mallocN<bNodeLink>(__func__);
*new_link = link;
if (link.fromnode->typeinfo->insert_link) {
bke::NodeInsertLinkParams params{ntree, *link.fromnode, *new_link};
bke::NodeInsertLinkParams params{ntree, *link.fromnode, *new_link, &C};
if (!link.fromnode->typeinfo->insert_link(params)) {
MEM_freeN(new_link);
continue;
}
}
if (link.tonode->typeinfo->insert_link) {
bke::NodeInsertLinkParams params{ntree, *link.tonode, *new_link};
bke::NodeInsertLinkParams params{ntree, *link.tonode, *new_link, &C};
if (!link.tonode->typeinfo->insert_link(params)) {
MEM_freeN(new_link);
continue;
@@ -1660,7 +1664,7 @@ static wmOperatorStatus node_make_link_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
snode_autoconnect(snode, true, replace);
snode_autoconnect(*C, snode, true, replace);
/* Deselect sockets after linking. */
node_deselect_all_input_sockets(node_tree, false);

View File

@@ -12,6 +12,7 @@ struct ReportList;
struct bContext;
struct bNodeTree;
struct Main;
struct bNodeSocket;
namespace blender::nodes {
@@ -32,16 +33,20 @@ void node_can_sync_cache_clear(Main &bmain);
void sync_sockets_evaluate_closure(SpaceNode &snode,
bNode &evaluate_closure_node,
ReportList *reports);
ReportList *reports,
const bNodeSocket *src_closure_socket = nullptr);
void sync_sockets_separate_bundle(SpaceNode &snode,
bNode &separate_bundle_node,
ReportList *reports);
ReportList *reports,
const bNodeSocket *src_bundle_socket = nullptr);
void sync_sockets_combine_bundle(SpaceNode &snode,
bNode &combine_bundle_node,
ReportList *reports);
ReportList *reports,
const bNodeSocket *src_bundle_socket = nullptr);
void sync_sockets_closure(SpaceNode &snode,
bNode &closure_input_node,
bNode &closure_output_node,
ReportList *reports);
ReportList *reports,
const bNodeSocket *src_closure_socket = nullptr);
} // namespace blender::nodes

View File

@@ -181,6 +181,20 @@ static void node_free_storage(bNode *node)
static bool node_insert_link(bke::NodeInsertLinkParams &params)
{
if (params.C && params.link.fromnode == &params.node) {
const NodeGeometryClosureOutput &storage = node_storage(params.node);
if (storage.input_items.items_num == 0 && storage.output_items.items_num == 0) {
SpaceNode *snode = CTX_wm_space_node(params.C);
if (snode && snode->edittree == &params.ntree) {
bNode *input_node = bke::zone_type_by_node_type(GEO_NODE_CLOSURE_OUTPUT)
->get_corresponding_input(params.ntree, params.node);
if (input_node) {
sync_sockets_closure(*snode, *input_node, params.node, nullptr, params.link.tosock);
}
}
}
return true;
}
return socket_items::try_add_item_via_any_extend_socket<ClosureOutputItemsAccessor>(
params.ntree, params.node, params.node, params.link);
}

View File

@@ -67,6 +67,16 @@ static void node_free_storage(bNode *node)
static bool node_insert_link(bke::NodeInsertLinkParams &params)
{
if (params.C && params.link.fromnode == &params.node) {
const NodeGeometryCombineBundle &storage = node_storage(params.node);
if (storage.items_num == 0) {
SpaceNode *snode = CTX_wm_space_node(params.C);
if (snode && snode->edittree == &params.ntree) {
sync_sockets_combine_bundle(*snode, params.node, nullptr, params.link.tosock);
}
}
return true;
}
return socket_items::try_add_item_via_any_extend_socket<CombineBundleItemsAccessor>(
params.ntree, params.node, params.node, params.link);
}

View File

@@ -77,6 +77,16 @@ static void node_free_storage(bNode *node)
static bool node_insert_link(bke::NodeInsertLinkParams &params)
{
if (params.link.tonode == &params.node) {
if (params.link.tosock == params.node.inputs.first) {
const NodeGeometryEvaluateClosure &storage = node_storage(params.node);
if (storage.input_items.items_num == 0 && storage.output_items.items_num == 0) {
SpaceNode *snode = CTX_wm_space_node(params.C);
if (snode && snode->edittree == &params.ntree) {
sync_sockets_evaluate_closure(*snode, params.node, nullptr, params.link.fromsock);
}
}
return true;
}
return socket_items::try_add_item_via_any_extend_socket<EvaluateClosureInputItemsAccessor>(
params.ntree, params.node, params.node, params.link);
}

View File

@@ -71,6 +71,16 @@ static void node_free_storage(bNode *node)
static bool node_insert_link(bke::NodeInsertLinkParams &params)
{
if (params.C && params.link.tonode == &params.node) {
const NodeGeometrySeparateBundle &storage = node_storage(params.node);
if (storage.items_num == 0) {
SpaceNode *snode = CTX_wm_space_node(params.C);
if (snode && snode->edittree == &params.ntree) {
sync_sockets_separate_bundle(*snode, params.node, nullptr, params.link.fromsock);
}
}
return true;
}
return socket_items::try_add_item_via_any_extend_socket<SeparateBundleItemsAccessor>(
params.ntree, params.node, params.node, params.link);
}

View File

@@ -53,18 +53,22 @@ struct ClosureSyncState {
std::optional<nodes::ClosureSignature> source_signature;
};
static BundleSyncState get_sync_state_separate_bundle(const SpaceNode &snode,
const bNode &separate_bundle_node)
static BundleSyncState get_sync_state_separate_bundle(
const SpaceNode &snode,
const bNode &separate_bundle_node,
const bNodeSocket *src_bundle_socket = nullptr)
{
BLI_assert(separate_bundle_node.is_type("GeometryNodeSeparateBundle"));
snode.edittree->ensure_topology_cache();
const bNodeSocket &bundle_socket = separate_bundle_node.input_socket(0);
if (!src_bundle_socket) {
src_bundle_socket = &separate_bundle_node.input_socket(0);
}
bke::ComputeContextCache compute_context_cache;
const ComputeContext *current_context = ed::space_node::compute_context_for_edittree_socket(
snode, compute_context_cache, bundle_socket);
snode, compute_context_cache, *src_bundle_socket);
const Vector<nodes::BundleSignature> source_signatures = gather_linked_origin_bundle_signatures(
current_context, bundle_socket, compute_context_cache);
current_context, *src_bundle_socket, compute_context_cache);
if (source_signatures.is_empty()) {
return {NodeSyncState::NoSyncSource};
}
@@ -80,18 +84,22 @@ static BundleSyncState get_sync_state_separate_bundle(const SpaceNode &snode,
return {NodeSyncState::Synced};
}
static BundleSyncState get_sync_state_combine_bundle(const SpaceNode &snode,
const bNode &combine_bundle_node)
static BundleSyncState get_sync_state_combine_bundle(
const SpaceNode &snode,
const bNode &combine_bundle_node,
const bNodeSocket *src_bundle_socket = nullptr)
{
BLI_assert(combine_bundle_node.is_type("GeometryNodeCombineBundle"));
snode.edittree->ensure_topology_cache();
const bNodeSocket &bundle_socket = combine_bundle_node.output_socket(0);
if (!src_bundle_socket) {
src_bundle_socket = &combine_bundle_node.output_socket(0);
}
bke::ComputeContextCache compute_context_cache;
const ComputeContext *current_context = ed::space_node::compute_context_for_edittree_socket(
snode, compute_context_cache, bundle_socket);
snode, compute_context_cache, *src_bundle_socket);
const Vector<nodes::BundleSignature> source_signatures = gather_linked_target_bundle_signatures(
current_context, bundle_socket, compute_context_cache);
current_context, *src_bundle_socket, compute_context_cache);
if (source_signatures.is_empty()) {
return {NodeSyncState::NoSyncSource};
}
@@ -107,18 +115,22 @@ static BundleSyncState get_sync_state_combine_bundle(const SpaceNode &snode,
return {NodeSyncState::Synced};
}
static ClosureSyncState get_sync_state_closure_output(const SpaceNode &snode,
const bNode &closure_output_node)
static ClosureSyncState get_sync_state_closure_output(
const SpaceNode &snode,
const bNode &closure_output_node,
const bNodeSocket *src_closure_socket = nullptr)
{
snode.edittree->ensure_topology_cache();
const bNodeSocket &closure_socket = closure_output_node.output_socket(0);
if (!src_closure_socket) {
src_closure_socket = &closure_output_node.output_socket(0);
}
bke::ComputeContextCache compute_context_cache;
const ComputeContext *current_context = ed::space_node::compute_context_for_edittree_socket(
snode, compute_context_cache, closure_socket);
snode, compute_context_cache, *src_closure_socket);
const Vector<nodes::ClosureSignature> source_signatures =
gather_linked_target_closure_signatures(
current_context, closure_socket, compute_context_cache);
current_context, *src_closure_socket, compute_context_cache);
if (source_signatures.is_empty()) {
return {NodeSyncState::NoSyncSource};
}
@@ -134,18 +146,22 @@ static ClosureSyncState get_sync_state_closure_output(const SpaceNode &snode,
return {NodeSyncState::Synced};
}
static ClosureSyncState get_sync_state_evaluate_closure(const SpaceNode &snode,
const bNode &evaluate_closure_node)
static ClosureSyncState get_sync_state_evaluate_closure(
const SpaceNode &snode,
const bNode &evaluate_closure_node,
const bNodeSocket *src_closure_socket = nullptr)
{
snode.edittree->ensure_topology_cache();
const bNodeSocket &closure_socket = evaluate_closure_node.input_socket(0);
if (!src_closure_socket) {
src_closure_socket = &evaluate_closure_node.input_socket(0);
}
bke::ComputeContextCache compute_context_cache;
const ComputeContext *current_context = ed::space_node::compute_context_for_edittree_socket(
snode, compute_context_cache, closure_socket);
snode, compute_context_cache, *src_closure_socket);
const Vector<nodes::ClosureSignature> source_signatures =
gather_linked_origin_closure_signatures(
current_context, closure_socket, compute_context_cache);
current_context, *src_closure_socket, compute_context_cache);
if (source_signatures.is_empty()) {
return {NodeSyncState::NoSyncSource};
}
@@ -163,9 +179,11 @@ static ClosureSyncState get_sync_state_evaluate_closure(const SpaceNode &snode,
void sync_sockets_separate_bundle(SpaceNode &snode,
bNode &separate_bundle_node,
ReportList *reports)
ReportList *reports,
const bNodeSocket *src_bundle_socket)
{
const BundleSyncState sync_state = get_sync_state_separate_bundle(snode, separate_bundle_node);
const BundleSyncState sync_state = get_sync_state_separate_bundle(
snode, separate_bundle_node, src_bundle_socket);
switch (sync_state.state) {
case NodeSyncState::Synced:
return;
@@ -200,9 +218,13 @@ void sync_sockets_separate_bundle(SpaceNode &snode,
BKE_ntree_update_tag_node_property(snode.edittree, &separate_bundle_node);
}
void sync_sockets_combine_bundle(SpaceNode &snode, bNode &combine_bundle_node, ReportList *reports)
void sync_sockets_combine_bundle(SpaceNode &snode,
bNode &combine_bundle_node,
ReportList *reports,
const bNodeSocket *src_bundle_socket)
{
const BundleSyncState sync_state = get_sync_state_combine_bundle(snode, combine_bundle_node);
const BundleSyncState sync_state = get_sync_state_combine_bundle(
snode, combine_bundle_node, src_bundle_socket);
switch (sync_state.state) {
case NodeSyncState::Synced:
return;
@@ -240,10 +262,11 @@ void sync_sockets_combine_bundle(SpaceNode &snode, bNode &combine_bundle_node, R
void sync_sockets_evaluate_closure(SpaceNode &snode,
bNode &evaluate_closure_node,
ReportList *reports)
ReportList *reports,
const bNodeSocket *src_closure_socket)
{
const ClosureSyncState sync_state = get_sync_state_evaluate_closure(snode,
evaluate_closure_node);
const ClosureSyncState sync_state = get_sync_state_evaluate_closure(
snode, evaluate_closure_node, src_closure_socket);
switch (sync_state.state) {
case NodeSyncState::Synced:
return;
@@ -297,9 +320,11 @@ void sync_sockets_evaluate_closure(SpaceNode &snode,
void sync_sockets_closure(SpaceNode &snode,
bNode &closure_input_node,
bNode &closure_output_node,
ReportList *reports)
ReportList *reports,
const bNodeSocket *src_closure_socket)
{
const ClosureSyncState sync_state = get_sync_state_closure_output(snode, closure_output_node);
const ClosureSyncState sync_state = get_sync_state_closure_output(
snode, closure_output_node, src_closure_socket);
switch (sync_state.state) {
case NodeSyncState::Synced:
return;

View File

@@ -8,6 +8,7 @@
#include "NOD_geometry_nodes_closure_location.hh"
#include "NOD_geometry_nodes_closure_signature.hh"
#include "NOD_node_in_compute_context.hh"
#include "NOD_trace_values.hh"
#include "BKE_compute_context_cache.hh"
#include "BKE_node_tree_zones.hh"