Example #1
0
def write_shader(shader, ext, rpass, keep_cache=True):
    if shader == None:
        return
    shader_path = mat_state.path + '/' + armutils.safe_source_name(mat_state.material.name) + '_' + rpass + '.' + ext + '.glsl'
    assets.add_shader(shader_path)
    if not os.path.isfile(shader_path) or keep_cache:
        with open(shader_path, 'w') as f:
            f.write(shader.get())
Example #2
0
 def __init__(self, material):
     self.material = material
     self.contexts = []
     self.sd = {}
     self.data = {}
     self.data['shader_datas'] = [self.sd]
     self.matname = armutils.safe_source_name(material.name)
     self.sd['name'] = self.matname + '_data'
     self.sd['vertex_structure'] = []
     self.sd['contexts'] = []
Example #3
0
 def __init__(self, material):
     self.material = material
     self.contexts = []
     self.sd = {}
     self.data = {}
     self.data['shader_datas'] = [self.sd]
     self.matname = armutils.safe_source_name(material.name)
     self.sd['name'] = self.matname + '_data'
     self.sd['vertex_structure'] = []
     self.sd['contexts'] = []
Example #4
0
def write_shader(shader, ext, rpass, keep_cache=True):
    if shader == None:
        return
    shader_rel_path = mat_state.rel_path + '/' + armutils.safe_source_name(
        mat_state.material.name) + '_' + rpass + '.' + ext + '.glsl'
    shader_path = armutils.get_fp() + '/' + shader_rel_path
    assets.add_shader(shader_rel_path)
    if not os.path.isfile(shader_path) or not keep_cache:
        with open(shader_path, 'w') as f:
            f.write(shader.get())
Example #5
0
def build_node_tree(node_group):
    path = "Sources/" + bpy.data.worlds["Arm"].arm_project_package.replace(".", "/") + "/node/"
    node_group_name = armutils.safe_source_name(node_group.name)

    with open(path + node_group_name + ".hx", "w") as f:
        f.write("package " + bpy.data.worlds["Arm"].arm_project_package + ".node;\n\n")
        f.write("import armory.logicnode.*;\n\n")
        f.write("@:keep class " + node_group_name + " extends armory.trait.internal.NodeExecutor {\n\n")
        f.write("\tpublic function new() { super(); notifyOnAdd(add); }\n\n")
        f.write("\tfunction add() {\n")
        # Make sure root node exists
        roots = get_root_nodes(node_group)
        created_nodes = []
        for rn in roots:
            name = "_" + rn.name.replace(".", "_").replace(" ", "")
            buildNode(node_group, rn, f, created_nodes)
            f.write("\n\t\tstart(" + name + ");\n\n")
        f.write("\t}\n")
        f.write("}\n")
Example #6
0
    def __init__(self, material, shader_data, props):
        self.vert = None
        self.frag = None
        self.geom = None
        self.tesc = None
        self.tese = None
        self.material = material
        self.matname = armutils.safe_source_name(material.name)
        self.shader_data = shader_data
        self.data = {}
        self.data['name'] = props['name']
        self.data['depth_write'] = props['depth_write']
        self.data['compare_mode'] = props['compare_mode']
        self.data['cull_mode'] = props['cull_mode']
        if 'blend_source' in props:
            self.data['blend_source'] = props['blend_source']
        if 'blend_destination' in props:
            self.data['blend_destination'] = props['blend_destination']
        if 'blend_operation' in props:
            self.data['blend_operation'] = props['blend_operation']
        if 'alpha_blend_source' in props:
            self.data['alpha_blend_source'] = props['alpha_blend_source']
        if 'alpha_blend_destination' in props:
            self.data['alpha_blend_destination'] = props[
                'alpha_blend_destination']
        if 'alpha_blend_operation' in props:
            self.data['alpha_blend_operation'] = props['alpha_blend_operation']
        if 'color_write_red' in props:
            self.data['color_write_red'] = props['color_write_red']
        if 'color_write_green' in props:
            self.data['color_write_green'] = props['color_write_green']
        if 'color_write_blue' in props:
            self.data['color_write_blue'] = props['color_write_blue']
        if 'color_write_alpha' in props:
            self.data['color_write_alpha'] = props['color_write_alpha']

        self.data['texture_units'] = []
        self.tunits = self.data['texture_units']
        self.data['constants'] = []
        self.constants = self.data['constants']
Example #7
0
    def __init__(self, material, shader_data, props):
        self.vert = None
        self.frag = None
        self.geom = None
        self.tesc = None
        self.tese = None
        self.material = material
        self.matname = armutils.safe_source_name(material.name)
        self.shader_data = shader_data
        self.data = {}
        self.data['name'] = props['name']
        self.data['depth_write'] = props['depth_write']
        self.data['compare_mode'] = props['compare_mode']
        self.data['cull_mode'] = props['cull_mode']
        if 'blend_source' in props:
            self.data['blend_source'] = props['blend_source']
        if 'blend_destination' in props:
            self.data['blend_destination'] = props['blend_destination']
        if 'blend_operation' in props:
            self.data['blend_operation'] = props['blend_operation']
        if 'alpha_blend_source' in props:
            self.data['alpha_blend_source'] = props['alpha_blend_source']
        if 'alpha_blend_destination' in props:
            self.data['alpha_blend_destination'] = props['alpha_blend_destination']
        if 'alpha_blend_operation' in props:
            self.data['alpha_blend_operation'] = props['alpha_blend_operation']
        if 'color_write_red' in props:
            self.data['color_write_red'] = props['color_write_red']
        if 'color_write_green' in props:
            self.data['color_write_green'] = props['color_write_green']
        if 'color_write_blue' in props:
            self.data['color_write_blue'] = props['color_write_blue']
        if 'color_write_alpha' in props:
            self.data['color_write_alpha'] = props['color_write_alpha']

        self.data['texture_units'] = []
        self.tunits = self.data['texture_units']
        self.data['constants'] = []
        self.constants = self.data['constants']
Example #8
0
def build_node_tree(node_group):
    path = 'Sources/' + bpy.data.worlds['Arm'].arm_project_package.replace(
        '.', '/') + '/node/'
    node_group_name = armutils.safe_source_name(node_group.name)

    with open(path + node_group_name + '.hx', 'w') as f:
        f.write('package ' + bpy.data.worlds['Arm'].arm_project_package +
                '.node;\n\n')
        f.write('import armory.logicnode.*;\n\n')
        f.write('@:keep class ' + node_group_name +
                ' extends armory.Trait {\n\n')
        f.write('\tpublic function new() { super(); notifyOnAdd(add); }\n\n')
        f.write('\tfunction add() {\n')
        # Make sure root node exists
        roots = get_root_nodes(node_group)
        created_nodes = []
        print(roots)
        for rn in roots:
            name = '_' + rn.name.replace('.', '_').replace(' ', '')
            buildNode(node_group, rn, f, created_nodes)
        f.write('\t}\n')
        f.write('}\n')
Example #9
0
def socket_name(s):
    return armutils.safe_source_name(s)
Example #10
0
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
Example #11
0
def parse(material, mat_data, mat_users, rid):
    wrd = bpy.data.worlds['Arm']
    mat_state.material = material
    mat_state.nodes = material.node_tree.nodes
    mat_state.mat_data = mat_data
    mat_state.mat_users = mat_users
    mat_state.output_node = cycles.node_by_type(mat_state.nodes, 'OUTPUT_MATERIAL')
    if mat_state.output_node == None:
        return None
    matname = armutils.safe_source_name(material.name)
    mat_state.path = armutils.get_fp() + '/build/compiled/ShaderRaws/' + matname
    if not os.path.exists(mat_state.path):
        os.makedirs(mat_state.path)

    mat_state.data = ShaderData(material)
    mat_state.data.add_elem('pos', 3)
    mat_state.data.add_elem('nor', 3)

    if mat_users != None:
        for bo in mat_users[material]:
            # GPU Skinning
            if bo.find_armature() and armutils.is_bone_animation_enabled(bo) and wrd.generate_gpu_skin == True:
                mat_state.data.add_elem('bone', 4)
                mat_state.data.add_elem('weight', 4)
            
            # Instancing
            if bo.instanced_children or len(bo.particle_systems) > 0:
                mat_state.data.add_elem('off', 3)

    rpasses = mat_utils.get_rpasses(material)

    for rp in rpasses:
        c = {}
        c['name'] = rp
        c['bind_constants'] = []
        c['bind_textures'] = []
        mat_state.mat_data['contexts'].append(c)
        mat_state.mat_context = c

        if rp == 'mesh':
            const = {}
            const['name'] = 'receiveShadow'
            const['bool'] = material.receive_shadow
            c['bind_constants'].append(const)
            con = make_mesh.make(rp, rid)

        elif rp == 'shadowmap':
            con = make_shadowmap.make(rp, rpasses)

        elif rp == 'translucent':
            const = {}
            const['name'] = 'receiveShadow'
            const['bool'] = material.receive_shadow
            c['bind_constants'].append(const)
            con = make_transluc.make(rp)

        elif rp == 'overlay':
            con = make_overlay.make(rp)

        elif rp == 'decal':
            con = make_decal.make(rp)

        elif rp == 'depth':
            con = make_depth.make(rp)
        
        write_shaders(con, rp)

    armutils.write_arm(mat_state.path + '/' + matname + '_data.arm', mat_state.data.get())

    shader_data_name = matname + '_data'
    shader_data_path = 'build/compiled/ShaderRaws/' + matname + '/' + shader_data_name + '.arm'
    assets.add_shader_data(shader_data_path)
    mat_data['shader'] = shader_data_name + '/' + shader_data_name

    return mat_state.data.sd
