Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the retrieval of audio tracks consistent and implement trimming to AnimationTrackEditor shortcut and clean-up #86661

Merged
merged 1 commit into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/classes/Animation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,10 @@
<param index="0" name="track_idx" type="int" />
<param index="1" name="time" type="float" />
<param index="2" name="find_mode" type="int" enum="Animation.FindMode" default="0" />
<param index="3" name="limit" type="bool" default="false" />
<description>
Finds the key index by time in a given track. Optionally, only find it if the approx/exact time is given.
If [param limit] is [code]true[/code], it does not return keys outside the animation range.
</description>
</method>
<method name="track_get_interpolation_loop_wrap" qualifiers="const">
Expand Down
178 changes: 177 additions & 1 deletion editor/animation_track_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5904,6 +5904,16 @@ bool AnimationTrackEditor::_is_track_compatible(int p_target_track_idx, Variant:
void AnimationTrackEditor::_edit_menu_about_to_popup() {
AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
edit->get_popup()->set_item_disabled(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), !player->can_apply_reset());

bool has_length = false;
for (const KeyValue<SelectedKey, KeyInfo> &E : selection) {
if (animation->track_get_type(E.key.track) == Animation::TYPE_AUDIO) {
has_length = true;
break;
}
}
edit->get_popup()->set_item_disabled(edit->get_popup()->get_item_index(EDIT_SET_START_OFFSET), !has_length);
edit->get_popup()->set_item_disabled(edit->get_popup()->get_item_index(EDIT_SET_END_OFFSET), !has_length);
}

void AnimationTrackEditor::goto_prev_step(bool p_from_mouse_event) {
Expand Down Expand Up @@ -6229,6 +6239,56 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
undo_redo->commit_action();
} break;

case EDIT_SET_START_OFFSET: {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Set Start Offset"), UndoRedo::MERGE_ENDS);
for (const KeyValue<SelectedKey, KeyInfo> &E : selection) {
if (animation->track_get_type(E.key.track) != Animation::TYPE_AUDIO) {
continue;
}
Ref<AudioStream> stream = animation->audio_track_get_key_stream(E.key.track, E.key.key);
double len = stream->get_length() - animation->audio_track_get_key_end_offset(E.key.track, E.key.key);
real_t prev_offset = animation->audio_track_get_key_start_offset(E.key.track, E.key.key);
double prev_time = animation->track_get_key_time(E.key.track, E.key.key);
float cur_time = timeline->get_play_position();
float diff = prev_offset + cur_time - prev_time;
float destination = cur_time - MIN(0, diff);
if (diff >= len || animation->track_find_key(E.key.track, destination, Animation::FIND_MODE_EXACT) >= 0) {
continue;
}
undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_start_offset", E.key.track, E.key.key, diff);
undo_redo->add_do_method(animation.ptr(), "track_set_key_time", E.key.track, E.key.key, destination);
undo_redo->add_undo_method(animation.ptr(), "track_set_key_time", E.key.track, E.key.key, prev_time);
undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_start_offset", E.key.track, E.key.key, prev_offset);
}
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
undo_redo->commit_action();
} break;
case EDIT_SET_END_OFFSET: {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Set End Offset"), UndoRedo::MERGE_ENDS);
for (const KeyValue<SelectedKey, KeyInfo> &E : selection) {
if (animation->track_get_type(E.key.track) != Animation::TYPE_AUDIO) {
continue;
}
Ref<AudioStream> stream = animation->audio_track_get_key_stream(E.key.track, E.key.key);
double len = stream->get_length() - animation->audio_track_get_key_start_offset(E.key.track, E.key.key);
real_t prev_offset = animation->audio_track_get_key_end_offset(E.key.track, E.key.key);
double prev_time = animation->track_get_key_time(E.key.track, E.key.key);
float cur_time = timeline->get_play_position();
float diff = prev_time + len - cur_time;
if (diff >= len) {
continue;
}
undo_redo->add_do_method(animation.ptr(), "audio_track_set_key_end_offset", E.key.track, E.key.key, diff);
undo_redo->add_undo_method(animation.ptr(), "audio_track_set_key_end_offset", E.key.track, E.key.key, prev_offset);
}
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
undo_redo->commit_action();
} break;

