Render: make Cycles and Evee support each other's output material nodes.

This changes the Cycles exporting and Cycles/Eevee UI code to support both
output material nodes, giving priority to the renderer native one. Still
missing is Eevee code to prefer the Eevee output node.
This commit is contained in:
Brecht Van Lommel 2017-08-01 18:03:16 +02:00
parent 110d6832a8
commit c42c129393
7 changed files with 85 additions and 66 deletions

@ -899,19 +899,22 @@ class CYCLES_OT_use_shading_nodes(Operator):
return {'FINISHED'}
def panel_node_draw(layout, id_data, output_type, input_name):
def panel_node_draw(layout, id_data, output_types, input_name):
if not id_data.use_nodes:
layout.operator("cycles.use_shading_nodes", icon='NODETREE')
return False
ntree = id_data.node_tree
node = find_output_node(ntree, output_type)
if not node:
layout.label(text="No output node")
else:
node = find_output_node(ntree, output_types)
if node:
input = find_node_input(node, input_name)
layout.template_node_view(ntree, node, input)
if input:
layout.template_node_view(ntree, node, input)
else:
layout.label(text="Incompatible output node")
else:
layout.label(text="No output node")
return True
@ -1000,7 +1003,7 @@ class CyclesLamp_PT_nodes(CyclesButtonsPanel, Panel):
layout = self.layout
lamp = context.lamp
if not panel_node_draw(layout, lamp, 'OUTPUT_LAMP', 'Surface'):
if not panel_node_draw(layout, lamp, ['OUTPUT_LAMP'], 'Surface'):
layout.prop(lamp, "color")
@ -1055,7 +1058,7 @@ class CyclesWorld_PT_surface(CyclesButtonsPanel, Panel):
world = context.world
if not panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Surface'):
if not panel_node_draw(layout, world, ['OUTPUT_WORLD'], 'Surface'):
layout.prop(world, "horizon_color", text="Color")
@ -1073,7 +1076,7 @@ class CyclesWorld_PT_volume(CyclesButtonsPanel, Panel):
layout = self.layout
world = context.world
panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume')
panel_node_draw(layout, world, ['OUTPUT_WORLD'], 'Volume')
class CyclesWorld_PT_ambient_occlusion(CyclesButtonsPanel, Panel):
@ -1218,7 +1221,7 @@ class CyclesMaterial_PT_surface(CyclesButtonsPanel, Panel):
layout = self.layout
mat = context.material
if not panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Surface'):
if not panel_node_draw(layout, mat, ['OUTPUT_MATERIAL', 'OUTPUT_EEVEE_MATERIAL'], 'Surface'):
layout.prop(mat, "diffuse_color")
@ -1238,7 +1241,7 @@ class CyclesMaterial_PT_volume(CyclesButtonsPanel, Panel):
mat = context.material
# cmat = mat.cycles
panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Volume')
panel_node_draw(layout, mat, ['OUTPUT_MATERIAL', 'OUTPUT_EEVEE_MATERIAL'], 'Volume')
class CyclesMaterial_PT_displacement(CyclesButtonsPanel, Panel):
@ -1254,7 +1257,7 @@ class CyclesMaterial_PT_displacement(CyclesButtonsPanel, Panel):
layout = self.layout
mat = context.material
panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Displacement')
panel_node_draw(layout, mat, ['OUTPUT_MATERIAL', 'OUTPUT_EEVEE_MATERIAL'], 'Displacement')
class CyclesMaterial_PT_settings(CyclesButtonsPanel, Panel):