Example #12
0
def parse(material, mat_data, mat_users, mat_armusers, rid):
    wrd = bpy.data.worlds['Arm']
    mat_state.material = material
    mat_state.nodes = material.node_tree.nodes
    mat_state.mat_data = mat_data
    mat_state.mat_users = mat_users
    mat_state.mat_armusers = mat_armusers
    mat_state.output_node = cycles.node_by_type(mat_state.nodes,
                                                'OUTPUT_MATERIAL')
    if mat_state.output_node == None:
        return None
    matname = armutils.safe_source_name(material.name)
    mat_state.rel_path = 'build/compiled/ShaderRaws/' + matname
    mat_state.path = armutils.get_fp() + '/' + mat_state.rel_path
    if not os.path.exists(mat_state.path):
        os.makedirs(mat_state.path)

    mat_state.data = ShaderData(material)
    mat_state.data.add_elem('pos', 3)
    mat_state.data.add_elem('nor', 3)

    if mat_users != None:
        for bo in mat_users[material]:
            # GPU Skinning
            if bo.find_armature() and armutils.is_bone_animation_enabled(
                    bo) and wrd.generate_gpu_skin == True:
                mat_state.data.add_elem('bone', 4)
                mat_state.data.add_elem('weight', 4)

            # Instancing
            if bo.instanced_children or len(bo.particle_systems) > 0:
                mat_state.data.add_elem('off', 3)

    rpasses = mat_utils.get_rpasses(material)

    for rp in rpasses:
        c = {}
        c['name'] = rp
        c['bind_constants'] = []
        c['bind_textures'] = []
        mat_state.mat_data['contexts'].append(c)
        mat_state.mat_context = c

        if rp == 'mesh':
            const = {}
            const['name'] = 'receiveShadow'
            const['bool'] = material.receive_shadow
            c['bind_constants'].append(const)
            con = mesh_make(rp, rid)

        elif rp == 'shadowmap':
            con = make_shadowmap.make(rp, rpasses)

        elif rp == 'translucent':
            const = {}
            const['name'] = 'receiveShadow'
            const['bool'] = material.receive_shadow
            c['bind_constants'].append(const)
            con = make_transluc.make(rp)

        elif rp == 'overlay':
            con = make_overlay.make(rp)

        elif rp == 'decal':
            con = make_decal.make(rp)

        elif rp == 'depth':
            con = make_depth.make(rp)

        elif rp == 'voxel':
            con = make_voxel.make(rp)

        elif rpass_hook != None:
            con = rpass_hook(rp)

        write_shaders(con, rp)

    armutils.write_arm(mat_state.path + '/' + matname + '_data.arm',
                       mat_state.data.get())

    shader_data_name = matname + '_data'
    shader_data_path = 'build/compiled/ShaderRaws/' + matname + '/' + shader_data_name + '.arm'
    assets.add_shader_data(shader_data_path)
    mat_data['shader'] = shader_data_name + '/' + shader_data_name

    return mat_state.data.sd, 'translucent' in rpasses, 'overlay' in rpasses, 'decal' in rpasses
