def assign_materials_to_floor_wall_ceiling(self): """ Assigns materials to the floor, wall and ceiling. These are randomly selected from the CCMaterials. This means it is required that the CCMaterialLoader has been executed before, this module is run. """ # first create a uv mapping for each of the three objects for obj in [self.floor_obj, self.wall_obj, self.ceiling_obj]: if obj is not None: bpy.ops.object.select_all(action='DESELECT') obj.select_set(True) bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.uv.cube_project(cube_size=1.0) bpy.ops.object.mode_set(mode='OBJECT') # only select non see through materials config = { "conditions": { "cp_is_cc_texture": True, "cf_principled_bsdf_Alpha_eq": 1.0 } } material_getter = Material(Config(config)) all_cc_materials = material_getter.run() def assign_material(cur_obj: bpy.types.Object, material: bpy.types.Material): """ First all materials are removed and then the given material is assigned :param cur_obj: Current object :param material: Current material, which will be assigned """ # first remove all existing if cur_obj.material_slots: for i in range(len(cur_obj.material_slots)): bpy.ops.object.material_slot_remove({'object': cur_obj}) # add the new one cur_obj.data.materials.append(material) if all_cc_materials: assign_material(self.floor_obj, random.choice(all_cc_materials)) assign_material(self.wall_obj, random.choice(all_cc_materials)) if self.ceiling_obj is not None and self.assign_material_to_ceiling: assign_material(self.ceiling_obj, random.choice(all_cc_materials)) else: warnings.warn( "There were no CCMaterials found, which means the CCMaterialLoader was not executed first!" "No materials have been assigned to the walls, floors and possible ceiling." )
def run(self): # use a loader module to load objects bpy.ops.object.select_all(action='SELECT') previously_selected_objects = set(bpy.context.selected_objects) module_list_config = self.config.get_list("used_loader_config") modules = Utility.initialize_modules(module_list_config) for module in modules: print("Running module " + module.__class__.__name__) module.run() bpy.ops.object.select_all(action='SELECT') loaded_objects = list( set(bpy.context.selected_objects) - previously_selected_objects) # only select non see through materials config = { "conditions": { "cp_is_cc_texture": True, "cf_principled_bsdf_Alpha_eq": 1.0 } } material_getter = MaterialProvider(Config(config)) all_cc_materials = Material.convert_to_materials(material_getter.run()) RandomRoomConstructor.construct( used_floor_area=self.used_floor_area, interior_objects=MeshObject.convert_to_meshes(loaded_objects), materials=all_cc_materials, amount_of_extrusions=self.amount_of_extrusions, fac_from_square_room=self.fac_from_square_room, corridor_width=self.corridor_width, wall_height=self.wall_height, amount_of_floor_cuts=self.amount_of_floor_cuts, only_use_big_edges=self.only_use_big_edges, create_ceiling=self.create_ceiling, assign_material_to_ceiling=self.assign_material_to_ceiling, placement_tries_per_face=self.tries_per_face, amount_of_objects_per_sq_meter=self.amount_of_objects_per_sq_meter)
def run(self): """ Randomizes materials for selected objects. 1. For each object assign a randomly chosen material from the pool. """ self.randomization_level = self.config.get_float( "randomization_level", 0.2) self._objects_to_manipulate = self.config.get_list( 'manipulated_objects', get_all_mesh_objects()) self._materials_to_replace_with = self.config.get_list( "materials_to_replace_with", get_all_materials()) self._obj_materials_cond_to_be_replaced = self.config.get_raw_dict( "obj_materials_cond_to_be_replaced", {}) op_mode = self.config.get_string("mode", "once_for_each") # if there were no materials selected throw an exception if not self._materials_to_replace_with: print( "Warning: No materials selected inside of the MaterialRandomizer!" ) return if op_mode == "once_for_all": random_material = np.random.choice(self._materials_to_replace_with) # walk over all objects for obj in self._objects_to_manipulate: if hasattr(obj, 'material_slots'): # walk over all materials for material in obj.material_slots: use_mat = True if self._obj_materials_cond_to_be_replaced: use_mat = len( Material.perform_and_condition_check( self._obj_materials_cond_to_be_replaced, [], [material.material])) == 1 if use_mat: if np.random.uniform(0, 1) <= self.randomization_level: if op_mode == "once_for_each": random_material = np.random.choice( self._materials_to_replace_with) # select a random material to replace the old one with material.material = random_material
def _randomize_materials(self, entity, value): """ Replaces each material of an entity with certain probability. :param entity: An object to modify. Type: bpy.types.Object. :param value: Configuration data. Type: dict. """ if hasattr(entity, 'material_slots'): if entity.material_slots: for mat in entity.material_slots: use_mat = True if value["obj_materials_cond_to_be_replaced"]: use_mat = len(Material.perform_and_condition_check(value["obj_materials_cond_to_be_replaced"], [], [mat.material])) == 1 if use_mat: if np.random.uniform(0, 1) <= value["randomization_level"]: mat.material = choice(value["materials_to_replace_with"]) elif value["add_to_objects_without_material"]: # this object didn't have a material before if np.random.uniform(0, 1) <= value["randomization_level"]: entity.data.materials.append(choice(value["materials_to_replace_with"]))
def _randomize_materials(self, entity, value): """ Replaces each material of an entity with certain probability. :param entity: An object to modify. Type: bpy.types.Object. :param value: Configuration data. Type: dict. """ if len(value["materials_to_replace_with"]) != 1: raise RuntimeError("getter.Material returned more than one or no substitute material, namely this much: {}. " "Please, make sure you enabled sampling in the Providers config by using " "'random_samples': 1 as a config parameter, and that conditions are not too strict such " "that some materials can meet them".format(len(value["materials_to_replace_with"]))) if hasattr(entity, 'material_slots'): for mat in entity.material_slots: use_mat = True if value["obj_materials_cond_to_be_replaced"]: use_mat = len(Material.perform_and_condition_check(value["obj_materials_cond_to_be_replaced"], [], [mat.material])) == 1 if use_mat: if np.random.uniform(0, 1) <= value["randomization_level"]: mat.material = value["materials_to_replace_with"][0]
def find_cc_material_by_name(material_name: str, custom_properties: dict): """ Finds from all loaded materials the cc material, which has the given material_name and the given custom_properties. :param material_name: Name of the searched material :param custom_properties: Custom properties, which have been assigned before :return: bpy.types.Material: Return the searched material, if not found returns None """ # find used cc materials with this name cond = {"cp_is_cc_texture": True, "cp_asset_name": material_name} for key, value in custom_properties.items(): cond[key] = value new_mats = Material.perform_and_condition_check(cond, []) if len(new_mats) == 1: new_mat = new_mats[0] return new_mat elif len(new_mats) > 1: raise Exception("There was more than one material found!") else: # the material was not even loaded return None
def run(self): self._folder_path = Utility.resolve_path(self.config.get_string("folder_path", "resources/cctextures")) self._used_assets = self.config.get_list("used_assets", []) self._add_cp = self.config.get_raw_dict("add_custom_properties", {}) self._preload = self.config.get_bool("preload", False) self._fill_used_empty_materials = self.config.get_bool("fill_used_empty_materials", False) if self._preload and self._fill_used_empty_materials: raise Exception("Preload and fill used empty materials can not be done at the same time, check config!") x_texture_node = -1500 y_texture_node = 300 if os.path.exists(self._folder_path) and os.path.isdir(self._folder_path): for asset in os.listdir(self._folder_path): if self._used_assets: skip_this_one = True for used_asset in self._used_assets: if asset.startswith(used_asset): skip_this_one = False break if skip_this_one: continue current_path = os.path.join(self._folder_path, asset) if os.path.isdir(current_path): base_image_path = os.path.join(current_path, "{}_2K_Color.jpg".format(asset)) if not os.path.exists(base_image_path): continue if self._fill_used_empty_materials: # find used cc materials with this name cond = {"cp_is_cc_texture": True, "cp_asset_name": asset} for key, value in self._add_cp.items(): cond[key] = value new_mats = Material.perform_and_condition_check(cond, []) if len(new_mats) == 1: new_mat = new_mats[0] elif len(new_mats) > 1: raise Exception("There was more than one material found!") else: # the material was not even loaded continue # check amount of usage of this material if new_mat.users == 0: # no one is using this material skip loading continue # only loads materials which are actually used print("Fill material: {}".format(asset)) else: # create a new material with the name of the asset new_mat = bpy.data.materials.new(asset) new_mat["is_cc_texture"] = True new_mat["asset_name"] = asset new_mat.use_nodes = True for key, value in self._add_cp.items(): cp_key = key if key.startswith("cp_"): cp_key = key[len("cp_"):] else: raise Exception("All cp have to start with cp_") new_mat[cp_key] = value if self._preload: continue collection_of_texture_nodes = [] nodes = new_mat.node_tree.nodes links = new_mat.node_tree.links principled_bsdf = Utility.get_the_one_node_with_type(nodes, "BsdfPrincipled") output_node = Utility.get_the_one_node_with_type(nodes, "OutputMaterial") base_color = nodes.new('ShaderNodeTexImage') base_color.image = bpy.data.images.load(base_image_path, check_existing=True) base_color.location.x = x_texture_node base_color.location.y = y_texture_node collection_of_texture_nodes.append(base_color) links.new(base_color.outputs["Color"], principled_bsdf.inputs["Base Color"]) principled_bsdf.inputs["Specular"].default_value = 0.333 ambient_occlusion_image_path = base_image_path.replace("Color", "AmbientOcclusion") if os.path.exists(ambient_occlusion_image_path): ao_color = nodes.new('ShaderNodeTexImage') ao_color.image = bpy.data.images.load(ambient_occlusion_image_path, check_existing=True) ao_color.location.x = x_texture_node ao_color.location.y = y_texture_node * 2 collection_of_texture_nodes.append(ao_color) math_node = nodes.new(type='ShaderNodeMixRGB') math_node.blend_type = "MULTIPLY" math_node.location.x = x_texture_node * 0.5 math_node.location.y = y_texture_node * 1.5 math_node.inputs["Fac"].default_value = 0.333 links.new(base_color.outputs["Color"], math_node.inputs[1]) links.new(ao_color.outputs["Color"], math_node.inputs[2]) links.new(math_node.outputs["Color"], principled_bsdf.inputs["Base Color"]) metalness_image_path = base_image_path.replace("Color", "Metalness") if os.path.exists(metalness_image_path): metalness_texture = nodes.new('ShaderNodeTexImage') metalness_texture.image = bpy.data.images.load(metalness_image_path, check_existing=True) metalness_texture.location.x = x_texture_node metalness_texture.location.y = y_texture_node * 0 collection_of_texture_nodes.append(metalness_texture) links.new(metalness_texture.outputs["Color"], principled_bsdf.inputs["Metallic"]) roughness_image_path = base_image_path.replace("Color", "Roughness") if os.path.exists(roughness_image_path): roughness_texture = nodes.new('ShaderNodeTexImage') roughness_texture.image = bpy.data.images.load(roughness_image_path, check_existing=True) roughness_texture.location.x = x_texture_node roughness_texture.location.y = y_texture_node * -1 collection_of_texture_nodes.append(roughness_texture) links.new(roughness_texture.outputs["Color"], principled_bsdf.inputs["Roughness"]) alpha_image_path = base_image_path.replace("Color", "Opacity") if os.path.exists(alpha_image_path): alpha_texture = nodes.new('ShaderNodeTexImage') alpha_texture.image = bpy.data.images.load(alpha_image_path, check_existing=True) alpha_texture.location.x = x_texture_node alpha_texture.location.y = y_texture_node * -2 collection_of_texture_nodes.append(alpha_texture) links.new(alpha_texture.outputs["Color"], principled_bsdf.inputs["Alpha"]) normal_image_path = base_image_path.replace("Color", "Normal") normal_y_value = y_texture_node * -3 if os.path.exists(normal_image_path): normal_texture = nodes.new('ShaderNodeTexImage') normal_texture.image = bpy.data.images.load(normal_image_path, check_existing=True) normal_texture.location.x = x_texture_node normal_texture.location.y = normal_y_value collection_of_texture_nodes.append(normal_texture) direct_x_mode = True if direct_x_mode: separate_rgba = nodes.new('ShaderNodeSeparateRGB') separate_rgba.location.x = 4.0/5.0 * x_texture_node separate_rgba.location.y = normal_y_value links.new(normal_texture.outputs["Color"], separate_rgba.inputs["Image"]) invert_node = nodes.new("ShaderNodeInvert") invert_node.inputs["Fac"].default_value = 1.0 invert_node.location.x = 3.0/5.0 * x_texture_node invert_node.location.y = normal_y_value links.new(separate_rgba.outputs["G"], invert_node.inputs["Color"]) combine_rgba = nodes.new('ShaderNodeCombineRGB') combine_rgba.location.x = 2.0/5.0 * x_texture_node combine_rgba.location.y = normal_y_value links.new(separate_rgba.outputs["R"], combine_rgba.inputs["R"]) links.new(invert_node.outputs["Color"], combine_rgba.inputs["G"]) links.new(separate_rgba.outputs["B"], combine_rgba.inputs["B"]) current_output = combine_rgba.outputs["Image"] else: current_output = normal_texture.outputs["Color"] normal_map = nodes.new("ShaderNodeNormalMap") normal_map.inputs["Strength"].default_value = 1.0 normal_map.location.x = 1.0 / 5.0 * x_texture_node normal_map.location.y = normal_y_value links.new(current_output, normal_map.inputs["Color"]) links.new(normal_map.outputs["Normal"], principled_bsdf.inputs["Normal"]) displacement_image_path = base_image_path.replace("Color", "Displacement") if os.path.exists(displacement_image_path): displacement_texture = nodes.new('ShaderNodeTexImage') displacement_texture.image = bpy.data.images.load(displacement_image_path, check_existing=True) displacement_texture.location.x = x_texture_node displacement_texture.location.y = y_texture_node * -4 collection_of_texture_nodes.append(displacement_texture) displacement_node = nodes.new("ShaderNodeDisplacement") displacement_node.inputs["Midlevel"].default_value = 0.5 displacement_node.inputs["Scale"].default_value = 0.05 displacement_node.location.x = x_texture_node * 0.5 displacement_node.location.y = y_texture_node * -4 links.new(displacement_texture.outputs["Color"], displacement_node.inputs["Height"]) links.new(displacement_node.outputs["Displacement"], output_node.inputs["Displacement"]) if len(collection_of_texture_nodes) > 0: texture_coords = nodes.new("ShaderNodeTexCoord") texture_coords.location.x = x_texture_node * 1.4 mapping_node = nodes.new("ShaderNodeMapping") mapping_node.location.x = x_texture_node * 1.2 links.new(texture_coords.outputs["UV"], mapping_node.inputs["Vector"]) for texture_node in collection_of_texture_nodes: links.new(mapping_node.outputs["Vector"], texture_node.inputs["Vector"])