@ -231,14 +231,6 @@ static void get_tex_mapping(TextureMapping *mapping,
mapping->max = get_float3(b_mapping.max());
}
static bool is_output_node(BL::Node& b_node)
{
return (b_node.is_a(&RNA_ShaderNodeOutputMaterial)
|| b_node.is_a(&RNA_ShaderNodeOutputWorld)
|| b_node.is_a(&RNA_ShaderNodeOutputLamp)
|| b_node.is_a(&RNA_ShaderNodeOutputEeveeMaterial));
}
static ShaderNode *add_node(Scene *scene,
BL::RenderEngine& b_engine,
BL::BlendData& b_data,
@ -951,6 +943,42 @@ static ShaderOutput *node_find_output_by_name(ShaderNode *node,
return node->output(name.c_str());
}
static BL::ShaderNode find_output_node(BL::ShaderNodeTree& b_ntree)
{
BL::ShaderNodeTree::nodes_iterator b_node;
BL::ShaderNode output_node(PointerRNA_NULL);
BL::ShaderNode eevee_output_node(PointerRNA_NULL);
for(b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) {
BL::ShaderNodeOutputMaterial b_output_node(*b_node);
if (b_output_node.is_a(&RNA_ShaderNodeOutputMaterial) ||
b_output_node.is_a(&RNA_ShaderNodeOutputWorld) ||
b_output_node.is_a(&RNA_ShaderNodeOutputLamp)) {
/* regular Cycles output node */
if(b_output_node.is_active_output()) {
output_node = b_output_node;
break;
}
else if(!output_node.ptr.data) {
output_node = b_output_node;
}
}
else if (b_output_node.is_a(&RNA_ShaderNodeOutputEeveeMaterial)) {
/* Eevee output used if no Cycles node exists */
if(b_output_node.is_active_output()) {
eevee_output_node = b_output_node;
}
else if(!eevee_output_node.ptr.data) {
eevee_output_node = b_output_node;
}
}
}
return (output_node.ptr.data) ? output_node : eevee_output_node;
}
static void add_nodes(Scene *scene,
BL::RenderEngine& b_engine,
BL::BlendData& b_data,
@ -971,23 +999,7 @@ static void add_nodes(Scene *scene,
BL::Node::outputs_iterator b_output;
/* find the node to use for output if there are multiple */
bool found_active_output = false;
BL::ShaderNode output_node(PointerRNA_NULL);
for(b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) {
if(is_output_node(*b_node)) {
BL::ShaderNodeOutputMaterial b_output_node(*b_node);
if(b_output_node.is_active_output()) {
output_node = b_output_node;
found_active_output = true;
break;
}
else if(!output_node.ptr.data && !found_active_output) {
output_node = b_output_node;
}
}
}
BL::ShaderNode output_node = find_output_node(b_ntree);
/* add nodes */
for(b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) {
@ -1085,10 +1097,8 @@ static void add_nodes(Scene *scene,
else {
ShaderNode *node = NULL;
if(is_output_node(*b_node)) {
if(b_node->ptr.data == output_node.ptr.data) {
node = graph->output();
}
if(b_node->ptr.data == output_node.ptr.data) {
node = graph->output();
}
else {
BL::ShaderNode b_shader_node(*b_node);

@ -32,16 +32,19 @@ def find_node_input(node, name):
return None
# Return the output node to display in the UI
def find_output_node(ntree, nodetype):
# Return the output node to display in the UI. In case multiple node types are
# specified, node types earlier in the list get priority.
def find_output_node(ntree, nodetypes):
if ntree:
active_output_node = None
for node in ntree.nodes:
if getattr(node, "type", None) == nodetype:
if getattr(node, "is_active_output", True):
return node
if not active_output_node:
active_output_node = node
return active_output_node
output_node = None
for nodetype in nodetypes:
for node in ntree.nodes:
if getattr(node, "type", None) == nodetype:
if getattr(node, "is_active_output", True):
return node
if not output_node:
output_node = node
if output_node:
return output_node
return None

@ -1120,10 +1120,12 @@ def panel_node_draw(layout, ntree, output_type):
if node:
input = find_node_input(node, 'Surface')
layout.template_node_view(ntree, node, input)
return True
return False
if input:
layout.template_node_view(ntree, node, input)
else:
layout.label(text="Incompatible output node")
else:
layout.label(text="No output node")
class EEVEE_MATERIAL_PT_surface(MaterialButtonsPanel, Panel):
@ -1145,8 +1147,7 @@ class EEVEE_MATERIAL_PT_surface(MaterialButtonsPanel, Panel):
layout.separator()
if mat.use_nodes:
if not panel_node_draw(layout, mat.node_tree, 'OUTPUT_EEVEE_MATERIAL'):
layout.label(text="No output node")
panel_node_draw(layout, mat.node_tree, ['OUTPUT_EEVEE_MATERIAL', 'OUTPUT_MATERIAL'])
else:
raym = mat.raytrace_mirror
layout.prop(mat, "diffuse_color", text="Base Color")

@ -270,13 +270,16 @@ class EEVEE_WORLD_PT_surface(WorldButtonsPanel, Panel):
if world.use_nodes:
ntree = world.node_tree
node = find_output_node(ntree, 'OUTPUT_WORLD')
node = find_output_node(ntree, ['OUTPUT_WORLD'])
if not node:
layout.label(text="No output node")
else:
if node:
input = find_node_input(node, 'Surface')
layout.template_node_view(ntree, node, input)
if input:
layout.template_node_view(ntree, node, input)
else:
layout.label(text="Incompatible output node")
else:
layout.label(text="No output node")
else:
layout.prop(world, "horizon_color", text="Color")

@ -214,6 +214,9 @@ static void ntree_shader_link_builtin_normal(bNodeTree *ntree,
* render engines works but it's how the GPU shader compilation works. This we
* can change in the future and make it a generic function, but for now it stays
* private here.
*
* It also does not yet take into account render engine specific output nodes,
* it should give priority to e.g. the Eevee material output node for Eevee.
*/
static bNode *ntree_shader_output_node(bNodeTree *ntree)
{

@ -42,10 +42,6 @@ static int node_shader_gpu_output_material(GPUMaterial *mat, bNode *node, bNodeE
{
GPUNodeLink *outlink;
if (BKE_scene_uses_blender_eevee(GPU_material_scene(mat))) {
return false;
}
GPU_stack_link(mat, node, "node_output_material", in, out, &outlink);
GPU_material_output_link(mat, outlink);