예제 #1
0
def parse_value(node: bpy.types.ShaderNodeValue,
                out_socket: bpy.types.NodeSocket,
                state: ParserState) -> floatstr:
    if node.arm_material_param:
        nn = 'param_' + c.node_name(node.name)
        value = c.to_vec1(node.outputs[0].default_value)
        is_arm_mat_param = True
        state.curshader.add_uniform('float {0}'.format(nn),
                                    link='{0}'.format(node.name),
                                    default_value=value,
                                    is_arm_mat_param=is_arm_mat_param)
        return nn
    else:
        return c.to_vec1(node.outputs[0].default_value)
예제 #2
0
def parse_maprange(node: bpy.types.ShaderNodeMapRange,
                   out_socket: bpy.types.NodeSocket,
                   state: ParserState) -> floatstr:

    interp = node.interpolation_type

    value: str = c.parse_value_input(
        node.inputs[0]) if node.inputs[0].is_linked else c.to_vec1(
            node.inputs[0].default_value)
    fromMin = c.parse_value_input(node.inputs[1])
    fromMax = c.parse_value_input(node.inputs[2])
    toMin = c.parse_value_input(node.inputs[3])
    toMax = c.parse_value_input(node.inputs[4])

    if interp == "LINEAR":

        state.curshader.add_function(c_functions.str_map_range_linear)
        return f'map_range_linear({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'

    elif interp == "STEPPED":

        steps = float(c.parse_value_input(node.inputs[5]))
        state.curshader.add_function(c_functions.str_map_range_stepped)
        return f'map_range_stepped({value}, {fromMin}, {fromMax}, {toMin}, {toMax}, {steps})'

    elif interp == "SMOOTHSTEP":

        state.curshader.add_function(c_functions.str_map_range_smoothstep)
        return f'map_range_smoothstep({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'

    elif interp == "SMOOTHERSTEP":

        state.curshader.add_function(c_functions.str_map_range_smootherstep)
        return f'map_range_smootherstep({value}, {fromMin}, {fromMax}, {toMin}, {toMax})'
예제 #3
0
def parse_attribute(node: bpy.types.ShaderNodeAttribute,
                    out_socket: bpy.types.NodeSocket,
                    state: ParserState) -> Union[floatstr, vec3str]:
    out_type = 'float' if out_socket.type == 'VALUE' else 'vec3'

    if node.attribute_name == 'time':
        state.curshader.add_uniform('float time', link='_time')

        if out_socket == node.outputs[3]:
            return '1.0'
        return c.cast_value('time', from_type='float', to_type=out_type)

    # UV maps (higher priority) and vertex colors
    if node.attribute_type == 'GEOMETRY':

        # Alpha output. Armory doesn't support vertex colors with alpha
        # values yet and UV maps don't have an alpha channel
        if out_socket == node.outputs[3]:
            return '1.0'

        # UV maps
        mat = c.mat_get_material()
        mat_users = c.mat_get_material_users()

        if mat_users is not None and mat in mat_users:
            mat_user = mat_users[mat][0]

            # Curves don't have uv layers, so check that first
            if hasattr(mat_user.data, 'uv_layers'):
                lays = mat_user.data.uv_layers

                # First UV map referenced
                if len(lays) > 0 and node.attribute_name == lays[0].name:
                    state.con.add_elem('tex', 'short2norm')
                    return c.cast_value(
                        'vec3(texCoord.x, 1.0 - texCoord.y, 0.0)',
                        from_type='vec3',
                        to_type=out_type)

                # Second UV map referenced
                elif len(lays) > 1 and node.attribute_name == lays[1].name:
                    state.con.add_elem('tex1', 'short2norm')
                    return c.cast_value(
                        'vec3(texCoord1.x, 1.0 - texCoord1.y, 0.0)',
                        from_type='vec3',
                        to_type=out_type)

        # Vertex colors
        # TODO: support multiple vertex color sets
        state.con.add_elem('col', 'short4norm')
        return c.cast_value('vcolor', from_type='vec3', to_type=out_type)

    # Check object properties
    # see https://developer.blender.org/rB6fdcca8de6 for reference
    mat = c.mat_get_material()
    mat_users = c.mat_get_material_users()
    if mat_users is not None and mat in mat_users:
        # Use first material user for now...
        mat_user = mat_users[mat][0]

        val = None
        # Custom properties first
        if node.attribute_name in mat_user:
            val = mat_user[node.attribute_name]
        # Blender properties
        elif hasattr(mat_user, node.attribute_name):
            val = getattr(mat_user, node.attribute_name)

        if val is not None:
            if isinstance(val, float):
                return c.cast_value(str(val),
                                    from_type='float',
                                    to_type=out_type)
            elif isinstance(val, int):
                return c.cast_value(str(val),
                                    from_type='int',
                                    to_type=out_type)
            elif isinstance(val, mathutils.Vector) and len(val) <= 4:
                out = val.to_4d()

                if out_socket == node.outputs[3]:
                    return c.to_vec1(out[3])
                return c.cast_value(c.to_vec3(out),
                                    from_type='vec3',
                                    to_type=out_type)

    # Default values, attribute name did not match
    if out_socket == node.outputs[3]:
        return '1.0'
    return c.cast_value('0.0', from_type='float', to_type=out_type)
