Nodes: Use dynamic declarations for group nodes

Since a year and a half ago we've been switching to a new way to
represent what sockets a node should have called "declarations"
that's easier to use, clearer, and more flexible for upcoming
features like dynamic socket counts or generic type sockets.

All builtin nodes with a static set of sockets have switched, but one
missing area has been group nodes and group input/output nodes. These
nodes have **dynamic** declarations which change based on their
properties or the group they're inside of. This patch addresses that,
in preparation for using the same dynamic declaration feature for
simulation nodes.

Generally there shouldn't be user-visible differences, but one benefit
is that user-created socket descriptions are now visible directly in
the node editor for group nodes and group input/output nodes.

The commit contains a few changes:
- Add a node type callback for building dynamic declarations with
  different arguments
- Add an `Extend` socket declaration for the "virtual" sockets used
  for connecting new links
- A similar `Custom` socket declaration is used for addon-defined socket
- Simplify the node update loop to use the declaration to build update
  sockets
- Replace the "group update" functions with the declaration building
- Move the node group input/output link creation to link drag operator
- Make the field status part of group node declarations
  (not for group input/output nodes though)
- Some fixes for declarations to make them update and build properly

Differential Revision: https://developer.blender.org/D16850
This commit is contained in:
Hans Goudey 2023-01-16 15:47:10 -06:00
parent 6d12d43a05
commit 7026096099
Notes: blender-bot 2023-07-27 14:56:07 +02:00
Referenced by issue #104296, Regression: crash loading test file library_test_scene.blend
Referenced by issue #104278, Crash in geometrynode doversion code when opening Sprite production file
Referenced by issue #105314, Regression: ShaderNodeCustomGroup based nodes lost their UI
Referenced by commit 15f59470a3, Fix #105314: Custom node groups missing sockets
Referenced by pull request #105975, Add helper function to node_utils to create links to virtual sockets
Referenced by commit 81815681d0, Python API: Add function to create links to virtual sockets
Referenced by issue #109231, In custom trees socket type in group input nodes is not changing.
Referenced by pull request #109298, Fix #109231: Python defined socket type switching broken
Referenced by commit c9a8b1f13f, Fix #109231: Python defined socket type switching broken
Referenced by pull request #110225, Fix #110210: Hide Value option doesn't work on color sockets
Referenced by commit 6a03f3a575, Fix #110210: Hide Value option doesn't work on color sockets
22 changed files with 510 additions and 371 deletions

View File

