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:
committed by
Richard Antalik
parent
126bff3099
commit
f4d6edd476
@@ -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)
|
||||
|
||||
@@ -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", "");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user