case EDIT_EASE_SELECTION: {
ease_dialog->popup_centered(Size2(200, 100) * EDSCALE);
} break;
Expand Down Expand Up @@ -6342,6 +6402,36 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
case EDIT_PASTE_KEYS: {
_anim_paste_keys(-1.0, -1.0);
} break;
case EDIT_MOVE_FIRST_SELECTED_KEY_TO_CURSOR: {
if (moving_selection || selection.is_empty()) {
break;
}
real_t from_t = 1e20;
for (const KeyValue<SelectedKey, KeyInfo> &E : selection) {
real_t t = animation->track_get_key_time(E.key.track, E.key.key);
if (t < from_t) {
from_t = t;
}
}
_move_selection_begin();
_move_selection(timeline->get_play_position() - from_t);
_move_selection_commit();
} break;
case EDIT_MOVE_LAST_SELECTED_KEY_TO_CURSOR: {
if (moving_selection || selection.is_empty()) {
break;
}
real_t to_t = -1e20;
for (const KeyValue<SelectedKey, KeyInfo> &E : selection) {
real_t t = animation->track_get_key_time(E.key.track, E.key.key);
if (t > to_t) {
to_t = t;
}
}
_move_selection_begin();
_move_selection(timeline->get_play_position() - to_t);
_move_selection_commit();
} break;
case EDIT_ADD_RESET_KEY: {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Animation Add RESET Keys"));
Expand Down Expand Up @@ -6590,6 +6680,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}