예제 #4
0
def parse_valtorgb(node: bpy.types.ShaderNodeValToRGB,
                   out_socket: bpy.types.NodeSocket,
                   state: ParserState) -> Union[floatstr, vec3str]:
    # Alpha (TODO: make ColorRamp calculation vec4-based and split afterwards)
    if out_socket == node.outputs[1]:
        return '1.0'

    input_fac: bpy.types.NodeSocket = node.inputs[0]

    fac: str = c.parse_value_input(
        input_fac) if input_fac.is_linked else c.to_vec1(
            input_fac.default_value)
    interp = node.color_ramp.interpolation
    elems = node.color_ramp.elements

    if len(elems) == 1:
        return c.to_vec3(elems[0].color)

    # Write color array
    # The last entry is included twice so that the interpolation
    # between indices works (no out of bounds error)
    cols_var = c.node_name(node.name).upper() + '_COLS'
    cols_entries = ', '.join(
        f'vec3({elem.color[0]}, {elem.color[1]}, {elem.color[2]})'
        for elem in elems)
    cols_entries += f', vec3({elems[len(elems) - 1].color[0]}, {elems[len(elems) - 1].color[1]}, {elems[len(elems) - 1].color[2]})'
    state.curshader.add_const("vec3",
                              cols_var,
                              cols_entries,
                              array_size=len(elems) + 1)

    fac_var = c.node_name(node.name) + '_fac'
    state.curshader.write(f'float {fac_var} = {fac};')

    # Get index of the nearest left element relative to the factor
    index = '0 + '
    index += ' + '.join([
        f'(({fac_var} > {elems[i].position}) ? 1 : 0)'
        for i in range(1, len(elems))
    ])

    # Write index
    index_var = c.node_name(node.name) + '_i'
    state.curshader.write(f'int {index_var} = {index};')

    if interp == 'CONSTANT':
        return f'{cols_var}[{index_var}]'

    # Linear interpolation
    else:
        # Write factor array
        facs_var = c.node_name(node.name).upper() + '_FACS'
        facs_entries = ', '.join(str(elem.position) for elem in elems)
        # Add one more entry at the rightmost position so that the
        # interpolation between indices works (no out of bounds error)
        facs_entries += ', 1.0'
        state.curshader.add_const("float",
                                  facs_var,
                                  facs_entries,
                                  array_size=len(elems) + 1)

        # Mix color
        prev_stop_fac = f'{facs_var}[{index_var}]'
        next_stop_fac = f'{facs_var}[{index_var} + 1]'
        prev_stop_col = f'{cols_var}[{index_var}]'
        next_stop_col = f'{cols_var}[{index_var} + 1]'
        rel_pos = f'({fac_var} - {prev_stop_fac}) * (1.0 / ({next_stop_fac} - {prev_stop_fac}))'
        return f'mix({prev_stop_col}, {next_stop_col}, max({rel_pos}, 0.0))'