Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes/intern/derived_node_tree.cc')
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc644
1 files changed, 191 insertions, 453 deletions
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index f5a0e14f18b..36c64b00f47 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -16,523 +16,261 @@
#include "NOD_derived_node_tree.hh"
-#include "BLI_dot_export.hh"
-
-#define UNINITIALIZED_ID UINT32_MAX
-
namespace blender::nodes {
-static const NodeTreeRef &get_tree_ref(NodeTreeRefMap &node_tree_refs, bNodeTree *btree)
+/* Construct a new derived node tree for a given root node tree. The generated derived node tree
+ * does not own the used node tree refs (so that those can be used by others as well). The caller
+ * has to make sure that the node tree refs added to #node_tree_refs live at least as long as the
+ * derived node tree. */
+DerivedNodeTree::DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs)
{
- return *node_tree_refs.lookup_or_add_cb(btree,
- [&]() { return std::make_unique<NodeTreeRef>(btree); });
+ /* Construct all possible contexts immediately. This is significantly cheaper than inlining all
+ * node groups. If it still becomes a performance issue in the future, contexts could be
+ * constructed lazily when they are needed. */
+ root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree, node_tree_refs);
}
-DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : btree_(btree)
+DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context,
+ const NodeRef *parent_node,
+ bNodeTree &btree,
+ NodeTreeRefMap &node_tree_refs)
{
- BLI_assert(btree != nullptr);
-
- const NodeTreeRef &main_tree_ref = get_tree_ref(node_tree_refs, btree);
- used_node_tree_refs_.add_new(&main_tree_ref);
-
- Vector<DNode *> all_nodes;
- Vector<DGroupInput *> all_group_inputs;
- Vector<DParentNode *> all_parent_nodes;
-
- this->insert_nodes_and_links_in_id_order(main_tree_ref, nullptr, all_nodes);
- this->expand_groups(all_nodes, all_group_inputs, all_parent_nodes, node_tree_refs);
- this->relink_and_remove_muted_nodes(all_nodes);
- this->remove_expanded_group_interfaces(all_nodes);
- this->remove_unused_group_inputs(all_group_inputs);
- this->store_in_this_and_init_ids(
- std::move(all_nodes), std::move(all_group_inputs), std::move(all_parent_nodes));
-}
-
-BLI_NOINLINE void DerivedNodeTree::insert_nodes_and_links_in_id_order(const NodeTreeRef &tree_ref,
- DParentNode *parent,
- Vector<DNode *> &all_nodes)
-{
- Array<DSocket *, 64> sockets_map(tree_ref.sockets().size());
-
- /* Insert nodes. */
- for (const NodeRef *node_ref : tree_ref.nodes()) {
- DNode &node = this->create_node(*node_ref, parent, sockets_map);
- all_nodes.append(&node);
- }
-
- /* Insert links. */
- for (const NodeRef *node_ref : tree_ref.nodes()) {
- for (const InputSocketRef *to_socket_ref : node_ref->inputs()) {
- DInputSocket *to_socket = static_cast<DInputSocket *>(sockets_map[to_socket_ref->id()]);
- for (const OutputSocketRef *from_socket_ref : to_socket_ref->linked_sockets()) {
- DOutputSocket *from_socket = static_cast<DOutputSocket *>(
- sockets_map[from_socket_ref->id()]);
- to_socket->linked_sockets_.append(from_socket);
- from_socket->linked_sockets_.append(to_socket);
+ DTreeContext &context = *allocator_.construct<DTreeContext>().release();
+ context.parent_context_ = parent_context;
+ context.parent_node_ = parent_node;
+ context.tree_ = &get_tree_ref_from_map(node_tree_refs, btree);
+ used_node_tree_refs_.add(context.tree_);
+
+ for (const NodeRef *node : context.tree_->nodes()) {
+ if (node->is_group_node()) {
+ bNode *bnode = node->bnode();
+ bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id);
+ if (child_btree != nullptr) {
+ DTreeContext &child = this->construct_context_recursively(
+ &context, node, *child_btree, node_tree_refs);
+ context.children_.add_new(node, &child);
}
}
}
+
+ return context;
}
-DNode &DerivedNodeTree::create_node(const NodeRef &node_ref,
- DParentNode *parent,
- MutableSpan<DSocket *> r_sockets_map)
+DerivedNodeTree::~DerivedNodeTree()
{
- DNode &node = *allocator_.construct<DNode>();
- node.node_ref_ = &node_ref;
- node.parent_ = parent;
- node.id_ = UNINITIALIZED_ID;
-
- node.inputs_ = allocator_.construct_elements_and_pointer_array<DInputSocket>(
- node_ref.inputs().size());
- node.outputs_ = allocator_.construct_elements_and_pointer_array<DOutputSocket>(
- node_ref.outputs().size());
-
- for (int i : node.inputs_.index_range()) {
- const InputSocketRef &socket_ref = node_ref.input(i);
- DInputSocket &socket = *node.inputs_[i];
- socket.is_multi_input_socket_ = socket_ref.bsocket()->flag & SOCK_MULTI_INPUT;
- socket.id_ = UNINITIALIZED_ID;
- socket.node_ = &node;
- socket.socket_ref_ = &socket_ref;
-
- r_sockets_map[socket_ref.id()] = &socket;
- }
-
- for (int i : node.outputs_.index_range()) {
- const OutputSocketRef &socket_ref = node_ref.output(i);
- DOutputSocket &socket = *node.outputs_[i];
-
- socket.id_ = UNINITIALIZED_ID;
- socket.node_ = &node;
- socket.socket_ref_ = &socket_ref;
-
- r_sockets_map[socket_ref.id()] = &socket;
- }
-
- return node;
+ /* Has to be destructed manually, because the context info is allocated in a linear allocator. */
+ this->destruct_context_recursively(root_context_);
}
-BLI_NOINLINE void DerivedNodeTree::expand_groups(Vector<DNode *> &all_nodes,
- Vector<DGroupInput *> &all_group_inputs,
- Vector<DParentNode *> &all_parent_nodes,
- NodeTreeRefMap &node_tree_refs)
+void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
{
- for (int i = 0; i < all_nodes.size(); i++) {
- DNode &node = *all_nodes[i];
- if (node.node_ref_->is_group_node()) {
- /* Muted nodes are relinked in a separate step. */
- if (!node.node_ref_->is_muted()) {
- this->expand_group_node(
- node, all_nodes, all_group_inputs, all_parent_nodes, node_tree_refs);
- }
- }
+ for (DTreeContext *child : context->children_.values()) {
+ this->destruct_context_recursively(child);
}
+ context->~DTreeContext();
}
-BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
- Vector<DNode *> &all_nodes,
- Vector<DGroupInput *> &all_group_inputs,
- Vector<DParentNode *> &all_parent_nodes,
- NodeTreeRefMap &node_tree_refs)
+/* Returns true if there are any cycles in the node tree. */
+bool DerivedNodeTree::has_link_cycles() const
{
- const NodeRef &group_node_ref = *group_node.node_ref_;
- BLI_assert(group_node_ref.is_group_node());
-
- bNodeTree *btree = reinterpret_cast<bNodeTree *>(group_node_ref.bnode()->id);
- if (btree == nullptr) {
- return;
+ for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
+ if (tree_ref->has_link_cycles()) {
+ return true;
+ }
}
-
- const NodeTreeRef &group_ref = get_tree_ref(node_tree_refs, btree);
- used_node_tree_refs_.add(&group_ref);
-
- DParentNode &parent = *allocator_.construct<DParentNode>();
- parent.id_ = all_parent_nodes.append_and_get_index(&parent);
- parent.parent_ = group_node.parent_;
- parent.node_ref_ = &group_node_ref;
-
- this->insert_nodes_and_links_in_id_order(group_ref, &parent, all_nodes);
- Span<DNode *> new_nodes_by_id = all_nodes.as_span().take_back(group_ref.nodes().size());
-
- this->create_group_inputs_for_unlinked_inputs(group_node, all_group_inputs);
- this->relink_group_inputs(group_ref, new_nodes_by_id, group_node);
- this->relink_group_outputs(group_ref, new_nodes_by_id, group_node);
+ return false;
}
-BLI_NOINLINE void DerivedNodeTree::create_group_inputs_for_unlinked_inputs(
- DNode &node, Vector<DGroupInput *> &all_group_inputs)
+/* Calls the given callback on all nodes in the (possibly nested) derived node tree. */
+void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
{
- for (DInputSocket *input_socket : node.inputs_) {
- if (input_socket->is_linked()) {
- continue;
- }
-
- DGroupInput &group_input = *allocator_.construct<DGroupInput>();
- group_input.id_ = UNINITIALIZED_ID;
- group_input.socket_ref_ = &input_socket->socket_ref();
- group_input.parent_ = node.parent_;
-
- group_input.linked_sockets_.append(input_socket);
- input_socket->linked_group_inputs_.append(&group_input);
- all_group_inputs.append(&group_input);
- }
+ this->foreach_node_in_context_recursive(*root_context_, callback);
}
-BLI_NOINLINE void DerivedNodeTree::relink_group_inputs(const NodeTreeRef &group_ref,
- Span<DNode *> nodes_by_id,
- DNode &group_node)
+void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
+ FunctionRef<void(DNode)> callback) const
{
- Span<const NodeRef *> node_refs = group_ref.nodes_by_type("NodeGroupInput");
- if (node_refs.size() == 0) {
- return;
+ for (const NodeRef *node_ref : context.tree_->nodes()) {
+ callback(DNode(&context, node_ref));
}
- /* TODO: Pick correct group input node if there are more than one. */
- const NodeRef &input_node_ref = *node_refs[0];
- DNode &input_node = *nodes_by_id[input_node_ref.id()];
-
- int input_amount = group_node.inputs().size();
- BLI_assert(input_amount == input_node_ref.outputs().size() - 1);
-
- for (int input_index : IndexRange(input_amount)) {
- DInputSocket *outside_group = group_node.inputs_[input_index];
- DOutputSocket *inside_group = input_node.outputs_[input_index];
-
- for (DOutputSocket *outside_connected : outside_group->linked_sockets_) {
- outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
- }
-
- for (DGroupInput *outside_connected : outside_group->linked_group_inputs_) {
- outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
- }
-
- for (DInputSocket *inside_connected : inside_group->linked_sockets_) {
- inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
-
- for (DOutputSocket *outside_connected : outside_group->linked_sockets_) {
- inside_connected->linked_sockets_.append(outside_connected);
- outside_connected->linked_sockets_.append(inside_connected);
- }
-
- for (DGroupInput *outside_connected : outside_group->linked_group_inputs_) {
- inside_connected->linked_group_inputs_.append(outside_connected);
- outside_connected->linked_sockets_.append(inside_connected);
- }
- }
-
- inside_group->linked_sockets_.clear();
- outside_group->linked_sockets_.clear();
- outside_group->linked_group_inputs_.clear();
+ for (const DTreeContext *child_context : context.children_.values()) {
+ this->foreach_node_in_context_recursive(*child_context, callback);
}
}
-BLI_NOINLINE void DerivedNodeTree::relink_group_outputs(const NodeTreeRef &group_ref,
- Span<DNode *> nodes_by_id,
- DNode &group_node)
+DOutputSocket DInputSocket::get_corresponding_group_node_output() const
{
- Span<const NodeRef *> node_refs = group_ref.nodes_by_type("NodeGroupOutput");
- if (node_refs.size() == 0) {
- return;
- }
- /* TODO: Pick correct group output node if there are more than one. */
- const NodeRef &output_node_ref = *node_refs[0];
- DNode &output_node = *nodes_by_id[output_node_ref.id()];
-
- int output_amount = group_node.outputs().size();
- BLI_assert(output_amount == output_node_ref.inputs().size() - 1);
-
- for (int output_index : IndexRange(output_amount)) {
- DOutputSocket *outside_group = group_node.outputs_[output_index];
- DInputSocket *inside_group = output_node.inputs_[output_index];
-
- for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
- outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
- }
-
- for (DOutputSocket *inside_connected : inside_group->linked_sockets_) {
- inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
-
- for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
- inside_connected->linked_sockets_.append(outside_connected);
- outside_connected->linked_sockets_.append(inside_connected);
- }
- }
+ BLI_assert(*this);
+ BLI_assert(socket_ref_->node().is_group_output_node());
+ BLI_assert(socket_ref_->index() < socket_ref_->node().inputs().size() - 1);
- for (DGroupInput *inside_connected : inside_group->linked_group_inputs_) {
- inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
+ const DTreeContext *parent_context = context_->parent_context();
+ const NodeRef *parent_node = context_->parent_node();
+ BLI_assert(parent_context != nullptr);
+ BLI_assert(parent_node != nullptr);
- for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
- inside_connected->linked_sockets_.append(outside_connected);
- outside_connected->linked_group_inputs_.append(inside_connected);
- }
- }
-
- outside_group->linked_sockets_.clear();
- inside_group->linked_sockets_.clear();
- }
+ const int socket_index = socket_ref_->index();
+ return {parent_context, &parent_node->output(socket_index)};
}
-BLI_NOINLINE void DerivedNodeTree::remove_expanded_group_interfaces(Vector<DNode *> &all_nodes)
+Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() const
{
- int index = 0;
- while (index < all_nodes.size()) {
- DNode &node = *all_nodes[index];
- const NodeRef &node_ref = *node.node_ref_;
- if (node_ref.is_group_node() ||
- (node.parent_ != nullptr &&
- (node_ref.is_group_input_node() || node_ref.is_group_output_node()))) {
- all_nodes.remove_and_reorder(index);
- node.destruct_with_sockets();
- }
- else {
- index++;
- }
+ BLI_assert(*this);
+ BLI_assert(socket_ref_->node().is_group_node());
+
+ const DTreeContext *child_context = context_->child_context(socket_ref_->node());
+ BLI_assert(child_context != nullptr);
+
+ const NodeTreeRef &child_tree = child_context->tree();
+ Span<const NodeRef *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
+ const int socket_index = socket_ref_->index();
+ Vector<DOutputSocket> sockets;
+ for (const NodeRef *group_input_node : group_input_nodes) {
+ sockets.append(DOutputSocket(child_context, &group_input_node->output(socket_index)));
}
+ return sockets;
}
-BLI_NOINLINE void DerivedNodeTree::remove_unused_group_inputs(
- Vector<DGroupInput *> &all_group_inputs)
+DInputSocket DOutputSocket::get_corresponding_group_node_input() const
{
- int index = 0;
- while (index < all_group_inputs.size()) {
- DGroupInput &group_input = *all_group_inputs[index];
- if (group_input.linked_sockets_.is_empty()) {
- all_group_inputs.remove_and_reorder(index);
- group_input.~DGroupInput();
- }
- else {
- index++;
- }
- }
+ BLI_assert(*this);
+ BLI_assert(socket_ref_->node().is_group_input_node());
+ BLI_assert(socket_ref_->index() < socket_ref_->node().outputs().size() - 1);
+
+ const DTreeContext *parent_context = context_->parent_context();
+ const NodeRef *parent_node = context_->parent_node();
+ BLI_assert(parent_context != nullptr);
+ BLI_assert(parent_node != nullptr);
+
+ const int socket_index = socket_ref_->index();
+ return {parent_context, &parent_node->input(socket_index)};
}
-BLI_NOINLINE void DerivedNodeTree::relink_and_remove_muted_nodes(Vector<DNode *> &all_nodes)
+DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
{
- int index = 0;
- while (index < all_nodes.size()) {
- DNode &node = *all_nodes[index];
- const NodeRef &node_ref = *node.node_ref_;
- if (node_ref.is_muted()) {
- this->relink_muted_node(node);
- all_nodes.remove_and_reorder(index);
- node.destruct_with_sockets();
- }
- else {
- index++;
+ BLI_assert(*this);
+ BLI_assert(socket_ref_->node().is_group_node());
+
+ const DTreeContext *child_context = context_->child_context(socket_ref_->node());
+ BLI_assert(child_context != nullptr);
+
+ const NodeTreeRef &child_tree = child_context->tree();
+ Span<const NodeRef *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
+ const int socket_index = socket_ref_->index();
+ for (const NodeRef *group_output_node : group_output_nodes) {
+ if (group_output_node->bnode()->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
+ return {child_context, &group_output_node->input(socket_index)};
}
}
+ return {};
}
-BLI_NOINLINE void DerivedNodeTree::relink_muted_node(DNode &node)
+/* Call the given callback for every "real" origin socket. "Real" means that reroutes, muted nodes
+ * and node groups are handled by this function. Origin sockets are ones where a node gets its
+ * inputs from. */
+void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback,
+ const bool follow_only_first_incoming_link) const
{
- const bNode &bnode = *node.bnode();
- LISTBASE_FOREACH (const bNodeLink *, internal_link, &bnode.internal_links) {
- BLI_assert(internal_link->fromnode == &bnode);
- BLI_assert(internal_link->tonode == &bnode);
- bNodeSocket *input_bsocket = internal_link->fromsock;
- bNodeSocket *output_bsocket = internal_link->tosock;
-
- /* Find internally linked sockets. */
- DInputSocket *input_socket = nullptr;
- DOutputSocket *output_socket = nullptr;
- for (DInputSocket *socket : node.inputs_) {
- if (socket->bsocket() == input_bsocket) {
- input_socket = socket;
- break;
- }
- }
- for (DOutputSocket *socket : node.outputs_) {
- if (socket->bsocket() == output_bsocket) {
- output_socket = socket;
- break;
+ BLI_assert(*this);
+ Span<const OutputSocketRef *> linked_sockets_to_check = socket_ref_->as_input().linked_sockets();
+ if (follow_only_first_incoming_link) {
+ linked_sockets_to_check = linked_sockets_to_check.take_front(1);
+ }
+ for (const OutputSocketRef *linked_socket : linked_sockets_to_check) {
+ const NodeRef &linked_node = linked_socket->node();
+ DOutputSocket linked_dsocket{context_, linked_socket};
+
+ if (linked_node.is_muted()) {
+ /* If the node is muted, follow the internal links of the node. */
+ for (const InternalLinkRef *internal_link : linked_node.internal_links()) {
+ if (&internal_link->to() == linked_socket) {
+ DInputSocket input_of_muted_node{context_, &internal_link->from()};
+ input_of_muted_node.foreach_origin_socket(callback, true);
+ }
}
}
- BLI_assert(input_socket != nullptr);
- BLI_assert(output_socket != nullptr);
-
- /* Link sockets connected to the input to sockets that are connected to the internally linked
- * output. */
- for (DInputSocket *to_socket : output_socket->linked_sockets_) {
- for (DOutputSocket *from_socket : input_socket->linked_sockets_) {
- from_socket->linked_sockets_.append_non_duplicates(to_socket);
- to_socket->linked_sockets_.append_non_duplicates(from_socket);
+ else if (linked_node.is_group_input_node()) {
+ if (context_->is_root()) {
+ /* This is a group input in the root node group. */
+ callback(linked_dsocket);
}
- for (DGroupInput *group_input : input_socket->linked_group_inputs_) {
- group_input->linked_sockets_.append_non_duplicates(to_socket);
- to_socket->linked_group_inputs_.append_non_duplicates(group_input);
+ else {
+ DInputSocket socket_in_parent_group = linked_dsocket.get_corresponding_group_node_input();
+ if (socket_in_parent_group->is_linked()) {
+ /* Follow the links coming into the corresponding socket on the parent group node. */
+ socket_in_parent_group.foreach_origin_socket(callback);
+ }
+ else {
+ /* The corresponding input on the parent group node is not connected. Therefore, we use
+ * the value of that input socket directly. */
+ callback(socket_in_parent_group);
+ }
}
}
- }
-
- /* Remove remaining links from muted node. */
- for (DInputSocket *to_socket : node.inputs_) {
- for (DOutputSocket *from_socket : to_socket->linked_sockets_) {
- from_socket->linked_sockets_.remove_first_occurrence_and_reorder(to_socket);
- }
- for (DGroupInput *from_group_input : to_socket->linked_group_inputs_) {
- from_group_input->linked_sockets_.remove_first_occurrence_and_reorder(to_socket);
- }
- to_socket->linked_sockets_.clear();
- to_socket->linked_group_inputs_.clear();
- }
- for (DOutputSocket *from_socket : node.outputs_) {
- for (DInputSocket *to_socket : from_socket->linked_sockets_) {
- to_socket->linked_sockets_.remove_first_occurrence_and_reorder(from_socket);
- }
- from_socket->linked_sockets_.clear();
- }
-}
-
-void DNode::destruct_with_sockets()
-{
- for (DInputSocket *socket : inputs_) {
- socket->~DInputSocket();
- }
- for (DOutputSocket *socket : outputs_) {
- socket->~DOutputSocket();
- }
- this->~DNode();
-}
-
-BLI_NOINLINE void DerivedNodeTree::store_in_this_and_init_ids(
- Vector<DNode *> &&all_nodes,
- Vector<DGroupInput *> &&all_group_inputs,
- Vector<DParentNode *> &&all_parent_nodes)
-{
- nodes_by_id_ = std::move(all_nodes);
- group_inputs_ = std::move(all_group_inputs);
- parent_nodes_ = std::move(all_parent_nodes);
-
- for (int node_index : nodes_by_id_.index_range()) {
- DNode *node = nodes_by_id_[node_index];
- node->id_ = node_index;
-
- const bNodeType *nodetype = node->node_ref_->bnode()->typeinfo;
- nodes_by_type_.add(nodetype, node);
-
- for (DInputSocket *socket : node->inputs_) {
- socket->id_ = sockets_by_id_.append_and_get_index(socket);
- input_sockets_.append(socket);
- }
- for (DOutputSocket *socket : node->outputs_) {
- socket->id_ = sockets_by_id_.append_and_get_index(socket);
- output_sockets_.append(socket);
+ else if (linked_node.is_group_node()) {
+ DInputSocket socket_in_group = linked_dsocket.get_active_corresponding_group_output_socket();
+ if (socket_in_group) {
+ if (socket_in_group->is_linked()) {
+ /* Follow the links coming into the group output node of the child node group. */
+ socket_in_group.foreach_origin_socket(callback);
+ }
+ else {
+ /* The output of the child node group is not connected, so we have to get the value from
+ * that socket. */
+ callback(socket_in_group);
+ }
+ }
}
- }
-
- for (int i : group_inputs_.index_range()) {
- group_inputs_[i]->id_ = i;
- }
-}
-
-DerivedNodeTree::~DerivedNodeTree()
-{
- for (DInputSocket *socket : input_sockets_) {
- socket->~DInputSocket();
- }
- for (DOutputSocket *socket : output_sockets_) {
- socket->~DOutputSocket();
- }
- for (DNode *node : nodes_by_id_) {
- node->~DNode();
- }
- for (DGroupInput *group_input : group_inputs_) {
- group_input->~DGroupInput();
- }
- for (DParentNode *parent : parent_nodes_) {
- parent->~DParentNode();
- }
-}
-
-bool DerivedNodeTree::has_link_cycles() const
-{
- for (const NodeTreeRef *tree : used_node_tree_refs_) {
- if (tree->has_link_cycles()) {
- return true;
+ else {
+ /* The normal case: just use the value of a linked output socket. */
+ callback(linked_dsocket);
}
}
- return false;
-}
-
-static dot::Cluster *get_cluster_for_parent(dot::DirectedGraph &graph,
- Map<const DParentNode *, dot::Cluster *> &clusters,
- const DParentNode *parent)
-{
- if (parent == nullptr) {
- return nullptr;
- }
- return clusters.lookup_or_add_cb(parent, [&]() {
- dot::Cluster *parent_cluster = get_cluster_for_parent(graph, clusters, parent->parent());
- bNodeTree *btree = reinterpret_cast<bNodeTree *>(parent->node_ref().bnode()->id);
- dot::Cluster *new_cluster = &graph.new_cluster(parent->node_ref().name() + " / " +
- StringRef(btree->id.name + 2));
- new_cluster->set_parent_cluster(parent_cluster);
- return new_cluster;
- });
}
-std::string DerivedNodeTree::to_dot() const
+/* Calls the given callback for every "real" target socket. "Real" means that reroutes, muted nodes
+ * and node groups are handled by this function. Target sockets are on the nodes that use the value
+ * from this socket. */
+void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const
{
- dot::DirectedGraph digraph;
- digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
-
- Map<const DNode *, dot::NodeWithSocketsRef> dot_nodes;
- Map<const DGroupInput *, dot::NodeWithSocketsRef> dot_group_inputs;
- Map<const DParentNode *, dot::Cluster *> dot_clusters;
-
- for (const DNode *node : nodes_by_id_) {
- dot::Node &dot_node = digraph.new_node("");
- dot_node.set_background_color("white");
-
- Vector<std::string> input_names;
- for (const DInputSocket *socket : node->inputs()) {
- input_names.append(socket->name());
- }
- Vector<std::string> output_names;
- for (const DOutputSocket *socket : node->outputs()) {
- output_names.append(socket->name());
+ for (const InputSocketRef *linked_socket : socket_ref_->as_output().linked_sockets()) {
+ const NodeRef &linked_node = linked_socket->node();
+ DInputSocket linked_dsocket{context_, linked_socket};
+
+ if (linked_node.is_muted()) {
+ /* If the target node is muted, follow its internal links. */
+ for (const InternalLinkRef *internal_link : linked_node.internal_links()) {
+ if (&internal_link->from() == linked_socket) {
+ DOutputSocket output_of_muted_node{context_, &internal_link->to()};
+ output_of_muted_node.foreach_target_socket(callback);
+ }
+ }
}
-
- dot_nodes.add_new(node,
- dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
-
- dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, node->parent());
- dot_node.set_parent_cluster(cluster);
- }
-
- for (const DGroupInput *group_input : group_inputs_) {
- dot::Node &dot_node = digraph.new_node("");
- dot_node.set_background_color("white");
-
- std::string group_input_name = group_input->name();
- dot_group_inputs.add_new(
- group_input, dot::NodeWithSocketsRef(dot_node, "Group Input", {}, {group_input_name}));
-
- dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, group_input->parent());
- dot_node.set_parent_cluster(cluster);
- }
-
- for (const DNode *to_node : nodes_by_id_) {
- dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(to_node);
-
- for (const DInputSocket *to_socket : to_node->inputs()) {
- for (const DOutputSocket *from_socket : to_socket->linked_sockets()) {
- const DNode *from_node = &from_socket->node();
- dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(from_node);
-
- digraph.new_edge(from_dot_node.output(from_socket->index()),
- to_dot_node.input(to_socket->index()));
+ else if (linked_node.is_group_output_node()) {
+ if (context_->is_root()) {
+ /* This is a group output in the root node group. */
+ callback(linked_dsocket);
}
- for (const DGroupInput *group_input : to_socket->linked_group_inputs()) {
- dot::NodeWithSocketsRef &from_dot_node = dot_group_inputs.lookup(group_input);
-
- digraph.new_edge(from_dot_node.output(0), to_dot_node.input(to_socket->index()));
+ else {
+ /* Follow the links going out of the group node in the parent node group. */
+ DOutputSocket socket_in_parent_group =
+ linked_dsocket.get_corresponding_group_node_output();
+ socket_in_parent_group.foreach_target_socket(callback);
+ }
+ }
+ else if (linked_node.is_group_node()) {
+ /* Follow the links within the nested node group. */
+ Vector<DOutputSocket> sockets_in_group =
+ linked_dsocket.get_corresponding_group_input_sockets();
+ for (DOutputSocket socket_in_group : sockets_in_group) {
+ socket_in_group.foreach_target_socket(callback);
}
}
+ else {
+ /* The normal case: just use the linked input socket as target. */
+ callback(linked_dsocket);
+ }
}
-
- digraph.set_random_cluster_bgcolors();
- return digraph.to_dot_string();
}
} // namespace blender::nodes