Nodes: use node storage to store type of reroute node

The reroute node used to be a bit special in the sense that its data type was
only stored in the sockets. However, typically, the ground truth data should be
stored in the node storage and then the socket types are derived from that.

For users, there should not be a noticable difference. However, from Python
it's not possible to modify the socket type directly on the socket anymore.
This is forbidden for other built-in nodes already too.

Instead, one can use the new `reroute_node.socket_idname` property to change
the type of a node. This internally also recreates the sockets with the correct
type.

Pull Request: https://projects.blender.org/blender/blender/pulls/121146
This commit is contained in:
Iliya Katueshenock
2024-09-23 14:47:05 +02:00
committed by Jacques Lucke
parent 3c8d4becc8
commit c40dc9aa03
8 changed files with 109 additions and 26 deletions

View File

@@ -31,7 +31,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 23
#define BLENDER_FILE_SUBVERSION 24
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -2780,6 +2780,22 @@ static void add_image_editor_asset_shelf(Main &bmain)
}
}
static void node_reroute_add_storage(bNodeTree &tree)
{
for (bNode *node : tree.all_nodes()) {
if (node->is_reroute()) {
if (node->storage != nullptr) {
continue;
}
const bNodeSocket &input = *static_cast<const bNodeSocket *>(node->inputs.first);
NodeReroute *data = MEM_cnew<NodeReroute>(__func__);
STRNCPY(data->type_idname, input.idname);
node->storage = data;
}
}
}
/**
* It was possible that curve attributes were initialized to 0 even if that is not allowed for some
* attributes.
@@ -4640,6 +4656,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 24)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
node_reroute_add_storage(*ntree);
}
FOREACH_NODETREE_END;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@@ -1018,6 +1018,12 @@ typedef struct NodeFrame {
short label_size;
} NodeFrame;
typedef struct NodeReroute {
/** Name of the socket type (e.g. `NodeSocketFloat`). */
char type_idname[64];
} NodeReroute;
/** \note This one has been replaced with #ImageUser, keep it for do_versions(). */
typedef struct NodeImageAnim {
int frames DNA_DEPRECATED;

View File

@@ -271,11 +271,7 @@ static void rna_NodeSocket_type_set(PointerRNA *ptr, int value)
blender::bke::node_find_node(ntree, sock, &node, nullptr);
if (node->type != NODE_CUSTOM) {
/* Can't change the socket type on built-in nodes like this. */
if (!node->is_reroute()) {
/* TODO: Refactor reroute node to avoid direct change of the socket type in built-in node and
* use proper node method for this. */
return;
}
return;
}
blender::bke::node_modify_socket_type_static(ntree, node, sock, value, 0);
}

View File

