VSE: Improve retiming UI

Currently retiming is quite awkward, when you need to retime multiple
strips strips in sync. It is possible to use meta strips, but this is
still not great. This is resolved by implementing selection.

General changes:
Gizmos are removed, since they are designed to operate only on active
strip and don't support selection.
Transform operator code is implemented for retiming data, which allows
more sophisticated manipulation.
Instead of drawing marker-like symbols, keyframes are drawn to
represent retiming data. Retiming handles are now called keys. To have
consistent names, DNA structures have been renamed.
Retiming data is drawn on strip as overlay.

UI changes:
Retiming tool is removed. To edit retiming data, press Ctrl + R, select
a key and move it. When retiming is edited, retiming menu and
context menu shows more relevant features, like making transitions.
Strip and retiming key selection can not be combined. It is possible to
use box select operator to select keys, if any key is selected.
Otherwise strips are selected.
Adding retiming keys is possible with I shortcut or from menu.
Retiming keys are always drawn at strip left and right boundary. These
keys do not really exist until they are selected. This is to simplify
retiming of strips that are resized. These keys are called "fake keys"
in code.

API changes:
Functions, properties and types related to retiming handles are renamed
to retiming keys:
retiming_handle_add() -> retiming_key_add()
retiming_handle_move() -> retiming_key_move()
retiming_handle_remove() -> retiming_key_remove()
retiming_handles -> retiming_keys
RetimingHandle -> RetimingKey

Retiming editing "mode" is activated by setting `Sequence.show_retiming_keys`.

Pull Request: https://projects.blender.org/blender/blender/pulls/109044
This commit is contained in:
Richard Antalik 2023-09-27 01:45:59 +02:00 committed by Richard Antalik
parent 46ade1c2df
commit 86a0d0015a
41 changed files with 2399 additions and 1755 deletions

View File

@ -677,6 +677,18 @@ const bTheme U_theme_default = {
.row_alternate = RGBA(0xffffff05),
.anim_preview_range = RGBA(0xa14d0066),
.metadatatext = RGBA(0xffffffff),
.keytype_keyframe = RGBA(0xbfbfbfff),
.keytype_extreme = RGBA(0xe8b3ccff),
.keytype_breakdown = RGBA(0xb3dbe8ff),
.keytype_jitter = RGBA(0x94e575ff),
.keytype_movehold = RGBA(0x808080ff),
.keytype_keyframe_select = RGBA(0xffbe33ff),
.keytype_extreme_select = RGBA(0xf28080ff),
.keytype_breakdown_select = RGBA(0x54bfedff),
.keytype_jitter_select = RGBA(0x61c042ff),
.keytype_movehold_select = RGBA(0xffaf23ff),
.keyborder = RGBA(0x000000ff),
.keyborder_select = RGBA(0x000000ff),
},
.space_image = {
.back = RGBA(0x30303000),

View File

@ -2909,7 +2909,7 @@ def km_sequencercommon(params):
{"properties": [("data_path", 'scene.sequence_editor.show_overlay_frame')]}),
("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}),
("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("sequencer.refresh_all", {"type": 'E', "value": 'PRESS', "ctrl": True}, None),
])
if params.select_mouse == 'LEFTMOUSE' and not params.legacy:
@ -3035,6 +3035,9 @@ def km_sequencer(params):
("wm.context_toggle", {"type": 'Z', "value": 'PRESS', "alt": True, "shift": True},
{"properties": [("data_path", "space_data.show_overlays")]}),
*_template_items_context_menu("SEQUENCER_MT_context_menu", params.context_menu_event),
op_menu("SEQUENCER_MT_retiming", {"type": 'I', "value": 'PRESS'}),
("sequencer.retiming_segment_speed_set", {"type": 'R', "value": 'PRESS'}, None),
("sequencer.retiming_show", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
])
return keymap

View File

