Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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))
Example #6
0
    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)
Example #7
0
    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)
Example #10
0
    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))