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)
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})'
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)
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))'