@ -316,6 +316,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel):
layout.prop(overlay_settings, "show_strip_offset", text="Offsets")
layout.prop(overlay_settings, "show_fcurves", text="F-Curves")
layout.prop(overlay_settings, "show_strip_retiming", text="Retiming")
layout.prop(overlay_settings, "show_thumbnails", text="Thumbnails")
layout.prop(overlay_settings, "show_grid", text="Grid")
@ -927,6 +928,53 @@ class SEQUENCER_MT_strip_movie(Menu):
layout.operator("sequencer.deinterlace_selected_movies")
class SEQUENCER_MT_strip_retiming(Menu):
bl_label = "Retiming"
def draw_strip_context(self, context):
layout = self.layout
layout.operator("sequencer.retiming_key_add")
layout.operator("sequencer.retiming_freeze_frame_add")
layout.separator()
layout.operator("sequencer.retiming_reset")
layout.separator()
icon = "CHECKBOX_DEHLT"
if context.active_sequence_strip.show_retiming_keys:
icon = "CHECKBOX_HLT"
layout.operator("sequencer.retiming_segment_speed_set")
layout.operator("sequencer.retiming_show", icon=icon)
def draw_retiming_context(self, context):
layout = self.layout
layout.operator("sequencer.retiming_key_add")
layout.operator("sequencer.retiming_freeze_frame_add")
layout.operator("sequencer.retiming_transition_add")
layout.separator()
layout.operator("sequencer.delete")
layout.separator()
layout.operator("sequencer.select_box")
layout.operator("sequencer.select_all")
layout.separator()
layout.operator("sequencer.retiming_segment_speed_set")
layout.operator("sequencer.retiming_show", icon="CHECKBOX_HLT")
def draw(self, context):
ed = context.scene.sequence_editor
if ed.selected_retiming_keys:
self.draw_retiming_context(context)
else:
self.draw_strip_context(context)
class SEQUENCER_MT_strip(Menu):
bl_label = "Strip"
@ -936,9 +984,10 @@ class SEQUENCER_MT_strip(Menu):
has_sequencer, _has_preview = _space_view_types(st)
layout.menu("SEQUENCER_MT_strip_transform")
layout.separator()
if has_sequencer:
layout.menu("SEQUENCER_MT_strip_retiming")
layout.separator()
props = layout.operator("sequencer.split", text="Split")
props.type = 'SOFT'
@ -950,11 +999,11 @@ class SEQUENCER_MT_strip(Menu):
layout.separator()
if has_sequencer:
layout.operator("sequencer.copy", text="Copy")
layout.operator("sequencer.paste", text="Paste")
layout.operator("sequencer.duplicate_move")
layout.separator()
layout.operator("sequencer.delete", text="Delete")
strip = context.active_sequence_strip
@ -1059,10 +1108,23 @@ class SEQUENCER_MT_image_apply(Menu):
layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH'
class SEQUENCER_MT_retiming(Menu):
bl_label = "Retiming"
bl_translation_context = i18n_contexts.operator_default
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("sequencer.retiming_key_add")
layout.operator("sequencer.retiming_freeze_frame_add")
class SEQUENCER_MT_context_menu(Menu):
bl_label = "Sequencer"
def draw(self, context):
def draw_generic(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
@ -1160,6 +1222,28 @@ class SEQUENCER_MT_context_menu(Menu):
layout.menu("SEQUENCER_MT_strip_lock_mute")
def draw_retime(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
if context.scene.sequence_editor.selected_retiming_keys:
layout.operator("sequencer.retiming_freeze_frame_add")
layout.operator("sequencer.retiming_transition_add")
layout.separator()
layout.operator("sequencer.retiming_segment_speed_set")
layout.separator()
layout.operator("sequencer.retiming_key_remove")
def draw(self, context):
ed = context.scene.sequence_editor
if ed.selected_retiming_keys:
self.draw_retime(context)
else:
self.draw_generic(context)
class SEQUENCER_MT_preview_context_menu(Menu):
bl_label = "Sequencer Preview"
@ -2711,6 +2795,7 @@ classes = (
SEQUENCER_MT_strip_movie,
SEQUENCER_MT_strip,
SEQUENCER_MT_strip_transform,
SEQUENCER_MT_strip_retiming,
SEQUENCER_MT_strip_input,
SEQUENCER_MT_strip_lock_mute,
SEQUENCER_MT_image,
@ -2721,6 +2806,7 @@ classes = (
SEQUENCER_MT_context_menu,
SEQUENCER_MT_preview_context_menu,
SEQUENCER_MT_pivot_pie,
SEQUENCER_MT_retiming,
SEQUENCER_MT_view_pie,
SEQUENCER_MT_preview_view_pie,

View File

@ -2562,18 +2562,6 @@ class _defs_sequencer_generic:
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def retime():
return dict(
idname="builtin.retime",
label="Retime",
icon="ops.sequencer.retime",
widget="SEQUENCER_GGT_gizmo_retime",
operator=None,
keymap=None,
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def sample():
return dict(
@ -3264,7 +3252,6 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
'SEQUENCER': [
*_tools_select,
_defs_sequencer_generic.blade,
_defs_sequencer_generic.retime,
],
'SEQUENCER_PREVIEW': [
*_tools_select,

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 26
#define BLENDER_FILE_SUBVERSION 27
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -90,7 +90,7 @@
#include "SEQ_channels.h"
#include "SEQ_effects.h"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@ -680,10 +680,10 @@ static bool do_versions_sequencer_init_retiming_tool_data(Sequence *seq, void *u
const int content_length = SEQ_time_strip_length_get(scene, seq);
SEQ_retiming_data_ensure(scene, seq);
SEQ_retiming_data_ensure(seq);
SeqRetimingHandle *handle = &seq->retiming_handles[seq->retiming_handle_num - 1];
handle->strip_frame_index = round_fl_to_int(content_length / seq->speed_factor);
SeqRetimingKey *key = &seq->retiming_keys[seq->retiming_keys_num - 1];
key->strip_frame_index = round_fl_to_int(content_length / seq->speed_factor);
seq->speed_factor = 1.0f;
return true;

View File

@ -51,6 +51,9 @@
#include "BKE_scene.h"
#include "BKE_tracking.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "ANIM_armature_iter.hh"
#include "ANIM_bone_collections.h"
@ -1481,18 +1484,17 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
/**
* Versioning code until next subversion bump goes here.
*
* \note Be sure to check when bumping the version:
* - #do_versions_after_linking_400 in this file.
* - `versioning_userdef.cc`, #blo_do_versions_userdef
* - `versioning_userdef.cc`, #do_versions_theme
*
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 27)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
sseq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_RETIMING;
}
}
}
}
if (!DNA_struct_member_exists(fd->filesdna, "SceneEEVEE", "float", "shadow_normal_bias")) {
SceneEEVEE default_scene_eevee = *DNA_struct_default_get(SceneEEVEE);
@ -1518,4 +1520,18 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
}
/**
* Versioning code until next subversion bump goes here.
*
* \note Be sure to check when bumping the version:
* - #do_versions_after_linking_400 in this file.
* - `versioning_userdef.cc`, #blo_do_versions_userdef
* - `versioning_userdef.cc`, #do_versions_theme
*
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
}
}

View File

@ -168,7 +168,8 @@ static void blo_update_defaults_screen(bScreen *screen,
seq->render_size = SEQ_RENDER_SIZE_PROXY_100;
seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME |
SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID |
SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG;
SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG |
SEQ_TIMELINE_SHOW_STRIP_RETIMING;
seq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED;
}
else if (area->spacetype == SPACE_TEXT) {

View File

@ -113,8 +113,20 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
}
if (!USER_VERSION_ATLEAST(400, 24)) {
FROM_DEFAULT_V4_UCHAR(space_sequencer.transition);
FROM_DEFAULT_V4_UCHAR(tui.wcol_list_item.inner_sel);
FROM_DEFAULT_V4_UCHAR(space_sequencer.transition);
}
if (!USER_VERSION_ATLEAST(400, 27)) {
FROM_DEFAULT_V4_UCHAR(space_sequencer.keytype_keyframe);
FROM_DEFAULT_V4_UCHAR(space_sequencer.keytype_breakdown);
FROM_DEFAULT_V4_UCHAR(space_sequencer.keytype_movehold);
FROM_DEFAULT_V4_UCHAR(space_sequencer.keytype_keyframe_select);
FROM_DEFAULT_V4_UCHAR(space_sequencer.keytype_breakdown_select);
FROM_DEFAULT_V4_UCHAR(space_sequencer.keytype_movehold_select);
FROM_DEFAULT_V4_UCHAR(space_sequencer.keyborder);
FROM_DEFAULT_V4_UCHAR(space_sequencer.keyborder_select);
FROM_DEFAULT_V4_UCHAR(space_sequencer.transition);
}
/**

View File

@ -45,3 +45,4 @@ void ED_operatormacros_sequencer();
Sequence *ED_sequencer_special_preview_get();
void ED_sequencer_special_preview_set(bContext *C, const int mval[2]);
void ED_sequencer_special_preview_clear();
bool sequencer_retiming_mode_is_active(const struct bContext *C);

View File

@ -30,14 +30,13 @@ set(SRC
sequencer_channels_edit.cc
sequencer_drag_drop.cc
sequencer_edit.cc
sequencer_gizmo_retime.cc
sequencer_gizmo_retime_type.cc
sequencer_modifier.cc
sequencer_ops.cc
sequencer_preview.cc
sequencer_preview_draw.cc
sequencer_proxy.cc
sequencer_retiming.cc
sequencer_retiming_draw.cc
sequencer_scopes.cc
sequencer_select.cc
sequencer_thumbnails.cc

View File

@ -1708,6 +1708,10 @@ static int sequencer_delete_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (sequencer_retiming_mode_is_active(C)) {
sequencer_retiming_key_remove_exec(C, op);
}
SEQ_prefetch_stop(scene);
SeqCollection *selected_strips = selected_strips_from_context(C);

View File

@ -1,112 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "BLI_span.hh"
#include "RNA_access.hh"
#include "UI_resources.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_gizmo_library.hh"
#include "ED_gizmo_utils.hh"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
/* Own include. */
#include "sequencer_intern.hh"
struct GizmoGroup_retime {
wmGizmo *add_handle_gizmo;
wmGizmo *move_handle_gizmo;
wmGizmo *remove_handle_gizmo;
wmGizmo *speed_set_gizmo;
};
static bool gizmogroup_retime_poll(const bContext *C, wmGizmoGroupType *gzgt)
{
/* Needed to prevent drawing gizmos when retiming tool is not activated. */
if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
return false;
}
if ((U.gizmo_flag & USER_GIZMO_DRAW) == 0) {
return false;
}
ScrArea *area = CTX_wm_area(C);
if (area == nullptr && area->spacetype != SPACE_SEQ) {
return false;
}
const SpaceSeq *sseq = (SpaceSeq *)area->spacedata.first;
if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_TOOL)) {
return false;
}
Editing *ed = SEQ_editing_get(CTX_data_scene(C));
Sequence *seq = ed->act_seq;
if (ed == nullptr || seq == nullptr || !SEQ_retiming_is_allowed(seq)) {
return false;
}
return true;
}
static void gizmogroup_retime_setup(const bContext * /*C*/, wmGizmoGroup *gzgroup)
{
GizmoGroup_retime *ggd = (GizmoGroup_retime *)MEM_callocN(sizeof(GizmoGroup_retime), __func__);
/* Assign gizmos. */
const wmGizmoType *gzt_add_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_add", true);
ggd->add_handle_gizmo = WM_gizmo_new_ptr(gzt_add_handle, gzgroup, nullptr);
const wmGizmoType *gzt_remove_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_remove", true);
ggd->remove_handle_gizmo = WM_gizmo_new_ptr(gzt_remove_handle, gzgroup, nullptr);
const wmGizmoType *gzt_move_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_move", true);
ggd->move_handle_gizmo = WM_gizmo_new_ptr(gzt_move_handle, gzgroup, nullptr);
const wmGizmoType *gzt_speed_set = WM_gizmotype_find("GIZMO_GT_retime_speed_set", true);
ggd->speed_set_gizmo = WM_gizmo_new_ptr(gzt_speed_set, gzgroup, nullptr);
gzgroup->customdata = ggd;
/* Assign operators. */
wmOperatorType *ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_move", true);
WM_gizmo_operator_set(ggd->move_handle_gizmo, 0, ot, nullptr);
ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_add", true);
WM_gizmo_operator_set(ggd->add_handle_gizmo, 0, ot, nullptr);
ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_remove", true);
WM_gizmo_operator_set(ggd->remove_handle_gizmo, 0, ot, nullptr);
ot = WM_operatortype_find("SEQUENCER_OT_retiming_segment_speed_set", true);
WM_gizmo_operator_set(ggd->speed_set_gizmo, 0, ot, nullptr);
}
void SEQUENCER_GGT_gizmo_retime(wmGizmoGroupType *gzgt)
{
gzgt->name = "Sequencer Transform Gizmo Retime";
gzgt->idname = "SEQUENCER_GGT_gizmo_retime";
gzgt->flag = WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL;
gzgt->gzmap_params.spaceid = SPACE_SEQ;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
gzgt->poll = gizmogroup_retime_poll;
gzgt->setup = gizmogroup_retime_setup;
}

View File

@ -1,772 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_span.hh"
#include "DNA_anim_types.h"
#include "DNA_sequence_types.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_scene.h"
#include "BLF_api.h"
#include "GPU_batch.h"
#include "GPU_batch_utils.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_select.h"
#include "GPU_state.h"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_gizmo_library.hh"
#include "ED_screen.hh"
#include "ED_view3d.hh"
#include "UI_interface.hh"
#include "UI_interface_icons.hh"
#include "UI_resources.hh"
#include "UI_view2d.hh"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
/* Own include. */
#include "sequencer_intern.hh"
using blender::MutableSpan;
/** Size in pixels. */
#define RETIME_HANDLE_MOUSEOVER_THRESHOLD (16.0f * UI_SCALE_FAC)
/** Factor based on icon size. */
#define RETIME_BUTTON_SIZE 0.6f
static float remove_gizmo_height_get(const View2D *v2d)
{
const float max_size = (SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM) * UI_view2d_scale_get_y(v2d);
return min_ff(14.0f * UI_SCALE_FAC, max_size * 0.4f);
}
static float strip_y_rescale(const Sequence *seq, const float y_value)
{
const float y_range = SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM;
return (y_value * y_range) + seq->machine + SEQ_STRIP_OFSBOTTOM;
}
static float handle_x_get(const Scene *scene, const Sequence *seq, const SeqRetimingHandle *handle)
{
const SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
const bool is_last_handle = (handle == last_handle);
return SEQ_retiming_handle_timeline_frame_get(scene, seq, handle) + (is_last_handle ? 1 : 0);
}
static const SeqRetimingHandle *mouse_over_handle_get(const Scene *scene,
const Sequence *seq,
const View2D *v2d,
const int mval[2])
{
int best_distance = INT_MAX;
const SeqRetimingHandle *best_handle = nullptr;
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (const SeqRetimingHandle &handle : handles) {
int distance = round_fl_to_int(
fabsf(UI_view2d_view_to_region_x(v2d, handle_x_get(scene, seq, &handle)) - mval[0]));
if (distance < RETIME_HANDLE_MOUSEOVER_THRESHOLD && distance < best_distance) {
best_distance = distance;
best_handle = &handle;
}
}
return best_handle;
}
static float pixels_to_view_width(const bContext *C, const float width)
{
const View2D *v2d = UI_view2d_fromcontext(C);
float scale_x = UI_view2d_view_to_region_x(v2d, 1) - UI_view2d_view_to_region_x(v2d, 0.0f);
return width / scale_x;
}
static float pixels_to_view_height(const bContext *C, const float height)
{
const View2D *v2d = UI_view2d_fromcontext(C);
float scale_y = UI_view2d_view_to_region_y(v2d, 1) - UI_view2d_view_to_region_y(v2d, 0.0f);
return height / scale_y;
}
static float strip_start_screenspace_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_left_handle_frame_get(scene, seq));
}
static float strip_end_screenspace_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_right_handle_frame_get(scene, seq));
}
static Sequence *active_seq_from_context(const bContext *C)
{
const Editing *ed = SEQ_editing_get(CTX_data_scene(C));
return ed->act_seq;
}
static rctf strip_box_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect;
rect.xmin = strip_start_screenspace_get(C, seq);
rect.xmax = strip_end_screenspace_get(C, seq);
rect.ymin = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0));
rect.ymax = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1));
return rect;
}
static rctf remove_box_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect = strip_box_get(C, seq);
rect.ymax = rect.ymin + remove_gizmo_height_get(v2d);
return rect;
}
static bool mouse_is_inside_box(const rctf *box, const int mval[2])
{
return mval[0] >= box->xmin && mval[0] <= box->xmax && mval[1] >= box->ymin &&
mval[1] <= box->ymax;
}
/* -------------------------------------------------------------------- */
/** \name Retiming Add Handle Gizmo
* \{ */
struct RetimeButtonGizmo {
wmGizmo gizmo;
int icon_id;
const Sequence *seq_under_mouse;
bool is_mouse_over_gizmo;
};
struct ButtonDimensions {
float height;
float width;
float x;
float y;
};
static ButtonDimensions button_dimensions_get(const bContext *C, const RetimeButtonGizmo *gizmo)
{
const Scene *scene = CTX_data_scene(C);
const View2D *v2d = UI_view2d_fromcontext(C);
const Sequence *seq = active_seq_from_context(C);
const float icon_height = UI_icon_get_height(gizmo->icon_id) * UI_SCALE_FAC;
const float icon_width = UI_icon_get_width(gizmo->icon_id) * UI_SCALE_FAC;
const float icon_x = UI_view2d_view_to_region_x(v2d, BKE_scene_frame_get(scene)) +
icon_width / 2;
const float icon_y = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.5)) -
icon_height / 2;
const ButtonDimensions dimensions = {icon_height, icon_width, icon_x, icon_y};
return dimensions;
}
static rctf button_box_get(const bContext *C, const RetimeButtonGizmo *gizmo)
{
ButtonDimensions button_dimensions = button_dimensions_get(C, gizmo);
float x_range = button_dimensions.width;
float y_range = button_dimensions.height;
rctf rect;
rect.xmin = button_dimensions.x;
rect.xmax = button_dimensions.x + x_range;
rect.ymin = button_dimensions.y;
rect.ymax = button_dimensions.y + y_range;
return rect;
}
static void gizmo_retime_handle_add_draw(const bContext *C, wmGizmo *gz)
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
return;
}
const Scene *scene = CTX_data_scene(C);
const Sequence *seq = active_seq_from_context(C);
const int frame_index = BKE_scene_frame_get(scene) - SEQ_time_start_frame_get(seq);
const SeqRetimingHandle *handle = SEQ_retiming_find_segment_start_handle(seq, frame_index);
if (handle != nullptr && (SEQ_retiming_handle_is_transition_type(handle) ||
SEQ_retiming_handle_is_freeze_frame(handle)))
{
return;
}
const ButtonDimensions button = button_dimensions_get(C, gizmo);
const rctf strip_box = strip_box_get(C, active_seq_from_context(C));
if (!BLI_rctf_isect_pt(&strip_box, button.x, button.y)) {
return;
}
wmOrtho2_region_pixelspace(CTX_wm_region(C));
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
const float alpha = gizmo->is_mouse_over_gizmo ? 1.0f : 0.6f;
immUniformColor4f(0.2f, 0.2f, 0.2f, alpha);
imm_draw_circle_fill_2d(pos,
button.x + button.width / 2,
button.y + button.height / 2,
button.width * RETIME_BUTTON_SIZE,
32);
immUnbindProgram();
GPU_polygon_smooth(false);
UI_icon_draw_alpha(button.x, button.y, gizmo->icon_id, alpha);
GPU_polygon_smooth(true);
GPU_blend(GPU_BLEND_NONE);
}
static int gizmo_retime_handle_add_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
Sequence *seq = active_seq_from_context(C);
Sequence *mouse_over_seq = nullptr;
gizmo->is_mouse_over_gizmo = false;
/* Store strip under mouse cursor. */
const rctf strip_box = strip_box_get(C, seq);
if (mouse_is_inside_box(&strip_box, mval)) {
mouse_over_seq = seq;
}
if (gizmo->seq_under_mouse != mouse_over_seq) {
gizmo->seq_under_mouse = mouse_over_seq;
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, CTX_data_scene(C));
}
if (gizmo->seq_under_mouse == nullptr) {
return -1;
}
const rctf button_box = button_box_get(C, gizmo);
if (!mouse_is_inside_box(&button_box, mval)) {
return -1;
}
gizmo->is_mouse_over_gizmo = true;
const Scene *scene = CTX_data_scene(C);
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "timeline_frame", BKE_scene_frame_get(scene));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, CTX_data_scene(C));
return 0;
}
static void gizmo_retime_handle_add_setup(wmGizmo *gz)
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
gizmo->icon_id = ICON_ADD;
}
void GIZMO_GT_retime_handle_add(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_add";
/* Api callbacks. */
gzt->setup = gizmo_retime_handle_add_setup;
gzt->draw = gizmo_retime_handle_add_draw;
gzt->test_select = gizmo_retime_handle_add_test_select;
gzt->struct_size = sizeof(RetimeButtonGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Move Handle Gizmo
* \{ */
enum eHandleMoveOperation {
DEFAULT_MOVE,
MAKE_TRANSITION,
MAKE_FREEZE_FRAME,
};
struct RetimeHandleMoveGizmo {
wmGizmo gizmo;
const Sequence *mouse_over_seq;
int mouse_over_handle_x;
eHandleMoveOperation operation;
};
static void retime_handle_draw(const bContext *C,
const RetimeHandleMoveGizmo *gizmo,
uint pos,
const Sequence *seq,
const SeqRetimingHandle *handle)
{
const Scene *scene = CTX_data_scene(C);
const float handle_x = handle_x_get(scene, seq, handle);
if (handle_x == SEQ_time_left_handle_frame_get(scene, seq)) {
return;
}
if (handle_x == SEQ_time_right_handle_frame_get(scene, seq)) {
return;
}
const View2D *v2d = UI_view2d_fromcontext(C);
const rctf strip_box = strip_box_get(C, seq);
if (!BLI_rctf_isect_x(&strip_box, UI_view2d_view_to_region_x(v2d, handle_x))) {
return; /* Handle out of strip bounds. */
}
const int ui_triangle_size = remove_gizmo_height_get(v2d);
const float bottom = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.0f)) + 2;
const float top = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1.0f)) - 2;
const float handle_position = UI_view2d_view_to_region_x(v2d, handle_x);
float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
if (seq == gizmo->mouse_over_seq && handle_x == gizmo->mouse_over_handle_x) {
bool handle_is_transition = SEQ_retiming_handle_is_transition_type(handle);
bool prev_handle_is_transition = SEQ_retiming_handle_is_transition_type(handle - 1);
bool handle_is_freeze_frame = SEQ_retiming_handle_is_freeze_frame(handle);
bool prev_handle_is_freeze_frame = SEQ_retiming_handle_is_freeze_frame(handle - 1);
if (!(handle_is_transition || prev_handle_is_transition || handle_is_freeze_frame ||
prev_handle_is_freeze_frame))
{
if (gizmo->operation == MAKE_TRANSITION) {
col[0] = 0.5f;
col[2] = 0.4f;
}
else if (gizmo->operation == MAKE_FREEZE_FRAME) {
col[0] = 0.4f;
col[1] = 0.8f;
}
}
}
else {
mul_v3_fl(col, 0.65f);
}
immUniformColor4fv(col);
immBegin(GPU_PRIM_TRI_FAN, 3);
immVertex2f(pos, handle_position - ui_triangle_size / 2, bottom);
immVertex2f(pos, handle_position + ui_triangle_size / 2, bottom);
immVertex2f(pos, handle_position, bottom + ui_triangle_size);
immEnd();
immBegin(GPU_PRIM_LINES, 2);
immVertex2f(pos, handle_position, bottom);
immVertex2f(pos, handle_position, top);
immEnd();
}
static void gizmo_retime_handle_draw(const bContext *C, wmGizmo *gz)
{
RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz;
const View2D *v2d = UI_view2d_fromcontext(C);
/* TODO: This is hard-coded behavior, same as pre-select gizmos in 3D view.
* Better solution would be to check operator keymap and display this information in status bar
* and tool-tip. */
wmEvent *event = CTX_wm_window(C)->eventstate;
if ((event->modifier & KM_SHIFT) != 0) {
gizmo->operation = MAKE_TRANSITION;
}
else if ((event->modifier & KM_CTRL) != 0) {
gizmo->operation = MAKE_FREEZE_FRAME;
}
else {
gizmo->operation = DEFAULT_MOVE;
}
wmOrtho2_region_pixelspace(CTX_wm_region(C));
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(CTX_data_scene(C), seq);
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (const SeqRetimingHandle &handle : handles) {
if (&handle == handles.begin()) {
continue; /* Ignore first handle. */
}
retime_handle_draw(C, gizmo, pos, seq, &handle);
}
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
UI_view2d_text_cache_draw(CTX_wm_region(C));
UI_view2d_view_ortho(v2d); /* 'UI_view2d_text_cache_draw()' messes up current view. */
}
static int gizmo_retime_handle_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz;
Scene *scene = CTX_data_scene(C);
gizmo->mouse_over_seq = nullptr;
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(scene, seq);
const SeqRetimingHandle *handle = mouse_over_handle_get(
scene, seq, UI_view2d_fromcontext(C), mval);
const int handle_index = SEQ_retiming_handle_index_get(seq, handle);
if (handle == nullptr) {
return -1;
}
if (handle_x_get(scene, seq, handle) == SEQ_time_left_handle_frame_get(scene, seq) ||
handle_index == 0)
{
return -1;
}
const View2D *v2d = UI_view2d_fromcontext(C);
rctf strip_box = strip_box_get(C, seq);
BLI_rctf_resize_x(&strip_box, BLI_rctf_size_x(&strip_box) + 2 * remove_gizmo_height_get(v2d));
if (!mouse_is_inside_box(&strip_box, mval)) {
return -1;
}
gizmo->mouse_over_seq = seq;
gizmo->mouse_over_handle_x = handle_x_get(scene, seq, handle);
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "handle_index", handle_index);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return 0;
}
static int gizmo_retime_handle_cursor_get(wmGizmo *gz)
{
if (RNA_boolean_get(gz->ptr, "show_drag")) {
return WM_CURSOR_EW_SCROLL;
}
return WM_CURSOR_DEFAULT;
}
static void gizmo_retime_handle_setup(wmGizmo *gz)
{
gz->flag = WM_GIZMO_DRAW_MODAL;
}
void GIZMO_GT_retime_handle(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_move";
/* Api callbacks. */
gzt->setup = gizmo_retime_handle_setup;
gzt->draw = gizmo_retime_handle_draw;
gzt->test_select = gizmo_retime_handle_test_select;
gzt->cursor_get = gizmo_retime_handle_cursor_get;
gzt->struct_size = sizeof(RetimeHandleMoveGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Remove Handle Gizmo
* \{ */
static void gizmo_retime_remove_draw(const bContext * /*C*/, wmGizmo * /*gz*/)
{
/* Pass. */
}
static int gizmo_retime_remove_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
Scene *scene = CTX_data_scene(C);
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(scene, seq);
const SeqRetimingHandle *handle = mouse_over_handle_get(
scene, seq, UI_view2d_fromcontext(C), mval);
const int handle_index = SEQ_retiming_handle_index_get(seq, handle);
if (handle == nullptr) {
return -1;
}
if (handle_x_get(scene, seq, handle) == SEQ_time_left_handle_frame_get(scene, seq) ||
handle_index == 0)
{
return -1; /* Ignore first handle. */
}
SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
if (handle == last_handle) {
return -1; /* Last handle can not be removed. */
}
const View2D *v2d = UI_view2d_fromcontext(C);
rctf box = remove_box_get(C, seq);
BLI_rctf_resize_x(&box, BLI_rctf_size_x(&box) + 2 * remove_gizmo_height_get(v2d));
if (!mouse_is_inside_box(&box, mval)) {
return -1;
}
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "handle_index", handle_index);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return 0;
}
static int gizmo_retime_remove_cursor_get(wmGizmo *gz)
{
if (RNA_boolean_get(gz->ptr, "show_drag")) {
return WM_CURSOR_ERASER;
}
return WM_CURSOR_DEFAULT;
}
void GIZMO_GT_retime_remove(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_remove";
/* Api callbacks. */
gzt->draw = gizmo_retime_remove_draw;
gzt->test_select = gizmo_retime_remove_test_select;
gzt->cursor_get = gizmo_retime_remove_cursor_get;
gzt->struct_size = sizeof(wmGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Speed Set Gizmo
* \{ */
static size_t label_str_get(const Sequence *seq,
const SeqRetimingHandle *handle,
char *r_label_str,
const size_t label_str_maxncpy)
{
const SeqRetimingHandle *next_handle = handle + 1;
if (SEQ_retiming_handle_is_transition_type(handle)) {
const float prev_speed = SEQ_retiming_handle_speed_get(seq, handle - 1);
const float next_speed = SEQ_retiming_handle_speed_get(seq, next_handle + 1);
return BLI_snprintf_rlen(r_label_str,
label_str_maxncpy,
"%d%% - %d%%",
round_fl_to_int(prev_speed * 100.0f),
round_fl_to_int(next_speed * 100.0f));
}
const float speed = SEQ_retiming_handle_speed_get(seq, next_handle);
return BLI_snprintf_rlen(
r_label_str, label_str_maxncpy, "%d%%", round_fl_to_int(speed * 100.0f));
}
static bool label_rect_get(const bContext *C,
const Sequence *seq,
const SeqRetimingHandle *handle,
char *label_str,
size_t label_len,
rctf *rect)
{
const Scene *scene = CTX_data_scene(C);
const SeqRetimingHandle *next_handle = handle + 1;
const float width = pixels_to_view_width(C, BLF_width(BLF_default(), label_str, label_len));
const float height = pixels_to_view_height(C, BLF_height(BLF_default(), label_str, label_len));
const float xmin = max_ff(SEQ_time_left_handle_frame_get(scene, seq),
handle_x_get(scene, seq, handle));
const float xmax = min_ff(SEQ_time_right_handle_frame_get(scene, seq),
handle_x_get(scene, seq, next_handle));
rect->xmin = (xmin + xmax - width) / 2;
rect->xmax = rect->xmin + width;
rect->ymin = strip_y_rescale(seq, 0) + pixels_to_view_height(C, 5);
rect->ymax = rect->ymin + height;
return width < xmax - xmin;
}
static void label_rect_apply_mouseover_offset(const View2D *v2d, rctf *rect)
{
float scale_x, scale_y;
UI_view2d_scale_get_inverse(v2d, &scale_x, &scale_y);
rect->xmin -= RETIME_HANDLE_MOUSEOVER_THRESHOLD * scale_x;
rect->xmax += RETIME_HANDLE_MOUSEOVER_THRESHOLD * scale_x;
rect->ymax += RETIME_HANDLE_MOUSEOVER_THRESHOLD * scale_y;
}
static void retime_speed_text_draw(const bContext *C,
const Sequence *seq,
const SeqRetimingHandle *handle)
{
SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
if (handle == last_handle) {
return;
}
const Scene *scene = CTX_data_scene(C);
const int start_frame = SEQ_time_left_handle_frame_get(scene, seq);
const int end_frame = SEQ_time_right_handle_frame_get(scene, seq);
const SeqRetimingHandle *next_handle = handle + 1;
if (handle_x_get(scene, seq, next_handle) < start_frame ||
handle_x_get(scene, seq, handle) > end_frame)
{
return; /* Label out of strip bounds. */
}
char label_str[40];
rctf label_rect;
size_t label_len = label_str_get(seq, handle, label_str, sizeof(label_str));
if (!label_rect_get(C, seq, handle, label_str, label_len, &label_rect)) {
return; /* Not enough space to draw label. */
}
const uchar col[4] = {255, 255, 255, 255};
UI_view2d_text_cache_add(
UI_view2d_fromcontext(C), label_rect.xmin, label_rect.ymin, label_str, label_len, col);
}
static void gizmo_retime_speed_set_draw(const bContext *C, wmGizmo * /*gz*/)
{
const View2D *v2d = UI_view2d_fromcontext(C);
wmOrtho2_region_pixelspace(CTX_wm_region(C));
GPU_blend(GPU_BLEND_ALPHA);
GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(CTX_data_scene(C), seq);
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (const SeqRetimingHandle &handle : handles) {
retime_speed_text_draw(C, seq, &handle);
}
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
UI_view2d_text_cache_draw(CTX_wm_region(C));
UI_view2d_view_ortho(v2d); /* 'UI_view2d_text_cache_draw()' messes up current view. */
}
static int gizmo_retime_speed_set_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
Scene *scene = CTX_data_scene(C);
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
const View2D *v2d = UI_view2d_fromcontext(C);
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(scene, seq);
for (const SeqRetimingHandle &handle : SEQ_retiming_handles_get(seq)) {
if (SEQ_retiming_handle_is_transition_type(&handle)) {
continue;
}
char label_str[40];
rctf label_rect;
size_t label_len = label_str_get(seq, &handle, label_str, sizeof(label_str));
if (!label_rect_get(C, seq, &handle, label_str, label_len, &label_rect)) {
continue;
}
label_rect_apply_mouseover_offset(v2d, &label_rect);
float mouse_view[2];
UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouse_view[0], &mouse_view[1]);
if (!BLI_rctf_isect_pt(&label_rect, mouse_view[0], mouse_view[1])) {
continue;
}
/* Store next handle in RNA property, since label rect uses first handle as reference. */
const int handle_index = SEQ_retiming_handle_index_get(seq, &handle) + 1;
RNA_int_set(&op_elem->ptr, "handle_index", handle_index);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return 0;
}
return -1;
}
static int gizmo_retime_speed_set_cursor_get(wmGizmo *gz)
{
if (RNA_boolean_get(gz->ptr, "show_drag")) {
return WM_CURSOR_TEXT_EDIT;
}
return WM_CURSOR_DEFAULT;
}
void GIZMO_GT_speed_set_remove(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_speed_set";
/* Api callbacks. */
gzt->draw = gizmo_retime_speed_set_draw;
gzt->test_select = gizmo_retime_speed_set_test_select;
gzt->cursor_get = gizmo_retime_speed_set_cursor_get;
gzt->struct_size = sizeof(wmGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */

View File

@ -8,6 +8,7 @@
#pragma once
#include "BLI_vector.hh"
#include "DNA_sequence_types.h"
#include "RNA_access.hh"
@ -21,6 +22,7 @@ struct wmGizmoType;
struct Main;
struct Scene;
struct SeqCollection;
struct SeqRetimingKey;
struct Sequence;
struct SpaceSeq;
struct StripElem;
@ -123,7 +125,6 @@ void channel_draw_context_init(const bContext *C,
/* `sequencer_edit.cc` */
void seq_rectf(const Scene *scene, Sequence *seq, rctf *rectf);
Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[2]);
Sequence *find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int sel);
void recurs_sel_seq(Sequence *seq_meta);
int seq_effect_find_selected(Scene *scene,
@ -233,6 +234,7 @@ void SEQUENCER_OT_select_side(wmOperatorType *ot);
void SEQUENCER_OT_select_box(wmOperatorType *ot);
void SEQUENCER_OT_select_inverse(wmOperatorType *ot);
void SEQUENCER_OT_select_grouped(wmOperatorType *ot);
Sequence *find_nearest_seq(const Scene *scene, const View2D *v2d, int *hand, const int mval[2]);
/* `sequencer_add.cc` */
@ -303,16 +305,25 @@ void sequencer_image_seq_reserve_frames(
/* `sequencer_retiming.cc` */
void SEQUENCER_OT_retiming_reset(wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_move(wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_add(wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_remove(wmOperatorType *ot);
void SEQUENCER_OT_retiming_show(wmOperatorType *ot);
void SEQUENCER_OT_retiming_key_add(wmOperatorType *ot);
void SEQUENCER_OT_retiming_freeze_frame_add(wmOperatorType *ot);
void SEQUENCER_OT_retiming_transition_add(wmOperatorType *ot);
void SEQUENCER_OT_retiming_segment_speed_set(wmOperatorType *ot);
int sequencer_retiming_key_select_exec(struct bContext *C, struct wmOperator *op);
int sequencer_select_exec(struct bContext *C, struct wmOperator *op);
int sequencer_retiming_key_remove_exec(struct bContext *C, struct wmOperator *op);
int sequencer_retiming_select_all_exec(struct bContext *C, struct wmOperator *op);
int sequencer_retiming_box_select_exec(struct bContext *C, struct wmOperator *op);
/* `sequencer_gizmo_retime.cc` */
void SEQUENCER_GGT_gizmo_retime(wmGizmoGroupType *gzgt);
/* `sequencer_gizmo_retime_type.cc` */
void GIZMO_GT_retime_handle_add(wmGizmoType *gzt);
void GIZMO_GT_retime_handle(wmGizmoType *gzt);
void GIZMO_GT_retime_remove(wmGizmoType *gzt);
void GIZMO_GT_speed_set_remove(wmGizmoType *gzt);
/* `sequencer_retiming_draw.cc` */
void sequencer_draw_retiming(const struct bContext *C);
blender::Vector<Sequence *> sequencer_visible_strips_get(const struct bContext *C);
struct SeqRetimingKey *try_to_realize_virtual_key(const struct bContext *C,
struct Sequence *seq,
const int mval[2]);
struct SeqRetimingKey *retiming_mousover_key_get(const struct bContext *C,
const int mval[2],
Sequence **r_seq);
int left_fake_key_frame_get(const bContext *C, const Sequence *seq);
int right_fake_key_frame_get(const bContext *C, const Sequence *seq);

View File

@ -71,9 +71,10 @@ void sequencer_operatortypes()
/* `sequencer_retiming.cc` */
WM_operatortype_append(SEQUENCER_OT_retiming_reset);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_move);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_add);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_remove);
WM_operatortype_append(SEQUENCER_OT_retiming_show);
WM_operatortype_append(SEQUENCER_OT_retiming_key_add);
WM_operatortype_append(SEQUENCER_OT_retiming_freeze_frame_add);
WM_operatortype_append(SEQUENCER_OT_retiming_transition_add);
WM_operatortype_append(SEQUENCER_OT_retiming_segment_speed_set);
/* `sequencer_select.cc` */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,545 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_span.hh"
#include "DNA_anim_types.h"
#include "DNA_sequence_types.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_scene.h"
#include "BLF_api.h"
#include "GPU_batch.h"
#include "GPU_batch_utils.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_select.h"
#include "GPU_state.h"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_keyframes_draw.hh"
#include "ED_keyframes_keylist.hh"
#include "ED_screen.hh"
#include "ED_sequencer.hh"
#include "ED_view3d.hh"
#include "UI_interface.hh"
#include "UI_interface_icons.hh"
#include "UI_resources.hh"
#include "UI_view2d.hh"
#include "SEQ_iterator.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
/* Own include. */
#include "sequencer_intern.hh"
#define KEY_SIZE (10 * U.pixelsize)
#define KEY_CENTER (UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.0f)) + 4 + KEY_SIZE / 2)
static float strip_y_rescale(const Sequence *seq, const float y_value)
{
const float y_range = SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM;
return (y_value * y_range) + seq->machine + SEQ_STRIP_OFSBOTTOM;
}
static float key_x_get(const Scene *scene, const Sequence *seq, const SeqRetimingKey *key)
{
return SEQ_retiming_key_timeline_frame_get(scene, seq, key);
}
static float pixels_to_view_width(const bContext *C, const float width)
{
const View2D *v2d = UI_view2d_fromcontext(C);
float scale_x = UI_view2d_view_to_region_x(v2d, 1) - UI_view2d_view_to_region_x(v2d, 0.0f);
return width / scale_x;
}
static float pixels_to_view_height(const bContext *C, const float height)
{
const View2D *v2d = UI_view2d_fromcontext(C);
float scale_y = UI_view2d_view_to_region_y(v2d, 1) - UI_view2d_view_to_region_y(v2d, 0.0f);
return height / scale_y;
}
static float strip_start_screenspace_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_left_handle_frame_get(scene, seq));
}
static float strip_end_screenspace_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_right_handle_frame_get(scene, seq));
}
static rctf strip_box_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect;
rect.xmin = strip_start_screenspace_get(C, seq);
rect.xmax = strip_end_screenspace_get(C, seq);
rect.ymin = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0));
rect.ymax = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1));
return rect;
}
blender::Vector<Sequence *> sequencer_visible_strips_get(const bContext *C)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(CTX_data_scene(C));
blender::Vector<Sequence *> strips;
LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
if (min_ii(SEQ_time_left_handle_frame_get(scene, seq), SEQ_time_start_frame_get(seq)) >
v2d->cur.xmax)
{
continue;
}
if (max_ii(SEQ_time_right_handle_frame_get(scene, seq),
SEQ_time_content_end_frame_get(scene, seq)) < v2d->cur.xmin)
{
continue;
}
if (seq->machine + 1.0f < v2d->cur.ymin) {
continue;
}
if (seq->machine > v2d->cur.ymax) {
continue;
}
strips.append(seq);
}
return strips;
}
/** Size in pixels. */
#define RETIME_KEY_MOUSEOVER_THRESHOLD (16.0f * UI_SCALE_FAC)
static rctf keys_box_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect = strip_box_get(C, seq);
rect.ymax = KEY_CENTER + KEY_SIZE / 2;
rect.ymin = KEY_CENTER - KEY_SIZE / 2;
rect.xmax += RETIME_KEY_MOUSEOVER_THRESHOLD;
rect.xmin -= RETIME_KEY_MOUSEOVER_THRESHOLD;
return rect;
}
int left_fake_key_frame_get(const bContext *C, const Sequence *seq)
{
const Scene *scene = CTX_data_scene(C);
const int content_start = SEQ_time_start_frame_get(seq);
return max_ii(content_start, SEQ_time_left_handle_frame_get(scene, seq));
}
int right_fake_key_frame_get(const bContext *C, const Sequence *seq)
{
const Scene *scene = CTX_data_scene(C);
const int content_end = SEQ_time_content_end_frame_get(scene, seq);
return min_ii(content_end, SEQ_time_right_handle_frame_get(scene, seq));
}
static bool retiming_fake_key_is_clicked(const bContext *C,
const Sequence *seq,
const int key_timeline_frame,
const int mval[2])
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf box = keys_box_get(C, seq);
if (!BLI_rctf_isect_pt(&box, mval[0], mval[1])) {
return false;
}
const float key_pos = UI_view2d_view_to_region_x(v2d, key_timeline_frame);
const float distance = fabs(key_pos - mval[0]);
return distance < RETIME_KEY_MOUSEOVER_THRESHOLD;
}
SeqRetimingKey *try_to_realize_virtual_key(const bContext *C, Sequence *seq, const int mval[2])
{
Scene *scene = CTX_data_scene(C);
SeqRetimingKey *key = nullptr;
if (retiming_fake_key_is_clicked(C, seq, left_fake_key_frame_get(C, seq), mval)) {
SEQ_retiming_data_ensure(seq);
int frame = SEQ_time_left_handle_frame_get(scene, seq);
key = SEQ_retiming_add_key(scene, seq, frame);
}
if (retiming_fake_key_is_clicked(C, seq, right_fake_key_frame_get(C, seq), mval)) {
SEQ_retiming_data_ensure(seq);
const int frame = SEQ_time_right_handle_frame_get(scene, seq);
key = SEQ_retiming_add_key(scene, seq, frame);
}
return key;
}
static SeqRetimingKey *mouse_over_key_get_from_strip(const bContext *C,
const Sequence *seq,
const int mval[2])
{
const Scene *scene = CTX_data_scene(C);
const View2D *v2d = UI_view2d_fromcontext(C);
int best_distance = INT_MAX;
SeqRetimingKey *best_key = nullptr;
for (SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
int distance = round_fl_to_int(
fabsf(UI_view2d_view_to_region_x(v2d, key_x_get(scene, seq, &key)) - mval[0]));
if (distance < RETIME_KEY_MOUSEOVER_THRESHOLD && distance < best_distance) {
best_distance = distance;
best_key = &key;
}
}
return best_key;
}
SeqRetimingKey *retiming_mousover_key_get(const bContext *C, const int mval[2], Sequence **r_seq)
{
for (Sequence *seq : sequencer_visible_strips_get(C)) {
rctf box = keys_box_get(C, seq);
if (!BLI_rctf_isect_pt(&box, mval[0], mval[1])) {
continue;
}
if (r_seq != nullptr) {
*r_seq = seq;
}
SeqRetimingKey *key = mouse_over_key_get_from_strip(C, seq, mval);
if (key == nullptr) {
continue;
}
return key;
}
return nullptr;
}
/* -------------------------------------------------------------------- */
/** \name Retiming Key
* \{ */
static void retime_key_draw(const bContext *C, const Sequence *seq, const SeqRetimingKey *key)
{
const Scene *scene = CTX_data_scene(C);
const float key_x = key_x_get(scene, seq, key);
const View2D *v2d = UI_view2d_fromcontext(C);
const rctf strip_box = strip_box_get(C, seq);
if (!BLI_rctf_isect_x(&strip_box, UI_view2d_view_to_region_x(v2d, key_x))) {
return; /* Key out of the strip bounds. */
}
GPUVertFormat *format = immVertexFormat();
KeyframeShaderBindings sh_bindings;
sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
sh_bindings.color_id = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
sh_bindings.outline_color_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", 1.0f);
immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
immBegin(GPU_PRIM_POINTS, 1);
eBezTriple_KeyframeType key_type = BEZT_KEYTYPE_KEYFRAME;
if (SEQ_retiming_key_is_freeze_frame(key)) {
key_type = BEZT_KEYTYPE_BREAKDOWN;
}
if (SEQ_retiming_key_is_transition_type(key)) {
key_type = BEZT_KEYTYPE_MOVEHOLD;
}
const bool is_selected = SEQ_retiming_selection_contains(SEQ_editing_get(scene), key);
const int size = KEY_SIZE;
const float bottom = KEY_CENTER;
/* Ensure, that key is always inside of strip. */
const float right_pos_max = UI_view2d_view_to_region_x(
v2d, SEQ_time_right_handle_frame_get(scene, seq)) -
(size / 2);
const float left_pos_min = UI_view2d_view_to_region_x(
v2d, SEQ_time_left_handle_frame_get(scene, seq)) +
(size / 2);
float key_position = UI_view2d_view_to_region_x(v2d, key_x);
CLAMP(key_position, left_pos_min, right_pos_max);
const float alpha = SEQ_retiming_data_is_editable(seq) ? 1.0f : 0.3f;
draw_keyframe_shape(key_position,
bottom,
size,
is_selected && SEQ_retiming_data_is_editable(seq),
key_type,
KEYFRAME_SHAPE_BOTH,
alpha,
&sh_bindings,
0,
0);
immEnd();
GPU_program_point_size(false);
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
}
static void draw_continuity(const bContext *C, const Sequence *seq, const SeqRetimingKey *key)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
if (key_x_get(scene, seq, key) == SEQ_time_left_handle_frame_get(scene, seq) ||
key->strip_frame_index == 0)
{
return;
}
const float left_handle_position = UI_view2d_view_to_region_x(
v2d, SEQ_time_left_handle_frame_get(scene, seq));
const float right_handle_position = UI_view2d_view_to_region_x(
v2d, SEQ_time_right_handle_frame_get(scene, seq));
float key_position = UI_view2d_view_to_region_x(v2d, key_x_get(scene, seq, key));
float prev_key_position = UI_view2d_view_to_region_x(v2d, key_x_get(scene, seq, key - 1));
prev_key_position = max_ff(prev_key_position, left_handle_position);
key_position = min_ff(key_position, right_handle_position);
const int size = KEY_SIZE;
const float y_center = KEY_CENTER;
const float width_fac = 0.5f;
const float bottom = y_center - size * width_fac;
const float top = y_center + size * width_fac;
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
if (SEQ_retiming_data_is_editable(seq) &&
(SEQ_retiming_selection_contains(ed, key) || SEQ_retiming_selection_contains(ed, key - 1)))
{
immUniform4f("color", 0.65f, 0.5f, 0.2f, 1.0f);
}
else {
immUniform4f("color", 0.0f, 0.0f, 0.0f, 0.1f);
}
immRectf(pos, prev_key_position, bottom, key_position, top);
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
}
/* If there are no keys, draw fake keys and create real key when they are selected. */
/* TODO: would be nice to draw continuity between fake keys. */
static void fake_keys_draw(const bContext *C, Sequence *seq)
{
if (!SEQ_retiming_is_active(seq) && !SEQ_retiming_data_is_editable(seq)) {
return;
}
const Scene *scene = CTX_data_scene(C);
const int left_key_frame = left_fake_key_frame_get(C, seq);
const int right_key_frame = right_fake_key_frame_get(C, seq);
if (SEQ_retiming_key_get_by_timeline_frame(scene, seq, left_key_frame) == nullptr) {
SeqRetimingKey fake_key;
fake_key.strip_frame_index = left_key_frame - SEQ_time_start_frame_get(seq);
fake_key.flag = 0;
retime_key_draw(C, seq, &fake_key);
}
if (SEQ_retiming_key_get_by_timeline_frame(scene, seq, right_key_frame) == nullptr) {
SeqRetimingKey fake_key;
fake_key.strip_frame_index = right_key_frame - SEQ_time_start_frame_get(seq);
fake_key.flag = 0;
retime_key_draw(C, seq, &fake_key);
}
}
static void retime_keys_draw(const bContext *C)
{
const SpaceSeq *sseq = CTX_wm_space_seq(C);
if (!sequencer_retiming_mode_is_active(C) &&
(sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_RETIMING) == 0)
{
return;
}
wmOrtho2_region_pixelspace(CTX_wm_region(C));
for (Sequence *seq : sequencer_visible_strips_get(C)) {
if (!SEQ_retiming_is_allowed(seq)) {
continue;
}
for (const SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
draw_continuity(C, seq, &key);
}
fake_keys_draw(C, seq);
for (const SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
retime_key_draw(C, seq, &key);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Speed Label
* \{ */
static size_t label_str_get(const Sequence *seq,
const SeqRetimingKey *key,
char *r_label_str,
const size_t label_str_maxncpy)
{
const SeqRetimingKey *next_key = key + 1;
if (SEQ_retiming_key_is_transition_start(key)) {
const float prev_speed = SEQ_retiming_key_speed_get(seq, key);
const float next_speed = SEQ_retiming_key_speed_get(seq, next_key + 1);
return BLI_snprintf_rlen(r_label_str,
label_str_maxncpy,
"%d%% - %d%%",
round_fl_to_int(prev_speed * 100.0f),
round_fl_to_int(next_speed * 100.0f));
}
const float speed = SEQ_retiming_key_speed_get(seq, next_key);
return BLI_snprintf_rlen(
r_label_str, label_str_maxncpy, "%d%%", round_fl_to_int(speed * 100.0f));
}
static bool label_rect_get(const bContext *C,
const Sequence *seq,
const SeqRetimingKey *key,
char *label_str,
const size_t label_len,
rctf *rect)
{
const Scene *scene = CTX_data_scene(C);
const SeqRetimingKey *next_key = key + 1;
const float width = pixels_to_view_width(C, BLF_width(BLF_default(), label_str, label_len));
const float height = pixels_to_view_height(C, BLF_height(BLF_default(), label_str, label_len));
const float xmin = max_ff(SEQ_time_left_handle_frame_get(scene, seq),
key_x_get(scene, seq, key));
const float xmax = min_ff(SEQ_time_right_handle_frame_get(scene, seq),
key_x_get(scene, seq, next_key));
rect->xmin = (xmin + xmax - width) / 2;
rect->xmax = rect->xmin + width;
rect->ymin = strip_y_rescale(seq, 0) + pixels_to_view_height(C, 5);
rect->ymax = rect->ymin + height;
return width < xmax - xmin - pixels_to_view_width(C, KEY_SIZE);
}
static void retime_speed_text_draw(const bContext *C,
const Sequence *seq,
const SeqRetimingKey *key)
{
if (SEQ_retiming_is_last_key(seq, key)) {
return;
}
const Scene *scene = CTX_data_scene(C);
const int start_frame = SEQ_time_left_handle_frame_get(scene, seq);
const int end_frame = SEQ_time_right_handle_frame_get(scene, seq);
const SeqRetimingKey *next_key = key + 1;
if (key_x_get(scene, seq, next_key) < start_frame || key_x_get(scene, seq, key) > end_frame) {
return; /* Label out of strip bounds. */
}
char label_str[40];
rctf label_rect;
size_t label_len = label_str_get(seq, key, label_str, sizeof(label_str));
if (!label_rect_get(C, seq, key, label_str, label_len, &label_rect)) {
return; /* Not enough space to draw the label. */
}
uchar col[4] = {255, 255, 255, 255};
if ((seq->flag & SELECT) == 0) {
memset(col, 0, sizeof(col));
col[3] = 255;
}
UI_view2d_text_cache_add(
UI_view2d_fromcontext(C), label_rect.xmin, label_rect.ymin, label_str, label_len, col);
}
static void retime_speed_draw(const bContext *C)
{
const SpaceSeq *sseq = CTX_wm_space_seq(C);
if (!sequencer_retiming_mode_is_active(C) &&
(sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_RETIMING) == 0)
{
return;
}
const View2D *v2d = UI_view2d_fromcontext(C);
wmOrtho2_region_pixelspace(CTX_wm_region(C));
GPU_blend(GPU_BLEND_ALPHA);
GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
for (const Sequence *seq : sequencer_visible_strips_get(C)) {
for (const SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
retime_speed_text_draw(C, seq, &key);
}
}
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
UI_view2d_text_cache_draw(CTX_wm_region(C));
UI_view2d_view_ortho(v2d); /* 'UI_view2d_text_cache_draw()' messes up current view. */
}
/** \} */
void sequencer_draw_retiming(const bContext *C)
{
retime_keys_draw(C);
retime_speed_draw(C);
}

View File

@ -23,6 +23,7 @@
#include "BKE_report.h"
#include "WM_api.hh"
#include "WM_toolsystem.h"
#include "WM_types.hh"
#include "RNA_define.hh"
@ -30,6 +31,7 @@
#include "SEQ_channels.h"
#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_retiming.hh"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@ -287,7 +289,7 @@ Sequence *find_neighboring_sequence(Scene *scene, Sequence *test, int lr, int se
return nullptr;
}
Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[2])
Sequence *find_nearest_seq(const Scene *scene, const View2D *v2d, int *hand, const int mval[2])
{
Sequence *seq;
Editing *ed = SEQ_editing_get(scene);
@ -442,6 +444,10 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (sequencer_retiming_mode_is_active(C)) {
return sequencer_retiming_select_all_exec(C, op);
}
SeqCollection *strips = all_strips_from_context(C);
Sequence *seq;
@ -885,7 +891,7 @@ static void sequencer_select_strip_impl(const Editing *ed,
}
}
static int sequencer_select_exec(bContext *C, wmOperator *op)
int sequencer_select_exec(bContext *C, wmOperator *op)
{
View2D *v2d = UI_view2d_fromcontext(C);
Scene *scene = CTX_data_scene(C);
@ -906,6 +912,10 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
}
}
if (sequencer_retiming_mode_is_active(C)) {
return sequencer_retiming_key_select_exec(C, op);
}
bool extend = RNA_boolean_get(op->ptr, "extend");
bool deselect = RNA_boolean_get(op->ptr, "deselect");
bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
@ -967,6 +977,29 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
return OPERATOR_RUNNING_MODAL;
}
Sequence *seq_key_test = nullptr;
SeqRetimingKey *key = retiming_mousover_key_get(C, mval, &seq_key_test);
if (seq_key_test && SEQ_retiming_data_is_editable(seq_key_test) &&
!sequencer_retiming_mode_is_active(C))
{
/* Realize "fake" key, if it is clicked on. */
if (key == nullptr && seq_key_test != nullptr) {
key = try_to_realize_virtual_key(C, seq_key_test, mval);
}
bool retiming_key_clicked = (key != nullptr);
if (seq_key_test && retiming_key_clicked) {
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
ED_sequencer_deselect_all(scene);
SEQ_retiming_selection_clear(ed);
SEQ_retiming_selection_append(key);
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
}
bool changed = false;
/* Deselect everything */
@ -1632,6 +1665,10 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (sequencer_retiming_mode_is_active(C)) {
return sequencer_retiming_box_select_exec(C, op);
}
const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
const bool handles = RNA_boolean_get(op->ptr, "include_handles");
const bool select = (sel_op != SEL_OP_SUB);

View File

@ -2084,6 +2084,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
draw_timeline_backdrop(&ctx);
draw_timeline_sfra_efra(&ctx);
draw_seq_strips(&ctx);
sequencer_draw_retiming(C);
draw_timeline_markers(&ctx);
ANIM_draw_previewrange(C, ctx.v2d, 1);
draw_timeline_gizmos(&ctx);

View File

@ -95,7 +95,8 @@ static SpaceLink *sequencer_create(const ScrArea * /*area*/, const Scene *scene)
sseq->preview_overlay.flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_PREVIEW_SHOW_OUTLINE_SELECTED;
sseq->timeline_overlay.flag = SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE |
SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID |
SEQ_TIMELINE_SHOW_FCURVES | SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG;
SEQ_TIMELINE_SHOW_FCURVES | SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG |
SEQ_TIMELINE_SHOW_STRIP_RETIMING;
BLI_rctf_init(&sseq->runtime.last_thumbnail_area, 0.0f, 0.0f, 0.0f, 0.0f);
sseq->runtime.last_displayed_thumbnails = nullptr;
@ -428,21 +429,14 @@ static void SEQUENCER_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt)
static void sequencer_gizmos()
{
const wmGizmoMapType_Params params = {SPACE_SEQ, RGN_TYPE_PREVIEW};
wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&params);
WM_gizmotype_append(GIZMO_GT_retime_handle_add);
WM_gizmotype_append(GIZMO_GT_retime_handle);
WM_gizmotype_append(GIZMO_GT_retime_remove);
WM_gizmotype_append(GIZMO_GT_speed_set_remove);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_translate);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_resize);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_rotate);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo_retime);
WM_gizmogrouptype_append_and_link(gzmap_type, SEQUENCER_GGT_navigate);
const wmGizmoMapType_Params params_preview = {SPACE_SEQ, RGN_TYPE_PREVIEW};
wmGizmoMapType *gzmap_type_preview = WM_gizmomaptype_ensure(&params_preview);
WM_gizmogrouptype_append_and_link(gzmap_type_preview, SEQUENCER_GGT_navigate);
}
/* *********************** sequencer (main) region ************************ */

