VSE: Add "Set Speed" gizmo to retiming tool

This gizmo is now responsible for drawing labels with speed of retimed
segments. By clicking on the value it is possible to type in new speed,
which is more convenient, than dragging handles by hand.

Note: Due to granularity of frames, the precise value may not be
respected.

Pull Request: https://projects.blender.org/blender/blender/pulls/108422
This commit is contained in:
Richard Antalik
2023-06-12 02:30:41 +02:00
committed by Richard Antalik
parent 126bff3099
commit f4d6edd476
8 changed files with 308 additions and 57 deletions

View File

@@ -37,6 +37,7 @@ typedef struct GizmoGroup_retime {
wmGizmo *add_handle_gizmo;
wmGizmo *move_handle_gizmo;
wmGizmo *remove_handle_gizmo;
wmGizmo *speed_set_gizmo;
} GizmoGroup_retime;
static bool gizmogroup_retime_poll(const bContext *C, wmGizmoGroupType *gzgt)
@@ -81,6 +82,8 @@ static void gizmogroup_retime_setup(const bContext * /* C */, wmGizmoGroup *gzgr
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. */
@@ -90,6 +93,8 @@ static void gizmogroup_retime_setup(const bContext * /* C */, wmGizmoGroup *gzgr
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)

View File

@@ -403,61 +403,6 @@ static void retime_handle_draw(const bContext *C,
immEnd();
}
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);
int next_handle_index = SEQ_retiming_handle_index_get(seq, handle) + 1;
const SeqRetimingHandle *next_handle = &SEQ_retiming_handles_get(seq)[next_handle_index];
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];
size_t label_len;
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);
label_len = SNPRINTF_RLEN(label_str,
"%d%% - %d%%",
round_fl_to_int(prev_speed * 100.0f),
round_fl_to_int(next_speed * 100.0f));
}
else {
const float speed = SEQ_retiming_handle_speed_get(seq, next_handle);
label_len = SNPRINTF_RLEN(label_str, "%d%%", round_fl_to_int(speed * 100.0f));
}
const float width = pixels_to_view_width(C, BLF_width(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));
const float text_x = (xmin + xmax - width) / 2;
const float text_y = strip_y_rescale(seq, 0) + pixels_to_view_height(C, 5);
if (width > xmax - xmin) {
return; /* Not enough space to draw label. */
}
const uchar col[4] = {255, 255, 255, 255};
UI_view2d_text_cache_add(UI_view2d_fromcontext(C), text_x, text_y, label_str, label_len, col);
}
static void gizmo_retime_handle_draw(const bContext *C, wmGizmo *gz)
{
RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz;
@@ -488,8 +433,6 @@ static void gizmo_retime_handle_draw(const bContext *C, wmGizmo *gz)
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (const SeqRetimingHandle &handle : handles) {
retime_speed_text_draw(C, seq, &handle);
if (&handle == handles.begin()) {
continue; /* Ignore first handle. */
}
@@ -646,3 +589,183 @@ void GIZMO_GT_retime_remove(wmGizmoType *gzt)
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Speed Set Gizmo
* \{ */
size_t label_str_get(const Sequence *seq,
const SeqRetimingHandle *handle,
size_t str_len,
char *r_label_str)
{
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,
str_len,
"%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, str_len, "%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, sizeof(label_str), 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(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(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, sizeof(label_str), 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

@@ -312,6 +312,7 @@ void SEQUENCER_OT_retiming_reset(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_move(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_add(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_remove(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_segment_speed_set(struct wmOperatorType *ot);
/* sequencer_gizmo_retime.c */
void SEQUENCER_GGT_gizmo_retime(struct wmGizmoGroupType *gzgt);
@@ -320,6 +321,7 @@ void SEQUENCER_GGT_gizmo_retime(struct wmGizmoGroupType *gzgt);
void GIZMO_GT_retime_handle_add(struct wmGizmoType *gzt);
void GIZMO_GT_retime_handle(struct wmGizmoType *gzt);
void GIZMO_GT_retime_remove(struct wmGizmoType *gzt);
void GIZMO_GT_speed_set_remove(struct wmGizmoType *gzt);
#ifdef __cplusplus
}

View File

@@ -74,6 +74,7 @@ void sequencer_operatortypes(void)
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_segment_speed_set);
/* sequencer_select.c */
WM_operatortype_append(SEQUENCER_OT_select_all);

View File

@@ -461,3 +461,95 @@ void SEQUENCER_OT_retiming_handle_remove(wmOperatorType *ot)
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Set Segment Speed
* \{ */
static int sequencer_retiming_segment_speed_set_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
MutableSpan handles = SEQ_retiming_handles_get(seq);
SeqRetimingHandle *handle = &handles[RNA_int_get(op->ptr, "handle_index")];
SEQ_retiming_handle_speed_set(scene, seq, handle, RNA_float_get(op->ptr, "speed"));
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
static int sequencer_retiming_segment_speed_set_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
const Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
const Sequence *seq = ed->act_seq;
if (seq == nullptr) {
return OPERATOR_CANCELLED;
}
MutableSpan handles = SEQ_retiming_handles_get(seq);
SeqRetimingHandle *handle = nullptr;
if (RNA_struct_property_is_set(op->ptr, "handle_index")) {
const int handle_index = RNA_int_get(op->ptr, "handle_index");
BLI_assert(handle_index < handles.size());
handle = &handles[handle_index];
}
else {
handle = closest_retiming_handle_get(C, seq, event->mval[0]);
}
if (handle == nullptr) {
BKE_report(op->reports, RPT_ERROR, "No handle available");
return OPERATOR_CANCELLED;
}
RNA_float_set(op->ptr, "speed", SEQ_retiming_handle_speed_get(seq, handle) * 100.0f);
RNA_int_set(op->ptr, "handle_index", SEQ_retiming_handle_index_get(seq, handle));
return WM_operator_props_popup(C, op, event);
}
void SEQUENCER_OT_retiming_segment_speed_set(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Speed";
ot->description = "Set speed of retimed segment";
ot->idname = "SEQUENCER_OT_retiming_segment_speed_set";
/* api callbacks */
ot->invoke = sequencer_retiming_segment_speed_set_invoke;
ot->exec = sequencer_retiming_segment_speed_set_exec;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
PropertyRNA *prop = RNA_def_int(ot->srna,
"handle_index",
0,
0,
INT_MAX,
"Handle Index",
"Index of handle to be removed",
0,
INT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_float(ot->srna,
"speed",
100.0f,
0.001f,
FLT_MAX,
"Speed",
"New speed of retimed segment",
0.1f,
INT_MAX);
}
/** \} */

View File

@@ -430,6 +430,7 @@ static void sequencer_gizmos(void)
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);

View File

@@ -48,6 +48,10 @@ void SEQ_retiming_offset_handle(const struct Scene *scene,
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);

View File

@@ -535,6 +535,29 @@ float SEQ_retiming_handle_speed_get(const Sequence *seq, const SeqRetimingHandle
return speed;
}
void SEQ_retiming_handle_speed_set(const Scene *scene,
Sequence *seq,
SeqRetimingHandle *handle,
const float speed)
{
if (handle->strip_frame_index == 0) {
return;
}
const SeqRetimingHandle *handle_prev = handle - 1;
const float speed_fac = 100.0f / speed;
const int frame_index_max = seq->len;
const int frame_retimed_prev = round_fl_to_int(handle_prev->retiming_factor * frame_index_max);
const int frame_retimed = round_fl_to_int(handle->retiming_factor * frame_index_max);
const int segment_duration = frame_retimed - frame_retimed_prev;
const int new_duration = segment_duration * speed_fac;
const int offset = (handle_prev->strip_frame_index + new_duration) - handle->strip_frame_index;
SEQ_retiming_offset_handle(scene, seq, handle, offset);
}
enum eRangeType {
LINEAR = 0,
TRANSITION = 1,