Adapt to Vanilla Blender 4.0 (#9)
* Fix Universal Playback * Fix on Load Post - On load post was dependant on scene_override -Alternative method is to loop through all the open window's scenes and use the first edit scene with scene strips * Synchronize all Windows by Default * Update 'Last_Master_Strip' string during render * on_load_post: loop through every scene - find first scene with a scene strip - assign as edit scene * Use Sync when Playing Master Scene * Remove 'scene_override' from sync * Fix Legacy Overrides * Remove Viewport Render Option from Batch Render * Remove bgl Module, replace with GPU Module
+59 −47 · 8 files changed
-
spa_sequencer/gpu_utils.py+8 −9 -
spa_sequencer/render/ops.py+14 −14 -
spa_sequencer/render/props.py+1 −4 -
spa_sequencer/render/tasks.py+5 −0 -
spa_sequencer/render/ui.py+0 −1 -
spa_sequencer/sequence/overlay.py+3 −1 -
spa_sequencer/sync/core.py+6 −9 -
spa_sequencer/sync/ops.py+22 −9
@@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Copyright (C) 2023, The SPA Studios. All rights reserved. -import bgl import bpy import gpu @@ -19,11 +18,11 @@ def ui_scaled(val): class OverlayDrawer: - """Helper class to draw overlays using bgl and gpu API.""" + """Helper class to draw overlays using gpu API.""" def __init__(self): self.format = gpu.types.GPUVertFormat() - self.shader = gpu.shader.from_builtin("2D_UNIFORM_COLOR") + self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") self.pos_id = self.format.attr_add( id="pos", comp_type="F32", len=2, fetch_mode="FLOAT" @@ -33,14 +32,14 @@ class OverlayDrawer: ) def draw(self, coords: list[Vec2f], indices: list[Vec3], color: Vec4f): - bgl.glEnable(bgl.GL_BLEND) + gpu.state.blend_set("ALPHA") batch = batch_for_shader(self.shader, "TRIS", {"pos": coords}, indices=indices) batch.program_set(self.shader) self.shader.uniform_float("color", color) batch.draw() - bgl.glDisable(bgl.GL_BLEND) + gpu.state.blend_set("NONE") def draw_points(self, coords: list[Vec2f], color: Vec4f): """Draw points at specified coords. @@ -48,12 +47,12 @@ class OverlayDrawer: :param coords: Points coordinates. :param color: Color of the points. """ - bgl.glEnable(bgl.GL_BLEND) + gpu.state.blend_set("ALPHA") batch = batch_for_shader(self.shader, "POINTS", {"pos": coords}) batch.program_set(self.shader) self.shader.uniform_float("color", color) batch.draw() - bgl.glDisable(bgl.GL_BLEND) + gpu.state.blend_set("NONE") def draw_lines(self, coords: list[Vec2f], color: Vec4f): """Draw lines at specified coords. @@ -61,12 +60,12 @@ class OverlayDrawer: :param coords: Lines points coordinates. :param color: Color of the lines. """ - bgl.glEnable(bgl.GL_BLEND) + gpu.state.blend_set("ALPHA") batch = batch_for_shader(self.shader, "LINES", {"pos": coords}) batch.program_set(self.shader) self.shader.uniform_float("color", color) batch.draw() - bgl.glDisable(bgl.GL_BLEND) + gpu.state.blend_set("NONE") def draw_rect(self, x: float, y: float, width: float, height: float, color: Vec4f): """Draw a filled rectangle, with [x,y] origin at bottom left corner. @@ -30,7 +30,8 @@ class RenderCancelled(RuntimeError): def close_window(window: bpy.types.Window): """Helper function to close the window `win`.""" - bpy.ops.wm.window_close({"window": window}, "INVOKE_DEFAULT") + with bpy.context.temp_override(window=window): + bpy.ops.wm.window_close("INVOKE_DEFAULT") class SEQUENCER_OT_batch_render(bpy.types.Operator): @@ -122,7 +123,8 @@ class SEQUENCER_OT_batch_render(bpy.types.Operator): if sed := output_scene.sequence_editor: # Deselect all strips in output scene's sequencer in order to keep # only the new strips as selected. - bpy.ops.sequencer.select_all({"scene": output_scene}, action="DESELECT") + with bpy.context.temp_override(scene=output_scene): + bpy.ops.sequencer.select_all(action="DESELECT") # Compute channel offset in output scene based on existing content if self.render_options.output_auto_offset_channels and sed.sequences: @@ -146,13 +148,12 @@ class SEQUENCER_OT_batch_render(bpy.types.Operator): # Get window region. region = next(region for region in area.regions if region.type == "WINDOW") # Ensure the entire rendered image is visible in the render window. - bpy.ops.image.view_all( - { - "window": self.render_window, - "area": self.render_window.screen.areas[0], - "region": region, - } - ) + with bpy.context.temp_override( + window=self.render_window, + area=self.render_window.screen.areas[0], + region=region, + ): + bpy.ops.image.view_all() def cancel(self, context): """Called when the operator is cancelled, e.g when the user closes the @@ -239,7 +240,6 @@ class SEQUENCER_OT_batch_render(bpy.types.Operator): # Show the render window ourselves to identify it. main_window = context.window - ctx = context.copy() bpy.ops.render.view_show("INVOKE_DEFAULT") # If render view is not active at this point, it means that it was already open. @@ -253,11 +253,11 @@ class SEQUENCER_OT_batch_render(bpy.types.Operator): try: # This operator will only succeed if the window and area # match a render view setup. - bpy.ops.render.view_cancel( - {"window": win, "area": win.screen.areas[0]} - ) + with context.temp_override(window=win, area=win.screen.areas[0]): + bpy.ops.render.view_cancel() # Re-open the render view - bpy.ops.render.view_show(ctx, "INVOKE_DEFAULT") + with context.temp_override(): + bpy.ops.render.view_show("INVOKE_DEFAULT") break except RuntimeError: pass @@ -34,10 +34,7 @@ class BatchRenderOptions(bpy.types.PropertyGroup): renderer: bpy.props.EnumProperty( name="Renderer", description="The renderer to use", - items=( - ("INTERNAL", "Internal", "Use Internal rendering"), - ("VIEWPORT", "Viewport", "Use Viewport rendering"), - ), + items=(("INTERNAL", "Internal", "Use Internal rendering"),), default="INTERNAL", options=set(), ) @@ -8,6 +8,8 @@ import os from typing import Any, Callable, Optional import bpy from spa_sequencer.render.props import MEDIA_TYPES_FORMATS, BatchRenderOptions +from spa_sequencer.sync.core import get_sync_settings + from spa_sequencer.sync.core import remap_frame_value @@ -232,6 +234,9 @@ class StripRenderTask(BaseRenderTask): self.overrides.set(scene.render, "filepath", filepath) def run(self, context: bpy.types.Context, render_options: BatchRenderOptions): + # Ensures functions dependant on current strip/sync are updated during render + get_sync_settings().last_master_strip = self.strip.name + overrides = {"scene": self.scene} if self.viewport_area: overrides["area"] = self.viewport_area @@ -16,7 +16,6 @@ class SEQUENCER_PT_batch_render(bpy.types.Panel): self.layout.use_property_split = True options = context.scene.batch_render_options self.layout.prop(options, "media_type") - self.layout.prop(options, "renderer") self.layout.prop(options, "render_engine") self.layout.prop(options, "resolution") if options.media_type == "MOVIE": @@ -93,7 +93,9 @@ def draw_shot_strip( # Shot name font_id = 0 blf.color(font_id, *(TEXT_COLOR_ACTIVE if active else TEXT_COLOR_BASE)) - blf.size(font_id, int(11 * bpy.context.preferences.system.ui_scale), 72) + blf.size( + font_id, int(11 * bpy.context.preferences.system.ui_scale) + ) # removed arg 72 # Compute text dimensions for horizontal centering dims = blf.dimensions(0, strip.name) @@ -39,7 +39,7 @@ class TimelineSyncSettings(bpy.types.PropertyGroup): sync_all_windows: bpy.props.BoolProperty( name="Synchronize all Windows", description="Whether the Timeline Synchronization impacts all Main Windows", - default=False, + default=True, ) keep_gpencil_tool_settings: bpy.props.BoolProperty( @@ -595,17 +595,14 @@ def on_load_pre(*args): def on_load_post(*args): sync_settings = get_sync_settings() # Auto-setup the system for the new file if the active screen contains - # a Sequence Editor area defining a scene override with at least 1 scene strip. - for area in bpy.context.screen.areas: - space = area.spaces.active - if isinstance(space, bpy.types.SpaceSequenceEditor) and getattr( - space, "scene_override", False - ): - seq_editor = area.spaces.active.scene_override.sequence_editor + # a Sequence Editor area defining a scene with at least 1 scene strip. + for scene in bpy.data.scenes: + if scene.sequence_editor: + seq_editor = scene.sequence_editor if seq_editor and any( isinstance(s, bpy.types.SceneSequence) for s in seq_editor.sequences ): - sync_settings.master_scene = area.spaces.active.scene_override + sync_settings.master_scene = scene sync_settings.enabled = True update_sync_cache_from_current_state() break @@ -18,14 +18,19 @@ class WM_OT_timeline_sync_toggle(bpy.types.Operator): sync_settings = get_sync_settings() sync_settings.enabled = not sync_settings.enabled + """NOTE: The below code is an integration with + https://github.com/The-SPA-Studios/blender + it is disabled unless space_data.scene_override + is included in master.""" + # Setup with active space's data if applicable - if ( - sync_settings.enabled - and isinstance(context.space_data, bpy.types.SpaceSequenceEditor) - and context.space_data.scene_override - ): - # Use overriden scene defined in the SpaceSequence editor as master scene - sync_settings.master_scene = context.space_data.scene_override + # if ( + # sync_settings.enabled + # and isinstance(context.space_data, bpy.types.SpaceSequenceEditor) + # # and context.space_data.scene_override + # ): + # Use overriden scene defined in the SpaceSequence editor as master scene + # sync_settings.master_scene = context.space_data.scene_override # Trigger sync system update sync_system_update(context) @@ -53,9 +58,17 @@ class WM_OT_timeline_sync_play_master(bpy.types.Operator): return get_sync_settings().master_scene is not None def execute(self, context: bpy.types.Context): + # TODO Improve this by using a pointer to find the main window instead + master_scene = get_sync_settings().master_scene + for window in context.window_manager.windows: + if window.scene == master_scene: + master_window = window # Trigger playback on master scene using context override. - with context.temp_override(scene=get_sync_settings().master_scene): - bpy.ops.screen.animation_play("INVOKE_DEFAULT") + with context.temp_override( + window=master_window, + scene=get_sync_settings().master_scene, + ): + bpy.ops.screen.animation_play(sync=True) return {"FINISHED"}