Example #13
0
def parse_rgb(node, socket):

    if node.type == 'GROUP':
        return parse_group(node, socket)

    elif node.type == 'GROUP_INPUT':
        return parse_group_input(node, socket)

    elif node.type == 'ATTRIBUTE':
        # Vcols only for now
        # node.attribute_name
        mat_state.data.add_elem('col', 3)
        return 'vcolor'

    elif node.type == 'RGB':
        return tovec3(socket.default_value)

    elif node.type == 'TEX_BRICK':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_CHECKER':
        curshader.add_function(functions.str_tex_checker)
        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])
        return 'tex_checker({0}, {1}, {2}, {3})'.format(co, col1, col2, scale)

    elif node.type == 'TEX_ENVIRONMENT':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_GRADIENT':
        if node.inputs[0].is_linked:
            co = parse_vector_input(node.inputs[0])
        else:
            co = 'wposition'
        grad = node.gradient_type
        if grad == 'LINEAR':
            f = '{0}.x'.format(co)
        elif grad == 'QUADRATIC':
            f = '0.0'
        elif grad == 'EASING':
            f = '0.0'
        elif grad == 'DIAGONAL':
            f = '({0}.x + {0}.y) * 0.5'.format(co)
        elif grad == 'RADIAL':
            f = 'atan({0}.y, {0}.x) / PI2 + 0.5'.format(co)
        elif grad == 'QUADRATIC_SPHERE':
            f = '0.0'
        elif grad == 'SPHERICAL':
            f = 'max(1.0 - sqrt({0}.x * {0}.x + {0}.y * {0}.y + {0}.z * {0}.z), 0.0)'.format(
                co)
        return 'vec3(clamp({0}, 0.0, 1.0))'.format(f)

    elif node.type == 'TEX_IMAGE':
        # Already fetched
        if res_var_name(node, node.outputs[1]) in parsed:
            return '{0}.rgb'.format(store_var_name(node))
        tex_name = armutils.safe_source_name(node.name)
        tex = texture.make_texture(node, tex_name)
        if tex != None:
            to_linear = parsing_basecol and not tex['file'].endswith('.hdr')
            return '{0}.rgb'.format(
                texture_store(node, tex, tex_name, to_linear))
        elif node.image == None:  # Empty texture
            tex = {}
            tex['name'] = tex_name
            tex['file'] = ''
            return '{0}.rgb'.format(texture_store(node, tex, tex_name, True))
        else:
            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)

    elif node.type == 'TEX_MAGIC':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_MUSGRAVE':
        # Fall back to noise
        curshader.add_function(functions.str_tex_noise)
        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)

    elif node.type == 'TEX_NOISE':
        curshader.add_function(functions.str_tex_noise)
        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])
        # Slow..
        return 'vec3(tex_noise({0} * {1}), tex_noise({0} * {1} + 0.33), tex_noise({0} * {1} + 0.66))'.format(
            co, scale)

    elif node.type == 'TEX_POINTDENSITY':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_SKY':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_VORONOI':
        curshader.add_function(functions.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')
        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)

    elif node.type == 'TEX_WAVE':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'BRIGHTCONTRAST':
        out_col = parse_vector_input(node.inputs[0])
        bright = parse_value_input(node.inputs[1])
        contr = parse_value_input(node.inputs[2])
        curshader.add_function(\
"""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)

    elif node.type == 'GAMMA':
        out_col = parse_vector_input(node.inputs[0])
        gamma = parse_value_input(node.inputs[1])
        return 'pow({0}, vec3({1}))'.format(out_col, gamma)

    elif node.type == 'HUE_SAT':
        #         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])
        #         curshader.add_function(\
        # """vec3 hue_sat(const float hue, const float sat, const float val, const float fac, const vec3 col) {
        # }
        # """)
        return out_col

    elif node.type == 'INVERT':
        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)

    elif node.type == 'MIX_RGB':
        fac = parse_value_input(node.inputs[0])
        fac_var = node_name(node.name) + '_fac'
        curshader.write('float {0} = {1};'.format(fac_var, fac))
        col1 = parse_vector_input(node.inputs[1])
        col2 = parse_vector_input(node.inputs[2])
        blend = node.blend_type
        if blend == 'MIX':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var)
        elif blend == 'ADD':
            out_col = 'mix({0}, {0} + {1}, {2})'.format(col1, col2, fac_var)
        elif blend == 'MULTIPLY':
            out_col = 'mix({0}, {0} * {1}, {2})'.format(col1, col2, fac_var)
        elif blend == 'SUBTRACT':
            out_col = 'mix({0}, {0} - {1}, {2})'.format(col1, col2, fac_var)
        elif blend == 'SCREEN':
            out_col = '(vec3(1.0) - (vec3(1.0 - {2}) + {2} * (vec3(1.0) - {1})) * (vec3(1.0) - {0}))'.format(
                col1, col2, fac_var)
        elif blend == 'DIVIDE':
            out_col = '(vec3((1.0 - {2}) * {0} + {2} * {0} / {1}))'.format(
                col1, col2, fac_var)
        elif blend == 'DIFFERENCE':
            out_col = 'mix({0}, abs({0} - {1}), {2})'.format(
                col1, col2, fac_var)
        elif blend == 'DARKEN':
            out_col = 'min({0}, {1} * {2})'.format(col1, col2, fac_var)
        elif blend == 'LIGHTEN':
            out_col = 'max({0}, {1} * {2})'.format(col1, col2, fac_var)
        elif blend == 'OVERLAY':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2,
                                                  fac_var)  # Revert to mix
        elif blend == 'DODGE':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2,
                                                  fac_var)  # Revert to mix
        elif blend == 'BURN':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2,
                                                  fac_var)  # Revert to mix
        elif blend == 'HUE':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2,
                                                  fac_var)  # Revert to mix
        elif blend == 'SATURATION':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2,
                                                  fac_var)  # Revert to mix
        elif blend == 'VALUE':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2,
                                                  fac_var)  # Revert to mix
        elif blend == 'COLOR':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2,
                                                  fac_var)  # Revert to mix
        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':
            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)
        if node.use_clamp:
            return 'clamp({0}, vec3(0.0), vec3(1.0))'.format(out_col)
        else:
            return out_col

    elif node.type == 'CURVE_RGB':
        # Pass throuh
        return parse_vector_input(node.inputs[1])

    elif node.type == 'BLACKBODY':
        # Pass constant
        return tovec3([0.84, 0.38, 0.0])

    elif node.type == 'VALTORGB':  # ColorRamp
        fac = parse_value_input(node.inputs[0])
        interp = node.color_ramp.interpolation
        elems = node.color_ramp.elements
        if len(elems) == 1:
            return tovec3(elems[0].color)
        if interp == 'CONSTANT':
            fac_var = node_name(node.name) + '_fac'
            curshader.write('float {0} = {1};'.format(fac_var, fac))
            # Get index
            out_i = '0'
            for i in range(1, len(elems)):
                out_i += ' + ({0} > {1} ? 1 : 0)'.format(
                    fac_var, elems[i].position)
            # Write cols array
            cols_var = node_name(node.name) + '_cols'
            curshader.write('vec3 {0}[{1}];'.format(cols_var, len(elems)))
            for i in range(0, len(elems)):
                curshader.write('{0}[{1}] = vec3({2}, {3}, {4});'.format(
                    cols_var, i, elems[i].color[0], elems[i].color[1],
                    elems[i].color[2]))
            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)

    elif node.type == 'COMBHSV':
        # 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
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'COMBRGB':
        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)

    elif node.type == 'WAVELENGTH':
        # Pass constant
        return tovec3([0.0, 0.27, 0.19])
Example #14
0
def socket_name(s):
    return armutils.safe_source_name(s)
Example #15
0
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
Example #16
0
def parse_rgb(node, socket):

    if node.type == 'GROUP':
        return parse_group(node, socket)

    elif node.type == 'GROUP_INPUT':
        return parse_input_group(node, socket)

    elif node.type == 'ATTRIBUTE':
        # Vcols only for now
        # node.attribute_name
        mat_state.data.add_elem('col', 3)
        return 'vcolor'

    elif node.type == 'RGB':
        return tovec3(socket.default_value)

    elif node.type == 'TEX_BRICK':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_CHECKER':
        curshader.add_function(functions.str_tex_checker)
        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])
        return 'tex_checker({0}, {1}, {2}, {3})'.format(co, col1, col2, scale)

    elif node.type == 'TEX_ENVIRONMENT':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_GRADIENT':
        if node.inputs[0].is_linked:
            co = parse_vector_input(node.inputs[0])
        else:
            co = 'wposition'
        grad = node.gradient_type
        if grad == 'LINEAR':
            f = '{0}.x'.format(co)
        elif grad == 'QUADRATIC':
            f = '0.0'
        elif grad == 'EASING':
            f = '0.0'
        elif grad == 'DIAGONAL':
            f = '({0}.x + {0}.y) * 0.5'.format(co)
        elif grad == 'RADIAL':
            f = 'atan({0}.y, {0}.x) / PI2 + 0.5'.format(co)
        elif grad == 'QUADRATIC_SPHERE':
            f = '0.0'
        elif grad == 'SPHERICAL':
            f = 'max(1.0 - sqrt({0}.x * {0}.x + {0}.y * {0}.y + {0}.z * {0}.z), 0.0)'.format(co)
        return 'vec3(clamp({0}, 0.0, 1.0))'.format(f)

    elif node.type == 'TEX_IMAGE':
        # Already fetched
        if res_var_name(node, node.outputs[1]) in parsed:
            return '{0}.rgb'.format(store_var_name(node))
        tex_name = armutils.safe_source_name(node.name)
        tex = texture.make_texture(node, tex_name)
        if tex != None:
            return '{0}.rgb'.format(texture_store(node, tex, tex_name))
        else:
            return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_MAGIC':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_MUSGRAVE':
        # Fall back to noise
        curshader.add_function(functions.str_tex_noise)
        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)

    elif node.type == 'TEX_NOISE':
        curshader.add_function(functions.str_tex_noise)
        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])
        # Slow..
        return 'vec3(tex_noise({0} * {1}), tex_noise({0} * {1} + vec3(0.33)), tex_noise({0} * {1} + vec3(0.66)))'.format(co, scale)

    elif node.type == 'TEX_POINTDENSITY':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_SKY':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'TEX_VORONOI':
        curshader.add_function(functions.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')
        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)

    elif node.type == 'TEX_WAVE':
        # Pass through
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'BRIGHTCONTRAST':
        out_col = parse_vector_input(node.inputs[0])
        bright = parse_value_input(node.inputs[1])
        contr = parse_value_input(node.inputs[2])
        curshader.add_function(\
"""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)

    elif node.type == 'GAMMA':
        out_col = parse_vector_input(node.inputs[0])
        gamma = parse_value_input(node.inputs[1])
        return 'pow({0}, vec3({1}))'.format(out_col, gamma)

    elif node.type == 'HUE_SAT':
#         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])
#         curshader.add_function(\
# """vec3 hue_sat(const float hue, const float sat, const float val, const float fac, const vec3 col) {
# }
# """)
        return out_col

    elif node.type == 'INVERT':
        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)

    elif node.type == 'MIX_RGB':
        fac = parse_value_input(node.inputs[0])
        fac_var = node_name(node.name) + '_fac'
        curshader.write('float {0} = {1};'.format(fac_var, fac))
        col1 = parse_vector_input(node.inputs[1])
        col2 = parse_vector_input(node.inputs[2])
        blend = node.blend_type
        if blend == 'MIX':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var)
        elif blend == 'ADD':
            out_col = 'mix({0}, {0} + {1}, {2})'.format(col1, col2, fac_var)
        elif blend == 'MULTIPLY':
            out_col = 'mix({0}, {0} * {1}, {2})'.format(col1, col2, fac_var)
        elif blend == 'SUBTRACT':
            out_col = 'mix({0}, {0} - {1}, {2})'.format(col1, col2, fac_var)
        elif blend == 'SCREEN':
            out_col = '(vec3(1.0) - (vec3(1.0 - {2}) + {2} * (vec3(1.0) - {1})) * (vec3(1.0) - {0}))'.format(col1, col2, fac_var)
        elif blend == 'DIVIDE':
            out_col = '(vec3((1.0 - {2}) * {0} + {2} * {0} / {1}))'.format(col1, col2, fac_var)
        elif blend == 'DIFFERENCE':
            out_col = 'mix({0}, abs({0} - {1}), {2})'.format(col1, col2, fac_var)
        elif blend == 'DARKEN':
            out_col = 'min({0}, {1} * {2})'.format(col1, col2, fac_var)
        elif blend == 'LIGHTEN':
            out_col = 'max({0}, {1} * {2})'.format(col1, col2, fac_var)
        elif blend == 'OVERLAY':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
        elif blend == 'DODGE':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
        elif blend == 'BURN':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
        elif blend == 'HUE':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
        elif blend == 'SATURATION':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
        elif blend == 'VALUE':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
        elif blend == 'COLOR':
            out_col = 'mix({0}, {1}, {2})'.format(col1, col2, fac_var) # Revert to mix
        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':
            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)
        if node.use_clamp:
            return 'clamp({0}, vec3(0.0), vec3(1.0))'.format(out_col)
        else:
            return out_col

    elif node.type == 'CURVE_RGB':
        # Pass throuh
        return parse_vector_input(node.inputs[1])

    elif node.type == 'BLACKBODY':
        # Pass constant
        return tovec3([0.84, 0.38, 0.0])

    elif node.type == 'VALTORGB': # ColorRamp
        fac = parse_value_input(node.inputs[0])
        interp = node.color_ramp.interpolation
        elems = node.color_ramp.elements
        if len(elems) == 1:
            return tovec3(elems[0].color)
        if interp == 'CONSTANT':
            fac_var = node_name(node.name) + '_fac'
            curshader.write('float {0} = {1};'.format(fac_var, fac))
            # Get index
            out_i = '0'
            for i in  range(1, len(elems)):
                out_i += ' + ({0} > {1} ? 1 : 0)'.format(fac_var, elems[i].position)
            # Write cols array
            cols_var = node_name(node.name) + '_cols'
            curshader.write('vec3 {0}[{1}];'.format(cols_var, len(elems)))
            for i in range(0, len(elems)):
                curshader.write('{0}[{1}] = vec3({2}, {3}, {4});'.format(cols_var, i, elems[i].color[0], elems[i].color[1], elems[i].color[2]))
            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)

    elif node.type == 'COMBHSV':
