From 654afac96c1fd5a8831321d1d60d029ddf2b9ab3 Mon Sep 17 00:00:00 2001 From: Luca Rood <dev@lucarood.com> Date: Thu, 19 Mar 2020 13:48:28 +0100 Subject: [PATCH] Add UI Animation Render addon UI Animation Render allows you to capture the Blender UI for each frame of an animation. This enables you to neatly show how the UI and different values are changing during animation. More info, and demo here: https://gitlab.com/LucaRood/screenrender Reviewed By: mont29 Differential Revision: https://developer.blender.org/D7180 --- render_ui_animation_render.py | 223 ++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 render_ui_animation_render.py diff --git a/render_ui_animation_render.py b/render_ui_animation_render.py new file mode 100644 index 000000000..27d84a4a2 --- /dev/null +++ b/render_ui_animation_render.py @@ -0,0 +1,223 @@ +# ##### 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 ##### + +import bpy + +bl_info = { + "name": "UI Animation Render", + "author": "Luca Rood", + "description": "Render animations of the Blender UI.", + "blender": (2, 80, 0), + "version": (0, 1, 0), + "location": "View3D > Sidebar > View Tab and Ctrl+Shift+F12", + "warning": "", + "category": "Render" +} + +km = None + + +def draw_ui(prefs, layout): + layout.prop(prefs, "delay") + + col = layout.column(align=True) + col.label(text="Animation Highlight:") + + row = col.row() + row.prop(prefs, "anim_highlight", expand=True) + + if prefs.anim_highlight == "replace": + split = col.split(factor=0.2) + split.label(text="Color") + row = split.row(align=True) + row.prop(prefs, "highlight_color", text="") + row.prop(prefs, "highlight_blend", text="Blend") + + +class UIAnimationRenderPreferences(bpy.types.AddonPreferences): + bl_idname = __name__ + + delay: bpy.props.FloatProperty( + name="Capture Delay", + description="How much time to wait (seconds) before capturing each frame, to allow the viewport to clean up", + default=0.5 + ) + + anim_highlight: bpy.props.EnumProperty( + name="Animation Highlight", + description="What to do with the animated field highlight color", + items=[("keep", "Keep", "Keep the animated field highlight", 0), + ("hide", "Hide", "Hide the animated field highlight", 1), + ("replace", "Replace", "Replace the animated field highlight", 2)], + default="keep" + ) + + highlight_color: bpy.props.FloatVectorProperty( + name="Highlight Color", + description="Color to use for animated field highlights", + subtype='COLOR', + default=(1.0, 1.0, 1.0) + ) + + highlight_blend: bpy.props.FloatProperty( + name="Highlight Blend", + description="How much the highlight color influences the field color", + default=0.5 + ) + + def draw(self, context): + draw_ui(self, self.layout) + + +class RenderScreen(bpy.types.Operator): + bl_idname = "render.render_screen" + bl_label = "Render Screen" + bl_description = "Capture the screen for each animation frame and write to the render output path" + + _timer = None + _f_initial = 1 + _theme_blend = 0.0 + _theme_key = (0, 0, 0) + _theme_key_sel = (0, 0, 0) + _theme_anim = (0, 0, 0) + _theme_anim_sel = (0, 0, 0) + _theme_driven = (0, 0, 0) + _theme_driven_sel = (0, 0, 0) + + def modal(self, context, event): + if event.type in {'RIGHTMOUSE', 'ESC'}: + self.stop(context) + return {'CANCELLED'} + + if event.type == 'TIMER': + scene = context.scene + f_curr = scene.frame_current + + bpy.ops.screen.screenshot(filepath=context.scene.render.frame_path(frame=f_curr)) + + if f_curr < scene.frame_end: + scene.frame_set(f_curr + 1) + else: + self.stop(context) + return {'FINISHED'} + + return {'RUNNING_MODAL'} + + def execute(self, context): + # Adjust animation highlight (theme) + prefs = context.preferences + addon_prefs = prefs.addons[__name__].preferences + theme = prefs.themes[0].user_interface.wcol_state + + if addon_prefs.anim_highlight == "hide": + self._theme_blend = theme.blend + theme.blend = 0.0 + elif addon_prefs.anim_highlight == "replace": + self._theme_blend = theme.blend + self._theme_key = theme.inner_key.copy() + self._theme_key_sel = theme.inner_key_sel.copy() + self._theme_anim = theme.inner_anim.copy() + self._theme_anim_sel = theme.inner_anim_sel.copy() + self._theme_driven = theme.inner_driven.copy() + self._theme_driven_sel = theme.inner_driven_sel.copy() + + theme.blend = addon_prefs.highlight_blend + theme.inner_key = addon_prefs.highlight_color + theme.inner_key_sel = addon_prefs.highlight_color + theme.inner_anim = addon_prefs.highlight_color + theme.inner_anim_sel = addon_prefs.highlight_color + theme.inner_driven = addon_prefs.highlight_color + theme.inner_driven_sel = addon_prefs.highlight_color + + # Set frame + scene = context.scene + self._f_initial = scene.frame_current + scene.frame_set(scene.frame_start) + + # Start timer + wm = context.window_manager + self._timer = wm.event_timer_add(addon_prefs.delay, window=context.window) + wm.modal_handler_add(self) + return {'RUNNING_MODAL'} + + def stop(self, context): + # Stop timer + wm = context.window_manager + wm.event_timer_remove(self._timer) + + # Reset frame + context.scene.frame_set(self._f_initial) + + # Reset theme + prefs = context.preferences + addon_prefs = prefs.addons[__name__].preferences + theme = prefs.themes[0].user_interface.wcol_state + + if addon_prefs.anim_highlight == "hide": + theme.blend = self._theme_blend + elif addon_prefs.anim_highlight == "replace": + theme.blend = self._theme_blend + theme.inner_key = self._theme_key + theme.inner_key_sel = self._theme_key_sel + theme.inner_anim = self._theme_anim + theme.inner_anim_sel = self._theme_anim_sel + theme.inner_driven = self._theme_driven + theme.inner_driven_sel = self._theme_driven_sel + + +class VIEW3D_PT_ui_animation_render(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "View" + bl_label = "UI Animation Render" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = False + + prefs = context.preferences + addon_prefs = prefs.addons[__name__].preferences + + layout.operator(RenderScreen.bl_idname) + draw_ui(addon_prefs, layout) + + +def register(): + global km + + bpy.utils.register_class(UIAnimationRenderPreferences) + bpy.utils.register_class(RenderScreen) + bpy.utils.register_class(VIEW3D_PT_ui_animation_render) + + wm = bpy.context.window_manager + km = wm.keyconfigs.addon.keymaps.new(name='Screen', space_type='EMPTY') + km.keymap_items.new('render.render_screen', 'F12', 'PRESS', shift=True, ctrl=True) + + +def unregister(): + global km + + bpy.utils.unregister_class(UIAnimationRenderPreferences) + bpy.utils.unregister_class(RenderScreen) + bpy.utils.unregister_class(VIEW3D_PT_ui_animation_render) + + if km is not None: + wm = bpy.context.window_manager + wm.keyconfigs.addon.keymaps.remove(km) + km = None -- GitLab