Skip to content

capture

Blender Capture Playblasting with independent viewport, camera and display options

applied_frame_range(window, start, end, step)

Context manager for setting frame range.

Source code in client/ayon_blender/api/capture.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
@contextlib.contextmanager
def applied_frame_range(window, start, end, step):
    """Context manager for setting frame range."""
    # Store current frame range
    current_frame_start = window.scene.frame_start
    current_frame_end = window.scene.frame_end
    current_frame_step = window.scene.frame_step
    # Apply frame range
    window.scene.frame_start = start
    window.scene.frame_end = end
    window.scene.frame_step = step
    try:
        yield
    finally:
        # Restore frame range
        window.scene.frame_start = current_frame_start
        window.scene.frame_end = current_frame_end
        window.scene.frame_step = current_frame_step

applied_image_settings(window, options)

Context manager to override image settings.

Source code in client/ayon_blender/api/capture.py
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
@contextlib.contextmanager
def applied_image_settings(window, options):
    """Context manager to override image settings."""

    options = options or ImageSettings.copy()
    ffmpeg = options.pop("ffmpeg", {})
    render = window.scene.render

    # Store current image settings
    original = {}
    for opt in options.copy():
        try:
            original[opt] = getattr(render.image_settings, opt)
        except ValueError:
            options.pop(opt)

    # Store current ffmpeg settings
    original_ffmpeg = {}
    for opt in ffmpeg.copy():
        try:
            original_ffmpeg[opt] = getattr(render.ffmpeg, opt)
        except ValueError:
            ffmpeg.pop(opt)

    # Apply image settings
    for opt, value in options.items():
        setattr(render.image_settings, opt, value)

    # Apply ffmpeg settings
    for opt, value in ffmpeg.items():
        setattr(render.ffmpeg, opt, value)

    try:
        yield
    finally:
        # Restore previous settings
        for opt, value in original.items():
            setattr(render.image_settings, opt, value)
        for opt, value in original_ffmpeg.items():
            setattr(render.ffmpeg, opt, value)

applied_render_options(window, options)

Context manager for setting render options.

Source code in client/ayon_blender/api/capture.py
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
@contextlib.contextmanager
def applied_render_options(window, options):
    """Context manager for setting render options."""
    render = window.scene.render

    # Store current settings
    original = {}
    for opt in options.copy():
        try:
            original[opt] = getattr(render, opt)
        except ValueError:
            options.pop(opt)

    # Apply settings
    _apply_options(render, options)

    try:
        yield
    finally:
        # Restore previous settings
        _apply_options(render, original)

applied_view(window, camera, isolate=None, options=None)

Apply view options to window.

Source code in client/ayon_blender/api/capture.py
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
def applied_view(window, camera, isolate=None, options=None):
    """Apply view options to window."""
    area = window.screen.areas[0]
    space = area.spaces[0]

    area.ui_type = "VIEW_3D"

    types = {"MESH", "GPENCIL"}
    objects = [obj for obj in window.scene.objects if obj.type in types]

    if camera == "AUTO":
        space.region_3d.view_perspective = "ORTHO"
        isolate_objects(window, isolate or objects)
    else:
        isolate_objects(window, isolate or objects)
        space.camera = window.scene.objects.get(camera)
        space.region_3d.view_perspective = "CAMERA"

    if isinstance(options, dict):
        _apply_options(space, options)
    else:
        space.shading.type = "SOLID"
        space.shading.color_type = "MATERIAL"
        space.show_gizmo = False
        space.overlay.show_overlays = False

capture(camera=None, width=None, height=None, filename=None, start_frame=None, end_frame=None, step_frame=None, sound=None, isolate=None, maintain_aspect_ratio=True, overwrite=False, image_settings=None, display_options=None)

Playblast in an independent windows Arguments: camera (str, optional): Name of camera, defaults to "Camera" width (int, optional): Width of output in pixels height (int, optional): Height of output in pixels filename (str, optional): Name of output file path. Defaults to current render output path. start_frame (int, optional): Defaults to current start frame. end_frame (int, optional): Defaults to current end frame. step_frame (int, optional): Defaults to 1. sound (str, optional): Specify the sound node to be used during playblast. When None (default) no sound will be used. isolate (list): List of nodes to isolate upon capturing maintain_aspect_ratio (bool, optional): Modify height in order to maintain aspect ratio. overwrite (bool, optional): Whether or not to overwrite if file already exists. If disabled and file exists and error will be raised. image_settings (dict, optional): Supplied image settings for render, using ImageSettings display_options (dict, optional): Supplied display options for render

