Source code for blenderproc.python.utility.MaterialGetter

""" This module provides functions to filter materials. """

import re

import mathutils

from blenderproc.python.utility.BlenderUtility import get_all_materials
from blenderproc.python.utility.Utility import Utility


[docs] class MaterialGetter: """ Filters materials. """
[docs] @staticmethod 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(f"Types are not matching: {type(getattr(material, key))} " "and {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(f"Types are not matching: {type(material[key])} and {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(f"This type of key is unknown: {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(f"This type of key is unknown: {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: " f"{extracted_input_name} in: {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(f"This type of key is unknown: {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