from typing import Generator import bpy import lnx.utils import lnx.make_state as make_state import lnx.material.cycles as cycles import lnx.assets as assets import lnx.log as log if lnx.is_reload(__name__): lnx.utils = lnx.reload_module(lnx.utils) make_state = lnx.reload_module(make_state) cycles = lnx.reload_module(cycles) log = lnx.reload_module(log) else: lnx.enable_reload(__name__) add_mesh_contexts = [] def disp_linked(output_node): linked = output_node.inputs[2].is_linked if not linked: return False # Leenkx PBR with unlinked height socket l = output_node.inputs[2].links[0] if l.from_node.type == 'GROUP' and l.from_node.node_tree.name.startswith('Leenkx PBR') and \ l.from_node.inputs[7].is_linked == False: return False disp_enabled = lnx.utils.disp_enabled(make_state.target) rpdat = lnx.utils.get_rp() if not disp_enabled and rpdat.lnx_rp_displacement == 'Tessellation': log.warn('Tessellation not available on ' + make_state.target) return disp_enabled def get_rpasses(material): ar = [] rpdat = lnx.utils.get_rp() has_voxels = lnx.utils.voxel_support() if material.lnx_decal: ar.append('decal') elif material.lnx_overlay: ar.append('overlay') else: ar.append('mesh') for con in add_mesh_contexts: ar.append(con) if is_transluc(material) and not material.lnx_discard and rpdat.rp_translucency_state != 'Off' and not material.lnx_blending and not rpdat.rp_ss_refraction: ar.append('translucent') elif is_transluc(material) and not material.lnx_discard and not material.lnx_blending and rpdat.rp_ss_refraction: ar.append('refraction') if rpdat.rp_voxels != "Off" and has_voxels: ar.append('voxel') if rpdat.rp_renderer == 'Forward' and rpdat.rp_depthprepass and not material.lnx_blending and not material.lnx_particle_flag: ar.append('depth') if material.lnx_cast_shadow and rpdat.rp_shadows and ('mesh' in ar): if 'translucent' in ar or 'refraction' in ar: ar.append('shadowmap_transparent') else: ar.append('shadowmap') return ar def is_transluc(material): nodes = material.node_tree.nodes output_node = cycles.node_by_type(nodes, 'OUTPUT_MATERIAL') if output_node == None or output_node.inputs[0].is_linked == False: return False surface_node = output_node.inputs[0].links[0].from_node return is_transluc_traverse(surface_node) def is_transluc_traverse(node): # TODO: traverse groups if is_transluc_type(node): return True for inp in node.inputs: if inp.is_linked: res = is_transluc_traverse(inp.links[0].from_node) if res: return True return False def is_transluc_type(node: bpy.types.ShaderNode) -> bool: return node.type in ('BSDF_GLASS', 'BSDF_TRANSPARENT', 'BSDF_TRANSLUCENT', 'BSDF_REFRACTION') \ or (is_leenkx_pbr_node(node) and (node.inputs['Opacity'].is_linked or node.inputs['Opacity'].default_value != 1.0)) \ or (node.type == 'BSDF_PRINCIPLED' and (node.inputs['Alpha'].is_linked or node.inputs['Alpha'].default_value != 1.0)) def is_leenkx_pbr_node(node: bpy.types.ShaderNode) -> bool: return node.type == 'GROUP' and node.node_tree.name.startswith('Leenkx PBR') def iter_nodes_leenkxpbr(node_group: bpy.types.NodeTree) -> Generator[bpy.types.Node, None, None]: for node in node_group.nodes: if is_leenkx_pbr_node(node): yield node def equals_color_socket(socket: bpy.types.NodeSocketColor, value: tuple[float, ...], *, comp_alpha=True) -> bool: # NodeSocketColor.default_value is of bpy_prop_array type that doesn't # support direct comparison return ( socket.default_value[0] == value[0] and socket.default_value[1] == value[1] and socket.default_value[2] == value[2] and (socket.default_value[3] == value[3] if comp_alpha else True) )