Source code in client/ayon_blender/api/capture.py
 12
 13
 14
 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
def capture(
    camera=None,
    width=None,
    height=None,
    filename=None,
    start_frame=None,
    end_frame=None,
    step_frame=None,
    sound=None,
    isolate=None,
    maintain_aspect_ratio=True,
    overwrite=False,
    image_settings=None,
    display_options=None
):
    """Playblast in an independent windows
    Arguments:
        camera (str, optional): Name of camera, defaults to "Camera"
        width (int, optional): Width of output in pixels
        height (int, optional): Height of output in pixels
        filename (str, optional): Name of output file path. Defaults to current
            render output path.
        start_frame (int, optional): Defaults to current start frame.
        end_frame (int, optional): Defaults to current end frame.
        step_frame (int, optional): Defaults to 1.
        sound (str, optional):  Specify the sound node to be used during
            playblast. When None (default) no sound will be used.
        isolate (list): List of nodes to isolate upon capturing
        maintain_aspect_ratio (bool, optional): Modify height in order to
            maintain aspect ratio.
        overwrite (bool, optional): Whether or not to overwrite if file
            already exists. If disabled and file exists and error will be
            raised.
        image_settings (dict, optional): Supplied image settings for render,
            using `ImageSettings`
        display_options (dict, optional): Supplied display options for render
    """

    scene = bpy.context.scene
    camera = camera or "Camera"

    # Ensure camera exists.
    if camera not in scene.objects and camera != "AUTO":
        raise RuntimeError("Camera does not exist: {0}".format(camera))

    # Ensure resolution.
    if width and height:
        maintain_aspect_ratio = False
    width = width or scene.render.resolution_x
    height = height or scene.render.resolution_y
    if maintain_aspect_ratio:
        ratio = scene.render.resolution_x / scene.render.resolution_y
        height = round(width / ratio)

    # Get frame range.
    if start_frame is None:
        start_frame = scene.frame_start
    if end_frame is None:
        end_frame = scene.frame_end
    if step_frame is None:
        step_frame = 1
    frame_range = (start_frame, end_frame, step_frame)

    if filename is None:
        filename = scene.render.filepath

    render_options = {
        "filepath": "{}.".format(filename.rstrip(".")),
        "resolution_x": width,
        "resolution_y": height,
        "use_overwrite": overwrite,
    }

    with _independent_window() as window:

        applied_view(window, camera, isolate, options=display_options)

        with contextlib.ExitStack() as stack:
            stack.enter_context(maintain_camera(window, camera))
            stack.enter_context(applied_frame_range(window, *frame_range))
            stack.enter_context(applied_render_options(window, render_options))
            stack.enter_context(applied_image_settings(window, image_settings))
            stack.enter_context(maintained_time())

            bpy.ops.render.opengl(
                animation=True,
                render_keyed_only=False,
                sequencer=False,
                write_still=False,
                view_context=True
            )

    return filename

isolate_objects(window, objects)

Isolate selection

Source code in client/ayon_blender/api/capture.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def isolate_objects(window, objects):
    """Isolate selection"""
    deselect_all()

    for obj in objects:
        obj.select_set(True)

    context = create_blender_context(selected=objects, window=window)

    with bpy.context.temp_override(**context):
        bpy.ops.view3d.view_axis(type="FRONT")
        bpy.ops.view3d.localview()

    deselect_all()

maintain_camera(window, camera)

Context manager to override camera.

Source code in client/ayon_blender/api/capture.py
271
272
273
274
275
276
277
278
279
280
@contextlib.contextmanager
def maintain_camera(window, camera):
    """Context manager to override camera."""
    current_camera = window.scene.camera
    if camera in window.scene.objects:
        window.scene.camera = window.scene.objects.get(camera)
    try:
        yield
    finally:
        window.scene.camera = current_camera

restore_global_view(window)

Exit local view if active.

Blender currently does not exit localview when closing windows.

Source code in client/ayon_blender/api/capture.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def restore_global_view(window):
    """Exit local view if active.

    Blender currently does not exit localview when closing windows.
    """

    types = {"MESH", "GPENCIL"}
    objects = [obj for obj in window.scene.objects if obj.type in types]

    context = create_blender_context(selected=objects, window=window)

    with bpy.context.temp_override(**context):
        # Only toggle back if in local view
        if bpy.context.space_data.local_view:
            bpy.ops.view3d.localview()