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.
This commit is contained in:
Lukas Toenne
2011-12-18 15:34:06 +00:00
parent b49463c439
commit 5f6bd44c82
6 changed files with 200 additions and 104 deletions

View 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;