[Bf-extensions-cvs] [76fba3f8] master: GPencil Tools: Add viewport timeline scrub feature

Pullusb noreply at git.blender.org
Sun Jan 24 18:36:52 CET 2021


Commit: 76fba3f84b6fdcc350d232eb740c0cd72ac9ffb7
Author: Pullusb
Date:   Sun Jan 24 18:36:37 2021 +0100
Branches: master
https://developer.blender.org/rBA76fba3f84b6fdcc350d232eb740c0cd72ac9ffb7

GPencil Tools: Add viewport timeline scrub feature

Add timeline scrubbing from the standalone add-on : https://github.com/Pullusb/viewport_timeline_scrub
This bind a shortcut to move in timeline directly from viewport, VSE and movie clip editors.
Display keyframes and snap using `Ctrl` key.
Propagate the chosen shortcut to timeline editors.
Shortcut, behavior and overlays can be customized in addon-preferences.

===================================================================

M	greasepencil_tools/__init__.py
M	greasepencil_tools/prefs.py
A	greasepencil_tools/timeline_scrub.py

===================================================================

diff --git a/greasepencil_tools/__init__.py b/greasepencil_tools/__init__.py
index 02e93c61..abc7a033 100644
--- a/greasepencil_tools/__init__.py
+++ b/greasepencil_tools/__init__.py
@@ -21,7 +21,7 @@ bl_info = {
 "name": "Grease Pencil Tools",
 "description": "Extra tools for Grease Pencil",
 "author": "Samuel Bernou, Antonio Vazquez, Daniel Martinez Lara, Matias Mendiola",
-"version": (1, 1, 6),
+"version": (1, 2, 0),
 "blender": (2, 91, 0),
 "location": "Sidebar > Grease Pencil > Grease Pencil Tools",
 "warning": "",
@@ -36,12 +36,14 @@ from .  import (prefs,
                 box_deform,
                 line_reshape,
                 rotate_canvas,
+                timeline_scrub,
                 import_brush_pack,
                 ui_panels,
                 )
 
 def register():
     prefs.register()
+    timeline_scrub.register()
     box_deform.register()
     line_reshape.register()
     rotate_canvas.register()
@@ -57,6 +59,7 @@ def unregister():
     rotate_canvas.unregister()
     box_deform.unregister()
     line_reshape.unregister()
+    timeline_scrub.unregister()
     prefs.unregister()
 
 if __name__ == "__main__":
diff --git a/greasepencil_tools/prefs.py b/greasepencil_tools/prefs.py
index 1475e95c..69b92780 100644
--- a/greasepencil_tools/prefs.py
+++ b/greasepencil_tools/prefs.py
@@ -22,6 +22,7 @@ from bpy.props import (
         BoolProperty,
         EnumProperty,
         StringProperty,
+        PointerProperty,
         # IntProperty,
         )
 
@@ -33,6 +34,8 @@ def get_addon_prefs():
     addon_prefs = bpy.context.preferences.addons[addon_name].preferences
     return (addon_prefs)
 
+from .timeline_scrub import GPTS_timeline_settings, draw_ts_pref
+
 ## Addons Preferences Update Panel
 def update_panel(self, context):
     try:
@@ -48,9 +51,11 @@ def auto_rebind(self, context):
     register_keymaps()
 
 class GreasePencilAddonPrefs(bpy.types.AddonPreferences):
-    bl_idname = os.path.splitext(__name__)[0]#'greasepencil-addon' ... __package__ ?
+    bl_idname = os.path.splitext(__name__)[0] #'greasepencil-addon' ... __package__ ?
     # bl_idname = __name__
 
+    ts: PointerProperty(type=GPTS_timeline_settings)
+
     category : StringProperty(
             name="Category",
             description="Choose a name for the category of the panel",
@@ -127,6 +132,7 @@ class GreasePencilAddonPrefs(bpy.types.AddonPreferences):
             update=auto_rebind)
 
     def draw(self, context):
+            prefs = get_addon_prefs()
             layout = self.layout
             # layout.use_property_split = True
             row= layout.row(align=True)
@@ -176,6 +182,9 @@ class GreasePencilAddonPrefs(bpy.types.AddonPreferences):
                     box.label(text="view3d.rotate_canvas")
                 box.prop(self, 'canvas_use_hud')
 
+                ## SCRUB TIMELINE
+                box = layout.box()
+                draw_ts_pref(prefs.ts, box)
 
             if self.pref_tabs == 'TUTO':
 
@@ -237,6 +246,7 @@ def unregister_keymaps():
 ### REGISTER ---
 
 def register():
+    bpy.utils.register_class(GPTS_timeline_settings)
     bpy.utils.register_class(GreasePencilAddonPrefs)
     # Force box deform running to false
     bpy.context.preferences.addons[os.path.splitext(__name__)[0]].preferences.boxdeform_running = False
@@ -245,3 +255,4 @@ def register():
 def unregister():
     unregister_keymaps()
     bpy.utils.unregister_class(GreasePencilAddonPrefs)
+    bpy.utils.unregister_class(GPTS_timeline_settings)
\ No newline at end of file
diff --git a/greasepencil_tools/timeline_scrub.py b/greasepencil_tools/timeline_scrub.py
new file mode 100644
index 00000000..7a783e0b
--- /dev/null
+++ b/greasepencil_tools/timeline_scrub.py
@@ -0,0 +1,807 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+'''Based on viewport_timeline_scrub standalone addon - Samuel Bernou'''
+
+from .prefs import get_addon_prefs
+
+import numpy as np
+from time import time
+import bpy
+import gpu
+import bgl
+import blf
+from gpu_extras.batch import batch_for_shader
+
+from bpy.props import (BoolProperty,
+                       StringProperty,
+                       IntProperty,
+                       FloatVectorProperty,
+                       IntProperty,
+                       PointerProperty,
+                       EnumProperty)
+
+""" bl_info = {
+    "name": "Viewport Scrub Timeline",
+    "description": "Scrub on timeline from viewport and snap to nearest keyframe",
+    "author": "Samuel Bernou",
+    "version": (0, 7, 5),
+    "blender": (2, 91, 0),
+    "location": "View3D > shortcut key chosen in addon prefs",
+    "warning": "",
+    "doc_url": "https://github.com/Pullusb/scrub_timeline",
+    "category": "Object"}
+ """
+
+def nearest(array, value):
+    '''
+    Get a numpy array and a target value
+    Return closest val found in array to passed value
+    '''
+    idx = (np.abs(array - value)).argmin()
+    return array[idx]
+
+
+def draw_callback_px(self, context):
+    '''Draw callback use by modal to draw in viewport'''
+    if context.area != self.current_area:
+        return
+    ## lines and shaders
+    # 50% alpha, 2 pixel width line
+
+    # text
+    font_id = 0
+
+    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')  # initiate shader
+    bgl.glEnable(bgl.GL_BLEND)
+    bgl.glLineWidth(1)
+
+    # - # Draw HUD
+    if self.use_hud_time_line:
+        shader.bind()
+        shader.uniform_float("color", self.color_timeline)
+        self.batch_timeline.draw(shader)
+
+    # - # Display keyframes
+    if self.use_hud_keyframes:
+        if self.keyframe_aspect == 'LINE':
+            bgl.glLineWidth(3)
+            shader.bind()
+            shader.uniform_float("color", self.color_timeline)
+            self.batch_keyframes.draw(shader)
+        else:
+            # - # Display keyframe as diamonds
+            bgl.glLineWidth(1)
+            shader.bind()
+            shader.uniform_float("color", self.color_timeline)
+            # shader.uniform_float("color", list(self.color_timeline[:3]) + [1]) # timeline color full opacity
+            # shader.uniform_float("color", (0.8, 0.8, 0.8, 0.8)) # grey
+            # shader.uniform_float("color", (0.9, 0.69, 0.027, 1.0)) # yellow-ish
+            # shader.uniform_float("color",(1.0, 0.515, 0.033, 1.0)) # orange 'selected keyframe'
+            self.batch_keyframes.draw(shader)
+
+    # - # Display init frame text (under playhead)
+    # if self.use_hud_frame_init: # propertie not existing currently
+    # blf.position(font_id, self.init_mouse_x,
+    #                 self.init_mouse_y - (60 *self.ui_scale), 0)
+    # blf.size(font_id, 16, self.dpi)
+    # blf.color(font_id, *self.color_timeline)
+    # blf.draw(font_id, f'{self.init_frame:.0f}')
+
+    # - # Show current frame line
+    bgl.glLineWidth(1)
+    if self.use_hud_playhead:
+        # -# old full height playhead
+        # playhead = [(self.cursor_x, 0), (self.cursor_x, context.area.height)]
+        playhead = [(self.cursor_x, self.my + self.playhead_size/2),
+                    (self.cursor_x, self.my - self.playhead_size/2)]
+        batch = batch_for_shader(shader, 'LINES', {"pos": playhead})
+        shader.bind()
+        shader.uniform_float("color", self.color_playhead)
+        batch.draw(shader)
+
+    # restore opengl defaults
+    bgl.glDisable(bgl.GL_BLEND)
+
+    # - # Display current frame text
+    blf.color(font_id, *self.color_text)
+    if self.use_hud_frame_current:
+        blf.position(font_id, self.mouse[0]+10, self.mouse[1]+10, 0)
+        # Id, Point size of the font, dots per inch value to use for drawing.
+        blf.size(font_id, 30, self.dpi)  # 72
+        blf.draw(font_id, f'{self.new_frame:.0f}')
+
+    # - # Display frame offset text
+    if self.use_hud_frame_offset:
+        blf.position(font_id, self.mouse[0]+10,
+                     self.mouse[1]+(40*self.ui_scale), 0)
+        blf.size(font_id, 16, self.dpi)
+        # blf.color(font_id, *self.color_text)
+        sign = '+' if self.offset > 0 else ''
+        blf.draw(font_id, f'{sign}{self.offset:.0f}')
+
+
+class GPTS_OT_time_scrub(bpy.types.Operator):
+    bl_idname = "animation.time_scrub"
+    bl_label = "Time scrub"
+    bl_description = "Quick time scrubbing with a shortcut"
+    bl_options = {"REGISTER", "INTERNAL", "UNDO"}
+
+    @classmethod
+    def poll(cls, context):
+        return context.space_data.type in ('VIEW_3D', 'SEQUENCE_EDITOR', 'CLIP_EDITOR')
+
+    def invoke(self, context, event):
+        prefs = get_addon_prefs().ts
+        # Gpencil contexts : ('PAINT_GPENCIL', 'EDIT_GPENCIL')
+        # if context.space_data.type != 'VIEW_3D':
+        #     self.report({'WARNING'}, "Work only in Viewport")
+        #     return {'CANCELLED'}
+
+        self.current_area = context.area
+        self.key = prefs.keycode
+        self.evaluate_gp_obj_key = prefs.evaluate_gp_obj_key
+
+        self.dpi = context.preferences.system.dpi
+        self.ui_scale = context.preferences.system.ui_scale
+        # hud prefs
+        self.color_timeline = prefs.color_timeline
+        self.color_playhead = prefs.color_playhead
+        self.color_text = prefs.color_playhead
+        self.use_hud_time_line = prefs.use_hud_time_line
+        self.use_hud_keyframes = prefs.use_hud_keyframes
+        self.keyframe_aspect = prefs.keyframe_aspect
+        self.use_hud_playhead = prefs.use_hud_playhead
+        self.use_hud_frame_current = prefs.use_hud_frame

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list