def parse_normal_map_color_input(inp): global normal_parsed global parse_teximage_vector if basecol_only: return if inp.is_linked == False: return if normal_parsed: return normal_parsed = True frag.write_pre_header = True parse_teximage_vector = False # Force texCoord for normal map image vector defplus = c_state.get_rp_renderer() == 'Deferred Plus' if not c_state.get_arm_export_tangents() or defplus or c_state.mat_get_material().arm_decal: # Compute TBN matrix frag.write('vec3 texn = ({0}) * 2.0 - 1.0;'.format(parse_vector_input(inp))) frag.add_include('../../Shaders/std/normals.glsl') if defplus: frag.write('mat3 TBN = cotangentFrame(n, -vVec, g2.xy, g2.zw);') else: frag.write('mat3 TBN = cotangentFrame(n, -vVec, texCoord);') frag.write('n = TBN * normalize(texn);') else: frag.write('vec3 n = ({0}) * 2.0 - 1.0;'.format(parse_vector_input(inp))) frag.write('n = normalize(TBN * n);') con.add_elem('tang', 3) parse_teximage_vector = True frag.write_pre_header = False
def parse_value(node, socket): global particle_info if node.type == 'GROUP': if node.node_tree.name.startswith('Armory PBR'): # Displacement if socket == node.outputs[1]: return parse_value_input(node.inputs[7]) 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[1]: # Object Index curshader.add_uniform('float objectInfoIndex', link='_objectInfoIndex') return 'objectInfoIndex' elif socket == node.outputs[2]: # Material Index curshader.add_uniform('float objectInfoMaterialIndex', link='_objectInfoMaterialIndex') return 'objectInfoMaterialIndex' elif socket == node.outputs[3]: # Random curshader.add_uniform('float objectInfoRandom', link='_objectInfoRandom') return 'objectInfoRandom' elif node.type == 'PARTICLE_INFO': if socket == node.outputs[0]: # Index particle_info['index'] = True return 'p_index' if c_state.mat_get_material().arm_particle == 'gpu' else '0.0' elif socket == node.outputs[1]: # Age particle_info['age'] = True return 'p_age' if c_state.mat_get_material().arm_particle == 'gpu' else '0.0' elif socket == node.outputs[2]: # Lifetime particle_info['lifetime'] = True return 'p_lifetime' if c_state.mat_get_material().arm_particle == 'gpu' else '0.0' elif socket == node.outputs[4]: # Size particle_info['size'] = True return '1.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(c_functions.str_tex_checker) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'mposition' 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 = c_state.safesrc(node.name) tex = c_state.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(c_functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'mposition' 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(c_functions.str_tex_noise) if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'mposition' 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(c_functions.str_tex_voronoi) c_state.assets_add(c_state.get_sdk_path() + '/armory/Assets/' + 'noise64.png') c_state.assets_add_embedded_data('noise64.png') curshader.add_uniform('sampler2D snoise', link='_noise64') if node.inputs[0].is_linked: co = parse_vector_input(node.inputs[0]) else: co = 'mposition' 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'
def parse_vector(node, socket): global particle_info if node.type == 'GROUP': return parse_group(node, socket) elif node.type == 'GROUP_INPUT': return parse_group_input(node, socket) elif node.type == 'ATTRIBUTE': # UVMaps only for now con.add_elem('tex', 2) mat = c_state.mat_get_material() mat_users = c_state.mat_get_material_users() if mat_users != None and mat in mat_users: mat_user = mat_users[mat][0] if hasattr(mat_user.data, 'uv_layers'): # No uvlayers for Curve lays = mat_user.data.uv_layers # Second uvmap referenced if len(lays) > 1 and node.attribute_name == lays[1].name: con.add_elem('tex1', 2) return 'texCoord1', 2 return 'texCoord', 2 elif node.type == 'CAMERA': # View Vector return 'vVec' elif node.type == 'NEW_GEOMETRY': 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 'vVec' elif socket == node.outputs[5]: # Parametric return 'mposition' elif node.type == 'HAIR_INFO': return 'vec3(0.0)' # Tangent Normal elif node.type == 'OBJECT_INFO': return 'wposition' elif node.type == 'PARTICLE_INFO': if socket == node.outputs[3]: # Location particle_info['location'] = True return 'p_location' if c_state.mat_get_material().arm_particle == 'gpu' else 'vec3(0.0)' elif socket == node.outputs[5]: # Velocity particle_info['velocity'] = True return 'p_velocity' if c_state.mat_get_material().arm_particle == 'gpu' else 'vec3(0.0)' elif socket == node.outputs[6]: # Angular Velocity particle_info['angular_velocity'] = True return 'vec3(0.0)' elif node.type == 'TANGENT': return 'vec3(0.0)' elif node.type == 'TEX_COORD': #obj = node.object #dupli = node.from_dupli if socket == node.outputs[0]: # Generated return 'vec2(0.0)', 2 elif socket == node.outputs[1]: # Normal return 'vec2(0.0)', 2 elif socket == node.outputs[2]: # UV con.add_elem('tex', 2) return 'texCoord', 2 elif socket == node.outputs[3]: # Object return 'vec2(0.0)', 2 elif socket == node.outputs[4]: # Camera return 'vec2(0.0)', 2 elif socket == node.outputs[5]: # Window return 'vec2(0.0)', 2 elif socket == node.outputs[6]: # Reflection return 'vec2(0.0)', 2 elif node.type == 'UVMAP': #map = node.uv_map #dupli = node.from_dupli return 'vec2(0.0)', 2 elif node.type == 'BUMP': #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]) # Sample height around the normal and compute normal return 'n' elif node.type == 'MAPPING': out = parse_vector_input(node.inputs[0]) if node.scale[0] != 1.0 or node.scale[1] != 1.0 or node.scale[2] != 1.0: out = '({0} * vec2({1}, {2}))'.format(out, node.scale[0], node.scale[1]) if node.translation[0] != 0.0 or node.translation[1] != 0.0 or node.translation[2] != 0.0: out = '({0} + vec2({1}, {2}))'.format(out, node.translation[0], node.translation[1]) # if node.rotation[0] != 0.0 or node.rotation[1] != 0.0 or node.rotation[2] != 0.0: # out.x = x * cos(rotation[0]) - y * sin(rotation[0]) # out.y = y * cos(rotation[0]) + x * sin(rotation[0]) # if node.use_min: # out = max(out, vec2(min[0], min[1])) # if node.use_max: # out = min(out, vec2(max[0], max[1])) return out, 2 elif node.type == 'NORMAL': if socket == node.outputs[0]: return tovec3(node.outputs[0].default_value) elif socket == node.outputs[1]: # TODO: is parse_value path preferred? nor = parse_vector_input(node.inputs[0]) return 'vec3(dot({0}, {1}))'.format(tovec3(node.outputs[0].default_value), nor) elif node.type == 'NORMAL_MAP': if curshader == tese: return parse_vector_input(node.inputs[1]) else: #space = node.space #map = node.uv_map # strength = parse_value_input(node.inputs[0]) parse_normal_map_color_input(node.inputs[1]) # Color return None elif node.type == 'CURVE_VEC': # fac = parse_value_input(node.inputs[0]) # Pass throuh return parse_vector_input(node.inputs[1]) elif node.type == 'VECT_TRANSFORM': #type = node.vector_type #conv_from = node.convert_from #conv_to = node.convert_to # Pass throuh return parse_vector_input(node.inputs[0]) elif node.type == 'COMBXYZ': 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) elif node.type == 'VECT_MATH': 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)