void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
_clear_selection();
for (int i = 0; i < p_animation->get_track_count(); i++) {
if (!root->has_node_and_resource(p_animation->track_get_path(i))) {
continue;
Expand Down Expand Up @@ -6618,6 +6709,76 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
continue;
}

if (cleanup_keys_with_trimming_head->is_pressed()) {
// Check is necessary because if there is already a key in position 0, it should not be replaced.
if (p_animation->track_get_type(i) == Animation::TYPE_AUDIO && p_animation->track_find_key(i, 0, Animation::FIND_MODE_EXACT) < 0) {
for (int j = 0; j < p_animation->track_get_key_count(i); j++) {
double t = p_animation->track_get_key_time(i, j);
if (t < 0) {
if (j == p_animation->track_get_key_count(i) - 1 || (j + 1 < p_animation->track_get_key_count(i) && p_animation->track_get_key_time(i, j + 1) > 0)) {
Ref<AudioStream> stream = p_animation->audio_track_get_key_stream(i, j);
double len = stream->get_length() - p_animation->audio_track_get_key_end_offset(i, j);
double prev_offset = p_animation->audio_track_get_key_start_offset(i, j);
double prev_time = p_animation->track_get_key_time(i, j);
double diff = prev_offset - prev_time;
if (diff >= len) {
p_animation->track_remove_key(i, j);
j--;
continue;
}
p_animation->audio_track_set_key_start_offset(i, j, diff);
p_animation->track_set_key_time(i, j, 0);
} else {
p_animation->track_remove_key(i, j);
j--;
}
}
}
} else {
for (int j = 0; j < p_animation->track_get_key_count(i); j++) {
double t = p_animation->track_get_key_time(i, j);
if (t < 0) {
p_animation->track_remove_key(i, j);
j--;
}
}
}
}

if (cleanup_keys_with_trimming_end->is_pressed()) {
if (p_animation->track_get_type(i) == Animation::TYPE_AUDIO) {
for (int j = 0; j < p_animation->track_get_key_count(i); j++) {
double t = p_animation->track_get_key_time(i, j);
if (t <= p_animation->get_length() && (j == p_animation->track_get_key_count(i) - 1 || (j + 1 < p_animation->track_get_key_count(i) && p_animation->track_get_key_time(i, j + 1) > p_animation->get_length()))) {
Ref<AudioStream> stream = animation->audio_track_get_key_stream(i, j);
double len = stream->get_length() - animation->audio_track_get_key_start_offset(i, j);
if (t + len < p_animation->get_length()) {
continue;
}
double prev_time = animation->track_get_key_time(i, j);
double diff = prev_time + len - p_animation->get_length();
if (diff >= len) {
p_animation->track_remove_key(i, j);
j--;
continue;
}
p_animation->audio_track_set_key_end_offset(i, j, diff);
} else if (t > p_animation->get_length()) {
p_animation->track_remove_key(i, j);
j--;
}
}
} else {
for (int j = 0; j < p_animation->track_get_key_count(i); j++) {
double t = p_animation->track_get_key_time(i, j);
if (t > p_animation->get_length()) {
p_animation->track_remove_key(i, j);
j--;
}
}
}
}

if (!prop_exists || p_animation->track_get_type(i) != Animation::TYPE_VALUE || !cleanup_keys->is_pressed()) {
continue;
}
Expand Down Expand Up @@ -6983,13 +7144,19 @@ AnimationTrackEditor::AnimationTrackEditor() {
edit->get_popup()->add_item(TTR("Scale Selection"), EDIT_SCALE_SELECTION);
edit->get_popup()->add_item(TTR("Scale From Cursor"), EDIT_SCALE_FROM_CURSOR);
edit->get_popup()->add_separator();
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/set_start_offset", TTR("Set Start Offset (Audio)"), KeyModifierMask::CMD_OR_CTRL | Key::BRACKETLEFT), EDIT_SET_START_OFFSET);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/set_end_offset", TTR("Set End Offset (Audio)"), KeyModifierMask::CMD_OR_CTRL | Key::BRACKETRIGHT), EDIT_SET_END_OFFSET);
edit->get_popup()->add_separator();
edit->get_popup()->add_item(TTR("Make Easing Selection"), EDIT_EASE_SELECTION);
edit->get_popup()->add_separator();
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/duplicate_selected_keys", TTR("Duplicate Selected Keys"), KeyModifierMask::CMD_OR_CTRL | Key::D), EDIT_DUPLICATE_SELECTED_KEYS);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/copy_selected_keys", TTR("Copy Selected Keys"), KeyModifierMask::CMD_OR_CTRL | Key::C), EDIT_COPY_KEYS);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/paste_keys", TTR("Paste Keys"), KeyModifierMask::CMD_OR_CTRL | Key::V), EDIT_PASTE_KEYS);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/add_reset_value", TTR("Add RESET Value(s)")));
edit->get_popup()->add_separator();
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/move_first_selected_key_to_cursor", TTR("Move First Selected Key to Cursor"), Key::BRACKETLEFT), EDIT_MOVE_FIRST_SELECTED_KEY_TO_CURSOR);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/move_last_selected_key_to_cursor", TTR("Move Last Selected Key to Cursor"), Key::BRACKETRIGHT), EDIT_MOVE_LAST_SELECTED_KEY_TO_CURSOR);
edit->get_popup()->add_separator();
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/delete_selection", TTR("Delete Selection"), Key::KEY_DELETE), EDIT_DELETE_SELECTION);