View File

@ -49,6 +49,7 @@ set(SRC
transform_convert_sculpt.cc
transform_convert_sequencer.cc
transform_convert_sequencer_image.cc
transform_convert_sequencer_retiming.cc
transform_convert_tracking.cc
transform_convert_tracking_curves.cc
transform_draw_cursors.cc

View File

@ -36,6 +36,7 @@
#include "ED_particle.hh"
#include "ED_screen.hh"
#include "ED_screen_types.hh"
#include "ED_sequencer.hh"
#include "UI_view2d.hh"
@ -942,6 +943,9 @@ static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj
if (t->options & CTX_SEQUENCER_IMAGE) {
return &TransConvertType_SequencerImage;
}
if (sequencer_retiming_mode_is_active(t->context)) {
return &TransConvertType_SequencerRetiming;
}
return &TransConvertType_Sequencer;
}
if (t->spacetype == SPACE_GRAPH) {

View File

@ -269,6 +269,10 @@ extern TransConvertTypeInfo TransConvertType_Sequencer;
extern TransConvertTypeInfo TransConvertType_SequencerImage;
/* `transform_convert_sequencer_retiming.cc` */
extern TransConvertTypeInfo TransConvertType_SequencerRetiming;
/* `transform_convert_tracking.cc` */
extern TransConvertTypeInfo TransConvertType_Tracking;

View File

@ -0,0 +1,158 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edtransform
*/
#include "MEM_guardedalloc.h"
#include "DNA_space_types.h"
#include "BLI_listbase.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "SEQ_relations.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "ED_keyframing.hh"
#include "UI_view2d.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "transform.hh"
#include "transform_convert.hh"
/** Used for sequencer transform. */
typedef struct TransDataSeq {
Sequence *seq;
int orig_timeline_frame;
int key_index; /* Some actions may need to destroy original data, use index to access it. */
} TransDataSeq;
static TransData *SeqToTransData(const Scene *scene,
Sequence *seq,
const SeqRetimingKey *key,
TransData *td,
TransData2D *td2d,
TransDataSeq *tdseq)
{
td2d->loc[0] = SEQ_retiming_key_timeline_frame_get(scene, seq, key);
td2d->loc[1] = key->retiming_factor;
td2d->loc2d = nullptr;
td->loc = td2d->loc;
copy_v3_v3(td->iloc, td->loc);
copy_v3_v3(td->center, td->loc);
memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
unit_m3(td->mtx);
unit_m3(td->smtx);
tdseq->seq = seq;
tdseq->orig_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, key);
tdseq->key_index = SEQ_retiming_key_index_get(seq, key);
td->extra = static_cast<void *>(tdseq);
td->ext = nullptr;
td->flag |= TD_SELECTED;
td->dist = 0.0;
return td;
}
static void freeSeqData(TransInfo * /*t*/,
TransDataContainer *tc,
TransCustomData * /*custom_data*/)
{
TransData *td = (TransData *)tc->data;
MEM_freeN(td->extra);
}
static void createTransSeqRetimingData(bContext * /*C*/, TransInfo *t)
{
const Editing *ed = SEQ_editing_get(t->scene);
if (ed == nullptr) {
return;
}
const blender::Map selection = SEQ_retiming_selection_get(SEQ_editing_get(t->scene));
if (selection.size() == 0) {
return;
}
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
tc->custom.type.free_cb = freeSeqData;
tc->data_len = selection.size();
tc->data = MEM_cnew_array<TransData>(tc->data_len, "TransSeq TransData");
tc->data_2d = MEM_cnew_array<TransData2D>(tc->data_len, "TransSeq TransData2D");
TransDataSeq *tdseq = MEM_cnew_array<TransDataSeq>(tc->data_len, "TransSeq TransDataSeq");
TransData *td = tc->data;
TransData2D *td2d = tc->data_2d;
for (auto item : selection.items()) {
SeqToTransData(t->scene, item.value, item.key, td++, td2d++, tdseq++);
}
}
static void seq_resize_speed_transition(const Scene *scene,
const Sequence *seq,
SeqRetimingKey *key,
const float loc)
{
SeqRetimingKey *key_start = SEQ_retiming_transition_start_get(key);
float offset;
if (key == key_start) {
offset = loc - SEQ_retiming_key_timeline_frame_get(scene, seq, key);
}
else {
offset = SEQ_retiming_key_timeline_frame_get(scene, seq, key) - loc;
}
SEQ_retiming_offset_transition_key(scene, seq, key_start, offset);
}
static void recalcData_sequencer_retiming(TransInfo *t)
{
const TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
const TransData *td = nullptr;
const TransData2D *td2d = nullptr;
int i;
for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) {
const TransDataSeq *tdseq = static_cast<TransDataSeq *>(td->extra);
Sequence *seq = tdseq->seq;
/* Calculate translation. */
const blender::MutableSpan keys = SEQ_retiming_keys_get(seq);
SeqRetimingKey *key = &keys[tdseq->key_index];
if (SEQ_retiming_key_is_transition_type(key) &&
!SEQ_retiming_selection_has_whole_transition(SEQ_editing_get(t->scene), key))
{
seq_resize_speed_transition(t->scene, seq, key, td2d->loc[0]);
}
else {
SEQ_retiming_key_timeline_frame_set(t->scene, seq, key, td2d->loc[0]);
}
SEQ_relations_invalidate_cache_preprocessed(t->scene, seq);
}
}
TransConvertTypeInfo TransConvertType_SequencerRetiming = {
/*flags*/ (T_POINTS | T_2D_EDIT),
/*createTransData*/ createTransSeqRetimingData,
/*recalcData*/ recalcData_sequencer_retiming,
};

