VSE: Add sound strip retiming support #105071
|
@ -165,6 +165,12 @@ AUD_API void AUD_SequenceEntry_move(AUD_SequenceEntry* entry, double begin, doub
|
||||||
(*entry)->move(begin, end, skip);
|
(*entry)->move(begin, end, skip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AUD_API void AUD_SequenceEntry_setAnimationData_constant_range(AUD_SequenceEntry* entry, AUD_AnimateablePropertyType type, int frame_start, int frame_end, float* data)
|
||||||
|
{
|
||||||
|
AnimateableProperty* prop = (*entry)->getAnimProperty(static_cast<AnimateablePropertyType>(type));
|
||||||
|
prop->write_range(data, frame_start, frame_end);
|
||||||
|
}
|
||||||
|
|
||||||
AUD_API void AUD_SequenceEntry_setAnimationData(AUD_SequenceEntry* entry, AUD_AnimateablePropertyType type, int frame, float* data, char animated)
|
AUD_API void AUD_SequenceEntry_setAnimationData(AUD_SequenceEntry* entry, AUD_AnimateablePropertyType type, int frame, float* data, char animated)
|
||||||
{
|
{
|
||||||
AnimateableProperty* prop = (*entry)->getAnimProperty(static_cast<AnimateablePropertyType>(type));
|
AnimateableProperty* prop = (*entry)->getAnimProperty(static_cast<AnimateablePropertyType>(type));
|
||||||
|
|
|
@ -68,6 +68,16 @@ extern AUD_API void AUD_Sequence_remove(AUD_Sound* sequence, AUD_SequenceEntry*
|
||||||
* Writes animation data to a sequence.
|
* Writes animation data to a sequence.
|
||||||
* \param sequence The sound scene.
|
* \param sequence The sound scene.
|
||||||
* \param type The type of animation data.
|
* \param type The type of animation data.
|
||||||
|
* \param frame_start Start of the frame range.
|
||||||
|
* \param frame_end End of the frame range.
|
||||||
|
* \param data The data to write.
|
||||||
|
*/
|
||||||
|
AUD_API void AUD_SequenceEntry_setAnimationData_constant_range(AUD_SequenceEntry* entry, AUD_AnimateablePropertyType type, int frame_start, int frame_end, float* data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes animation data to a sequenced entry.
|
||||||
|
* \param entry The sequenced entry.
|
||||||
|
* \param type The type of animation data.
|
||||||
* \param frame The frame this data is for.
|
* \param frame The frame this data is for.
|
||||||
* \param data The data to write.
|
* \param data The data to write.
|
||||||
* \param animated Whether the attribute is animated.
|
* \param animated Whether the attribute is animated.
|
||||||
|
|
|
@ -112,6 +112,14 @@ public:
|
||||||
*/
|
*/
|
||||||
void write(const float* data, int position, int count);
|
void write(const float* data, int position, int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills the properties frame range with constant value and marks it animated.
|
||||||
|
* \param data The new value.
|
||||||
|
* \param position_start The start position in the animation in frames.
|
||||||
|
* \param position_end The end position in the animation in frames.
|
||||||
|
*/
|
||||||
|
void write_range(const float* data, int position_start, int position_end);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the properties value.
|
* Reads the properties value.
|
||||||
* \param position The position in the animation in frames.
|
* \param position The position in the animation in frames.
|
||||||
|
|
|
@ -63,6 +63,9 @@ private:
|
||||||
/// How many seconds are skipped at the beginning.
|
/// How many seconds are skipped at the beginning.
|
||||||
double m_skip;
|
double m_skip;
|
||||||
|
|
||||||
|
/// The FPS of the scene.
|
||||||
|
float m_fps;
|
||||||
|
|
||||||
/// Whether the entry is muted.
|
/// Whether the entry is muted.
|
||||||
bool m_muted;
|
bool m_muted;
|
||||||
|
|
||||||
|
@ -124,7 +127,7 @@ public:
|
||||||
* \param skip How much seconds should be skipped at the beginning.
|
* \param skip How much seconds should be skipped at the beginning.
|
||||||
* \param id The ID of the entry.
|
* \param id The ID of the entry.
|
||||||
*/
|
*/
|
||||||
SequenceEntry(std::shared_ptr<ISound> sound, double begin, double end, double skip, int id);
|
SequenceEntry(std::shared_ptr<ISound> sound, double begin, double end, double skip, float fps, int id);
|
||||||
virtual ~SequenceEntry();
|
virtual ~SequenceEntry();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -65,6 +65,19 @@ void AnimateableProperty::write(const float* data)
|
||||||
std::memcpy(getBuffer(), data, m_count * sizeof(float));
|
std::memcpy(getBuffer(), data, m_count * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimateableProperty::write_range(const float* data, int position_start, int position_end)
|
||||||
|
{
|
||||||
|
assureSize(position_end * m_count * sizeof(float), true);
|
||||||
|
float* buf = getBuffer();
|
||||||
|
|
||||||
|
for(int i = position_start; i < position_end; i++)
|
||||||
|
{
|
||||||
|
std::memcpy(buf + i * m_count, data, m_count * sizeof(float));
|
||||||
|
}
|
||||||
|
m_isAnimated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void AnimateableProperty::write(const float* data, int position, int count)
|
void AnimateableProperty::write(const float* data, int position, int count)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||||
|
|
|
@ -153,7 +153,7 @@ std::shared_ptr<SequenceEntry> SequenceData::add(std::shared_ptr<ISound> sound,
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||||
|
|
||||||
std::shared_ptr<SequenceEntry> entry = std::shared_ptr<SequenceEntry>(new SequenceEntry(sound, begin, end, skip, m_id++));
|
std::shared_ptr<SequenceEntry> entry = std::shared_ptr<SequenceEntry>(new SequenceEntry(sound, begin, end, skip, m_fps, m_id++));
|
||||||
|
|
||||||
m_entries.push_back(entry);
|
m_entries.push_back(entry);
|
||||||
m_entry_status++;
|
m_entry_status++;
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
AUD_NAMESPACE_BEGIN
|
AUD_NAMESPACE_BEGIN
|
||||||
|
|
||||||
SequenceEntry::SequenceEntry(std::shared_ptr<ISound> sound, double begin, double end, double skip, int id) :
|
SequenceEntry::SequenceEntry(std::shared_ptr<ISound> sound, double begin, double end, double skip, float fps, int id) :
|
||||||
m_status(0),
|
m_status(0),
|
||||||
m_pos_status(1),
|
m_pos_status(1),
|
||||||
m_sound_status(0),
|
m_sound_status(0),
|
||||||
|
@ -31,6 +31,7 @@ SequenceEntry::SequenceEntry(std::shared_ptr<ISound> sound, double begin, double
|
||||||
m_begin(begin),
|
m_begin(begin),
|
||||||
m_end(end),
|
m_end(end),
|
||||||
m_skip(skip),
|
m_skip(skip),
|
||||||
|
m_fps(fps),
|
||||||
m_muted(false),
|
m_muted(false),
|
||||||
m_relative(true),
|
m_relative(true),
|
||||||
m_volume_max(1.0f),
|
m_volume_max(1.0f),
|
||||||
|
|
|
@ -241,10 +241,30 @@ bool SequenceHandle::seek(double position)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::lock_guard<ILockable> lock(*m_entry);
|
std::lock_guard<ILockable> lock(*m_entry);
|
||||||
double seekpos = position - m_entry->m_begin;
|
float seek_frame = (position - m_entry->m_begin) * m_entry->m_fps;
|
||||||
if(seekpos < 0)
|
if(seek_frame < 0)
|
||||||
seekpos = 0;
|
seek_frame = 0;
|
||||||
seekpos += m_entry->m_skip;
|
seek_frame += m_entry->m_skip * m_entry->m_fps;
|
||||||
|
|
||||||
|
AnimateableProperty* pitch_property = m_entry->getAnimProperty(AP_PITCH);
|
||||||
|
|
||||||
|
float target_frame = 0;
|
||||||
|
if(pitch_property != nullptr)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < seek_frame; i++)
|
||||||
|
{
|
||||||
|
float pitch;
|
||||||
|
pitch_property->read(i, &pitch);
|
||||||
|
target_frame += pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target_frame = seek_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
double seekpos = target_frame / m_entry->m_fps;
|
||||||
|
|
||||||
m_handle->setPitch(1.0f);
|
m_handle->setPitch(1.0f);
|
||||||
m_handle->seek(seekpos);
|
m_handle->seek(seekpos);
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -1872,12 +1872,6 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel):
|
||||||
split.label(text="Channel")
|
split.label(text="Channel")
|
||||||
split.prop(strip, "channel", text="")
|
split.prop(strip, "channel", text="")
|
||||||
|
|
||||||
if not is_effect:
|
|
||||||
split = layout.split(factor=0.5 + max_factor)
|
|
||||||
split.alignment = 'RIGHT'
|
|
||||||
split.label(text="Speed Factor")
|
|
||||||
split.prop(strip, "speed_factor", text="")
|
|
||||||
|
|
||||||
sub = layout.column(align=True)
|
sub = layout.column(align=True)
|
||||||
split = sub.split(factor=0.5 + max_factor, align=True)
|
split = sub.split(factor=0.5 + max_factor, align=True)
|
||||||
split.alignment = 'RIGHT'
|
split.alignment = 'RIGHT'
|
||||||
|
|
|
@ -2620,6 +2620,18 @@ class _defs_sequencer_generic:
|
||||||
options={"KEYMAP_FALLBACK"},
|
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
|
@ToolDef.from_fn
|
||||||
def sample():
|
def sample():
|
||||||
return dict(
|
return dict(
|
||||||
|
@ -3308,6 +3320,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
|
||||||
"SEQUENCER": [
|
"SEQUENCER": [
|
||||||
*_tools_select,
|
*_tools_select,
|
||||||
_defs_sequencer_generic.blade,
|
_defs_sequencer_generic.blade,
|
||||||
|
_defs_sequencer_generic.retime,
|
||||||
],
|
],
|
||||||
"SEQUENCER_PREVIEW": [
|
"SEQUENCER_PREVIEW": [
|
||||||
*_tools_select,
|
*_tools_select,
|
||||||
|
|
|
@ -154,6 +154,11 @@ void BKE_sound_set_scene_sound_volume(void *handle, float volume, char animated)
|
||||||
|
|
||||||
void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated);
|
void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated);
|
||||||
|
|
||||||
|
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle,
|
||||||
|
int frame_start,
|
||||||
|
int frame_end,
|
||||||
|
float pitch);
|
||||||
|
|
||||||
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated);
|
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated);
|
||||||
|
|
||||||
void BKE_sound_update_sequencer(struct Main *main, struct bSound *sound);
|
void BKE_sound_update_sequencer(struct Main *main, struct bSound *sound);
|
||||||
|
|
|
@ -819,6 +819,15 @@ void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated)
|
||||||
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, sound_cfra, &pitch, animated);
|
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, sound_cfra, &pitch, animated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle,
|
||||||
|
int frame_start,
|
||||||
|
int frame_end,
|
||||||
|
float pitch)
|
||||||
|
{
|
||||||
|
AUD_SequenceEntry_setAnimationData_constant_range(
|
||||||
|
handle, AUD_AP_PITCH, frame_start, frame_end, &pitch);
|
||||||
|
}
|
||||||
|
|
||||||
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated)
|
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated)
|
||||||
{
|
{
|
||||||
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PANNING, sound_cfra, &pan, animated);
|
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PANNING, sound_cfra, &pan, animated);
|
||||||
|
|
|
@ -77,6 +77,7 @@
|
||||||
|
|
||||||
#include "SEQ_channels.h"
|
#include "SEQ_channels.h"
|
||||||
#include "SEQ_iterator.h"
|
#include "SEQ_iterator.h"
|
||||||
|
#include "SEQ_retiming.h"
|
||||||
#include "SEQ_sequencer.h"
|
#include "SEQ_sequencer.h"
|
||||||
#include "SEQ_time.h"
|
#include "SEQ_time.h"
|
||||||
|
|
||||||
|
@ -685,6 +686,25 @@ static bool seq_speed_factor_set(Sequence *seq, void *user_data)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool do_versions_sequencer_init_retiming_tool_data(Sequence *seq, void *user_data)
|
||||||
|
{
|
||||||
|
const Scene *scene = static_cast<const Scene *>(user_data);
|
||||||
|
|
||||||
|
if (seq->speed_factor == 1 || !SEQ_retiming_is_allowed(seq)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int content_length = SEQ_time_strip_length_get(scene, seq);
|
||||||
|
|
||||||
|
SEQ_retiming_data_ensure(scene, seq);
|
||||||
|
|
||||||
|
SeqRetimingHandle *handle = &seq->retiming_handles[seq->retiming_handle_num - 1];
|
||||||
|
handle->strip_frame_index = round_fl_to_int(content_length / seq->speed_factor);
|
||||||
|
seq->speed_factor = 0.0f;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void version_geometry_nodes_replace_transfer_attribute_node(bNodeTree *ntree)
|
static void version_geometry_nodes_replace_transfer_attribute_node(bNodeTree *ntree)
|
||||||
{
|
{
|
||||||
using namespace blender;
|
using namespace blender;
|
||||||
|
@ -1205,6 +1225,16 @@ void do_versions_after_linking_300(Main *bmain, ReportList * /*reports*/)
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
/* Keep this block, even when empty. */
|
/* Keep this block, even when empty. */
|
||||||
|
|
||||||
|
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||||
|
Editing *ed = SEQ_editing_get(scene);
|
||||||
|
if (ed == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEQ_for_each_callback(
|
||||||
|
&scene->ed->seqbase, do_versions_sequencer_init_retiming_tool_data, scene);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -867,6 +867,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
|
||||||
ops.sculpt.mask_by_color
|
ops.sculpt.mask_by_color
|
||||||
ops.sculpt.mesh_filter
|
ops.sculpt.mesh_filter
|
||||||
ops.sequencer.blade
|
ops.sequencer.blade
|
||||||
|
ops.sequencer.retime
|
||||||
ops.transform.bone_envelope
|
ops.transform.bone_envelope
|
||||||
ops.transform.bone_size
|
ops.transform.bone_size
|
||||||
ops.transform.edge_slide
|
ops.transform.edge_slide
|
||||||
|
|
|
@ -35,10 +35,13 @@ set(SRC
|
||||||
sequencer_drag_drop.c
|
sequencer_drag_drop.c
|
||||||
sequencer_draw.c
|
sequencer_draw.c
|
||||||
sequencer_edit.c
|
sequencer_edit.c
|
||||||
|
sequencer_gizmo_retime.cc
|
||||||
|
sequencer_gizmo_retime_type.cc
|
||||||
sequencer_modifier.c
|
sequencer_modifier.c
|
||||||
sequencer_ops.c
|
sequencer_ops.c
|
||||||
sequencer_preview.c
|
sequencer_preview.c
|
||||||
sequencer_proxy.c
|
sequencer_proxy.c
|
||||||
|
sequencer_retiming.cc
|
||||||
sequencer_scopes.c
|
sequencer_scopes.c
|
||||||
sequencer_select.c
|
sequencer_select.c
|
||||||
sequencer_thumbnails.c
|
sequencer_thumbnails.c
|
||||||
|
|
|
@ -413,7 +413,6 @@ static void draw_seq_waveform_overlay(
|
||||||
|
|
||||||
const float frames_per_pixel = BLI_rctf_size_x(®ion->v2d.cur) / region->winx;
|
const float frames_per_pixel = BLI_rctf_size_x(®ion->v2d.cur) / region->winx;
|
||||||
const float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
|
const float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
|
||||||
float samples_per_pixel = samples_per_frame * frames_per_pixel;
|
|
||||||
|
|
||||||
/* Align strip start with nearest pixel to prevent waveform flickering. */
|
/* Align strip start with nearest pixel to prevent waveform flickering. */
|
||||||
const float x1_aligned = align_frame_with_pixel(x1, frames_per_pixel);
|
const float x1_aligned = align_frame_with_pixel(x1, frames_per_pixel);
|
||||||
|
@ -439,15 +438,17 @@ static void draw_seq_waveform_overlay(
|
||||||
size_t wave_data_len = 0;
|
size_t wave_data_len = 0;
|
||||||
|
|
||||||
/* Offset must be also aligned, otherwise waveform flickers when moving left handle. */
|
/* Offset must be also aligned, otherwise waveform flickers when moving left handle. */
|
||||||
const float strip_offset = align_frame_with_pixel(seq->startofs + seq->anim_startofs,
|
float start_frame = SEQ_time_left_handle_frame_get(scene, seq);
|
||||||
frames_per_pixel);
|
|
||||||
float start_sample = strip_offset * samples_per_frame;
|
|
||||||
start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
|
|
||||||
/* Add off-screen part of strip to offset. */
|
/* Add off-screen part of strip to offset. */
|
||||||
start_sample += (frame_start - x1_aligned) * samples_per_frame;
|
start_frame += (frame_start - x1_aligned);
|
||||||
|
start_frame += seq->sound->offset_time / FPS;
|
||||||
|
|
||||||
for (int i = 0; i < pixels_to_draw; i++) {
|
for (int i = 0; i < pixels_to_draw; i++) {
|
||||||
float sample = start_sample + i * samples_per_pixel;
|
float timeline_frame = start_frame + i * frames_per_pixel;
|
||||||
|
/* TODO: Use linear interpolation between frames to avoid bad drawing quality. */
|
||||||
|
float frame_index = SEQ_give_frame_index(scene, seq, timeline_frame);
|
||||||
|
float sample = frame_index * samples_per_frame;
|
||||||
int sample_index = round_fl_to_int(sample);
|
int sample_index = round_fl_to_int(sample);
|
||||||
|
|
||||||
if (sample_index < 0) {
|
if (sample_index < 0) {
|
||||||
|
@ -468,6 +469,8 @@ static void draw_seq_waveform_overlay(
|
||||||
value_min = (1.0f - f) * value_min + f * waveform->data[sample_index * 3 + 3];
|
value_min = (1.0f - f) * value_min + f * waveform->data[sample_index * 3 + 3];
|
||||||
value_max = (1.0f - f) * value_max + f * waveform->data[sample_index * 3 + 4];
|
value_max = (1.0f - f) * value_max + f * waveform->data[sample_index * 3 + 4];
|
||||||
rms = (1.0f - f) * rms + f * waveform->data[sample_index * 3 + 5];
|
rms = (1.0f - f) * rms + f * waveform->data[sample_index * 3 + 5];
|
||||||
|
|
||||||
|
float samples_per_pixel = samples_per_frame * frames_per_pixel;
|
||||||
if (samples_per_pixel > 1.0f) {
|
if (samples_per_pixel > 1.0f) {
|
||||||
/* We need to sum up the values we skip over until the next step. */
|
/* We need to sum up the values we skip over until the next step. */
|
||||||
float next_pos = sample + samples_per_pixel;
|
float next_pos = sample + samples_per_pixel;
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Copyright 2022 Blender Foundation. All rights reserved. */
|
||||||
|
|
||||||
|
/** \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.h"
|
||||||
|
|
||||||
|
#include "UI_resources.h"
|
||||||
|
|
||||||
|
#include "WM_api.h"
|
||||||
|
#include "WM_types.h"
|
||||||
|
|
||||||
|
#include "ED_gizmo_library.h"
|
||||||
|
#include "ED_gizmo_utils.h"
|
||||||
|
|
||||||
|
#include "SEQ_iterator.h"
|
||||||
|
#include "SEQ_retiming.h"
|
||||||
|
#include "SEQ_retiming.hh"
|
||||||
|
#include "SEQ_sequencer.h"
|
||||||
|
|
||||||
|
/* Own include. */
|
||||||
|
#include "sequencer_intern.h"
|
||||||
|
|
||||||
|
typedef struct GizmoGroup_retime {
|
||||||
|
wmGizmo *add_handle_gizmo;
|
||||||
|
wmGizmo *move_handle_gizmo;
|
||||||
|
wmGizmo *remove_handle_gizmo;
|
||||||
|
} GizmoGroup_retime;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \} */
|
|
@ -0,0 +1,568 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Copyright 2022 Blender Foundation. All rights reserved. */
|
||||||
|
|
||||||
|
/** \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.h"
|
||||||
|
#include "RNA_define.h"
|
||||||
|
#include "RNA_enum_types.h"
|
||||||
|
|
||||||
|
#include "WM_api.h"
|
||||||
|
#include "WM_types.h"
|
||||||
|
|
||||||
|
#include "ED_gizmo_library.h"
|
||||||
|
#include "ED_screen.h"
|
||||||
|
#include "ED_view3d.h"
|
||||||
|
|
||||||
|
#include "UI_interface.h"
|
||||||
|
#include "UI_interface_icons.h"
|
||||||
|
#include "UI_resources.h"
|
||||||
|
#include "UI_view2d.h"
|
||||||
|
|
||||||
|
#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.h"
|
||||||
|
|
||||||
|
using blender::MutableSpan;
|
||||||
|
|
||||||
|
#define REMOVE_GIZMO_HEIGHT 14.0f * U.dpi_fac /* Pixels from bottom of strip. */
|
||||||
|
#define RETIME_HANDLE_TRIANGLE_SIZE 14.0f * U.dpi_fac /* Size in pixels. */
|
||||||
|
#define RETIME_HANDLE_MOUSEOVER_THRESHOLD 16.0f * U.dpi_fac /* Size in pixels. */
|
||||||
|
#define RETIME_BUTTON_SIZE 0.6f /* Factor based on icon size. */
|
||||||
|
|
||||||
|
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 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_time_start_frame_get(seq) + handle->strip_frame_index + (is_last_handle ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SeqRetimingHandle *mouse_over_handle_get(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(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)
|
||||||
|
{
|
||||||
|
rctf rect = strip_box_get(C, seq);
|
||||||
|
rect.ymax = rect.ymin + REMOVE_GIZMO_HEIGHT;
|
||||||
|
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
|
||||||
|
* \{ */
|
||||||
|
|
||||||
|
typedef struct RetimeButtonGizmo {
|
||||||
|
wmGizmo gizmo;
|
||||||
|
int icon_id;
|
||||||
|
const Sequence *seq_under_mouse;
|
||||||
|
bool is_mouse_over_gizmo;
|
||||||
|
} RetimeButtonGizmo;
|
||||||
|
|
||||||
|
typedef struct ButtonDimensions {
|
||||||
|
float height;
|
||||||
|
float width;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
} ButtonDimensions;
|
||||||
|
|
||||||
|
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) * U.dpi_fac;
|
||||||
|
const float icon_width = UI_icon_get_width(gizmo->icon_id) * U.dpi_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 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
|
||||||
|
* \{ */
|
||||||
|
|
||||||
|
typedef struct RetimeHandleMoveGizmo {
|
||||||
|
wmGizmo gizmo;
|
||||||
|
const Sequence *mouse_over_seq;
|
||||||
|
int mouse_over_handle_x;
|
||||||
|
} RetimeHandleMoveGizmo;
|
||||||
|
|
||||||
|
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(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 = RETIME_HANDLE_TRIANGLE_SIZE;
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (seq == gizmo->mouse_over_seq && handle_x == gizmo->mouse_over_handle_x) {
|
||||||
|
immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
immUniformColor4f(0.65f, 0.65f, 0.65f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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(seq, next_handle) < start_frame || handle_x_get(seq, handle) > end_frame) {
|
||||||
|
return; /* Label out of strip bounds. */
|
||||||
|
}
|
||||||
|
|
||||||
|
const float speed = SEQ_retiming_handle_speed_get(scene, seq, next_handle);
|
||||||
|
|
||||||
|
char label_str[20];
|
||||||
|
const size_t label_len = BLI_snprintf_rlen(
|
||||||
|
label_str, sizeof(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(seq, handle));
|
||||||
|
const float xmax = min_ff(SEQ_time_right_handle_frame_get(scene, seq),
|
||||||
|
handle_x_get(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)
|
||||||
|
{
|
||||||
|
const RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz;
|
||||||
|
const View2D *v2d = UI_view2d_fromcontext(C);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
retime_speed_text_draw(C, seq, &handle);
|
||||||
|
|
||||||
|
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(CTX_data_scene(C), seq);
|
||||||
|
const SeqRetimingHandle *handle = mouse_over_handle_get(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(seq, handle) == SEQ_time_left_handle_frame_get(scene, seq) ||
|
||||||
|
handle_index == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rctf strip_box = strip_box_get(C, seq);
|
||||||
|
BLI_rctf_resize_x(&strip_box, BLI_rctf_size_x(&strip_box) + 2 * RETIME_HANDLE_TRIANGLE_SIZE);
|
||||||
|
if (!mouse_is_inside_box(&strip_box, mval)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gizmo->mouse_over_seq = seq;
|
||||||
|
gizmo->mouse_over_handle_x = handle_x_get(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(CTX_data_scene(C), seq);
|
||||||
|
const SeqRetimingHandle *handle = mouse_over_handle_get(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(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. */
|
||||||
|
}
|
||||||
|
|
||||||
|
rctf box = remove_box_get(C, seq);
|
||||||
|
|
||||||
|
BLI_rctf_resize_x(&box, BLI_rctf_size_x(&box) + 2 * RETIME_HANDLE_TRIANGLE_SIZE);
|
||||||
|
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", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \} */
|
|
@ -10,11 +10,17 @@
|
||||||
#include "DNA_sequence_types.h"
|
#include "DNA_sequence_types.h"
|
||||||
#include "RNA_access.h"
|
#include "RNA_access.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Internal exports only. */
|
/* Internal exports only. */
|
||||||
|
|
||||||
struct ARegion;
|
struct ARegion;
|
||||||
struct ARegionType;
|
struct ARegionType;
|
||||||
struct Depsgraph;
|
struct Depsgraph;
|
||||||
|
struct wmGizmoGroupType;
|
||||||
|
struct wmGizmoType;
|
||||||
struct Main;
|
struct Main;
|
||||||
struct Scene;
|
struct Scene;
|
||||||
struct SeqCollection;
|
struct SeqCollection;
|
||||||
|
@ -298,3 +304,21 @@ int sequencer_image_seq_get_minmax_frame(struct wmOperator *op,
|
||||||
int *r_numdigits);
|
int *r_numdigits);
|
||||||
void sequencer_image_seq_reserve_frames(
|
void sequencer_image_seq_reserve_frames(
|
||||||
struct wmOperator *op, struct StripElem *se, int len, int minframe, int numdigits);
|
struct wmOperator *op, struct StripElem *se, int len, int minframe, int numdigits);
|
||||||
|
|
||||||
|
/* sequencer_retiming.c */
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* sequencer_gizmo_retime.c */
|
||||||
|
void SEQUENCER_GGT_gizmo_retime(struct wmGizmoGroupType *gzgt);
|
||||||
|
|
||||||
|
/* sequencer_gizmo_retime_type.c */
|
||||||
|
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);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -68,6 +68,12 @@ void sequencer_operatortypes(void)
|
||||||
WM_operatortype_append(SEQUENCER_OT_cursor_set);
|
WM_operatortype_append(SEQUENCER_OT_cursor_set);
|
||||||
WM_operatortype_append(SEQUENCER_OT_scene_frame_range_update);
|
WM_operatortype_append(SEQUENCER_OT_scene_frame_range_update);
|
||||||
|
|
||||||
|
/* sequencer_retiming.c */
|
||||||
|
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);
|
||||||
|
|
||||||
/* sequencer_select.c */
|
/* sequencer_select.c */
|
||||||
WM_operatortype_append(SEQUENCER_OT_select_all);
|
WM_operatortype_append(SEQUENCER_OT_select_all);
|
||||||
WM_operatortype_append(SEQUENCER_OT_select);
|
WM_operatortype_append(SEQUENCER_OT_select);
|
||||||
|
|
|
@ -0,0 +1,377 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup spseq
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "BLI_blenlib.h"
|
||||||
|
#include "BLI_math.h"
|
||||||
|
|
||||||
|
#include "DNA_anim_types.h"
|
||||||
|
#include "DNA_scene_types.h"
|
||||||
|
|
||||||
|
#include "BKE_context.h"
|
||||||
|
#include "BKE_report.h"
|
||||||
|
#include "BKE_scene.h"
|
||||||
|
|
||||||
|
#include "SEQ_iterator.h"
|
||||||
|
#include "SEQ_relations.h"
|
||||||
|
#include "SEQ_retiming.h"
|
||||||
|
#include "SEQ_retiming.hh"
|
||||||
|
#include "SEQ_sequencer.h"
|
||||||
|
#include "SEQ_time.h"
|
||||||
|
#include "SEQ_transform.h"
|
||||||
|
|
||||||
|
#include "WM_api.h"
|
||||||
|
|
||||||
|
#include "RNA_define.h"
|
||||||
|
|
||||||
|
#include "UI_interface.h"
|
||||||
|
#include "UI_view2d.h"
|
||||||
|
|
||||||
|
#include "DEG_depsgraph.h"
|
||||||
|
|
||||||
|
/* Own include. */
|
||||||
|
#include "sequencer_intern.h"
|
||||||
|
|
||||||
|
using blender::MutableSpan;
|
||||||
|
|
||||||
|
static bool retiming_poll(bContext *C)
|
||||||
|
{
|
||||||
|
if (!sequencer_edit_poll(C)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Editing *ed = SEQ_editing_get(CTX_data_scene(C));
|
||||||
|
Sequence *seq = ed->act_seq;
|
||||||
|
|
||||||
|
if (seq != nullptr && !SEQ_retiming_is_allowed(seq)) {
|
||||||
|
CTX_wm_operator_poll_msg_set(C, "This strip type can not be retimed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void retiming_handle_overlap(Scene *scene, Sequence *seq)
|
||||||
|
{
|
||||||
|
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
|
||||||
|
SeqCollection *strips = SEQ_collection_create(__func__);
|
||||||
|
SEQ_collection_append_strip(seq, strips);
|
||||||
|
SeqCollection *dependant = SEQ_collection_create(__func__);
|
||||||
|
SEQ_collection_expand(scene, seqbase, strips, SEQ_query_strip_effect_chain);
|
||||||
|
SEQ_collection_remove_strip(seq, dependant);
|
||||||
|
SEQ_transform_handle_overlap(scene, seqbase, strips, dependant, true);
|
||||||
|
SEQ_collection_free(strips);
|
||||||
|
SEQ_collection_free(dependant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------- */
|
||||||
|
/** \name Retiming Reset
|
||||||
|
* \{ */
|
||||||
|
|
||||||
|
static int sequencer_retiming_reset_exec(bContext *C, wmOperator * /* op */)
|
||||||
|
{
|
||||||
|
Scene *scene = CTX_data_scene(C);
|
||||||
|
const Editing *ed = SEQ_editing_get(scene);
|
||||||
|
Sequence *seq = ed->act_seq;
|
||||||
|
|
||||||
|
SEQ_retiming_data_clear(seq);
|
||||||
|
|
||||||
|
retiming_handle_overlap(scene, seq);
|
||||||
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQUENCER_OT_retiming_reset(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Reset Retiming";
|
||||||
|
ot->description = "Reset strip retiming";
|
||||||
|
ot->idname = "SEQUENCER_OT_retiming_reset";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->exec = sequencer_retiming_reset_exec;
|
||||||
|
ot->poll = retiming_poll;
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \} */
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/** \name Retiming Move Handle
|
||||||
|
* \{ */
|
||||||
|
|
||||||
|
static SeqRetimingHandle *closest_retiming_handle_get(const bContext *C,
|
||||||
|
const Sequence *seq,
|
||||||
|
const float mouse_x)
|
||||||
|
{
|
||||||
|
const View2D *v2d = UI_view2d_fromcontext(C);
|
||||||
|
int best_distance = INT_MAX;
|
||||||
|
SeqRetimingHandle *closest_handle = nullptr;
|
||||||
|
|
||||||
|
const float distance_threshold = UI_view2d_region_to_view_x(v2d, 10);
|
||||||
|
const float mouse_x_view = UI_view2d_region_to_view_x(v2d, mouse_x);
|
||||||
|
|
||||||
|
for (int i = 0; i < SEQ_retiming_handles_count(seq); i++) {
|
||||||
|
SeqRetimingHandle *handle = seq->retiming_handles + i;
|
||||||
|
const int distance = round_fl_to_int(fabsf(handle->strip_frame_index - mouse_x_view));
|
||||||
|
|
||||||
|
if (distance < distance_threshold && distance < best_distance) {
|
||||||
|
best_distance = distance;
|
||||||
|
closest_handle = handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closest_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sequencer_retiming_handle_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||||
|
{
|
||||||
|
Scene *scene = CTX_data_scene(C);
|
||||||
|
const Editing *ed = SEQ_editing_get(scene);
|
||||||
|
Sequence *seq = ed->act_seq;
|
||||||
|
|
||||||
|
int handle_index = 0;
|
||||||
|
if (RNA_struct_property_is_set(op->ptr, "handle_index")) {
|
||||||
|
handle_index = RNA_int_get(op->ptr, "handle_index");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure retiming handle at left handle position. This way user gets more predictable result
|
||||||
|
* when strips have offsets. */
|
||||||
|
const int left_handle_frame = SEQ_time_left_handle_frame_get(scene, seq);
|
||||||
|
if (SEQ_retiming_add_handle(seq, left_handle_frame) != nullptr) {
|
||||||
|
handle_index++; /* Advance index, because new handle was created. */
|
||||||
|
}
|
||||||
|
|
||||||
|
MutableSpan handles = SEQ_retiming_handles_get(seq);
|
||||||
|
SeqRetimingHandle *handle = nullptr;
|
||||||
|
if (RNA_struct_property_is_set(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
op->customdata = handle;
|
||||||
|
WM_event_add_modal_handler(C, op);
|
||||||
|
return OPERATOR_RUNNING_MODAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sequencer_retiming_handle_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||||
|
{
|
||||||
|
Scene *scene = CTX_data_scene(C);
|
||||||
|
const ARegion *region = CTX_wm_region(C);
|
||||||
|
const View2D *v2d = ®ion->v2d;
|
||||||
|
const Editing *ed = SEQ_editing_get(scene);
|
||||||
|
Sequence *seq = ed->act_seq;
|
||||||
|
|
||||||
|
switch (event->type) {
|
||||||
|
case MOUSEMOVE: {
|
||||||
|
float mouse_x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
SeqRetimingHandle *handle = (SeqRetimingHandle *)op->customdata;
|
||||||
|
SeqRetimingHandle *handle_prev = handle - 1;
|
||||||
|
|
||||||
|
/* Limit retiming handle movement. */
|
||||||
|
int xmin = SEQ_time_start_frame_get(seq) + handle_prev->strip_frame_index + 1;
|
||||||
|
mouse_x = max_ff(xmin, mouse_x);
|
||||||
|
offset = mouse_x - (SEQ_time_start_frame_get(seq) + handle->strip_frame_index);
|
||||||
|
|
||||||
|
SEQ_retiming_offset_handle(scene, seq, handle, offset);
|
||||||
|
|
||||||
|
SEQ_relations_invalidate_cache_raw(scene, seq);
|
||||||
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
||||||
|
return OPERATOR_RUNNING_MODAL;
|
||||||
|
}
|
||||||
|
case LEFTMOUSE:
|
||||||
|
case EVT_RETKEY:
|
||||||
|
case EVT_SPACEKEY: {
|
||||||
|
retiming_handle_overlap(scene, seq);
|
||||||
|
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
|
||||||
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EVT_ESCKEY:
|
||||||
|
case RIGHTMOUSE: {
|
||||||
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OPERATOR_RUNNING_MODAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQUENCER_OT_retiming_handle_move(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Move Retiming Handle";
|
||||||
|
ot->description = "Move retiming handle";
|
||||||
|
ot->idname = "SEQUENCER_OT_retiming_handle_move";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->invoke = sequencer_retiming_handle_move_invoke;
|
||||||
|
ot->modal = sequencer_retiming_handle_move_modal;
|
||||||
|
ot->poll = retiming_poll;
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
RNA_def_int(ot->srna,
|
||||||
|
"handle_index",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
INT_MAX,
|
||||||
|
"Handle Index",
|
||||||
|
"Index of handle to be moved",
|
||||||
|
0,
|
||||||
|
INT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \} */
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/** \name Retiming Add Handle
|
||||||
|
* \{ */
|
||||||
|
|
||||||
|
static int sequesequencer_retiming_handle_add_exec(bContext *C, wmOperator *op)
|
||||||
|
{
|
||||||
|
Scene *scene = CTX_data_scene(C);
|
||||||
|
const Editing *ed = SEQ_editing_get(scene);
|
||||||
|
Sequence *seq = ed->act_seq;
|
||||||
|
|
||||||
|
float timeline_frame;
|
||||||
|
if (RNA_struct_property_is_set(op->ptr, "timeline_frame")) {
|
||||||
|
timeline_frame = RNA_int_get(op->ptr, "timeline_frame");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timeline_frame = BKE_scene_frame_get(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inserted = false;
|
||||||
|
const float end_frame = seq->start + SEQ_time_strip_length_get(scene, seq);
|
||||||
|
if (seq->start < timeline_frame && end_frame > timeline_frame) {
|
||||||
|
SEQ_retiming_add_handle(seq, timeline_frame);
|
||||||
|
inserted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
||||||
|
|
||||||
|
return inserted ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQUENCER_OT_retiming_handle_add(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Add Retiming Handle";
|
||||||
|
ot->description = "Add retiming Handle";
|
||||||
|
ot->idname = "SEQUENCER_OT_retiming_handle_add";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->exec = sequesequencer_retiming_handle_add_exec;
|
||||||
|
ot->poll = retiming_poll;
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
RNA_def_int(ot->srna,
|
||||||
|
"timeline_frame",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
INT_MAX,
|
||||||
|
"Timeline Frame",
|
||||||
|
"Frame where handle will be added",
|
||||||
|
0,
|
||||||
|
INT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \} */
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/** \name Retiming Remove Handle
|
||||||
|
* \{ */
|
||||||
|
|
||||||
|
static int sequencer_retiming_handle_remove_exec(bContext *C, wmOperator *op)
|
||||||
|
{
|
||||||
|
Scene *scene = CTX_data_scene(C);
|
||||||
|
const Editing *ed = SEQ_editing_get(scene);
|
||||||
|
Sequence *seq = ed->act_seq;
|
||||||
|
|
||||||
|
SeqRetimingHandle *handle = (SeqRetimingHandle *)op->customdata;
|
||||||
|
SEQ_retiming_remove_handle(seq, handle);
|
||||||
|
SEQ_relations_invalidate_cache_raw(scene, seq);
|
||||||
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sequencer_retiming_handle_remove_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
op->customdata = handle;
|
||||||
|
return sequencer_retiming_handle_remove_exec(C, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQUENCER_OT_retiming_handle_remove(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Remove Retiming Handle";
|
||||||
|
ot->description = "Remove retiming handle";
|
||||||
|
ot->idname = "SEQUENCER_OT_retiming_handle_remove";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->invoke = sequencer_retiming_handle_remove_invoke;
|
||||||
|
ot->exec = sequencer_retiming_handle_remove_exec;
|
||||||
|
ot->poll = retiming_poll;
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
/* properties */
|
||||||
|
RNA_def_int(ot->srna,
|
||||||
|
"handle_index",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
INT_MAX,
|
||||||
|
"Handle Index",
|
||||||
|
"Index of handle to be removed",
|
||||||
|
0,
|
||||||
|
INT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \} */
|
|
@ -494,10 +494,15 @@ static void sequencer_gizmos(void)
|
||||||
wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(
|
wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(
|
||||||
&(const struct wmGizmoMapType_Params){SPACE_SEQ, RGN_TYPE_PREVIEW});
|
&(const struct wmGizmoMapType_Params){SPACE_SEQ, RGN_TYPE_PREVIEW});
|
||||||
|
|
||||||
|
WM_gizmotype_append(GIZMO_GT_retime_handle_add);
|
||||||
|
WM_gizmotype_append(GIZMO_GT_retime_handle);
|
||||||
|
WM_gizmotype_append(GIZMO_GT_retime_remove);
|
||||||
|
|
||||||
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d);
|
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d);
|
||||||
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_translate);
|
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_translate);
|
||||||
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_resize);
|
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_resize);
|
||||||
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_rotate);
|
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_rotate);
|
||||||
|
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo_retime);
|
||||||
|
|
||||||
WM_gizmogrouptype_append_and_link(gzmap_type, SEQUENCER_GGT_navigate);
|
WM_gizmogrouptype_append_and_link(gzmap_type, SEQUENCER_GGT_navigate);
|
||||||
}
|
}
|
||||||
|
@ -645,6 +650,7 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
|
||||||
case ND_SEQUENCER:
|
case ND_SEQUENCER:
|
||||||
case ND_RENDER_RESULT:
|
case ND_RENDER_RESULT:
|
||||||
ED_region_tag_redraw(region);
|
ED_region_tag_redraw(region);
|
||||||
|
WM_gizmomap_tag_refresh(region->gizmo_map);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -658,6 +664,7 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
|
||||||
case NC_SPACE:
|
case NC_SPACE:
|
||||||
if (wmn->data == ND_SPACE_SEQUENCER) {
|
if (wmn->data == ND_SPACE_SEQUENCER) {
|
||||||
ED_region_tag_redraw(region);
|
ED_region_tag_redraw(region);
|
||||||
|
WM_gizmomap_tag_refresh(region->gizmo_map);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NC_ID:
|
case NC_ID:
|
||||||
|
@ -668,6 +675,7 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
|
||||||
case NC_SCREEN:
|
case NC_SCREEN:
|
||||||
if (ELEM(wmn->data, ND_ANIMPLAY)) {
|
if (ELEM(wmn->data, ND_ANIMPLAY)) {
|
||||||
ED_region_tag_redraw(region);
|
ED_region_tag_redraw(region);
|
||||||
|
WM_gizmomap_tag_refresh(region->gizmo_map);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1069,7 +1077,6 @@ void ED_spacetype_sequencer(void)
|
||||||
art->on_view2d_changed = sequencer_main_region_view2d_changed;
|
art->on_view2d_changed = sequencer_main_region_view2d_changed;
|
||||||
art->listener = sequencer_main_region_listener;
|
art->listener = sequencer_main_region_listener;
|
||||||
art->message_subscribe = sequencer_main_region_message_subscribe;
|
art->message_subscribe = sequencer_main_region_message_subscribe;
|
||||||
/* NOTE: inclusion of #ED_KEYMAP_GIZMO is currently for scripts and isn't used by default. */
|
|
||||||
art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES |
|
art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES |
|
||||||
ED_KEYMAP_ANIMATION;
|
ED_KEYMAP_ANIMATION;
|
||||||
BLI_addhead(&st->regiontypes, art);
|
BLI_addhead(&st->regiontypes, art);
|
||||||
|
|
|
@ -120,6 +120,12 @@ typedef struct Strip {
|
||||||
ColorManagedColorspaceSettings colorspace_settings;
|
ColorManagedColorspaceSettings colorspace_settings;
|
||||||
} Strip;
|
} Strip;
|
||||||
|
|
||||||
|
typedef struct SeqRetimingHandle {
|
||||||
|
int strip_frame_index;
|
||||||
|
int _pad0[2];
|
||||||
|
float retiming_factor; /* Value between 0-1 mapped to original content range. */
|
||||||
|
} SeqRetimingHandle;
|
||||||
|
|
||||||
typedef struct SequenceRuntime {
|
typedef struct SequenceRuntime {
|
||||||
SessionUUID session_uuid;
|
SessionUUID session_uuid;
|
||||||
} SequenceRuntime;
|
} SequenceRuntime;
|
||||||
|
@ -164,12 +170,12 @@ typedef struct Sequence {
|
||||||
float startstill, endstill;
|
float startstill, endstill;
|
||||||
/** Machine: the strip channel */
|
/** Machine: the strip channel */
|
||||||
int machine;
|
int machine;
|
||||||
int _pad3;
|
int _pad;
|
||||||
/** Starting and ending points of the effect strip. Undefined for other strip types. */
|
/** Starting and ending points of the effect strip. Undefined for other strip types. */
|
||||||
int startdisp, enddisp;
|
int startdisp, enddisp;
|
||||||
float sat;
|
float sat;
|
||||||
float mul;
|
float mul;
|
||||||
float _pad;
|
float _pad1;
|
||||||
|
|
||||||
short anim_preseek; /* UNUSED. */
|
short anim_preseek; /* UNUSED. */
|
||||||
/** Streamindex for movie or sound files with several streams. */
|
/** Streamindex for movie or sound files with several streams. */
|
||||||
|
@ -231,7 +237,7 @@ typedef struct Sequence {
|
||||||
int8_t color_tag;
|
int8_t color_tag;
|
||||||
|
|
||||||
char alpha_mode;
|
char alpha_mode;
|
||||||
char _pad4[2];
|
char _pad2[2];
|
||||||
|
|
||||||
int cache_flag;
|
int cache_flag;
|
||||||
|
|
||||||
|
@ -241,7 +247,7 @@ typedef struct Sequence {
|
||||||
|
|
||||||
/* Multiview */
|
/* Multiview */
|
||||||
char views_format;
|
char views_format;
|
||||||
char _pad1[3];
|
char _pad3[3];
|
||||||
struct Stereo3dFormat *stereo3d_format;
|
struct Stereo3dFormat *stereo3d_format;
|
||||||
|
|
||||||
struct IDProperty *prop;
|
struct IDProperty *prop;
|
||||||
|
@ -251,9 +257,13 @@ typedef struct Sequence {
|
||||||
|
|
||||||
/* Playback rate of strip content in frames per second. */
|
/* Playback rate of strip content in frames per second. */
|
||||||
float media_playback_rate;
|
float media_playback_rate;
|
||||||
/* Multiply strip playback speed. */
|
|
||||||
float speed_factor;
|
float speed_factor;
|
||||||
|
|
||||||
|
struct SeqRetimingHandle *retiming_handles;
|
||||||
|
void *_pad5;
|
||||||
|
int retiming_handle_num;
|
||||||
|
char _pad6[4];
|
||||||
|
|
||||||
SequenceRuntime runtime;
|
SequenceRuntime runtime;
|
||||||
} Sequence;
|
} Sequence;
|
||||||
|
|
||||||
|
|
|
@ -468,6 +468,7 @@ void RNA_api_region_view3d(struct StructRNA *srna);
|
||||||
void RNA_api_texture(struct StructRNA *srna);
|
void RNA_api_texture(struct StructRNA *srna);
|
||||||
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, bool metastrip);
|
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, bool metastrip);
|
||||||
void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop);
|
void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop);
|
||||||
|
void RNA_api_sequence_retiming_handles(BlenderRNA *brna, PropertyRNA *cprop);
|
||||||
void RNA_api_sound(struct StructRNA *srna);
|
void RNA_api_sound(struct StructRNA *srna);
|
||||||
void RNA_api_vfont(struct StructRNA *srna);
|
void RNA_api_vfont(struct StructRNA *srna);
|
||||||
void RNA_api_workspace(struct StructRNA *srna);
|
void RNA_api_workspace(struct StructRNA *srna);
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "SEQ_prefetch.h"
|
#include "SEQ_prefetch.h"
|
||||||
#include "SEQ_proxy.h"
|
#include "SEQ_proxy.h"
|
||||||
#include "SEQ_relations.h"
|
#include "SEQ_relations.h"
|
||||||
|
#include "SEQ_retiming.h"
|
||||||
#include "SEQ_select.h"
|
#include "SEQ_select.h"
|
||||||
#include "SEQ_sequencer.h"
|
#include "SEQ_sequencer.h"
|
||||||
#include "SEQ_sound.h"
|
#include "SEQ_sound.h"
|
||||||
|
@ -272,7 +273,7 @@ static int rna_SequenceEditor_elements_length(PointerRNA *ptr)
|
||||||
return (int)olen;
|
return (int)olen;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_SequenceEditor_elements_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
static void rna_Sequence_elements_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||||
{
|
{
|
||||||
Sequence *seq = (Sequence *)ptr->data;
|
Sequence *seq = (Sequence *)ptr->data;
|
||||||
rna_iterator_array_begin(iter,
|
rna_iterator_array_begin(iter,
|
||||||
|
@ -283,6 +284,85 @@ static void rna_SequenceEditor_elements_begin(CollectionPropertyIterator *iter,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rna_Sequence_retiming_handles_length(PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
return SEQ_retiming_handles_count((Sequence *)ptr->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_SequenceEditor_retiming_handles_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),
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Sequence *strip_by_handle_find(Scene *scene, SeqRetimingHandle *handle)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (handle >= first && handle <= last) {
|
||||||
|
return seq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_Sequence_retiming_handle_remove(ID *id, SeqRetimingHandle *handle)
|
||||||
|
{
|
||||||
|
Scene *scene = (Scene *)id;
|
||||||
|
Sequence *seq = strip_by_handle_find(scene, handle);
|
||||||
|
|
||||||
|
if (seq == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEQ_retiming_remove_handle(seq, handle);
|
||||||
|
|
||||||
|
SEQ_relations_invalidate_cache_raw(scene, seq);
|
||||||
|
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rna_Sequence_retiming_handle_frame_get(PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
SeqRetimingHandle *handle = (SeqRetimingHandle *)ptr->data;
|
||||||
|
Scene *scene = (Scene *)ptr->owner_id;
|
||||||
|
Sequence *seq = strip_by_handle_find(scene, handle);
|
||||||
|
|
||||||
|
if (seq == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SEQ_time_start_frame_get(seq) + handle->strip_frame_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_Sequence_retiming_handle_frame_set(PointerRNA *ptr, int value)
|
||||||
|
{
|
||||||
|
SeqRetimingHandle *handle = (SeqRetimingHandle *)ptr->data;
|
||||||
|
Scene *scene = (Scene *)ptr->owner_id;
|
||||||
|
Sequence *seq = strip_by_handle_find(scene, handle);
|
||||||
|
|
||||||
|
if (seq == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int offset = value - SEQ_time_start_frame_get(seq) + handle->strip_frame_index;
|
||||||
|
SEQ_retiming_offset_handle(scene, seq, handle, offset);
|
||||||
|
SEQ_relations_invalidate_cache_raw(scene, seq);
|
||||||
|
}
|
||||||
|
|
||||||
static void rna_Sequence_views_format_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
static void rna_Sequence_views_format_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||||
{
|
{
|
||||||
rna_Sequence_invalidate_raw_update(bmain, scene, ptr);
|
rna_Sequence_invalidate_raw_update(bmain, scene, ptr);
|
||||||
|
@ -819,19 +899,6 @@ static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
|
||||||
DEG_id_tag_update(ptr->owner_id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO);
|
DEG_id_tag_update(ptr->owner_id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_Sequence_speed_factor_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
|
||||||
{
|
|
||||||
SEQ_cache_cleanup(scene);
|
|
||||||
rna_Sequence_audio_update(bmain, scene, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rna_Sequence_speed_factor_set(PointerRNA *ptr, float value)
|
|
||||||
{
|
|
||||||
Sequence *seq = (Sequence *)ptr->data;
|
|
||||||
Scene *scene = (Scene *)ptr->owner_id;
|
|
||||||
SEQ_time_speed_factor_set(scene, seq, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rna_Sequence_pan_range(
|
static void rna_Sequence_pan_range(
|
||||||
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
|
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
|
||||||
{
|
{
|
||||||
|
@ -1493,6 +1560,31 @@ static void rna_def_strip_element(BlenderRNA *brna)
|
||||||
RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second");
|
RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rna_def_retiming_handle(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "RetimingHandle", NULL);
|
||||||
|
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");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "timeline_frame", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_int_sdna(prop, NULL, "strip_frame_index");
|
||||||
|
RNA_def_property_int_funcs(prop,
|
||||||
|
"rna_Sequence_retiming_handle_frame_get",
|
||||||
|
"rna_Sequence_retiming_handle_frame_set",
|
||||||
|
NULL);
|
||||||
|
RNA_def_property_ui_text(prop, "Timeline Frame", "Position of retiming handle in timeline");
|
||||||
|
|
||||||
|
FunctionRNA *func = RNA_def_function(srna, "remove", "rna_Sequence_retiming_handle_remove");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
|
||||||
|
RNA_def_function_ui_description(func, "Remove retiming handle");
|
||||||
|
}
|
||||||
|
|
||||||
static void rna_def_strip_crop(BlenderRNA *brna)
|
static void rna_def_strip_crop(BlenderRNA *brna)
|
||||||
{
|
{
|
||||||
StructRNA *srna;
|
StructRNA *srna;
|
||||||
|
@ -2351,19 +2443,6 @@ static void rna_def_editor(BlenderRNA *brna)
|
||||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_speed_factor(StructRNA *srna)
|
|
||||||
{
|
|
||||||
PropertyRNA *prop = RNA_def_property(srna, "speed_factor", PROP_FLOAT, PROP_NONE);
|
|
||||||
RNA_def_property_float_sdna(prop, NULL, "speed_factor");
|
|
||||||
RNA_def_property_float_default(prop, 1.0f);
|
|
||||||
RNA_def_property_range(prop, 0.1f, FLT_MAX);
|
|
||||||
RNA_def_property_ui_range(prop, 1.0f, 100.0f, 10.0, 3);
|
|
||||||
RNA_def_property_ui_text(prop, "Speed Factor", "Multiply playback speed");
|
|
||||||
RNA_def_property_float_funcs(
|
|
||||||
prop, NULL, "rna_Sequence_speed_factor_set", NULL); /* overlap test */
|
|
||||||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_speed_factor_update");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rna_def_filter_video(StructRNA *srna)
|
static void rna_def_filter_video(StructRNA *srna)
|
||||||
{
|
{
|
||||||
PropertyRNA *prop;
|
PropertyRNA *prop;
|
||||||
|
@ -2564,7 +2643,7 @@ static void rna_def_image(BlenderRNA *brna)
|
||||||
RNA_def_property_struct_type(prop, "SequenceElement");
|
RNA_def_property_struct_type(prop, "SequenceElement");
|
||||||
RNA_def_property_ui_text(prop, "Elements", "");
|
RNA_def_property_ui_text(prop, "Elements", "");
|
||||||
RNA_def_property_collection_funcs(prop,
|
RNA_def_property_collection_funcs(prop,
|
||||||
"rna_SequenceEditor_elements_begin",
|
"rna_Sequence_elements_begin",
|
||||||
"rna_iterator_array_next",
|
"rna_iterator_array_next",
|
||||||
"rna_iterator_array_end",
|
"rna_iterator_array_end",
|
||||||
"rna_iterator_array_get",
|
"rna_iterator_array_get",
|
||||||
|
@ -2596,7 +2675,6 @@ static void rna_def_image(BlenderRNA *brna)
|
||||||
rna_def_proxy(srna);
|
rna_def_proxy(srna);
|
||||||
rna_def_input(srna);
|
rna_def_input(srna);
|
||||||
rna_def_color_management(srna);
|
rna_def_color_management(srna);
|
||||||
rna_def_speed_factor(srna);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_meta(BlenderRNA *brna)
|
static void rna_def_meta(BlenderRNA *brna)
|
||||||
|
@ -2628,7 +2706,6 @@ static void rna_def_meta(BlenderRNA *brna)
|
||||||
rna_def_filter_video(srna);
|
rna_def_filter_video(srna);
|
||||||
rna_def_proxy(srna);
|
rna_def_proxy(srna);
|
||||||
rna_def_input(srna);
|
rna_def_input(srna);
|
||||||
rna_def_speed_factor(srna);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_scene(BlenderRNA *brna)
|
static void rna_def_scene(BlenderRNA *brna)
|
||||||
|
@ -2677,7 +2754,6 @@ static void rna_def_scene(BlenderRNA *brna)
|
||||||
rna_def_proxy(srna);
|
rna_def_proxy(srna);
|
||||||
rna_def_input(srna);
|
rna_def_input(srna);
|
||||||
rna_def_movie_types(srna);
|
rna_def_movie_types(srna);
|
||||||
rna_def_speed_factor(srna);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_movie(BlenderRNA *brna)
|
static void rna_def_movie(BlenderRNA *brna)
|
||||||
|
@ -2705,7 +2781,7 @@ static void rna_def_movie(BlenderRNA *brna)
|
||||||
RNA_def_property_struct_type(prop, "SequenceElement");
|
RNA_def_property_struct_type(prop, "SequenceElement");
|
||||||
RNA_def_property_ui_text(prop, "Elements", "");
|
RNA_def_property_ui_text(prop, "Elements", "");
|
||||||
RNA_def_property_collection_funcs(prop,
|
RNA_def_property_collection_funcs(prop,
|
||||||
"rna_SequenceEditor_elements_begin",
|
"rna_Sequence_elements_begin",
|
||||||
"rna_iterator_array_next",
|
"rna_iterator_array_next",
|
||||||
"rna_iterator_array_end",
|
"rna_iterator_array_end",
|
||||||
"rna_iterator_array_get",
|
"rna_iterator_array_get",
|
||||||
|
@ -2714,6 +2790,21 @@ static void rna_def_movie(BlenderRNA *brna)
|
||||||
NULL,
|
NULL,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "retiming_handles", PROP_COLLECTION, PROP_NONE);
|
||||||
|
RNA_def_property_collection_sdna(prop, NULL, "retiming_handles", NULL);
|
||||||
|
RNA_def_property_struct_type(prop, "RetimingHandle");
|
||||||
|
RNA_def_property_ui_text(prop, "Retiming Hndles", "");
|
||||||
|
RNA_def_property_collection_funcs(prop,
|
||||||
|
"rna_SequenceEditor_retiming_handles_begin",
|
||||||
|
"rna_iterator_array_next",
|
||||||
|
"rna_iterator_array_end",
|
||||||
|
"rna_iterator_array_get",
|
||||||
|
"rna_Sequence_retiming_handles_length",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
RNA_api_sequence_retiming_handles(brna, prop);
|
||||||
|
|
||||||
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
|
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
|
||||||
RNA_def_property_ui_text(prop, "File", "");
|
RNA_def_property_ui_text(prop, "File", "");
|
||||||
RNA_def_property_string_funcs(prop,
|
RNA_def_property_string_funcs(prop,
|
||||||
|
@ -2761,7 +2852,6 @@ static void rna_def_movie(BlenderRNA *brna)
|
||||||
rna_def_input(srna);
|
rna_def_input(srna);
|
||||||
rna_def_color_management(srna);
|
rna_def_color_management(srna);
|
||||||
rna_def_movie_types(srna);
|
rna_def_movie_types(srna);
|
||||||
rna_def_speed_factor(srna);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_movieclip(BlenderRNA *brna)
|
static void rna_def_movieclip(BlenderRNA *brna)
|
||||||
|
@ -2789,7 +2879,6 @@ static void rna_def_movieclip(BlenderRNA *brna)
|
||||||
rna_def_filter_video(srna);
|
rna_def_filter_video(srna);
|
||||||
rna_def_input(srna);
|
rna_def_input(srna);
|
||||||
rna_def_movie_types(srna);
|
rna_def_movie_types(srna);
|
||||||
rna_def_speed_factor(srna);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_mask(BlenderRNA *brna)
|
static void rna_def_mask(BlenderRNA *brna)
|
||||||
|
@ -2808,7 +2897,6 @@ static void rna_def_mask(BlenderRNA *brna)
|
||||||
|
|
||||||
rna_def_filter_video(srna);
|
rna_def_filter_video(srna);
|
||||||
rna_def_input(srna);
|
rna_def_input(srna);
|
||||||
rna_def_speed_factor(srna);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_sound(BlenderRNA *brna)
|
static void rna_def_sound(BlenderRNA *brna)
|
||||||
|
@ -2851,7 +2939,6 @@ static void rna_def_sound(BlenderRNA *brna)
|
||||||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
|
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
|
||||||
|
|
||||||
rna_def_input(srna);
|
rna_def_input(srna);
|
||||||
rna_def_speed_factor(srna);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_def_effect(BlenderRNA *brna)
|
static void rna_def_effect(BlenderRNA *brna)
|
||||||
|
@ -3613,6 +3700,7 @@ void RNA_def_sequencer(BlenderRNA *brna)
|
||||||
rna_def_color_balance(brna);
|
rna_def_color_balance(brna);
|
||||||
|
|
||||||
rna_def_strip_element(brna);
|
rna_def_strip_element(brna);
|
||||||
|
rna_def_retiming_handle(brna);
|
||||||
rna_def_strip_proxy(brna);
|
rna_def_strip_proxy(brna);
|
||||||
rna_def_strip_color_balance(brna);
|
rna_def_strip_color_balance(brna);
|
||||||
rna_def_strip_crop(brna);
|
rna_def_strip_crop(brna);
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
# include "SEQ_effects.h"
|
# include "SEQ_effects.h"
|
||||||
# include "SEQ_relations.h"
|
# include "SEQ_relations.h"
|
||||||
# include "SEQ_render.h"
|
# include "SEQ_render.h"
|
||||||
|
# include "SEQ_retiming.h"
|
||||||
# include "SEQ_sequencer.h"
|
# include "SEQ_sequencer.h"
|
||||||
# include "SEQ_time.h"
|
# include "SEQ_time.h"
|
||||||
|
|
||||||
|
@ -637,6 +638,29 @@ 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)
|
||||||
|
{
|
||||||
|
Scene *scene = (Scene *)id;
|
||||||
|
|
||||||
|
SeqRetimingHandle *handle = SEQ_retiming_add_handle(seq, timeline_frame);
|
||||||
|
|
||||||
|
SEQ_relations_invalidate_cache_raw(scene, seq);
|
||||||
|
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_Sequence_retiming_handles_reset(ID *id, Sequence *seq)
|
||||||
|
{
|
||||||
|
Scene *scene = (Scene *)id;
|
||||||
|
|
||||||
|
SEQ_retiming_data_clear(seq);
|
||||||
|
|
||||||
|
SEQ_relations_invalidate_cache_raw(scene, seq);
|
||||||
|
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void RNA_api_sequence_strip(StructRNA *srna)
|
void RNA_api_sequence_strip(StructRNA *srna)
|
||||||
|
@ -744,6 +768,30 @@ void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop)
|
||||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RNA_api_sequence_retiming_handles(BlenderRNA *brna, PropertyRNA *cprop)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
|
||||||
|
RNA_def_property_srna(cprop, "RetimingHandles");
|
||||||
|
srna = RNA_def_struct(brna, "RetimingHandles", NULL);
|
||||||
|
RNA_def_struct_sdna(srna, "Sequence");
|
||||||
|
RNA_def_struct_ui_text(srna, "RetimingHandles", "Collection of RetimingHandle");
|
||||||
|
|
||||||
|
FunctionRNA *func = RNA_def_function(srna, "add", "rna_Sequence_retiming_handles_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");
|
||||||
|
/* return type */
|
||||||
|
PropertyRNA *parm = RNA_def_pointer(
|
||||||
|
func, "retiming_handle", "RetimingHandle", "", "New RetimingHandle");
|
||||||
|
RNA_def_function_return(func, parm);
|
||||||
|
|
||||||
|
func = RNA_def_function(srna, "reset", "rna_Sequence_retiming_handles_reset");
|
||||||
|
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
|
||||||
|
RNA_def_function_ui_description(func, "Remove all retiming handles");
|
||||||
|
}
|
||||||
|
|
||||||
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastrip)
|
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastrip)
|
||||||
{
|
{
|
||||||
StructRNA *srna;
|
StructRNA *srna;
|
||||||
|
|
|
@ -42,6 +42,8 @@ set(SRC
|
||||||
SEQ_proxy.h
|
SEQ_proxy.h
|
||||||
SEQ_relations.h
|
SEQ_relations.h
|
||||||
SEQ_render.h
|
SEQ_render.h
|
||||||
|
SEQ_retiming.h
|
||||||
|
SEQ_retiming.hh
|
||||||
SEQ_select.h
|
SEQ_select.h
|
||||||
SEQ_sequencer.h
|
SEQ_sequencer.h
|
||||||
SEQ_sound.h
|
SEQ_sound.h
|
||||||
|
@ -76,6 +78,7 @@ set(SRC
|
||||||
intern/strip_add.c
|
intern/strip_add.c
|
||||||
intern/strip_edit.c
|
intern/strip_edit.c
|
||||||
intern/strip_relations.c
|
intern/strip_relations.c
|
||||||
|
intern/strip_retiming.cc
|
||||||
intern/strip_select.c
|
intern/strip_select.c
|
||||||
intern/strip_time.c
|
intern/strip_time.c
|
||||||
intern/strip_time.h
|
intern/strip_time.h
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Copyright 2004 Blender Foundation. All rights reserved. */
|
||||||
|
|
||||||
|
#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(struct Sequence *seq, const int timeline_frame);
|
||||||
|
struct SeqRetimingHandle *SEQ_retiming_last_handle_get(const struct Sequence *seq);
|
||||||
|
void SEQ_retiming_remove_handle(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 Scene *scene,
|
||||||
|
const struct Sequence *seq,
|
||||||
|
const struct SeqRetimingHandle *handle);
|
||||||
|
int SEQ_retiming_handle_index_get(const struct Sequence *seq,
|
||||||
|
const struct SeqRetimingHandle *handle);
|
||||||
|
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);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,15 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Copyright 2022 Blender Foundation. All rights reserved. */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup sequencer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BLI_span.hh"
|
||||||
|
|
||||||
|
struct Sequence;
|
||||||
|
struct SeqRetimingHandle;
|
||||||
|
|
||||||
|
blender::MutableSpan<SeqRetimingHandle> SEQ_retiming_handles_get(const Sequence *seq);
|
|
@ -73,6 +73,8 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene,
|
||||||
bool SEQ_time_strip_intersects_frame(const struct Scene *scene,
|
bool SEQ_time_strip_intersects_frame(const struct Scene *scene,
|
||||||
const struct Sequence *seq,
|
const struct Sequence *seq,
|
||||||
int timeline_frame);
|
int timeline_frame);
|
||||||
|
/* Convert timeline frame so strip frame index. */
|
||||||
|
float SEQ_give_frame_index(const struct Scene *scene, struct Sequence *seq, float timeline_frame);
|
||||||
/**
|
/**
|
||||||
* Returns true if strip has frames without content to render.
|
* Returns true if strip has frames without content to render.
|
||||||
*/
|
*/
|
||||||
|
@ -136,7 +138,6 @@ void SEQ_time_start_frame_set(const struct Scene *scene, struct Sequence *seq, i
|
||||||
* \note this function is currently only used internally and in versioning code.
|
* \note this function is currently only used internally and in versioning code.
|
||||||
*/
|
*/
|
||||||
void SEQ_time_update_meta_strip_range(const struct Scene *scene, struct Sequence *seq_meta);
|
void SEQ_time_update_meta_strip_range(const struct Scene *scene, struct Sequence *seq_meta);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2620,7 +2620,7 @@ float seq_speed_effect_target_frame_get(Scene *scene,
|
||||||
}
|
}
|
||||||
|
|
||||||
SEQ_effect_handle_get(seq_speed); /* Ensure, that data are initialized. */
|
SEQ_effect_handle_get(seq_speed); /* Ensure, that data are initialized. */
|
||||||
int frame_index = seq_give_frame_index(scene, seq_speed, timeline_frame);
|
int frame_index = SEQ_give_frame_index(scene, seq_speed, timeline_frame);
|
||||||
SpeedControlVars *s = (SpeedControlVars *)seq_speed->effectdata;
|
SpeedControlVars *s = (SpeedControlVars *)seq_speed->effectdata;
|
||||||
const Sequence *source = seq_speed->seq1;
|
const Sequence *source = seq_speed->seq1;
|
||||||
|
|
||||||
|
|
|
@ -142,7 +142,7 @@ static float seq_cache_timeline_frame_to_frame_index(Scene *scene,
|
||||||
* images or extended frame range of movies will only generate one cache entry. No special
|
* images or extended frame range of movies will only generate one cache entry. No special
|
||||||
* treatment in converting frame index to timeline_frame is needed. */
|
* treatment in converting frame index to timeline_frame is needed. */
|
||||||
if (ELEM(type, SEQ_CACHE_STORE_RAW, SEQ_CACHE_STORE_THUMBNAIL)) {
|
if (ELEM(type, SEQ_CACHE_STORE_RAW, SEQ_CACHE_STORE_THUMBNAIL)) {
|
||||||
return seq_give_frame_index(scene, seq, timeline_frame);
|
return SEQ_give_frame_index(scene, seq, timeline_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
return timeline_frame - SEQ_time_start_frame_get(seq);
|
return timeline_frame - SEQ_time_start_frame_get(seq);
|
||||||
|
|
|
@ -209,7 +209,7 @@ ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int timeline
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) {
|
if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) {
|
||||||
int frameno = (int)seq_give_frame_index(context->scene, seq, timeline_frame) +
|
int frameno = (int)SEQ_give_frame_index(context->scene, seq, timeline_frame) +
|
||||||
seq->anim_startofs;
|
seq->anim_startofs;
|
||||||
if (proxy->anim == NULL) {
|
if (proxy->anim == NULL) {
|
||||||
if (seq_proxy_get_fname(
|
if (seq_proxy_get_fname(
|
||||||
|
|
|
@ -238,7 +238,7 @@ StripElem *SEQ_render_give_stripelem(const Scene *scene, Sequence *seq, int time
|
||||||
* all other strips don't use this...
|
* all other strips don't use this...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int frame_index = (int)seq_give_frame_index(scene, seq, timeline_frame);
|
int frame_index = (int)SEQ_give_frame_index(scene, seq, timeline_frame);
|
||||||
|
|
||||||
if (frame_index == -1 || se == NULL) {
|
if (frame_index == -1 || se == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1042,7 +1042,7 @@ static ImBuf *seq_render_movie_strip_custom_file_proxy(const SeqRenderData *cont
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int frameno = (int)seq_give_frame_index(context->scene, seq, timeline_frame) +
|
int frameno = (int)SEQ_give_frame_index(context->scene, seq, timeline_frame) +
|
||||||
seq->anim_startofs;
|
seq->anim_startofs;
|
||||||
return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE);
|
return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE);
|
||||||
}
|
}
|
||||||
|
@ -1658,7 +1658,7 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context,
|
||||||
bool *r_is_proxy_image)
|
bool *r_is_proxy_image)
|
||||||
{
|
{
|
||||||
ImBuf *ibuf = NULL;
|
ImBuf *ibuf = NULL;
|
||||||
float frame_index = seq_give_frame_index(context->scene, seq, timeline_frame);
|
float frame_index = SEQ_give_frame_index(context->scene, seq, timeline_frame);
|
||||||
int type = (seq->type & SEQ_TYPE_EFFECT) ? SEQ_TYPE_EFFECT : seq->type;
|
int type = (seq->type & SEQ_TYPE_EFFECT) ? SEQ_TYPE_EFFECT : seq->type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SEQ_TYPE_META: {
|
case SEQ_TYPE_META: {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include "BLI_listbase.h"
|
#include "BLI_listbase.h"
|
||||||
|
|
||||||
|
#include "BKE_fcurve.h"
|
||||||
#include "BKE_idprop.h"
|
#include "BKE_idprop.h"
|
||||||
#include "BKE_lib_id.h"
|
#include "BKE_lib_id.h"
|
||||||
#include "BKE_sound.h"
|
#include "BKE_sound.h"
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
#include "SEQ_modifier.h"
|
#include "SEQ_modifier.h"
|
||||||
#include "SEQ_proxy.h"
|
#include "SEQ_proxy.h"
|
||||||
#include "SEQ_relations.h"
|
#include "SEQ_relations.h"
|
||||||
|
#include "SEQ_retiming.h"
|
||||||
#include "SEQ_select.h"
|
#include "SEQ_select.h"
|
||||||
#include "SEQ_sequencer.h"
|
#include "SEQ_sequencer.h"
|
||||||
#include "SEQ_sound.h"
|
#include "SEQ_sound.h"
|
||||||
|
@ -218,6 +220,12 @@ static void seq_sequence_free_ex(Scene *scene,
|
||||||
SEQ_channels_free(&seq->channels);
|
SEQ_channels_free(&seq->channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seq->retiming_handles != NULL) {
|
||||||
|
MEM_freeN(seq->retiming_handles);
|
||||||
|
seq->retiming_handles = NULL;
|
||||||
|
seq->retiming_handle_num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
MEM_freeN(seq);
|
MEM_freeN(seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,6 +584,11 @@ static Sequence *seq_dupli(const Scene *scene_src,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seq->retiming_handles != NULL) {
|
||||||
|
seqn->retiming_handles = MEM_dupallocN(seq->retiming_handles);
|
||||||
|
seqn->retiming_handle_num = seq->retiming_handle_num;
|
||||||
|
}
|
||||||
|
|
||||||
return seqn;
|
return seqn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,6 +762,12 @@ static bool seq_write_data_cb(Sequence *seq, void *userdata)
|
||||||
LISTBASE_FOREACH (SeqTimelineChannel *, channel, &seq->channels) {
|
LISTBASE_FOREACH (SeqTimelineChannel *, channel, &seq->channels) {
|
||||||
BLO_write_struct(writer, SeqTimelineChannel, channel);
|
BLO_write_struct(writer, SeqTimelineChannel, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seq->retiming_handles != NULL) {
|
||||||
|
int size = SEQ_retiming_handles_count(seq);
|
||||||
|
BLO_write_struct_array(writer, SeqRetimingHandle, size, seq->retiming_handles);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,6 +837,11 @@ static bool seq_read_data_cb(Sequence *seq, void *user_data)
|
||||||
SEQ_modifier_blend_read_data(reader, &seq->modifiers);
|
SEQ_modifier_blend_read_data(reader, &seq->modifiers);
|
||||||
|
|
||||||
BLO_read_list(reader, &seq->channels);
|
BLO_read_list(reader, &seq->channels);
|
||||||
|
|
||||||
|
if (seq->retiming_handles != NULL) {
|
||||||
|
BLO_read_data_address(reader, &seq->retiming_handles);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void SEQ_blend_read(BlendDataReader *reader, ListBase *seqbase)
|
void SEQ_blend_read(BlendDataReader *reader, ListBase *seqbase)
|
||||||
|
@ -966,9 +990,7 @@ static bool seq_update_seq_cb(Sequence *seq, void *user_data)
|
||||||
}
|
}
|
||||||
BKE_sound_set_scene_sound_volume(
|
BKE_sound_set_scene_sound_volume(
|
||||||
seq->scene_sound, seq->volume, (seq->flag & SEQ_AUDIO_VOLUME_ANIMATED) != 0);
|
seq->scene_sound, seq->volume, (seq->flag & SEQ_AUDIO_VOLUME_ANIMATED) != 0);
|
||||||
BKE_sound_set_scene_sound_pitch(seq->scene_sound,
|
SEQ_retiming_sound_animation_data_set(scene, seq);
|
||||||
SEQ_sound_pitch_get(scene, seq),
|
|
||||||
(seq->flag & SEQ_AUDIO_PITCH_ANIMATED) != 0);
|
|
||||||
BKE_sound_set_scene_sound_pan(
|
BKE_sound_set_scene_sound_pan(
|
||||||
seq->scene_sound, seq->pan, (seq->flag & SEQ_AUDIO_PAN_ANIMATED) != 0);
|
seq->scene_sound, seq->pan, (seq->flag & SEQ_AUDIO_PAN_ANIMATED) != 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -433,6 +433,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
|
||||||
if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) {
|
if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) {
|
||||||
scene->r.frs_sec = fps_denom;
|
scene->r.frs_sec = fps_denom;
|
||||||
scene->r.frs_sec_base = fps_num;
|
scene->r.frs_sec_base = fps_num;
|
||||||
|
DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_FPS | ID_RECALC_SEQUENCER_STRIPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
load_data->r_video_stream_start = IMD_anim_get_offset(anim_arr[0]);
|
load_data->r_video_stream_start = IMD_anim_get_offset(anim_arr[0]);
|
||||||
|
|
|
@ -275,9 +275,7 @@ static void seq_split_set_right_hold_offset(Main *bmain,
|
||||||
/* Adjust within range of strip contents. */
|
/* Adjust within range of strip contents. */
|
||||||
else if ((timeline_frame >= content_start) && (timeline_frame <= content_end)) {
|
else if ((timeline_frame >= content_start) && (timeline_frame <= content_end)) {
|
||||||
seq->endofs = 0;
|
seq->endofs = 0;
|
||||||
float speed_factor = (seq->type == SEQ_TYPE_SOUND_RAM) ?
|
float speed_factor = seq_time_media_playback_rate_factor_get(scene, seq);
|
||||||
seq_time_media_playback_rate_factor_get(scene, seq) :
|
|
||||||
seq_time_playback_rate_factor_get(scene, seq);
|
|
||||||
seq->anim_endofs += round_fl_to_int((content_end - timeline_frame) * speed_factor);
|
seq->anim_endofs += round_fl_to_int((content_end - timeline_frame) * speed_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,9 +294,7 @@ static void seq_split_set_left_hold_offset(Main *bmain,
|
||||||
|
|
||||||
/* Adjust within range of strip contents. */
|
/* Adjust within range of strip contents. */
|
||||||
if ((timeline_frame >= content_start) && (timeline_frame <= content_end)) {
|
if ((timeline_frame >= content_start) && (timeline_frame <= content_end)) {
|
||||||
float speed_factor = (seq->type == SEQ_TYPE_SOUND_RAM) ?
|
float speed_factor = seq_time_media_playback_rate_factor_get(scene, seq);
|
||||||
seq_time_media_playback_rate_factor_get(scene, seq) :
|
|
||||||
seq_time_playback_rate_factor_get(scene, seq);
|
|
||||||
seq->anim_startofs += round_fl_to_int((timeline_frame - content_start) * speed_factor);
|
seq->anim_startofs += round_fl_to_int((timeline_frame - content_start) * speed_factor);
|
||||||
seq->start = timeline_frame;
|
seq->start = timeline_frame;
|
||||||
seq->startofs = 0;
|
seq->startofs = 0;
|
||||||
|
|
|
@ -0,0 +1,376 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Copyright 2022 Blender Foundation. All rights reserved. */
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup bke
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "DNA_scene_types.h"
|
||||||
|
#include "DNA_sequence_types.h"
|
||||||
|
|
||||||
|
#include "BLI_listbase.h"
|
||||||
|
#include "BLI_math.h"
|
||||||
|
#include "BLI_span.hh"
|
||||||
|
|
||||||
|
#include "BKE_fcurve.h"
|
||||||
|
#include "BKE_movieclip.h"
|
||||||
|
#include "BKE_scene.h"
|
||||||
|
#include "BKE_sound.h"
|
||||||
|
|
||||||
|
#include "DNA_anim_types.h"
|
||||||
|
#include "DNA_sound_types.h"
|
||||||
|
|
||||||
|
#include "IMB_imbuf.h"
|
||||||
|
|
||||||
|
#include "RNA_prototypes.h"
|
||||||
|
|
||||||
|
#include "SEQ_channels.h"
|
||||||
|
#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"
|
||||||
|
|
||||||
|
#include "sequencer.h"
|
||||||
|
#include "strip_time.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
using blender::MutableSpan;
|
||||||
|
|
||||||
|
MutableSpan<SeqRetimingHandle> SEQ_retiming_handles_get(const Sequence *seq)
|
||||||
|
{
|
||||||
|
blender::MutableSpan<SeqRetimingHandle> handles(seq->retiming_handles, seq->retiming_handle_num);
|
||||||
|
return handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SeqRetimingHandle *SEQ_retiming_last_handle_get(const struct Sequence *seq)
|
||||||
|
{
|
||||||
|
return seq->retiming_handles + seq->retiming_handle_num - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SEQ_retiming_handle_index_get(const Sequence *seq, const SeqRetimingHandle *handle)
|
||||||
|
{
|
||||||
|
return handle - seq->retiming_handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool seq_retiming_is_last_handle(const Sequence *seq, const SeqRetimingHandle *handle)
|
||||||
|
{
|
||||||
|
return SEQ_retiming_handle_index_get(seq, handle) == seq->retiming_handle_num - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SeqRetimingHandle *retiming_find_segment_start_handle(const Sequence *seq,
|
||||||
|
const int frame_index)
|
||||||
|
{
|
||||||
|
const SeqRetimingHandle *start_handle = nullptr;
|
||||||
|
for (auto const &handle : SEQ_retiming_handles_get(seq)) {
|
||||||
|
if (seq_retiming_is_last_handle(seq, &handle)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (handle.strip_frame_index > frame_index) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_handle = &handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return start_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SEQ_retiming_handles_count(const Sequence *seq)
|
||||||
|
{
|
||||||
|
return seq->retiming_handle_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQ_retiming_data_ensure(const Scene *scene, Sequence *seq)
|
||||||
|
{
|
||||||
|
if (!SEQ_retiming_is_allowed(seq)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seq->retiming_handles != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
seq->retiming_handles = (SeqRetimingHandle *)MEM_calloc_arrayN(
|
||||||
|
2, sizeof(SeqRetimingHandle), __func__);
|
||||||
|
SeqRetimingHandle *handle = seq->retiming_handles + 1;
|
||||||
|
handle->strip_frame_index = seq_time_strip_original_content_length_get(scene, seq) - 1;
|
||||||
|
handle->retiming_factor = 1.0f;
|
||||||
|
seq->retiming_handle_num = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQ_retiming_data_clear(Sequence *seq)
|
||||||
|
{
|
||||||
|
seq->retiming_handles = nullptr;
|
||||||
|
seq->retiming_handle_num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SEQ_retiming_is_active(const Sequence *seq)
|
||||||
|
{
|
||||||
|
return seq->retiming_handle_num > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SEQ_retiming_is_allowed(const Sequence *seq)
|
||||||
|
{
|
||||||
|
return ELEM(seq->type,
|
||||||
|
SEQ_TYPE_SOUND_RAM,
|
||||||
|
SEQ_TYPE_IMAGE,
|
||||||
|
SEQ_TYPE_META,
|
||||||
|
SEQ_TYPE_SCENE,
|
||||||
|
SEQ_TYPE_MOVIE,
|
||||||
|
SEQ_TYPE_MOVIECLIP,
|
||||||
|
SEQ_TYPE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
float seq_retiming_evaluate(const Sequence *seq, const float frame_index)
|
||||||
|
{
|
||||||
|
const SeqRetimingHandle *previous_handle = retiming_find_segment_start_handle(seq, frame_index);
|
||||||
|
const SeqRetimingHandle *next_handle = previous_handle + 1;
|
||||||
|
const int previous_handle_index = previous_handle - seq->retiming_handles;
|
||||||
|
|
||||||
|
BLI_assert(previous_handle_index < seq->retiming_handle_num);
|
||||||
|
UNUSED_VARS_NDEBUG(previous_handle_index);
|
||||||
|
|
||||||
|
if (next_handle == nullptr) {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int segment_length = next_handle->strip_frame_index - previous_handle->strip_frame_index;
|
||||||
|
const float segment_frame_index = frame_index - previous_handle->strip_frame_index;
|
||||||
|
const float segment_fac = segment_frame_index / (float)segment_length;
|
||||||
|
|
||||||
|
const float target_diff = next_handle->retiming_factor - previous_handle->retiming_factor;
|
||||||
|
return previous_handle->retiming_factor + (target_diff * segment_fac);
|
||||||
|
}
|
||||||
|
|
||||||
|
SeqRetimingHandle *SEQ_retiming_add_handle(Sequence *seq, const int timeline_frame)
|
||||||
|
{
|
||||||
|
float frame_index = timeline_frame - SEQ_time_start_frame_get(seq);
|
||||||
|
float value = seq_retiming_evaluate(seq, frame_index);
|
||||||
|
|
||||||
|
const SeqRetimingHandle *closest_handle = retiming_find_segment_start_handle(seq, frame_index);
|
||||||
|
if (closest_handle->strip_frame_index == frame_index) {
|
||||||
|
return nullptr; /* Retiming handle already exists. */
|
||||||
|
}
|
||||||
|
|
||||||
|
SeqRetimingHandle *handles = seq->retiming_handles;
|
||||||
|
size_t handle_count = SEQ_retiming_handles_count(seq);
|
||||||
|
const int new_handle_index = closest_handle - handles + 1;
|
||||||
|
BLI_assert(new_handle_index >= 0);
|
||||||
|
BLI_assert(new_handle_index < handle_count);
|
||||||
|
|
||||||
|
SeqRetimingHandle *new_handles = (SeqRetimingHandle *)MEM_callocN(
|
||||||
|
(handle_count + 1) * sizeof(SeqRetimingHandle), __func__);
|
||||||
|
if (new_handle_index > 0) {
|
||||||
|
memcpy(new_handles, handles, new_handle_index * sizeof(SeqRetimingHandle));
|
||||||
|
}
|
||||||
|
if (new_handle_index < handle_count) {
|
||||||
|
memcpy(new_handles + new_handle_index + 1,
|
||||||
|
handles + new_handle_index,
|
||||||
|
(handle_count - new_handle_index) * sizeof(SeqRetimingHandle));
|
||||||
|
}
|
||||||
|
MEM_freeN(handles);
|
||||||
|
seq->retiming_handles = new_handles;
|
||||||
|
seq->retiming_handle_num++;
|
||||||
|
|
||||||
|
SeqRetimingHandle *added_handle = (new_handles + new_handle_index);
|
||||||
|
added_handle->strip_frame_index = frame_index;
|
||||||
|
added_handle->retiming_factor = value;
|
||||||
|
|
||||||
|
return added_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQ_retiming_offset_handle(const Scene *scene,
|
||||||
|
Sequence *seq,
|
||||||
|
SeqRetimingHandle *handle,
|
||||||
|
const int offset)
|
||||||
|
{
|
||||||
|
if (handle->strip_frame_index == 0) {
|
||||||
|
return; /* First handle can not be moved. */
|
||||||
|
}
|
||||||
|
|
||||||
|
MutableSpan handles = SEQ_retiming_handles_get(seq);
|
||||||
|
for (; handle < handles.end(); handle++) {
|
||||||
|
handle->strip_frame_index += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEQ_time_update_meta_strip_range(scene, seq_sequence_lookup_meta_by_seq(scene, seq));
|
||||||
|
seq_time_update_effects_strip_range(scene, seq_sequence_lookup_effects_by_seq(scene, seq));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQ_retiming_remove_handle(Sequence *seq, SeqRetimingHandle *handle)
|
||||||
|
{
|
||||||
|
SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
|
||||||
|
if (handle->strip_frame_index == 0 || handle == last_handle) {
|
||||||
|
return; /* First and last handle can not be removed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t handle_count = SEQ_retiming_handles_count(seq);
|
||||||
|
SeqRetimingHandle *handles = (SeqRetimingHandle *)MEM_callocN(
|
||||||
|
(handle_count - 1) * sizeof(SeqRetimingHandle), __func__);
|
||||||
|
|
||||||
|
const int handle_index = handle - seq->retiming_handles;
|
||||||
|
memcpy(handles, seq->retiming_handles, (handle_index) * sizeof(SeqRetimingHandle));
|
||||||
|
memcpy(handles + handle_index,
|
||||||
|
seq->retiming_handles + handle_index + 1,
|
||||||
|
(handle_count - handle_index - 1) * sizeof(SeqRetimingHandle));
|
||||||
|
MEM_freeN(seq->retiming_handles);
|
||||||
|
seq->retiming_handles = handles;
|
||||||
|
seq->retiming_handle_num--;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SEQ_retiming_handle_speed_get(const Scene *scene,
|
||||||
|
const Sequence *seq,
|
||||||
|
const SeqRetimingHandle *handle)
|
||||||
|
{
|
||||||
|
if (handle->strip_frame_index == 0) {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SeqRetimingHandle *handle_prev = handle - 1;
|
||||||
|
|
||||||
|
const int frame_index_max = seq_time_strip_original_content_length_get(scene, seq) - 1;
|
||||||
|
const int frame_retimed_prev = round_fl_to_int(handle_prev->retiming_factor * frame_index_max);
|
||||||
|
const int frame_index_prev = handle_prev->strip_frame_index;
|
||||||
|
const int frame_retimed = round_fl_to_int(handle->retiming_factor * frame_index_max);
|
||||||
|
const int frame_index = handle->strip_frame_index;
|
||||||
|
|
||||||
|
const int fragment_length_retimed = frame_retimed - frame_retimed_prev;
|
||||||
|
const int fragment_length_original = frame_index - frame_index_prev;
|
||||||
|
|
||||||
|
const float speed = (float)fragment_length_retimed / (float)fragment_length_original;
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
class RetimingRange {
|
||||||
|
public:
|
||||||
|
int start, end;
|
||||||
|
float speed;
|
||||||
|
|
||||||
|
enum eIntersectType {
|
||||||
|
FULL,
|
||||||
|
PARTIAL_START,
|
||||||
|
PARTIAL_END,
|
||||||
|
INSIDE,
|
||||||
|
NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
RetimingRange(int start_frame, int end_frame, float speed)
|
||||||
|
: start(start_frame), end(end_frame), speed(speed)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const eIntersectType intersect_type(const RetimingRange &other) const
|
||||||
|
{
|
||||||
|
if (other.start <= start && other.end >= end) {
|
||||||
|
return FULL;
|
||||||
|
}
|
||||||
|
if (other.start > start && other.start < end && other.end > start && other.end < end) {
|
||||||
|
return INSIDE;
|
||||||
|
}
|
||||||
|
if (other.start > start && other.start < end) {
|
||||||
|
return PARTIAL_END;
|
||||||
|
}
|
||||||
|
if (other.end > start && other.end < end) {
|
||||||
|
return PARTIAL_START;
|
||||||
|
}
|
||||||
|
return NONE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RetimingRangeData {
|
||||||
|
public:
|
||||||
|
vector<RetimingRange> ranges;
|
||||||
|
RetimingRangeData(const Scene *scene, const Sequence *seq)
|
||||||
|
{
|
||||||
|
MutableSpan handles = SEQ_retiming_handles_get(seq);
|
||||||
|
for (const SeqRetimingHandle &handle : handles) {
|
||||||
|
if (handle.strip_frame_index == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const SeqRetimingHandle *handle_prev = &handle - 1;
|
||||||
|
float speed = SEQ_retiming_handle_speed_get(scene, seq, &handle);
|
||||||
|
int frame_start = SEQ_time_start_frame_get(seq) + handle_prev->strip_frame_index;
|
||||||
|
int frame_end = SEQ_time_start_frame_get(seq) + handle.strip_frame_index;
|
||||||
|
|
||||||
|
RetimingRange range = RetimingRange(frame_start, frame_end, speed);
|
||||||
|
ranges.push_back(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RetimingRangeData &operator*=(const RetimingRangeData rhs)
|
||||||
|
{
|
||||||
|
if (ranges.size() == 0) {
|
||||||
|
for (const RetimingRange &rhs_range : rhs.ranges) {
|
||||||
|
RetimingRange range = RetimingRange(rhs_range.start, rhs_range.end, rhs_range.speed);
|
||||||
|
ranges.push_back(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < ranges.size(); i++) {
|
||||||
|
RetimingRange &range = ranges[i];
|
||||||
|
for (const RetimingRange &rhs_range : rhs.ranges) {
|
||||||
|
if (range.intersect_type(rhs_range) == range.NONE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (range.intersect_type(rhs_range) == range.FULL) {
|
||||||
|
range.speed *= rhs_range.speed;
|
||||||
|
}
|
||||||
|
else if (range.intersect_type(rhs_range) == range.PARTIAL_START) {
|
||||||
|
RetimingRange range_left = RetimingRange(
|
||||||
|
range.start, rhs_range.end, range.speed * rhs_range.speed);
|
||||||
|
range.start = rhs_range.end + 1;
|
||||||
|
ranges.insert(ranges.begin() + i, range_left);
|
||||||
|
}
|
||||||
|
else if (range.intersect_type(rhs_range) == range.PARTIAL_END) {
|
||||||
|
RetimingRange range_left = RetimingRange(
|
||||||
|
range.start, rhs_range.start - 1, range.speed);
|
||||||
|
range.start = rhs_range.start;
|
||||||
|
ranges.insert(ranges.begin() + i, range_left);
|
||||||
|
}
|
||||||
|
else if (range.intersect_type(rhs_range) == range.INSIDE) {
|
||||||
|
RetimingRange range_left = RetimingRange(
|
||||||
|
range.start, rhs_range.start - 1, range.speed);
|
||||||
|
RetimingRange range_mid = RetimingRange(
|
||||||
|
rhs_range.start, rhs_range.start, rhs_range.speed * range.speed);
|
||||||
|
range.start = rhs_range.end + 1;
|
||||||
|
ranges.insert(ranges.begin() + i, range_left);
|
||||||
|
ranges.insert(ranges.begin() + i, range_mid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static RetimingRangeData seq_retiming_range_data_get(const Scene *scene, const Sequence *seq)
|
||||||
|
{
|
||||||
|
RetimingRangeData strip_retiming_data = RetimingRangeData(scene, seq);
|
||||||
|
|
||||||
|
const Sequence *meta_parent = seq_sequence_lookup_meta_by_seq(scene, seq);
|
||||||
|
if (meta_parent == nullptr) {
|
||||||
|
return strip_retiming_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetimingRangeData meta_retiming_data = RetimingRangeData(scene, meta_parent);
|
||||||
|
strip_retiming_data *= meta_retiming_data;
|
||||||
|
return strip_retiming_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SEQ_retiming_sound_animation_data_set(const Scene *scene, const Sequence *seq)
|
||||||
|
{
|
||||||
|
RetimingRangeData retiming_data = seq_retiming_range_data_get(scene, seq);
|
||||||
|
for (const RetimingRange &range : retiming_data.ranges) {
|
||||||
|
BKE_sound_set_scene_sound_pitch_constant_range(
|
||||||
|
seq->scene_sound, range.start, range.end, range.speed);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,22 +7,31 @@
|
||||||
* \ingroup bke
|
* \ingroup bke
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
#include "DNA_scene_types.h"
|
#include "DNA_scene_types.h"
|
||||||
#include "DNA_sequence_types.h"
|
#include "DNA_sequence_types.h"
|
||||||
|
|
||||||
#include "BLI_listbase.h"
|
#include "BLI_listbase.h"
|
||||||
#include "BLI_math.h"
|
#include "BLI_math.h"
|
||||||
|
|
||||||
|
#include "BKE_fcurve.h"
|
||||||
#include "BKE_movieclip.h"
|
#include "BKE_movieclip.h"
|
||||||
#include "BKE_scene.h"
|
#include "BKE_scene.h"
|
||||||
#include "BKE_sound.h"
|
#include "BKE_sound.h"
|
||||||
|
|
||||||
|
#include "DNA_anim_types.h"
|
||||||
#include "DNA_sound_types.h"
|
#include "DNA_sound_types.h"
|
||||||
|
|
||||||
#include "IMB_imbuf.h"
|
#include "IMB_imbuf.h"
|
||||||
|
|
||||||
|
#include "RNA_prototypes.h"
|
||||||
|
|
||||||
#include "SEQ_channels.h"
|
#include "SEQ_channels.h"
|
||||||
#include "SEQ_iterator.h"
|
#include "SEQ_iterator.h"
|
||||||
|
#include "SEQ_relations.h"
|
||||||
#include "SEQ_render.h"
|
#include "SEQ_render.h"
|
||||||
|
#include "SEQ_retiming.h"
|
||||||
#include "SEQ_sequencer.h"
|
#include "SEQ_sequencer.h"
|
||||||
#include "SEQ_time.h"
|
#include "SEQ_time.h"
|
||||||
#include "SEQ_transform.h"
|
#include "SEQ_transform.h"
|
||||||
|
@ -44,16 +53,21 @@ float seq_time_media_playback_rate_factor_get(const Scene *scene, const Sequence
|
||||||
return seq->media_playback_rate / scene_playback_rate;
|
return seq->media_playback_rate / scene_playback_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
float seq_time_playback_rate_factor_get(const Scene *scene, const Sequence *seq)
|
int seq_time_strip_original_content_length_get(const Scene *scene, const Sequence *seq)
|
||||||
{
|
{
|
||||||
return seq_time_media_playback_rate_factor_get(scene, seq) * seq->speed_factor;
|
if (seq->type == SEQ_TYPE_SOUND_RAM) {
|
||||||
|
return seq->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return seq->len / seq_time_media_playback_rate_factor_get(scene, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
float seq_give_frame_index(const Scene *scene, Sequence *seq, float timeline_frame)
|
float SEQ_give_frame_index(const Scene *scene, Sequence *seq, float timeline_frame)
|
||||||
{
|
{
|
||||||
float frame_index;
|
float frame_index;
|
||||||
float sta = SEQ_time_start_frame_get(seq);
|
float sta = SEQ_time_start_frame_get(seq);
|
||||||
float end = SEQ_time_content_end_frame_get(scene, seq) - 1;
|
float end = SEQ_time_content_end_frame_get(scene, seq) - 1;
|
||||||
|
const float length = seq_time_strip_original_content_length_get(scene, seq);
|
||||||
|
|
||||||
if (seq->type & SEQ_TYPE_EFFECT) {
|
if (seq->type & SEQ_TYPE_EFFECT) {
|
||||||
end = SEQ_time_right_handle_frame_get(scene, seq);
|
end = SEQ_time_right_handle_frame_get(scene, seq);
|
||||||
|
@ -70,9 +84,15 @@ float seq_give_frame_index(const Scene *scene, Sequence *seq, float timeline_fra
|
||||||
frame_index = timeline_frame - sta;
|
frame_index = timeline_frame - sta;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clamp frame index to strip frame range. */
|
if (SEQ_retiming_is_active(seq)) {
|
||||||
frame_index = clamp_f(frame_index, 0, end - sta);
|
const float retiming_factor = seq_retiming_evaluate(seq, frame_index);
|
||||||
frame_index *= seq_time_playback_rate_factor_get(scene, seq);
|
frame_index = retiming_factor * (length - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frame_index *= seq_time_media_playback_rate_factor_get(scene, seq);
|
||||||
|
/* Clamp frame index to strip frame range. */
|
||||||
|
frame_index = clamp_f(frame_index, 0, end - sta);
|
||||||
|
}
|
||||||
|
|
||||||
if (seq->strobe < 1.0f) {
|
if (seq->strobe < 1.0f) {
|
||||||
seq->strobe = 1.0f;
|
seq->strobe = 1.0f;
|
||||||
|
@ -442,27 +462,6 @@ bool SEQ_time_strip_intersects_frame(const Scene *scene,
|
||||||
(SEQ_time_right_handle_frame_get(scene, seq) > timeline_frame);
|
(SEQ_time_right_handle_frame_get(scene, seq) > timeline_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SEQ_time_speed_factor_set(const Scene *scene, Sequence *seq, const float speed_factor)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (seq->type == SEQ_TYPE_SOUND_RAM) {
|
|
||||||
seq->speed_factor = speed_factor;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const float left_handle_frame = SEQ_time_left_handle_frame_get(scene, seq);
|
|
||||||
const float unity_start_offset = seq->startofs * seq->speed_factor;
|
|
||||||
const float unity_end_offset = seq->endofs * seq->speed_factor;
|
|
||||||
/* Left handle is pivot point for content scaling - it must always show same frame. */
|
|
||||||
seq->speed_factor = speed_factor;
|
|
||||||
seq->startofs = unity_start_offset / speed_factor;
|
|
||||||
seq->start = left_handle_frame - seq->startofs;
|
|
||||||
seq->endofs = unity_end_offset / speed_factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEQ_time_update_meta_strip_range(scene, seq_sequence_lookup_meta_by_seq(scene, seq));
|
|
||||||
seq_time_update_effects_strip_range(scene, seq_sequence_lookup_effects_by_seq(scene, seq));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SEQ_time_has_left_still_frames(const Scene *scene, const Sequence *seq)
|
bool SEQ_time_has_left_still_frames(const Scene *scene, const Sequence *seq)
|
||||||
{
|
{
|
||||||
return SEQ_time_left_handle_frame_get(scene, seq) < SEQ_time_start_frame_get(seq);
|
return SEQ_time_left_handle_frame_get(scene, seq) < SEQ_time_start_frame_get(seq);
|
||||||
|
@ -480,11 +479,13 @@ bool SEQ_time_has_still_frames(const Scene *scene, const Sequence *seq)
|
||||||
|
|
||||||
int SEQ_time_strip_length_get(const Scene *scene, const Sequence *seq)
|
int SEQ_time_strip_length_get(const Scene *scene, const Sequence *seq)
|
||||||
{
|
{
|
||||||
if (seq->type == SEQ_TYPE_SOUND_RAM) {
|
if (SEQ_retiming_is_active(seq)) {
|
||||||
return seq->len;
|
SeqRetimingHandle *handle_start = seq->retiming_handles;
|
||||||
|
SeqRetimingHandle *handle_end = seq->retiming_handles + (SEQ_retiming_handles_count(seq) - 1);
|
||||||
|
return handle_end->strip_frame_index - handle_start->strip_frame_index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return seq->len / seq_time_playback_rate_factor_get(scene, seq);
|
return seq_time_strip_original_content_length_get(scene, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
float SEQ_time_start_frame_get(const Sequence *seq)
|
float SEQ_time_start_frame_get(const Sequence *seq)
|
||||||
|
|
|
@ -16,7 +16,6 @@ struct Scene;
|
||||||
struct Sequence;
|
struct Sequence;
|
||||||
struct SeqCollection;
|
struct SeqCollection;
|
||||||
|
|
||||||
float seq_give_frame_index(const struct Scene *scene, struct Sequence *seq, float timeline_frame);
|
|
||||||
void seq_update_sound_bounds_recursive(const struct Scene *scene, struct Sequence *metaseq);
|
void seq_update_sound_bounds_recursive(const struct Scene *scene, struct Sequence *metaseq);
|
||||||
|
|
||||||
/* Describes gap between strips in timeline. */
|
/* Describes gap between strips in timeline. */
|
||||||
|
@ -43,7 +42,9 @@ void seq_time_update_effects_strip_range(const struct Scene *scene, struct SeqCo
|
||||||
void seq_time_translate_handles(const struct Scene *scene, struct Sequence *seq, const int offset);
|
void seq_time_translate_handles(const struct Scene *scene, struct Sequence *seq, const int offset);
|
||||||
float seq_time_media_playback_rate_factor_get(const struct Scene *scene,
|
float seq_time_media_playback_rate_factor_get(const struct Scene *scene,
|
||||||
const struct Sequence *seq);
|
const struct Sequence *seq);
|
||||||
float seq_time_playback_rate_factor_get(const struct Scene *scene, const struct Sequence *seq);
|
int seq_time_strip_original_content_length_get(const struct Scene *scene,
|
||||||
|
const struct Sequence *seq);
|
||||||
|
float seq_retiming_evaluate(const struct Sequence *seq, const float frame_index);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue