← sequencer-addon

Adapt to Vanilla Blender 4.0 (#9)

7aa205d4f06dbb5558018d7f8ed0a4413ac6c6e3 · Nick Alberelli · Jan 10, 2024 14:26

* 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
diff --git a/spa_sequencer/gpu_utils.py b/spa_sequencer/gpu_utils.py
index 3122554..1f386af 100644
--- a/spa_sequencer/gpu_utils.py
+++ b/spa_sequencer/gpu_utils.py
@@ -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.
diff --git a/spa_sequencer/render/ops.py b/spa_sequencer/render/ops.py
index cbc172f..164fa92 100644
--- a/spa_sequencer/render/ops.py
+++ b/spa_sequencer/render/ops.py
@@ -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
diff --git a/spa_sequencer/render/props.py b/spa_sequencer/render/props.py
index 208d0b5..9118c69 100644
--- a/spa_sequencer/render/props.py
+++ b/spa_sequencer/render/props.py
@@ -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(),
     )
diff --git a/spa_sequencer/render/tasks.py b/spa_sequencer/render/tasks.py
index 0a7d3fb..5debcba 100644
--- a/spa_sequencer/render/tasks.py
+++ b/spa_sequencer/render/tasks.py
@@ -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
diff --git a/spa_sequencer/render/ui.py b/spa_sequencer/render/ui.py
index 8c75300..a3c97c6 100644
--- a/spa_sequencer/render/ui.py
+++ b/spa_sequencer/render/ui.py
@@ -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":
diff --git a/spa_sequencer/sequence/overlay.py b/spa_sequencer/sequence/overlay.py
index 5bcade9..61c7169 100644
--- a/spa_sequencer/sequence/overlay.py
+++ b/spa_sequencer/sequence/overlay.py
@@ -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)
diff --git a/spa_sequencer/sync/core.py b/spa_sequencer/sync/core.py
index 4260214..72e9752 100644
--- a/spa_sequencer/sync/core.py
+++ b/spa_sequencer/sync/core.py
@@ -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
diff --git a/spa_sequencer/sync/ops.py b/spa_sequencer/sync/ops.py
index 922910e..7e6692e 100644
--- a/spa_sequencer/sync/ops.py
+++ b/spa_sequencer/sync/ops.py
@@ -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"}