@ -104,6 +104,7 @@ namespace nodes {
class DNode;
class NodeMultiFunctionBuilder;
class GeoNodeExecParams;
class NodeDeclaration;
class NodeDeclarationBuilder;
class GatherLinkSearchOpParams;
} // namespace nodes
@ -118,6 +119,9 @@ using CPPTypeHandle = blender::CPPType;
using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder);
using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params);
using NodeDeclareFunction = void (*)(blender::nodes::NodeDeclarationBuilder &builder);
using NodeDeclareDynamicFunction = void (*)(const bNodeTree &tree,
const bNode &node,
blender::nodes::NodeDeclaration &r_declaration);
using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value);
using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket,
void *r_value);
@ -137,6 +141,7 @@ typedef void *NodeGetCompositorShaderNodeFunction;
typedef void *NodeMultiFunctionBuildFunction;
typedef void *NodeGeometryExecFunction;
typedef void *NodeDeclareFunction;
typedef void *NodeDeclareDynamicFunction;
typedef void *NodeGatherSocketLinkOperationsFunction;
typedef void *SocketGetCPPTypeFunction;
typedef void *SocketGetGeometryNodesCPPTypeFunction;
@ -173,11 +178,6 @@ typedef struct bNodeSocketType {
struct bNode *node,
struct bNodeSocket *sock,
const char *data_path);
void (*interface_verify_socket)(struct bNodeTree *ntree,
const struct bNodeSocket *interface_socket,
struct bNode *node,
struct bNodeSocket *sock,
const char *data_path);
void (*interface_from_socket)(struct bNodeTree *ntree,
struct bNodeSocket *interface_socket,
const struct bNode *node,
@ -306,8 +306,8 @@ typedef struct bNodeType {
const struct bNodeTree *nodetree,
const char **r_disabled_hint);
/* optional handling of link insertion */
void (*insert_link)(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
/* optional handling of link insertion. Returns false if the link shouldn't be created. */
bool (*insert_link)(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
void (*free_self)(struct bNodeType *ntype);
@ -344,8 +344,13 @@ typedef struct bNodeType {
/* Declares which sockets the node has. */
NodeDeclareFunction declare;
/* Different nodes of this type can have different declarations. */
bool declaration_is_dynamic;
/**
* Declare which sockets the node has for declarations that aren't static per node type.
* In other words, defining this callback means that different nodes of this type can have
* different declarations and different sockets.
*/
NodeDeclareDynamicFunction declare_dynamic;
/* Declaration to be used when it is not dynamic. */
NodeDeclarationHandle *fixed_declaration;

View File

@ -1378,7 +1378,7 @@ void nodeRegisterType(bNodeType *nt)
BLI_assert(nt->idname[0] != '\0');
BLI_assert(nt->poll != nullptr);
if (nt->declare && !nt->declaration_is_dynamic) {
if (nt->declare && !nt->declare_dynamic) {
if (nt->fixed_declaration == nullptr) {
nt->fixed_declaration = new blender::nodes::NodeDeclaration();
blender::nodes::build_node_declaration(*nt, *nt->fixed_declaration);
@ -2990,7 +2990,7 @@ void node_free_node(bNodeTree *ntree, bNode *node)
MEM_freeN(node->prop);
}
if (node->typeinfo->declaration_is_dynamic) {
if (node->typeinfo->declare_dynamic) {
delete node->runtime->declaration;
}
@ -3602,7 +3602,7 @@ bool nodeDeclarationEnsureOnOutdatedNode(bNodeTree * /*ntree*/, bNode *node)
if (node->typeinfo->declare == nullptr) {
return false;
}
if (node->typeinfo->declaration_is_dynamic) {
if (node->typeinfo->declare_dynamic) {
node->runtime->declaration = new blender::nodes::NodeDeclaration();
blender::nodes::build_node_declaration(*node->typeinfo, *node->runtime->declaration);
}

View File

@ -22,6 +22,7 @@
#include "MOD_nodes.h"
#include "NOD_node_declaration.hh"
#include "NOD_socket.h"
#include "NOD_texture.h"
#include "DEG_depsgraph_query.h"
@ -538,7 +539,6 @@ class NodeTreeMainUpdater {
void update_individual_nodes(bNodeTree &ntree)
{
Vector<bNode *> group_inout_nodes;
for (bNode *node : ntree.all_nodes()) {
nodeDeclarationEnsure(&ntree, node);
if (this->should_update_individual_node(ntree, *node)) {
@ -549,18 +549,9 @@ class NodeTreeMainUpdater {
if (ntype.updatefunc) {
ntype.updatefunc(&ntree, node);
}
}
if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
group_inout_nodes.append(node);
}
}
/* The update function of group input/output nodes may add new interface sockets. When that
* happens, all the input/output nodes have to be updated again. In the future it would be
* better to move this functionality out of the node update function into the operator that's
* supposed to create the new interface socket. */
if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE) {
for (bNode *node : group_inout_nodes) {
node->typeinfo->updatefunc(&ntree, node);
if (ntype.declare_dynamic) {
nodes::update_node_declaration_and_sockets(ntree, *node);
}
}
}
}
@ -574,23 +565,8 @@ class NodeTreeMainUpdater {
return true;
}
if (ntree.runtime->changed_flag & NTREE_CHANGED_LINK) {
ntree.ensure_topology_cache();
/* Node groups currently always rebuilt their sockets when they are updated.
* So avoid calling the update method when no new link was added to it. */
if (node.type == NODE_GROUP_INPUT) {
if (node.output_sockets().last()->is_directly_linked()) {
return true;
}
}
else if (node.type == NODE_GROUP_OUTPUT) {
if (node.input_sockets().last()->is_directly_linked()) {
return true;
}
}
else {
/* Currently we have no way to tell if a node needs to be updated when a link changed. */
return true;
}
/* Currently we have no way to tell if a node needs to be updated when a link changed. */
return true;
}
if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE) {
if (ELEM(node.type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {

View File

@ -13,6 +13,7 @@
#include "BKE_node_tree_update.h"
#include "BKE_screen.h"
#include "NOD_socket.h"
#include "NOD_socket_search_link.hh"
#include "BLT_translation.h"
@ -198,7 +199,7 @@ static void search_link_ops_for_asset_metadata(const bNodeTree &node_tree,
DEG_relations_tag_update(&bmain);
/* Create the inputs and outputs on the new node. */
node.typeinfo->group_update_func(&params.node_tree, &node);
nodes::update_node_declaration_and_sockets(params.node_tree, node);
bNodeSocket *new_node_socket = bke::node_find_enabled_socket(
node, in_out, socket_property->name);

View File

@ -933,9 +933,9 @@ static void node_group_make_insert_selected(const bContext &C,
}
nodeRebuildIDVector(&ntree);
node_group_update(&ntree, gnode);
node_group_input_update(&group, input_node);
node_group_output_update(&group, output_node);
/* Update input and output node first, since the group node declaration can depend on them. */
nodes::update_node_declaration_and_sockets(group, *input_node);
nodes::update_node_declaration_and_sockets(group, *output_node);
/* move nodes in the group to the center */
for (bNode *node : nodes_to_move) {
@ -956,6 +956,7 @@ static void node_group_make_insert_selected(const bContext &C,
nodeRemLink(&ntree, link);
}
/* Handle links to the new group inputs. */
for (const auto item : input_links.items()) {
const char *interface_identifier = item.value.interface_socket->identifier;
bNodeSocket *input_socket = node_group_input_find_socket(input_node, interface_identifier);
@ -969,23 +970,17 @@ static void node_group_make_insert_selected(const bContext &C,
link->fromnode = input_node;
link->fromsock = input_socket;
}
/* Add a new link outside of the group. */
bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, interface_identifier);
nodeAddLink(&ntree, item.value.from_node, item.key, gnode, group_node_socket);
}
/* Handle links to new group outputs. */
for (const OutputLinkInfo &info : output_links) {
/* Create a new link inside of the group. */
const char *io_identifier = info.interface_socket->identifier;
bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier);
nodeAddLink(&group, info.link->fromnode, info.link->fromsock, output_node, output_sock);
/* Reconnect the link to the group node instead of the node now inside the group. */
info.link->fromnode = gnode;
info.link->fromsock = node_group_find_output_socket(gnode, io_identifier);
}
/* Handle new links inside the group. */
for (const NewInternalLinkInfo &info : new_internal_links) {
const char *io_identifier = info.interface_socket->identifier;
if (info.socket->in_out == SOCK_IN) {
@ -997,6 +992,25 @@ static void node_group_make_insert_selected(const bContext &C,
nodeAddLink(&group, info.node, info.socket, output_node, output_socket);
}
}
bke::node_field_inferencing::update_field_inferencing(group);
nodes::update_node_declaration_and_sockets(ntree, *gnode);
/* Add new links to inputs outside of the group. */
for (const auto item : input_links.items()) {
const char *interface_identifier = item.value.interface_socket->identifier;
bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, interface_identifier);
nodeAddLink(&ntree, item.value.from_node, item.key, gnode, group_node_socket);
}
/* Add new links to outputs outside the group. */
for (const OutputLinkInfo &info : output_links) {
/* Reconnect the link to the group node instead of the node now inside the group. */
info.link->fromnode = gnode;
info.link->fromsock = node_group_find_output_socket(gnode, info.interface_socket->identifier);
}
ED_node_tree_propagate_change(&C, bmain, nullptr);
}
static bNode *node_group_make_from_nodes(const bContext &C,
@ -1051,8 +1065,6 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
}
}
ED_node_tree_propagate_change(C, bmain, nullptr);
WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr);
/* We broke relations in node tree, need to rebuild them in the graphs. */
@ -1105,7 +1117,6 @@ static int node_group_insert_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
const char *node_idname = node_group_idname(C);
Main *bmain = CTX_data_main(C);
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
@ -1137,7 +1148,6 @@ static int node_group_insert_exec(bContext *C, wmOperator *op)
nodeSetActive(ntree, gnode);
ED_node_tree_push(snode, ngroup, gnode);
ED_node_tree_propagate_change(C, bmain, nullptr);
return OPERATOR_FINISHED;
}

View File

@ -902,10 +902,14 @@ static void add_dragged_links_to_tree(bContext &C, bNodeLinkDrag &nldrag)
/* Before actually adding the link let nodes perform special link insertion handling. */
bNodeLink *new_link = MEM_new<bNodeLink>(__func__, link);
if (link.fromnode->typeinfo->insert_link) {
link.fromnode->typeinfo->insert_link(&ntree, link.fromnode, new_link);
if (!link.fromnode->typeinfo->insert_link(&ntree, link.fromnode, new_link)) {
continue;
}
}
if (link.tonode->typeinfo->insert_link) {
link.tonode->typeinfo->insert_link(&ntree, link.tonode, new_link);
if (!link.tonode->typeinfo->insert_link(&ntree, link.tonode, new_link)) {
continue;
}
}
/* Add link to the node tree. */

View File

@ -1738,7 +1738,7 @@ static void rna_Node_update_reg(bNodeTree *ntree, bNode *node)
RNA_parameter_list_free(&list);
}
static void rna_Node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
static bool rna_Node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
extern FunctionRNA rna_Node_insert_link_func;
@ -1754,6 +1754,7 @@ static void rna_Node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
node->typeinfo->rna_ext.call(NULL, &ptr, func, &list);
RNA_parameter_list_free(&list);
return true;
}
static void rna_Node_init(const bContext *C, PointerRNA *ptr)
@ -3386,9 +3387,6 @@ static StructRNA *rna_NodeCustomGroup_register(Main *bmain,
return NULL;
}
/* this updates the group node instance from the tree's interface */
nt->group_update_func = node_group_update;
nodeRegisterType(nt);
/* update while blender is running */
@ -3412,7 +3410,6 @@ static StructRNA *rna_GeometryNodeCustomGroup_register(Main *bmain,
return NULL;
}
nt->group_update_func = node_group_update;
nt->type = NODE_CUSTOM_GROUP;
register_node_type_geo_custom_group(nt);
@ -3441,7 +3438,6 @@ static StructRNA *rna_ShaderNodeCustomGroup_register(Main *bmain,
return NULL;
}
nt->group_update_func = node_group_update;
nt->type = NODE_CUSTOM_GROUP;
register_node_type_sh_custom_group(nt);
@ -3467,7 +3463,6 @@ static StructRNA *rna_CompositorNodeCustomGroup_register(Main *bmain,
return NULL;
}
nt->group_update_func = node_group_update;
nt->type = NODE_CUSTOM_GROUP;
register_node_type_cmp_custom_group(nt);

View File

@ -17,16 +17,24 @@ extern "C" {
struct bNodeSocket *node_group_find_input_socket(struct bNode *groupnode, const char *identifier);
struct bNodeSocket *node_group_find_output_socket(struct bNode *groupnode, const char *identifier);
/** Make sure all group node in ntree, which use ngroup, are sync'd. */
void node_group_update(struct bNodeTree *ntree, struct bNode *node);
struct bNodeSocket *node_group_input_find_socket(struct bNode *node, const char *identifier);
struct bNodeSocket *node_group_output_find_socket(struct bNode *node, const char *identifier);
void node_group_input_update(struct bNodeTree *ntree, struct bNode *node);
void node_group_output_update(struct bNodeTree *ntree, struct bNode *node);
void node_internal_links_create(struct bNodeTree *ntree, struct bNode *node);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
namespace blender::nodes {
void node_group_declare_dynamic(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration);
} // namespace blender::nodes
#endif

View File

@ -160,6 +160,7 @@ class SocketDeclaration {
InputSocketFieldType input_field_type = InputSocketFieldType::None;
OutputFieldDependency output_field_dependency;
private:
/** The priority of the input for determining the domain of the node. See
* realtime_compositor::InputDescriptor for more information. */
int compositor_domain_priority_ = 0;
@ -461,6 +462,11 @@ class NodeDeclaration {
Vector<SocketDeclarationPtr> outputs;
std::unique_ptr<aal::RelationsInNode> anonymous_attribute_relations_;
/** Leave the sockets in place, even if they don't match the declaration. Used for dynamic
* declarations when the information used to build the declaration is missing, but might become
* available again in the future. */
bool skip_updating_sockets = false;
friend NodeDeclarationBuilder;
bool matches(const bNode &node) const;
@ -523,6 +529,9 @@ void id_or_index(const bNode &node, void *r_value);
} // namespace implicit_field_inputs
void build_node_declaration(const bNodeType &typeinfo, NodeDeclaration &r_declaration);
void build_node_declaration_dynamic(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration);
template<typename SocketDecl>
typename SocketDeclarationBuilder<SocketDecl>::Self &SocketDeclarationBuilder<

View File

@ -36,3 +36,13 @@ void register_standard_node_socket_types(void);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
namespace blender::nodes {
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node);
} // namespace blender::nodes
#endif

View File

@ -104,6 +104,7 @@ class Bool : public SocketDeclaration {
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
bool matches(const bNodeSocket &socket) const override;
bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override;
bool can_connect(const bNodeSocket &socket) const override;
};
@ -124,6 +125,7 @@ class Color : public SocketDeclaration {
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
bool matches(const bNodeSocket &socket) const override;
bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override;
bool can_connect(const bNodeSocket &socket) const override;
};
@ -144,6 +146,7 @@ class String : public SocketDeclaration {
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
bool matches(const bNodeSocket &socket) const override;
bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override;
bool can_connect(const bNodeSocket &socket) const override;
};
@ -216,6 +219,34 @@ class Shader : public SocketDeclaration {
class ShaderBuilder : public SocketDeclarationBuilder<Shader> {
};
class ExtendBuilder;
class Extend : public SocketDeclaration {
private:
friend ExtendBuilder;
public:
using Builder = ExtendBuilder;
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
bool matches(const bNodeSocket &socket) const override;
bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override;
bool can_connect(const bNodeSocket &socket) const override;
};
class ExtendBuilder : public SocketDeclarationBuilder<Extend> {
};
class Custom : public SocketDeclaration {
public:
const char *idname_;
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
bool matches(const bNodeSocket &socket) const override;
bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override;
bool can_connect(const bNodeSocket &socket) const override;
};
/* -------------------------------------------------------------------- */
/** \name #FloatBuilder Inline Methods
* \{ */

View File

@ -32,7 +32,7 @@ void register_node_type_cmp_group()
node_type_size(&ntype, 140, 60, 400);
ntype.labelfunc = node_group_label;
ntype.group_update_func = node_group_update;
ntype.declare_dynamic = blender::nodes::node_group_declare_dynamic;
nodeRegisterType(&ntype);
}

View File

@ -3,11 +3,41 @@
#include "BKE_node.h"
#include "NOD_geometry.h"
#include "NOD_node_declaration.hh"
#include "NOD_common.h"
#include "node_common.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void node_declare(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration)
{
const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node.id);
if (!group) {
return;
}
node_group_declare_dynamic(node_tree, node, r_declaration);
if (!node.id) {
return;
}
if (ID_IS_LINKED(&group->id) && (group->id.tag & LIB_TAG_MISSING)) {
return;
}
const FieldInferencingInterface &field_interface = *group->runtime->field_inferencing_interface;
for (const int i : r_declaration.inputs.index_range()) {
r_declaration.inputs[i]->input_field_type = field_interface.inputs[i];
}
for (const int i : r_declaration.outputs.index_range()) {
r_declaration.outputs[i]->output_field_dependency = field_interface.outputs[i];
}
}
} // namespace blender::nodes
void register_node_type_geo_group()
{
static bNodeType ntype;
@ -23,7 +53,7 @@ void register_node_type_geo_group()
node_type_size(&ntype, 140, 60, 400);
ntype.labelfunc = node_group_label;
ntype.group_update_func = node_group_update;
ntype.declare_dynamic = blender::nodes::node_declare;
nodeRegisterType(&ntype);
}

View File

@ -30,7 +30,11 @@
#include "MEM_guardedalloc.h"
#include "NOD_common.h"
#include "NOD_node_declaration.hh"
#include "NOD_register.hh"
#include "NOD_socket.h"
#include "NOD_socket_declarations.hh"
#include "NOD_socket_declarations_geometry.hh"
#include "node_common.h"
#include "node_util.h"
@ -120,124 +124,124 @@ bool nodeGroupPoll(const bNodeTree *nodetree,
return true;
}
static void add_new_socket_from_interface(bNodeTree &node_tree,
bNode &node,
const bNodeSocket &interface_socket,
const eNodeSocketInOut in_out)
{
bNodeSocket *socket = nodeAddSocket(&node_tree,
&node,
in_out,
interface_socket.idname,
interface_socket.identifier,
interface_socket.name);
namespace blender::nodes {
if (interface_socket.typeinfo->interface_init_socket) {
interface_socket.typeinfo->interface_init_socket(
&node_tree, &interface_socket, &node, socket, "interface");
static SocketDeclarationPtr declaration_for_interface_socket(const bNodeSocket &io_socket)
{
SocketDeclarationPtr dst;
switch (io_socket.type) {
case SOCK_FLOAT: {
const auto &value = *io_socket.default_value_typed<bNodeSocketValueFloat>();
std::unique_ptr<decl::Float> decl = std::make_unique<decl::Float>();
decl->subtype = PropertySubType(io_socket.typeinfo->subtype);
decl->default_value = value.value;
decl->soft_min_value = value.min;
decl->soft_max_value = value.max;
dst = std::move(decl);
break;
}
case SOCK_VECTOR: {
const auto &value = *io_socket.default_value_typed<bNodeSocketValueVector>();
std::unique_ptr<decl::Vector> decl = std::make_unique<decl::Vector>();
decl->subtype = PropertySubType(io_socket.typeinfo->subtype);
decl->default_value = value.value;
decl->soft_min_value = value.min;
decl->soft_max_value = value.max;
dst = std::move(decl);
break;
}
case SOCK_RGBA: {
const auto &value = *io_socket.default_value_typed<bNodeSocketValueRGBA>();
std::unique_ptr<decl::Color> decl = std::make_unique<decl::Color>();
decl->default_value = value.value;
dst = std::move(decl);
break;
}
case SOCK_SHADER: {
std::unique_ptr<decl::Shader> decl = std::make_unique<decl::Shader>();
dst = std::move(decl);
break;
}
case SOCK_BOOLEAN: {
const auto &value = *io_socket.default_value_typed<bNodeSocketValueBoolean>();
std::unique_ptr<decl::Bool> decl = std::make_unique<decl::Bool>();
decl->default_value = value.value;
dst = std::move(decl);
break;
}
case SOCK_INT: {
const auto &value = *io_socket.default_value_typed<bNodeSocketValueInt>();
std::unique_ptr<decl::Int> decl = std::make_unique<decl::Int>();
decl->subtype = PropertySubType(io_socket.typeinfo->subtype);
decl->default_value = value.value;
decl->soft_min_value = value.min;
decl->soft_max_value = value.max;
dst = std::move(decl);
break;
}
case SOCK_STRING: {
const auto &value = *io_socket.default_value_typed<bNodeSocketValueString>();
std::unique_ptr<decl::String> decl = std::make_unique<decl::String>();
decl->default_value = value.value;
dst = std::move(decl);
break;
}
case SOCK_OBJECT:
dst = std::make_unique<decl::Object>();
break;
case SOCK_IMAGE:
dst = std::make_unique<decl::Image>();
break;
case SOCK_GEOMETRY:
dst = std::make_unique<decl::Geometry>();
break;
case SOCK_COLLECTION:
dst = std::make_unique<decl::Collection>();
break;
case SOCK_TEXTURE:
dst = std::make_unique<decl::Texture>();
break;
case SOCK_MATERIAL:
dst = std::make_unique<decl::Material>();
break;
case SOCK_CUSTOM:
std::unique_ptr<decl::Custom> decl = std::make_unique<decl::Custom>();
decl->idname_ = io_socket.idname;
dst = std::move(decl);
break;
}
dst->name = io_socket.name;
dst->identifier = io_socket.identifier;
dst->in_out = eNodeSocketInOut(io_socket.in_out);
dst->description = io_socket.description;
dst->hide_value = io_socket.flag & SOCK_HIDE_VALUE;
dst->compact = io_socket.flag & SOCK_COMPACT;
return dst;
}
void node_group_declare_dynamic(const bNodeTree & /*node_tree*/,
const bNode &node,
NodeDeclaration &r_declaration)
{
const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node.id);
if (!group) {
return;
}
if (ID_IS_LINKED(&group->id) && (group->id.tag & LIB_TAG_MISSING)) {
r_declaration.skip_updating_sockets = true;
return;
}
r_declaration.skip_updating_sockets = false;
LISTBASE_FOREACH (const bNodeSocket *, input, &group->inputs) {
r_declaration.inputs.append(declaration_for_interface_socket(*input));
}
LISTBASE_FOREACH (const bNodeSocket *, output, &group->outputs) {
r_declaration.outputs.append(declaration_for_interface_socket(*output));
}
}
static void update_socket_to_match_interface(bNodeTree &node_tree,
bNode &node,
bNodeSocket &socket_to_update,
const bNodeSocket &interface_socket)
{
strcpy(socket_to_update.name, interface_socket.name);
const int mask = SOCK_HIDE_VALUE;
socket_to_update.flag = (socket_to_update.flag & ~mask) | (interface_socket.flag & mask);
/* Update socket type if necessary */
if (socket_to_update.typeinfo != interface_socket.typeinfo) {
nodeModifySocketType(&node_tree, &node, &socket_to_update, interface_socket.idname);
}
if (interface_socket.typeinfo->interface_verify_socket) {
interface_socket.typeinfo->interface_verify_socket(
&node_tree, &interface_socket, &node, &socket_to_update, "interface");
}
}
/**
* Used for group nodes and group input/output nodes to update the list of input or output sockets
* on a node to match the provided interface. Assumes that \a verify_lb is the node's matching
* input or output socket list, depending on whether the node is a group input/output or a group
* node.
*/
static void group_verify_socket_list(bNodeTree &node_tree,
bNode &node,
const ListBase &interface_sockets,
ListBase &verify_lb,
const eNodeSocketInOut in_out,
const bool ensure_extend_socket_exists)
{
ListBase old_sockets = verify_lb;
Vector<bNodeSocket *> ordered_old_sockets = old_sockets;
BLI_listbase_clear(&verify_lb);
LISTBASE_FOREACH (const bNodeSocket *, interface_socket, &interface_sockets) {
bNodeSocket *matching_socket = find_matching_socket(old_sockets, interface_socket->identifier);
if (matching_socket) {
/* If a socket with the same identifier exists in the previous socket list, update it
* with the correct name, type, etc. Then move it from the old list to the new one. */
update_socket_to_match_interface(node_tree, node, *matching_socket, *interface_socket);
BLI_remlink(&old_sockets, matching_socket);
BLI_addtail(&verify_lb, matching_socket);
}
else {
/* If there was no socket with the same identifier already, simply create a new socket
* based on the interface socket, which will already add it to the new list. */
add_new_socket_from_interface(node_tree, node, *interface_socket, in_out);
}
}
if (ensure_extend_socket_exists) {
bNodeSocket *last_socket = static_cast<bNodeSocket *>(old_sockets.last);
if (last_socket != nullptr && STREQ(last_socket->identifier, "__extend__")) {
BLI_remlink(&old_sockets, last_socket);
BLI_addtail(&verify_lb, last_socket);
}
else {
nodeAddSocket(&node_tree, &node, in_out, "NodeSocketVirtual", "__extend__", "");
}
}
/* Remove leftover sockets that didn't match the node group's interface. */
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, unused_socket, &old_sockets) {
nodeRemoveSocket(&node_tree, &node, unused_socket);
}
{
/* Check if new sockets match the old sockets. */
int index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, new_socket, &verify_lb, index) {
if (index < ordered_old_sockets.size()) {
if (ordered_old_sockets[index] != new_socket) {
BKE_ntree_update_tag_interface(&node_tree);
break;
}
}
}
}
}
void node_group_update(struct bNodeTree *ntree, struct bNode *node)
{
/* check inputs and outputs, and remove or insert them */
if (node->id == nullptr) {
nodeRemoveAllSockets(ntree, node);
}
else if (ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING)) {
/* Missing data-block, leave sockets unchanged so that when it comes back
* the links remain valid. */
}
else {
bNodeTree *ngroup = (bNodeTree *)node->id;
group_verify_socket_list(*ntree, *node, ngroup->inputs, node->inputs, SOCK_IN, false);
group_verify_socket_list(*ntree, *node, ngroup->outputs, node->outputs, SOCK_OUT, false);
}
}
} // namespace blender::nodes
/** \} */
@ -419,16 +423,6 @@ bool BKE_node_is_connected_to_output(const bNodeTree *ntree, const bNode *node)
/** \name Node #GROUP_INPUT / #GROUP_OUTPUT
* \{ */
static bool is_group_extension_socket(const bNode *node, const bNodeSocket *socket)
{
return socket->type == SOCK_CUSTOM && ELEM(node->type, NODE_GROUP_OUTPUT, NODE_GROUP_INPUT);
}
static void node_group_input_init(bNodeTree *ntree, bNode *node)
{
node_group_input_update(ntree, node);
}
bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier)
{
bNodeSocket *sock;
@ -440,59 +434,83 @@ bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier)
return nullptr;
}
void node_group_input_update(bNodeTree *ntree, bNode *node)
namespace blender::nodes {
static SocketDeclarationPtr extend_declaration(const eNodeSocketInOut in_out)
{
bNodeSocket *extsock = (bNodeSocket *)node->outputs.last;
/* Adding a tree socket and verifying will remove the extension socket!
* This list caches the existing links from the extension socket
* so they can be recreated after verification. */
Vector<bNodeLink> temp_links;
/* find links from the extension socket and store them */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if (nodeLinkIsHidden(link)) {
continue;
}
if (link->fromsock == extsock) {
temp_links.append(*link);
nodeRemLink(ntree, link);
}
}
/* find valid link to expose */
bNodeLink *exposelink = nullptr;
for (bNodeLink &link : temp_links) {
/* XXX Multiple sockets can be connected to the extension socket at once,
* in that case the arbitrary first link determines name and type.
* This could be improved by choosing the "best" type among all links,
* whatever that means.
*/
if (!is_group_extension_socket(link.tonode, link.tosock)) {
exposelink = &link;
break;
}
}
if (exposelink) {
bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(
ntree, exposelink->tonode, exposelink->tosock);
node_group_input_update(ntree, node);
bNodeSocket *newsock = node_group_input_find_socket(node, gsock->identifier);
/* redirect links from the extension socket */
for (bNodeLink &link : temp_links) {
bNodeLink *newlink = nodeAddLink(ntree, node, newsock, link.tonode, link.tosock);
if (newlink->tosock->flag & SOCK_MULTI_INPUT) {
newlink->multi_input_socket_index = link.multi_input_socket_index;
}
}
}
group_verify_socket_list(*ntree, *node, ntree->inputs, node->outputs, SOCK_OUT, true);
std::unique_ptr<decl::Extend> decl = std::make_unique<decl::Extend>();
decl->name = "";
decl->identifier = "__extend__";
decl->in_out = in_out;
return decl;
}
static void group_input_declare_dynamic(const bNodeTree &node_tree,
const bNode & /*node*/,
NodeDeclaration &r_declaration)
{
LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.inputs) {
r_declaration.outputs.append(declaration_for_interface_socket(*input));
r_declaration.outputs.last()->in_out = SOCK_OUT;
}
r_declaration.outputs.append(extend_declaration(SOCK_OUT));
}
static void group_output_declare_dynamic(const bNodeTree &node_tree,
const bNode & /*node*/,
NodeDeclaration &r_declaration)
{
LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.outputs) {
r_declaration.inputs.append(declaration_for_interface_socket(*input));
r_declaration.inputs.last()->in_out = SOCK_IN;
}
r_declaration.inputs.append(extend_declaration(SOCK_IN));
}
static bool group_input_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
BLI_assert(link->tonode != node);
BLI_assert(link->tosock->in_out == SOCK_IN);
if (link->fromsock->identifier != StringRef("__extend__")) {
return true;
}
if (link->tosock->identifier == StringRef("__extend__")) {
/* Don't connect to other "extend" sockets. */
return false;
}
const bNodeSocket *io_socket = ntreeAddSocketInterfaceFromSocket(
ntree, link->tonode, link->tosock);
if (!io_socket) {
return false;
}
update_node_declaration_and_sockets(*ntree, *node);
link->fromsock = node_group_input_find_socket(node, io_socket->identifier);
return true;
}
static bool group_output_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
BLI_assert(link->fromnode != node);
BLI_assert(link->fromsock->in_out == SOCK_OUT);
if (link->tosock->identifier != StringRef("__extend__")) {
return true;
}
if (link->fromsock->identifier == StringRef("__extend__")) {
/* Don't connect to other "extend" sockets. */
return false;
}
const bNodeSocket *io_socket = ntreeAddSocketInterfaceFromSocket(
ntree, link->fromnode, link->fromsock);
if (!io_socket) {
return false;
}
update_node_declaration_and_sockets(*ntree, *node);
link->tosock = node_group_output_find_socket(node, io_socket->identifier);
return true;
}
} // namespace blender::nodes
void register_node_type_group_input()
{
/* used for all tree types, needs dynamic allocation */
@ -501,17 +519,12 @@ void register_node_type_group_input()
node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE);
node_type_size(ntype, 140, 80, 400);
ntype->initfunc = node_group_input_init;
ntype->updatefunc = node_group_input_update;
ntype->declare_dynamic = blender::nodes::group_input_declare_dynamic;
ntype->insert_link = blender::nodes::group_input_insert_link;
nodeRegisterType(ntype);
}
static void node_group_output_init(bNodeTree *ntree, bNode *node)
{
node_group_output_update(ntree, node);
}
bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier)
{
bNodeSocket *sock;
@ -523,57 +536,6 @@ bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier)
return nullptr;
}
void node_group_output_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *extsock = (bNodeSocket *)node->inputs.last;
/* Adding a tree socket and verifying will remove the extension socket!
* This list caches the existing links to the extension socket
* so they can be recreated after verification. */
Vector<bNodeLink> temp_links;
/* find links to the extension socket and store them */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if (nodeLinkIsHidden(link)) {
continue;
}
if (link->tosock == extsock) {
temp_links.append(*link);
nodeRemLink(ntree, link);
}
}
/* find valid link to expose */
bNodeLink *exposelink = nullptr;
for (bNodeLink &link : temp_links) {
/* XXX Multiple sockets can be connected to the extension socket at once,
* in that case the arbitrary first link determines name and type.
* This could be improved by choosing the "best" type among all links,
* whatever that means.
*/
if (!is_group_extension_socket(link.fromnode, link.fromsock)) {
exposelink = &link;
break;
}
}
if (exposelink) {
/* XXX what if connecting virtual to virtual socket?? */
bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(
ntree, exposelink->fromnode, exposelink->fromsock);
node_group_output_update(ntree, node);
bNodeSocket *newsock = node_group_output_find_socket(node, gsock->identifier);
/* redirect links to the extension socket */
for (bNodeLink &link : temp_links) {
nodeAddLink(ntree, link.fromnode, link.fromsock, node, newsock);
}
}
group_verify_socket_list(*ntree, *node, ntree->outputs, node->inputs, SOCK_IN, true);
}
void register_node_type_group_output()
{
/* used for all tree types, needs dynamic allocation */
@ -582,8 +544,8 @@ void register_node_type_group_output()
node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE);
node_type_size(ntype, 140, 80, 400);
ntype->initfunc = node_group_output_init;
ntype->updatefunc = node_group_output_update;
ntype->declare_dynamic = blender::nodes::group_output_declare_dynamic;
ntype->insert_link = blender::nodes::group_output_insert_link;
ntype->no_muting = true;

