Geometry Nodes: show sync button on bundle and closure nodes if syncing is possible
This shows a sync button when Geometry Nodes has detected that syncing is possible and might be necessary. Pull Request: https://projects.blender.org/blender/blender/pulls/140967
This commit is contained in:
@@ -421,6 +421,13 @@ struct bNodeType {
|
||||
/** True when the node still works but it's usage is discouraged. */
|
||||
const char *deprecation_notice = nullptr;
|
||||
|
||||
/**
|
||||
* In some nodes the set of sockets depends on other data like linked nodes. For example, the
|
||||
* Separate Bundle node can adapt based on what the bundle contains that is linked to it. When
|
||||
* this function returns true, a sync button should be shown for the node that updates the node.
|
||||
*/
|
||||
bool (*can_sync_sockets)(const bContext &C, const bNodeTree &tree, const bNode &node) = nullptr;
|
||||
|
||||
/* RNA integration */
|
||||
ExtensionRNA rna_ext = {};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "BKE_anim_data.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_enum.hh"
|
||||
@@ -291,6 +292,12 @@ struct NodeTreeRelations {
|
||||
BLI_assert(group_node_users_.has_value());
|
||||
return group_node_users_->lookup(ntree);
|
||||
}
|
||||
|
||||
Span<bNodeTree *> get_all_trees()
|
||||
{
|
||||
this->ensure_all_trees();
|
||||
return *all_trees_;
|
||||
}
|
||||
};
|
||||
|
||||
struct TreeUpdateResult {
|
||||
@@ -305,6 +312,7 @@ 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)
|
||||
@@ -408,6 +416,13 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -509,6 +524,7 @@ 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);
|
||||
@@ -800,6 +816,50 @@ 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. */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_light_types.h"
|
||||
@@ -2504,18 +2505,20 @@ static void node_draw_extra_info_row(const bNode &node,
|
||||
0,
|
||||
0,
|
||||
extra_info_row.tooltip);
|
||||
if (extra_info_row.set_execute_fn) {
|
||||
extra_info_row.set_execute_fn(*but_icon);
|
||||
}
|
||||
if (extra_info_row.tooltip_fn != nullptr) {
|
||||
UI_but_func_tooltip_set(
|
||||
but_icon, extra_info_row.tooltip_fn, tooltip_arg, extra_info_row.tooltip_fn_free_arg);
|
||||
}
|
||||
UI_block_emboss_set(&block, ui::EmbossType::Emboss);
|
||||
|
||||
const float but_text_left = but_icon_right + 6.0f * UI_SCALE_FAC;
|
||||
const float but_text_right = rect.xmax;
|
||||
const float but_text_width = but_text_right - but_text_left;
|
||||
|
||||
uiBut *but_text = uiDefBut(&block,
|
||||
ButType::Label,
|
||||
extra_info_row.set_execute_fn ? ButType::But : ButType::Label,
|
||||
0,
|
||||
extra_info_row.text.c_str(),
|
||||
int(but_text_left),
|
||||
@@ -2526,7 +2529,10 @@ static void node_draw_extra_info_row(const bNode &node,
|
||||
0,
|
||||
0,
|
||||
extra_info_row.tooltip);
|
||||
|
||||
UI_but_drawflag_enable(but_text, UI_BUT_TEXT_LEFT);
|
||||
if (extra_info_row.set_execute_fn) {
|
||||
extra_info_row.set_execute_fn(*but_text);
|
||||
}
|
||||
if (extra_info_row.tooltip_fn != nullptr) {
|
||||
/* Don't pass tooltip free function because it's already used on the uiBut above. */
|
||||
UI_but_func_tooltip_set(but_text, extra_info_row.tooltip_fn, tooltip_arg, nullptr);
|
||||
@@ -2536,6 +2542,8 @@ static void node_draw_extra_info_row(const bNode &node,
|
||||
UI_but_flag_enable(but_text, UI_BUT_INACTIVE);
|
||||
UI_but_flag_enable(but_icon, UI_BUT_INACTIVE);
|
||||
}
|
||||
|
||||
UI_block_emboss_set(&block, ui::EmbossType::Emboss);
|
||||
}
|
||||
|
||||
static void node_draw_extra_info_panel_back(const bNode &node, const rctf &extra_info_rect)
|
||||
@@ -2857,6 +2865,31 @@ 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)) {
|
||||
iconofs -= iconbutw;
|
||||
UI_block_emboss_set(&block, ui::EmbossType::None);
|
||||
uiBut *but = uiDefIconBut(&block,
|
||||
ButType::ButToggle,
|
||||
0,
|
||||
ICON_FILE_REFRESH,
|
||||
iconofs,
|
||||
rct.ymax - NODE_DY,
|
||||
iconbutw,
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
"");
|
||||
|
||||
wmOperatorType *ot = WM_operatortype_find("NODE_OT_sockets_sync", false);
|
||||
UI_but_operator_set(but, ot, wm::OpCallContext::InvokeDefault);
|
||||
PointerRNA *opptr = UI_but_operator_ptr_ensure(but);
|
||||
opptr->data = bke::idprop::create_group("wmOperatorProperties").release();
|
||||
RNA_string_set(opptr, "node_name", node.name);
|
||||
UI_block_emboss_set(&block, ui::EmbossType::Emboss);
|
||||
}
|
||||
|
||||
/* Preview. */
|
||||
if (node_is_previewable(snode, ntree, node)) {
|
||||
const bool is_active = node.flag & NODE_PREVIEW;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_windowmanager_enums.h"
|
||||
@@ -24,6 +26,8 @@
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "NOD_geo_bundle.hh"
|
||||
#include "NOD_geo_closure.hh"
|
||||
#include "NOD_socket_items.hh"
|
||||
@@ -392,6 +396,37 @@ void sync_sockets_closure(SpaceNode &snode,
|
||||
}
|
||||
}
|
||||
|
||||
static Vector<bNode *> get_nodes_to_sync(bContext &C, PointerRNA *ptr)
|
||||
{
|
||||
SpaceNode &snode = *CTX_wm_space_node(&C);
|
||||
if (!snode.edittree) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::string> node_name;
|
||||
PropertyRNA *node_name_prop = RNA_struct_find_property(ptr, "node_name");
|
||||
if (RNA_property_is_set(ptr, node_name_prop)) {
|
||||
node_name = RNA_property_string_get(ptr, node_name_prop);
|
||||
}
|
||||
|
||||
Vector<bNode *> nodes_to_sync;
|
||||
bNodeTree &tree = *snode.edittree;
|
||||
for (bNode *node : tree.all_nodes()) {
|
||||
if (node_name.has_value()) {
|
||||
if (node->name != node_name) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(node->flag & NODE_SELECT)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
nodes_to_sync.append(node);
|
||||
}
|
||||
return nodes_to_sync;
|
||||
}
|
||||
|
||||
static wmOperatorStatus sockets_sync_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main &bmain = *CTX_data_main(C);
|
||||
@@ -400,14 +435,16 @@ static wmOperatorStatus sockets_sync_exec(bContext *C, wmOperator *op)
|
||||
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 : tree.all_nodes()) {
|
||||
if (!(node->flag & NODE_SELECT)) {
|
||||
continue;
|
||||
}
|
||||
for (bNode *node : nodes_to_sync) {
|
||||
if (node->is_type("GeometryNodeEvaluateClosure")) {
|
||||
sync_sockets_evaluate_closure(snode, *node, op->reports);
|
||||
}
|
||||
@@ -438,16 +475,182 @@ static wmOperatorStatus sockets_sync_exec(bContext *C, wmOperator *op)
|
||||
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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
void NODE_OT_sockets_sync(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Sync Sockets";
|
||||
ot->idname = "NODE_OT_sockets_sync";
|
||||
ot->description = "Update sockets to match what is actually used";
|
||||
ot->get_description = sockets_sync_get_description;
|
||||
|
||||
ot->poll = ED_operator_node_editable;
|
||||
ot->exec = sockets_sync_exec;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
PropertyRNA *prop;
|
||||
prop = RNA_def_string(ot->srna, "node_name", nullptr, 0, "Node Name", nullptr);
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::space_node
|
||||
|
||||
@@ -2225,9 +2225,16 @@ 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 {
|
||||
@@ -2266,9 +2273,16 @@ 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 {
|
||||
@@ -2392,12 +2406,17 @@ 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;
|
||||
char _pad[4];
|
||||
/** #NodeGeometryCombineBundleFlag */
|
||||
uint32_t flag;
|
||||
} NodeGeometryCombineBundle;
|
||||
|
||||
typedef struct NodeGeometrySeparateBundleItem {
|
||||
@@ -2407,12 +2426,17 @@ 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;
|
||||
char _pad[4];
|
||||
/** #NodeGeometrySeparateBundleFlag */
|
||||
uint32_t flag;
|
||||
} NodeGeometrySeparateBundle;
|
||||
|
||||
typedef struct NodeFunctionFormatStringItem {
|
||||
|
||||
@@ -4,17 +4,34 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_node.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
struct BundleSignature {
|
||||
|
||||
struct Item {
|
||||
std::string key;
|
||||
const bke::bNodeSocketType *type = nullptr;
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return get_default_hash(this->key);
|
||||
}
|
||||
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_1(Item, key)
|
||||
};
|
||||
|
||||
Vector<Item> items;
|
||||
struct ItemKeyGetter {
|
||||
std::string operator()(const Item &item)
|
||||
{
|
||||
return item.key;
|
||||
}
|
||||
};
|
||||
|
||||
CustomIDVectorSet<Item, ItemKeyGetter> items;
|
||||
|
||||
bool matches_exactly(const BundleSignature &other) const;
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "BKE_node.hh"
|
||||
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
/** Describes the names and types of the inputs and outputs of a closure. */
|
||||
@@ -17,8 +19,15 @@ class ClosureSignature {
|
||||
std::optional<StructureType> structure_type = std::nullopt;
|
||||
};
|
||||
|
||||
Vector<Item> inputs;
|
||||
Vector<Item> outputs;
|
||||
struct ItemKeyGetter {
|
||||
std::string operator()(const Item &item)
|
||||
{
|
||||
return item.key;
|
||||
}
|
||||
};
|
||||
|
||||
CustomIDVectorSet<Item, ItemKeyGetter> inputs;
|
||||
CustomIDVectorSet<Item, ItemKeyGetter> outputs;
|
||||
|
||||
std::optional<int> find_input_index(StringRef key) const;
|
||||
std::optional<int> find_output_index(StringRef key) const;
|
||||
|
||||
@@ -19,6 +19,8 @@ struct NodeExtraInfoRow {
|
||||
void *tooltip_fn_arg = nullptr;
|
||||
void (*tooltip_fn_free_arg)(void *) = nullptr;
|
||||
void *(*tooltip_fn_copy_arg)(void *) = nullptr;
|
||||
|
||||
std::function<void(uiBut &)> set_execute_fn;
|
||||
};
|
||||
|
||||
struct NodeExtraInfoParams {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
#include "NOD_geo_closure.hh"
|
||||
#include "NOD_socket_items_blend.hh"
|
||||
#include "NOD_socket_items_ops.hh"
|
||||
@@ -225,6 +227,37 @@ 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;
|
||||
@@ -239,6 +272,7 @@ 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);
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "NOD_socket_items_ui.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
@@ -151,6 +153,37 @@ 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;
|
||||
@@ -168,6 +201,7 @@ 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);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "NOD_socket_items_ui.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
@@ -155,6 +157,37 @@ 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;
|
||||
@@ -171,6 +204,7 @@ 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);
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "NOD_socket_items_ui.hh"
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
||||
#include "BKE_idprop.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
@@ -198,6 +200,37 @@ 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;
|
||||
@@ -215,6 +248,7 @@ 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);
|
||||
|
||||
@@ -293,7 +293,7 @@ BundleSignature BundleSignature::from_combine_bundle_node(const bNode &node)
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const NodeGeometryCombineBundleItem &item = storage.items[i];
|
||||
if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) {
|
||||
signature.items.append({item.name, stype});
|
||||
signature.items.add({item.name, stype});
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
@@ -307,7 +307,7 @@ BundleSignature BundleSignature::from_separate_bundle_node(const bNode &node)
|
||||
for (const int i : IndexRange(storage.items_num)) {
|
||||
const NodeGeometrySeparateBundleItem &item = storage.items[i];
|
||||
if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) {
|
||||
signature.items.append({item.name, stype});
|
||||
signature.items.add({item.name, stype});
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
|
||||
@@ -94,13 +94,13 @@ ClosureSignature ClosureSignature::from_closure_output_node(const bNode &node)
|
||||
for (const int i : IndexRange(storage.input_items.items_num)) {
|
||||
const NodeGeometryClosureInputItem &item = storage.input_items.items[i];
|
||||
if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) {
|
||||
signature.inputs.append({item.name, stype});
|
||||
signature.inputs.add({item.name, stype});
|
||||
}
|
||||
}
|
||||
for (const int i : IndexRange(storage.output_items.items_num)) {
|
||||
const NodeGeometryClosureOutputItem &item = storage.output_items.items[i];
|
||||
if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) {
|
||||
signature.outputs.append({item.name, stype});
|
||||
signature.outputs.add({item.name, stype});
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
@@ -114,13 +114,13 @@ ClosureSignature ClosureSignature::from_evaluate_closure_node(const bNode &node)
|
||||
for (const int i : IndexRange(storage.input_items.items_num)) {
|
||||
const NodeGeometryEvaluateClosureInputItem &item = storage.input_items.items[i];
|
||||
if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) {
|
||||
signature.inputs.append({item.name, stype, nodes::StructureType(item.structure_type)});
|
||||
signature.inputs.add({item.name, stype, nodes::StructureType(item.structure_type)});
|
||||
}
|
||||
}
|
||||
for (const int i : IndexRange(storage.output_items.items_num)) {
|
||||
const NodeGeometryEvaluateClosureOutputItem &item = storage.output_items.items[i];
|
||||
if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) {
|
||||
signature.outputs.append({item.name, stype, nodes::StructureType(item.structure_type)});
|
||||
signature.outputs.add({item.name, stype, nodes::StructureType(item.structure_type)});
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
|
||||
@@ -125,11 +125,11 @@ class LazyFunctionForClosureZone : public LazyFunction {
|
||||
|
||||
for (const int i : IndexRange(storage.input_items.items_num)) {
|
||||
const bNodeSocket &bsocket = zone_.input_node()->output_socket(i);
|
||||
closure_signature_->inputs.append({bsocket.name, bsocket.typeinfo});
|
||||
closure_signature_->inputs.add({bsocket.name, bsocket.typeinfo});
|
||||
}
|
||||
for (const int i : IndexRange(storage.output_items.items_num)) {
|
||||
const bNodeSocket &bsocket = zone_.output_node()->input_socket(i);
|
||||
closure_signature_->outputs.append({bsocket.name, bsocket.typeinfo});
|
||||
closure_signature_->outputs.add({bsocket.name, bsocket.typeinfo});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user