Source code for blenderproc.python.modules.provider.getter.Texture

import json
import re
from random import sample

import mathutils

from blenderproc.python.modules.main.Provider import Provider
from blenderproc.python.utility.BlenderUtility import get_all_textures


[docs]class Texture(Provider): """ Returns a list of textures in accordance with defined conditions. Example 1: Return a list of textures that match a name pattern. .. code-block:: yaml { "provider": "getter.Texture", "conditions": { "name": "ct_.*" } } Example 2: Returns the first texture to: {match the name pattern, AND to have nodes use enabled }, OR {match anothername pattern, AND have a certain value of a cust. prop} .. code-block:: yaml { "provider": "getter.Texture", "index": 0, "conditions": [ { "name": "ct_1.*", "use_nodes": True }, { "name": "ct_2.*", "cp_type": "custom" }] } Example 3: Returns two random textures with a certain value of a custom property. .. code-block:: yaml { "provider": "getter.Texture", "random_samples": 2, "conditions": { "cp_type": "custom" } } **Configuration**: .. list-table:: :widths: 25 100 10 :header-rows: 1 * - Parameter - Description - Type * - conditions - List of dicts/a dict of entries of format {attribute_name: attribute_value}. Entries in a dict are conditions connected with AND, if there multiple dicts are defined (i.e. 'conditions' is a list of dicts, each cell is connected by OR. - list/dict * - conditions/attribute_name - Name of any valid object's attribute, custom property, or custom function. Any given attribute_value of the type string will be treated as a REGULAR EXPRESSION. Also, any attribute_value for a custom property can be a string/int/bool/float, while only attribute_value for valid attributes of objects can be a bool or a list (mathutils.Vector, mathurils.Color and mathutils.Euler are covered by the 'list' type). " In order to specify, what exactly one wants to look for: For attribute: key of the pair must be a valid attribute name. For custom property: key of the pair must start with `cp_` prefix. For calling custom function: key of the pair must start with `cf_` prefix. See table below for supported custom functions. - string * - conditions/attribute_value - Any value to set. . - string, list/Vector, int, bool or float * - index - If set, after the conditions are applied only the entity with the specified index is returned. - int * - random_samples - If set, this Provider returns random_samples objects from the pool of selected ones. Define index or random_samples property, only one is allowed at a time. Default: 0. - int * - check_empty - If this is True, the returned list can not be empty, if it is empty an error will be thrown. Default: False. - bool """ def __init__(self, config): Provider.__init__(self, config)
[docs] def run(self): conditions = self.config.get_raw_dict('conditions') if isinstance(conditions, list): textures = [] # each single condition is treated as and condition for and_condition in conditions: textures.extend(self.perform_and_condition_check(and_condition, textures)) else: # only one condition was given, treat it as and condition textures = self.perform_and_condition_check(conditions, []) random_samples = self.config.get_int("random_samples", 0) has_index = self.config.has_param("index") if has_index and not random_samples: textures = [textures[self.config.get_int("index")]] elif random_samples and not has_index: textures = sample(textures, k=min(random_samples, len(textures))) elif has_index and random_samples: raise RuntimeError("Please, define only one of two: `index` or `random_samples`.") check_if_return_is_empty = self.config.get_bool("check_empty", False) if check_if_return_is_empty and not textures: raise Exception(f"There were no materials selected with the following " f"condition: \n{self._get_conditions_as_string()}") return textures
[docs] def _get_conditions_as_string(self): """ Returns the used conditions as neatly formatted string :return: str: containing the conditions """ conditions = self.config.get_raw_dict('conditions') text = json.dumps(conditions, indent=2, sort_keys=True) # Add indent text = "\n".join(" " * len("Exception: ") + e for e in text.split("\n")) return text
[docs] @staticmethod def perform_and_condition_check(and_condition, textures, used_textures_to_check=None): """ Checks for all textures and if all given conditions are true, collects them in the return list. :param and_condition: Given conditions. Type: dict. :param textures: Textures, that are already in the return list. Type: list. :param used_textures_to_check: Textures to perform the check on. Type: list. Default: all materials :return: Textures that comply with given conditions. Type: list. """ new_textures = [] if used_textures_to_check is None: used_textures_to_check = get_all_textures() for texture in used_textures_to_check: if texture in new_textures or texture in textures: continue select_texture = 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:] raise RuntimeError("Custom functions for texture objects are yet to be implemented!") if hasattr(texture, key) and not requested_custom_property: # check if the type of the value of attribute matches desired if isinstance(getattr(texture, key), type(value)): new_value = value # if not, try to enforce some mathutils-specific type else: if isinstance(getattr(texture, key), mathutils.Vector): new_value = mathutils.Vector(value) elif isinstance(getattr(texture, key), mathutils.Euler): new_value = mathutils.Euler(value) elif isinstance(getattr(texture, 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(texture, key)), type(value))) # or check for equality if not ((isinstance(getattr(texture, key), str) and re.fullmatch(value, getattr(texture, key)) is not None) or getattr(texture, key) == new_value): select_texture = False break # check if a custom property with this name exists elif key in texture and requested_custom_property: # check if the type of the value of such custom property matches desired if isinstance(texture[key], type(value)) or ( isinstance(texture[key], int) and isinstance(value, bool)): # if it is a string and if the whole string matches the given pattern if not ((isinstance(texture[key], str) and re.fullmatch(value, texture[key]) is not None) or texture[key] == value): select_texture = False break else: # raise an exception if not raise Exception("Types are not matching: {} and {} !".format(type(texture[key]), type(value))) else: select_texture = False break if select_texture: new_textures.append(texture) return new_textures