View File

@ -30,6 +30,8 @@
#include "BLT_translation.h"
#include "ED_sequencer.hh"
#include "transform.hh"
#include "transform_convert.hh"
#include "transform_gizmo.hh"
@ -1151,6 +1153,10 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
t->mode = eTfmMode(mode);
t->mode_info = mode_info_get(t, mode);
if (t->spacetype == SPACE_SEQ && sequencer_retiming_mode_is_active(t->context)) {
t->mode_info = &TransMode_translate;
}
if (t->mode_info) {
t->flag |= eTFlag(t->mode_info->flags);
t->mode_info->init_fn(t, op);

View File

@ -249,7 +249,7 @@ static int seq_snap_threshold_get_frame_distance(const TransInfo *t)
TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t)
{
if (t->data_type == &TransConvertType_SequencerImage) {
if (ELEM(t->data_type, &TransConvertType_SequencerImage, &TransConvertType_SequencerRetiming)) {
return nullptr;
}

View File

@ -118,20 +118,23 @@ typedef struct Strip {
ColorManagedColorspaceSettings colorspace_settings;
} Strip;
typedef enum eSeqRetimingHandleFlag {
SPEED_TRANSITION = (1 << 0),
FREEZE_FRAME = (1 << 1),
} eSeqRetimingHandleFlag;
typedef enum eSeqRetimingKeyFlag {
SEQ_SPEED_TRANSITION_IN = (1 << 0),
SEQ_SPEED_TRANSITION_OUT = (1 << 1),
SEQ_FREEZE_FRAME_IN = (1 << 2),
SEQ_FREEZE_FRAME_OUT = (1 << 3),
SEQ_KEY_SELECTED = (1 << 4),
} eSeqRetimingKeyFlag;
typedef struct SeqRetimingHandle {
typedef struct SeqRetimingKey {
int strip_frame_index;
int flag; /* eSeqRetimingHandleFlag */
int flag; /* eSeqRetimingKeyFlag */
int _pad0;
float retiming_factor; /* Value between 0-1 mapped to original content range. */
int original_strip_frame_index; /* Used for transition handles only. */
float original_retiming_factor; /* Used for transition handles only. */
} SeqRetimingHandle;
int original_strip_frame_index; /* Used for transition keys only. */
float original_retiming_factor; /* Used for transition keys only. */
} SeqRetimingKey;
typedef struct SequenceRuntime {
SessionUUID session_uuid;
@ -266,9 +269,9 @@ typedef struct Sequence {
float media_playback_rate;
float speed_factor;
struct SeqRetimingHandle *retiming_handles;
struct SeqRetimingKey *retiming_keys;
void *_pad5;
int retiming_handle_num;
int retiming_keys_num;
char _pad6[4];
SequenceRuntime runtime;
@ -609,7 +612,7 @@ enum {
SEQ_IGNORE_CHANNEL_LOCK = (1 << 16),
SEQ_AUTO_PLAYBACK_RATE = (1 << 17),
SEQ_SINGLE_FRAME_CONTENT = (1 << 18),
SEQ_FLAG_UNUSED_19 = (1 << 19), /* cleared */
SEQ_SHOW_RETIMING = (1 << 19),
SEQ_FLAG_UNUSED_21 = (1 << 21), /* cleared */
SEQ_USE_EFFECT_DEFAULT_FADE = (1 << 22),

View File

@ -617,6 +617,7 @@ typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag {
SEQ_TIMELINE_SHOW_THUMBNAILS = (1 << 2),
/** Use #Sequence::color_tag */
SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG = (1 << 3),
SEQ_TIMELINE_SHOW_STRIP_RETIMING = (1 << 4),
SEQ_TIMELINE_SHOW_FCURVES = (1 << 5),
/** Draw all wave-forms. */
SEQ_TIMELINE_ALL_WAVEFORMS = (1 << 7),

View File

@ -43,6 +43,7 @@
/* NOTE: Keep sorted! */
DNA_STRUCT_RENAME(Lamp, Light)
DNA_STRUCT_RENAME(SeqRetimingHandle, SeqRetimingKey)
DNA_STRUCT_RENAME(SpaceButs, SpaceProperties)
DNA_STRUCT_RENAME(SpaceIpo, SpaceGraph)
DNA_STRUCT_RENAME(SpaceOops, SpaceOutliner)
@ -158,6 +159,8 @@ DNA_STRUCT_RENAME_ELEM(RenderData, bake_filter, bake_margin)
DNA_STRUCT_RENAME_ELEM(RigidBodyWorld, steps_per_second, substeps_per_frame)
DNA_STRUCT_RENAME_ELEM(SDefBind, numverts, verts_num)
DNA_STRUCT_RENAME_ELEM(SDefVert, numbinds, binds_num)
DNA_STRUCT_RENAME_ELEM(Sequence, retiming_handle_num, retiming_keys_num)
DNA_STRUCT_RENAME_ELEM(Sequence, retiming_handles, retiming_keys)
DNA_STRUCT_RENAME_ELEM(SpaceImage, pixel_snap_mode, pixel_round_mode)
DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type)
DNA_STRUCT_RENAME_ELEM(Strip, dir, dirpath)

View File

@ -466,7 +466,7 @@ void RNA_api_region_view3d(struct StructRNA *srna);
void RNA_api_texture(struct StructRNA *srna);
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, bool metastrip);
void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_api_sequence_retiming_handles(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_api_sequence_retiming_keys(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_api_sound(struct StructRNA *srna);
void RNA_api_vfont(struct StructRNA *srna);
void RNA_api_workspace(struct StructRNA *srna);

View File

@ -46,7 +46,7 @@
#include "SEQ_prefetch.h"
#include "SEQ_proxy.h"
#include "SEQ_relations.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
#include "SEQ_sound.h"
@ -308,35 +308,34 @@ static void rna_Sequence_elements_begin(CollectionPropertyIterator *iter, Pointe
nullptr);
}
static int rna_Sequence_retiming_handles_length(PointerRNA *ptr)
static int rna_Sequence_retiming_keys_length(PointerRNA *ptr)
{
return SEQ_retiming_handles_count((Sequence *)ptr->data);
return SEQ_retiming_keys_count((Sequence *)ptr->data);
}
static void rna_SequenceEditor_retiming_handles_begin(CollectionPropertyIterator *iter,
PointerRNA *ptr)
static void rna_SequenceEditor_retiming_keys_begin(CollectionPropertyIterator *iter,
PointerRNA *ptr)
{
Sequence *seq = (Sequence *)ptr->data;
rna_iterator_array_begin(iter,
(void *)seq->retiming_handles,
sizeof(SeqRetimingHandle),
SEQ_retiming_handles_count(seq),
(void *)seq->retiming_keys,
sizeof(SeqRetimingKey),
SEQ_retiming_keys_count(seq),
0,
nullptr);
}
static Sequence *strip_by_handle_find(Scene *scene, SeqRetimingHandle *handle)
static Sequence *strip_by_key_find(Scene *scene, SeqRetimingKey *key)
{
Editing *ed = SEQ_editing_get(scene);
SeqCollection *strips = SEQ_query_all_strips_recursive(&ed->seqbase);
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
const int retiming_handle_count = SEQ_retiming_handles_count(seq);
SeqRetimingHandle *first = seq->retiming_handles;
SeqRetimingHandle *last = seq->retiming_handles + retiming_handle_count - 1;
SeqRetimingKey *first = seq->retiming_keys;
SeqRetimingKey *last = seq->retiming_keys + SEQ_retiming_keys_count(seq) - 1;
if (handle >= first && handle <= last) {
if (key >= first && key <= last) {
return seq;
}
}
@ -344,49 +343,54 @@ static Sequence *strip_by_handle_find(Scene *scene, SeqRetimingHandle *handle)
return nullptr;
}
static void rna_Sequence_retiming_handle_remove(ID *id, SeqRetimingHandle *handle)
static void rna_Sequence_retiming_key_remove(ID *id, SeqRetimingKey *key)
{
Scene *scene = (Scene *)id;
Sequence *seq = strip_by_handle_find(scene, handle);
Sequence *seq = strip_by_key_find(scene, key);
if (seq == nullptr) {
return;
}
SEQ_retiming_remove_handle(scene, seq, handle);
SEQ_retiming_remove_key(scene, seq, key);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, nullptr);
}
static int rna_Sequence_retiming_handle_frame_get(PointerRNA *ptr)
static int rna_Sequence_retiming_key_frame_get(PointerRNA *ptr)
{
SeqRetimingHandle *handle = (SeqRetimingHandle *)ptr->data;
SeqRetimingKey *key = (SeqRetimingKey *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
Sequence *seq = strip_by_handle_find(scene, handle);
Sequence *seq = strip_by_key_find(scene, key);
if (seq == nullptr) {
return 0;
}
return SEQ_time_start_frame_get(seq) + handle->strip_frame_index;
return SEQ_time_start_frame_get(seq) + key->strip_frame_index;
}
static void rna_Sequence_retiming_handle_frame_set(PointerRNA *ptr, int value)
static void rna_Sequence_retiming_key_frame_set(PointerRNA *ptr, int value)
{
SeqRetimingHandle *handle = (SeqRetimingHandle *)ptr->data;
SeqRetimingKey *key = (SeqRetimingKey *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
Sequence *seq = strip_by_handle_find(scene, handle);
Sequence *seq = strip_by_key_find(scene, key);
if (seq == nullptr) {
return;
}
const int offset = value - (SEQ_time_start_frame_get(seq) + handle->strip_frame_index);
SEQ_retiming_offset_handle(scene, seq, handle, offset);
SEQ_retiming_key_timeline_frame_set(scene, seq, key, value);
SEQ_relations_invalidate_cache_raw(scene, seq);
}
static bool rna_SequenceEditor_selected_retiming_key_get(PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
return SEQ_retiming_selection_get(SEQ_editing_get(scene)).size() != 0;
}
static void rna_Sequence_views_format_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
rna_Sequence_invalidate_raw_update(bmain, scene, ptr);
@ -1644,29 +1648,27 @@ static void rna_def_strip_element(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second");
}
static void rna_def_retiming_handle(BlenderRNA *brna)
static void rna_def_retiming_key(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "RetimingHandle", nullptr);
srna = RNA_def_struct(brna, "RetimingKey", nullptr);
RNA_def_struct_ui_text(
srna,
"Retiming Handle",
"Handle mapped to particular frame that can be moved to change playback speed");
RNA_def_struct_sdna(srna, "SeqRetimingHandle");
"Retiming Key",
"Key mapped to particular frame that can be moved to change playback speed");
RNA_def_struct_sdna(srna, "SeqRetimingKey");
prop = RNA_def_property(srna, "timeline_frame", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "strip_frame_index");
RNA_def_property_int_funcs(prop,
"rna_Sequence_retiming_handle_frame_get",
"rna_Sequence_retiming_handle_frame_set",
nullptr);
RNA_def_property_ui_text(prop, "Timeline Frame", "Position of retiming handle in timeline");
RNA_def_property_int_funcs(
prop, "rna_Sequence_retiming_key_frame_get", "rna_Sequence_retiming_key_frame_set", nullptr);
RNA_def_property_ui_text(prop, "Timeline Frame", "Position of retiming key in timeline");
FunctionRNA *func = RNA_def_function(srna, "remove", "rna_Sequence_retiming_handle_remove");
FunctionRNA *func = RNA_def_function(srna, "remove", "rna_Sequence_retiming_key_remove");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Remove retiming handle");
RNA_def_function_ui_description(func, "Remove retiming key");
}
static void rna_def_strip_crop(BlenderRNA *brna)
@ -2342,6 +2344,10 @@ static void rna_def_sequence(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, nullptr, "cache_flag", SEQ_CACHE_OVERRIDE);
RNA_def_property_ui_text(prop, "Override Cache Settings", "Override global cache settings");
prop = RNA_def_property(srna, "show_retiming_keys", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", SEQ_SHOW_RETIMING);
RNA_def_property_ui_text(prop, "Show Retiming Keys", "Show retiming keys, so they can be moved");
RNA_api_sequence_strip(srna);
}
@ -2441,6 +2447,11 @@ static void rna_def_editor(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Active Strip", "Sequencer's active strip");
prop = RNA_def_property(srna, "selected_retiming_keys", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_ui_text(prop, "Retiming Key Selection Status", "");
RNA_def_property_boolean_funcs(prop, "rna_SequenceEditor_selected_retiming_key_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "show_overlay_frame", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "overlay_frame_flag", SEQ_EDIT_OVERLAY_FRAME_SHOW);
RNA_def_property_ui_text(
@ -2903,20 +2914,20 @@ static void rna_def_movie(BlenderRNA *brna)
nullptr,
nullptr);
prop = RNA_def_property(srna, "retiming_handles", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "retiming_handles", nullptr);
RNA_def_property_struct_type(prop, "RetimingHandle");
RNA_def_property_ui_text(prop, "Retiming Handles", "");
prop = RNA_def_property(srna, "retiming_keys", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "retiming_keys", nullptr);
RNA_def_property_struct_type(prop, "RetimingKey");
RNA_def_property_ui_text(prop, "Retiming Keys", "");
RNA_def_property_collection_funcs(prop,
"rna_SequenceEditor_retiming_handles_begin",
"rna_SequenceEditor_retiming_keys_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_Sequence_retiming_handles_length",
"rna_Sequence_retiming_keys_length",
nullptr,
nullptr,
nullptr);
RNA_api_sequence_retiming_handles(brna, prop);
RNA_api_sequence_retiming_keys(brna, prop);
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
RNA_def_property_ui_text(prop, "File", "");
@ -3903,7 +3914,7 @@ void RNA_def_sequencer(BlenderRNA *brna)
rna_def_color_balance(brna);
rna_def_strip_element(brna);
rna_def_retiming_handle(brna);
rna_def_retiming_key(brna);
rna_def_strip_proxy(brna);
rna_def_strip_color_balance(brna);
rna_def_strip_crop(brna);

View File

@ -46,7 +46,7 @@
# include "SEQ_effects.h"
# include "SEQ_relations.h"
# include "SEQ_render.h"
# include "SEQ_retiming.h"
# include "SEQ_retiming.hh"
# include "SEQ_sequencer.h"
# include "SEQ_time.h"
@ -647,20 +647,18 @@ static void rna_Sequence_invalidate_cache_rnafunc(ID *id, Sequence *self, int ty
}
}
static SeqRetimingHandle *rna_Sequence_retiming_handles_add(ID *id,
Sequence *seq,
int timeline_frame)
static SeqRetimingKey *rna_Sequence_retiming_keys_add(ID *id, Sequence *seq, int timeline_frame)
{
Scene *scene = (Scene *)id;
SeqRetimingHandle *handle = SEQ_retiming_add_handle(scene, seq, timeline_frame);
SeqRetimingKey *key = SEQ_retiming_add_key(scene, seq, timeline_frame);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, nullptr);
return handle;
return key;
}
static void rna_Sequence_retiming_handles_reset(ID *id, Sequence *seq)
static void rna_Sequence_retiming_keys_reset(ID *id, Sequence *seq)
{
Scene *scene = (Scene *)id;
@ -777,28 +775,27 @@ void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
}
void RNA_api_sequence_retiming_handles(BlenderRNA *brna, PropertyRNA *cprop)
void RNA_api_sequence_retiming_keys(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
RNA_def_property_srna(cprop, "RetimingHandles");
srna = RNA_def_struct(brna, "RetimingHandles", nullptr);
RNA_def_property_srna(cprop, "RetimingKeys");
srna = RNA_def_struct(brna, "RetimingKeys", nullptr);
RNA_def_struct_sdna(srna, "Sequence");
RNA_def_struct_ui_text(srna, "RetimingHandles", "Collection of RetimingHandle");
RNA_def_struct_ui_text(srna, "RetimingKeys", "Collection of RetimingKey");
FunctionRNA *func = RNA_def_function(srna, "add", "rna_Sequence_retiming_handles_add");
FunctionRNA *func = RNA_def_function(srna, "add", "rna_Sequence_retiming_keys_add");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_int(
func, "timeline_frame", 0, -MAXFRAME, MAXFRAME, "Timeline Frame", "", -MAXFRAME, MAXFRAME);
RNA_def_function_ui_description(func, "Add retiming handle");
RNA_def_function_ui_description(func, "Add retiming key");
/* return type */
PropertyRNA *parm = RNA_def_pointer(
func, "retiming_handle", "RetimingHandle", "", "New RetimingHandle");
PropertyRNA *parm = RNA_def_pointer(func, "retiming_key", "RetimingKey", "", "New RetimingKey");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "reset", "rna_Sequence_retiming_handles_reset");
func = RNA_def_function(srna, "reset", "rna_Sequence_retiming_keys_reset");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Remove all retiming handles");
RNA_def_function_ui_description(func, "Remove all retiming keys");
}
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastrip)

View File

@ -5816,6 +5816,11 @@ static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Show Color Tags", "Display the strip color tags in the sequencer");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, nullptr);
prop = RNA_def_property(srna, "show_strip_retiming", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", SEQ_TIMELINE_SHOW_STRIP_RETIMING);
RNA_def_property_ui_text(prop, "Show Retiming Keys", "Display retiming keys on top of strips");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, nullptr);
}
static void rna_def_space_sequencer(BlenderRNA *brna)

View File

@ -3471,9 +3471,53 @@ static void rna_def_userdef_theme_space_seq(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "keyframe", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, nullptr, "vertex_select");
RNA_def_property_float_sdna(prop, nullptr, "keytype_keyframe");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Keyframe", "");
RNA_def_property_ui_text(prop, "Keyframe", "Color of Keyframe");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "keyframe_selected", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, nullptr, "keytype_keyframe_select");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Keyframe Selected", "Color of selected keyframe");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "keyframe_breakdown", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, nullptr, "keytype_breakdown");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Breakdown Keyframe", "Color of breakdown keyframe");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "keyframe_breakdown_selected", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, nullptr, "keytype_breakdown_select");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(
prop, "Breakdown Keyframe Selected", "Color of selected breakdown keyframe");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "keyframe_movehold", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, nullptr, "keytype_movehold");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Moving Hold Keyframe", "Color of moving hold keyframe");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "keyframe_movehold_selected", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, nullptr, "keytype_movehold_select");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(
prop, "Moving Hold Keyframe Selected", "Color of selected moving hold keyframe");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "keyframe_border", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, nullptr, "keyborder");
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Keyframe Border", "Color of keyframe border");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "keyframe_border_selected", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, nullptr, "keyborder_select");
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Keyframe Border Selected", "Color of selected keyframe border");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "draw_action", PROP_FLOAT, PROP_COLOR_GAMMA);

