Generalized node groups for Cycles.
authorLukas Toenne <lukas.toenne@googlemail.com>
Sun, 18 Dec 2011 15:34:06 +0000 (15:34 +0000)
committerLukas Toenne <lukas.toenne@googlemail.com>
Sun, 18 Dec 2011 15:34:06 +0000 (15:34 +0000)
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.

intern/cycles/blender/blender_shader.cpp
intern/cycles/render/graph.cpp
intern/cycles/render/graph.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
source/blender/makesrna/intern/rna_nodetree.c

index ecc85a92ae03a5e10de0cc75c81eeaf2dfcc1801..5c39a3f169d132677f97a0b3df59672510b0beac 100644 (file)
@@ -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;
index cdded403cbe0d72bd5db91d10e04c1fd78220c1c..cc29047f0487cad59dd33afe311e0cea216383bb 100644 (file)
@@ -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);
index 2a2df5e1377c5e8c1b43d64f0c4456d6905ee1fe..91ec83aba210728b0a553656d572c95553516092 100644 (file)
@@ -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();
index 5cabb2487095400dc3dc0c7688939b08703de432..7d873221cd6ece18cda70d8052db7cd979444442 100644 (file)
@@ -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()
index 28fff22fc342e2dc7c8ccf17bb494192d54fa8aa..f0a669f3f8761f74f44abeb90ddf963dbde35e63 100644 (file)
@@ -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)
index c588dc9c8acd1fa56a0cce24874bab9fef5f6382..c15758eed1f23ced067b39678d6e52c4049d1e23 100644 (file)
@@ -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[] = {