def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]], mat_armusers) -> tuple: wrd = bpy.data.worlds['Arm'] rpdat = arm.utils.get_rp() needs_sss = material_needs_sss(material) if needs_sss and rpdat.rp_sss_state != 'Off' and '_SSS' not in wrd.world_defs: # Must be set before calling make_shader.build() wrd.world_defs += '_SSS' # No batch - shader data per material if material.arm_custom_material != '': rpasses = ['mesh'] con = {'vertex_elements': []} con['vertex_elements'].append({'name': 'pos', 'data': 'short4norm'}) con['vertex_elements'].append({'name': 'nor', 'data': 'short2norm'}) con['vertex_elements'].append({'name': 'tex', 'data': 'short2norm'}) con['vertex_elements'].append({'name': 'tex1', 'data': 'short2norm'}) sd = {'contexts': [con]} shader_data_name = material.arm_custom_material bind_constants = {'mesh': []} bind_textures = {'mesh': []} make_shader.make_instancing_and_skinning(material, mat_users) elif not wrd.arm_batch_materials or material.name.startswith('armdefault'): rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build( material, mat_users, mat_armusers) sd = shader_data.sd else: rpasses, shader_data, shader_data_name, bind_constants, bind_textures = mat_batch.get( material) sd = shader_data.sd sss_used = False # Material for rp in rpasses: c = { 'name': rp, 'bind_constants': [] + bind_constants[rp], 'bind_textures': [] + bind_textures[rp], } mat_data['contexts'].append(c) if rp == 'mesh': c['bind_constants'].append({ 'name': 'receiveShadow', 'bool': material.arm_receive_shadow }) if material.arm_material_id != 0: c['bind_constants'].append({ 'name': 'materialID', 'int': material.arm_material_id }) if material.arm_material_id == 2: wrd.world_defs += '_Hair' elif rpdat.rp_sss_state != 'Off': const = {'name': 'materialID'} if needs_sss: const['int'] = 2 sss_used = True else: const['int'] = 0 c['bind_constants'].append(const) # TODO: Mesh only material batching if wrd.arm_batch_materials: # Set textures uniforms if len(c['bind_textures']) > 0: c['bind_textures'] = [] for node in material.node_tree.nodes: if node.type == 'TEX_IMAGE': tex_name = arm.utils.safesrc(node.name) tex = cycles.make_texture(node, tex_name) # Empty texture if tex is None: tex = {'name': tex_name, 'file': ''} c['bind_textures'].append(tex) # Set marked inputs as uniforms for node in material.node_tree.nodes: for inp in node.inputs: if inp.is_uniform: uname = arm.utils.safesrc( inp.node.name) + arm.utils.safesrc( inp.name) # Merge with cycles module c['bind_constants'].append({ 'name': uname, cycles.glsl_type(inp.type): glsl_value(inp.default_value) }) elif rp == 'translucent': c['bind_constants'].append({ 'name': 'receiveShadow', 'bool': material.arm_receive_shadow }) if wrd.arm_single_data_file: mat_data['shader'] = shader_data_name else: # Make sure that custom materials are not expected to be in .arm format ext = '' if wrd.arm_minimize and material.arm_custom_material == "" else '.json' mat_data['shader'] = shader_data_name + ext + '/' + shader_data_name return sd, rpasses, sss_used
def parse(material, mat_data, mat_users, mat_armusers): wrd = bpy.data.worlds['Arm'] rpdat = arm.utils.get_rp() # No batch - shader data per material if material.arm_custom_material != '': rpasses = ['mesh'] sd = {} sd['contexts'] = [] con = {} con['vertex_elements'] = [] elem = {} elem['name'] = 'pos' elem['data'] = 'short4norm' con['vertex_elements'].append(elem) elem = {} elem['name'] = 'nor' elem['data'] = 'short2norm' con['vertex_elements'].append(elem) sd['contexts'].append(con) shader_data_name = material.arm_custom_material bind_constants = {} bind_constants['mesh'] = [] bind_textures = {} bind_textures['mesh'] = [] elif not wrd.arm_batch_materials or material.name.startswith('armdefault'): rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build( material, mat_users, mat_armusers) sd = shader_data.sd else: rpasses, shader_data, shader_data_name, bind_constants, bind_textures = mat_batch.get( material) sd = shader_data.sd # Material for rp in rpasses: c = {} c['name'] = rp c['bind_constants'] = [] + bind_constants[rp] c['bind_textures'] = [] + bind_textures[rp] mat_data['contexts'].append(c) if rp == 'mesh': const = {} const['name'] = 'receiveShadow' const['bool'] = material.arm_receive_shadow c['bind_constants'].append(const) if material.arm_material_id != 0: const = {} const['name'] = 'materialID' const['int'] = material.arm_material_id c['bind_constants'].append(const) if material.arm_material_id == 2: wrd.world_defs += '_Hair' elif rpdat.rp_sss_state == 'On': sss = False sss_node = arm.node_utils.get_node_by_type( material.node_tree, 'SUBSURFACE_SCATTERING') if sss_node != None and sss_node.outputs[ 0].is_linked: # Check linked node sss = True sss_node = arm.node_utils.get_node_by_type( material.node_tree, 'BSDF_PRINCIPLED') if sss_node != None and sss_node.outputs[0].is_linked and ( sss_node.inputs[1].is_linked or sss_node.inputs[1].default_value != 0.0): sss = True sss_node = arm.node_utils.get_node_armorypbr( material.node_tree) if sss_node != None and sss_node.outputs[0].is_linked and ( sss_node.inputs[8].is_linked or sss_node.inputs[8].default_value != 0.0): sss = True const = {} const['name'] = 'materialID' if sss: const['int'] = 2 else: const['int'] = 0 c['bind_constants'].append(const) # TODO: Mesh only material batching if wrd.arm_batch_materials: # Set textures uniforms if len(c['bind_textures']) > 0: c['bind_textures'] = [] for node in material.node_tree.nodes: if node.type == 'TEX_IMAGE': tex_name = arm.utils.safesrc(node.name) tex = cycles.make_texture(node, tex_name) if tex == None: # Empty texture tex = {} tex['name'] = tex_name tex['file'] = '' c['bind_textures'].append(tex) # Set marked inputs as uniforms for node in material.node_tree.nodes: for inp in node.inputs: if inp.is_uniform: uname = arm.utils.safesrc( inp.node.name) + arm.utils.safesrc( inp.name) # Merge with cycles const = {} const['name'] = uname const[glsl_type(inp.type)] = glsl_value( inp.default_value) c['bind_constants'].append(const) elif rp == 'translucent': const = {} const['name'] = 'receiveShadow' const['bool'] = material.arm_receive_shadow c['bind_constants'].append(const) ext = '' if wrd.arm_minimize else '.json' mat_data['shader'] = shader_data_name + ext + '/' + shader_data_name return sd, rpasses
def parse_tex_image(node: bpy.types.ShaderNodeTexImage, out_socket: bpy.types.NodeSocket, state: ParserState) -> Union[floatstr, vec3str]: if state.context == ParserContext.OBJECT: # Color or Alpha output use_color_out = out_socket == node.outputs[0] # Already fetched if c.is_parsed(c.store_var_name(node)): if use_color_out: return f'{c.store_var_name(node)}.rgb' else: return f'{c.store_var_name(node)}.a' tex_name = c.node_name(node.name) tex = c.make_texture(node, tex_name) tex_link = None tex_default_file = None is_arm_mat_param = None if node.arm_material_param: tex_link = node.name is_arm_mat_param = True if tex is not None: state.curshader.write_textures += 1 if node.arm_material_param and tex['file'] is not None: tex_default_file = tex['file'] if use_color_out: to_linear = node.image is not None and node.image.colorspace_settings.name == 'sRGB' res = f'{c.texture_store(node, tex, tex_name, to_linear, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.rgb' else: res = f'{c.texture_store(node, tex, tex_name, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.a' state.curshader.write_textures -= 1 return res # Empty texture elif node.image is None: tex = { 'name': tex_name, 'file': '' } if use_color_out: return '{0}.rgb'.format(c.texture_store(node, tex, tex_name, to_linear=False, tex_link=tex_link, is_arm_mat_param=is_arm_mat_param)) return '{0}.a'.format(c.texture_store(node, tex, tex_name, to_linear=True, tex_link=tex_link, is_arm_mat_param=is_arm_mat_param)) # Pink color for missing texture else: tex_store = c.store_var_name(node) if use_color_out: state.parsed.add(tex_store) state.curshader.write_textures += 1 state.curshader.write(f'vec4 {tex_store} = vec4(1.0, 0.0, 1.0, 1.0);') state.curshader.write_textures -= 1 return f'{tex_store}.rgb' else: state.curshader.write(f'vec4 {tex_store} = vec4(1.0, 0.0, 1.0, 1.0);') return f'{tex_store}.a' # World context # TODO: Merge with above implementation to also allow mappings other than using view coordinates else: world = state.world world.world_defs += '_EnvImg' # Background texture state.curshader.add_uniform('sampler2D envmap', link='_envmap') state.curshader.add_uniform('vec2 screenSize', link='_screenSize') image = node.image filepath = image.filepath if image.packed_file is not None: # Extract packed data filepath = arm.utils.build_dir() + '/compiled/Assets/unpacked' unpack_path = arm.utils.get_fp() + filepath if not os.path.exists(unpack_path): os.makedirs(unpack_path) unpack_filepath = unpack_path + '/' + image.name if not os.path.isfile(unpack_filepath) or os.path.getsize(unpack_filepath) != image.packed_file.size: with open(unpack_filepath, 'wb') as f: f.write(image.packed_file.data) assets.add(unpack_filepath) else: # Link image path to assets assets.add(arm.utils.asset_path(image.filepath)) # Reference image name tex_file = arm.utils.extract_filename(image.filepath) base = tex_file.rsplit('.', 1) ext = base[1].lower() if ext == 'hdr': target_format = 'HDR' else: target_format = 'JPEG' # Generate prefiltered envmaps world.arm_envtex_name = tex_file world.arm_envtex_irr_name = tex_file.rsplit('.', 1)[0] disable_hdr = target_format == 'JPEG' from_srgb = image.colorspace_settings.name == "sRGB" rpdat = arm.utils.get_rp() mip_count = world.arm_envtex_num_mips mip_count = write_probes.write_probes(filepath, disable_hdr, from_srgb, mip_count, arm_radiance=rpdat.arm_radiance) world.arm_envtex_num_mips = mip_count # Will have to get rid of gl_FragCoord, pass texture coords from vertex shader state.curshader.write_init('vec2 texco = gl_FragCoord.xy / screenSize;') return 'texture(envmap, vec2(texco.x, 1.0 - texco.y)).rgb * envmapStrength'
def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]], mat_armusers): wrd = bpy.data.worlds['Arm'] rpdat = arm.utils.get_rp() # No batch - shader data per material if material.arm_custom_material != '': rpasses = ['mesh'] con = {'vertex_elements': []} con['vertex_elements'].append({'name': 'pos', 'data': 'short4norm'}) con['vertex_elements'].append({'name': 'nor', 'data': 'short2norm'}) con['vertex_elements'].append({'name': 'tex', 'data': 'short2norm'}) con['vertex_elements'].append({'name': 'tex1', 'data': 'short2norm'}) sd = {'contexts': [con]} shader_data_name = material.arm_custom_material bind_constants = {'mesh': []} bind_textures = {'mesh': []} make_shader.make_instancing_and_skinning(material, mat_users) elif not wrd.arm_batch_materials or material.name.startswith('armdefault'): rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build( material, mat_users, mat_armusers) sd = shader_data.sd else: rpasses, shader_data, shader_data_name, bind_constants, bind_textures = mat_batch.get( material) sd = shader_data.sd # Material for rp in rpasses: c = { 'name': rp, 'bind_constants': [] + bind_constants[rp], 'bind_textures': [] + bind_textures[rp], } mat_data['contexts'].append(c) if rp == 'mesh': c['bind_constants'].append({ 'name': 'receiveShadow', 'bool': material.arm_receive_shadow }) if material.arm_material_id != 0: c['bind_constants'].append({ 'name': 'materialID', 'int': material.arm_material_id }) if material.arm_material_id == 2: wrd.world_defs += '_Hair' elif rpdat.rp_sss_state == 'On': sss = False sss_node = arm.node_utils.get_node_by_type( material.node_tree, 'SUBSURFACE_SCATTERING') if sss_node is not None and sss_node.outputs[ 0].is_linked: # Check linked node sss = True sss_node = arm.node_utils.get_node_by_type( material.node_tree, 'BSDF_PRINCIPLED') if sss_node is not None and sss_node.outputs[0].is_linked and ( sss_node.inputs[1].is_linked or sss_node.inputs[1].default_value != 0.0): sss = True sss_node = arm.node_utils.get_node_armorypbr( material.node_tree) if sss_node is not None and sss_node.outputs[0].is_linked and ( sss_node.inputs[8].is_linked or sss_node.inputs[8].default_value != 0.0): sss = True const = {'name': 'materialID'} if sss: const['int'] = 2 else: const['int'] = 0 c['bind_constants'].append(const) # TODO: Mesh only material batching if wrd.arm_batch_materials: # Set textures uniforms if len(c['bind_textures']) > 0: c['bind_textures'] = [] for node in material.node_tree.nodes: if node.type == 'TEX_IMAGE': tex_name = arm.utils.safesrc(node.name) tex = cycles.make_texture(node, tex_name) # Empty texture if tex is None: tex = {'name': tex_name, 'file': ''} c['bind_textures'].append(tex) # Set marked inputs as uniforms for node in material.node_tree.nodes: for inp in node.inputs: if inp.is_uniform: uname = arm.utils.safesrc( inp.node.name) + arm.utils.safesrc( inp.name) # Merge with cycles module c['bind_constants'].append({ 'name': uname, cycles.glsl_type(inp.type): glsl_value(inp.default_value) }) elif rp == 'translucent': c['bind_constants'].append({ 'name': 'receiveShadow', 'bool': material.arm_receive_shadow }) if wrd.arm_single_data_file: mat_data['shader'] = shader_data_name else: ext = '' if wrd.arm_minimize else '.json' mat_data['shader'] = shader_data_name + ext + '/' + shader_data_name return sd, rpasses