diff options
author | Lukas Toenne <lukas.toenne@googlemail.com> | 2011-12-18 19:34:06 +0400 |
---|---|---|
committer | Lukas Toenne <lukas.toenne@googlemail.com> | 2011-12-18 19:34:06 +0400 |
commit | 5f6bd44c82bef24a43696449d8b905beeb53015b (patch) | |
tree | 7e955e53c4e9ee8a44c716b79ceaeebc34511b08 | |
parent | b49463c439820c5039caf63421bdc8f3a58554b2 (diff) |
Generalized node groups for Cycles.
This allows group nodes inside other group nodes in cycles and makes the
code more generic for all possible cases, like direct group
input-to-output links and unused group sockets.
Previous code tried to connect external nodes and internal group sockets
by following links until a "real" node input/output. This quickly
becomes complicated in corner cases as described above and can lead to
unexpected behavior when the group socket is of a different type than
the internal/external sockets, but that conversion is skipped.
The new code uses the concept of "proxy nodes" similar to what the new
compositor does. Each group socket is replaced with a proxy node with a
single input and output, to which other nodes in the same tree and
internal nodes can link to. After all groups have been expanded in the
graph, these proxy nodes are removed again, adding converter nodes if
necessary.
-rw-r--r-- | intern/cycles/blender/blender_shader.cpp | 212 | ||||
-rw-r--r-- | intern/cycles/render/graph.cpp | 53 | ||||
-rw-r--r-- | intern/cycles/render/graph.h | 1 | ||||
-rw-r--r-- | intern/cycles/render/nodes.cpp | 20 | ||||
-rw-r--r-- | intern/cycles/render/nodes.h | 8 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 10 |
6 files changed, 200 insertions, 104 deletions
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index ecc85a92ae0..5c39a3f169d 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -50,28 +50,6 @@ void BlenderSync::find_shader(BL::ID id, vector<uint>& used_shaders, int default /* Graph */ -static BL::NodeSocket get_node_input(BL::Node *b_group_node, BL::NodeSocket b_in) -{ - if(b_group_node) { - - BL::NodeTree b_ntree = BL::NodeGroup(*b_group_node).node_tree(); - BL::NodeTree::links_iterator b_link; - - for(b_ntree.links.begin(b_link); b_link != b_ntree.links.end(); ++b_link) { - if(b_link->to_socket().ptr.data == b_in.ptr.data) { - BL::Node::inputs_iterator b_gin; - - for(b_group_node->inputs.begin(b_gin); b_gin != b_group_node->inputs.end(); ++b_gin) - if(b_gin->group_socket().ptr.data == b_link->from_socket().ptr.data) - return *b_gin; - - } - } - } - - return b_in; -} - static BL::NodeSocket get_node_output(BL::Node b_node, const string& name) { BL::Node::outputs_iterator b_out; @@ -121,7 +99,7 @@ static void get_tex_mapping(TextureMapping *mapping, BL::ShaderNodeMapping b_map mapping->scale = get_float3(b_mapping.scale()); } -static ShaderNode *add_node(BL::BlendData b_data, ShaderGraph *graph, BL::Node *b_group_node, BL::ShaderNode b_node) +static ShaderNode *add_node(BL::BlendData b_data, ShaderGraph *graph, BL::ShaderNode b_node) { ShaderNode *node = NULL; @@ -470,59 +448,115 @@ static SocketPair node_socket_map_pair(PtrNodeMap& node_map, BL::Node b_node, BL return SocketPair(node_map[b_node.ptr.data], name); } -static void add_nodes(BL::BlendData b_data, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, BL::Node *b_group_node, PtrSockMap& sockets_map) +static ShaderSocketType convert_socket_type(BL::NodeSocket::type_enum b_type) +{ + switch (b_type) { + case BL::NodeSocket::type_VALUE: + return SHADER_SOCKET_FLOAT; + case BL::NodeSocket::type_VECTOR: + return SHADER_SOCKET_VECTOR; + case BL::NodeSocket::type_RGBA: + return SHADER_SOCKET_COLOR; + case BL::NodeSocket::type_SHADER: + return SHADER_SOCKET_CLOSURE; + + case BL::NodeSocket::type_BOOLEAN: + case BL::NodeSocket::type_MESH: + case BL::NodeSocket::type_INT: + default: + return SHADER_SOCKET_FLOAT; + } +} + +static void set_default_value(ShaderInput *input, BL::NodeSocket sock) +{ + /* copy values for non linked inputs */ + switch(input->type) { + case SHADER_SOCKET_FLOAT: { + BL::NodeSocketFloatNone value_sock(sock); + input->set(value_sock.default_value()); + break; + } + case SHADER_SOCKET_COLOR: { + BL::NodeSocketRGBA rgba_sock(sock); + input->set(get_float3(rgba_sock.default_value())); + break; + } + case SHADER_SOCKET_NORMAL: + case SHADER_SOCKET_POINT: + case SHADER_SOCKET_VECTOR: { + BL::NodeSocketVectorNone vec_sock(sock); + input->set(get_float3(vec_sock.default_value())); + break; + } + case SHADER_SOCKET_CLOSURE: + break; + } +} + +static void add_nodes(BL::BlendData b_data, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, PtrSockMap& sockets_map) { /* add nodes */ BL::ShaderNodeTree::nodes_iterator b_node; PtrNodeMap node_map; - map<void*, PtrSockMap> node_groups; + PtrSockMap proxy_map; for(b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) { if(b_node->is_a(&RNA_NodeGroup)) { + /* add proxy converter nodes for inputs and outputs */ BL::NodeGroup b_gnode(*b_node); BL::ShaderNodeTree b_group_ntree(b_gnode.node_tree()); - - node_groups[b_node->ptr.data] = PtrSockMap(); - add_nodes(b_data, graph, b_group_ntree, &b_gnode, node_groups[b_node->ptr.data]); + BL::Node::inputs_iterator b_input; + BL::Node::outputs_iterator b_output; + + PtrSockMap group_sockmap; + + for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) { + ShaderSocketType extern_type = convert_socket_type(b_input->type()); + ShaderSocketType intern_type = convert_socket_type(b_input->group_socket().type()); + ShaderNode *proxy = graph->add(new ProxyNode(extern_type, intern_type)); + + /* map the external node socket to the proxy node socket */ + proxy_map[b_input->ptr.data] = SocketPair(proxy, proxy->inputs[0]->name); + /* map the internal group socket to the proxy node socket */ + group_sockmap[b_input->group_socket().ptr.data] = SocketPair(proxy, proxy->outputs[0]->name); + + /* default input values of the group node */ + set_default_value(proxy->inputs[0], *b_input); + } + + for(b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) { + ShaderSocketType extern_type = convert_socket_type(b_output->type()); + ShaderSocketType intern_type = convert_socket_type(b_output->group_socket().type()); + ShaderNode *proxy = graph->add(new ProxyNode(intern_type, extern_type)); + + /* map the external node socket to the proxy node socket */ + proxy_map[b_output->ptr.data] = SocketPair(proxy, proxy->outputs[0]->name); + /* map the internal group socket to the proxy node socket */ + group_sockmap[b_output->group_socket().ptr.data] = SocketPair(proxy, proxy->inputs[0]->name); + + /* default input values of internal, unlinked group outputs */ + set_default_value(proxy->inputs[0], b_output->group_socket()); + } + + add_nodes(b_data, graph, b_group_ntree, group_sockmap); } else { - ShaderNode *node = add_node(b_data, graph, b_group_node, BL::ShaderNode(*b_node)); - + ShaderNode *node = add_node(b_data, graph, BL::ShaderNode(*b_node)); + if(node) { BL::Node::inputs_iterator b_input; - BL::Node::outputs_iterator b_output; - + node_map[b_node->ptr.data] = node; - + for(b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) { SocketPair pair = node_socket_map_pair(node_map, *b_node, *b_input); ShaderInput *input = pair.first->input(pair.second.c_str()); - BL::NodeSocket sock(get_node_input(b_group_node, *b_input)); - + assert(input); - + /* copy values for non linked inputs */ - switch(input->type) { - case SHADER_SOCKET_FLOAT: { - BL::NodeSocketFloatNone value_sock(sock); - input->set(value_sock.default_value()); - break; - } - case SHADER_SOCKET_COLOR: { - BL::NodeSocketRGBA rgba_sock(sock); - input->set(get_float3(rgba_sock.default_value())); - break; - } - case SHADER_SOCKET_NORMAL: - case SHADER_SOCKET_POINT: - case SHADER_SOCKET_VECTOR: { - BL::NodeSocketVectorNone vec_sock(sock); - input->set(get_float3(vec_sock.default_value())); - break; - } - case SHADER_SOCKET_CLOSURE: - break; - } + set_default_value(input, *b_input); } } } @@ -539,54 +573,34 @@ static void add_nodes(BL::BlendData b_data, ShaderGraph *graph, BL::ShaderNodeTr BL::NodeSocket b_from_sock = b_link->from_socket(); BL::NodeSocket b_to_sock = b_link->to_socket(); - /* if link with group socket, add to map so we can connect it later */ - if(b_group_node) { - if(!b_from_node) { - sockets_map[b_from_sock.ptr.data] = - node_socket_map_pair(node_map, b_to_node, b_to_sock); - - continue; - } - else if(!b_to_node) { - sockets_map[b_to_sock.ptr.data] = - node_socket_map_pair(node_map, b_from_node, b_from_sock); - - continue; - } - } - SocketPair from_pair, to_pair; + /* links without a node pointer are connections to group inputs/outputs */ + /* from sock */ - if(b_from_node.is_a(&RNA_NodeGroup)) { - /* group node */ - BL::NodeSocket group_sock = b_from_sock.group_socket(); - from_pair = node_groups[b_from_node.ptr.data][group_sock.ptr.data]; - } - else { - /* regular node */ - from_pair = node_socket_map_pair(node_map, b_from_node, b_from_sock); + if(b_from_node) { + if (b_from_node.is_a(&RNA_NodeGroup)) + from_pair = proxy_map[b_from_sock.ptr.data]; + else + from_pair = node_socket_map_pair(node_map, b_from_node, b_from_sock); } + else + from_pair = sockets_map[b_from_sock.ptr.data]; /* to sock */ - if(b_to_node.is_a(&RNA_NodeGroup)) { - /* group node */ - BL::NodeSocket group_sock = b_to_sock.group_socket(); - to_pair = node_groups[b_to_node.ptr.data][group_sock.ptr.data]; - } - else { - /* regular node */ - to_pair = node_socket_map_pair(node_map, b_to_node, b_to_sock); + if(b_to_node) { + if (b_to_node.is_a(&RNA_NodeGroup)) + to_pair = proxy_map[b_to_sock.ptr.data]; + else + to_pair = node_socket_map_pair(node_map, b_to_node, b_to_sock); } + else + to_pair = sockets_map[b_to_sock.ptr.data]; - /* in case of groups there may not actually be a node inside the group - that the group socket connects to, so from_node or to_node may be NULL */ - if(from_pair.first && to_pair.first) { - ShaderOutput *output = from_pair.first->output(from_pair.second.c_str()); - ShaderInput *input = to_pair.first->input(to_pair.second.c_str()); + ShaderOutput *output = from_pair.first->output(from_pair.second.c_str()); + ShaderInput *input = to_pair.first->input(to_pair.second.c_str()); - graph->connect(output, input); - } + graph->connect(output, input); } } @@ -613,7 +627,7 @@ void BlenderSync::sync_materials() PtrSockMap sock_to_node; BL::ShaderNodeTree b_ntree(b_mat->node_tree()); - add_nodes(b_data, graph, b_ntree, NULL, sock_to_node); + add_nodes(b_data, graph, b_ntree, sock_to_node); } else { ShaderNode *closure, *out; @@ -654,7 +668,7 @@ void BlenderSync::sync_world() PtrSockMap sock_to_node; BL::ShaderNodeTree b_ntree(b_world.node_tree()); - add_nodes(b_data, graph, b_ntree, NULL, sock_to_node); + add_nodes(b_data, graph, b_ntree, sock_to_node); } else if(b_world) { ShaderNode *closure, *out; @@ -703,7 +717,7 @@ void BlenderSync::sync_lamps() PtrSockMap sock_to_node; BL::ShaderNodeTree b_ntree(b_lamp->node_tree()); - add_nodes(b_data, graph, b_ntree, NULL, sock_to_node); + add_nodes(b_data, graph, b_ntree, sock_to_node); } else { ShaderNode *closure, *out; diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index cdded403cbe..cc29047f048 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -292,6 +292,42 @@ void ShaderGraph::copy_nodes(set<ShaderNode*>& nodes, map<ShaderNode*, ShaderNod } } +void ShaderGraph::remove_proxy_nodes(vector<bool>& removed) +{ + foreach(ShaderNode *node, nodes) { + ProxyNode *proxy = dynamic_cast<ProxyNode*>(node); + if (proxy) { + ShaderInput *input = proxy->inputs[0]; + ShaderOutput *output = proxy->outputs[0]; + + /* temp. copy of the output links list. + * output->links is modified when we disconnect! + */ + vector<ShaderInput*> links(output->links); + ShaderOutput *from = input->link; + + /* bypass the proxy node */ + if (from) { + disconnect(input); + foreach(ShaderInput *to, links) { + disconnect(to); + connect(from, to); + } + } + else { + foreach(ShaderInput *to, links) { + disconnect(to); + + /* transfer the default input value to the target socket */ + to->set(input->value); + } + } + + removed[proxy->id] = true; + } + } +} + void ShaderGraph::break_cycles(ShaderNode *node, vector<bool>& visited, vector<bool>& on_stack) { visited[node->id] = true; @@ -322,15 +358,28 @@ void ShaderGraph::clean() nodes that don't feed into the output. how cycles are broken is undefined, they are invalid input, the important thing is to not crash */ + vector<bool> removed(nodes.size(), false); vector<bool> visited(nodes.size(), false); vector<bool> on_stack(nodes.size(), false); + + list<ShaderNode*> newnodes; + + /* remove proxy nodes */ + remove_proxy_nodes(removed); + + foreach(ShaderNode *node, nodes) { + if(!removed[node->id]) + newnodes.push_back(node); + else + delete node; + } + nodes = newnodes; + newnodes.clear(); /* break cycles */ break_cycles(output(), visited, on_stack); /* remove unused nodes */ - list<ShaderNode*> newnodes; - foreach(ShaderNode *node, nodes) { if(visited[node->id]) newnodes.push_back(node); diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h index 2a2df5e1377..91ec83aba21 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/render/graph.h @@ -217,6 +217,7 @@ protected: void find_dependencies(set<ShaderNode*>& dependencies, ShaderInput *input); void copy_nodes(set<ShaderNode*>& nodes, map<ShaderNode*, ShaderNode*>& nnodemap); + void remove_proxy_nodes(vector<bool>& removed); void break_cycles(ShaderNode *node, vector<bool>& visited, vector<bool>& on_stack); void clean(); void bump_from_displacement(); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 5cabb248709..7d873221cd6 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -869,6 +869,26 @@ void ConvertNode::compile(OSLCompiler& compiler) assert(0); } +/* Proxy */ + +ProxyNode::ProxyNode(ShaderSocketType from_, ShaderSocketType to_) +: ShaderNode("proxy") +{ + from = from_; + to = to_; + + add_input("Input", from); + add_output("Output", to); +} + +void ProxyNode::compile(SVMCompiler& compiler) +{ +} + +void ProxyNode::compile(OSLCompiler& compiler) +{ +} + /* BSDF Closure */ BsdfNode::BsdfNode() diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 28fff22fc34..f0a669f3f87 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -158,6 +158,14 @@ public: ShaderSocketType from, to; }; +class ProxyNode : public ShaderNode { +public: + ProxyNode(ShaderSocketType from, ShaderSocketType to); + SHADER_NODE_BASE_CLASS(ProxyNode) + + ShaderSocketType from, to; +}; + class BsdfNode : public ShaderNode { public: SHADER_NODE_CLASS(BsdfNode) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index c588dc9c8ac..c15758eed1f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -64,9 +64,13 @@ EnumPropertyItem nodetree_type_items[] = { EnumPropertyItem node_socket_type_items[] = { - {SOCK_FLOAT, "VALUE", 0, "Value", ""}, - {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, - {SOCK_RGBA, "RGBA", 0, "RGBA", ""}, + {SOCK_FLOAT, "VALUE", 0, "Value", ""}, + {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, + {SOCK_RGBA, "RGBA", 0, "RGBA", ""}, + {SOCK_SHADER, "SHADER", 0, "Shader", ""}, + {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""}, + {SOCK_MESH, "MESH", 0, "Mesh", ""}, + {SOCK_INT, "INT", 0, "Int", ""}, {0, NULL, 0, NULL, NULL}}; EnumPropertyItem node_math_items[] = { |