/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2007 Blender Foundation. All rights reserved. */ /** \file * \ingroup nodes */ #include #include #include "DNA_node_types.h" #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_multi_value_map.hh" #include "BLI_set.hh" #include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_string_ref.hh" #include "BLI_utildefines.h" #include "BLT_translation.h" #include "BKE_node.h" #include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "RNA_types.h" #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" using blender::Map; using blender::MultiValueMap; using blender::Set; using blender::Stack; using blender::StringRef; using blender::Vector; /* -------------------------------------------------------------------- */ /** \name Node Group * \{ */ static bNodeSocket *find_matching_socket(ListBase &sockets, StringRef identifier) { LISTBASE_FOREACH (bNodeSocket *, socket, &sockets) { if (socket->identifier == identifier) { return socket; } } return nullptr; } bNodeSocket *node_group_find_input_socket(bNode *groupnode, const char *identifier) { return find_matching_socket(groupnode->inputs, identifier); } bNodeSocket *node_group_find_output_socket(bNode *groupnode, const char *identifier) { return find_matching_socket(groupnode->outputs, identifier); } void node_group_label(const bNodeTree * /*ntree*/, const bNode *node, char *label, int maxlen) { BLI_strncpy(label, (node->id) ? node->id->name + 2 : IFACE_("Missing Data-Block"), maxlen); } bool node_group_poll_instance(const bNode *node, const bNodeTree *nodetree, const char **disabled_hint) { if (node->typeinfo->poll(node->typeinfo, nodetree, disabled_hint)) { const bNodeTree *grouptree = (const bNodeTree *)node->id; if (grouptree) { return nodeGroupPoll(nodetree, grouptree, disabled_hint); } return true; /* without a linked node tree, group node is always ok */ } return false; } bool nodeGroupPoll(const bNodeTree *nodetree, const bNodeTree *grouptree, const char **r_disabled_hint) { /* unspecified node group, generally allowed * (if anything, should be avoided on operator level) */ if (grouptree == nullptr) { return true; } if (nodetree == grouptree) { if (r_disabled_hint) { *r_disabled_hint = TIP_("Nesting a node group inside of itself is not allowed"); } return false; } if (nodetree->type != grouptree->type) { if (r_disabled_hint) { *r_disabled_hint = TIP_("Node group has different type"); } return false; } LISTBASE_FOREACH (const bNode *, node, &grouptree->nodes) { if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance( const_cast(node), const_cast(nodetree), r_disabled_hint)) { return false; } } return true; } namespace blender::nodes { 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(); std::unique_ptr decl = std::make_unique(); 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(); std::unique_ptr decl = std::make_unique(); 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(); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); break; } case SOCK_SHADER: { std::unique_ptr decl = std::make_unique(); dst = std::move(decl); break; } case SOCK_BOOLEAN: { const auto &value = *io_socket.default_value_typed(); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); break; } case SOCK_INT: { const auto &value = *io_socket.default_value_typed(); std::unique_ptr decl = std::make_unique(); 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(); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); break; } case SOCK_OBJECT: dst = std::make_unique(); break; case SOCK_IMAGE: dst = std::make_unique(); break; case SOCK_GEOMETRY: dst = std::make_unique(); break; case SOCK_COLLECTION: dst = std::make_unique(); break; case SOCK_TEXTURE: dst = std::make_unique(); break; case SOCK_MATERIAL: dst = std::make_unique(); break; case SOCK_CUSTOM: std::unique_ptr decl = std::make_unique(); 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(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)); } } } // namespace blender::nodes /** \} */ /* -------------------------------------------------------------------- */ /** \name Node Frame * \{ */ static void node_frame_init(bNodeTree * /*ntree*/, bNode *node) { NodeFrame *data = MEM_cnew("frame node storage"); node->storage = data; data->flag |= NODE_FRAME_SHRINK; data->label_size = 20; } void register_node_type_frame() { /* frame type is used for all tree types, needs dynamic allocation */ bNodeType *ntype = MEM_cnew("frame node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT); ntype->initfunc = node_frame_init; node_type_storage(ntype, "NodeFrame", node_free_standard_storage, node_copy_standard_storage); node_type_size(ntype, 150, 100, 0); ntype->flag |= NODE_BACKGROUND; nodeRegisterType(ntype); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Node Re-Route * \{ */ static void node_reroute_init(bNodeTree *ntree, bNode *node) { /* NOTE: Cannot use socket templates for this, since it would reset the socket type * on each file read via the template verification procedure. */ nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, "Input", "Input"); nodeAddStaticSocket(ntree, node, SOCK_OUT, SOCK_RGBA, PROP_NONE, "Output", "Output"); } void register_node_type_reroute() { /* frame type is used for all tree types, needs dynamic allocation */ bNodeType *ntype = MEM_cnew("frame node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_REROUTE, "Reroute", NODE_CLASS_LAYOUT); ntype->initfunc = node_reroute_init; nodeRegisterType(ntype); } static void propagate_reroute_type_from_start_socket( bNodeSocket *start_socket, const MultiValueMap &links_map, Map &r_reroute_types) { Stack nodes_to_check; for (bNodeLink *link : links_map.lookup(start_socket)) { if (link->tonode->type == NODE_REROUTE) { nodes_to_check.push(link->tonode); } if (link->fromnode->type == NODE_REROUTE) { nodes_to_check.push(link->fromnode); } } const bNodeSocketType *current_type = start_socket->typeinfo; while (!nodes_to_check.is_empty()) { bNode *reroute_node = nodes_to_check.pop(); BLI_assert(reroute_node->type == NODE_REROUTE); if (r_reroute_types.add(reroute_node, current_type)) { for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->inputs.first)) { if (link->fromnode->type == NODE_REROUTE) { nodes_to_check.push(link->fromnode); } } for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->outputs.first)) { if (link->tonode->type == NODE_REROUTE) { nodes_to_check.push(link->tonode); } } } } } void ntree_update_reroute_nodes(bNodeTree *ntree) { /* Contains nodes that are linked to at least one reroute node. */ Set nodes_linked_with_reroutes; /* Contains all links that are linked to at least one reroute node. */ MultiValueMap links_map; /* Build acceleration data structures for the algorithm below. */ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { if (link->fromsock == nullptr || link->tosock == nullptr) { continue; } if (link->fromnode->type != NODE_REROUTE && link->tonode->type != NODE_REROUTE) { continue; } if (link->fromnode->type != NODE_REROUTE) { nodes_linked_with_reroutes.add(link->fromnode); } if (link->tonode->type != NODE_REROUTE) { nodes_linked_with_reroutes.add(link->tonode); } links_map.add(link->fromsock, link); links_map.add(link->tosock, link); } /* Will contain the socket type for every linked reroute node. */ Map reroute_types; /* Propagate socket types from left to right. */ for (bNode *start_node : nodes_linked_with_reroutes) { LISTBASE_FOREACH (bNodeSocket *, output_socket, &start_node->outputs) { propagate_reroute_type_from_start_socket(output_socket, links_map, reroute_types); } } /* Propagate socket types from right to left. This affects reroute nodes that haven't been * changed in the loop above. */ for (bNode *start_node : nodes_linked_with_reroutes) { LISTBASE_FOREACH (bNodeSocket *, input_socket, &start_node->inputs) { propagate_reroute_type_from_start_socket(input_socket, links_map, reroute_types); } } /* Actually update reroute nodes with changed types. */ for (const auto item : reroute_types.items()) { bNode *reroute_node = item.key; const 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) { nodeModifySocketType(ntree, reroute_node, input_socket, socket_type->idname); } if (output_socket->typeinfo != socket_type) { nodeModifySocketType(ntree, reroute_node, output_socket, socket_type->idname); } } } bool BKE_node_is_connected_to_output(const bNodeTree *ntree, const bNode *node) { ntree->ensure_topology_cache(); Stack nodes_to_check; for (const bNodeSocket *socket : node->output_sockets()) { for (const bNodeLink *link : socket->directly_linked_links()) { nodes_to_check.push(link->tonode); } } while (!nodes_to_check.is_empty()) { const bNode *next_node = nodes_to_check.pop(); for (const bNodeSocket *socket : next_node->output_sockets()) { for (const bNodeLink *link : socket->directly_linked_links()) { if (link->tonode->typeinfo->nclass == NODE_CLASS_OUTPUT && link->tonode->flag & NODE_DO_OUTPUT) { return true; } nodes_to_check.push(link->tonode); } } } return false; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Node #GROUP_INPUT / #GROUP_OUTPUT * \{ */ bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier) { bNodeSocket *sock; for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { if (STREQ(sock->identifier, identifier)) { return sock; } } return nullptr; } namespace blender::nodes { static SocketDeclarationPtr extend_declaration(const eNodeSocketInOut in_out) { std::unique_ptr decl = std::make_unique(); 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 */ bNodeType *ntype = MEM_cnew("node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE); node_type_size(ntype, 140, 80, 400); ntype->declare_dynamic = blender::nodes::group_input_declare_dynamic; ntype->insert_link = blender::nodes::group_input_insert_link; nodeRegisterType(ntype); } bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier) { bNodeSocket *sock; for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (STREQ(sock->identifier, identifier)) { return sock; } } return nullptr; } void register_node_type_group_output() { /* used for all tree types, needs dynamic allocation */ bNodeType *ntype = MEM_cnew("node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE); node_type_size(ntype, 140, 80, 400); ntype->declare_dynamic = blender::nodes::group_output_declare_dynamic; ntype->insert_link = blender::nodes::group_output_insert_link; ntype->no_muting = true; nodeRegisterType(ntype); } /** \} */