2016-12-13 20:06:23 +01:00
|
|
|
#
|
|
|
|
# This module builds upon Cycles nodes work licensed as
|
|
|
|
# Copyright 2011-2013 Blender Foundation
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
#
|
2017-04-22 15:08:44 +02:00
|
|
|
import arm.material.cycles_functions as c_functions
|
|
|
|
import arm.material.cycles_state as c_state
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2017-05-24 11:04:15 +02:00
|
|
|
basecol_texname = ''
|
2017-06-26 23:53:27 +02:00
|
|
|
emission_found = False
|
2017-05-24 11:04:15 +02:00
|
|
|
|
2017-05-25 16:48:41 +02:00
|
|
|
def parse(nodes, con, vert, frag, geom, tesc, tese, parse_surface=True, parse_opacity=True, parse_displacement=True, basecol_only=False):
|
2016-12-13 11:42:00 +01:00
|
|
|
output_node = node_by_type(nodes, 'OUTPUT_MATERIAL')
|
|
|
|
if output_node != None:
|
2017-05-25 16:48:41 +02:00
|
|
|
parse_output(output_node, con, vert, frag, geom, tesc, tese, parse_surface, parse_opacity, parse_displacement, basecol_only)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2017-05-25 16:48:41 +02:00
|
|
|
def parse_output(node, _con, _vert, _frag, _geom, _tesc, _tese, _parse_surface, _parse_opacity, _parse_displacement, _basecol_only):
|
2016-12-17 15:34:43 +01:00
|
|
|
global parsed # Compute nodes only once
|
2016-12-13 11:42:00 +01:00
|
|
|
global parents
|
2016-12-15 22:31:20 +01:00
|
|
|
global normal_written # Normal socket is linked on shader node - overwrite fs normal
|
2016-12-17 15:34:43 +01:00
|
|
|
global curshader # Active shader - frag for surface / tese for displacement
|
2017-05-25 16:48:41 +02:00
|
|
|
global con
|
2016-12-13 20:06:23 +01:00
|
|
|
global vert
|
|
|
|
global frag
|
2016-12-17 15:34:43 +01:00
|
|
|
global geom
|
|
|
|
global tesc
|
|
|
|
global tese
|
2016-12-20 00:39:18 +01:00
|
|
|
global parse_surface
|
|
|
|
global parse_opacity
|
2016-12-21 19:15:51 +01:00
|
|
|
global parsing_basecol
|
2017-01-08 13:21:54 +01:00
|
|
|
global parse_teximage_vector
|
2017-04-26 14:21:22 +02:00
|
|
|
global basecol_only
|
2017-05-24 11:04:15 +02:00
|
|
|
global basecol_texname
|
2017-06-26 23:53:27 +02:00
|
|
|
global emission_found
|
2017-05-25 16:48:41 +02:00
|
|
|
con = _con
|
2016-12-13 20:06:23 +01:00
|
|
|
vert = _vert
|
|
|
|
frag = _frag
|
2016-12-17 15:34:43 +01:00
|
|
|
geom = _geom
|
|
|
|
tesc = _tesc
|
|
|
|
tese = _tese
|
2016-12-20 00:39:18 +01:00
|
|
|
parse_surface = _parse_surface
|
|
|
|
parse_opacity = _parse_opacity
|
2016-12-21 19:15:51 +01:00
|
|
|
parsing_basecol = False
|
2017-01-08 13:21:54 +01:00
|
|
|
parse_teximage_vector = True
|
2017-04-26 14:21:22 +02:00
|
|
|
basecol_only = _basecol_only
|
2017-05-24 11:04:15 +02:00
|
|
|
basecol_texname = ''
|
2017-06-26 23:53:27 +02:00
|
|
|
emission_found = False
|
2016-12-17 15:34:43 +01:00
|
|
|
|
|
|
|
# Surface
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface or parse_opacity:
|
2016-12-17 15:34:43 +01:00
|
|
|
parsed = []
|
|
|
|
parents = []
|
|
|
|
normal_written = False
|
|
|
|
curshader = frag
|
2017-04-26 14:21:22 +02:00
|
|
|
|
|
|
|
if basecol_only:
|
|
|
|
frag.lock = True
|
2016-12-20 00:39:18 +01:00
|
|
|
|
|
|
|
out_basecol, out_roughness, out_metallic, out_occlusion, out_opacity = parse_shader_input(node.inputs[0])
|
|
|
|
if parse_surface:
|
2017-04-26 14:21:22 +02:00
|
|
|
frag.lock = False
|
2016-12-20 00:39:18 +01:00
|
|
|
frag.write('basecol = {0};'.format(out_basecol))
|
2017-04-26 14:21:22 +02:00
|
|
|
if not basecol_only:
|
|
|
|
frag.write('roughness = {0};'.format(out_roughness))
|
|
|
|
frag.write('metallic = {0};'.format(out_metallic))
|
|
|
|
frag.write('occlusion = {0};'.format(out_occlusion))
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_opacity:
|
|
|
|
frag.write('opacity = {0};'.format(out_opacity))
|
2016-12-17 15:34:43 +01:00
|
|
|
|
|
|
|
# Volume
|
|
|
|
# parse_volume_input(node.inputs[1])
|
|
|
|
|
|
|
|
# Displacement
|
2017-04-26 14:21:22 +02:00
|
|
|
if _parse_displacement and c_state.tess_enabled() and node.inputs[2].is_linked and tese != None:
|
2016-12-17 15:34:43 +01:00
|
|
|
parsed = []
|
|
|
|
parents = []
|
|
|
|
normal_written = False
|
|
|
|
curshader = tese
|
|
|
|
|
|
|
|
out_disp = parse_displacement_input(node.inputs[2])
|
|
|
|
tese.write('float disp = {0};'.format(out_disp))
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
def parse_group(node, socket): # Entering group
|
|
|
|
index = socket_index(node, socket)
|
|
|
|
output_node = node_by_type(node.node_tree.nodes, 'GROUP_OUTPUT')
|
|
|
|
if output_node == None:
|
|
|
|
return
|
|
|
|
inp = output_node.inputs[index]
|
|
|
|
parents.append(node)
|
|
|
|
out_group = parse_input(inp)
|
|
|
|
parents.pop()
|
|
|
|
return out_group
|
|
|
|
|
2017-01-04 00:13:52 +01:00
|
|
|
def parse_group_input(node, socket):
|
2016-12-13 11:42:00 +01:00
|
|
|
index = socket_index(node, socket)
|
2017-01-04 00:13:52 +01:00
|
|
|
parent = parents.pop() # Leaving group
|
2016-12-13 11:42:00 +01:00
|
|
|
inp = parent.inputs[index]
|
2017-01-04 00:13:52 +01:00
|
|
|
res = parse_input(inp)
|
|
|
|
parents.append(parent) # Return to group
|
|
|
|
return res
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
def parse_input(inp):
|
|
|
|
if inp.type == 'SHADER':
|
|
|
|
return parse_shader_input(inp)
|
|
|
|
elif inp.type == 'RGB':
|
2016-12-13 20:06:23 +01:00
|
|
|
return parse_vector_input(inp)
|
2016-12-13 11:42:00 +01:00
|
|
|
elif inp.type == 'RGBA':
|
2016-12-13 20:06:23 +01:00
|
|
|
return parse_vector_input(inp)
|
2016-12-13 11:42:00 +01:00
|
|
|
elif inp.type == 'VECTOR':
|
|
|
|
return parse_vector_input(inp)
|
|
|
|
elif inp.type == 'VALUE':
|
|
|
|
return parse_value_input(inp)
|
|
|
|
|
|
|
|
def parse_shader_input(inp):
|
|
|
|
if inp.is_linked:
|
|
|
|
l = inp.links[0]
|
2016-12-15 00:18:59 +01:00
|
|
|
|
|
|
|
if l.from_node.type == 'REROUTE':
|
|
|
|
return parse_shader_input(l.from_node.inputs[0])
|
|
|
|
|
2016-12-13 11:42:00 +01:00
|
|
|
return parse_shader(l.from_node, l.from_socket)
|
|
|
|
else:
|
2016-12-13 20:06:23 +01:00
|
|
|
out_basecol = 'vec3(0.8)'
|
2016-12-13 11:42:00 +01:00
|
|
|
out_roughness = '0.0'
|
|
|
|
out_metallic = '0.0'
|
2016-12-13 20:06:23 +01:00
|
|
|
out_occlusion = '1.0'
|
2016-12-20 00:39:18 +01:00
|
|
|
out_opacity = '1.0'
|
|
|
|
return out_basecol, out_roughness, out_metallic, out_occlusion, out_opacity
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2016-12-15 22:31:20 +01:00
|
|
|
def write_normal(inp):
|
|
|
|
if inp.is_linked:
|
2016-12-17 23:48:18 +01:00
|
|
|
normal_res = parse_vector_input(inp)
|
|
|
|
if normal_res != None:
|
|
|
|
curshader.write('n = {0};'.format(normal_res))
|
|
|
|
normal_written = True
|
2016-12-15 22:31:20 +01:00
|
|
|
|
2017-04-26 14:21:22 +02:00
|
|
|
def parsing_basecolor(b):
|
|
|
|
global parsing_basecol
|
|
|
|
global basecol_only
|
|
|
|
global curshader
|
|
|
|
parsing_basecol = b
|
|
|
|
if basecol_only:
|
|
|
|
curshader.lock = not b
|
|
|
|
|
2016-12-13 11:42:00 +01:00
|
|
|
def parse_shader(node, socket):
|
2016-12-21 19:15:51 +01:00
|
|
|
global parsing_basecol
|
2017-08-06 17:36:25 +02:00
|
|
|
global emission_found
|
2016-12-13 20:06:23 +01:00
|
|
|
out_basecol = 'vec3(0.8)'
|
|
|
|
out_roughness = '0.0'
|
|
|
|
out_metallic = '0.0'
|
|
|
|
out_occlusion = '1.0'
|
2016-12-20 00:39:18 +01:00
|
|
|
out_opacity = '1.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.type == 'GROUP':
|
2016-12-13 11:42:00 +01:00
|
|
|
if node.node_tree.name.startswith('Armory PBR'):
|
2016-12-20 00:39:18 +01:00
|
|
|
|
|
|
|
if parse_surface:
|
2017-07-07 18:21:44 +02:00
|
|
|
|
|
|
|
if len(node.inputs) != 14:
|
|
|
|
# Base color
|
|
|
|
parsing_basecolor(True)
|
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
|
|
|
parsing_basecolor(False)
|
|
|
|
# Occlusion
|
|
|
|
out_occlusion = parse_value_input(node.inputs[2])
|
|
|
|
# Roughness
|
|
|
|
out_roughness = parse_value_input(node.inputs[3])
|
|
|
|
# Metallic
|
|
|
|
out_metallic = parse_value_input(node.inputs[4])
|
|
|
|
# Normal
|
|
|
|
if node.inputs[5].is_linked and node.inputs[5].links[0].from_node.type == 'NORMAL_MAP':
|
|
|
|
c_state.warn(c_state.mat_name() + ' - Do not use Normal Map node with Armory PBR, connect Image Texture directly')
|
2017-07-09 19:44:19 +02:00
|
|
|
parse_normal_map_color_input(node.inputs[5])
|
2017-07-07 18:21:44 +02:00
|
|
|
# Emission
|
2017-08-06 17:36:25 +02:00
|
|
|
if node.inputs[6].is_linked or node.inputs[6].default_value != 0.0:
|
2017-07-09 19:44:19 +02:00
|
|
|
out_emission = parse_value_input(node.inputs[6])
|
2017-07-07 18:21:44 +02:00
|
|
|
emission_found = True
|
2017-07-09 19:44:19 +02:00
|
|
|
out_basecol = '({0} + vec3({1} * 100.0))'.format(out_basecol, out_emission)
|
2017-07-07 18:21:44 +02:00
|
|
|
|
|
|
|
else: # Deprecated
|
|
|
|
# Base color
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(True)
|
2017-07-07 18:21:44 +02:00
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(False)
|
2017-07-07 18:21:44 +02:00
|
|
|
# Occlusion TODO: deprecated, occlussion is value instead of vector now
|
|
|
|
if node.inputs[1].type == 'RGBA':
|
|
|
|
out_occlusion = '{0}.r'.format(parse_vector_input(node.inputs[1]))
|
|
|
|
else:
|
|
|
|
out_occlusion = parse_value_input(node.inputs[1])
|
|
|
|
if node.inputs[2].is_linked or node.inputs[2].default_value != 1.0:
|
|
|
|
out_occlusion = '({0} * {1})'.format(out_occlusion, parse_value_input(node.inputs[2]))
|
|
|
|
# Roughness
|
|
|
|
out_roughness = parse_value_input(node.inputs[3])
|
|
|
|
if node.inputs[4].is_linked or node.inputs[4].default_value != 1.0:
|
|
|
|
out_roughness = '({0} * {1})'.format(out_roughness, parse_value_input(node.inputs[4]))
|
|
|
|
# Metallic
|
|
|
|
out_metallic = parse_value_input(node.inputs[5])
|
|
|
|
# Normal
|
|
|
|
if node.inputs[6].is_linked and node.inputs[6].links[0].from_node.type == 'NORMAL_MAP':
|
|
|
|
c_state.warn(c_state.mat_name() + ' - Do not use Normal Map node with Armory PBR, connect Image Texture directly')
|
2017-07-09 19:44:19 +02:00
|
|
|
parse_normal_map_color_input(node.inputs[6])
|
2017-07-07 18:21:44 +02:00
|
|
|
# Emission
|
|
|
|
if node.inputs[8].is_linked:
|
|
|
|
parsing_basecolor(True)
|
|
|
|
out_emission = parse_vector_input(node.inputs[8])
|
|
|
|
emission_found = True
|
|
|
|
parsing_basecolor(False)
|
|
|
|
if node.inputs[9].is_linked or node.inputs[9].default_value != 1.0:
|
|
|
|
out_emission = '({0} * {1})'.format(out_emission, parse_value_input(node.inputs[9]))
|
|
|
|
out_basecol = '({0} + {1} * 100.0)'.format(out_basecol, out_emission)
|
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
|
|
|
|
if parse_opacity:
|
2017-07-07 18:21:44 +02:00
|
|
|
|
|
|
|
if len(node.inputs) != 14:
|
|
|
|
out_opacity = parse_value_input(node.inputs[1])
|
|
|
|
else: # Deprecated
|
|
|
|
out_opacity = parse_value_input(node.inputs[12])
|
|
|
|
if node.inputs[13].is_linked or node.inputs[13].default_value != 1.0:
|
|
|
|
out_opacity = '({0} * {1})'.format(out_opacity, parse_value_input(node.inputs[13]))
|
|
|
|
|
2016-12-13 11:42:00 +01:00
|
|
|
else:
|
|
|
|
return parse_group(node, socket)
|
|
|
|
|
|
|
|
elif node.type == 'GROUP_INPUT':
|
2017-01-04 00:13:52 +01:00
|
|
|
return parse_group_input(node, socket)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'MIX_SHADER':
|
2017-03-14 20:43:54 +01:00
|
|
|
prefix = '' if node.inputs[0].is_linked else 'const '
|
2016-12-13 11:42:00 +01:00
|
|
|
fac = parse_value_input(node.inputs[0])
|
2016-12-15 00:18:59 +01:00
|
|
|
fac_var = node_name(node.name) + '_fac'
|
|
|
|
fac_inv_var = node_name(node.name) + '_fac_inv'
|
2017-03-14 20:43:54 +01:00
|
|
|
curshader.write('{0}float {1} = {2};'.format(prefix, fac_var, fac))
|
|
|
|
curshader.write('{0}float {1} = 1.0 - {2};'.format(prefix, fac_inv_var, fac_var))
|
2016-12-20 00:39:18 +01:00
|
|
|
bc1, rough1, met1, occ1, opac1 = parse_shader_input(node.inputs[1])
|
|
|
|
bc2, rough2, met2, occ2, opac2 = parse_shader_input(node.inputs[2])
|
|
|
|
if parse_surface:
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(True)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_basecol = '({0} * {3} + {1} * {2})'.format(bc1, bc2, fac_var, fac_inv_var)
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(False)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_roughness = '({0} * {3} + {1} * {2})'.format(rough1, rough2, fac_var, fac_inv_var)
|
|
|
|
out_metallic = '({0} * {3} + {1} * {2})'.format(met1, met2, fac_var, fac_inv_var)
|
|
|
|
out_occlusion = '({0} * {3} + {1} * {2})'.format(occ1, occ2, fac_var, fac_inv_var)
|
|
|
|
if parse_opacity:
|
|
|
|
out_opacity = '({0} * {3} + {1} * {2})'.format(opac1, opac2, fac_var, fac_inv_var)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'ADD_SHADER':
|
2016-12-20 00:39:18 +01:00
|
|
|
bc1, rough1, met1, occ1, opac1 = parse_shader_input(node.inputs[0])
|
|
|
|
bc2, rough2, met2, occ2, opac2 = parse_shader_input(node.inputs[1])
|
|
|
|
if parse_surface:
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(True)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_basecol = '({0} + {1})'.format(bc1, bc2)
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(False)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_roughness = '({0} * 0.5 + {1} * 0.5)'.format(rough1, rough2)
|
|
|
|
out_metallic = '({0} * 0.5 + {1} * 0.5)'.format(met1, met2)
|
|
|
|
out_occlusion = '({0} * 0.5 + {1} * 0.5)'.format(occ1, occ2)
|
|
|
|
if parse_opacity:
|
|
|
|
out_opacity = '({0} * 0.5 + {1} * 0.5)'.format(opac1, opac2)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2017-06-27 10:55:46 +02:00
|
|
|
elif node.type == 'BSDF_PRINCIPLED':
|
|
|
|
if parse_surface:
|
|
|
|
write_normal(node.inputs[16])
|
|
|
|
parsing_basecolor(True)
|
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
|
|
|
parsing_basecolor(False)
|
|
|
|
out_roughness = parse_value_input(node.inputs[7])
|
|
|
|
out_metallic = parse_value_input(node.inputs[4])
|
|
|
|
# subsurface = parse_vector_input(node.inputs[1])
|
|
|
|
# subsurface_radius = parse_vector_input(node.inputs[2])
|
|
|
|
# subsurface_color = parse_vector_input(node.inputs[3])
|
|
|
|
# specular = parse_vector_input(node.inputs[5])
|
|
|
|
# specular_tint = parse_vector_input(node.inputs[6])
|
|
|
|
# aniso = parse_vector_input(node.inputs[8])
|
|
|
|
# aniso_rot = parse_vector_input(node.inputs[9])
|
|
|
|
# sheen = parse_vector_input(node.inputs[10])
|
|
|
|
# sheen_tint = parse_vector_input(node.inputs[11])
|
|
|
|
# clearcoat = parse_vector_input(node.inputs[12])
|
|
|
|
# clearcoat_rough = parse_vector_input(node.inputs[13])
|
|
|
|
# ior = parse_vector_input(node.inputs[14])
|
|
|
|
# transmission = parse_vector_input(node.inputs[15])
|
|
|
|
|
2016-12-13 11:42:00 +01:00
|
|
|
elif node.type == 'BSDF_DIFFUSE':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
write_normal(node.inputs[2])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(True)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(False)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_roughness = parse_value_input(node.inputs[1])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BSDF_GLOSSY':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
write_normal(node.inputs[2])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(True)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(False)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_roughness = parse_value_input(node.inputs[1])
|
|
|
|
out_metallic = '1.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'AMBIENT_OCCLUSION':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
# Single channel
|
|
|
|
out_occlusion = parse_vector_input(node.inputs[0]) + '.r'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BSDF_ANISOTROPIC':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
write_normal(node.inputs[4])
|
|
|
|
# Revert to glossy
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(True)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(False)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_roughness = parse_value_input(node.inputs[1])
|
|
|
|
out_metallic = '1.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'EMISSION':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
# Multiply basecol
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(True)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(False)
|
2017-06-26 23:53:27 +02:00
|
|
|
emission_found = True
|
2016-12-20 00:39:18 +01:00
|
|
|
strength = parse_value_input(node.inputs[1])
|
2017-06-27 14:10:26 +02:00
|
|
|
out_basecol = '({0} * ({1} * 100.0))'.format(out_basecol, strength)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BSDF_GLASS':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
write_normal(node.inputs[3])
|
|
|
|
out_roughness = parse_value_input(node.inputs[1])
|
|
|
|
if parse_opacity:
|
|
|
|
out_opacity = '(1.0 - {0}.r)'.format(parse_vector_input(node.inputs[0]))
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BSDF_HAIR':
|
|
|
|
pass
|
|
|
|
|
|
|
|
elif node.type == 'HOLDOUT':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
# Occlude
|
|
|
|
out_occlusion = '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BSDF_REFRACTION':
|
2016-12-15 22:31:20 +01:00
|
|
|
# write_normal(node.inputs[3])
|
2016-12-13 11:42:00 +01:00
|
|
|
pass
|
|
|
|
|
|
|
|
elif node.type == 'SUBSURFACE_SCATTERING':
|
2017-05-23 15:01:56 +02:00
|
|
|
if parse_surface:
|
|
|
|
write_normal(node.inputs[4])
|
|
|
|
parsing_basecolor(True)
|
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
|
|
|
parsing_basecolor(False)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BSDF_TOON':
|
2016-12-15 22:31:20 +01:00
|
|
|
# write_normal(node.inputs[3])
|
2016-12-13 11:42:00 +01:00
|
|
|
pass
|
|
|
|
|
|
|
|
elif node.type == 'BSDF_TRANSLUCENT':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
write_normal(node.inputs[1])
|
|
|
|
if parse_opacity:
|
|
|
|
out_opacity = '(1.0 - {0}.r)'.format(parse_vector_input(node.inputs[0]))
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BSDF_TRANSPARENT':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_opacity:
|
|
|
|
out_opacity = '(1.0 - {0}.r)'.format(parse_vector_input(node.inputs[0]))
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BSDF_VELVET':
|
2016-12-20 00:39:18 +01:00
|
|
|
if parse_surface:
|
|
|
|
write_normal(node.inputs[2])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(True)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_basecol = parse_vector_input(node.inputs[0])
|
2017-04-26 14:21:22 +02:00
|
|
|
parsing_basecolor(False)
|
2016-12-20 00:39:18 +01:00
|
|
|
out_roughness = '1.0'
|
|
|
|
out_metallic = '1.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'VOLUME_ABSORPTION':
|
|
|
|
pass
|
|
|
|
|
|
|
|
elif node.type == 'VOLUME_SCATTER':
|
|
|
|
pass
|
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
return out_basecol, out_roughness, out_metallic, out_occlusion, out_opacity
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2016-12-17 15:34:43 +01:00
|
|
|
def parse_displacement_input(inp):
|
|
|
|
if inp.is_linked:
|
|
|
|
l = inp.links[0]
|
|
|
|
|
|
|
|
if l.from_node.type == 'REROUTE':
|
|
|
|
return parse_displacement_input(l.from_node.inputs[0])
|
|
|
|
|
|
|
|
return parse_value_input(inp)
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
def res_var_name(node, socket):
|
2017-05-13 17:17:43 +02:00
|
|
|
return node_name(node.name) + '_' + c_state.safesrc(socket.name) + '_res'
|
2016-12-20 00:39:18 +01:00
|
|
|
|
2016-12-15 14:28:22 +01:00
|
|
|
def write_result(l):
|
2016-12-20 00:39:18 +01:00
|
|
|
res_var = res_var_name(l.from_node, l.from_socket)
|
2016-12-15 14:28:22 +01:00
|
|
|
st = l.from_socket.type
|
|
|
|
if res_var not in parsed:
|
|
|
|
parsed.append(res_var)
|
|
|
|
if st == 'RGB' or st == 'RGBA':
|
|
|
|
res = parse_rgb(l.from_node, l.from_socket)
|
2016-12-17 23:48:18 +01:00
|
|
|
if res == None:
|
|
|
|
return None
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.write('vec3 {0} = {1};'.format(res_var, res))
|
2016-12-15 14:28:22 +01:00
|
|
|
elif st == 'VECTOR':
|
|
|
|
res = parse_vector(l.from_node, l.from_socket)
|
2016-12-17 23:48:18 +01:00
|
|
|
if res == None:
|
|
|
|
return None
|
2016-12-19 01:25:22 +01:00
|
|
|
size = 3
|
|
|
|
if isinstance(res, tuple):
|
|
|
|
size = res[1]
|
|
|
|
res = res[0]
|
|
|
|
curshader.write('vec{2} {0} = {1};'.format(res_var, res, size))
|
2016-12-15 14:28:22 +01:00
|
|
|
elif st == 'VALUE':
|
|
|
|
res = parse_value(l.from_node, l.from_socket)
|
2016-12-17 23:48:18 +01:00
|
|
|
if res == None:
|
|
|
|
return None
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.write('float {0} = {1};'.format(res_var, res))
|
2017-01-17 18:13:54 +01:00
|
|
|
# Normal map already parsed, return
|
|
|
|
elif l.from_node.type == 'NORMAL_MAP':
|
|
|
|
return None
|
2016-12-15 14:28:22 +01:00
|
|
|
return res_var
|
|
|
|
|
2017-03-14 20:43:54 +01:00
|
|
|
def glsltype(t):
|
|
|
|
if t == 'RGB' or t == 'RGBA' or t == 'VECTOR':
|
|
|
|
return 'vec3'
|
|
|
|
else:
|
|
|
|
return 'float'
|
|
|
|
|
|
|
|
def touniform(inp):
|
2017-05-13 17:17:43 +02:00
|
|
|
uname = c_state.safesrc(inp.node.name) + c_state.safesrc(inp.name)
|
2017-03-14 20:43:54 +01:00
|
|
|
curshader.add_uniform(glsltype(inp.type) + ' ' + uname)
|
|
|
|
return uname
|
|
|
|
|
2016-12-13 20:06:23 +01:00
|
|
|
def parse_vector_input(inp):
|
2016-12-13 11:42:00 +01:00
|
|
|
if inp.is_linked:
|
|
|
|
l = inp.links[0]
|
2016-12-15 00:18:59 +01:00
|
|
|
|
|
|
|
if l.from_node.type == 'REROUTE':
|
|
|
|
return parse_vector_input(l.from_node.inputs[0])
|
|
|
|
|
2016-12-15 14:28:22 +01:00
|
|
|
res_var = write_result(l)
|
|
|
|
st = l.from_socket.type
|
|
|
|
if st == 'RGB' or st == 'RGBA' or st == 'VECTOR':
|
|
|
|
return res_var
|
|
|
|
else: # VALUE
|
|
|
|
return 'vec3({0})'.format(res_var)
|
2016-12-13 11:42:00 +01:00
|
|
|
else:
|
2016-12-15 00:18:59 +01:00
|
|
|
if inp.type == 'VALUE': # Unlinked reroute
|
|
|
|
return tovec3([0.0, 0.0, 0.0])
|
|
|
|
else:
|
2017-04-22 15:08:44 +02:00
|
|
|
if c_state.mat_batch() and inp.is_uniform:
|
2017-03-14 20:43:54 +01:00
|
|
|
return touniform(inp)
|
|
|
|
else:
|
|
|
|
return tovec3(inp.default_value)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
def parse_rgb(node, socket):
|
|
|
|
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.type == 'GROUP':
|
2016-12-13 11:42:00 +01:00
|
|
|
return parse_group(node, socket)
|
|
|
|
|
|
|
|
elif node.type == 'GROUP_INPUT':
|
2017-01-04 00:13:52 +01:00
|
|
|
return parse_group_input(node, socket)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'ATTRIBUTE':
|
2016-12-17 23:48:18 +01:00
|
|
|
# Vcols only for now
|
|
|
|
# node.attribute_name
|
2017-05-25 16:48:41 +02:00
|
|
|
con.add_elem('col', 3)
|
2016-12-19 01:25:22 +01:00
|
|
|
return 'vcolor'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'RGB':
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3(socket.default_value)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_BRICK':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass through
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.0, 0.0, 0.0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_CHECKER':
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_tex_checker)
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
2016-12-13 20:06:23 +01:00
|
|
|
col1 = parse_vector_input(node.inputs[1])
|
|
|
|
col2 = parse_vector_input(node.inputs[2])
|
|
|
|
scale = parse_value_input(node.inputs[3])
|
2016-12-15 00:18:59 +01:00
|
|
|
return 'tex_checker({0}, {1}, {2}, {3})'.format(co, col1, col2, scale)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_ENVIRONMENT':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass through
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.0, 0.0, 0.0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_GRADIENT':
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
2016-12-13 20:06:23 +01:00
|
|
|
grad = node.gradient_type
|
|
|
|
if grad == 'LINEAR':
|
2016-12-15 00:18:59 +01:00
|
|
|
f = '{0}.x'.format(co)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif grad == 'QUADRATIC':
|
|
|
|
f = '0.0'
|
|
|
|
elif grad == 'EASING':
|
|
|
|
f = '0.0'
|
|
|
|
elif grad == 'DIAGONAL':
|
2016-12-15 00:18:59 +01:00
|
|
|
f = '({0}.x + {0}.y) * 0.5'.format(co)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif grad == 'RADIAL':
|
2016-12-15 00:18:59 +01:00
|
|
|
f = 'atan({0}.y, {0}.x) / PI2 + 0.5'.format(co)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif grad == 'QUADRATIC_SPHERE':
|
|
|
|
f = '0.0'
|
|
|
|
elif grad == 'SPHERICAL':
|
2016-12-15 00:18:59 +01:00
|
|
|
f = 'max(1.0 - sqrt({0}.x * {0}.x + {0}.y * {0}.y + {0}.z * {0}.z), 0.0)'.format(co)
|
2016-12-13 20:06:23 +01:00
|
|
|
return 'vec3(clamp({0}, 0.0, 1.0))'.format(f)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_IMAGE':
|
2016-12-20 00:39:18 +01:00
|
|
|
# Already fetched
|
|
|
|
if res_var_name(node, node.outputs[1]) in parsed:
|
|
|
|
return '{0}.rgb'.format(store_var_name(node))
|
2017-08-04 11:54:32 +02:00
|
|
|
tex_name = node_name(node.name)
|
2017-04-22 15:08:44 +02:00
|
|
|
tex = c_state.make_texture(node, tex_name)
|
2016-12-15 23:50:21 +01:00
|
|
|
if tex != None:
|
2016-12-21 19:15:51 +01:00
|
|
|
to_linear = parsing_basecol and not tex['file'].endswith('.hdr')
|
|
|
|
return '{0}.rgb'.format(texture_store(node, tex, tex_name, to_linear))
|
2017-02-13 13:23:43 +01:00
|
|
|
elif node.image == None: # Empty texture
|
|
|
|
tex = {}
|
|
|
|
tex['name'] = tex_name
|
|
|
|
tex['file'] = ''
|
|
|
|
return '{0}.rgb'.format(texture_store(node, tex, tex_name, True))
|
2016-12-15 23:50:21 +01:00
|
|
|
else:
|
2017-01-17 15:49:33 +01:00
|
|
|
tex_store = store_var_name(node) # Pink color for missing texture
|
|
|
|
curshader.write('vec4 {0} = vec4(1.0, 0.0, 1.0, 1.0);'.format(tex_store))
|
|
|
|
return '{0}.rgb'.format(tex_store)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_MAGIC':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass through
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.0, 0.0, 0.0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_MUSGRAVE':
|
2016-12-15 00:18:59 +01:00
|
|
|
# Fall back to noise
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_tex_noise)
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
|
|
|
scale = parse_value_input(node.inputs[1])
|
|
|
|
# detail = parse_value_input(node.inputs[2])
|
|
|
|
# distortion = parse_value_input(node.inputs[3])
|
|
|
|
return 'vec3(tex_noise_f({0} * {1}))'.format(co, scale)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_NOISE':
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_tex_noise)
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
|
|
|
scale = parse_value_input(node.inputs[1])
|
2016-12-13 20:06:23 +01:00
|
|
|
# detail = parse_value_input(node.inputs[2])
|
|
|
|
# distortion = parse_value_input(node.inputs[3])
|
2016-12-15 00:18:59 +01:00
|
|
|
# Slow..
|
2017-01-04 00:13:52 +01:00
|
|
|
return 'vec3(tex_noise({0} * {1}), tex_noise({0} * {1} + 0.33), tex_noise({0} * {1} + 0.66))'.format(co, scale)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_POINTDENSITY':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass through
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.0, 0.0, 0.0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_SKY':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass through
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.0, 0.0, 0.0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_VORONOI':
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_tex_voronoi)
|
|
|
|
c_state.assets_add(c_state.get_sdk_path() + '/armory/Assets/' + 'noise64.png')
|
|
|
|
c_state.assets_add_embedded_data('noise64.png')
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.add_uniform('sampler2D snoise', link='_noise64')
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
|
|
|
scale = parse_value_input(node.inputs[1])
|
|
|
|
if node.coloring == 'INTENSITY':
|
|
|
|
return 'vec3(tex_voronoi({0} / {1}).a)'.format(co, scale)
|
|
|
|
else: # CELLS
|
|
|
|
return 'tex_voronoi({0} / {1}).rgb'.format(co, scale)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_WAVE':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass through
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.0, 0.0, 0.0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BRIGHTCONTRAST':
|
2016-12-13 20:06:23 +01:00
|
|
|
out_col = parse_vector_input(node.inputs[0])
|
|
|
|
bright = parse_value_input(node.inputs[1])
|
|
|
|
contr = parse_value_input(node.inputs[2])
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.add_function(\
|
2016-12-13 20:06:23 +01:00
|
|
|
"""vec3 brightcontrast(const vec3 col, const float bright, const float contr) {
|
|
|
|
float a = 1.0 + contr;
|
|
|
|
float b = bright - contr * 0.5;
|
|
|
|
return max(a * col + b, 0.0);
|
|
|
|
}
|
|
|
|
""")
|
|
|
|
return 'brightcontrast({0}, {1}, {2})'.format(out_col, bright, contr)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'GAMMA':
|
2016-12-13 20:06:23 +01:00
|
|
|
out_col = parse_vector_input(node.inputs[0])
|
|
|
|
gamma = parse_value_input(node.inputs[1])
|
|
|
|
return 'pow({0}, vec3({1}))'.format(out_col, gamma)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'HUE_SAT':
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_hsv_to_rgb)
|
2017-03-20 01:56:46 +01:00
|
|
|
hue = parse_value_input(node.inputs[0])
|
|
|
|
sat = parse_value_input(node.inputs[1])
|
|
|
|
val = parse_value_input(node.inputs[2])
|
|
|
|
# fac = parse_value_input(node.inputs[3])
|
|
|
|
# col = parse_vector_input(node.inputs[4])
|
|
|
|
return 'hsv_to_rgb(vec3({0}, {1}, {2}))'.format(hue, sat, val)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'INVERT':
|
2016-12-13 20:06:23 +01:00
|
|
|
fac = parse_value_input(node.inputs[0])
|
|
|
|
out_col = parse_vector_input(node.inputs[1])
|
|
|
|
return 'mix({0}, vec3(1.0) - ({0}), {1})'.format(out_col, fac)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'MIX_RGB':
|
2016-12-13 20:06:23 +01:00
|
|
|
fac = parse_value_input(node.inputs[0])
|
2016-12-15 00:18:59 +01:00
|
|
|
fac_var = node_name(node.name) + '_fac'
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.write('float {0} = {1};'.format(fac_var, fac))
|
2016-12-13 20:06:23 +01:00
|
|
|
col1 = parse_vector_input(node.inputs[1])
|
|
|
|
col2 = parse_vector_input(node.inputs[2])
|
|
|
|
blend = node.blend_type
|
|
|
|
if blend == 'MIX':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'ADD':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {0} + {1}, {2})'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'MULTIPLY':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {0} * {1}, {2})'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'SUBTRACT':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {0} - {1}, {2})'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'SCREEN':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = '(vec3(1.0) - (vec3(1.0 - {2}) + {2} * (vec3(1.0) - {1})) * (vec3(1.0) - {0}))'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'DIVIDE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = '(vec3((1.0 - {2}) * {0} + {2} * {0} / {1}))'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'DIFFERENCE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, abs({0} - {1}), {2})'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'DARKEN':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'min({0}, {1} * {2})'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'LIGHTEN':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'max({0}, {1} * {2})'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'OVERLAY':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'DODGE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'BURN':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'HUE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'SATURATION':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'VALUE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'COLOR':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
|
2016-12-13 20:06:23 +01:00
|
|
|
elif blend == 'SOFT_LIGHT':
|
|
|
|
out_col = '((1.0 - {2}) * {0} + {2} * ((vec3(1.0) - {0}) * {1} * {0} + {0} * (vec3(1.0) - (vec3(1.0) - {1}) * (vec3(1.0) - {0}))));'.format(col1, col2, fac)
|
|
|
|
elif blend == 'LINEAR_LIGHT':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
|
|
|
|
# out_col = '({0} + {2} * (2.0 * ({1} - vec3(0.5))))'.format(col1, col2, fac_var)
|
2016-12-13 20:06:23 +01:00
|
|
|
if node.use_clamp:
|
|
|
|
return 'clamp({0}, vec3(0.0), vec3(1.0))'.format(out_col)
|
|
|
|
else:
|
|
|
|
return out_col
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'CURVE_RGB':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass throuh
|
|
|
|
return parse_vector_input(node.inputs[1])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BLACKBODY':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass constant
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.84, 0.38, 0.0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2016-12-13 20:06:23 +01:00
|
|
|
elif node.type == 'VALTORGB': # ColorRamp
|
|
|
|
fac = parse_value_input(node.inputs[0])
|
2016-12-15 00:18:59 +01:00
|
|
|
interp = node.color_ramp.interpolation
|
2016-12-13 20:06:23 +01:00
|
|
|
elems = node.color_ramp.elements
|
|
|
|
if len(elems) == 1:
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3(elems[0].color)
|
|
|
|
if interp == 'CONSTANT':
|
|
|
|
fac_var = node_name(node.name) + '_fac'
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.write('float {0} = {1};'.format(fac_var, fac))
|
2016-12-15 00:18:59 +01:00
|
|
|
# Get index
|
|
|
|
out_i = '0'
|
|
|
|
for i in range(1, len(elems)):
|
2016-12-15 14:28:22 +01:00
|
|
|
out_i += ' + ({0} > {1} ? 1 : 0)'.format(fac_var, elems[i].position)
|
2016-12-15 00:18:59 +01:00
|
|
|
# Write cols array
|
|
|
|
cols_var = node_name(node.name) + '_cols'
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.write('vec3 {0}[{1}];'.format(cols_var, len(elems)))
|
2016-12-15 00:18:59 +01:00
|
|
|
for i in range(0, len(elems)):
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.write('{0}[{1}] = vec3({2}, {3}, {4});'.format(cols_var, i, elems[i].color[0], elems[i].color[1], elems[i].color[2]))
|
2016-12-15 00:18:59 +01:00
|
|
|
return '{0}[{1}]'.format(cols_var, out_i)
|
|
|
|
else: # Linear, .. - 2 elems only, end pos assumed to be 1
|
|
|
|
# float f = clamp((pos - start) * (1.0 / (1.0 - start)), 0.0, 1.0);
|
|
|
|
return 'mix({0}, {1}, clamp(({2} - {3}) * (1.0 / (1.0 - {3})), 0.0, 1.0))'.format(tovec3(elems[0].color), tovec3(elems[1].color), fac, elems[0].position)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'COMBHSV':
|
2016-12-13 20:06:23 +01:00
|
|
|
# vec3 hsv2rgb(vec3 c) {
|
|
|
|
# vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
|
|
# vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
|
|
# return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
|
|
# }
|
|
|
|
# vec3 rgb2hsv(vec3 c) {
|
|
|
|
# vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
|
|
|
# vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
|
|
|
# vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
|
|
|
|
|
|
|
# float d = q.x - min(q.w, q.y);
|
|
|
|
# float e = 1.0e-10;
|
|
|
|
# return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
|
|
|
# }
|
|
|
|
# Pass constant
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.0, 0.0, 0.0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'COMBRGB':
|
2016-12-13 20:06:23 +01:00
|
|
|
r = parse_value_input(node.inputs[0])
|
|
|
|
g = parse_value_input(node.inputs[1])
|
|
|
|
b = parse_value_input(node.inputs[2])
|
|
|
|
return 'vec3({0}, {1}, {2})'.format(r, g, b)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'WAVELENGTH':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Pass constant
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3([0.0, 0.27, 0.19])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
def store_var_name(node):
|
|
|
|
return node_name(node.name) + '_store'
|
|
|
|
|
2016-12-21 19:15:51 +01:00
|
|
|
def texture_store(node, tex, tex_name, to_linear=False):
|
2017-01-08 13:21:54 +01:00
|
|
|
global parse_teximage_vector
|
2017-05-24 11:04:15 +02:00
|
|
|
global parsing_basecol
|
|
|
|
global basecol_texname
|
2017-04-22 15:08:44 +02:00
|
|
|
c_state.mat_bind_texture(tex)
|
2017-05-25 16:48:41 +02:00
|
|
|
con.add_elem('tex', 2)
|
2016-12-20 00:39:18 +01:00
|
|
|
curshader.add_uniform('sampler2D {0}'.format(tex_name))
|
2017-01-08 13:21:54 +01:00
|
|
|
if node.inputs[0].is_linked and parse_teximage_vector:
|
2016-12-20 00:39:18 +01:00
|
|
|
uv_name = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
uv_name = 'texCoord'
|
|
|
|
tex_store = store_var_name(node)
|
2017-04-22 15:08:44 +02:00
|
|
|
if c_state.mat_texture_grad():
|
2017-03-28 14:30:51 +02:00
|
|
|
curshader.write('vec4 {0} = textureGrad({1}, {2}.xy, g2.xy, g2.zw);'.format(tex_store, tex_name, uv_name))
|
|
|
|
else:
|
|
|
|
curshader.write('vec4 {0} = texture({1}, {2}.xy);'.format(tex_store, tex_name, uv_name))
|
2016-12-21 19:15:51 +01:00
|
|
|
if to_linear:
|
2016-12-20 14:15:09 +01:00
|
|
|
curshader.write('{0}.rgb = pow({0}.rgb, vec3(2.2));'.format(tex_store))
|
2017-05-24 11:04:15 +02:00
|
|
|
if parsing_basecol:
|
|
|
|
basecol_texname = tex_store
|
2016-12-20 00:39:18 +01:00
|
|
|
return tex_store
|
|
|
|
|
2016-12-13 11:42:00 +01:00
|
|
|
def parse_vector(node, socket):
|
|
|
|
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.type == 'GROUP':
|
2016-12-13 11:42:00 +01:00
|
|
|
return parse_group(node, socket)
|
|
|
|
|
|
|
|
elif node.type == 'GROUP_INPUT':
|
2017-01-04 00:13:52 +01:00
|
|
|
return parse_group_input(node, socket)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'ATTRIBUTE':
|
2016-12-19 01:25:22 +01:00
|
|
|
# UVMaps only for now
|
2017-05-25 16:48:41 +02:00
|
|
|
con.add_elem('tex', 2)
|
2017-04-22 15:08:44 +02:00
|
|
|
mat = c_state.mat_get_material()
|
|
|
|
mat_users = c_state.mat_get_material_users()
|
2016-12-20 00:39:18 +01:00
|
|
|
if mat_users != None and mat in mat_users:
|
2016-12-19 01:25:22 +01:00
|
|
|
mat_user = mat_users[mat][0]
|
|
|
|
if hasattr(mat_user.data, 'uv_layers'): # No uvlayers for Curve
|
|
|
|
lays = mat_user.data.uv_layers
|
|
|
|
# Second uvmap referenced
|
|
|
|
if len(lays) > 1 and node.attribute_name == lays[1].name:
|
2017-05-25 16:48:41 +02:00
|
|
|
con.add_elem('tex1', 2)
|
2016-12-19 01:25:22 +01:00
|
|
|
return 'texCoord1', 2
|
|
|
|
return 'texCoord', 2
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'CAMERA':
|
2016-12-13 20:06:23 +01:00
|
|
|
# View Vector
|
2017-03-14 20:43:54 +01:00
|
|
|
return 'vVec'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'NEW_GEOMETRY':
|
2016-12-13 20:06:23 +01:00
|
|
|
if socket == node.outputs[0]: # Position
|
|
|
|
return 'wposition'
|
|
|
|
elif socket == node.outputs[1]: # Normal
|
|
|
|
return 'n'
|
|
|
|
elif socket == node.outputs[2]: # Tangent
|
|
|
|
return 'vec3(0.0)'
|
|
|
|
elif socket == node.outputs[3]: # True Normal
|
|
|
|
return 'n'
|
|
|
|
elif socket == node.outputs[4]: # Incoming
|
2017-03-14 20:43:54 +01:00
|
|
|
return 'vVec'
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[5]: # Parametric
|
|
|
|
return 'wposition'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'HAIR_INFO':
|
2016-12-13 20:06:23 +01:00
|
|
|
return 'vec3(0.0)' # Tangent Normal
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'OBJECT_INFO':
|
2016-12-13 20:06:23 +01:00
|
|
|
return 'wposition'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'PARTICLE_INFO':
|
2016-12-13 20:06:23 +01:00
|
|
|
if socket == node.outputs[3]: # Location
|
|
|
|
return 'vec3(0.0)'
|
|
|
|
elif socket == node.outputs[5]: # Velocity
|
|
|
|
return 'vec3(0.0)'
|
|
|
|
elif socket == node.outputs[6]: # Angular Velocity
|
|
|
|
return 'vec3(0.0)'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TANGENT':
|
2016-12-13 20:06:23 +01:00
|
|
|
return 'vec3(0.0)'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_COORD':
|
2016-12-13 20:06:23 +01:00
|
|
|
#obj = node.object
|
|
|
|
#dupli = node.from_dupli
|
|
|
|
if socket == node.outputs[0]: # Generated
|
2016-12-21 19:15:51 +01:00
|
|
|
return 'vec2(0.0)', 2
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[1]: # Normal
|
2016-12-21 19:15:51 +01:00
|
|
|
return 'vec2(0.0)', 2
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[2]: # UV
|
2017-05-25 16:48:41 +02:00
|
|
|
con.add_elem('tex', 2)
|
2016-12-21 19:15:51 +01:00
|
|
|
return 'texCoord', 2
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[3]: # Object
|
2016-12-21 19:15:51 +01:00
|
|
|
return 'vec2(0.0)', 2
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[4]: # Camera
|
2016-12-21 19:15:51 +01:00
|
|
|
return 'vec2(0.0)', 2
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[5]: # Window
|
2016-12-21 19:15:51 +01:00
|
|
|
return 'vec2(0.0)', 2
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[6]: # Reflection
|
2016-12-21 19:15:51 +01:00
|
|
|
return 'vec2(0.0)', 2
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'UVMAP':
|
2016-12-13 20:06:23 +01:00
|
|
|
#map = node.uv_map
|
|
|
|
#dupli = node.from_dupli
|
2016-12-21 19:15:51 +01:00
|
|
|
return 'vec2(0.0)', 2
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'BUMP':
|
2016-12-13 20:06:23 +01:00
|
|
|
#invert = node.invert
|
|
|
|
# strength = parse_value_input(node.inputs[0])
|
|
|
|
# distance = parse_value_input(node.inputs[1])
|
|
|
|
# height = parse_value_input(node.inputs[2])
|
|
|
|
# nor = parse_vector_input(node.inputs[3])
|
2016-12-17 15:34:43 +01:00
|
|
|
# Sample height around the normal and compute normal
|
2016-12-17 23:48:18 +01:00
|
|
|
return 'n'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'MAPPING':
|
2017-08-22 10:43:33 +02:00
|
|
|
out = parse_vector_input(node.inputs[0])
|
|
|
|
if node.scale[0] != 1.0 or node.scale[1] != 1.0 or node.scale[2] != 1.0:
|
|
|
|
out = '({0} * vec2({1}, {2}))'.format(out, node.scale[0], node.scale[1])
|
|
|
|
if node.translation[0] != 0.0 or node.translation[1] != 0.0 or node.translation[2] != 0.0:
|
|
|
|
out = '({0} + vec2({1}, {2}))'.format(out, node.translation[0], node.translation[1])
|
|
|
|
# if node.rotation[0] != 0.0 or node.rotation[1] != 0.0 or node.rotation[2] != 0.0:
|
|
|
|
# out.x = x * cos(rotation[0]) - y * sin(rotation[0])
|
|
|
|
# out.y = y * cos(rotation[0]) + x * sin(rotation[0])
|
|
|
|
# if node.use_min:
|
|
|
|
# out = max(out, vec2(min[0], min[1]))
|
|
|
|
# if node.use_max:
|
|
|
|
# out = min(out, vec2(max[0], max[1]))
|
|
|
|
return out, 2
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'NORMAL':
|
2016-12-13 20:06:23 +01:00
|
|
|
if socket == node.outputs[0]:
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec3(node.outputs[0].default_value)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[1]: # TODO: is parse_value path preferred?
|
|
|
|
nor = parse_vector_input(node.inputs[0])
|
2016-12-15 00:18:59 +01:00
|
|
|
return 'vec3(dot({0}, {1}))'.format(tovec3(node.outputs[0].default_value), nor)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'NORMAL_MAP':
|
2016-12-21 19:15:51 +01:00
|
|
|
if curshader == tese:
|
|
|
|
return parse_vector_input(node.inputs[1])
|
|
|
|
else:
|
|
|
|
#space = node.space
|
|
|
|
#map = node.uv_map
|
|
|
|
# strength = parse_value_input(node.inputs[0])
|
|
|
|
parse_normal_map_color_input(node.inputs[1]) # Color
|
|
|
|
return None
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'CURVE_VEC':
|
2016-12-13 20:06:23 +01:00
|
|
|
# fac = parse_value_input(node.inputs[0])
|
|
|
|
# Pass throuh
|
|
|
|
return parse_vector_input(node.inputs[1])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'VECT_TRANSFORM':
|
2016-12-13 20:06:23 +01:00
|
|
|
#type = node.vector_type
|
|
|
|
#conv_from = node.convert_from
|
|
|
|
#conv_to = node.convert_to
|
|
|
|
# Pass throuh
|
|
|
|
return parse_vector_input(node.inputs[0])
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'COMBXYZ':
|
2016-12-13 20:06:23 +01:00
|
|
|
x = parse_value_input(node.inputs[0])
|
|
|
|
y = parse_value_input(node.inputs[1])
|
|
|
|
z = parse_value_input(node.inputs[2])
|
|
|
|
return 'vec3({0}, {1}, {2})'.format(x, y, z)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'VECT_MATH':
|
2016-12-13 20:06:23 +01:00
|
|
|
vec1 = parse_vector_input(node.inputs[0])
|
|
|
|
vec2 = parse_vector_input(node.inputs[1])
|
|
|
|
op = node.operation
|
|
|
|
if op == 'ADD':
|
|
|
|
return '({0} + {1})'.format(vec1, vec2)
|
|
|
|
elif op == 'SUBTRACT':
|
|
|
|
return '({0} - {1})'.format(vec1, vec2)
|
|
|
|
elif op == 'AVERAGE':
|
|
|
|
return '(({0} + {1}) / 2.0)'.format(vec1, vec2)
|
|
|
|
elif op == 'DOT_PRODUCT':
|
|
|
|
return 'vec3(dot({0}, {1}))'.format(vec1, vec2)
|
|
|
|
elif op == 'CROSS_PRODUCT':
|
|
|
|
return 'cross({0}, {1})'.format(vec1, vec2)
|
|
|
|
elif op == 'NORMALIZE':
|
|
|
|
return 'normalize({0})'.format(vec1)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2017-07-09 19:44:19 +02:00
|
|
|
def parse_normal_map_color_input(inp):
|
2017-01-08 13:21:54 +01:00
|
|
|
global parse_teximage_vector
|
2016-12-17 23:48:18 +01:00
|
|
|
if inp.is_linked == False:
|
|
|
|
return
|
|
|
|
frag.write_pre = True
|
2017-01-08 13:21:54 +01:00
|
|
|
parse_teximage_vector = False # Force texCoord for normal map image vector
|
2017-04-22 15:08:44 +02:00
|
|
|
defplus = c_state.get_rp_renderer() == 'Deferred Plus'
|
2017-08-21 12:17:55 +02:00
|
|
|
if not c_state.get_arm_export_tangents() or defplus or c_state.mat_get_material().arm_decal: # Compute TBN matrix
|
2017-04-01 10:06:49 +02:00
|
|
|
frag.write('vec3 texn = ({0}) * 2.0 - 1.0;'.format(parse_vector_input(inp)))
|
|
|
|
frag.add_include('../../Shaders/std/normals.glsl')
|
|
|
|
if defplus:
|
|
|
|
frag.write('mat3 TBN = cotangentFrame(n, -vVec, g2.xy, g2.zw);')
|
|
|
|
else:
|
|
|
|
frag.write('mat3 TBN = cotangentFrame(n, -vVec, texCoord);')
|
|
|
|
frag.write('n = TBN * normalize(texn);')
|
|
|
|
else:
|
2017-03-31 14:18:47 +02:00
|
|
|
frag.write('vec3 n = ({0}) * 2.0 - 1.0;'.format(parse_vector_input(inp)))
|
|
|
|
# frag.write('n = normalize(TBN * normalize(n));')
|
|
|
|
frag.write('n = TBN * normalize(n);')
|
2017-05-25 16:48:41 +02:00
|
|
|
con.add_elem('tang', 3)
|
2017-04-01 10:06:49 +02:00
|
|
|
|
2017-01-08 13:21:54 +01:00
|
|
|
parse_teximage_vector = True
|
2016-12-17 23:48:18 +01:00
|
|
|
frag.write_pre = False
|
|
|
|
|
2016-12-13 11:42:00 +01:00
|
|
|
def parse_value_input(inp):
|
|
|
|
if inp.is_linked:
|
|
|
|
l = inp.links[0]
|
2016-12-15 00:18:59 +01:00
|
|
|
|
|
|
|
if l.from_node.type == 'REROUTE':
|
|
|
|
return parse_value_input(l.from_node.inputs[0])
|
|
|
|
|
2016-12-15 14:28:22 +01:00
|
|
|
res_var = write_result(l)
|
|
|
|
st = l.from_socket.type
|
|
|
|
if st == 'RGB' or st == 'RGBA' or st == 'VECTOR':
|
|
|
|
return '{0}.x'.format(res_var)
|
|
|
|
else: # VALUE
|
|
|
|
return res_var
|
2016-12-13 11:42:00 +01:00
|
|
|
else:
|
2017-04-22 15:08:44 +02:00
|
|
|
if c_state.mat_batch() and inp.is_uniform:
|
2017-03-14 20:43:54 +01:00
|
|
|
return touniform(inp)
|
|
|
|
else:
|
|
|
|
return tovec1(inp.default_value)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
def parse_value(node, socket):
|
|
|
|
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.type == 'GROUP':
|
2016-12-17 23:48:18 +01:00
|
|
|
if node.node_tree.name.startswith('Armory PBR'):
|
|
|
|
# Displacement
|
|
|
|
if socket == node.outputs[1]:
|
2017-07-07 18:21:44 +02:00
|
|
|
|
|
|
|
if len(node.inputs) != 14:
|
2017-07-09 19:44:19 +02:00
|
|
|
res = parse_value_input(node.inputs[7])
|
2017-07-07 18:21:44 +02:00
|
|
|
else: # Deprecated
|
|
|
|
res = parse_value_input(node.inputs[10])
|
|
|
|
if node.inputs[11].is_linked or node.inputs[11].default_value != 1.0:
|
|
|
|
res = "({0} * {1})".format(res, parse_value_input(node.inputs[11]))
|
|
|
|
|
2016-12-19 01:25:22 +01:00
|
|
|
return res
|
2016-12-17 23:48:18 +01:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return parse_group(node, socket)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'GROUP_INPUT':
|
2017-01-04 00:13:52 +01:00
|
|
|
return parse_group_input(node, socket)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'ATTRIBUTE':
|
2016-12-17 15:34:43 +01:00
|
|
|
# Pass time till drivers are implemented
|
|
|
|
if node.attribute_name == 'time':
|
|
|
|
curshader.add_uniform('float time', link='_time')
|
|
|
|
return 'time'
|
|
|
|
else:
|
2017-01-14 12:44:43 +01:00
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'CAMERA':
|
2016-12-13 20:06:23 +01:00
|
|
|
# View Z Depth
|
|
|
|
if socket == node.outputs[1]:
|
|
|
|
return 'gl_FragCoord.z'
|
|
|
|
# View Distance
|
|
|
|
else:
|
|
|
|
return 'length(eyeDir)'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'FRESNEL':
|
2016-12-13 20:06:23 +01:00
|
|
|
ior = parse_value_input(node.inputs[0])
|
|
|
|
#nor = parse_vectorZ_input(node.inputs[1])
|
|
|
|
return 'pow(1.0 - dotNV, 7.25 / {0})'.format(ior) # max(dotNV, 0.0)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'NEW_GEOMETRY':
|
2016-12-13 20:06:23 +01:00
|
|
|
if socket == node.outputs[6]: # Backfacing
|
|
|
|
return '0.0'
|
|
|
|
elif socket == node.outputs[7]: # Pointiness
|
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'HAIR_INFO':
|
2016-12-13 20:06:23 +01:00
|
|
|
# Is Strand
|
|
|
|
# Intercept
|
|
|
|
# Thickness
|
2017-01-14 12:44:43 +01:00
|
|
|
return '0.5'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'LAYER_WEIGHT':
|
2016-12-13 20:06:23 +01:00
|
|
|
blend = parse_value_input(node.inputs[0])
|
|
|
|
# nor = parse_vector_input(node.inputs[1])
|
|
|
|
if socket == node.outputs[0]: # Fresnel
|
2016-12-15 00:18:59 +01:00
|
|
|
return 'clamp(pow(1.0 - dotNV, (1.0 - {0}) * 10.0), 0.0, 1.0)'.format(blend)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif socket == node.outputs[1]: # Facing
|
|
|
|
return '((1.0 - dotNV) * {0})'.format(blend)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'LIGHT_PATH':
|
2016-12-13 20:06:23 +01:00
|
|
|
if socket == node.outputs[0]: # Is Camera Ray
|
|
|
|
return '1.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[1]: # Is Shadow Ray
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[2]: # Is Diffuse Ray
|
2016-12-13 20:06:23 +01:00
|
|
|
return '1.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[3]: # Is Glossy Ray
|
2016-12-13 20:06:23 +01:00
|
|
|
return '1.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[4]: # Is Singular Ray
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[5]: # Is Reflection Ray
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[6]: # Is Transmission Ray
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[7]: # Ray Length
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[8]: # Ray Depth
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[9]: # Transparent Depth
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-21 19:15:51 +01:00
|
|
|
elif socket == node.outputs[10]: # Transmission Depth
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'OBJECT_INFO':
|
2017-03-20 01:56:46 +01:00
|
|
|
if socket == node.outputs[1]: # Object Index
|
|
|
|
curshader.add_uniform('float objectInfoIndex', link='_objectInfoIndex')
|
|
|
|
return 'objectInfoIndex'
|
|
|
|
elif socket == node.outputs[2]: # Material Index
|
|
|
|
curshader.add_uniform('float objectInfoMaterialIndex', link='_objectInfoMaterialIndex')
|
|
|
|
return 'objectInfoMaterialIndex'
|
|
|
|
elif socket == node.outputs[3]: # Random
|
|
|
|
curshader.add_uniform('float objectInfoRandom', link='_objectInfoRandom')
|
|
|
|
return 'objectInfoRandom'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'PARTICLE_INFO':
|
2016-12-13 20:06:23 +01:00
|
|
|
if socket == node.outputs[0]: # Index
|
|
|
|
return '0.0'
|
|
|
|
elif socket == node.outputs[1]: # Age
|
|
|
|
return '0.0'
|
|
|
|
elif socket == node.outputs[2]: # Lifetime
|
|
|
|
return '0.0'
|
|
|
|
elif socket == node.outputs[4]: # Size
|
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'VALUE':
|
2016-12-15 00:18:59 +01:00
|
|
|
return tovec1(node.outputs[0].default_value)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'WIREFRAME':
|
2016-12-13 20:06:23 +01:00
|
|
|
#node.use_pixel_size
|
|
|
|
# size = parse_value_input(node.inputs[0])
|
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_BRICK':
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_CHECKER':
|
2016-12-15 00:18:59 +01:00
|
|
|
# TODO: do not recompute when color socket is also connected
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_tex_checker)
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
|
|
|
col1 = parse_vector_input(node.inputs[1])
|
|
|
|
col2 = parse_vector_input(node.inputs[2])
|
|
|
|
scale = parse_value_input(node.inputs[3])
|
2016-12-15 14:28:22 +01:00
|
|
|
return 'tex_checker({0}, {1}, {2}, {3}).r'.format(co, col1, col2, scale)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_GRADIENT':
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_IMAGE':
|
2016-12-20 00:39:18 +01:00
|
|
|
# Already fetched
|
|
|
|
if res_var_name(node, node.outputs[0]) in parsed:
|
|
|
|
return '{0}.a'.format(store_var_name(node))
|
2017-05-13 17:17:43 +02:00
|
|
|
tex_name = c_state.safesrc(node.name)
|
2017-04-22 15:08:44 +02:00
|
|
|
tex = c_state.make_texture(node, tex_name)
|
2016-12-20 00:39:18 +01:00
|
|
|
if tex != None:
|
|
|
|
return '{0}.a'.format(texture_store(node, tex, tex_name))
|
|
|
|
else:
|
2017-01-17 15:49:33 +01:00
|
|
|
tex_store = store_var_name(node) # Pink color for missing texture
|
|
|
|
curshader.write('vec4 {0} = vec4(1.0, 0.0, 1.0, 1.0);'.format(tex_store))
|
|
|
|
return '{0}.a'.format(tex_store)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_MAGIC':
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_MUSGRAVE':
|
2016-12-15 00:18:59 +01:00
|
|
|
# Fall back to noise
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_tex_noise)
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
|
|
|
scale = parse_value_input(node.inputs[1])
|
|
|
|
# detail = parse_value_input(node.inputs[2])
|
|
|
|
# distortion = parse_value_input(node.inputs[3])
|
|
|
|
return 'tex_noise_f({0} * {1})'.format(co, scale)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_NOISE':
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_tex_noise)
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
|
|
|
scale = parse_value_input(node.inputs[1])
|
|
|
|
# detail = parse_value_input(node.inputs[2])
|
|
|
|
# distortion = parse_value_input(node.inputs[3])
|
|
|
|
return 'tex_noise({0} * {1})'.format(co, scale)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_POINTDENSITY':
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_VORONOI':
|
2017-04-22 15:08:44 +02:00
|
|
|
curshader.add_function(c_functions.str_tex_voronoi)
|
|
|
|
c_state.assets_add(c_state.get_sdk_path() + '/armory/Assets/' + 'noise64.png')
|
|
|
|
c_state.assets_add_embedded_data('noise64.png')
|
2016-12-17 15:34:43 +01:00
|
|
|
curshader.add_uniform('sampler2D snoise', link='_noise64')
|
2016-12-15 00:18:59 +01:00
|
|
|
if node.inputs[0].is_linked:
|
|
|
|
co = parse_vector_input(node.inputs[0])
|
|
|
|
else:
|
|
|
|
co = 'wposition'
|
|
|
|
scale = parse_value_input(node.inputs[1])
|
|
|
|
if node.coloring == 'INTENSITY':
|
|
|
|
return 'tex_voronoi({0} * {1}).a'.format(co, scale)
|
|
|
|
else: # CELLS
|
|
|
|
return 'tex_voronoi({0} * {1}).r'.format(co, scale)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'TEX_WAVE':
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'LIGHT_FALLOFF':
|
2017-02-22 15:50:19 +01:00
|
|
|
# Constant, linear, quadratic
|
|
|
|
# Shaders default to quadratic for now
|
|
|
|
return '1.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'NORMAL':
|
2016-12-13 20:06:23 +01:00
|
|
|
nor = parse_vector_input(node.inputs[0])
|
2016-12-15 00:18:59 +01:00
|
|
|
return 'dot({0}, {1})'.format(tovec3(node.outputs[0].default_value), nor)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2016-12-13 20:06:23 +01:00
|
|
|
elif node.type == 'VALTORGB': # ColorRamp
|
|
|
|
return '1.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'MATH':
|
2016-12-13 20:06:23 +01:00
|
|
|
val1 = parse_value_input(node.inputs[0])
|
|
|
|
val2 = parse_value_input(node.inputs[1])
|
|
|
|
op = node.operation
|
|
|
|
if op == 'ADD':
|
|
|
|
out_val = '({0} + {1})'.format(val1, val2)
|
|
|
|
elif op == 'SUBTRACT':
|
|
|
|
out_val = '({0} - {1})'.format(val1, val2)
|
|
|
|
elif op == 'MULTIPLY':
|
|
|
|
out_val = '({0} * {1})'.format(val1, val2)
|
|
|
|
elif op == 'DIVIDE':
|
|
|
|
out_val = '({0} / {1})'.format(val1, val2)
|
|
|
|
elif op == 'SINE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_val = 'sin({0})'.format(val1)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif op == 'COSINE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_val = 'cos({0})'.format(val1)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif op == 'TANGENT':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_val = 'tan({0})'.format(val1)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif op == 'ARCSINE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_val = 'asin({0})'.format(val1)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif op == 'ARCCOSINE':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_val = 'acos({0})'.format(val1)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif op == 'ARCTANGENT':
|
2016-12-15 00:18:59 +01:00
|
|
|
out_val = 'atan({0})'.format(val1)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif op == 'POWER':
|
|
|
|
out_val = 'pow({0}, {1})'.format(val1, val2)
|
|
|
|
elif op == 'LOGARITHM':
|
|
|
|
out_val = 'log({0})'.format(val1)
|
|
|
|
elif op == 'MINIMUM':
|
|
|
|
out_val = 'min({0}, {1})'.format(val1, val2)
|
|
|
|
elif op == 'MAXIMUM':
|
|
|
|
out_val = 'max({0}, {1})'.format(val1, val2)
|
|
|
|
elif op == 'ROUND':
|
2016-12-15 00:18:59 +01:00
|
|
|
# out_val = 'round({0})'.format(val1)
|
|
|
|
out_val = 'floor({0} + 0.5)'.format(val1)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif op == 'LESS_THAN':
|
|
|
|
out_val = 'float({0} < {1})'.format(val1, val2)
|
|
|
|
elif op == 'GREATER_THAN':
|
|
|
|
out_val = 'float({0} > {1})'.format(val1, val2)
|
|
|
|
elif op == 'MODULO':
|
2016-12-15 00:18:59 +01:00
|
|
|
# out_val = 'float({0} % {1})'.format(val1, val2)
|
|
|
|
out_val = 'mod({0}, {1})'.format(val1, val2)
|
2016-12-13 20:06:23 +01:00
|
|
|
elif op == 'ABSOLUTE':
|
|
|
|
out_val = 'abs({0})'.format(val1)
|
|
|
|
if node.use_clamp:
|
|
|
|
return 'clamp({0}, 0.0, 1.0)'.format(out_val)
|
|
|
|
else:
|
|
|
|
return out_val
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'RGBTOBW':
|
2016-12-13 20:06:23 +01:00
|
|
|
col = parse_vector_input(node.inputs[0])
|
|
|
|
return '((({0}.r * 0.3 + {0}.g * 0.59 + {0}.b * 0.11) / 3.0) * 2.5)'.format(col)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'SEPHSV':
|
2016-12-13 20:06:23 +01:00
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'SEPRGB':
|
2016-12-13 20:06:23 +01:00
|
|
|
col = parse_vector_input(node.inputs[0])
|
|
|
|
if socket == node.outputs[0]:
|
|
|
|
return '{0}.r'.format(col)
|
|
|
|
elif socket == node.outputs[1]:
|
|
|
|
return '{0}.g'.format(col)
|
|
|
|
elif socket == node.outputs[2]:
|
|
|
|
return '{0}.b'.format(col)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'SEPXYZ':
|
2016-12-13 20:06:23 +01:00
|
|
|
vec = parse_vector_input(node.inputs[0])
|
|
|
|
if socket == node.outputs[0]:
|
|
|
|
return '{0}.x'.format(vec)
|
|
|
|
elif socket == node.outputs[1]:
|
|
|
|
return '{0}.y'.format(vec)
|
|
|
|
elif socket == node.outputs[2]:
|
|
|
|
return '{0}.z'.format(vec)
|
2016-12-13 11:42:00 +01:00
|
|
|
|
|
|
|
elif node.type == 'VECT_MATH':
|
2016-12-13 20:06:23 +01:00
|
|
|
vec1 = parse_vector_input(node.inputs[0])
|
|
|
|
vec2 = parse_vector_input(node.inputs[1])
|
|
|
|
op = node.operation
|
|
|
|
if op == 'DOT_PRODUCT':
|
|
|
|
return 'dot({0}, {1})'.format(vec1, vec2)
|
|
|
|
else:
|
|
|
|
return '0.0'
|
2016-12-13 11:42:00 +01:00
|
|
|
|
2016-12-15 00:18:59 +01:00
|
|
|
def tovec1(v):
|
2016-12-13 11:42:00 +01:00
|
|
|
return str(v)
|
|
|
|
|
2016-12-15 00:18:59 +01:00
|
|
|
def tovec2(v):
|
2016-12-13 11:42:00 +01:00
|
|
|
return 'vec2({0}, {1})'.format(v[0], v[1])
|
|
|
|
|
2016-12-15 00:18:59 +01:00
|
|
|
def tovec3(v):
|
2016-12-13 11:42:00 +01:00
|
|
|
return 'vec3({0}, {1}, {2})'.format(v[0], v[1], v[2])
|
|
|
|
|
2016-12-15 00:18:59 +01:00
|
|
|
def tovec4(v):
|
2016-12-13 11:42:00 +01:00
|
|
|
return 'vec4({0}, {1}, {2}, {3})'.format(v[0], v[1], v[2], v[3])
|
2016-12-15 23:50:21 +01:00
|
|
|
|
|
|
|
def node_by_type(nodes, ntype):
|
|
|
|
for n in nodes:
|
|
|
|
if n.type == ntype:
|
|
|
|
return n
|
|
|
|
|
|
|
|
def socket_index(node, socket):
|
|
|
|
for i in range(0, len(node.outputs)):
|
|
|
|
if node.outputs[i] == socket:
|
|
|
|
return i
|
|
|
|
|
|
|
|
def node_name(s):
|
2017-05-13 17:17:43 +02:00
|
|
|
s = c_state.safesrc(s)
|
2016-12-15 23:50:21 +01:00
|
|
|
if len(parents) > 0:
|
2017-05-13 17:17:43 +02:00
|
|
|
s = c_state.safesrc(parents[-1].name) + '_' + s
|
2017-08-04 11:54:32 +02:00
|
|
|
if '__' in s: # Consecutive _ are reserved
|
|
|
|
s = s.replace('_', '_x')
|
2016-12-15 23:50:21 +01:00
|
|
|
return s
|