diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2019-03-05 19:24:31 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2019-03-05 19:24:39 +0300 |
commit | d1a744173e73ddb86b3ad0f0fb65b89600ad8d55 (patch) | |
tree | 14c0fefe9d6d5a0dc497f07174a54484e69846de /source/blender/nodes/shader | |
parent | 81283b1c81e30623a2c7d97763bfe264c93a0d28 (diff) |
GPUMaterial: Make Shader Output nodes inside nodegroups work
Works as expected and mimics Cycles behavior.
The patch is a bit hacky: In order to not touch the lower level function,
we search for the active output inside groups (recursively) and the first
valid one is then copied (or extracted if you want) in the previous parent
nodetree. So we recursively extract the output node back to the main
nodetree while preserving the links through the nodegroups interfaces.
This way everything works as expected in gpu tree evaluation and bsdf
tagging.
Fix T61869 Material Output Node Inside Node Group Renders Pink in Eevee
Diffstat (limited to 'source/blender/nodes/shader')
-rw-r--r-- | source/blender/nodes/shader/node_shader_tree.c | 114 |
1 files changed, 113 insertions, 1 deletions
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index 26973d619d9..311bf43f3e8 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -36,6 +36,7 @@ #include "BLI_listbase.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_alloca.h" #include "BLT_translation.h" @@ -202,6 +203,113 @@ static void ntree_shader_link_builtin_normal(bNodeTree *ntree, bNode *displacement_node, bNodeSocket *displacement_socket); +static bNodeSocket *ntree_shader_node_find_input(bNode *node, + const char *identifier); + +static bNode *ntree_group_output_node(bNodeTree *ntree); + +static bNode *ntree_shader_relink_output_from_group(bNodeTree *ntree, + bNode *group_node, + bNode *sh_output_node, + int target) +{ + int i; + bNodeTree *group_ntree = (bNodeTree *)group_node->id; + + int sock_len = BLI_listbase_count(&sh_output_node->inputs); + bNodeSocket **group_surface_sockets = BLI_array_alloca(group_surface_sockets, sock_len); + + /* Create output sockets to plug output connection to. */ + i = 0; + for (bNodeSocket *sock = sh_output_node->inputs.first; sock; sock = sock->next, ++i) { + group_surface_sockets[i] = + ntreeAddSocketInterface(group_ntree, + SOCK_OUT, + sock->typeinfo->idname, + sock->name); + } + + bNode *group_output_node = ntree_group_output_node(group_ntree); + + /* If no group output node is present, we need to create one. */ + if (group_output_node == NULL) { + group_output_node = nodeAddStaticNode(NULL, group_ntree, NODE_GROUP_OUTPUT); + } + + /* Need to update tree so all node instances nodes gets proper sockets. */ + node_group_verify(ntree, group_node, &group_ntree->id); + node_group_output_verify(group_ntree, group_output_node, &group_ntree->id); + ntreeUpdateTree(G.main, group_ntree); + + /* Remove other shader output nodes so that only the new one can be selected as active. */ + for (bNode *node = ntree->nodes.first; node; node = node->next) { + if (ELEM(node->type, SH_NODE_OUTPUT_MATERIAL, + SH_NODE_OUTPUT_WORLD, + SH_NODE_OUTPUT_LIGHT)) + { + nodeFreeNode(ntree, node); + } + } + + /* Create new shader output node outside the group. */ + bNode *new_output_node = nodeAddStaticNode(NULL, ntree, sh_output_node->type); + new_output_node->custom1 = target; + + i = 0; + for (bNodeSocket *sock = sh_output_node->inputs.first; sock; sock = sock->next, ++i) { + if (sock->link != NULL) { + /* Link the shader output node incoming link to the group output sockets */ + bNodeSocket *group_output_node_surface_input_sock = nodeFindSocket(group_output_node, + SOCK_IN, + group_surface_sockets[i]->identifier); + nodeAddLink(group_ntree, + sock->link->fromnode, sock->link->fromsock, + group_output_node, group_output_node_surface_input_sock); + + /* Link the group output sockets to the new shader output node. */ + bNodeSocket *group_node_surface_output = nodeFindSocket(group_node, + SOCK_OUT, + group_surface_sockets[i]->identifier); + bNodeSocket *output_node_surface_input = ntree_shader_node_find_input(new_output_node, sock->name); + + nodeAddLink(ntree, + group_node, group_node_surface_output, + new_output_node, output_node_surface_input); + } + } + + ntreeUpdateTree(G.main, group_ntree); + ntreeUpdateTree(G.main, ntree); + + return new_output_node; +} + +static bNode *ntree_shader_output_node_from_group(bNodeTree *ntree, int target) +{ + bNode *output_node = NULL; + + /* Search if node groups do not contain valid output nodes (recursively). */ + for (bNode *node = ntree->nodes.first; node; node = node->next) { + if (!ELEM(node->type, NODE_GROUP)){ + continue; + } + if (node->id != NULL) { + output_node = ntree_shader_output_node_from_group((bNodeTree *)node->id, target); + + if (output_node == NULL) { + output_node = ntreeShaderOutputNode((bNodeTree *)node->id, target); + } + + if (output_node != NULL) { + /* Output is inside this group node. Create relink to make the output outside the group. */ + output_node = ntree_shader_relink_output_from_group(ntree, node, output_node, target); + break; + } + } + } + return output_node; +} + /* Find an output node of the shader tree. * * NOTE: it will only return output which is NOT in the group, which isn't how @@ -694,9 +802,13 @@ void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tag /* This one needs to work on a local tree. */ void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat, bool *has_surface_output, bool *has_volume_output) { - bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE); bNodeTreeExec *exec; + /* Extract output nodes from inside nodegroups. */ + ntree_shader_output_node_from_group(localtree, SHD_OUTPUT_EEVEE); + + bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE); + ntree_shader_groups_expand_inputs(localtree); /* Perform all needed modifications on the tree in order to support |