View File

@ -16,6 +16,15 @@ void build_node_declaration(const bNodeType &typeinfo, NodeDeclaration &r_declar
node_decl_builder.finalize();
}
void build_node_declaration_dynamic(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration)
{
r_declaration.inputs.clear();
r_declaration.outputs.clear();
node.typeinfo->declare_dynamic(node_tree, node, r_declaration);
}
void NodeDeclarationBuilder::finalize()
{
if (is_function_node_) {

View File

@ -19,6 +19,7 @@
#include "BKE_lib_id.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "DNA_collection_types.h"
#include "DNA_material_types.h"
@ -171,6 +172,8 @@ static void verify_socket_template_list(bNodeTree *ntree,
}
}
namespace blender::nodes {
static void refresh_socket_list(bNodeTree &ntree,
bNode &node,
ListBase &sockets,
@ -232,6 +235,7 @@ static void refresh_socket_list(bNodeTree &ntree,
}
}
new_sockets.add_new(new_socket);
BKE_ntree_update_tag_socket_new(&ntree, new_socket);
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &sockets) {
if (!new_sockets.contains(old_socket)) {
@ -249,10 +253,29 @@ static void refresh_node(bNodeTree &ntree,
blender::nodes::NodeDeclaration &node_decl,
bool do_id_user)
{
refresh_socket_list(ntree, node, node.inputs, node_decl.inputs, do_id_user);
refresh_socket_list(ntree, node, node.outputs, node_decl.outputs, do_id_user);
if (node_decl.skip_updating_sockets) {
return;
}
if (!node_decl.matches(node)) {
refresh_socket_list(ntree, node, node.inputs, node_decl.inputs, do_id_user);
refresh_socket_list(ntree, node, node.outputs, node_decl.outputs, do_id_user);
}
nodeSocketDeclarationsUpdate(&node);
}
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node)
{
if (node.typeinfo->declare_dynamic) {
if (!node.runtime->declaration) {
node.runtime->declaration = new NodeDeclaration();
}
build_node_declaration_dynamic(ntree, node, *node.runtime->declaration);
}
refresh_node(ntree, node, *node.runtime->declaration, true);
}
} // namespace blender::nodes
void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user)
{
bNodeType *ntype = node->typeinfo;
@ -261,10 +284,7 @@ void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user)
}
if (ntype->declare != nullptr) {
nodeDeclarationEnsureOnOutdatedNode(ntree, node);
if (!node->runtime->declaration->matches(*node)) {
refresh_node(*ntree, *node, *node->runtime->declaration, do_id_user);
}
nodeSocketDeclarationsUpdate(node);
refresh_node(*ntree, *node, *node->runtime->declaration, do_id_user);
return;
}
/* Don't try to match socket lists when there are no templates.
@ -499,52 +519,6 @@ static void standard_node_socket_interface_init_socket(bNodeTree * /*ntree*/,
node_socket_copy_default_value(sock, interface_socket);
}
/* copies settings that are not changed for each socket instance */
static void standard_node_socket_interface_verify_socket(bNodeTree * /*ntree*/,
const bNodeSocket *interface_socket,
bNode * /*node*/,
bNodeSocket *sock,
const char * /*data_path*/)
{
/* sanity check */
if (sock->type != interface_socket->typeinfo->type) {
return;
}
/* make sure both exist */
if (!interface_socket->default_value) {
return;
}
node_socket_init_default_value(sock);
switch (interface_socket->typeinfo->type) {
case SOCK_FLOAT: {
bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)sock->default_value;
const bNodeSocketValueFloat *fromval = (const bNodeSocketValueFloat *)
interface_socket->default_value;
toval->min = fromval->min;
toval->max = fromval->max;
break;
}
case SOCK_INT: {
bNodeSocketValueInt *toval = (bNodeSocketValueInt *)sock->default_value;
const bNodeSocketValueInt *fromval = (const bNodeSocketValueInt *)
interface_socket->default_value;
toval->min = fromval->min;
toval->max = fromval->max;
break;
}
case SOCK_VECTOR: {
bNodeSocketValueVector *toval = (bNodeSocketValueVector *)sock->default_value;
const bNodeSocketValueVector *fromval = (const bNodeSocketValueVector *)
interface_socket->default_value;
toval->min = fromval->min;
toval->max = fromval->max;
break;
}
}
}
static void standard_node_socket_interface_from_socket(bNodeTree * /*ntree*/,
bNodeSocket *stemp,
const bNode * /*node*/,
@ -592,7 +566,6 @@ static bNodeSocketType *make_standard_socket_type(int type, int subtype)
stype->interface_init_socket = standard_node_socket_interface_init_socket;
stype->interface_from_socket = standard_node_socket_interface_from_socket;
stype->interface_verify_socket = standard_node_socket_interface_verify_socket;
stype->use_link_limits_of_type = true;
stype->input_link_limit = 1;

View File

@ -234,6 +234,14 @@ bool Vector::matches(const bNodeSocket &socket) const
if (socket.typeinfo->subtype != this->subtype) {
return false;
}
const bNodeSocketValueVector &value = *static_cast<const bNodeSocketValueVector *>(
socket.default_value);
if (value.min != this->soft_min_value) {
return false;
}
if (value.max != this->soft_max_value) {
return false;
}
return true;
}
@ -257,6 +265,8 @@ bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket
this->set_common_flags(socket);
bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value;
value.subtype = this->subtype;
value.min = this->soft_min_value;
value.max = this->soft_max_value;
STRNCPY(socket.name, this->name.c_str());
return socket;
}
@ -301,6 +311,17 @@ bool Bool::can_connect(const bNodeSocket &socket) const
return basic_types_can_connect(*this, socket);
}
bNodeSocket &Bool::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const
{
if (socket.type != SOCK_BOOLEAN) {
BLI_assert(socket.in_out == this->in_out);
return this->build(ntree, node);
}
this->set_common_flags(socket);
STRNCPY(socket.name, this->name.c_str());
return socket;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -346,6 +367,17 @@ bool Color::can_connect(const bNodeSocket &socket) const
return basic_types_can_connect(*this, socket);
}
bNodeSocket &Color::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const
{
if (socket.type != SOCK_RGBA) {
BLI_assert(socket.in_out == this->in_out);
return this->build(ntree, node);
}
this->set_common_flags(socket);
STRNCPY(socket.name, this->name.c_str());
return socket;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -382,6 +414,17 @@ bool String::can_connect(const bNodeSocket &socket) const
return sockets_can_connect(*this, socket) && socket.type == SOCK_STRING;
}
bNodeSocket &String::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const
{
if (socket.type != SOCK_STRING) {
BLI_assert(socket.in_out == this->in_out);
return this->build(ntree, node);
}
this->set_common_flags(socket);
STRNCPY(socket.name, this->name.c_str());
return socket;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -542,4 +585,77 @@ bool Shader::can_connect(const bNodeSocket &socket) const
/** \} */
/* -------------------------------------------------------------------- */
/** \name #Extend
* \{ */
bNodeSocket &Extend::build(bNodeTree &ntree, bNode &node) const
{
bNodeSocket &socket = *nodeAddSocket(&ntree,
&node,
this->in_out,
"NodeSocketVirtual",
this->identifier.c_str(),
this->name.c_str());
return socket;
}
bool Extend::matches(const bNodeSocket &socket) const
{
if (socket.identifier != this->identifier) {
return false;
}
return true;
}
bool Extend::can_connect(const bNodeSocket & /*socket*/) const
{
return false;
}
bNodeSocket &Extend::update_or_build(bNodeTree & /*ntree*/,
bNode & /*node*/,
bNodeSocket &socket) const
{
return socket;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #Custom
* \{ */
bNodeSocket &Custom::build(bNodeTree &ntree, bNode &node) const
{
bNodeSocket &socket = *nodeAddSocket(
&ntree, &node, this->in_out, idname_, this->identifier.c_str(), this->name.c_str());
return socket;
}
bool Custom::matches(const bNodeSocket &socket) const
{
if (!this->matches_common_data(socket)) {
return false;
}
if (socket.type != SOCK_CUSTOM) {
return false;
}
return true;
}
bool Custom::can_connect(const bNodeSocket &socket) const
{
return sockets_can_connect(*this, socket) && STREQ(socket.idname, idname_);
}
bNodeSocket &Custom::update_or_build(bNodeTree & /*ntree*/,
bNode & /*node*/,
bNodeSocket &socket) const
{
return socket;
}
/** \} */
} // namespace blender::nodes::decl

View File

@ -324,19 +324,19 @@ static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree,
return nullptr;
}
void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
bool node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
bNodeSocket *socket = link->tosock;
if (node != link->tonode) {
return;
return true;
}
/* If we're not at the link limit of the target socket, we can skip
* trying to move existing links to another socket. */
const int to_link_limit = nodeSocketLinkLimit(socket);
if (socket->runtime->total_inputs + 1 < to_link_limit) {
return;
return true;
}
LISTBASE_FOREACH_MUTABLE (bNodeLink *, to_link, &ntree->links) {
@ -345,16 +345,17 @@ void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
if (new_socket && new_socket != socket) {
/* Attempt to redirect the existing link to the new socket. */
to_link->tosock = new_socket;
return;
return true;
}
if (new_socket == nullptr) {
/* No possible replacement, remove the existing link. */
nodeRemLink(ntree, to_link);
return;
return true;
}
}
}
return true;
}
/** \} */

