Refactor: Geometry Nodes: simplify detection of syncable nodes
This moves most of the code to deal with syncable nodes (such as Combine/Separate Bundle) to the nodes module. Over time it might be possible to decentralize it more. This also changes the caching mechanism from storing a flag on the node to storing a map on the node editor runtime data. This simplifies the code quite significantly and also removes the need to store any of this data in DNA. The node tree update code now always clears this cache because before it was missing many cases, e.g. when creating links that would connect a Combine to a Separate Bundle node. Pull Request: https://projects.blender.org/blender/blender/pulls/143661
This commit is contained in:
@@ -33,13 +33,13 @@
|
||||
|
||||
#include "MOD_nodes.hh"
|
||||
|
||||
#include "NOD_geo_closure.hh"
|
||||
#include "NOD_geometry_nodes_dependencies.hh"
|
||||
#include "NOD_geometry_nodes_gizmos.hh"
|
||||
#include "NOD_geometry_nodes_lazy_function.hh"
|
||||
#include "NOD_node_declaration.hh"
|
||||
#include "NOD_socket.hh"
|
||||
#include "NOD_socket_declarations.hh"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
#include "NOD_texture.h"
|
||||
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
@@ -312,7 +312,6 @@ class NodeTreeMainUpdater {
|
||||
Map<bNodeTree *, TreeUpdateResult> update_result_by_tree_;
|
||||
NodeTreeRelations relations_;
|
||||
bool needs_relations_update_ = false;
|
||||
bool found_updated_sync_node_ = false;
|
||||
|
||||
public:
|
||||
NodeTreeMainUpdater(Main *bmain, const NodeTreeUpdateExtraParams ¶ms)
|
||||
@@ -416,12 +415,8 @@ class NodeTreeMainUpdater {
|
||||
DEG_relations_tag_update(bmain_);
|
||||
}
|
||||
}
|
||||
if (found_updated_sync_node_) {
|
||||
for (bNodeTree *ntree : relations_.get_all_trees()) {
|
||||
if (ID_IS_EDITABLE(&ntree->id)) {
|
||||
this->tag_possibly_outdated_sync_nodes(*ntree);
|
||||
}
|
||||
}
|
||||
if (bmain_) {
|
||||
nodes::node_can_sync_cache_clear(*bmain_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,7 +519,6 @@ class NodeTreeMainUpdater {
|
||||
this->update_internal_links(ntree);
|
||||
this->update_generic_callback(ntree);
|
||||
this->remove_unused_previews_when_necessary(ntree);
|
||||
this->check_for_updated_sync_nodes(ntree);
|
||||
this->make_node_previews_dirty(ntree);
|
||||
|
||||
this->propagate_runtime_flags(ntree);
|
||||
@@ -816,50 +810,6 @@ class NodeTreeMainUpdater {
|
||||
ntree.typeinfo->update(&ntree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any node has been updated that may be synced with other nodes.
|
||||
*/
|
||||
void check_for_updated_sync_nodes(const bNodeTree &ntree)
|
||||
{
|
||||
if (found_updated_sync_node_) {
|
||||
return;
|
||||
}
|
||||
ntree.ensure_topology_cache();
|
||||
for (const StringRefNull idname : {"GeometryNodeClosureInput",
|
||||
"GeometryNodeClosureOutput",
|
||||
"GeometryNodeEvaluateClosure",
|
||||
"GeometryNodeCombineBundle",
|
||||
"GeometryNodeSeparateBundle"})
|
||||
{
|
||||
for (const bNode *node : ntree.nodes_by_type(idname)) {
|
||||
if (node->runtime->changed_flag & NTREE_CHANGED_NODE_PROPERTY) {
|
||||
found_updated_sync_node_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tag_possibly_outdated_sync_nodes(bNodeTree &ntree)
|
||||
{
|
||||
for (bNode *node : ntree.nodes_by_type("GeometryNodeClosureOutput")) {
|
||||
auto &storage = *static_cast<NodeGeometryClosureOutput *>(node->storage);
|
||||
storage.flag |= NODE_GEO_CLOSURE_FLAG_MAY_NEED_SYNC;
|
||||
}
|
||||
for (bNode *node : ntree.nodes_by_type("GeometryNodeEvaluateClosure")) {
|
||||
auto &storage = *static_cast<NodeGeometryEvaluateClosure *>(node->storage);
|
||||
storage.flag |= NODE_GEO_EVALUATE_CLOSURE_FLAG_MAY_NEED_SYNC;
|
||||
}
|
||||
for (bNode *node : ntree.nodes_by_type("GeometryNodeCombineBundle")) {
|
||||
auto &storage = *static_cast<NodeGeometryCombineBundle *>(node->storage);
|
||||
storage.flag |= NODE_GEO_COMBINE_BUNDLE_FLAG_MAY_NEED_SYNC;
|
||||
}
|
||||
for (bNode *node : ntree.nodes_by_type("GeometryNodeSeparateBundle")) {
|
||||
auto &storage = *static_cast<NodeGeometrySeparateBundle *>(node->storage);
|
||||
storage.flag |= NODE_GEO_SEPARATE_BUNDLE_FLAG_MAY_NEED_SYNC;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_unused_previews_when_necessary(bNodeTree &ntree)
|
||||
{
|
||||
/* Don't trigger preview removal when only those flags are set. */
|
||||
|
||||
@@ -162,35 +162,7 @@ void ui_template_node_asset_menu_items(uiLayout &layout,
|
||||
const bContext &C,
|
||||
StringRef catalog_path);
|
||||
|
||||
void sync_sockets_evaluate_closure(SpaceNode &snode,
|
||||
bNode &evaluate_closure_node,
|
||||
ReportList *reports);
|
||||
void sync_sockets_separate_bundle(SpaceNode &snode,
|
||||
bNode &separate_bundle_node,
|
||||
ReportList *reports);
|
||||
void sync_sockets_combine_bundle(SpaceNode &snode,
|
||||
bNode &combine_bundle_node,
|
||||
ReportList *reports);
|
||||
void sync_sockets_closure(SpaceNode &snode,
|
||||
bNode &closure_input_node,
|
||||
bNode &closure_output_node,
|
||||
const bool initialize_internal_links,
|
||||
ReportList *reports);
|
||||
|
||||
enum class NodeSyncState {
|
||||
Synced,
|
||||
CanBeSynced,
|
||||
NoSyncSource,
|
||||
ConflictingSyncSources,
|
||||
};
|
||||
|
||||
NodeSyncState sync_sockets_state_separate_bundle(const SpaceNode &snode,
|
||||
const bNode &separate_bundle_node);
|
||||
NodeSyncState sync_sockets_state_combine_bundle(const SpaceNode &snode,
|
||||
const bNode &combine_bundle_node);
|
||||
NodeSyncState sync_sockets_state_closure_output(const SpaceNode &snode,
|
||||
const bNode &closure_output_node);
|
||||
NodeSyncState sync_sockets_state_evaluate_closure(const SpaceNode &snode,
|
||||
const bNode &evaluate_closure_node);
|
||||
/** See #SpaceNode_Runtime::node_can_sync_states. */
|
||||
Map<int, bool> &node_can_sync_cache_get(SpaceNode &snode);
|
||||
|
||||
} // namespace blender::ed::space_node
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
#include "NOD_geometry_nodes_log.hh"
|
||||
#include "NOD_node_declaration.hh"
|
||||
#include "NOD_node_extra_info.hh"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
|
||||
#include "GEO_fillet_curves.hh"
|
||||
|
||||
@@ -2866,7 +2867,7 @@ static void node_draw_basis(const bContext &C,
|
||||
UI_block_emboss_set(&block, ui::EmbossType::Emboss);
|
||||
}
|
||||
|
||||
if (node.typeinfo->can_sync_sockets && node.typeinfo->can_sync_sockets(C, ntree, node)) {
|
||||
if (nodes::node_can_sync_sockets(C, ntree, node)) {
|
||||
iconofs -= iconbutw;
|
||||
UI_block_emboss_set(&block, ui::EmbossType::None);
|
||||
uiBut *but = uiDefIconBut(&block,
|
||||
|
||||
@@ -117,6 +117,14 @@ struct SpaceNode_Runtime {
|
||||
* Stored with a shared pointer so that it can be forward declared.
|
||||
*/
|
||||
std::shared_ptr<asset::AssetItemTree> assets_for_menu;
|
||||
|
||||
/**
|
||||
* Caches the sockets of which nodes can be synced. This can occasionally be expensive to compute
|
||||
* because it needs to traverse the tree. Also, we don't want to check whether syncing is
|
||||
* necessary for all nodes eagerly but only if a relevant node is visible to the user. The cache
|
||||
* is reset when something changes that may affect what nodes need to be synced.
|
||||
*/
|
||||
Map<int, bool> node_can_sync_states;
|
||||
};
|
||||
|
||||
enum NodeResizeDirection {
|
||||
|
||||
@@ -13,389 +13,20 @@
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "BKE_compute_context_cache.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_main_invariants.hh"
|
||||
#include "BKE_node_legacy_types.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_update.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "ED_node.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "NOD_geo_bundle.hh"
|
||||
#include "NOD_geo_closure.hh"
|
||||
#include "NOD_socket_items.hh"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
|
||||
#include "node_intern.hh"
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
|
||||
struct BundleSyncState {
|
||||
NodeSyncState state;
|
||||
std::optional<nodes::BundleSignature> source_signature;
|
||||
};
|
||||
|
||||
struct ClosureSyncState {
|
||||
NodeSyncState state;
|
||||
std::optional<nodes::ClosureSignature> source_signature;
|
||||
};
|
||||
|
||||
static BundleSyncState get_sync_state_separate_bundle(const SpaceNode &snode,
|
||||
const bNode &separate_bundle_node)
|
||||
{
|
||||
BLI_assert(separate_bundle_node.is_type("GeometryNodeSeparateBundle"));
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNodeSocket &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);
|
||||
const Vector<nodes::BundleSignature> source_signatures =
|
||||
ed::space_node::gather_linked_origin_bundle_signatures(
|
||||
current_context, bundle_socket, compute_context_cache);
|
||||
if (source_signatures.is_empty()) {
|
||||
return {NodeSyncState::NoSyncSource};
|
||||
}
|
||||
if (!nodes::BundleSignature::all_matching_exactly(source_signatures)) {
|
||||
return {NodeSyncState::ConflictingSyncSources};
|
||||
}
|
||||
const nodes::BundleSignature &source_signature = source_signatures[0];
|
||||
const nodes::BundleSignature ¤t_signature =
|
||||
nodes::BundleSignature::from_separate_bundle_node(separate_bundle_node);
|
||||
if (!source_signature.matches_exactly(current_signature)) {
|
||||
return {NodeSyncState::CanBeSynced, source_signature};
|
||||
}
|
||||
return {NodeSyncState::Synced};
|
||||
}
|
||||
|
||||
static BundleSyncState get_sync_state_combine_bundle(const SpaceNode &snode,
|
||||
const bNode &combine_bundle_node)
|
||||
{
|
||||
BLI_assert(combine_bundle_node.is_type("GeometryNodeCombineBundle"));
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNodeSocket &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);
|
||||
const Vector<nodes::BundleSignature> source_signatures =
|
||||
ed::space_node::gather_linked_target_bundle_signatures(
|
||||
current_context, bundle_socket, compute_context_cache);
|
||||
if (source_signatures.is_empty()) {
|
||||
return {NodeSyncState::NoSyncSource};
|
||||
}
|
||||
if (!nodes::BundleSignature::all_matching_exactly(source_signatures)) {
|
||||
return {NodeSyncState::ConflictingSyncSources};
|
||||
}
|
||||
const nodes::BundleSignature &source_signature = source_signatures[0];
|
||||
const nodes::BundleSignature ¤t_signature =
|
||||
nodes::BundleSignature::from_combine_bundle_node(combine_bundle_node);
|
||||
if (!source_signature.matches_exactly(current_signature)) {
|
||||
return {NodeSyncState::CanBeSynced, source_signature};
|
||||
}
|
||||
return {NodeSyncState::Synced};
|
||||
}
|
||||
|
||||
static ClosureSyncState get_sync_state_closure_output(const SpaceNode &snode,
|
||||
const bNode &closure_output_node)
|
||||
{
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNodeSocket &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);
|
||||
const Vector<nodes::ClosureSignature> source_signatures =
|
||||
ed::space_node::gather_linked_target_closure_signatures(
|
||||
current_context, closure_socket, compute_context_cache);
|
||||
if (source_signatures.is_empty()) {
|
||||
return {NodeSyncState::NoSyncSource};
|
||||
}
|
||||
if (!nodes::ClosureSignature::all_matching_exactly(source_signatures)) {
|
||||
return {NodeSyncState::ConflictingSyncSources};
|
||||
}
|
||||
const nodes::ClosureSignature &source_signature = source_signatures[0];
|
||||
const nodes::ClosureSignature ¤t_signature =
|
||||
nodes::ClosureSignature::from_closure_output_node(closure_output_node);
|
||||
if (!source_signature.matches_exactly(current_signature)) {
|
||||
return {NodeSyncState::CanBeSynced, source_signature};
|
||||
}
|
||||
return {NodeSyncState::Synced};
|
||||
}
|
||||
|
||||
static ClosureSyncState get_sync_state_evaluate_closure(const SpaceNode &snode,
|
||||
const bNode &evaluate_closure_node)
|
||||
{
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNodeSocket &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);
|
||||
const Vector<nodes::ClosureSignature> source_signatures =
|
||||
ed::space_node::gather_linked_origin_closure_signatures(
|
||||
current_context, closure_socket, compute_context_cache);
|
||||
if (source_signatures.is_empty()) {
|
||||
return {NodeSyncState::NoSyncSource};
|
||||
}
|
||||
if (!nodes::ClosureSignature::all_matching_exactly(source_signatures)) {
|
||||
return {NodeSyncState::ConflictingSyncSources};
|
||||
}
|
||||
const nodes::ClosureSignature &source_signature = source_signatures[0];
|
||||
const nodes::ClosureSignature ¤t_signature =
|
||||
nodes::ClosureSignature::from_evaluate_closure_node(evaluate_closure_node);
|
||||
if (!source_signature.matches_exactly(current_signature)) {
|
||||
return {NodeSyncState::CanBeSynced, source_signature};
|
||||
}
|
||||
return {NodeSyncState::Synced};
|
||||
}
|
||||
|
||||
NodeSyncState sync_sockets_state_separate_bundle(const SpaceNode &snode,
|
||||
const bNode &separate_bundle_node)
|
||||
{
|
||||
return get_sync_state_separate_bundle(snode, separate_bundle_node).state;
|
||||
}
|
||||
|
||||
NodeSyncState sync_sockets_state_combine_bundle(const SpaceNode &snode,
|
||||
const bNode &combine_bundle_node)
|
||||
{
|
||||
return get_sync_state_combine_bundle(snode, combine_bundle_node).state;
|
||||
}
|
||||
|
||||
NodeSyncState sync_sockets_state_closure_output(const SpaceNode &snode,
|
||||
const bNode &closure_output_node)
|
||||
{
|
||||
return get_sync_state_closure_output(snode, closure_output_node).state;
|
||||
}
|
||||
|
||||
NodeSyncState sync_sockets_state_evaluate_closure(const SpaceNode &snode,
|
||||
const bNode &evaluate_closure_node)
|
||||
{
|
||||
return get_sync_state_evaluate_closure(snode, evaluate_closure_node).state;
|
||||
}
|
||||
|
||||
void sync_sockets_separate_bundle(SpaceNode &snode,
|
||||
bNode &separate_bundle_node,
|
||||
ReportList *reports)
|
||||
{
|
||||
const BundleSyncState sync_state = get_sync_state_separate_bundle(snode, separate_bundle_node);
|
||||
switch (sync_state.state) {
|
||||
case NodeSyncState::Synced:
|
||||
return;
|
||||
case NodeSyncState::NoSyncSource:
|
||||
BKE_report(reports, RPT_INFO, "No bundle signature found");
|
||||
return;
|
||||
case NodeSyncState::ConflictingSyncSources:
|
||||
BKE_report(reports, RPT_INFO, "Found conflicting bundle signatures");
|
||||
return;
|
||||
case NodeSyncState::CanBeSynced:
|
||||
break;
|
||||
}
|
||||
|
||||
auto &storage = *static_cast<NodeGeometrySeparateBundle *>(separate_bundle_node.storage);
|
||||
|
||||
Map<std::string, int> old_identifiers;
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const NodeGeometrySeparateBundleItem &item = storage.items[i];
|
||||
old_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
|
||||
nodes::socket_items::clear<nodes::SeparateBundleItemsAccessor>(separate_bundle_node);
|
||||
for (const nodes::BundleSignature::Item &item : sync_state.source_signature->items) {
|
||||
NodeGeometrySeparateBundleItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes ::SeparateBundleItemsAccessor>(
|
||||
separate_bundle_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &separate_bundle_node);
|
||||
}
|
||||
|
||||
void sync_sockets_combine_bundle(SpaceNode &snode, bNode &combine_bundle_node, ReportList *reports)
|
||||
{
|
||||
const BundleSyncState sync_state = get_sync_state_combine_bundle(snode, combine_bundle_node);
|
||||
switch (sync_state.state) {
|
||||
case NodeSyncState::Synced:
|
||||
return;
|
||||
case NodeSyncState::NoSyncSource:
|
||||
BKE_report(reports, RPT_INFO, "No bundle signature found");
|
||||
return;
|
||||
case NodeSyncState::ConflictingSyncSources:
|
||||
BKE_report(reports, RPT_INFO, "Found conflicting bundle signatures");
|
||||
return;
|
||||
case NodeSyncState::CanBeSynced:
|
||||
break;
|
||||
}
|
||||
|
||||
auto &storage = *static_cast<NodeGeometryCombineBundle *>(combine_bundle_node.storage);
|
||||
|
||||
Map<std::string, int> old_identifiers;
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const NodeGeometryCombineBundleItem &item = storage.items[i];
|
||||
old_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
|
||||
nodes::socket_items::clear<nodes::CombineBundleItemsAccessor>(combine_bundle_node);
|
||||
for (const nodes::BundleSignature::Item &item : sync_state.source_signature->items) {
|
||||
NodeGeometryCombineBundleItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes ::CombineBundleItemsAccessor>(
|
||||
combine_bundle_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &combine_bundle_node);
|
||||
}
|
||||
|
||||
void sync_sockets_evaluate_closure(SpaceNode &snode,
|
||||
bNode &evaluate_closure_node,
|
||||
ReportList *reports)
|
||||
{
|
||||
const ClosureSyncState sync_state = get_sync_state_evaluate_closure(snode,
|
||||
evaluate_closure_node);
|
||||
switch (sync_state.state) {
|
||||
case NodeSyncState::Synced:
|
||||
return;
|
||||
case NodeSyncState::NoSyncSource:
|
||||
BKE_report(reports, RPT_INFO, "No closure signature found");
|
||||
return;
|
||||
case NodeSyncState::ConflictingSyncSources:
|
||||
BKE_report(reports, RPT_INFO, "Found conflicting closure signatures");
|
||||
return;
|
||||
case NodeSyncState::CanBeSynced:
|
||||
break;
|
||||
}
|
||||
|
||||
auto &storage = *static_cast<NodeGeometryEvaluateClosure *>(evaluate_closure_node.storage);
|
||||
|
||||
Map<std::string, int> old_input_identifiers;
|
||||
Map<std::string, int> old_output_identifiers;
|
||||
for (const int i : IndexRange(storage.input_items.items_num)) {
|
||||
const NodeGeometryEvaluateClosureInputItem &item = storage.input_items.items[i];
|
||||
old_input_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
for (const int i : IndexRange(storage.output_items.items_num)) {
|
||||
const NodeGeometryEvaluateClosureOutputItem &item = storage.output_items.items[i];
|
||||
old_output_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
|
||||
nodes::socket_items::clear<nodes::EvaluateClosureInputItemsAccessor>(evaluate_closure_node);
|
||||
nodes::socket_items::clear<nodes::EvaluateClosureOutputItemsAccessor>(evaluate_closure_node);
|
||||
|
||||
for (const nodes::ClosureSignature::Item &item : sync_state.source_signature->inputs) {
|
||||
NodeGeometryEvaluateClosureInputItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes::EvaluateClosureInputItemsAccessor>(
|
||||
evaluate_closure_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_input_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &item : sync_state.source_signature->outputs) {
|
||||
NodeGeometryEvaluateClosureOutputItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes::EvaluateClosureOutputItemsAccessor>(
|
||||
evaluate_closure_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_output_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &evaluate_closure_node);
|
||||
}
|
||||
|
||||
void sync_sockets_closure(SpaceNode &snode,
|
||||
bNode &closure_input_node,
|
||||
bNode &closure_output_node,
|
||||
const bool initialize_internal_links,
|
||||
ReportList *reports)
|
||||
{
|
||||
const ClosureSyncState sync_state = get_sync_state_closure_output(snode, closure_output_node);
|
||||
switch (sync_state.state) {
|
||||
case NodeSyncState::Synced:
|
||||
return;
|
||||
case NodeSyncState::NoSyncSource:
|
||||
BKE_report(reports, RPT_INFO, "No closure signature found");
|
||||
return;
|
||||
case NodeSyncState::ConflictingSyncSources:
|
||||
BKE_report(reports, RPT_INFO, "Found conflicting closure signatures");
|
||||
return;
|
||||
case NodeSyncState::CanBeSynced:
|
||||
break;
|
||||
}
|
||||
const nodes::ClosureSignature &signature = *sync_state.source_signature;
|
||||
|
||||
auto &storage = *static_cast<NodeGeometryClosureOutput *>(closure_output_node.storage);
|
||||
|
||||
Map<std::string, int> old_input_identifiers;
|
||||
Map<std::string, int> old_output_identifiers;
|
||||
for (const int i : IndexRange(storage.input_items.items_num)) {
|
||||
const NodeGeometryClosureInputItem &item = storage.input_items.items[i];
|
||||
old_input_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
for (const int i : IndexRange(storage.output_items.items_num)) {
|
||||
const NodeGeometryClosureOutputItem &item = storage.output_items.items[i];
|
||||
old_output_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
|
||||
nodes::socket_items::clear<nodes::ClosureInputItemsAccessor>(closure_output_node);
|
||||
nodes::socket_items::clear<nodes::ClosureOutputItemsAccessor>(closure_output_node);
|
||||
|
||||
for (const nodes::ClosureSignature::Item &item : signature.inputs) {
|
||||
NodeGeometryClosureInputItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<nodes::ClosureInputItemsAccessor>(
|
||||
closure_output_node, item.type->type, item.key.c_str());
|
||||
if (item.structure_type) {
|
||||
new_item.structure_type = int(*item.structure_type);
|
||||
}
|
||||
if (const std::optional<int> old_identifier = old_input_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &item : signature.outputs) {
|
||||
NodeGeometryClosureOutputItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes::ClosureOutputItemsAccessor>(
|
||||
closure_output_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_output_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &closure_input_node);
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &closure_output_node);
|
||||
|
||||
if (initialize_internal_links) {
|
||||
nodes::update_node_declaration_and_sockets(*snode.edittree, closure_input_node);
|
||||
nodes::update_node_declaration_and_sockets(*snode.edittree, closure_output_node);
|
||||
|
||||
snode.edittree->ensure_topology_cache();
|
||||
Vector<std::pair<bNodeSocket *, bNodeSocket *>> internal_links;
|
||||
for (const int input_i : signature.inputs.index_range()) {
|
||||
const nodes::ClosureSignature::Item &input_item = signature.inputs[input_i];
|
||||
for (const int output_i : signature.outputs.index_range()) {
|
||||
const nodes::ClosureSignature::Item &output_item = signature.outputs[output_i];
|
||||
if (input_item.key == output_item.key) {
|
||||
internal_links.append({&closure_input_node.output_socket(input_i),
|
||||
&closure_output_node.input_socket(output_i)});
|
||||
}
|
||||
};
|
||||
}
|
||||
for (auto &&[from_socket, to_socket] : internal_links) {
|
||||
bke::node_add_link(
|
||||
*snode.edittree, closure_input_node, *from_socket, closure_output_node, *to_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Vector<bNode *> get_nodes_to_sync(bContext &C, PointerRNA *ptr)
|
||||
{
|
||||
SpaceNode &snode = *CTX_wm_space_node(&C);
|
||||
@@ -434,206 +65,29 @@ static wmOperatorStatus sockets_sync_exec(bContext *C, wmOperator *op)
|
||||
if (!snode.edittree) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
Vector<bNode *> nodes_to_sync = get_nodes_to_sync(*C, op->ptr);
|
||||
if (nodes_to_sync.is_empty()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bNodeTree &tree = *snode.edittree;
|
||||
const bke::bNodeZoneType &closure_zone_type = *bke::zone_type_by_node_type(
|
||||
GEO_NODE_CLOSURE_OUTPUT);
|
||||
|
||||
for (bNode *node : nodes_to_sync) {
|
||||
if (node->is_type("GeometryNodeEvaluateClosure")) {
|
||||
sync_sockets_evaluate_closure(snode, *node, op->reports);
|
||||
}
|
||||
else if (node->is_type("GeometryNodeSeparateBundle")) {
|
||||
sync_sockets_separate_bundle(snode, *node, op->reports);
|
||||
}
|
||||
else if (node->is_type("GeometryNodeCombineBundle")) {
|
||||
sync_sockets_combine_bundle(snode, *node, op->reports);
|
||||
}
|
||||
else if (node->is_type("GeometryNodeClosureInput")) {
|
||||
bNode &closure_input_node = *node;
|
||||
if (bNode *closure_output_node = closure_zone_type.get_corresponding_output(
|
||||
tree, closure_input_node))
|
||||
{
|
||||
sync_sockets_closure(snode, closure_input_node, *closure_output_node, false, op->reports);
|
||||
}
|
||||
}
|
||||
else if (node->is_type("GeometryNodeClosureOutput")) {
|
||||
bNode &closure_output_node = *node;
|
||||
if (bNode *closure_input_node = closure_zone_type.get_corresponding_input(
|
||||
tree, closure_output_node))
|
||||
{
|
||||
sync_sockets_closure(snode, *closure_input_node, closure_output_node, false, op->reports);
|
||||
}
|
||||
}
|
||||
nodes::sync_node(*C, *node, op->reports);
|
||||
}
|
||||
BKE_main_ensure_invariants(bmain, tree.id);
|
||||
BKE_main_ensure_invariants(bmain, snode.edittree->id);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static std::string get_bundle_sync_tooltip(const nodes::BundleSignature &old_signature,
|
||||
const nodes::BundleSignature &new_signature)
|
||||
{
|
||||
Vector<StringRef> added_items;
|
||||
Vector<StringRef> removed_items;
|
||||
Vector<StringRef> changed_items;
|
||||
|
||||
for (const nodes::BundleSignature::Item &new_item : new_signature.items) {
|
||||
if (const nodes::BundleSignature::Item *old_item = old_signature.items.lookup_key_ptr_as(
|
||||
new_item.key))
|
||||
{
|
||||
if (new_item.type->type != old_item->type->type) {
|
||||
changed_items.append(new_item.key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
added_items.append(new_item.key);
|
||||
}
|
||||
}
|
||||
for (const nodes::BundleSignature ::Item &old_item : old_signature.items) {
|
||||
if (!new_signature.items.contains_as(old_item.key)) {
|
||||
removed_items.append(old_item.key);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::memory_buffer string_buffer;
|
||||
auto buf = fmt::appender(string_buffer);
|
||||
if (!added_items.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Add"), fmt::join(added_items, ", "));
|
||||
}
|
||||
if (!removed_items.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Remove"), fmt::join(removed_items, ", "));
|
||||
}
|
||||
if (!changed_items.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Change"), fmt::join(changed_items, ", "));
|
||||
}
|
||||
fmt::format_to(buf, TIP_("\nUpdate based on linked bundle signature"));
|
||||
|
||||
return fmt::to_string(string_buffer);
|
||||
}
|
||||
|
||||
static std::string get_closure_sync_tooltip(const nodes::ClosureSignature &old_signature,
|
||||
const nodes::ClosureSignature &new_signature)
|
||||
{
|
||||
Vector<StringRef> added_inputs;
|
||||
Vector<StringRef> removed_inputs;
|
||||
Vector<StringRef> changed_inputs;
|
||||
|
||||
Vector<StringRef> added_outputs;
|
||||
Vector<StringRef> removed_outputs;
|
||||
Vector<StringRef> changed_outputs;
|
||||
|
||||
for (const nodes::ClosureSignature::Item &new_item : new_signature.inputs) {
|
||||
if (const nodes::ClosureSignature::Item *old_item = old_signature.inputs.lookup_key_ptr_as(
|
||||
new_item.key))
|
||||
{
|
||||
if (new_item.type->type != old_item->type->type) {
|
||||
changed_inputs.append(new_item.key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
added_inputs.append(new_item.key);
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &old_item : old_signature.inputs) {
|
||||
if (!new_signature.inputs.contains_as(old_item.key)) {
|
||||
removed_inputs.append(old_item.key);
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &new_item : new_signature.outputs) {
|
||||
if (const nodes::ClosureSignature::Item *old_item = old_signature.outputs.lookup_key_ptr_as(
|
||||
new_item.key))
|
||||
{
|
||||
if (new_item.type->type != old_item->type->type) {
|
||||
changed_outputs.append(new_item.key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
added_outputs.append(new_item.key);
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &old_item : old_signature.outputs) {
|
||||
if (!new_signature.outputs.contains_as(old_item.key)) {
|
||||
removed_outputs.append(old_item.key);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::memory_buffer string_buffer;
|
||||
auto buf = fmt::appender(string_buffer);
|
||||
if (!added_inputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Add Inputs"), fmt::join(added_inputs, ", "));
|
||||
}
|
||||
if (!removed_inputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Remove Inputs"), fmt::join(removed_inputs, ", "));
|
||||
}
|
||||
if (!changed_inputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Change Inputs"), fmt::join(changed_inputs, ", "));
|
||||
}
|
||||
if (!added_outputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Add Outputs"), fmt::join(added_outputs, ", "));
|
||||
}
|
||||
if (!removed_outputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Remove Outputs"), fmt::join(removed_outputs, ", "));
|
||||
}
|
||||
if (!changed_outputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Change Outputs"), fmt::join(changed_outputs, ", "));
|
||||
}
|
||||
fmt::format_to(buf, TIP_("\nUpdate based on linked closure signature"));
|
||||
|
||||
return fmt::to_string(string_buffer);
|
||||
}
|
||||
|
||||
static std::string sockets_sync_get_description(bContext *C, wmOperatorType *ot, PointerRNA *ptr)
|
||||
{
|
||||
const SpaceNode &snode = *CTX_wm_space_node(C);
|
||||
Vector<bNode *> nodes_to_sync = get_nodes_to_sync(*C, ptr);
|
||||
if (nodes_to_sync.size() != 1) {
|
||||
return ot->description;
|
||||
}
|
||||
const bNode &node = *nodes_to_sync.first();
|
||||
|
||||
if (node.is_type("GeometryNodeSeparateBundle")) {
|
||||
const nodes::BundleSignature old_signature = nodes::BundleSignature::from_separate_bundle_node(
|
||||
node);
|
||||
if (const std::optional<nodes::BundleSignature> new_signature =
|
||||
get_sync_state_separate_bundle(snode, node).source_signature)
|
||||
{
|
||||
return get_bundle_sync_tooltip(old_signature, *new_signature);
|
||||
}
|
||||
std::string description = nodes::sync_node_description_get(*C, node);
|
||||
if (description.empty()) {
|
||||
return ot->description;
|
||||
}
|
||||
else if (node.is_type("GeometryNodeCombineBundle")) {
|
||||
const nodes::BundleSignature old_signature = nodes::BundleSignature::from_combine_bundle_node(
|
||||
node);
|
||||
if (const std::optional<nodes::BundleSignature> new_signature =
|
||||
get_sync_state_combine_bundle(snode, node).source_signature)
|
||||
{
|
||||
return get_bundle_sync_tooltip(old_signature, *new_signature);
|
||||
}
|
||||
}
|
||||
else if (node.is_type("GeometryNodeEvaluateClosure")) {
|
||||
const nodes::ClosureSignature old_signature =
|
||||
nodes::ClosureSignature::from_evaluate_closure_node(node);
|
||||
if (const std::optional<nodes::ClosureSignature> new_signature =
|
||||
get_sync_state_evaluate_closure(snode, node).source_signature)
|
||||
{
|
||||
return get_closure_sync_tooltip(old_signature, *new_signature);
|
||||
}
|
||||
}
|
||||
else if (node.is_type("GeometryNodeClosureOutput")) {
|
||||
const nodes::ClosureSignature old_signature =
|
||||
nodes::ClosureSignature::from_closure_output_node(node);
|
||||
if (const std::optional<nodes::ClosureSignature> new_signature =
|
||||
get_sync_state_closure_output(snode, node).source_signature)
|
||||
{
|
||||
return get_closure_sync_tooltip(old_signature, *new_signature);
|
||||
}
|
||||
}
|
||||
|
||||
return ot->description;
|
||||
return description;
|
||||
}
|
||||
|
||||
void NODE_OT_sockets_sync(wmOperatorType *ot)
|
||||
@@ -653,4 +107,9 @@ void NODE_OT_sockets_sync(wmOperatorType *ot)
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
Map<int, bool> &node_can_sync_cache_get(SpaceNode &snode)
|
||||
{
|
||||
return snode.runtime->node_can_sync_states;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::space_node
|
||||
|
||||
@@ -112,6 +112,7 @@ void ED_node_tree_start(ARegion *region, SpaceNode *snode, bNodeTree *ntree, ID
|
||||
snode->from = from;
|
||||
|
||||
ED_node_set_active_viewer_key(snode);
|
||||
snode->runtime->node_can_sync_states.clear();
|
||||
|
||||
WM_main_add_notifier(NC_SCENE | ND_NODES, nullptr);
|
||||
}
|
||||
@@ -151,6 +152,7 @@ void ED_node_tree_push(ARegion *region, SpaceNode *snode, bNodeTree *ntree, bNod
|
||||
snode->edittree = ntree;
|
||||
|
||||
ED_node_set_active_viewer_key(snode);
|
||||
snode->runtime->node_can_sync_states.clear();
|
||||
|
||||
WM_main_add_notifier(NC_SCENE | ND_NODES, nullptr);
|
||||
}
|
||||
@@ -177,6 +179,7 @@ void ED_node_tree_pop(ARegion *region, SpaceNode *snode)
|
||||
}
|
||||
|
||||
ED_node_set_active_viewer_key(snode);
|
||||
snode->runtime->node_can_sync_states.clear();
|
||||
|
||||
WM_main_add_notifier(NC_SCENE | ND_NODES, nullptr);
|
||||
}
|
||||
|
||||
@@ -2225,16 +2225,9 @@ typedef struct NodeGeometryClosureOutputItems {
|
||||
char _pad[4];
|
||||
} NodeGeometryClosureOutputItems;
|
||||
|
||||
typedef enum NodeGeometryClosureFlag {
|
||||
NODE_GEO_CLOSURE_FLAG_MAY_NEED_SYNC = 1 << 0,
|
||||
} NodeGeometryClosureFlag;
|
||||
|
||||
typedef struct NodeGeometryClosureOutput {
|
||||
NodeGeometryClosureInputItems input_items;
|
||||
NodeGeometryClosureOutputItems output_items;
|
||||
/** #NodeGeometryClosureFlag */
|
||||
uint32_t flag;
|
||||
char _pad[4];
|
||||
} NodeGeometryClosureOutput;
|
||||
|
||||
typedef struct NodeGeometryEvaluateClosureInputItem {
|
||||
@@ -2273,16 +2266,9 @@ typedef struct NodeGeometryEvaluateClosureOutputItems {
|
||||
char _pad[4];
|
||||
} NodeGeometryEvaluateClosureOutputItems;
|
||||
|
||||
typedef enum NodeGeometryEvaluateClosureFlag {
|
||||
NODE_GEO_EVALUATE_CLOSURE_FLAG_MAY_NEED_SYNC = 1 << 0,
|
||||
} NodeGeometryEvaluateClosureFlag;
|
||||
|
||||
typedef struct NodeGeometryEvaluateClosure {
|
||||
NodeGeometryEvaluateClosureInputItems input_items;
|
||||
NodeGeometryEvaluateClosureOutputItems output_items;
|
||||
/** #NodeGeometryEvaluateClosureFlag */
|
||||
uint32_t flag;
|
||||
char _pad[4];
|
||||
} NodeGeometryEvaluateClosure;
|
||||
|
||||
typedef struct IndexSwitchItem {
|
||||
@@ -2406,17 +2392,12 @@ typedef struct NodeGeometryCombineBundleItem {
|
||||
char _pad[2];
|
||||
} NodeGeometryCombineBundleItem;
|
||||
|
||||
typedef enum NodeGeometryCombineBundleFlag {
|
||||
NODE_GEO_COMBINE_BUNDLE_FLAG_MAY_NEED_SYNC = 1 << 0,
|
||||
} NodeGeometryCombineBundleFlag;
|
||||
|
||||
typedef struct NodeGeometryCombineBundle {
|
||||
NodeGeometryCombineBundleItem *items;
|
||||
int items_num;
|
||||
int next_identifier;
|
||||
int active_index;
|
||||
/** #NodeGeometryCombineBundleFlag */
|
||||
uint32_t flag;
|
||||
char _pad[4];
|
||||
} NodeGeometryCombineBundle;
|
||||
|
||||
typedef struct NodeGeometrySeparateBundleItem {
|
||||
@@ -2426,17 +2407,12 @@ typedef struct NodeGeometrySeparateBundleItem {
|
||||
char _pad[2];
|
||||
} NodeGeometrySeparateBundleItem;
|
||||
|
||||
typedef enum NodeGeometrySeparateBundleFlag {
|
||||
NODE_GEO_SEPARATE_BUNDLE_FLAG_MAY_NEED_SYNC = 1 << 0,
|
||||
} NodeGeometrySeparateBundleFlag;
|
||||
|
||||
typedef struct NodeGeometrySeparateBundle {
|
||||
NodeGeometrySeparateBundleItem *items;
|
||||
int items_num;
|
||||
int next_identifier;
|
||||
int active_index;
|
||||
/** #NodeGeometrySeparateBundleFlag */
|
||||
uint32_t flag;
|
||||
char _pad[4];
|
||||
} NodeGeometrySeparateBundle;
|
||||
|
||||
typedef struct NodeFunctionFormatStringItem {
|
||||
|
||||
@@ -98,6 +98,7 @@ set(SRC
|
||||
intern/partial_eval.cc
|
||||
intern/socket_search_link.cc
|
||||
intern/socket_usage_inference.cc
|
||||
intern/sync_sockets.cc
|
||||
intern/value_elem.cc
|
||||
intern/volume_grid_function_eval.cc
|
||||
|
||||
@@ -146,6 +147,7 @@ set(SRC
|
||||
NOD_socket_search_link.hh
|
||||
NOD_socket_usage_inference.hh
|
||||
NOD_socket_usage_inference_fwd.hh
|
||||
NOD_sync_sockets.hh
|
||||
NOD_texture.h
|
||||
NOD_value_elem.hh
|
||||
NOD_value_elem_eval.hh
|
||||
|
||||
46
source/blender/nodes/NOD_sync_sockets.hh
Normal file
46
source/blender/nodes/NOD_sync_sockets.hh
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <string>
|
||||
|
||||
struct SpaceNode;
|
||||
struct bNode;
|
||||
struct ReportList;
|
||||
struct bContext;
|
||||
struct bNodeTree;
|
||||
struct Main;
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
/**
|
||||
* Sync the sockets of that node if possible. For example, a Separate Bundle node will be updated
|
||||
* to match a linked Combine Bundle if one is found.
|
||||
*/
|
||||
void sync_node(bContext &C, bNode &node, ReportList *reports);
|
||||
|
||||
/**
|
||||
* Get a description of what syncing the node would do. This can be used as tooltip.
|
||||
*/
|
||||
std::string sync_node_description_get(const bContext &C, const bNode &node);
|
||||
|
||||
/** Access (cached) information of whether a specific node can be synced currently. */
|
||||
bool node_can_sync_sockets(const bContext &C, const bNodeTree &tree, const bNode &node);
|
||||
void node_can_sync_cache_clear(Main &bmain);
|
||||
|
||||
void sync_sockets_evaluate_closure(SpaceNode &snode,
|
||||
bNode &evaluate_closure_node,
|
||||
ReportList *reports);
|
||||
void sync_sockets_separate_bundle(SpaceNode &snode,
|
||||
bNode &separate_bundle_node,
|
||||
ReportList *reports);
|
||||
void sync_sockets_combine_bundle(SpaceNode &snode,
|
||||
bNode &combine_bundle_node,
|
||||
ReportList *reports);
|
||||
void sync_sockets_closure(SpaceNode &snode,
|
||||
bNode &closure_input_node,
|
||||
bNode &closure_output_node,
|
||||
const bool initialize_internal_links,
|
||||
ReportList *reports);
|
||||
|
||||
} // namespace blender::nodes
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "NOD_socket_items_ops.hh"
|
||||
#include "NOD_socket_items_ui.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
@@ -211,7 +212,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
params.connect_available_socket(output_node, "Closure");
|
||||
|
||||
SpaceNode &snode = *CTX_wm_space_node(¶ms.C);
|
||||
ed::space_node::sync_sockets_closure(snode, input_node, output_node, true, nullptr);
|
||||
sync_sockets_closure(snode, input_node, output_node, true, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -227,37 +228,6 @@ static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &
|
||||
socket_items::blend_read_data<ClosureOutputItemsAccessor>(&reader, node);
|
||||
}
|
||||
|
||||
static bool node_can_sync_sockets(const bContext &C,
|
||||
const bNodeTree & /*ntree*/,
|
||||
const bNode &node)
|
||||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(&C);
|
||||
if (!snode) {
|
||||
return false;
|
||||
}
|
||||
const NodeGeometryClosureOutput &storage = node_storage(node);
|
||||
if (!(storage.flag & NODE_GEO_CLOSURE_FLAG_MAY_NEED_SYNC)) {
|
||||
return false;
|
||||
}
|
||||
const ed::space_node::NodeSyncState state = ed::space_node::sync_sockets_state_closure_output(
|
||||
*snode, node);
|
||||
switch (state) {
|
||||
case ed::space_node::NodeSyncState::NoSyncSource:
|
||||
case ed::space_node::NodeSyncState::Synced: {
|
||||
const_cast<NodeGeometryClosureOutput &>(storage).flag &=
|
||||
~NODE_GEO_CLOSURE_FLAG_MAY_NEED_SYNC;
|
||||
break;
|
||||
}
|
||||
case ed::space_node::NodeSyncState::CanBeSynced: {
|
||||
return true;
|
||||
}
|
||||
case ed::space_node::NodeSyncState::ConflictingSyncSources: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
@@ -272,7 +242,6 @@ static void node_register()
|
||||
ntype.gather_link_search_ops = node_gather_link_searches;
|
||||
ntype.insert_link = node_insert_link;
|
||||
ntype.draw_buttons_ex = node_layout_ex;
|
||||
ntype.can_sync_sockets = node_can_sync_sockets;
|
||||
ntype.blend_write_storage_content = node_blend_write;
|
||||
ntype.blend_data_read_storage_content = node_blend_read;
|
||||
bke::node_type_storage(ntype, "NodeGeometryClosureOutput", node_free_storage, node_copy_storage);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "NOD_socket_items_ops.hh"
|
||||
#include "NOD_socket_items_ui.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
@@ -139,7 +140,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
params.connect_available_socket(node, "Bundle");
|
||||
|
||||
SpaceNode &snode = *CTX_wm_space_node(¶ms.C);
|
||||
ed::space_node::sync_sockets_combine_bundle(snode, node, nullptr);
|
||||
sync_sockets_combine_bundle(snode, node, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -153,37 +154,6 @@ static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &
|
||||
socket_items::blend_read_data<CombineBundleItemsAccessor>(&reader, node);
|
||||
}
|
||||
|
||||
static bool node_can_sync_sockets(const bContext &C,
|
||||
const bNodeTree & /*ntree*/,
|
||||
const bNode &node)
|
||||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(&C);
|
||||
if (!snode) {
|
||||
return false;
|
||||
}
|
||||
const NodeGeometryCombineBundle &storage = node_storage(node);
|
||||
if (!(storage.flag & NODE_GEO_COMBINE_BUNDLE_FLAG_MAY_NEED_SYNC)) {
|
||||
return false;
|
||||
}
|
||||
const ed::space_node::NodeSyncState state = ed::space_node::sync_sockets_state_combine_bundle(
|
||||
*snode, node);
|
||||
switch (state) {
|
||||
case ed::space_node::NodeSyncState::NoSyncSource:
|
||||
case ed::space_node::NodeSyncState::Synced: {
|
||||
const_cast<NodeGeometryCombineBundle &>(storage).flag &=
|
||||
~NODE_GEO_COMBINE_BUNDLE_FLAG_MAY_NEED_SYNC;
|
||||
break;
|
||||
}
|
||||
case ed::space_node::NodeSyncState::CanBeSynced: {
|
||||
return true;
|
||||
}
|
||||
case ed::space_node::NodeSyncState::ConflictingSyncSources: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
@@ -201,7 +171,6 @@ static void node_register()
|
||||
ntype.register_operators = node_operators;
|
||||
ntype.blend_write_storage_content = node_blend_write;
|
||||
ntype.blend_data_read_storage_content = node_blend_read;
|
||||
ntype.can_sync_sockets = node_can_sync_sockets;
|
||||
bke::node_type_storage(ntype, "NodeGeometryCombineBundle", node_free_storage, node_copy_storage);
|
||||
blender::bke::node_register_type(ntype);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "NOD_socket_items_ops.hh"
|
||||
#include "NOD_socket_items_ui.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
@@ -135,7 +136,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
params.connect_available_socket(node, "Closure");
|
||||
|
||||
SpaceNode &snode = *CTX_wm_space_node(¶ms.C);
|
||||
ed::space_node::sync_sockets_evaluate_closure(snode, node, nullptr);
|
||||
sync_sockets_evaluate_closure(snode, node, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -157,37 +158,6 @@ static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &
|
||||
socket_items::blend_read_data<EvaluateClosureOutputItemsAccessor>(&reader, node);
|
||||
}
|
||||
|
||||
static bool node_can_sync_sockets(const bContext &C,
|
||||
const bNodeTree & /*ntree*/,
|
||||
const bNode &node)
|
||||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(&C);
|
||||
if (!snode) {
|
||||
return false;
|
||||
}
|
||||
const NodeGeometryEvaluateClosure &storage = node_storage(node);
|
||||
if (!(storage.flag & NODE_GEO_EVALUATE_CLOSURE_FLAG_MAY_NEED_SYNC)) {
|
||||
return false;
|
||||
}
|
||||
const ed::space_node::NodeSyncState state = ed::space_node::sync_sockets_state_evaluate_closure(
|
||||
*snode, node);
|
||||
switch (state) {
|
||||
case ed::space_node::NodeSyncState::NoSyncSource:
|
||||
case ed::space_node::NodeSyncState::Synced: {
|
||||
const_cast<NodeGeometryEvaluateClosure &>(storage).flag &=
|
||||
~NODE_GEO_EVALUATE_CLOSURE_FLAG_MAY_NEED_SYNC;
|
||||
break;
|
||||
}
|
||||
case ed::space_node::NodeSyncState::CanBeSynced: {
|
||||
return true;
|
||||
}
|
||||
case ed::space_node::NodeSyncState::ConflictingSyncSources: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
@@ -204,7 +174,6 @@ static void node_register()
|
||||
ntype.register_operators = node_operators;
|
||||
ntype.blend_write_storage_content = node_blend_write;
|
||||
ntype.blend_data_read_storage_content = node_blend_read;
|
||||
ntype.can_sync_sockets = node_can_sync_sockets;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeGeometryEvaluateClosure", node_free_storage, node_copy_storage);
|
||||
blender::bke::node_register_type(ntype);
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "NOD_geo_bundle.hh"
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
#include "NOD_socket_items_blend.hh"
|
||||
#include "NOD_socket_items_ops.hh"
|
||||
#include "NOD_socket_items_ui.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
|
||||
#include <fmt/format.h>
|
||||
@@ -186,7 +186,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
params.connect_available_socket(node, "Bundle");
|
||||
|
||||
SpaceNode &snode = *CTX_wm_space_node(¶ms.C);
|
||||
ed::space_node::sync_sockets_separate_bundle(snode, node, nullptr);
|
||||
sync_sockets_separate_bundle(snode, node, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -200,37 +200,6 @@ static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &
|
||||
socket_items::blend_read_data<SeparateBundleItemsAccessor>(&reader, node);
|
||||
}
|
||||
|
||||
static bool node_can_sync_sockets(const bContext &C,
|
||||
const bNodeTree & /*ntree*/,
|
||||
const bNode &node)
|
||||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(&C);
|
||||
if (!snode) {
|
||||
return false;
|
||||
}
|
||||
const NodeGeometrySeparateBundle &storage = node_storage(node);
|
||||
if (!(storage.flag & NODE_GEO_SEPARATE_BUNDLE_FLAG_MAY_NEED_SYNC)) {
|
||||
return false;
|
||||
}
|
||||
const ed::space_node::NodeSyncState state = ed::space_node::sync_sockets_state_separate_bundle(
|
||||
*snode, node);
|
||||
switch (state) {
|
||||
case ed::space_node::NodeSyncState::NoSyncSource:
|
||||
case ed::space_node::NodeSyncState::Synced: {
|
||||
const_cast<NodeGeometrySeparateBundle &>(storage).flag &=
|
||||
~NODE_GEO_SEPARATE_BUNDLE_FLAG_MAY_NEED_SYNC;
|
||||
break;
|
||||
}
|
||||
case ed::space_node::NodeSyncState::CanBeSynced: {
|
||||
return true;
|
||||
}
|
||||
case ed::space_node::NodeSyncState::ConflictingSyncSources: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
@@ -248,7 +217,6 @@ static void node_register()
|
||||
ntype.register_operators = node_operators;
|
||||
ntype.blend_write_storage_content = node_blend_write;
|
||||
ntype.blend_data_read_storage_content = node_blend_read;
|
||||
ntype.can_sync_sockets = node_can_sync_sockets;
|
||||
bke::node_type_storage(
|
||||
ntype, "NodeGeometrySeparateBundle", node_free_storage, node_copy_storage);
|
||||
blender::bke::node_register_type(ntype);
|
||||
|
||||
616
source/blender/nodes/intern/sync_sockets.cc
Normal file
616
source/blender/nodes/intern/sync_sockets.cc
Normal file
@@ -0,0 +1,616 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "BKE_compute_context_cache.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_main_invariants.hh"
|
||||
#include "BKE_node_legacy_types.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_update.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_workspace.hh"
|
||||
|
||||
#include "ED_node.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "NOD_geo_bundle.hh"
|
||||
#include "NOD_geo_closure.hh"
|
||||
#include "NOD_socket_items.hh"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
enum class NodeSyncState {
|
||||
Synced,
|
||||
CanBeSynced,
|
||||
NoSyncSource,
|
||||
ConflictingSyncSources,
|
||||
};
|
||||
|
||||
struct BundleSyncState {
|
||||
NodeSyncState state;
|
||||
std::optional<nodes::BundleSignature> source_signature;
|
||||
};
|
||||
|
||||
struct ClosureSyncState {
|
||||
NodeSyncState state;
|
||||
std::optional<nodes::ClosureSignature> source_signature;
|
||||
};
|
||||
|
||||
static BundleSyncState get_sync_state_separate_bundle(const SpaceNode &snode,
|
||||
const bNode &separate_bundle_node)
|
||||
{
|
||||
BLI_assert(separate_bundle_node.is_type("GeometryNodeSeparateBundle"));
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNodeSocket &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);
|
||||
const Vector<nodes::BundleSignature> source_signatures =
|
||||
ed::space_node::gather_linked_origin_bundle_signatures(
|
||||
current_context, bundle_socket, compute_context_cache);
|
||||
if (source_signatures.is_empty()) {
|
||||
return {NodeSyncState::NoSyncSource};
|
||||
}
|
||||
if (!nodes::BundleSignature::all_matching_exactly(source_signatures)) {
|
||||
return {NodeSyncState::ConflictingSyncSources};
|
||||
}
|
||||
const nodes::BundleSignature &source_signature = source_signatures[0];
|
||||
const nodes::BundleSignature ¤t_signature =
|
||||
nodes::BundleSignature::from_separate_bundle_node(separate_bundle_node);
|
||||
if (!source_signature.matches_exactly(current_signature)) {
|
||||
return {NodeSyncState::CanBeSynced, source_signature};
|
||||
}
|
||||
return {NodeSyncState::Synced};
|
||||
}
|
||||
|
||||
static BundleSyncState get_sync_state_combine_bundle(const SpaceNode &snode,
|
||||
const bNode &combine_bundle_node)
|
||||
{
|
||||
BLI_assert(combine_bundle_node.is_type("GeometryNodeCombineBundle"));
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNodeSocket &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);
|
||||
const Vector<nodes::BundleSignature> source_signatures =
|
||||
ed::space_node::gather_linked_target_bundle_signatures(
|
||||
current_context, bundle_socket, compute_context_cache);
|
||||
if (source_signatures.is_empty()) {
|
||||
return {NodeSyncState::NoSyncSource};
|
||||
}
|
||||
if (!nodes::BundleSignature::all_matching_exactly(source_signatures)) {
|
||||
return {NodeSyncState::ConflictingSyncSources};
|
||||
}
|
||||
const nodes::BundleSignature &source_signature = source_signatures[0];
|
||||
const nodes::BundleSignature ¤t_signature =
|
||||
nodes::BundleSignature::from_combine_bundle_node(combine_bundle_node);
|
||||
if (!source_signature.matches_exactly(current_signature)) {
|
||||
return {NodeSyncState::CanBeSynced, source_signature};
|
||||
}
|
||||
return {NodeSyncState::Synced};
|
||||
}
|
||||
|
||||
static ClosureSyncState get_sync_state_closure_output(const SpaceNode &snode,
|
||||
const bNode &closure_output_node)
|
||||
{
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNodeSocket &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);
|
||||
const Vector<nodes::ClosureSignature> source_signatures =
|
||||
ed::space_node::gather_linked_target_closure_signatures(
|
||||
current_context, closure_socket, compute_context_cache);
|
||||
if (source_signatures.is_empty()) {
|
||||
return {NodeSyncState::NoSyncSource};
|
||||
}
|
||||
if (!nodes::ClosureSignature::all_matching_exactly(source_signatures)) {
|
||||
return {NodeSyncState::ConflictingSyncSources};
|
||||
}
|
||||
const nodes::ClosureSignature &source_signature = source_signatures[0];
|
||||
const nodes::ClosureSignature ¤t_signature =
|
||||
nodes::ClosureSignature::from_closure_output_node(closure_output_node);
|
||||
if (!source_signature.matches_exactly(current_signature)) {
|
||||
return {NodeSyncState::CanBeSynced, source_signature};
|
||||
}
|
||||
return {NodeSyncState::Synced};
|
||||
}
|
||||
|
||||
static ClosureSyncState get_sync_state_evaluate_closure(const SpaceNode &snode,
|
||||
const bNode &evaluate_closure_node)
|
||||
{
|
||||
snode.edittree->ensure_topology_cache();
|
||||
const bNodeSocket &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);
|
||||
const Vector<nodes::ClosureSignature> source_signatures =
|
||||
ed::space_node::gather_linked_origin_closure_signatures(
|
||||
current_context, closure_socket, compute_context_cache);
|
||||
if (source_signatures.is_empty()) {
|
||||
return {NodeSyncState::NoSyncSource};
|
||||
}
|
||||
if (!nodes::ClosureSignature::all_matching_exactly(source_signatures)) {
|
||||
return {NodeSyncState::ConflictingSyncSources};
|
||||
}
|
||||
const nodes::ClosureSignature &source_signature = source_signatures[0];
|
||||
const nodes::ClosureSignature ¤t_signature =
|
||||
nodes::ClosureSignature::from_evaluate_closure_node(evaluate_closure_node);
|
||||
if (!source_signature.matches_exactly(current_signature)) {
|
||||
return {NodeSyncState::CanBeSynced, source_signature};
|
||||
}
|
||||
return {NodeSyncState::Synced};
|
||||
}
|
||||
|
||||
void sync_sockets_separate_bundle(SpaceNode &snode,
|
||||
bNode &separate_bundle_node,
|
||||
ReportList *reports)
|
||||
{
|
||||
const BundleSyncState sync_state = get_sync_state_separate_bundle(snode, separate_bundle_node);
|
||||
switch (sync_state.state) {
|
||||
case NodeSyncState::Synced:
|
||||
return;
|
||||
case NodeSyncState::NoSyncSource:
|
||||
BKE_report(reports, RPT_INFO, "No bundle signature found");
|
||||
return;
|
||||
case NodeSyncState::ConflictingSyncSources:
|
||||
BKE_report(reports, RPT_INFO, "Found conflicting bundle signatures");
|
||||
return;
|
||||
case NodeSyncState::CanBeSynced:
|
||||
break;
|
||||
}
|
||||
|
||||
auto &storage = *static_cast<NodeGeometrySeparateBundle *>(separate_bundle_node.storage);
|
||||
|
||||
Map<std::string, int> old_identifiers;
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const NodeGeometrySeparateBundleItem &item = storage.items[i];
|
||||
old_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
|
||||
nodes::socket_items::clear<nodes::SeparateBundleItemsAccessor>(separate_bundle_node);
|
||||
for (const nodes::BundleSignature::Item &item : sync_state.source_signature->items) {
|
||||
NodeGeometrySeparateBundleItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes ::SeparateBundleItemsAccessor>(
|
||||
separate_bundle_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &separate_bundle_node);
|
||||
}
|
||||
|
||||
void sync_sockets_combine_bundle(SpaceNode &snode, bNode &combine_bundle_node, ReportList *reports)
|
||||
{
|
||||
const BundleSyncState sync_state = get_sync_state_combine_bundle(snode, combine_bundle_node);
|
||||
switch (sync_state.state) {
|
||||
case NodeSyncState::Synced:
|
||||
return;
|
||||
case NodeSyncState::NoSyncSource:
|
||||
BKE_report(reports, RPT_INFO, "No bundle signature found");
|
||||
return;
|
||||
case NodeSyncState::ConflictingSyncSources:
|
||||
BKE_report(reports, RPT_INFO, "Found conflicting bundle signatures");
|
||||
return;
|
||||
case NodeSyncState::CanBeSynced:
|
||||
break;
|
||||
}
|
||||
|
||||
auto &storage = *static_cast<NodeGeometryCombineBundle *>(combine_bundle_node.storage);
|
||||
|
||||
Map<std::string, int> old_identifiers;
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const NodeGeometryCombineBundleItem &item = storage.items[i];
|
||||
old_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
|
||||
nodes::socket_items::clear<nodes::CombineBundleItemsAccessor>(combine_bundle_node);
|
||||
for (const nodes::BundleSignature::Item &item : sync_state.source_signature->items) {
|
||||
NodeGeometryCombineBundleItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes ::CombineBundleItemsAccessor>(
|
||||
combine_bundle_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &combine_bundle_node);
|
||||
}
|
||||
|
||||
void sync_sockets_evaluate_closure(SpaceNode &snode,
|
||||
bNode &evaluate_closure_node,
|
||||
ReportList *reports)
|
||||
{
|
||||
const ClosureSyncState sync_state = get_sync_state_evaluate_closure(snode,
|
||||
evaluate_closure_node);
|
||||
switch (sync_state.state) {
|
||||
case NodeSyncState::Synced:
|
||||
return;
|
||||
case NodeSyncState::NoSyncSource:
|
||||
BKE_report(reports, RPT_INFO, "No closure signature found");
|
||||
return;
|
||||
case NodeSyncState::ConflictingSyncSources:
|
||||
BKE_report(reports, RPT_INFO, "Found conflicting closure signatures");
|
||||
return;
|
||||
case NodeSyncState::CanBeSynced:
|
||||
break;
|
||||
}
|
||||
|
||||
auto &storage = *static_cast<NodeGeometryEvaluateClosure *>(evaluate_closure_node.storage);
|
||||
|
||||
Map<std::string, int> old_input_identifiers;
|
||||
Map<std::string, int> old_output_identifiers;
|
||||
for (const int i : IndexRange(storage.input_items.items_num)) {
|
||||
const NodeGeometryEvaluateClosureInputItem &item = storage.input_items.items[i];
|
||||
old_input_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
for (const int i : IndexRange(storage.output_items.items_num)) {
|
||||
const NodeGeometryEvaluateClosureOutputItem &item = storage.output_items.items[i];
|
||||
old_output_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
|
||||
nodes::socket_items::clear<nodes::EvaluateClosureInputItemsAccessor>(evaluate_closure_node);
|
||||
nodes::socket_items::clear<nodes::EvaluateClosureOutputItemsAccessor>(evaluate_closure_node);
|
||||
|
||||
for (const nodes::ClosureSignature::Item &item : sync_state.source_signature->inputs) {
|
||||
NodeGeometryEvaluateClosureInputItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes::EvaluateClosureInputItemsAccessor>(
|
||||
evaluate_closure_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_input_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &item : sync_state.source_signature->outputs) {
|
||||
NodeGeometryEvaluateClosureOutputItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes::EvaluateClosureOutputItemsAccessor>(
|
||||
evaluate_closure_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_output_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &evaluate_closure_node);
|
||||
}
|
||||
|
||||
void sync_sockets_closure(SpaceNode &snode,
|
||||
bNode &closure_input_node,
|
||||
bNode &closure_output_node,
|
||||
const bool initialize_internal_links,
|
||||
ReportList *reports)
|
||||
{
|
||||
const ClosureSyncState sync_state = get_sync_state_closure_output(snode, closure_output_node);
|
||||
switch (sync_state.state) {
|
||||
case NodeSyncState::Synced:
|
||||
return;
|
||||
case NodeSyncState::NoSyncSource:
|
||||
BKE_report(reports, RPT_INFO, "No closure signature found");
|
||||
return;
|
||||
case NodeSyncState::ConflictingSyncSources:
|
||||
BKE_report(reports, RPT_INFO, "Found conflicting closure signatures");
|
||||
return;
|
||||
case NodeSyncState::CanBeSynced:
|
||||
break;
|
||||
}
|
||||
const nodes::ClosureSignature &signature = *sync_state.source_signature;
|
||||
|
||||
auto &storage = *static_cast<NodeGeometryClosureOutput *>(closure_output_node.storage);
|
||||
|
||||
Map<std::string, int> old_input_identifiers;
|
||||
Map<std::string, int> old_output_identifiers;
|
||||
for (const int i : IndexRange(storage.input_items.items_num)) {
|
||||
const NodeGeometryClosureInputItem &item = storage.input_items.items[i];
|
||||
old_input_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
for (const int i : IndexRange(storage.output_items.items_num)) {
|
||||
const NodeGeometryClosureOutputItem &item = storage.output_items.items[i];
|
||||
old_output_identifiers.add_new(StringRef(item.name), item.identifier);
|
||||
}
|
||||
|
||||
nodes::socket_items::clear<nodes::ClosureInputItemsAccessor>(closure_output_node);
|
||||
nodes::socket_items::clear<nodes::ClosureOutputItemsAccessor>(closure_output_node);
|
||||
|
||||
for (const nodes::ClosureSignature::Item &item : signature.inputs) {
|
||||
NodeGeometryClosureInputItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<nodes::ClosureInputItemsAccessor>(
|
||||
closure_output_node, item.type->type, item.key.c_str());
|
||||
if (item.structure_type) {
|
||||
new_item.structure_type = int(*item.structure_type);
|
||||
}
|
||||
if (const std::optional<int> old_identifier = old_input_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &item : signature.outputs) {
|
||||
NodeGeometryClosureOutputItem &new_item =
|
||||
*nodes::socket_items::add_item_with_socket_type_and_name<
|
||||
nodes::ClosureOutputItemsAccessor>(
|
||||
closure_output_node, item.type->type, item.key.c_str());
|
||||
if (const std::optional<int> old_identifier = old_output_identifiers.lookup_try(item.key)) {
|
||||
new_item.identifier = *old_identifier;
|
||||
}
|
||||
}
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &closure_input_node);
|
||||
BKE_ntree_update_tag_node_property(snode.edittree, &closure_output_node);
|
||||
|
||||
if (initialize_internal_links) {
|
||||
nodes::update_node_declaration_and_sockets(*snode.edittree, closure_input_node);
|
||||
nodes::update_node_declaration_and_sockets(*snode.edittree, closure_output_node);
|
||||
|
||||
snode.edittree->ensure_topology_cache();
|
||||
Vector<std::pair<bNodeSocket *, bNodeSocket *>> internal_links;
|
||||
for (const int input_i : signature.inputs.index_range()) {
|
||||
const nodes::ClosureSignature::Item &input_item = signature.inputs[input_i];
|
||||
for (const int output_i : signature.outputs.index_range()) {
|
||||
const nodes::ClosureSignature::Item &output_item = signature.outputs[output_i];
|
||||
if (input_item.key == output_item.key) {
|
||||
internal_links.append({&closure_input_node.output_socket(input_i),
|
||||
&closure_output_node.input_socket(output_i)});
|
||||
}
|
||||
};
|
||||
}
|
||||
for (auto &&[from_socket, to_socket] : internal_links) {
|
||||
bke::node_add_link(
|
||||
*snode.edittree, closure_input_node, *from_socket, closure_output_node, *to_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_bundle_sync_tooltip(const nodes::BundleSignature &old_signature,
|
||||
const nodes::BundleSignature &new_signature)
|
||||
{
|
||||
Vector<StringRef> added_items;
|
||||
Vector<StringRef> removed_items;
|
||||
Vector<StringRef> changed_items;
|
||||
|
||||
for (const nodes::BundleSignature::Item &new_item : new_signature.items) {
|
||||
if (const nodes::BundleSignature::Item *old_item = old_signature.items.lookup_key_ptr_as(
|
||||
new_item.key))
|
||||
{
|
||||
if (new_item.type->type != old_item->type->type) {
|
||||
changed_items.append(new_item.key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
added_items.append(new_item.key);
|
||||
}
|
||||
}
|
||||
for (const nodes::BundleSignature ::Item &old_item : old_signature.items) {
|
||||
if (!new_signature.items.contains_as(old_item.key)) {
|
||||
removed_items.append(old_item.key);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::memory_buffer string_buffer;
|
||||
auto buf = fmt::appender(string_buffer);
|
||||
if (!added_items.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Add"), fmt::join(added_items, ", "));
|
||||
}
|
||||
if (!removed_items.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Remove"), fmt::join(removed_items, ", "));
|
||||
}
|
||||
if (!changed_items.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Change"), fmt::join(changed_items, ", "));
|
||||
}
|
||||
fmt::format_to(buf, TIP_("\nUpdate based on linked bundle signature"));
|
||||
|
||||
return fmt::to_string(string_buffer);
|
||||
}
|
||||
|
||||
static std::string get_closure_sync_tooltip(const nodes::ClosureSignature &old_signature,
|
||||
const nodes::ClosureSignature &new_signature)
|
||||
{
|
||||
Vector<StringRef> added_inputs;
|
||||
Vector<StringRef> removed_inputs;
|
||||
Vector<StringRef> changed_inputs;
|
||||
|
||||
Vector<StringRef> added_outputs;
|
||||
Vector<StringRef> removed_outputs;
|
||||
Vector<StringRef> changed_outputs;
|
||||
|
||||
for (const nodes::ClosureSignature::Item &new_item : new_signature.inputs) {
|
||||
if (const nodes::ClosureSignature::Item *old_item = old_signature.inputs.lookup_key_ptr_as(
|
||||
new_item.key))
|
||||
{
|
||||
if (new_item.type->type != old_item->type->type) {
|
||||
changed_inputs.append(new_item.key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
added_inputs.append(new_item.key);
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &old_item : old_signature.inputs) {
|
||||
if (!new_signature.inputs.contains_as(old_item.key)) {
|
||||
removed_inputs.append(old_item.key);
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &new_item : new_signature.outputs) {
|
||||
if (const nodes::ClosureSignature::Item *old_item = old_signature.outputs.lookup_key_ptr_as(
|
||||
new_item.key))
|
||||
{
|
||||
if (new_item.type->type != old_item->type->type) {
|
||||
changed_outputs.append(new_item.key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
added_outputs.append(new_item.key);
|
||||
}
|
||||
}
|
||||
for (const nodes::ClosureSignature::Item &old_item : old_signature.outputs) {
|
||||
if (!new_signature.outputs.contains_as(old_item.key)) {
|
||||
removed_outputs.append(old_item.key);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::memory_buffer string_buffer;
|
||||
auto buf = fmt::appender(string_buffer);
|
||||
if (!added_inputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Add Inputs"), fmt::join(added_inputs, ", "));
|
||||
}
|
||||
if (!removed_inputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Remove Inputs"), fmt::join(removed_inputs, ", "));
|
||||
}
|
||||
if (!changed_inputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Change Inputs"), fmt::join(changed_inputs, ", "));
|
||||
}
|
||||
if (!added_outputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Add Outputs"), fmt::join(added_outputs, ", "));
|
||||
}
|
||||
if (!removed_outputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Remove Outputs"), fmt::join(removed_outputs, ", "));
|
||||
}
|
||||
if (!changed_outputs.is_empty()) {
|
||||
fmt::format_to(buf, "{}: {}\n", TIP_("Change Outputs"), fmt::join(changed_outputs, ", "));
|
||||
}
|
||||
fmt::format_to(buf, TIP_("\nUpdate based on linked closure signature"));
|
||||
|
||||
return fmt::to_string(string_buffer);
|
||||
}
|
||||
|
||||
void sync_node(bContext &C, bNode &node, ReportList *reports)
|
||||
{
|
||||
const bke::bNodeZoneType &closure_zone_type = *bke::zone_type_by_node_type(
|
||||
GEO_NODE_CLOSURE_OUTPUT);
|
||||
SpaceNode &snode = *CTX_wm_space_node(&C);
|
||||
if (node.is_type("GeometryNodeEvaluateClosure")) {
|
||||
sync_sockets_evaluate_closure(snode, node, reports);
|
||||
}
|
||||
else if (node.is_type("GeometryNodeSeparateBundle")) {
|
||||
sync_sockets_separate_bundle(snode, node, reports);
|
||||
}
|
||||
else if (node.is_type("GeometryNodeCombineBundle")) {
|
||||
sync_sockets_combine_bundle(snode, node, reports);
|
||||
}
|
||||
else if (node.is_type("GeometryNodeClosureInput")) {
|
||||
bNode &closure_input_node = node;
|
||||
if (bNode *closure_output_node = closure_zone_type.get_corresponding_output(
|
||||
*snode.edittree, closure_input_node))
|
||||
{
|
||||
sync_sockets_closure(snode, closure_input_node, *closure_output_node, false, reports);
|
||||
}
|
||||
}
|
||||
else if (node.is_type("GeometryNodeClosureOutput")) {
|
||||
bNode &closure_output_node = node;
|
||||
if (bNode *closure_input_node = closure_zone_type.get_corresponding_input(*snode.edittree,
|
||||
closure_output_node))
|
||||
{
|
||||
sync_sockets_closure(snode, *closure_input_node, closure_output_node, false, reports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string sync_node_description_get(const bContext &C, const bNode &node)
|
||||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(&C);
|
||||
if (!snode) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (node.is_type("GeometryNodeSeparateBundle")) {
|
||||
const nodes::BundleSignature old_signature = nodes::BundleSignature::from_separate_bundle_node(
|
||||
node);
|
||||
if (const std::optional<nodes::BundleSignature> new_signature =
|
||||
get_sync_state_separate_bundle(*snode, node).source_signature)
|
||||
{
|
||||
return get_bundle_sync_tooltip(old_signature, *new_signature);
|
||||
}
|
||||
}
|
||||
else if (node.is_type("GeometryNodeCombineBundle")) {
|
||||
const nodes::BundleSignature old_signature = nodes::BundleSignature::from_combine_bundle_node(
|
||||
node);
|
||||
if (const std::optional<nodes::BundleSignature> new_signature =
|
||||
get_sync_state_combine_bundle(*snode, node).source_signature)
|
||||
{
|
||||
return get_bundle_sync_tooltip(old_signature, *new_signature);
|
||||
}
|
||||
}
|
||||
else if (node.is_type("GeometryNodeEvaluateClosure")) {
|
||||
const nodes::ClosureSignature old_signature =
|
||||
nodes::ClosureSignature::from_evaluate_closure_node(node);
|
||||
if (const std::optional<nodes::ClosureSignature> new_signature =
|
||||
get_sync_state_evaluate_closure(*snode, node).source_signature)
|
||||
{
|
||||
return get_closure_sync_tooltip(old_signature, *new_signature);
|
||||
}
|
||||
}
|
||||
else if (node.is_type("GeometryNodeClosureOutput")) {
|
||||
const nodes::ClosureSignature old_signature =
|
||||
nodes::ClosureSignature::from_closure_output_node(node);
|
||||
if (const std::optional<nodes::ClosureSignature> new_signature =
|
||||
get_sync_state_closure_output(*snode, node).source_signature)
|
||||
{
|
||||
return get_closure_sync_tooltip(old_signature, *new_signature);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool node_can_sync_sockets(const bContext &C, const bNodeTree & /*tree*/, const bNode &node)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(&C);
|
||||
if (!snode) {
|
||||
return false;
|
||||
}
|
||||
Map<int, bool> &cache = ed::space_node::node_can_sync_cache_get(*snode);
|
||||
const bool can_sync = cache.lookup_or_add_cb(node.identifier, [&]() {
|
||||
if (node.is_type("GeometryNodeEvaluateClosure")) {
|
||||
return get_sync_state_evaluate_closure(*snode, node).source_signature.has_value();
|
||||
}
|
||||
if (node.is_type("GeometryNodeClosureOutput")) {
|
||||
return get_sync_state_closure_output(*snode, node).source_signature.has_value();
|
||||
}
|
||||
if (node.is_type("GeometryNodeCombineBundle")) {
|
||||
return get_sync_state_combine_bundle(*snode, node).source_signature.has_value();
|
||||
}
|
||||
if (node.is_type("GeometryNodeSeparateBundle")) {
|
||||
return get_sync_state_separate_bundle(*snode, node).source_signature.has_value();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return can_sync;
|
||||
}
|
||||
|
||||
void node_can_sync_cache_clear(Main &bmain)
|
||||
{
|
||||
if (wmWindowManager *wm = static_cast<wmWindowManager *>(bmain.wm.first)) {
|
||||
LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
|
||||
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_NODE) {
|
||||
SpaceNode *snode = reinterpret_cast<SpaceNode *>(sl);
|
||||
/* This may be called before runtime data is initialized currently. */
|
||||
if (snode->runtime) {
|
||||
Map<int, bool> &cache = ed::space_node::node_can_sync_cache_get(*snode);
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
Reference in New Issue
Block a user