def enable_diffuse_color_output(output_dir: Optional[str] = None, file_prefix: str = "diffuse_",
                                output_key: str = "diffuse"):
    """ Enables writing diffuse color (albedo) images.

    Diffuse color images will be written in the form of .png files during the next rendering.

    :param output_dir: The directory to write files to, if this is None the temporary directory is used.
    :param file_prefix: The prefix to use for writing the files.
    :param output_key: The key to use for registering the diffuse color output.
    """
    if output_dir is None:
        output_dir = Utility.get_temporary_directory()

    bpy.context.scene.render.use_compositing = True
    bpy.context.scene.use_nodes = True
    tree = bpy.context.scene.node_tree
    links = tree.links

    bpy.context.view_layer.use_pass_diffuse_color = True
    render_layer_node = Utility.get_the_one_node_with_type(tree.nodes, 'CompositorNodeRLayers')
    final_output = render_layer_node.outputs["DiffCol"]

    output_file = tree.nodes.new('CompositorNodeOutputFile')
    output_file.base_path = output_dir
    output_file.format.file_format = "PNG"
    output_file.file_slots.values()[0].path = file_prefix
    links.new(final_output, output_file.inputs['Image'])

    Utility.add_output_entry({
        "key": output_key,
        "path": os.path.join(output_dir, file_prefix) + "%04d" + ".png",
        "version": "2.0.0"
    })
Example #2
0
    def write_attributes_to_file(self,
                                 item_writer,
                                 items,
                                 default_file_prefix,
                                 default_output_key,
                                 default_attributes,
                                 version="1.0.0"):
        """ Writes the state of the given items to a file with the configured prefix.

        This method also registers the corresponding output.

        :param item_writer: The item writer object to use. Type: object.
        :param items: The list of items. Type: list.
        :param default_file_prefix: The default file name prefix to use. Type: string.
        :param default_output_key: The default output key to use. Type: string.
        :param default_attributes: The default attributes to write, if no attributes are specified in the config. Type: list.
        :param version: The version to use when registering the output. Type: string.
        """
        if self._avoid_output:
            print("Avoid output is on, no output produced!")
            return

        file_prefix = self.config.get_string("output_file_prefix",
                                             default_file_prefix)
        path_prefix = os.path.join(self._determine_output_dir(), file_prefix)
        item_writer.write_items_to_file(
            path_prefix,
            items,
            self.config.get_list("attributes_to_write", default_attributes),
            world_frame_change=self.destination_frame)
        Utility.register_output(
            self._determine_output_dir(), file_prefix,
            self.config.get_string("output_key", default_output_key), ".npy",
            version)
Example #3
0
    def set_type(self, type: str, frame: int = None):
        """ Sets the type of the light.

        :param type: The type to set, can be one of [POINT, SUN, SPOT, AREA].
        :param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
        """
        self.blender_obj.data.type = type
        Utility.insert_keyframe(self.blender_obj.data, "type", frame)
Example #4
0
    def set_distance(self, distance: float, frame: int = None):
        """ Sets the falloff distance of the light = point where light is half the original intensity.

        :param distance: The falloff distance to set.
        :param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
        """
        self.blender_obj.data.distance = distance
        Utility.insert_keyframe(self.blender_obj.data, "distance", frame)
Example #5
0
    def set_color(self, color: Union[list, Color], frame: int = None):
        """ Sets the color of the light.

        :param color: The rgb color to set.
        :param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
        """
        self.blender_obj.data.color = color
        Utility.insert_keyframe(self.blender_obj.data, "color", frame)
Example #6
0
    def set_energy(self, energy: float, frame: int = None):
        """ Sets the energy of the light.

        :param energy: The energy to set. If the type is SUN this value is interpreted as Watt per square meter, otherwise it is interpreted as Watt.
        :param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
        """
        self.blender_obj.data.energy = energy
        Utility.insert_keyframe(self.blender_obj.data, "energy", frame)
def enable_distance_output(output_dir: Optional[str] = None, file_prefix: str = "distance_",
                           output_key: str = "distance", distance_start: float = 0.1, distance_range: float = 25.0,
                           distance_falloff: str = "LINEAR"):
    """ Enables writing distance images.

    Distance images will be written in the form of .exr files during the next rendering.

    :param output_dir: The directory to write files to, if this is None the temporary directory is used.
    :param file_prefix: The prefix to use for writing the files.
    :param output_key: The key to use for registering the distance output.
    :param distance_start: Starting distance of the distance, measured from the camera.
    :param distance_range: Total distance in which the distance is measured. \
                           distance_end = distance_start + distance_range.
    :param distance_falloff: Type of transition used to fade distance. Available: [LINEAR, QUADRATIC, INVERSE_QUADRATIC]
    """
    if output_dir is None:
        output_dir = Utility.get_temporary_directory()

    bpy.context.scene.render.use_compositing = True
    bpy.context.scene.use_nodes = True
    GlobalStorage.add("renderer_distance_end", distance_start + distance_range)

    tree = bpy.context.scene.node_tree
    links = tree.links
    # Use existing render layer
    render_layer_node = Utility.get_the_one_node_with_type(tree.nodes, 'CompositorNodeRLayers')

    # Set mist pass limits
    bpy.context.scene.world.mist_settings.start = distance_start
    bpy.context.scene.world.mist_settings.depth = distance_range
    bpy.context.scene.world.mist_settings.falloff = distance_falloff

    bpy.context.view_layer.use_pass_mist = True  # Enable distance pass
    # Create a mapper node to map from 0-1 to SI units
    mapper_node = tree.nodes.new("CompositorNodeMapRange")
    links.new(render_layer_node.outputs["Mist"], mapper_node.inputs['Value'])
    # map the values 0-1 to range distance_start to distance_range
    mapper_node.inputs['To Min'].default_value = distance_start
    mapper_node.inputs['To Max'].default_value = distance_start + distance_range
    final_output = mapper_node.outputs['Value']

    # Build output node
    output_file = tree.nodes.new("CompositorNodeOutputFile")
    output_file.base_path = output_dir
    output_file.format.file_format = "OPEN_EXR"
    output_file.file_slots.values()[0].path = file_prefix

    # Feed the Z-Buffer or Mist output of the render layer to the input of the file IO layer
    links.new(final_output, output_file.inputs['Image'])

    Utility.add_output_entry({
        "key": output_key,
        "path": os.path.join(output_dir, file_prefix) + "%04d" + ".exr",
        "version": "2.0.0",
        "trim_redundant_channels": True
    })
    def run(self):
        """ Does the stereo global matching in the following steps:
        1. Collect camera object and its state,
        2. For each frame, load left and right images and call the `sgm()` methode.
        3. Write the results to a numpy file.
        """
        if self._avoid_output:
            print("Avoid output is on, no output produced!")
            return

        if GlobalStorage.is_in_storage("renderer_distance_end"):
            depth_max = GlobalStorage.get("renderer_distance_end")
        else:
            raise RuntimeError(
                "A distance rendering has to be executed before this module is executed, "
                "else the `renderer_distance_end` is not set!")

        rgb_output_path = Utility.find_registered_output_by_key(
            self.rgb_output_key)["path"]

        color_images = []
        for frame in range(bpy.context.scene.frame_start,
                           bpy.context.scene.frame_end):
            path_split = rgb_output_path.split(".")
            path_l = "{}_L.{}".format(path_split[0], path_split[1])
            path_r = "{}_R.{}".format(path_split[0], path_split[1])

            imgL = load_image(path_l % frame)
            imgR = load_image(path_r % frame)
            color_images.append(np.stack((imgL, imgR), 0))

        depth, disparity = stereo_global_matching(
            color_images=color_images,
            depth_max=depth_max,
            window_size=self.config.get_int("window_size", 7),
            num_disparities=self.config.get_int("num_disparities", 32),
            min_disparity=self.config.get_int("min_disparity", 0),
            disparity_filter=self.config.get_bool("disparity_filter", True),
            depth_completion=self.config.get_bool("depth_completion", True))

        for frame in range(bpy.context.scene.frame_start,
                           bpy.context.scene.frame_end):
            np.save(
                os.path.join(self._determine_output_dir(), "stereo-depth_%04d")
                % frame, depth[frame])

            if self.config.get_bool("output_disparity", False):
                np.save(
                    os.path.join(self._determine_output_dir(),
                                 "disparity_%04d") % frame, disparity[frame])

        Utility.register_output(self._determine_output_dir(), "stereo-depth_",
                                "stereo-depth", ".npy", "1.0.0")
        if self.config.get_bool("output_disparity", False):
            Utility.register_output(self._determine_output_dir(), "disparity_",
                                    "disparity", ".npy", "1.0.0")
Example #9
0
    def set_scale(self,
                  scale: Union[list, np.ndarray, Vector],
                  frame: int = None):
        """ Sets the scale of the entity along all three axes.

        :param scale: The scale to set.
        :param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
        """
        self.blender_obj.scale = scale
        Utility.insert_keyframe(self.blender_obj, "scale", frame)
Example #10
0
    def set_rotation_euler(self,
                           rotation_euler: Union[list, Euler, np.ndarray],
                           frame: int = None):
        """ Sets the rotation of the entity in euler angles.

        :param rotation_euler: The euler angles to set.
        :param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
        """
        self.blender_obj.rotation_euler = rotation_euler
        Utility.insert_keyframe(self.blender_obj, "rotation_euler", frame)
Example #11
0
    def set_location(self,
                     location: Union[list, Vector, np.ndarray],
                     frame: int = None):
        """ Sets the location of the entity in 3D world coordinates.

        :param location: The location to set.
        :param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
        """
        self.blender_obj.location = location
        Utility.insert_keyframe(self.blender_obj, "location", frame)
Example #12
0
    def __init__(self, config):
        Module.__init__(self, config)

        object_pose_sampler_config = config.get_raw_dict(
            "object_pose_sampler", {})
        camera_pose_sampler_config = config.get_raw_dict(
            "camera_pose_sampler", {})

        self._object_pose_sampler = Utility.initialize_modules(
            [object_pose_sampler_config])[0]
        self._camera_pose_sampler = Utility.initialize_modules(
            [camera_pose_sampler_config])[0]
Example #13
0
    def run(self):
        """ Hides objects for the set number of frames.
        """

        objects = self.config.get_list("selector")
        number_of_frames = self.config.get_int("number_of_frames",
                                               bpy.context.scene.frame_end)
        if number_of_frames > bpy.context.scene.frame_end:
            number_of_frames = bpy.context.scene.frame_end
        print(
            f"Will use {number_of_frames} number of frames, for {len(objects)} objects."
        )
        # iterate over all objects
        for obj in objects:
            obj.hide_render = False
            # Insert all selected objects to each frame for normal rendering.
            for frame in range(number_of_frames):
                Utility.insert_keyframe(obj, "hide_render", frame)

            # Insert updated selected objects to each frame with the field hide_render modified
            obj.hide_render = True
            for frame in range(number_of_frames):
                Utility.insert_keyframe(obj, "hide_render",
                                        frame + bpy.context.scene.frame_end)

        # Copy and modify camera location and rotation to the new frames where objects are hidden.
        camera = bpy.context.scene.camera
        for frame in range(number_of_frames):
            bpy.context.scene.frame_set(frame)
            Utility.insert_keyframe(camera, "location",
                                    frame + bpy.context.scene.frame_end)
            Utility.insert_keyframe(camera, "rotation_euler",
                                    frame + bpy.context.scene.frame_end)
        bpy.context.scene.frame_end += number_of_frames
    def set_cp(self, key: str, value: Any, frame: int = None):
        """ Sets the custom property with the given key.

        Keyframes can be only set for custom properties for the types int, float or bool.

        :param key: The key of the custom property.
        :param value: The value to set.
        :param frame: The frame number which the value should be set to. If None is given, the current frame number is used.
        """
        self.blender_obj[key] = value
        if isinstance(self.blender_obj[key], float) or isinstance(
                self.blender_obj[key], int):
            Utility.insert_keyframe(self.blender_obj, "[\"" + key + "\"]",
                                    frame)
def _colorize_object(obj: bpy.types.Object, color: mathutils.Vector,
                     use_alpha_channel: bool):
    """ 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 #16
0
def light_suncg_scene(lightbulb_emission_strength: float = 15, lampshade_emission_strength: float = 7,
                      ceiling_emission_strength: float = 1.5):
    """ Makes the lamps, windows and ceilings object emit light.

    :param lightbulb_emission_strength: The emission strength that should be used for light bulbs. Default: 15
    :param lampshade_emission_strength: The emission strength that should be used for lamp shades. Default: 7
    :param ceiling_emission_strength: The emission strength that should be used for the ceiling. Default: 1.5
    """
    # Read in the materials for lights and windows
    lights, windows = Utility.read_suncg_lights_windows_materials()

    collection_of_mats: Dict[str, Dict[str, Material]] = {"lamp": {}, "window": {}, "ceiling": {}}

    # Make some objects emit lights
    for obj in get_all_mesh_objects():
        if obj.has_cp("modelId"):
            obj_id = obj.get_cp("modelId")

            # In the case of the lamp
            if obj_id in lights:
                SuncgLighting._make_lamp_emissive(obj, lights[obj_id], collection_of_mats, lightbulb_emission_strength,
                                                  lampshade_emission_strength)

            # Make the windows emit light
            if obj_id in windows:
                SuncgLighting._make_window_emissive(obj, collection_of_mats)

            # Also make ceilings slightly emit light
            if obj.get_name().startswith("Ceiling#"):
                SuncgLighting._make_ceiling_emissive(obj, collection_of_mats, ceiling_emission_strength)
    def build_convex_decomposition_collision_shape(
            self,
            vhacd_path: str,
            temp_dir: str = None,
            cache_dir: str = "blenderproc_resources/decomposition_cache"):
        """ Builds a collision shape of the object by decomposing it into near convex parts using V-HACD

        :param vhacd_path: The directory in which vhacd should be installed or is already installed.
        :param temp_dir: The temp dir to use for storing the object files created by v-hacd.
        :param cache_dir: If a directory is given, convex decompositions are stored there named after the meshes hash. If the same mesh is decomposed a second time, the result is loaded from the cache and the actual decomposition is skipped.
        """
        if platform == "win32":
            raise Exception("This is currently not supported under Windows")

        if temp_dir is None:
            temp_dir = Utility.get_temporary_directory()

        # Decompose the object
        parts = convex_decomposition(self,
                                     temp_dir,
                                     resolve_path(vhacd_path),
                                     cache_dir=resolve_path(cache_dir))
        parts = [MeshObject(p) for p in parts]

        # Make the convex parts children of this object, enable their rigid body component and hide them
        for part in parts:
            part.set_parent(self)
            part.enable_rigidbody(True, "CONVEX_HULL")
            part.hide()
def _colorize_objects_for_instance_segmentation(objects: List[bpy.types.Object], use_alpha_channel: bool,
                                                render_colorspace_size_per_dimension: int) \
        -> Tuple[List[List[int]], int, List[bpy.types.Object]]:
    """ Sets a different color to each object.

    :param objects: A list of objects.
    :param use_alpha_channel: If true, the alpha channel stored in .png textures is used.
    :param render_colorspace_size_per_dimension: The limit of the colorspace to use per dimension for generating colors.
    :return: The num_splits_per_dimension of the spanned color space, the color map
    """
    # + 1 for the background
    colors, num_splits_per_dimension = Utility.generate_equidistant_values(
        len(objects) + 1, render_colorspace_size_per_dimension)
    # this list maps ids in the image back to the objects
    color_map = []

    # Set world background label, which is always label zero
    _set_world_background_color(colors[0])
    color_map.append(bpy.context.scene.world
                     )  # add the world background as an object to this list

    for idx, obj in enumerate(objects):
        _colorize_object(obj, colors[idx + 1], use_alpha_channel)
        color_map.append(obj)

    return colors, num_splits_per_dimension, color_map
def _disable_all_denoiser():
    """ Disables all denoiser.

    At the moment this includes the cycles and the intel denoiser.
    """
    # Disable cycles denoiser
    bpy.context.view_layer.cycles.use_denoising = False

    # Disable intel denoiser
    if bpy.context.scene.use_nodes:
        nodes = bpy.context.scene.node_tree.nodes
        links = bpy.context.scene.node_tree.links

        # Go through all existing denoiser nodes
        for denoiser_node in Utility.get_nodes_with_type(nodes, 'CompositorNodeDenoise'):
            in_node = denoiser_node.inputs['Image']
            out_node = denoiser_node.outputs['Image']

            # If it is fully included into the node tree
            if in_node.is_linked and out_node.is_linked:
                # There is always only one input link
                in_link = in_node.links[0]
                # Connect from_socket of the incoming link with all to_sockets of the out going links
                for link in out_node.links:
                    links.new(in_link.from_socket, link.to_socket)

            # Finally remove the denoiser node
            nodes.remove(denoiser_node)
Example #20
0
    def get_nodes_with_type(self, node_type: str) -> List[bpy.types.Node]:
        """ Returns all nodes which are of the given node_type

        :param node_type: The note type to look for.
        :return: The list of nodes with the given type.
        """
        return Utility.get_nodes_with_type(self.nodes, node_type)
Example #21
0
def set_world_background_hdr_img(path_to_hdr_file):
    """
    Sets the world background to the given hdr_file.

    :param path_to_hdr_file: Path to the .hdr file
    """

    if not os.path.exists(path_to_hdr_file):
        raise Exception(
            "The given path does not exists: {}".format(path_to_hdr_file))

    world = bpy.context.scene.world
    nodes = world.node_tree.nodes
    links = world.node_tree.links

    # add a texture node and load the image and link it
    texture_node = nodes.new(type="ShaderNodeTexEnvironment")
    texture_node.image = bpy.data.images.load(path_to_hdr_file,
                                              check_existing=True)

    # get the one output node of the world shader
    output_node = Utility.get_the_one_node_with_type(nodes, "Output")

    # link the new texture node to the output
    links.new(texture_node.outputs["Color"], output_node.inputs["Surface"])
Example #22
0
    def _load_texture(cur_obj: bpy.types.Object, texture_file_path: str, bop_dataset_name: str):
        """
        Load the textures for the ycbv objects, only those contain texture information

        :param cur_obj: The object to use.
        :param texture_file_path: path to the texture file (most likely ".png")
        :param bop_dataset_name: The name of the used bop dataset.
        """
        mat = bpy.data.materials.new(name="bop_" + bop_dataset_name + "_texture_material")

        mat.use_nodes = True

        nodes = mat.node_tree.nodes
        links = mat.node_tree.links

        color_image = nodes.new('ShaderNodeTexImage')
        if not os.path.exists(texture_file_path):
            raise Exception("The texture path for the ycbv object could not be loaded from the "
                            "file: {}".format(texture_file_path))
        color_image.image = bpy.data.images.load(texture_file_path, check_existing=True)

        principled = Utility.get_the_one_node_with_type(nodes, "BsdfPrincipled")
        links.new(color_image.outputs["Color"], principled.inputs["Base Color"])

        if cur_obj.data.materials:
            # assign to 1st material slot
            cur_obj.data.materials[0] = mat
        else:
            # no slots
            cur_obj.data.materials.append(mat)
Example #23
0
def add_nodes_to_group(nodes: bpy.types.Node,
                       group_name: str) -> bpy.types.ShaderNodeTree:
    """
    Creates the node group, copies all attributes and links and adds the group input and output
    https://blender.stackexchange.com/a/175604

    :param nodes: Nodes, which should be used
    :param group_name: Name of the group
    :return bpy.types.ShaderNodeTree: the group which can be used inside of a bpy.types.ShaderNodeGroup
    """
    # create new node group
    group = bpy.data.node_groups.new(name=group_name, type="ShaderNodeTree")

    # copy all nodes from the list to the created group with all their attributes
    copy_nodes(nodes, group.nodes)

    # copy the links between the nodes to the created groups nodes
    copy_links(nodes, group.nodes, group.links)

    # add the group input and output node to the created group
    group_input, group_output = add_group_nodes(group)

    # check if the selection of nodes goes over a material, if so replace the material output with the output of
    # the group
    material_outputs = Utility.get_nodes_with_type(group.nodes,
                                                   "OutputMaterial")
    if len(material_outputs) == 1:
        for input in material_outputs[0].inputs:
            group.outputs.new(input.bl_idname, input.name)
            for link in input.links:
                group.links.new(link.from_socket,
                                group_output.inputs[input.name])
        # remove the material output, the material output should never be inside of a group
        group.nodes.remove(material_outputs[0])
    return group
Example #24
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 #25
0
    def _adjust_material_nodes(mat: Material, adjustments: Dict[str, str]):
        """ Adjust the material node of the given material according to the given adjustments.

        Textures or diffuse colors will be changed according to the given material_adjustments.

        :param mat: The blender material.
        :param adjustments: A dict containing a new "diffuse" color or a new "texture" path
        """

        if "diffuse" in adjustments:
            principle_node = mat.get_the_one_node_with_type("BsdfPrincipled")
            principle_node.inputs[
                'Base Color'].default_value = Utility.hex_to_rgba(
                    adjustments["diffuse"])

        if "texture" in adjustments:
            image_path = os.path.join(SuncgLoader._suncg_dir, "texture",
                                      adjustments["texture"])
            image_path = resolve_path(image_path)

            if os.path.exists(image_path + ".png"):
                image_path += ".png"
            else:
                image_path += ".jpg"

            image_node = mat.get_the_one_node_with_type("ShaderNodeTexImage")
            if os.path.exists(image_path):
                image_node.image = bpy.data.images.load(image_path,
                                                        check_existing=True)
            else:
                print(
                    "Warning: Cannot load texture, path does not exist: {}, remove image node again"
                    .format(image_path))
                mat.remove_node(image_node)
def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_key: Optional[str] = "colors",
           load_keys: Optional[Set[str]] = None, return_data: bool = True,
           keys_with_alpha_channel: Optional[Set[str]] = None) -> Dict[str, Union[np.ndarray, List[np.ndarray]]]:
    """ Render all frames.

    This will go through all frames from scene.frame_start to scene.frame_end and render each of them.

    :param output_dir: The directory to write files to, if this is None the temporary directory is used. \
                       The temporary directory is usually in the shared memory (only true for linux).
    :param file_prefix: The prefix to use for writing the images.
    :param output_key: The key to use for registering the output.
    :param load_keys: Set of output keys to load when available
    :param return_data: Whether to load and return generated data. Backwards compatibility to config-based pipeline.
    :param keys_with_alpha_channel: A set containing all keys whose alpha channels should be loaded.
    :return: dict of lists of raw renderer output. Keys can be 'distance', 'colors', 'normals'
    """
    if output_dir is None:
        output_dir = Utility.get_temporary_directory()
    if load_keys is None:
        load_keys = {'colors', 'distance', 'depth', 'normals', 'diffuse'}
        keys_with_alpha_channel = {'colors'} if bpy.context.scene.render.film_transparent else None

    if output_key is not None:
        Utility.add_output_entry({
            "key": output_key,
            "path": os.path.join(output_dir, file_prefix) + "%04d" +
                    map_file_format_to_file_ending(bpy.context.scene.render.image_settings.file_format),
            "version": "2.0.0"
        })
        load_keys.add(output_key)

    bpy.context.scene.render.filepath = os.path.join(output_dir, file_prefix)

    # Skip if there is nothing to render
    if bpy.context.scene.frame_end != bpy.context.scene.frame_start:
        if len(get_all_blender_mesh_objects()) == 0:
            raise Exception("There are no mesh-objects to render, "
                            "please load an object before invoking the renderer.")
        # As frame_end is pointing to the next free frame, decrease it by one, as
        # blender will render all frames in [frame_start, frame_ned]
        bpy.context.scene.frame_end -= 1
        bpy.ops.render.render(animation=True, write_still=True)
        # Revert changes
        bpy.context.scene.frame_end += 1

    return WriterUtility.load_registered_outputs(load_keys, keys_with_alpha_channel) if return_data else {}
Example #27
0
    def get_the_one_node_with_type(self, node_type: str) -> bpy.types.Node:
        """ Returns the one node which is of the given node_type

        This function will only work if there is only one of the nodes of this type.

        :param node_type: The node type to look for.
        :return: The node.
        """
        return Utility.get_the_one_node_with_type(self.nodes, node_type)
Example #28
0
    def insert_node_instead_existing_link(
            self, source_socket: bpy.types.NodeSocket,
            new_node_dest_socket: bpy.types.NodeSocket,
            new_node_src_socket: bpy.types.NodeSocket,
            dest_socket: bpy.types.NodeSocket):
        """ Replaces the node between source_socket and dest_socket with a new node.

        Before: source_socket -> dest_socket
        After: source_socket -> new_node_dest_socket and new_node_src_socket -> dest_socket

        :param source_socket: The source socket.
        :param new_node_dest_socket: The new destination for the link starting from source_socket.
        :param new_node_src_socket: The new source for the link towards dest_socket.
        :param dest_socket: The destination socket
        """
        Utility.insert_node_instead_existing_link(self.links, source_socket,
                                                  new_node_dest_socket,
                                                  new_node_src_socket,
                                                  dest_socket)
Example #29
0
    def _default_init(self):
        """
        These operations are called during all modules inits
        """
        self._output_dir = resolve_path(
            self.config.get_string("output_dir", ""))
        os.makedirs(self._output_dir, exist_ok=True)

        self._temp_dir = Utility.get_temporary_directory()

        self._avoid_output = self.config.get_bool("avoid_output", False)
def set_denoiser(denoiser: Optional[str]):
    """ Enables the specified denoiser.

    Automatically disables all previously activated denoiser.

    :param denoiser: The name of the denoiser which should be enabled. Options are "INTEL", "BLENDER" and None. \
                     If None is given, then no denoiser will be active.
    """
    # Make sure there is no denoiser active
    _disable_all_denoiser()
    if denoiser is None:
        pass
    elif denoiser.upper() == "INTEL":
        # The intel denoiser is activated via the compositor
        bpy.context.scene.use_nodes = True
        nodes = bpy.context.scene.node_tree.nodes
        links = bpy.context.scene.node_tree.links

        # The denoiser gets normal and diffuse color as input
        bpy.context.view_layer.use_pass_normal = True
        bpy.context.view_layer.use_pass_diffuse_color = True

        # Add denoiser node
        denoise_node = nodes.new("CompositorNodeDenoise")

        # Link nodes
        render_layer_node = Utility.get_the_one_node_with_type(nodes, 'CompositorNodeRLayers')
        composite_node = Utility.get_the_one_node_with_type(nodes, 'CompositorNodeComposite')
        Utility.insert_node_instead_existing_link(links,
                                                  render_layer_node.outputs['Image'],
                                                  denoise_node.inputs['Image'],
                                                  denoise_node.outputs['Image'],
                                                  composite_node.inputs['Image'])

        links.new(render_layer_node.outputs['DiffCol'], denoise_node.inputs['Albedo'])
        links.new(render_layer_node.outputs['Normal'], denoise_node.inputs['Normal'])
    elif denoiser.upper() == "BLENDER":
        bpy.context.view_layer.cycles.use_denoising = True
    else:
        raise Exception("No such denoiser: " + denoiser)