View File

@ -74,7 +74,7 @@ void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode
* already linked (and if its not an Multi Input Socket), we try to find a replacement socket for
* the link that we try to overwrite and connect that previous link to the new socket.
*/
void node_insert_link_default(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
bool node_insert_link_default(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
float node_socket_get_float(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *sock);
void node_socket_set_float(struct bNodeTree *ntree,

View File

@ -132,9 +132,8 @@ void search_link_ops_for_basic_node(GatherLinkSearchOpParams &params)
return;
}
if (node_type.declaration_is_dynamic) {
/* Dynamic declarations (whatever they end up being) aren't supported
* by this function, but still avoid a crash in release builds. */
if (node_type.declare_dynamic) {
/* Dynamic declarations aren't supported here, but avoid crashing in release builds. */
BLI_assert_unreachable();
return;
}

View File

@ -95,7 +95,7 @@ void register_node_type_sh_group()
node_type_size(&ntype, 140, 60, 400);
ntype.labelfunc = node_group_label;
ntype.group_update_func = node_group_update;
ntype.declare_dynamic = blender::nodes::node_group_declare_dynamic;
ntype.gpu_fn = gpu_group_execute;
nodeRegisterType(&ntype);

View File

@ -157,7 +157,7 @@ void register_node_type_tex_group(void)
node_type_size(&ntype, 140, 60, 400);
ntype.labelfunc = node_group_label;
ntype.group_update_func = node_group_update;
ntype.declare_dynamic = blender::nodes::node_group_declare_dynamic;
ntype.init_exec_fn = group_initexec;
ntype.free_exec_fn = group_freeexec;
ntype.exec_fn = group_execute;