def run(self): # if the rendering is not performed -> it is probably the debug case. do_undo = not self._avoid_output with Utility.UndoAfterExecution(perform_undo_op=do_undo): self._configure_renderer(use_denoiser=True, default_denoiser="Intel") # check if texture less render mode is active if self._texture_less_mode: MaterialLoaderUtility.change_to_texture_less_render( self._use_alpha_channel) if self._use_alpha_channel: MaterialLoaderUtility.add_alpha_channel_to_textures( blurry_edges=True) # motion blur if self._use_motion_blur: RendererUtility.enable_motion_blur( self._motion_blur_length, 'TOP' if self._use_rolling_shutter else "NONE", self._rolling_shutter_length) self._render("rgb_", "colors", enable_transparency=self.config.get_bool( "transparent_background", False), file_format=self._image_type)
def create_material(new_mat: bpy.types.Material, base_image_path: str, ambient_occlusion_image_path: str, metallic_image_path: str, roughness_image_path: str, alpha_image_path: str, normal_image_path: str, displacement_image_path: str): """ Create a material for the cctexture datatset, the combination used here is calibrated to this. :param new_mat: The new material, which will get all the given textures :param base_image_path: The path to the color image :param ambient_occlusion_image_path: The path to the ambient occlusion image :param metallic_image_path: The path to the metallic image :param roughness_image_path: The path to the roughness image :param alpha_image_path: The path to the alpha image (when this was written there was no alpha image provided \ in the haven dataset) :param normal_image_path: The path to the normal image :param displacement_image_path: The path to the displacement image """ 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") collection_of_texture_nodes = [] base_color = MaterialLoaderUtility.add_base_color(nodes, links, base_image_path, principled_bsdf) collection_of_texture_nodes.append(base_color) principled_bsdf.inputs["Specular"].default_value = 0.333 ao_node = MaterialLoaderUtility.add_ambient_occlusion(nodes, links, ambient_occlusion_image_path, principled_bsdf, base_color) collection_of_texture_nodes.append(ao_node) metallic_node = MaterialLoaderUtility.add_metal(nodes, links, metallic_image_path, principled_bsdf) collection_of_texture_nodes.append(metallic_node) roughness_node = MaterialLoaderUtility.add_roughness(nodes, links, roughness_image_path, principled_bsdf) collection_of_texture_nodes.append(roughness_node) alpha_node = MaterialLoaderUtility.add_alpha(nodes, links, alpha_image_path, principled_bsdf) collection_of_texture_nodes.append(alpha_node) normal_node = MaterialLoaderUtility.add_normal(nodes, links, normal_image_path, principled_bsdf, invert_y_channel=True) collection_of_texture_nodes.append(normal_node) displacement_node = MaterialLoaderUtility.add_displacement(nodes, links, displacement_image_path, output_node) collection_of_texture_nodes.append(displacement_node) collection_of_texture_nodes = [node for node in collection_of_texture_nodes if node is not None] MaterialLoaderUtility.connect_uv_maps(nodes, links, collection_of_texture_nodes)
def _colorize_object(obj, color, use_alpha_channel): """ Adjusts the materials of the given object, s.t. they are ready for rendering the seg map. This is done by replacing all nodes just with an emission node, which emits the color corresponding to the category of the object. :param obj: The object to use. :param color: RGB array of a color in the range of [0, self.render_colorspace_size_per_dimension]. :param use_alpha_channel: If true, the alpha channel stored in .png textures is used. """ # Create new material emitting the given color new_mat = bpy.data.materials.new(name="segmentation") new_mat.use_nodes = True nodes = new_mat.node_tree.nodes links = new_mat.node_tree.links emission_node = nodes.new(type='ShaderNodeEmission') output = Utility.get_the_one_node_with_type(nodes, 'OutputMaterial') emission_node.inputs['Color'].default_value[:3] = color links.new(emission_node.outputs['Emission'], output.inputs['Surface']) # Set material to be used for coloring all faces of the given object if len(obj.material_slots) > 0: for i in range(len(obj.material_slots)): if use_alpha_channel: obj.data.materials[ i] = MaterialLoaderUtility.add_alpha_texture_node( obj.material_slots[i].material, new_mat) else: obj.data.materials[i] = new_mat else: obj.data.materials.append(new_mat)
height = np.random.uniform(0.5, 2) location, _ = point_sampler.sample(height) # Sample rotation (fix around X and Y axis) euler_rotation = np.random.uniform([1.2217, 0, 0], [1.2217, 0, 6.283185307]) cam2world_matrix = MathUtility.build_transformation_mat( location, euler_rotation) # Check that obstacles are at least 1 meter away from the camera and make sure the view interesting enough if CameraValidation.perform_obstacle_in_view_check( cam2world_matrix, {"min": 1.0}, bvh_tree ) and CameraValidation.scene_coverage_score(cam2world_matrix) > 0.4: CameraUtility.add_camera_pose(cam2world_matrix) poses += 1 tries += 1 # activate normal and distance rendering RendererUtility.enable_normals_output() RendererUtility.enable_distance_output() MaterialLoaderUtility.add_alpha_channel_to_textures(blurry_edges=True) # render the whole pipeline data = RendererUtility.render() data.update( SegMapRendererUtility.render(Utility.get_temporary_directory(), Utility.get_temporary_directory(), "class")) # write the data to a .hdf5 container WriterUtility.save_to_hdf5(args.output_dir, data)
def load(folder_path: str = "resources/cctextures", used_assets: list = None, preload: bool = False, fill_used_empty_materials: bool = False, add_custom_properties: dict = None, use_all_materials: bool = False) -> List[Material]: """ This method loads all textures obtained from https://cc0textures.com, use the script (scripts/download_cc_textures.py) to download all the textures to your pc. All textures here support Physically based rendering (PBR), which makes the textures more realistic. All materials will have the custom property "is_cc_texture": True, which will make the selection later on easier. :param folder_path: The path to the downloaded cc0textures. :param used_assets: A list of all asset names, you want to use. The asset-name must not be typed in completely, only the beginning the name starts with. By default all assets will be loaded, specified by an empty list. :param preload: If set true, only the material names are loaded and not the complete material. :param fill_used_empty_materials: If set true, the preloaded materials, which are used are now loaded completely. :param add_custom_properties: A dictionary of materials and the respective properties. :param use_all_materials: If this is false only a selection of probably useful textures is used. This excludes \ some see through texture and non tileable texture. :return a list of all loaded materials, if preload is active these materials do not contain any textures yet and have to be filled before rendering (by calling this function again, no need to save the prior returned list) """ folder_path = Utility.resolve_path(folder_path) # this selected textures are probably useful for random selection probably_useful_texture = ["paving stones", "tiles", "wood", "fabric", "bricks", "metal", "wood floor", "ground", "rock", "concrete", "leather", "planks", "rocks", "gravel", "asphalt", "painted metal", "painted plaster", "marble", "carpet", "plastic", "roofing tiles", "bark", "metal plates", "wood siding", "terrazzo", "plaster", "paint", "corrugated steel", "painted wood", "lava" "cardboard", "clay", "diamond plate", "ice", "moss", "pipe", "candy", "chipboard", "rope", "sponge", "tactile paving", "paper", "cork", "wood chips"] if not use_all_materials and used_assets is None: used_assets = probably_useful_texture else: used_assets = [asset.lower() for asset in used_assets] if add_custom_properties is None: add_custom_properties = dict() if preload and fill_used_empty_materials: raise Exception("Preload and fill used empty materials can not be done at the same time, check config!") if os.path.exists(folder_path) and os.path.isdir(folder_path): materials = [] for asset in os.listdir(folder_path): if used_assets: skip_this_one = True for used_asset in used_assets: # lower is necessary here, as all used assets are made that that way if asset.lower().startswith(used_asset.replace(" ", "")): skip_this_one = False break if skip_this_one: continue current_path = os.path.join(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 # construct all image paths ambient_occlusion_image_path = base_image_path.replace("Color", "AmbientOcclusion") metallic_image_path = base_image_path.replace("Color", "Metalness") roughness_image_path = base_image_path.replace("Color", "Roughness") alpha_image_path = base_image_path.replace("Color", "Opacity") normal_image_path = base_image_path.replace("Color", "Normal") displacement_image_path = base_image_path.replace("Color", "Displacement") # if the material was already created it only has to be searched if fill_used_empty_materials: new_mat = MaterialLoaderUtility.find_cc_material_by_name(asset, add_custom_properties) else: new_mat = MaterialLoaderUtility.create_new_cc_material(asset, add_custom_properties) # if preload then the material is only created but not filled if preload: # Set alpha to 0 if the material has an alpha texture, so it can be detected e.q. in the material getter. nodes = new_mat.node_tree.nodes principled_bsdf = Utility.get_the_one_node_with_type(nodes, "BsdfPrincipled") principled_bsdf.inputs["Alpha"].default_value = 0 if os.path.exists(alpha_image_path) else 1 # add it here for the preload case materials.append(Material(new_mat)) continue elif fill_used_empty_materials and not MaterialLoaderUtility.is_material_used(new_mat): # now only the materials, which have been used should be filled continue # create material based on these image paths CCMaterialLoader.create_material(new_mat, base_image_path, ambient_occlusion_image_path, metallic_image_path, roughness_image_path, alpha_image_path, normal_image_path, displacement_image_path) materials.append(Material(new_mat)) return materials else: raise Exception("The folder path does not exist: {}".format(folder_path))
def render(output_dir, temp_dir, used_attributes, used_default_values={}, file_prefix="segmap_", output_key="segmap", segcolormap_output_file_prefix="class_inst_col_map", segcolormap_output_key="segcolormap", use_alpha_channel=False, render_colorspace_size_per_dimension=2048): """ Renders segmentation maps for all frames. :param output_dir: The directory to write images to. :param temp_dir: The directory to write intermediate data to. :param used_attributes: The attributes to be used for color mapping. :param used_default_values: The default values used for the keys used in used_attributes. :param file_prefix: The prefix to use for writing the images. :param output_key: The key to use for registering the output. :param segcolormap_output_file_prefix: The prefix to use for writing the segmation-color map csv. :param segcolormap_output_key: The key to use for registering the segmation-color map output. :param use_alpha_channel: If true, the alpha channel stored in .png textures is used. :param render_colorspace_size_per_dimension: As we use float16 for storing the rendering, the interval of integers which can be precisely stored is [-2048, 2048]. As blender does not allow negative values for colors, we use [0, 2048] ** 3 as our color space which allows ~8 billion different colors/objects. This should be enough. """ with Utility.UndoAfterExecution(): RendererUtility.init() RendererUtility.set_samples(1) RendererUtility.set_adaptive_sampling(0) RendererUtility.set_denoiser(None) RendererUtility.set_light_bounces(1, 0, 0, 1, 0, 8, 0) # Get objects with meshes (i.e. not lights or cameras) objs_with_mats = get_all_blender_mesh_objects() colors, num_splits_per_dimension, used_objects = SegMapRendererUtility._colorize_objects_for_instance_segmentation( objs_with_mats, use_alpha_channel, render_colorspace_size_per_dimension) bpy.context.scene.cycles.filter_width = 0.0 if use_alpha_channel: MaterialLoaderUtility.add_alpha_channel_to_textures( blurry_edges=False) # Determine path for temporary and for final output temporary_segmentation_file_path = os.path.join(temp_dir, "seg_") final_segmentation_file_path = os.path.join( output_dir, file_prefix) RendererUtility.set_output_format("OPEN_EXR", 16) RendererUtility.render(temp_dir, "seg_", None) # Find optimal dtype of output based on max index for dtype in [np.uint8, np.uint16, np.uint32]: optimal_dtype = dtype if np.iinfo(optimal_dtype).max >= len(colors) - 1: break if 'class' in used_default_values: used_default_values['cp_category_id'] = used_default_values[ 'class'] if isinstance(used_attributes, str): # only one result is requested result_channels = 1 used_attributes = [used_attributes] elif isinstance(used_attributes, list): result_channels = len(used_attributes) else: raise Exception( "The type of this is not supported here: {}".format( used_attributes)) save_in_csv_attributes = {} # define them for the avoid rendering case there_was_an_instance_rendering = False list_of_used_attributes = [] # Check if stereo is enabled if bpy.context.scene.render.use_multiview: suffixes = ["_L", "_R"] else: suffixes = [""] # After rendering for frame in range( bpy.context.scene.frame_start, bpy.context.scene.frame_end): # for each rendered frame for suffix in suffixes: file_path = temporary_segmentation_file_path + ( "%04d" % frame) + suffix + ".exr" segmentation = load_image(file_path) print(file_path, segmentation.shape) segmap = Utility.map_back_from_equally_spaced_equidistant_values( segmentation, num_splits_per_dimension, render_colorspace_size_per_dimension) segmap = segmap.astype(optimal_dtype) used_object_ids = np.unique(segmap) max_id = np.max(used_object_ids) if max_id >= len(used_objects): raise Exception( "There are more object colors than there are objects" ) combined_result_map = [] there_was_an_instance_rendering = False list_of_used_attributes = [] used_channels = [] for channel_id in range(result_channels): resulting_map = np.empty( (segmap.shape[0], segmap.shape[1])) was_used = False current_attribute = used_attributes[channel_id] org_attribute = current_attribute # if the class is used the category_id attribute is evaluated if current_attribute == "class": current_attribute = "cp_category_id" # in the instance case the resulting ids are directly used if current_attribute == "instance": there_was_an_instance_rendering = True resulting_map = segmap was_used = True # a non default value was also used non_default_value_was_used = True else: if current_attribute != "cp_category_id": list_of_used_attributes.append( current_attribute) # for the current attribute remove cp_ and _csv, if present used_attribute = current_attribute if used_attribute.startswith("cp_"): used_attribute = used_attribute[len("cp_"):] # check if a default value was specified default_value_set = False if current_attribute in used_default_values or used_attribute in used_default_values: default_value_set = True if current_attribute in used_default_values: default_value = used_default_values[ current_attribute] elif used_attribute in used_default_values: default_value = used_default_values[ used_attribute] last_state_save_in_csv = None # this avoids that for certain attributes only the default value is written non_default_value_was_used = False # iterate over all object ids for object_id in used_object_ids: is_default_value = False # get the corresponding object via the id current_obj = used_objects[object_id] # if the current obj has a attribute with that name -> get it if hasattr(current_obj, used_attribute): used_value = getattr( current_obj, used_attribute) # if the current object has a custom property with that name -> get it elif current_attribute.startswith( "cp_" ) and used_attribute in current_obj: used_value = current_obj[used_attribute] elif current_attribute.startswith("cf_"): if current_attribute == "cf_basename": used_value = current_obj.name if "." in used_value: used_value = used_value[:used_value .rfind("." )] elif default_value_set: # if none of the above applies use the default value used_value = default_value is_default_value = True else: # if the requested current_attribute is not a custom property or a attribute # or there is a default value stored # it throws an exception raise Exception( "The obj: {} does not have the " "attribute: {}, striped: {}. Maybe try a default " "value.".format( current_obj.name, current_attribute, used_attribute)) # check if the value should be saved as an image or in the csv file save_in_csv = False try: resulting_map[segmap == object_id] = used_value was_used = True if not is_default_value: non_default_value_was_used = True # save everything which is not instance also in the .csv if current_attribute != "instance": save_in_csv = True except ValueError: save_in_csv = True if last_state_save_in_csv is not None and last_state_save_in_csv != save_in_csv: raise Exception( "During creating the mapping, the saving to an image or a csv file " "switched, this might indicated that the used default value, does " "not have the same type as the returned value, " "for: {}".format(current_attribute)) last_state_save_in_csv = save_in_csv if save_in_csv: if object_id in save_in_csv_attributes: save_in_csv_attributes[object_id][ used_attribute] = used_value else: save_in_csv_attributes[object_id] = { used_attribute: used_value } if was_used and non_default_value_was_used: used_channels.append(org_attribute) combined_result_map.append(resulting_map) fname = final_segmentation_file_path + ("%04d" % frame) + suffix # combine all resulting images to one image resulting_map = np.stack(combined_result_map, axis=2) # remove the unneeded third dimension if resulting_map.shape[2] == 1: resulting_map = resulting_map[:, :, 0] np.save(fname, resulting_map) if not there_was_an_instance_rendering: if len(list_of_used_attributes) > 0: raise Exception( "There were attributes specified in the may_by, which could not be saved as " "there was no \"instance\" may_by key used. This is true for this/these " "keys: {}".format(", ".join(list_of_used_attributes))) # if there was no instance rendering no .csv file is generated! # delete all saved infos about .csv save_in_csv_attributes = {} # write color mappings to file if save_in_csv_attributes: csv_file_path = os.path.join( output_dir, segcolormap_output_file_prefix + ".csv") with open(csv_file_path, 'w', newline='') as csvfile: # get from the first element the used field names fieldnames = ["idx"] # get all used object element keys for object_element in save_in_csv_attributes.values(): fieldnames.extend(list(object_element.keys())) break for channel_name in used_channels: fieldnames.append("channel_{}".format(channel_name)) writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() # save for each object all values in one row for obj_idx, object_element in save_in_csv_attributes.items( ): object_element["idx"] = obj_idx for i, channel_name in enumerate(used_channels): object_element["channel_{}".format( channel_name)] = i writer.writerow(object_element) Utility.register_output(output_dir, file_prefix, output_key, ".npy", "2.0.0") if save_in_csv_attributes: Utility.register_output(output_dir, segcolormap_output_file_prefix, segcolormap_output_key, ".csv", "2.0.0", unique_for_camposes=False)
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!" ) 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 the material was already created it only has to be searched if self._fill_used_empty_materials: new_mat = MaterialLoaderUtility.find_cc_material_by_name( asset, self._add_cp) else: new_mat = MaterialLoaderUtility.create_new_cc_material( asset, self._add_cp) if self._preload: # if preload then the material is only created but not filled continue elif self._fill_used_empty_materials and not MaterialLoaderUtility.is_material_used( new_mat): # now only the materials, which have been used should be filled continue # construct all image paths ambient_occlusion_image_path = base_image_path.replace( "Color", "AmbientOcclusion") metallic_image_path = base_image_path.replace( "Color", "Metalness") roughness_image_path = base_image_path.replace( "Color", "Roughness") alpha_image_path = base_image_path.replace( "Color", "Opacity") normal_image_path = base_image_path.replace( "Color", "Normal") displacement_image_path = base_image_path.replace( "Color", "Displacement") # create material based on these image paths CCMaterialLoader.create_material( new_mat, base_image_path, ambient_occlusion_image_path, metallic_image_path, roughness_image_path, alpha_image_path, normal_image_path, displacement_image_path) else: raise Exception("The folder path does not exist: {}".format( self._folder_path))
def load(folder_path: str = "resources/haven", used_assets: list = [], preload: bool = False, fill_used_empty_materials: bool = False, add_cp: dict = {}): """ Loads all specified haven textures from the given directory. :param folder_path: The path to the downloaded haven. :param used_assets: A list of all asset names, you want to use. The asset-name must not be typed in completely, only the beginning the name starts with. By default all assets will be loaded, specified by an empty list. :param preload: If set true, only the material names are loaded and not the complete material. :param fill_used_empty_materials: If set true, the preloaded materials, which are used are now loaded completely. :param add_cp: A dictionary of materials and the respective properties. """ # makes the integration of complex materials easier addon_utils.enable("node_wrangler") folder_path = Utility.resolve_path(folder_path) if preload and fill_used_empty_materials: raise Exception( "Preload and fill used empty materials can not be done at the same time, check config!" ) if os.path.exists(folder_path) and os.path.isdir(folder_path): for asset in os.listdir(folder_path): if used_assets: skip_this_one = True for used_asset in used_assets: if asset.startswith(used_asset): skip_this_one = False break if skip_this_one: continue current_path = os.path.join(folder_path, asset) if os.path.isdir(current_path): # find the current base_image_path by search for _diff_, this make it independent of the used res all_paths = glob.glob(os.path.join(current_path, "*.jpg")) base_image_path = "" for path in all_paths: if "_diff_" in path: base_image_path = path break if not os.path.exists(base_image_path): continue # if the material was already created it only has to be searched if fill_used_empty_materials: new_mat = MaterialLoaderUtility.find_cc_material_by_name( asset, add_cp) else: new_mat = MaterialLoaderUtility.create_new_cc_material( asset, add_cp) if preload: # if preload then the material is only created but not filled continue elif fill_used_empty_materials and not MaterialLoaderUtility.is_material_used( new_mat): # now only the materials, which have been used should be filled continue # construct all image paths # the images path contain the words named in this list, but some of them are differently # capitalized, e.g. Nor, NOR, NoR, ... used_elements = [ "ao", "spec", "rough", "nor", "disp", "bump", "alpha" ] final_paths = {} for ele in used_elements: new_path = base_image_path.replace("diff", ele).lower() found_path = "" for path in all_paths: if path.lower() == new_path: found_path = path break final_paths[ele] = found_path # create material based on these image paths HavenMaterialLoader.create_material( new_mat, base_image_path, final_paths["ao"], final_paths["spec"], final_paths["rough"], final_paths["alpha"], final_paths["nor"], final_paths["disp"], final_paths["bump"]) else: raise Exception( "The folder path does not exist: {}".format(folder_path))
def create_material(new_mat: bpy.types.Material, base_image_path: str, ambient_occlusion_image_path: str, specular_image_path: str, roughness_image_path: str, alpha_image_path: str, normal_image_path: str, displacement_image_path: str, bump_image_path: str): """ Create a material for the haven datatset, the combination used here is calibrated to the haven dataset format. :param new_mat: The new material, which will get all the given textures :param base_image_path: The path to the color image :param ambient_occlusion_image_path: The path to the ambient occlusion image :param specular_image_path: The path to the specular image :param roughness_image_path: The path to the roughness image :param alpha_image_path: The path to the alpha image (when this was written there was no alpha image provided \ in the haven dataset) :param normal_image_path: The path to the normal image :param displacement_image_path: The path to the displacement image :param bump_image_path: The path to the bump image """ 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") collection_of_texture_nodes = [] base_color = MaterialLoaderUtility.add_base_color( nodes, links, base_image_path, principled_bsdf) collection_of_texture_nodes.append(base_color) specular_color = MaterialLoaderUtility.add_specular( nodes, links, specular_image_path, principled_bsdf) collection_of_texture_nodes.append(specular_color) ao_node = MaterialLoaderUtility.add_ambient_occlusion( nodes, links, ambient_occlusion_image_path, principled_bsdf, base_color) collection_of_texture_nodes.append(ao_node) roughness_node = MaterialLoaderUtility.add_roughness( nodes, links, roughness_image_path, principled_bsdf) collection_of_texture_nodes.append(roughness_node) alpha_node = MaterialLoaderUtility.add_alpha(nodes, links, alpha_image_path, principled_bsdf) collection_of_texture_nodes.append(alpha_node) # only add a bump map if no normal map was found if not os.path.exists(normal_image_path): bump_node = MaterialLoaderUtility.add_bump(nodes, links, bump_image_path, principled_bsdf) collection_of_texture_nodes.append(bump_node) else: normal_node = MaterialLoaderUtility.add_normal( nodes, links, normal_image_path, principled_bsdf, invert_y_channel=False) collection_of_texture_nodes.append(normal_node) displacement_node = MaterialLoaderUtility.add_displacement( nodes, links, displacement_image_path, output_node) collection_of_texture_nodes.append(displacement_node) collection_of_texture_nodes = [ node for node in collection_of_texture_nodes if node is not None ] MaterialLoaderUtility.connect_uv_maps(nodes, links, collection_of_texture_nodes)
def run(self): """ Load the materials """ self._folder_path = Utility.resolve_path( self.config.get_string("folder_path", "resources/haven")) 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!" ) 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): # find the current base_image_path by search for _diff_, this make it independent of the used res all_paths = glob.glob(os.path.join(current_path, "*.jpg")) base_image_path = "" for path in all_paths: if "_diff_" in path: base_image_path = path break if not os.path.exists(base_image_path): continue # if the material was already created it only has to be searched if self._fill_used_empty_materials: new_mat = MaterialLoaderUtility.find_cc_material_by_name( asset, self._add_cp) else: new_mat = MaterialLoaderUtility.create_new_cc_material( asset, self._add_cp) if self._preload: # if preload then the material is only created but not filled continue elif self._fill_used_empty_materials and not MaterialLoaderUtility.is_material_used( new_mat): # now only the materials, which have been used should be filled continue # construct all image paths # the images path contain the words named in this list, but some of them are differently # capitalized, e.g. Nor, NOR, NoR, ... used_elements = [ "ao", "spec", "rough", "nor", "disp", "bump", "alpha" ] final_paths = {} for ele in used_elements: new_path = base_image_path.replace("diff", ele).lower() found_path = "" for path in all_paths: if path.lower() == new_path: found_path = path break final_paths[ele] = found_path # create material based on these image paths HavenMaterialLoader.create_material( new_mat, base_image_path, final_paths["ao"], final_paths["spec"], final_paths["rough"], final_paths["alpha"], final_paths["nor"], final_paths["disp"], final_paths["bump"]) else: raise Exception("The folder path does not exist: {}".format( self._folder_path))