Skip to content

collect_render

Collect render data.

CollectBlenderRender

Bases: BlenderInstancePlugin

Gather all publishable render instances.

Source code in client/ayon_blender/plugins/publish/collect_render.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
class CollectBlenderRender(plugin.BlenderInstancePlugin):
    """Gather all publishable render instances."""

    order = pyblish.api.CollectorOrder + 0.01
    hosts = ["blender"]
    families = ["renderlayer"]
    label = "Collect Render"
    sync_workfile_version = False

    @staticmethod
    def generate_expected_files(
        render_product, frame_start, frame_end, frame_step, ext
    ):
        """
        Generate the expected files for the render product for the beauty
        render. This returns a list of files that should be rendered. It
        replaces the sequence of `#` with the frame number.
        """
        expected_files = {}
        aov_files = []
        for render_name, render_file in render_product:
            path = os.path.dirname(render_file)
            file = os.path.basename(render_file)

            for frame in range(frame_start, frame_end + 1, frame_step):
                frame_str = str(frame).rjust(4, "0")
                filename = re.sub("#+", frame_str, file)
                expected_file = f"{os.path.join(path, filename)}.{ext}"
                aov_files.append(expected_file.replace("\\", "/"))

            expected_files[render_name] = [
                aov for aov in aov_files if render_name in aov
            ]

        return expected_files

    def process(self, instance):

        instance_node = instance.data["transientData"]["instance_node"]
        render_data = instance_node.get("render_data")

        assert render_data, "No render data found."

        render_product = render_data.get("render_product")
        aov_file_product = render_data.get("aov_file_product")
        ext = render_data.get("image_format")
        multilayer = render_data.get("multilayer_exr")
        review = render_data.get("review", False)

        frame_start = instance.data["frameStartHandle"]
        frame_end = instance.data["frameEndHandle"]

        if multilayer:
            expected_files = next((rn_product for rn_product in render_product.values()), None)
            expected_beauty = self.generate_expected_files(
                expected_files, int(frame_start), int(frame_end),
                int(bpy.context.scene.frame_step), ext)

            instance.data.update({
                "families": ["render", "render.farm"],
                "frameStart": frame_start,
                "frameEnd": frame_end,
                "productType": "render",
                "frameStartHandle": frame_start,
                "frameEndHandle": frame_end,
                "fps": instance.context.data["fps"],
                "byFrameStep": bpy.context.scene.frame_step,
                "review": review,
                "multipartExr": ext == "exr" and multilayer,
                "farm": True,
                "expectedFiles": [expected_beauty],
                # OCIO not currently implemented in Blender, but the following
                # settings are required by the schema, so it is hardcoded.
                # TODO: Implement OCIO in Blender
                "colorspaceConfig": "",
                "colorspaceDisplay": "sRGB",
                "colorspaceView": "ACES 1.0 SDR-video",
                "renderProducts": colorspace.ARenderProduct(
                    frame_start=frame_start,
                    frame_end=frame_end
                ),
            })

        else:
            instance.data["integrate"] = False
            self.create_renderlayer_instance(
                instance, render_product,
                aov_file_product, ext, multilayer,
                frame_start, frame_end, review)

    def create_renderlayer_instance(self, instance, render_product,
                                    aov_file_product, ext, multilayer,
                                    frame_start, frame_end, review):
        context = instance.context
        prod_type = "render"
        project_name = instance.context.data["projectName"]
        folder_entity = ayon_api.get_folder_by_path(
            project_name,
            instance.data["folderPath"]
        )
        task_name = instance.data.get("task")
        task_entity = None
        if folder_entity and task_name:
            task_entity = ayon_api.get_task_by_name(
                project_name, folder_entity["id"], task_name
            )

        for view_layer in bpy.context.scene.view_layers:
            viewlayer_name = view_layer.name
            rn_product = render_product[viewlayer_name]
            aov_product = aov_file_product[viewlayer_name] if aov_file_product else {}
            viewlayer_product_name = get_product_name(
                context.data["projectName"],
                task_entity["name"],
                task_entity["taskType"],
                context.data["hostName"],
                product_type=prod_type,
                variant=instance.data["variant"] + viewlayer_name,
                project_settings=context.data["project_settings"]
            )
            rn_layer_instance = context.create_instance(viewlayer_product_name)
            rn_layer_instance[:] = instance[:]
            expected_beauty = self.generate_expected_files(
                rn_product, int(frame_start), int(frame_end),
                int(bpy.context.scene.frame_step), ext)

            expected_aovs = self.generate_expected_files(
                aov_product, int(frame_start), int(frame_end),
                int(bpy.context.scene.frame_step), ext)

            expected_files = expected_beauty | expected_aovs
            rn_layer_instance.data.update({
                "family": prod_type,
                "families": [prod_type, "render.farm"],
                "fps": context.data["fps"],
                "byFrameStep": instance.data["creator_attributes"].get("step", 1),
                "review": review,
                "multipartExr": ext == "exr" and multilayer,
                "farm": True,
                "folderPath": instance.data["folderPath"],
                "productName": viewlayer_product_name,
                "productType": prod_type,
                "expectedFiles": [expected_files],
                "frameStart": instance.data["frameStart"],
                "frameEnd": instance.data["frameEnd"],
                "frameStartHandle": frame_start,
                "frameEndHandle": frame_end,
                "task": instance.data["task"],
                # OCIO not currently implemented in Blender, but the following
                # settings are required by the schema, so it is hardcoded.
                # TODO: Implement OCIO in Blender
                "colorspaceConfig": "",
                "colorspaceDisplay": "sRGB",
                "colorspaceView": "ACES 1.0 SDR-video",
                "renderProducts": colorspace.ARenderProduct(
                    frame_start=frame_start,
                    frame_end=frame_end
                ),
                "publish_attributes": instance.data["publish_attributes"]
            })
            instance.append(rn_layer_instance)

generate_expected_files(render_product, frame_start, frame_end, frame_step, ext) staticmethod

Generate the expected files for the render product for the beauty render. This returns a list of files that should be rendered. It replaces the sequence of # with the frame number.

Source code in client/ayon_blender/plugins/publish/collect_render.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@staticmethod
def generate_expected_files(
    render_product, frame_start, frame_end, frame_step, ext
):
    """
    Generate the expected files for the render product for the beauty
    render. This returns a list of files that should be rendered. It
    replaces the sequence of `#` with the frame number.
    """
    expected_files = {}
    aov_files = []
    for render_name, render_file in render_product:
        path = os.path.dirname(render_file)
        file = os.path.basename(render_file)

        for frame in range(frame_start, frame_end + 1, frame_step):
            frame_str = str(frame).rjust(4, "0")
            filename = re.sub("#+", frame_str, file)
            expected_file = f"{os.path.join(path, filename)}.{ext}"
            aov_files.append(expected_file.replace("\\", "/"))

        expected_files[render_name] = [
            aov for aov in aov_files if render_name in aov
        ]

    return expected_files