VSE: Add "Set Speed" gizmo to retiming tool #108422

Merged
Richard Antalik merged 4 commits from iss/blender:set_speed into main 2023-06-12 02:30:48 +02:00
8 changed files with 308 additions and 57 deletions

View File

@ -36,6 +36,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)
@ -80,6 +81,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. */
@ -89,6 +92,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

@ -385,61 +385,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;
@ -461,8 +406,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. */
}
@ -619,3 +562,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

@ -311,6 +311,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);
@ -319,6 +320,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

@ -73,6 +73,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

@ -440,3 +440,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

@ -429,6 +429,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

@ -43,6 +43,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

@ -495,6 +495,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,