armory/blender/material/make_cycles.py

1096 lines
38 KiB
Python
Raw Normal View History

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.
#
import armutils
2016-12-17 15:34:43 +01:00
import assets
2016-12-15 23:50:21 +01:00
import material.make_texture as make_texture
2016-12-17 15:34:43 +01:00
import material.mat_state as mat_state
import make_state as state
2016-12-13 11:42:00 +01:00
2016-12-15 00:18:59 +01:00
str_tex_checker = """vec3 tex_checker(const vec3 co, const vec3 col1, const vec3 col2, const float scale) {
vec3 p = co * scale;
// Prevent precision issues on unit coordinates
//p.x = (p.x + 0.000001) * 0.999999;
//p.y = (p.y + 0.000001) * 0.999999;
//p.z = (p.z + 0.000001) * 0.999999;
float xi = abs(floor(p.x));
float yi = abs(floor(p.y));
float zi = abs(floor(p.z));
bool check = ((mod(xi, 2.0) == mod(yi, 2.0)) == bool(mod(zi, 2.0)));
return check ? col1 : col2;
}
"""
# Created by inigo quilez - iq/2013
# License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
str_tex_voronoi = """//vec3 hash(vec3 x) {
//return texture(snoise, (x.xy + vec2(3.0, 1.0) * x.z + 0.5) / 64.0, -100.0).xyz;
//x = vec3(dot(x, vec3(127.1, 311.7, 74.7)),
// dot(x, vec3(269.5, 183.3, 246.1)),
// dot(x, vec3(113.5, 271.9, 124.6)));
//return fract(sin(x) * 43758.5453123);
//}
vec4 tex_voronoi(const vec3 x) {
vec3 xx = x / 3.0; // Match cycles
vec3 p = floor(xx);
vec3 f = fract(xx);
float id = 0.0;
float res = 100.0;
for (int k = -1; k <= 1; k++)
for (int j = -1; j <= 1; j++)
for (int i = -1; i <= 1; i++) {
vec3 b = vec3(float(i), float(j), float(k));
vec3 pb = p + b;
2016-12-17 15:34:43 +01:00
//vec3 r = vec3(b) - f + texture(snoise, (pb.xy + vec2(3.0, 1.0) * pb.z + 0.5) / 64.0, -100.0).xyz; // No bias in tese
vec3 r = vec3(b) - f + texture(snoise, (pb.xy + vec2(3.0, 1.0) * pb.z + 0.5) / 64.0).xyz;
2016-12-15 00:18:59 +01:00
//vec3 r = vec3(b) - f + hash(p + b);
float d = dot(r, r);
if (d < res) {
id = dot(p + b, vec3(1.0, 57.0, 113.0));
res = d;
}
}
vec3 col = 0.5 + 0.5 * cos(id * 0.35 + vec3(0.0, 1.0, 2.0));
return vec4(col, sqrt(res));
}
"""
# str_tex_noise = """
# float tex_noise_f(const vec3 x) {
# vec3 p = floor(x);
# vec3 f = fract(x);
# f = f * f * (3.0 - 2.0 * f);
# vec2 uv = (p.xy + vec2(37.0, 17.0) * p.z) + f.xy;
# vec2 rg = texture(snoisea, (uv + 0.5) / 64.0, -100.0).yx;
# return mix(rg.x, rg.y, f.z);
# }
# float tex_noise(vec3 q) {
# //return fract(sin(dot(q.xy, vec2(12.9898,78.233))) * 43758.5453);
# q *= 2.0; // Match to Cycles
# const mat3 m = mat3(0.00, 0.80, 0.60, -0.80, 0.36, -0.48, -0.60, -0.48, 0.64);
# float f = 0.5000 * tex_noise_f(q); q = m * q * 2.01;
# f += 0.2500 * tex_noise_f(q); q = m * q * 2.02;
# f += 0.1250 * tex_noise_f(q); q = m * q * 2.03;
# f += 0.0625 * tex_noise_f(q); q = m * q * 2.01;
# return pow(f, 3.0);
# }
# """
# Created by Nikita Miropolskiy, nikat/2013
# Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
str_tex_noise = """
vec3 random3(vec3 c) {
// Might not be precise on lowp floats
float j = 4096.0 * sin(dot(c, vec3(17.0, 59.4, 15.0)));
vec3 r;
r.z = fract(512.0 * j);
j *= 0.125;
r.x = fract(512.0 * j);
j *= 0.125;
r.y = fract(512.0 * j);
return r - 0.5;
}
float tex_noise_f(vec3 p) {
const float F3 = 0.3333333;
const float G3 = 0.1666667;
vec3 s = floor(p + dot(p, vec3(F3)));
vec3 x = p - s + dot(s, vec3(G3));
vec3 e = step(vec3(0.0), x - x.yzx);
vec3 i1 = e*(1.0 - e.zxy);
vec3 i2 = 1.0 - e.zxy*(1.0 - e);
vec3 x1 = x - i1 + G3;
vec3 x2 = x - i2 + 2.0*G3;
vec3 x3 = x - 1.0 + 3.0*G3;
vec4 w, d;
w.x = dot(x, x);
w.y = dot(x1, x1);
w.z = dot(x2, x2);
w.w = dot(x3, x3);
w = max(0.6 - w, 0.0);
d.x = dot(random3(s), x);
d.y = dot(random3(s + i1), x1);
d.z = dot(random3(s + i2), x2);
d.w = dot(random3(s + 1.0), x3);
w *= w;
w *= w;
d *= w;
return clamp(dot(d, vec4(52.0)), 0.0, 1.0);
}
float tex_noise(vec3 p) {
return 0.5333333 * tex_noise_f(p)
+ 0.2666667 * tex_noise_f(2.0 * p)
+ 0.1333333 * tex_noise_f(4.0 * p)
+ 0.0666667 * tex_noise_f(8.0 * p);
}
"""
2016-12-17 15:34:43 +01:00
def parse(nodes, vert, frag, geom, tesc, tese, parse_surface=True):
2016-12-13 11:42:00 +01:00
output_node = node_by_type(nodes, 'OUTPUT_MATERIAL')
if output_node != None:
2016-12-17 15:34:43 +01:00
parse_output(output_node, vert, frag, geom, tesc, tese, parse_surface)
2016-12-13 11:42:00 +01:00
2016-12-17 15:34:43 +01:00
def parse_output(node, _vert, _frag, _geom, _tesc, _tese, parse_surface):
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
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-15 00:18:59 +01:00
global str_tex_checker
global str_tex_voronoi
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
# Surface
if parse_surface:
parsed = []
parents = []
normal_written = False
curshader = frag
out_basecol, out_roughness, out_metallic, out_occlusion = parse_shader_input(node.inputs[0])
frag.write('basecol = {0};'.format(out_basecol))
frag.write('roughness = {0};'.format(out_roughness))
frag.write('metallic = {0};'.format(out_metallic))
frag.write('occlusion = {0};'.format(out_occlusion))
# Volume
# parse_volume_input(node.inputs[1])
# Displacement
if armutils.tess_enabled(state.target) and node.inputs[2].is_linked:
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
def parse_input_group(node, socket): # Leaving group
index = socket_index(node, socket)
parent = parents[-1]
inp = parent.inputs[index]
return parse_input(inp)
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'
return out_basecol, out_roughness, out_metallic, out_occlusion
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 15:34:43 +01:00
curshader.write('n = {0};'.format(parse_vector_input(inp)))
2016-12-15 22:31:20 +01:00
normal_written = True
2016-12-13 11:42:00 +01:00
def parse_shader(node, socket):
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-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'):
pass
else:
return parse_group(node, socket)
elif node.type == 'GROUP_INPUT':
return parse_input_group(node, socket)
elif node.type == 'MIX_SHADER':
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'
2016-12-17 15:34:43 +01:00
curshader.write('float {0} = {1};'.format(fac_var, fac))
curshader.write('float {0} = 1.0 - {1};'.format(fac_inv_var, fac_var))
2016-12-13 20:06:23 +01:00
bc1, rough1, met1, occ1 = parse_shader_input(node.inputs[1])
bc2, rough2, met2, occ2 = parse_shader_input(node.inputs[2])
2016-12-15 00:18:59 +01:00
out_basecol = '({0} * {3} + {1} * {2})'.format(bc1, bc2, fac_var, fac_inv_var)
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)
2016-12-13 11:42:00 +01:00
elif node.type == 'ADD_SHADER':
2016-12-13 20:06:23 +01:00
bc1, rough1, met1, occ1 = parse_shader_input(node.inputs[0])
bc2, rough2, met2, occ2 = parse_shader_input(node.inputs[1])
out_basecol = '({0} + {1})'.format(bc1, bc2)
2016-12-17 15:34:43 +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)
2016-12-13 11:42:00 +01:00
elif node.type == 'BSDF_DIFFUSE':
2016-12-15 22:31:20 +01:00
write_normal(node.inputs[2])
2016-12-13 20:06:23 +01:00
out_basecol = parse_vector_input(node.inputs[0])
2016-12-13 11:42:00 +01:00
out_roughness = parse_value_input(node.inputs[1])
elif node.type == 'BSDF_GLOSSY':
2016-12-15 22:31:20 +01:00
write_normal(node.inputs[2])
2016-12-13 20:06:23 +01:00
out_basecol = parse_vector_input(node.inputs[0])
2016-12-13 11:42:00 +01:00
out_roughness = parse_value_input(node.inputs[1])
out_metallic = '1.0'
elif node.type == 'AMBIENT_OCCLUSION':
2016-12-13 20:06:23 +01:00
# 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-15 22:31:20 +01:00
write_normal(node.inputs[4])
2016-12-13 20:06:23 +01:00
# Revert to glossy
out_basecol = parse_vector_input(node.inputs[0])
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-13 20:06:23 +01:00
# Multiply basecol
out_basecol = parse_vector_input(node.inputs[0])
strength = parse_value_input(node.inputs[1])
out_basecol = '({0} * {1} * 50.0)'.format(out_basecol, strength)
2016-12-13 11:42:00 +01:00
elif node.type == 'BSDF_GLASS':
2016-12-15 22:31:20 +01:00
# write_normal(node.inputs[3])
2016-12-13 20:06:23 +01:00
# Switch to translucent
2016-12-13 11:42:00 +01:00
pass
elif node.type == 'BSDF_HAIR':
pass
elif node.type == 'HOLDOUT':
2016-12-13 20:06:23 +01:00
# 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':
2016-12-15 22:31:20 +01:00
# write_normal(node.inputs[4])
2016-12-13 11:42:00 +01:00
pass
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-15 22:31:20 +01:00
# write_normal(node.inputs[1])
2016-12-13 11:42:00 +01:00
pass
elif node.type == 'BSDF_TRANSPARENT':
pass
elif node.type == 'BSDF_VELVET':
2016-12-15 22:31:20 +01:00
write_normal(node.inputs[2])
2016-12-13 20:06:23 +01:00
out_basecol = parse_vector_input(node.inputs[0])
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-13 20:06:23 +01:00
return out_basecol, out_roughness, out_metallic, out_occlusion
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-15 14:28:22 +01:00
def write_result(l):
res_var = node_name(l.from_node.name) + '_' + socket_name(l.from_socket.name) + '_res'
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 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 15:34:43 +01:00
curshader.write('vec3 {0} = {1};'.format(res_var, res))
2016-12-15 14:28:22 +01:00
elif st == 'VALUE':
res = parse_value(l.from_node, l.from_socket)
2016-12-17 15:34:43 +01:00
curshader.write('float {0} = {1};'.format(res_var, res))
2016-12-15 14:28:22 +01:00
return res_var
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:
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':
return parse_input_group(node, socket)
elif node.type == 'ATTRIBUTE':
2016-12-13 20:06:23 +01:00
# Vcols
2016-12-13 11:42:00 +01:00
pass
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':
2016-12-17 15:34:43 +01:00
curshader.add_function(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-15 23:50:21 +01:00
tex_name = armutils.safe_source_name(node.name)
tex = make_texture.make_texture(node, tex_name)
if tex != None:
2016-12-17 15:34:43 +01:00
mat_state.mat_context['bind_textures'].append(tex)
mat_state.data.add_elem('tex', 2)
curshader.add_uniform('sampler2D {0}'.format(tex_name))
2016-12-15 23:50:21 +01:00
return 'texture({0}, texCoord).rgb'.format(tex_name)
else:
return tovec3([0.0, 0.0, 0.0])
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
2016-12-17 15:34:43 +01:00
curshader.add_function(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':
2016-12-17 15:34:43 +01:00
curshader.add_function(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..
return 'vec3(tex_noise({0} * {1}), tex_noise({0} * {1} + vec3(0.33)), tex_noise({0} * {1} + vec3(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':
2016-12-17 15:34:43 +01:00
curshader.add_function(str_tex_voronoi)
assets.add(armutils.get_sdk_path() + '/armory/Assets/' + 'noise64.png')
assets.add_embedded_data('noise64.png')
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':
2016-12-13 20:06:23 +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])
out_col = parse_vector_input(node.inputs[4])
2016-12-17 15:34:43 +01:00
# curshader.add_function(\
2016-12-13 20:06:23 +01:00
# """vec3 hue_sat(const float hue, const float sat, const float val, const float fac, const vec3 col) {
# }
# """)
return out_col
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
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':
return parse_input_group(node, socket)
elif node.type == 'ATTRIBUTE':
2016-12-13 20:06:23 +01:00
# Vector
2016-12-13 11:42:00 +01:00
pass
elif node.type == 'CAMERA':
2016-12-13 20:06:23 +01:00
# View Vector
return 'v'
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
return 'v'
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
return 'vec2(0.0)'
elif socket == node.outputs[1]: # Normal
return 'vec2(0.0)'
elif socket == node.outputs[2]: # UV
return 'vec2(0.0)'
elif socket == node.outputs[3]: # Object
return 'vec2(0.0)'
elif socket == node.outputs[4]: # Camera
return 'vec2(0.0)'
elif socket == node.outputs[5]: # Window
return 'vec2(0.0)'
elif socket == node.outputs[6]: # Reflection
return 'vec2(0.0)'
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
return 'vec2(0.0)'
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
return 'wnormal'
2016-12-13 11:42:00 +01:00
elif node.type == 'MAPPING':
2016-12-13 20:06:23 +01:00
# vector = parse_vector_input(node.inputs[0])
return 'vec3(0.0)'
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-13 20:06:23 +01:00
#space = node.space
#map = node.uv_map
# strength = parse_value_input(node.inputs[0])
# color = parse_vector_input(node.inputs[1])
return 'vec3(0.0)'
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
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:
2016-12-15 00:18:59 +01:00
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-13 11:42:00 +01:00
return parse_group(node, socket)
elif node.type == 'GROUP_INPUT':
return parse_input_group(node, socket)
elif node.type == 'ATTRIBUTE':
2016-12-13 20:06:23 +01:00
# Fac
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:
return '1.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
2016-12-13 11:42:00 +01:00
pass
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'
elif socket == node.outputs[0]: # Is Shadow Ray
return '0.0'
elif socket == node.outputs[0]: # Is Diffuse Ray
return '1.0'
elif socket == node.outputs[0]: # Is Glossy Ray
return '1.0'
elif socket == node.outputs[0]: # Is Singular Ray
return '0.0'
elif socket == node.outputs[0]: # Is Reflection Ray
return '0.0'
elif socket == node.outputs[0]: # Is Transmission Ray
return '0.0'
elif socket == node.outputs[0]: # Ray Length
return '0.0'
elif socket == node.outputs[0]: # Ray Depth
return '0.0'
elif socket == node.outputs[0]: # Transparent Depth
return '0.0'
elif socket == node.outputs[0]: # Transmission Depth
return '0.0'
2016-12-13 11:42:00 +01:00
elif node.type == 'OBJECT_INFO':
2016-12-13 20:06:23 +01:00
if socket == node.outputs[0]: # Object Index
return '0.0'
elif socket == node.outputs[0]: # Material Index
return '0.0'
elif socket == node.outputs[0]: # Random
return '0.0'
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
2016-12-17 15:34:43 +01:00
curshader.add_function(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-13 20:06:23 +01:00
return '0.0'
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
2016-12-17 15:34:43 +01:00
curshader.add_function(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':
2016-12-17 15:34:43 +01:00
curshader.add_function(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':
2016-12-17 15:34:43 +01:00
curshader.add_function(str_tex_voronoi)
assets.add(armutils.get_sdk_path() + '/armory/Assets/' + 'noise64.png')
assets.add_embedded_data('noise64.png')
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':
2016-12-13 20:06:23 +01:00
return '0.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):
s = armutils.safe_source_name(s)
if len(parents) > 0:
s = armutils.safe_source_name(parents[-1].name) + '_' + s
return s
def socket_name(s):
return armutils.safe_source_name(s)