VSE: Add "Set Speed" gizmo to retiming tool #108422
|
@ -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)
|
||||
|
|
|
@ -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", "");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue