Skip to content

rendering

get_render_config(project_name, render_preset=None, project_render_settings=None)

Returns Unreal asset from render config.

Expects configured location of render config set in Settings. This path must contain stored render config in Unreal project.

Render config in the settings are deprecated, use render preset on the instance instead.

Parameters:

Name Type Description Default
project_name str
required
render_preset str

Name of the render preset to use from instance.

None
project_settings dict

Project render settings from get_project_settings.

required

Returns:

Type Description
(str, uasset)

path and UAsset

Source code in client/ayon_unreal/api/rendering.py
 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
def get_render_config(
        project_name: str,
        render_preset: Optional[str] = None,
        project_render_settings=None):
    """Returns Unreal asset from render config.

    Expects configured location of render config set in Settings. This path
    must contain stored render config in Unreal project.

    Render config in the settings are deprecated, use render preset
    on the instance instead.

    Args:
        project_name (str):
        render_preset (str): Name of the render preset to
            use from instance.
        project_settings (dict): Project render settings from
            get_project_settings.

    Returns:
        (str, uasset): path and UAsset

    Raises:
        RuntimeError if no path to config is set

    """
    ar = unreal.AssetRegistryHelpers.get_asset_registry()
    config = None
    config_path = None

    if render_preset:
        asset_filter = unreal.ARFilter(
            class_names=["MoviePipelinePrimaryConfig"],
            recursive_paths=True,
        )
        render_presets = ar.get_assets(asset_filter)
        for preset in render_presets:
            if preset.asset_name == render_preset:
                config = preset.get_asset()
                config_path = preset.package_path
                break

    if config:
        unreal.log(f"Using render preset {render_preset}")
        return config_path, config

    unreal.log(
        "No render preset found on instance, "
        "falling back to project settings")

    if not project_render_settings:
        project_settings = get_project_settings(project_name)
        project_render_settings = project_settings["unreal"]["unreal_setup"]

    config_path = project_render_settings["render_config_path"]

    if not config_path:
        raise RuntimeError("Please provide location for stored render "
            "config in `ayon+settings://unreal/render_setup/render_config_path`")

    unreal.log(f"Configured config path {config_path}")
    if not unreal.EditorAssetLibrary.does_asset_exist(config_path):
        raise RuntimeError(f"No config found at {config_path}")

    unreal.log("Found saved render configuration")
    config = ar.get_asset_by_object_path(config_path).get_asset()

    return config_path, config

set_output_extension_from_settings(render_format, config)

Forces output extension from Settings if available.

Clear all other extensions if there is value in Settings. Args: render_format (str): "png"|"jpg"|"exr"|"bmp" config (unreal.MoviePipelineMasterConfig) Returns (unreal.MoviePipelineMasterConfig)

Source code in client/ayon_unreal/api/rendering.py
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
def set_output_extension_from_settings(render_format, config):
    """Forces output extension from Settings if available.

    Clear all other extensions if there is value in Settings.
    Args:
        render_format (str): "png"|"jpg"|"exr"|"bmp"
        config (unreal.MoviePipelineMasterConfig)
    Returns
        (unreal.MoviePipelineMasterConfig)
    """
    if not render_format:
        return config

    cls_from_map = SUPPORTED_EXTENSION_MAP.get(render_format.lower())
    if not cls_from_map:
        return config

    for ext, cls in SUPPORTED_EXTENSION_MAP.items():
        current_sett = config.find_setting_by_class(cls)
        if current_sett and ext == render_format:
            return config
        config.remove_setting(current_sett)

    config.find_or_add_setting_by_class(cls_from_map)
    return config

start_rendering()

Start the rendering process.

Source code in client/ayon_unreal/api/rendering.py
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def start_rendering():
    """
    Start the rendering process.
    """
    unreal.log("Starting rendering...")

    # Get selected sequences
    assets = unreal.EditorUtilityLibrary.get_selected_assets()

    if not assets:
        show_message_dialog(
            title="No assets selected",
            message="No assets selected. Select a render instance.",
            level="warning")
        raise RuntimeError(
            "No assets selected. You need to select a render instance.")

    # instances = pipeline.ls_inst()
    instances = [
        a for a in assets
        if a.get_class().get_name() == "AyonPublishInstance"]
    if not instances:
        show_message_dialog(
            title="No AyonPublishInstance selected",
            message="No AyonPublishInstance selected. Select render instance data asset.",      # noqa
            level="warning"
        )
        raise RuntimeError(
            "No AyonPublishInstance selected. Select render instance data asset.")
    inst_data = []

    for i in instances:
        data = pipeline.parse_container(i.get_path_name())
        if data["productType"] == "render":
            inst_data.append(data)

    try:
        project_name = os.environ.get("AYON_PROJECT_NAME")
        anatomy = Anatomy(project_name)
        root = anatomy.roots['renders']
    except Exception as e:
        raise Exception(
            "Could not find render root in anatomy settings.") from e

    render_dir = f"{root}/{project_name}"

    # subsystem = unreal.get_editor_subsystem(
    #     unreal.MoviePipelineQueueSubsystem)
    # queue = subsystem.get_queue()
    global queue
    queue = unreal.MoviePipelineQueue()

    ar = unreal.AssetRegistryHelpers.get_asset_registry()

    project_settings = get_project_settings(project_name)
    render_settings = project_settings["unreal"]["render_setup"]


    les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
    current_level = les.get_current_level()
    current_level_name = current_level.get_outer().get_path_name()

    for i in inst_data:
        # for some reason the instance data has strings, convert them
        # back to their original types
        render_preset = ast.literal_eval(i["creator_attributes"]).get(
            "render_preset"
        )

        _, config = get_render_config(
            project_name, render_preset, render_settings)


        sequence = ar.get_asset_by_object_path(i["sequence"]).get_asset()

        sequences = [{
            "sequence": sequence,
            "output": f"{i['output']}",
            "frame_range": (
                int(float(i["frameStart"])),
                int(float(i["frameEnd"])) + 1)
        }]
        render_list = []

        # Get all the sequences to render. If there are subsequences,
        # add them and their frame ranges to the render list. We also
        # use the names for the output paths.
        for seq in sequences:
            subscenes = pipeline.get_subsequences(seq.get('sequence'))

            if subscenes:
                sequences.extend(
                    {
                        "sequence": sub_seq.get_sequence(),
                        "output": (
                            f"{seq.get('output')}/"
                            f"{sub_seq.get_sequence().get_name()}"
                        ),
                        "frame_range": (
                            sub_seq.get_start_frame(),
                            sub_seq.get_end_frame(),
                        ),
                    }
                    for sub_seq in subscenes
                )
            elif "_camera" not in seq.get('sequence').get_name():
                render_list.append(seq)

        if i["master_level"] != current_level_name:
            unreal.log_warning(
                "{} is not the persistent level, use {} for rendering".format(
                i["master_level"], current_level_name)
            )
            i["master_level"] = current_level_name

        # Create the rendering jobs and add them to the queue.
        for render_setting in render_list:
            job = queue.allocate_new_job(unreal.MoviePipelineExecutorJob)
            job.sequence = unreal.SoftObjectPath(i["master_sequence"])
            job.map = unreal.SoftObjectPath(i["master_level"])
            job.author = "Ayon"

            job.set_configuration(config)

            # If we have a saved configuration, copy it to the job.
            if config:
                job.get_configuration().copy_from(config)

            job_config = job.get_configuration()
            # User data could be used to pass data to the job, that can be
            # read in the job's OnJobFinished callback. We could,
            # for instance, pass the AyonPublishInstance's path to the job.
            # job.user_data = ""

            output_dir = render_setting.get('output')
            shot_name = render_setting.get('sequence').get_name()

            settings = job_config.find_or_add_setting_by_class(
                unreal.MoviePipelineOutputSetting)
            settings.output_resolution = unreal.IntPoint(1920, 1080)
            settings.custom_start_frame = render_setting.get("frame_range")[0]
            settings.custom_end_frame = render_setting.get("frame_range")[1]
            settings.use_custom_playback_range = True
            settings.file_name_format = f"{shot_name}" + ".{frame_number}"
            settings.output_directory.path = f"{render_dir}/{output_dir}"

            job_config.find_or_add_setting_by_class(
                unreal.MoviePipelineDeferredPassBase)

            render_format = render_settings.get("render_format",
                                                "png")

            set_output_extension_from_settings(render_format,
                                               job_config)

    # If there are jobs in the queue, start the rendering process.
    if queue.get_jobs():
        global executor
        executor = unreal.MoviePipelinePIEExecutor()
        preroll_frames = render_settings.get("preroll_frames", 0)

        settings = unreal.MoviePipelinePIEExecutorSettings()
        settings.set_editor_property(
            "initial_delay_frame_count", preroll_frames)

        executor.on_executor_finished_delegate.add_callable_unique(
            _queue_finish_callback)
        executor.on_individual_job_finished_delegate.add_callable_unique(
            _job_finish_callback)  # Only available on PIE Executor
        executor.execute(queue)