# 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
        return tovec3([0.0, 0.0, 0.0])

    elif node.type == 'COMBRGB':
        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)

    elif node.type == 'WAVELENGTH':
        # Pass constant
        return tovec3([0.0, 0.27, 0.19])
Example #17
0
def parse_value(node, socket):

    if node.type == 'GROUP':
        if node.node_tree.name.startswith('Armory PBR'):
            # Displacement
            if socket == node.outputs[1]:
                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]))
                return res
            else:
                return None
        else:
            return parse_group(node, socket)

    elif node.type == 'GROUP_INPUT':
        return parse_input_group(node, socket)

    elif node.type == 'ATTRIBUTE':
        # Pass time till drivers are implemented
        if node.attribute_name == 'time':
            curshader.add_uniform('float time', link='_time')
            return 'time'
        else:
            return None

    elif node.type == 'CAMERA':
        # View Z Depth
        if socket == node.outputs[1]:
            return 'gl_FragCoord.z'
        # View Distance
        else:
            return 'length(eyeDir)'

    elif node.type == 'FRESNEL':
        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)

    elif node.type == 'NEW_GEOMETRY':
        if socket == node.outputs[6]: # Backfacing
            return '0.0'
        elif socket == node.outputs[7]: # Pointiness
            return '0.0'

    elif node.type == 'HAIR_INFO':
        # Is Strand
        # Intercept
        # Thickness
        pass

    elif node.type == 'LAYER_WEIGHT':
        blend = parse_value_input(node.inputs[0])
        # nor = parse_vector_input(node.inputs[1])
        if socket == node.outputs[0]: # Fresnel
            return 'clamp(pow(1.0 - dotNV, (1.0 - {0}) * 10.0), 0.0, 1.0)'.format(blend)
        elif socket == node.outputs[1]: # Facing
            return '((1.0 - dotNV) * {0})'.format(blend)

    elif node.type == 'LIGHT_PATH':
        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'

    elif node.type == 'OBJECT_INFO':
        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'

    elif node.type == 'PARTICLE_INFO':
        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'

    elif node.type == 'VALUE':
        return tovec1(node.outputs[0].default_value)

    elif node.type == 'WIREFRAME':
        #node.use_pixel_size
        # size = parse_value_input(node.inputs[0])
        return '0.0'

    elif node.type == 'TEX_BRICK':
        return '0.0'

    elif node.type == 'TEX_CHECKER':
        # TODO: do not recompute when color socket is also connected
        curshader.add_function(functions.str_tex_checker)
        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])
        return 'tex_checker({0}, {1}, {2}, {3}).r'.format(co, col1, col2, scale)

    elif node.type == 'TEX_GRADIENT':
        return '0.0'

    elif node.type == 'TEX_IMAGE':
        # Already fetched
        if res_var_name(node, node.outputs[0]) in parsed:
            return '{0}.a'.format(store_var_name(node))
        tex_name = armutils.safe_source_name(node.name)
        tex = texture.make_texture(node, tex_name)
        if tex != None:
            return '{0}.a'.format(texture_store(node, tex, tex_name))
        else:
            return '0.0'

    elif node.type == 'TEX_MAGIC':
        return '0.0'

    elif node.type == 'TEX_MUSGRAVE':
        # Fall back to noise
        curshader.add_function(functions.str_tex_noise)
        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)

    elif node.type == 'TEX_NOISE':
        curshader.add_function(functions.str_tex_noise)
        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)

    elif node.type == 'TEX_POINTDENSITY':
        return '0.0'

    elif node.type == 'TEX_VORONOI':
        curshader.add_function(functions.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')
        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)

    elif node.type == 'TEX_WAVE':
        return '0.0'

    elif node.type == 'LIGHT_FALLOFF':
        return '0.0'

    elif node.type == 'NORMAL':
        nor = parse_vector_input(node.inputs[0])
        return 'dot({0}, {1})'.format(tovec3(node.outputs[0].default_value), nor)

    elif node.type == 'VALTORGB': # ColorRamp
        return '1.0'

    elif node.type == 'MATH':
        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':
            out_val = 'sin({0})'.format(val1)
        elif op == 'COSINE':
            out_val = 'cos({0})'.format(val1)
        elif op == 'TANGENT':
            out_val = 'tan({0})'.format(val1)
        elif op == 'ARCSINE':
            out_val = 'asin({0})'.format(val1)
        elif op == 'ARCCOSINE':
            out_val = 'acos({0})'.format(val1)
        elif op == 'ARCTANGENT':
            out_val = 'atan({0})'.format(val1)
        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':
            # out_val = 'round({0})'.format(val1)
            out_val = 'floor({0} + 0.5)'.format(val1)
        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':
            # out_val = 'float({0} % {1})'.format(val1, val2)
            out_val = 'mod({0}, {1})'.format(val1, val2)
        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

    elif node.type == 'RGBTOBW':
        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)

    elif node.type == 'SEPHSV':
        return '0.0'

    elif node.type == 'SEPRGB':
        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)

    elif node.type == 'SEPXYZ':
        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)

    elif node.type == 'VECT_MATH':
        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'
