def _disable_all_denoiser(): """ Disables all denoiser. At the moment this includes the cycles and the intel denoiser. """ # Disable cycles denoiser bpy.context.view_layer.cycles.use_denoising = False # Disable intel denoiser if bpy.context.scene.use_nodes: nodes = bpy.context.scene.node_tree.nodes links = bpy.context.scene.node_tree.links # Go through all existing denoiser nodes for denoiser_node in Utility.get_nodes_with_type(nodes, 'CompositorNodeDenoise'): in_node = denoiser_node.inputs['Image'] out_node = denoiser_node.outputs['Image'] # If it is fully included into the node tree if in_node.is_linked and out_node.is_linked: # There is always only one input link in_link = in_node.links[0] # Connect from_socket of the incoming link with all to_sockets of the out going links for link in out_node.links: links.new(in_link.from_socket, link.to_socket) # Finally remove the denoiser node nodes.remove(denoiser_node)
def get_nodes_with_type(self, node_type: str) -> List[bpy.types.Node]: """ Returns all nodes which are of the given node_type :param node_type: The note type to look for. :return: The list of nodes with the given type. """ return Utility.get_nodes_with_type(self.nodes, node_type)
def add_nodes_to_group(nodes: bpy.types.Node, group_name: str) -> bpy.types.ShaderNodeTree: """ Creates the node group, copies all attributes and links and adds the group input and output https://blender.stackexchange.com/a/175604 :param nodes: Nodes, which should be used :param group_name: Name of the group :return bpy.types.ShaderNodeTree: the group which can be used inside of a bpy.types.ShaderNodeGroup """ # create new node group group = bpy.data.node_groups.new(name=group_name, type="ShaderNodeTree") # copy all nodes from the list to the created group with all their attributes copy_nodes(nodes, group.nodes) # copy the links between the nodes to the created groups nodes copy_links(nodes, group.nodes, group.links) # add the group input and output node to the created group group_input, group_output = add_group_nodes(group) # check if the selection of nodes goes over a material, if so replace the material output with the output of # the group material_outputs = Utility.get_nodes_with_type(group.nodes, "OutputMaterial") if len(material_outputs) == 1: for input in material_outputs[0].inputs: group.outputs.new(input.bl_idname, input.name) for link in input.links: group.links.new(link.from_socket, group_output.inputs[input.name]) # remove the material output, the material output should never be inside of a group group.nodes.remove(material_outputs[0]) return group
def _rename_materials(): """ Rename all materials based on their texture if they have one This makes the accessing later on easier """ # TODO: should only be done to suncg materials for material in bpy.data.materials: if material.use_nodes: nodes = material.node_tree.nodes textures = Utility.get_nodes_with_type(nodes, "ShaderNodeTexImage") if len(textures) == 1: material.name = textures[0].image.name
def _set_textures(self, ground_tile, images, uv_scaling, ambient_occlusion, displacement_strength): """ Sets available loaded images to a texture of a current processed ground tile. :param ground_tile: Ground tile (plane). Type: bpy.types.Object. :param images: Loaded images of a chosen texture. Type: dict. :param uv_scaling: Scaling factor for the UV layer of the tile. Type: float. """ # get a target material in case ground tile has more than one for material in ground_tile.data.materials.items(): if re.fullmatch(self.target_material, material[0]): mat_obj = bpy.data.materials[material[0]] else: raise Exception( "No RE material " + self.target_material + " found in selected objects. Check if " "constructor.RockEssentialsGroundConstructor module was run at least once and created " "at least one ground tile!") # get the node tree of the current material nodes = mat_obj.node_tree.nodes # get all Image Texture nodes in the tree image_texture_nodes = Utility.get_nodes_with_type( nodes, 'ShaderNodeTexImage') # for each Image Texture node set a texture (image) if one was loaded for node in image_texture_nodes: if node.label in images.keys(): node.image = images[node.label] # get texture name for a displacement modifier of the current ground tile texture_name = ground_tile.name + "_texture" # if displacement map (image) was provided and loaded - set it to the modifier if "displacement" in images.keys(): bpy.data.textures[texture_name].image = images['displacement'] # set ambient occlusion nodes.get("Group").inputs["AO"].default_value = ambient_occlusion # set displacement modifier strength bpy.context.object.modifiers[ "Displace"].strength = displacement_strength # and scale the texture for point in ground_tile.data.uv_layers.active.data[:]: point.uv = point.uv * uv_scaling
def perform_and_condition_check(and_condition, materials, used_materials_to_check=None): """ Checks for all materials in the scene if all given conditions are true, collects them in the return list. :param and_condition: Given conditions. Type: dict. :param materials: Materials, that are already in the return list. Type: list. :param used_materials_to_check: a list of materials to perform the check on. Type: list. Default: all materials :return: Materials that fulfilled given conditions. Type: list. """ new_materials = [] if used_materials_to_check is None: used_materials_to_check = get_all_materials() # through every material for material in used_materials_to_check: if material in new_materials or material in materials or material is None: continue select_material = True for key, value in and_condition.items(): # check if the key is a requested custom property requested_custom_property = False requested_custom_function = False if key.startswith('cp_'): requested_custom_property = True key = key[3:] if key.startswith('cf_'): requested_custom_function = True key = key[3:] if hasattr( material, key ) and not requested_custom_property and not requested_custom_function: # check if the type of the value of attribute matches desired if isinstance(getattr(material, key), type(value)): new_value = value # if not, try to enforce some mathutils-specific type else: if isinstance(getattr(material, key), mathutils.Vector): new_value = mathutils.Vector(value) elif isinstance(getattr(material, key), mathutils.Euler): new_value = mathutils.Euler(value) elif isinstance(getattr(material, key), mathutils.Color): new_value = mathutils.Color(value) # raise an exception if it is none of them else: raise Exception( "Types are not matching: %s and %s !" % (type(getattr(material, key)), type(value))) # or check for equality if not ((isinstance(getattr(material, key), str) and re.fullmatch(value, getattr(material, key)) is not None) or getattr(material, key) == new_value): select_material = False break # check if a custom property with this name exists elif key in material and requested_custom_property: # check if the type of the value of such custom property matches desired if isinstance(material[key], type(value)) or (isinstance( material[key], int) and isinstance(value, bool)): # if it is a string and if the whole string matches the given pattern if not ((isinstance(material[key], str) and re.fullmatch(value, material[key]) is not None) or material[key] == value): select_material = False break else: # raise an exception if not raise Exception( "Types are not matching: {} and {} !".format( type(material[key]), type(value))) elif requested_custom_function: if key.startswith("texture_amount_"): if material.use_nodes: value = int(value) nodes = material.node_tree.nodes texture_nodes = Utility.get_nodes_with_type( nodes, "TexImage") amount_of_texture_nodes = len( texture_nodes ) if texture_nodes is not None else 0 if "min" in key: if not (amount_of_texture_nodes >= value): select_material = False break elif "max" in key: if not (amount_of_texture_nodes <= value): select_material = False break elif "eq" in key: if not (amount_of_texture_nodes == value): select_material = False break else: raise Exception( "This type of key is unknown: {}".format( key)) else: select_material = False break elif key.startswith("principled_bsdf_amount_"): if material.use_nodes: value = int(value) nodes = material.node_tree.nodes principled = Utility.get_nodes_with_type( nodes, "BsdfPrincipled") amount_of_principled_bsdf_nodes = len( principled) if principled is not None else 0 if "min" in key: if not (amount_of_principled_bsdf_nodes >= value): select_material = False break elif "max" in key: if not (amount_of_principled_bsdf_nodes <= value): select_material = False break elif "eq" in key: if not (amount_of_principled_bsdf_nodes == value): select_material = False break else: raise Exception( "This type of key is unknown: {}".format( key)) else: select_material = False break elif key.startswith("principled_bsdf_" ): # must be after the amount check # This custom function can check the value of a certain Principled BSDF shader input. # For example this can be used to avoid using materials, which have an Alpha Texture by # adding they key: `"cf_principled_bsdf_Alpha_eq": 1.0` if material.use_nodes: value = float(value) # first check if there is only one Principled BSDF node in the material nodes = material.node_tree.nodes principled = Utility.get_nodes_with_type( nodes, "BsdfPrincipled") amount_of_principled_bsdf_nodes = len( principled) if principled is not None else 0 if amount_of_principled_bsdf_nodes != 1: select_material = False break principled = principled[0] # then extract the input name from the key, for the Alpha example: `Alpha` extracted_input_name = key[len("principled_bsdf_" ):key.rfind("_")] # check if this key exists, else throw an error if extracted_input_name not in principled.inputs: raise Exception( "Only valid inputs of a principled node are allowed: " "{} in: {}".format(extracted_input_name, key)) # extract this input value used_value = principled.inputs[ extracted_input_name] # if this input value is not a default value it will be connected via the links if len(used_value.links) > 0: select_material = False break # if no link is found check the default value used_value = used_value.default_value # compare the given value to the default value if key.endswith("min"): if not (used_value >= value): select_material = False break elif key.endswith("max"): if not (used_value <= value): select_material = False break elif key.endswith("eq"): if not (used_value == value): select_material = False break else: raise Exception( "This type of key is unknown: {}".format( key)) else: select_material = False break elif key == "use_materials_of_objects": objects = Utility.build_provider_based_on_config( value).run() found_material = False # iterate over all selected objects for obj in objects: # check if they have materials if hasattr(obj, "material_slots"): for mat_slot in obj.material_slots: # if the material is the same as the currently checked one if mat_slot.material == material: found_material = True break if found_material: break if not found_material: select_material = False break else: select_material = False break else: select_material = False break if select_material: new_materials.append(material) return new_materials