edit->get_popup()->add_separator();
Expand Down Expand Up @@ -7083,12 +7250,21 @@ AnimationTrackEditor::AnimationTrackEditor() {
optimize_dialog->connect("confirmed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_OPTIMIZE_ANIMATION_CONFIRM));

//

cleanup_dialog = memnew(ConfirmationDialog);
add_child(cleanup_dialog);
VBoxContainer *cleanup_vb = memnew(VBoxContainer);
cleanup_dialog->add_child(cleanup_vb);

cleanup_keys_with_trimming_head = memnew(CheckBox);
cleanup_keys_with_trimming_head->set_text(TTR("Trim keys placed in negative time"));
cleanup_keys_with_trimming_head->set_pressed(true);
cleanup_vb->add_child(cleanup_keys_with_trimming_head);

cleanup_keys_with_trimming_end = memnew(CheckBox);
cleanup_keys_with_trimming_end->set_text(TTR("Trim keys placed exceed the animation length"));
cleanup_keys_with_trimming_end->set_pressed(true);
cleanup_vb->add_child(cleanup_keys_with_trimming_end);

cleanup_keys = memnew(CheckBox);
cleanup_keys->set_text(TTR("Remove invalid keys"));
cleanup_keys->set_pressed(true);
Expand Down
8 changes: 8 additions & 0 deletions editor/animation_track_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,8 @@ class AnimationTrackEditor : public VBoxContainer {
SpinBox *optimize_precision_error = nullptr;

ConfirmationDialog *cleanup_dialog = nullptr;
CheckBox *cleanup_keys_with_trimming_head = nullptr;
CheckBox *cleanup_keys_with_trimming_end = nullptr;
CheckBox *cleanup_keys = nullptr;
CheckBox *cleanup_tracks = nullptr;
CheckBox *cleanup_all = nullptr;
Expand Down Expand Up @@ -654,9 +656,15 @@ class AnimationTrackEditor : public VBoxContainer {
EDIT_SCALE_SELECTION,
EDIT_SCALE_FROM_CURSOR,
EDIT_SCALE_CONFIRM,
EDIT_SET_START_OFFSET,
EDIT_SET_END_OFFSET,
EDIT_EASE_SELECTION,
EDIT_EASE_CONFIRM,
EDIT_DUPLICATE_SELECTED_KEYS,
EDIT_DUPLICATE_SELECTION,
EDIT_DUPLICATE_TRANSPOSED,
EDIT_MOVE_FIRST_SELECTED_KEY_TO_CURSOR,
EDIT_MOVE_LAST_SELECTED_KEY_TO_CURSOR,
EDIT_ADD_RESET_KEY,
EDIT_DELETE_SELECTION,
EDIT_GOTO_NEXT_STEP,
Expand Down
7 changes: 7 additions & 0 deletions misc/extension_api_validation/4.2-stable.expected
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,10 @@ Validate extension JSON: Error: Field 'classes/Animation/methods/blend_shape_tra
Validate extension JSON: Error: Field 'classes/Animation/methods/value_track_interpolate/arguments': size changed value in new API, from 2 to 3.

Added optional argument to track_interpolate to treat playing backward correctly. Compatibility method registered.


GH-86661
--------
Validate extension JSON: Error: Field 'classes/Animation/methods/track_find_key/arguments': size changed value in new API, from 3 to 4.

Added optional argument to track_find_key to avoid finding keys out of the animation range. Compatibility method registered.
10 changes: 6 additions & 4 deletions scene/animation/animation_mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1458,7 +1458,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
} else {
if (seeked) {
int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true);
if (idx < 0) {
continue;
}
Expand Down Expand Up @@ -1494,7 +1494,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
if (seeked) {
int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true);
if (idx < 0) {
continue;
}
Expand Down Expand Up @@ -1541,7 +1541,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
// Find stream.
int idx = -1;
if (seeked) {
idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
// Audio key may be playbacked from the middle, should use FIND_MODE_NEAREST.
// Then, check the current playing stream to prevent to playback doubly.
idx = a->track_find_key(i, time, Animation::FIND_MODE_NEAREST, true);
// Discard previous stream when seeking.
if (map.has(idx)) {
t->audio_stream_playback->stop_stream(map[idx].index);
Expand Down Expand Up @@ -1609,7 +1611,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
if (seeked) {
// Seek.
int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true);
if (idx < 0) {
continue;
}
Expand Down
5 changes: 5 additions & 0 deletions scene/resources/animation.compat.inc
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,17 @@ Variant Animation::_value_track_interpolate_bind_compat_86629(int p_track, doubl
return value_track_interpolate(p_track, p_time, false);
}

int Animation::_track_find_key_bind_compat_86661(int p_track, double p_time, FindMode p_find_mode) const {
return track_find_key(p_track, p_time, p_find_mode, false);
}

void Animation::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("position_track_interpolate", "track_idx", "time_sec"), &Animation::_position_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("rotation_track_interpolate", "track_idx", "time_sec"), &Animation::_rotation_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::_scale_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::_blend_shape_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::_value_track_interpolate_bind_compat_86629);
ClassDB::bind_compatibility_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::_track_find_key_bind_compat_86661, DEFVAL(FIND_MODE_NEAREST));
}

#endif // DISABLE_DEPRECATED
Loading
Loading