Example #18
0
def parse_value(node, socket):

    if node.type == 'GROUP':
        if node.node_tree.name.startswith('Armory PBR'):
            # Displacement
            if socket == node.outputs[1]:
                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]))
                return res
            else:
                return None
        else:
            return parse_group(node, socket)

    elif node.type == 'GROUP_INPUT':
        return parse_group_input(node, socket)

    elif node.type == 'ATTRIBUTE':
        # Pass time till drivers are implemented
        if node.attribute_name == 'time':
            curshader.add_uniform('float time', link='_time')
            return 'time'
        else:
            return '0.0'

    elif node.type == 'CAMERA':
        # View Z Depth
        if socket == node.outputs[1]:
            return 'gl_FragCoord.z'
        # View Distance
        else:
            return 'length(eyeDir)'

    elif node.type == 'FRESNEL':
        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)

    elif node.type == 'NEW_GEOMETRY':
        if socket == node.outputs[6]:  # Backfacing
            return '0.0'
        elif socket == node.outputs[7]:  # Pointiness
            return '0.0'

    elif node.type == 'HAIR_INFO':
        # Is Strand
        # Intercept
        # Thickness
        return '0.5'

    elif node.type == 'LAYER_WEIGHT':
        blend = parse_value_input(node.inputs[0])
        # nor = parse_vector_input(node.inputs[1])
        if socket == node.outputs[0]:  # Fresnel
            return 'clamp(pow(1.0 - dotNV, (1.0 - {0}) * 10.0), 0.0, 1.0)'.format(
                blend)
        elif socket == node.outputs[1]:  # Facing
            return '((1.0 - dotNV) * {0})'.format(blend)

    elif node.type == 'LIGHT_PATH':
        if socket == node.outputs[0]:  # Is Camera Ray
            return '1.0'
        elif socket == node.outputs[1]:  # Is Shadow Ray
            return '0.0'
        elif socket == node.outputs[2]:  # Is Diffuse Ray
            return '1.0'
        elif socket == node.outputs[3]:  # Is Glossy Ray
            return '1.0'
        elif socket == node.outputs[4]:  # Is Singular Ray
            return '0.0'
        elif socket == node.outputs[5]:  # Is Reflection Ray
            return '0.0'
        elif socket == node.outputs[6]:  # Is Transmission Ray
            return '0.0'
        elif socket == node.outputs[7]:  # Ray Length
            return '0.0'
        elif socket == node.outputs[8]:  # Ray Depth
            return '0.0'
        elif socket == node.outputs[9]:  # Transparent Depth
            return '0.0'
        elif socket == node.outputs[10]:  # Transmission Depth
            return '0.0'

    elif node.type == 'OBJECT_INFO':
        if socket == node.outputs[0]:  # Object Index
            return '0.0'
        elif socket == node.outputs[1]:  # Material Index
            return '0.0'
        elif socket == node.outputs[2]:  # Random
            return '0.0'

    elif node.type == 'PARTICLE_INFO':
        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'

    elif node.type == 'VALUE':
        return tovec1(node.outputs[0].default_value)

    elif node.type == 'WIREFRAME':
        #node.use_pixel_size
        # size = parse_value_input(node.inputs[0])
        return '0.0'

    elif node.type == 'TEX_BRICK':
        return '0.0'

    elif node.type == 'TEX_CHECKER':
        # TODO: do not recompute when color socket is also connected
        curshader.add_function(functions.str_tex_checker)
        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])
        return 'tex_checker({0}, {1}, {2}, {3}).r'.format(
            co, col1, col2, scale)

    elif node.type == 'TEX_GRADIENT':
        return '0.0'

    elif node.type == 'TEX_IMAGE':
        # Already fetched
        if res_var_name(node, node.outputs[0]) in parsed:
            return '{0}.a'.format(store_var_name(node))
        tex_name = armutils.safe_source_name(node.name)
        tex = texture.make_texture(node, tex_name)
        if tex != None:
            return '{0}.a'.format(texture_store(node, tex, tex_name))
        else:
            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)

    elif node.type == 'TEX_MAGIC':
        return '0.0'

    elif node.type == 'TEX_MUSGRAVE':
        # Fall back to noise
        curshader.add_function(functions.str_tex_noise)
        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)

    elif node.type == 'TEX_NOISE':
        curshader.add_function(functions.str_tex_noise)
        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)

    elif node.type == 'TEX_POINTDENSITY':
        return '0.0'

    elif node.type == 'TEX_VORONOI':
        curshader.add_function(functions.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')
        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)

    elif node.type == 'TEX_WAVE':
        return '0.0'

    elif node.type == 'LIGHT_FALLOFF':
        # Constant, linear, quadratic
        # Shaders default to quadratic for now
        return '1.0'

    elif node.type == 'NORMAL':
        nor = parse_vector_input(node.inputs[0])
        return 'dot({0}, {1})'.format(tovec3(node.outputs[0].default_value),
                                      nor)

    elif node.type == 'VALTORGB':  # ColorRamp
        return '1.0'

    elif node.type == 'MATH':
        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':
            out_val = 'sin({0})'.format(val1)
        elif op == 'COSINE':
            out_val = 'cos({0})'.format(val1)
        elif op == 'TANGENT':
            out_val = 'tan({0})'.format(val1)
        elif op == 'ARCSINE':
            out_val = 'asin({0})'.format(val1)
        elif op == 'ARCCOSINE':
            out_val = 'acos({0})'.format(val1)
        elif op == 'ARCTANGENT':
            out_val = 'atan({0})'.format(val1)
        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':
            # out_val = 'round({0})'.format(val1)
            out_val = 'floor({0} + 0.5)'.format(val1)
        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':
            # out_val = 'float({0} % {1})'.format(val1, val2)
            out_val = 'mod({0}, {1})'.format(val1, val2)
        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

    elif node.type == 'RGBTOBW':
        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)

    elif node.type == 'SEPHSV':
        return '0.0'

    elif node.type == 'SEPRGB':
        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)

    elif node.type == 'SEPXYZ':
        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)

    elif node.type == 'VECT_MATH':
        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'