View File

@ -37,7 +37,6 @@ set(SRC
SEQ_proxy.h
SEQ_relations.h
SEQ_render.h
SEQ_retiming.h
SEQ_retiming.hh
SEQ_select.h
SEQ_sequencer.h

View File

@ -1,67 +0,0 @@
/* SPDX-FileCopyrightText: 2004 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup sequencer
*/
#ifdef __cplusplus
extern "C" {
#endif
struct Scene;
struct Sequence;
struct SeqRetimingHandle;
int SEQ_retiming_handles_count(const struct Sequence *seq);
bool SEQ_retiming_is_active(const struct Sequence *seq);
void SEQ_retiming_data_ensure(const struct Scene *scene, struct Sequence *seq);
void SEQ_retiming_data_clear(struct Sequence *seq);
bool SEQ_retiming_is_allowed(const struct Sequence *seq);
/**
* Add new retiming handle.
* This function always reallocates memory, so when function is used all stored pointers will
* become invalid.
*/
struct SeqRetimingHandle *SEQ_retiming_add_handle(const struct Scene *scene,
struct Sequence *seq,
const int timeline_frame);
SeqRetimingHandle *SEQ_retiming_add_transition(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingHandle *handle,
const int offset);
SeqRetimingHandle *SEQ_retiming_add_freeze_frame(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingHandle *handle,
const int offset);
struct SeqRetimingHandle *SEQ_retiming_last_handle_get(const struct Sequence *seq);
void SEQ_retiming_remove_handle(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingHandle *handle);
void SEQ_retiming_offset_handle(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingHandle *handle,
const int offset);
float SEQ_retiming_handle_speed_get(const struct Sequence *seq,
const struct SeqRetimingHandle *handle);
void SEQ_retiming_handle_speed_set(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingHandle *handle,
const float speed);
int SEQ_retiming_handle_index_get(const struct Sequence *seq,
const struct SeqRetimingHandle *handle);
void SEQ_retiming_sound_animation_data_set(const struct Scene *scene, const struct Sequence *seq);
float SEQ_retiming_handle_timeline_frame_get(const struct Scene *scene,
const struct Sequence *seq,
const struct SeqRetimingHandle *handle);
const SeqRetimingHandle *SEQ_retiming_find_segment_start_handle(const struct Sequence *seq,
const int frame_index);
bool SEQ_retiming_handle_is_transition_type(const struct SeqRetimingHandle *handle);
bool SEQ_retiming_handle_is_freeze_frame(const struct SeqRetimingHandle *handle);
#ifdef __cplusplus
}
#endif

View File

@ -8,9 +8,73 @@
* \ingroup sequencer
*/
#include "BLI_map.hh"
#include "BLI_span.hh"
struct Sequence;
struct SeqRetimingHandle;
struct SeqRetimingKey;
blender::MutableSpan<SeqRetimingHandle> SEQ_retiming_handles_get(const Sequence *seq);
blender::MutableSpan<SeqRetimingKey> SEQ_retiming_keys_get(const Sequence *seq);
blender::Map<SeqRetimingKey *, Sequence *> SEQ_retiming_selection_get(const struct Editing *ed);
int SEQ_retiming_keys_count(const struct Sequence *seq);
bool SEQ_retiming_is_active(const struct Sequence *seq);
void SEQ_retiming_data_ensure(struct Sequence *seq);
void SEQ_retiming_data_clear(struct Sequence *seq);
bool SEQ_retiming_is_allowed(const struct Sequence *seq);
/**
* Add new retiming key.
* This function always reallocates memory, so when function is used all stored pointers will
* become invalid.
*/
SeqRetimingKey *SEQ_retiming_add_key(const struct Scene *scene,
struct Sequence *seq,
const int timeline_frame);
SeqRetimingKey *SEQ_retiming_add_transition(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingKey *key,
const int offset);
SeqRetimingKey *SEQ_retiming_add_freeze_frame(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingKey *key,
const int offset);
bool SEQ_retiming_is_last_key(const struct Sequence *seq, const struct SeqRetimingKey *key);
struct SeqRetimingKey *SEQ_retiming_last_key_get(const struct Sequence *seq);
void SEQ_retiming_remove_key(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingKey *key);
void SEQ_retiming_offset_transition_key(const struct Scene *scene,
const struct Sequence *seq,
struct SeqRetimingKey *key,
const int offset);
float SEQ_retiming_key_speed_get(const struct Sequence *seq, const struct SeqRetimingKey *key);
void SEQ_retiming_key_speed_set(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingKey *key,
const float speed);
int SEQ_retiming_key_index_get(const struct Sequence *seq, const struct SeqRetimingKey *key);
SeqRetimingKey *SEQ_retiming_key_get_by_timeline_frame(const struct Scene *scene,
const struct Sequence *seq,
const int timeline_frame);
void SEQ_retiming_sound_animation_data_set(const struct Scene *scene, const struct Sequence *seq);
float SEQ_retiming_key_timeline_frame_get(const struct Scene *scene,
const struct Sequence *seq,
const struct SeqRetimingKey *key);
void SEQ_retiming_key_timeline_frame_set(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingKey *key,
const int timeline_frame);
SeqRetimingKey *SEQ_retiming_find_segment_start_key(const struct Sequence *seq,
const int frame_index);
bool SEQ_retiming_key_is_transition_type(const struct SeqRetimingKey *key);
bool SEQ_retiming_key_is_transition_start(const struct SeqRetimingKey *key);
SeqRetimingKey *SEQ_retiming_transition_start_get(struct SeqRetimingKey *key);
bool SEQ_retiming_key_is_freeze_frame(const struct SeqRetimingKey *key);
bool SEQ_retiming_selection_clear(const struct Editing *ed);
void SEQ_retiming_selection_append(struct SeqRetimingKey *key);
void SEQ_retiming_selection_remove(struct SeqRetimingKey *key);
void SEQ_retiming_remove_multiple_keys(struct Sequence *seq,
blender::Vector<SeqRetimingKey *> &keys);
bool SEQ_retiming_selection_contains(const struct Editing *ed, const struct SeqRetimingKey *key);
bool SEQ_retiming_selection_has_whole_transition(const struct Editing *ed,
struct SeqRetimingKey *key);
bool SEQ_retiming_data_is_editable(const struct Sequence *seq);

View File

@ -38,7 +38,7 @@
#include "SEQ_modifier.h"
#include "SEQ_proxy.h"
#include "SEQ_relations.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
#include "SEQ_sound.h"
@ -226,10 +226,10 @@ static void seq_sequence_free_ex(Scene *scene,
SEQ_channels_free(&seq->channels);
}
if (seq->retiming_handles != nullptr) {
MEM_freeN(seq->retiming_handles);
seq->retiming_handles = nullptr;
seq->retiming_handle_num = 0;
if (seq->retiming_keys != nullptr) {
MEM_freeN(seq->retiming_keys);
seq->retiming_keys = nullptr;
seq->retiming_keys_num = 0;
}
MEM_freeN(seq);
@ -293,6 +293,7 @@ void SEQ_editing_free(Scene *scene, const bool do_id_user)
BLI_freelistN(&ed->metastack);
SEQ_sequence_lookup_free(scene);
SEQ_channels_free(&ed->channels);
MEM_freeN(ed);
scene->ed = nullptr;
@ -587,10 +588,9 @@ static Sequence *seq_dupli(const Scene *scene_src,
}
}
if (seq->retiming_handles != nullptr) {
seqn->retiming_handles = static_cast<SeqRetimingHandle *>(
MEM_dupallocN(seq->retiming_handles));
seqn->retiming_handle_num = seq->retiming_handle_num;
if (seq->retiming_keys != nullptr) {
seqn->retiming_keys = static_cast<SeqRetimingKey *>(MEM_dupallocN(seq->retiming_keys));
seqn->retiming_keys_num = seq->retiming_keys_num;
}
return seqn;
@ -764,9 +764,9 @@ static bool seq_write_data_cb(Sequence *seq, void *userdata)
BLO_write_struct(writer, SeqTimelineChannel, channel);
}
if (seq->retiming_handles != nullptr) {
int size = SEQ_retiming_handles_count(seq);
BLO_write_struct_array(writer, SeqRetimingHandle, size, seq->retiming_handles);
if (seq->retiming_keys != nullptr) {
int size = SEQ_retiming_keys_count(seq);
BLO_write_struct_array(writer, SeqRetimingKey, size, seq->retiming_keys);
}
return true;
@ -845,8 +845,8 @@ static bool seq_read_data_cb(Sequence *seq, void *user_data)
BLO_read_list(reader, &seq->channels);
if (seq->retiming_handles != nullptr) {
BLO_read_data_address(reader, &seq->retiming_handles);
if (seq->retiming_keys != nullptr) {
BLO_read_data_address(reader, &seq->retiming_keys);
}
return true;

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,7 @@
#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_render.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "SEQ_transform.h"
@ -502,10 +502,10 @@ bool SEQ_time_has_still_frames(const Scene *scene, const Sequence *seq)
int SEQ_time_strip_length_get(const Scene *scene, const Sequence *seq)
{
if (SEQ_retiming_is_active(seq)) {
SeqRetimingHandle *handle_start = seq->retiming_handles;
SeqRetimingHandle *handle_end = seq->retiming_handles + (SEQ_retiming_handles_count(seq) - 1);
return handle_end->strip_frame_index / seq_time_media_playback_rate_factor_get(scene, seq) -
(handle_start->strip_frame_index) / seq_time_media_playback_rate_factor_get(scene, seq);
const SeqRetimingKey *key_start = seq->retiming_keys;
const SeqRetimingKey *key_end = seq->retiming_keys + (SEQ_retiming_keys_count(seq) - 1);
return key_end->strip_frame_index / seq_time_media_playback_rate_factor_get(scene, seq) -
(key_start->strip_frame_index) / seq_time_media_playback_rate_factor_get(scene, seq);
}
return seq->len / seq_time_media_playback_rate_factor_get(scene, seq);