@@ -4119,6 +4119,27 @@ static void rna_NodeConvertColorSpace_to_color_space_set(PointerRNA *ptr, int va
}
}
static void rna_reroute_node_socket_type_set(PointerRNA *ptr, const char *value)
{
const bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
blender::bke::bNodeTreeType *ntree_type = ntree.typeinfo;
bNode &node = *static_cast<bNode *>(ptr->data);
blender::bke::bNodeSocketType *socket_type = blender::bke::node_socket_type_find(value);
if (socket_type == nullptr) {
return;
}
if (socket_type->subtype != PROP_NONE) {
return;
}
if (ntree_type->valid_socket_type && !ntree_type->valid_socket_type(ntree_type, socket_type)) {
return;
}
NodeReroute *storage = static_cast<NodeReroute *>(node.storage);
STRNCPY(storage->type_idname, value);
}
static const EnumPropertyItem *rna_NodeConvertColorSpace_color_space_itemf(bContext * /*C*/,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
@@ -10228,6 +10249,17 @@ static void rna_def_function_node(BlenderRNA *brna)
/* -------------------------------------------------------------------------- */
static void def_reroute(StructRNA *srna)
{
RNA_def_struct_sdna_from(srna, "NodeReroute", "storage");
PropertyRNA *prop = RNA_def_property(srna, "socket_idname", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "type_idname");
RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_reroute_node_socket_type_set");
RNA_def_property_ui_text(prop, "Type of socket", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void rna_def_internal_node(BlenderRNA *brna)
{
StructRNA *srna;

View File

@@ -336,10 +336,16 @@ class Extend : public SocketDeclaration {
class ExtendBuilder : public SocketDeclarationBuilder<Extend> {};
class CustomTypeBuilder;
class Custom : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_CUSTOM;
friend CustomTypeBuilder;
using Builder = CustomTypeBuilder;
const char *idname_;
std::function<void(bNode &node, bNodeSocket &socket, const char *data_path)> init_socket_fn;
@@ -349,6 +355,11 @@ class Custom : public SocketDeclaration {
bool can_connect(const bNodeSocket &socket) const override;
};
class CustomTypeBuilder : public SocketDeclarationBuilder<Custom> {
public:
CustomTypeBuilder &idname(const char *name);
};
/* -------------------------------------------------------------------- */
/** \name #FloatBuilder Inline Methods
* \{ */
@@ -529,6 +540,18 @@ inline Image::Image() : IDSocketDeclaration("NodeSocketImage") {}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #CustomTypeBuilder Inline Methods
* \{ */
inline CustomTypeBuilder &CustomTypeBuilder::idname(const char *idname)
{
decl_->idname_ = idname;
return *this;
}
/** \} */
SocketDeclarationPtr create_extend_declaration(const eNodeSocketInOut in_out);
} // namespace blender::nodes::decl

View File

@@ -23,7 +23,7 @@ DefNode(Node, NODE_FRAME, def_frame, "FRAME"
DefNode(Node, NODE_GROUP, def_group, "GROUP", Group, "Group", "")
DefNode(Node, NODE_GROUP_INPUT, def_group_input, "GROUP_INPUT", GroupInput, "Group Input", "Expose connected data from inside a node group as inputs to its interface")
DefNode(Node, NODE_GROUP_OUTPUT, def_group_output, "GROUP_OUTPUT", GroupOutput, "Group Output", "Output data from inside of a node group")
DefNode(Node, NODE_REROUTE, 0, "REROUTE", Reroute, "Reroute", "A single-socket organization tool that supports one input and multiple outputs")
DefNode(Node, NODE_REROUTE, def_reroute, "REROUTE", Reroute, "Reroute", "A single-socket organization tool that supports one input and multiple outputs")
DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "A color picker")
DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "Input numerical values to other nodes in the tree")

View File

@@ -572,15 +572,24 @@ void register_node_type_frame()
/** \name Node Re-Route
* \{ */
static void node_reroute_init(bNodeTree *ntree, bNode *node)
static void node_reroute_declare(blender::nodes::NodeDeclarationBuilder &b)
{
/* NOTE: Cannot use socket templates for this, since it would reset the socket type
* on each file read via the template verification procedure.
*/
blender::bke::node_add_static_socket(
ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Input", "Input");
blender::bke::node_add_static_socket(
ntree, node, SOCK_OUT, SOCK_RGBA, PROP_NONE, "Output", "Output");
const bNode *node = b.node_or_null();
if (node == nullptr) {
return;
}
const blender::StringRefNull socket_idname(
static_cast<const NodeReroute *>(node->storage)->type_idname);
b.add_input<blender::nodes::decl::Custom>("Input").idname(socket_idname.c_str());
b.add_output<blender::nodes::decl::Custom>("Output").idname(socket_idname.c_str());
}
static void node_reroute_init(bNodeTree * /*ntree*/, bNode *node)
{
NodeReroute *data = MEM_cnew<NodeReroute>(__func__);
STRNCPY(data->type_idname, "NodeSocketColor");
node->storage = data;
}
void register_node_type_reroute()
@@ -590,7 +599,9 @@ void register_node_type_reroute()
ntype->free_self = (void (*)(blender::bke::bNodeType *))MEM_freeN;
blender::bke::node_type_base(ntype, NODE_REROUTE, "Reroute", NODE_CLASS_LAYOUT);
ntype->declare = node_reroute_declare;
ntype->initfunc = node_reroute_init;
node_type_storage(ntype, "NodeReroute", node_free_standard_storage, node_copy_standard_storage);
blender::bke::node_register_type(ntype);
}
@@ -674,17 +685,9 @@ void ntree_update_reroute_nodes(bNodeTree *ntree)
for (const auto item : reroute_types.items()) {
bNode *reroute_node = item.key;
const blender::bke::bNodeSocketType *socket_type = item.value;
bNodeSocket *input_socket = (bNodeSocket *)reroute_node->inputs.first;
bNodeSocket *output_socket = (bNodeSocket *)reroute_node->outputs.first;
if (input_socket->typeinfo != socket_type) {
blender::bke::node_modify_socket_type(
ntree, reroute_node, input_socket, socket_type->idname);
}
if (output_socket->typeinfo != socket_type) {
blender::bke::node_modify_socket_type(
ntree, reroute_node, output_socket, socket_type->idname);
}
NodeReroute *storage = static_cast<NodeReroute *>(reroute_node->storage);
STRNCPY(storage->type_idname, socket_type->idname);
blender::nodes::update_node_declaration_and_sockets(*ntree, *reroute_node);
}
}