From 465909054338b6e0ce9c1f62952694f114bce8d2 Mon Sep 17 00:00:00 2001 From: "Silc Lizard (Tokage) Renew" <61938263+TokageItLab@users.noreply.github.com> Date: Sun, 31 Dec 2023 16:05:34 +0900 Subject: [PATCH] Make consistent the retrieval of audio tracks --- doc/classes/Animation.xml | 2 + editor/animation_track_editor.cpp | 178 +++++++++++++++++- editor/animation_track_editor.h | 8 + .../4.2-stable.expected | 7 + scene/animation/animation_mixer.cpp | 10 +- scene/resources/animation.compat.inc | 5 + scene/resources/animation.cpp | 34 ++-- scene/resources/animation.h | 41 ++-- 8 files changed, 245 insertions(+), 40 deletions(-) diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index ebdca643ced4..b00889f48315 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -359,8 +359,10 @@ + 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. diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index caeae69ededf..02bb1d219e8c 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -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 &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) { @@ -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 &E : selection) { + if (animation->track_get_type(E.key.track) != Animation::TYPE_AUDIO) { + continue; + } + Ref 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 &E : selection) { + if (animation->track_get_type(E.key.track) != Animation::TYPE_AUDIO) { + continue; + } + Ref 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; @@ -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 &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 &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")); @@ -6590,6 +6680,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { } void AnimationTrackEditor::_cleanup_animation(Ref 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; @@ -6618,6 +6709,76 @@ void AnimationTrackEditor::_cleanup_animation(Ref 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 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 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; } @@ -6983,6 +7144,9 @@ 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); @@ -6990,6 +7154,9 @@ AnimationTrackEditor::AnimationTrackEditor() { 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(); @@ -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); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index bb84577ba31e..3f8dedb25cf0 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -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; @@ -654,9 +656,15 @@ public: 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, diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 32795330c451..2b7a39944ab4 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -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. diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 078d27aa5088..4240df9d277d 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -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; } @@ -1494,7 +1494,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } TrackCacheMethod *t = static_cast(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; } @@ -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); @@ -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; } diff --git a/scene/resources/animation.compat.inc b/scene/resources/animation.compat.inc index bbf016b34d1d..d7d886774888 100644 --- a/scene/resources/animation.compat.inc +++ b/scene/resources/animation.compat.inc @@ -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 diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 796a03bd8bac..079101f679d8 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1474,7 +1474,7 @@ void Animation::track_remove_key(int p_track, int p_idx) { emit_changed(); } -int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) const { +int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode, bool p_limit) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; @@ -1496,7 +1496,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) return key_index; } - int k = _find(tt->positions, p_time); + int k = _find(tt->positions, p_time, false, p_limit); if (k < 0 || k >= tt->positions.size()) { return -1; } @@ -1523,7 +1523,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) return key_index; } - int k = _find(rt->rotations, p_time); + int k = _find(rt->rotations, p_time, false, p_limit); if (k < 0 || k >= rt->rotations.size()) { return -1; } @@ -1550,7 +1550,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) return key_index; } - int k = _find(st->scales, p_time); + int k = _find(st->scales, p_time, false, p_limit); if (k < 0 || k >= st->scales.size()) { return -1; } @@ -1577,7 +1577,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) return key_index; } - int k = _find(bst->blend_shapes, p_time); + int k = _find(bst->blend_shapes, p_time, false, p_limit); if (k < 0 || k >= bst->blend_shapes.size()) { return -1; } @@ -1589,7 +1589,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) } break; case TYPE_VALUE: { ValueTrack *vt = static_cast(t); - int k = _find(vt->values, p_time); + int k = _find(vt->values, p_time, false, p_limit); if (k < 0 || k >= vt->values.size()) { return -1; } @@ -1601,7 +1601,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) } break; case TYPE_METHOD: { MethodTrack *mt = static_cast(t); - int k = _find(mt->methods, p_time); + int k = _find(mt->methods, p_time, false, p_limit); if (k < 0 || k >= mt->methods.size()) { return -1; } @@ -1613,7 +1613,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) } break; case TYPE_BEZIER: { BezierTrack *bt = static_cast(t); - int k = _find(bt->values, p_time); + int k = _find(bt->values, p_time, false, p_limit); if (k < 0 || k >= bt->values.size()) { return -1; } @@ -1625,7 +1625,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) } break; case TYPE_AUDIO: { AudioTrack *at = static_cast(t); - int k = _find(at->values, p_time); + int k = _find(at->values, p_time, false, p_limit); if (k < 0 || k >= at->values.size()) { return -1; } @@ -1637,7 +1637,7 @@ int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) } break; case TYPE_ANIMATION: { AnimationTrack *at = static_cast(t); - int k = _find(at->values, p_time); + int k = _find(at->values, p_time, false, p_limit); if (k < 0 || k >= at->values.size()) { return -1; } @@ -2332,7 +2332,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr } template -int Animation::_find(const Vector &p_keys, double p_time, bool p_backward) const { +int Animation::_find(const Vector &p_keys, double p_time, bool p_backward, bool p_limit) const { int len = p_keys.size(); if (len == 0) { return -2; @@ -2344,7 +2344,7 @@ int Animation::_find(const Vector &p_keys, double p_time, bool p_backward) co #ifdef DEBUG_ENABLED if (low > high) { - ERR_PRINT("low > high, this may be a bug"); + ERR_PRINT("low > high, this may be a bug."); } #endif @@ -2372,6 +2372,14 @@ int Animation::_find(const Vector &p_keys, double p_time, bool p_backward) co } } + if (p_limit) { + double diff = length - keys[middle].time; + if ((signbit(keys[middle].time) && !Math::is_zero_approx(keys[middle].time)) || (signbit(diff) && !Math::is_zero_approx(diff))) { + ERR_PRINT_ONCE_ED("Found the key outside the animation range. Consider using the clean-up option in AnimationTrackEditor to fix it."); + return -1; + } + } + return middle; } @@ -3804,7 +3812,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count); ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value); ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time); - ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST)); + ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode", "limit"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST), DEFVAL(false)); ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type); ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type); diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 115a1a50504d..5b27958005ba 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -44,13 +44,13 @@ public: typedef uint32_t TypeHash; enum TrackType { - TYPE_VALUE, ///< Set a value in a property, can be interpolated. - TYPE_POSITION_3D, ///< Position 3D track - TYPE_ROTATION_3D, ///< Rotation 3D track - TYPE_SCALE_3D, ///< Scale 3D track - TYPE_BLEND_SHAPE, ///< Blend Shape track - TYPE_METHOD, ///< Call any method on a specific node. - TYPE_BEZIER, ///< Bezier curve + TYPE_VALUE, // Set a value in a property, can be interpolated. + TYPE_POSITION_3D, // Position 3D track, can be compressed. + TYPE_ROTATION_3D, // Rotation 3D track, can be compressed. + TYPE_SCALE_3D, // Scale 3D track, can be compressed. + TYPE_BLEND_SHAPE, // Blend Shape track, can be compressed. + TYPE_METHOD, // Call any method on a specific node. + TYPE_BEZIER, // Bezier curve. TYPE_AUDIO, TYPE_ANIMATION, }; @@ -116,10 +116,10 @@ private: struct Key { real_t transition = 1.0; - double time = 0.0; // time in secs + double time = 0.0; // Time in secs. }; - // transform key holds either Vector3 or Quaternion + // Transform key holds either Vector3 or Quaternion. template struct TKey : public Key { T value; @@ -188,8 +188,8 @@ private: /* BEZIER TRACK */ struct BezierKey { - Vector2 in_handle; //relative (x always <0) - Vector2 out_handle; //relative (x always >0) + Vector2 in_handle; // Relative (x always <0) + Vector2 out_handle; // Relative (x always >0) real_t value = 0.0; #ifdef TOOLS_ENABLED HandleMode handle_mode = HANDLE_MODE_FREE; @@ -208,8 +208,8 @@ private: struct AudioKey { Ref stream; - real_t start_offset = 0.0; //offset from start - real_t end_offset = 0.0; //offset from end, if 0 then full length or infinite + real_t start_offset = 0.0; // Offset from start. + real_t end_offset = 0.0; // Offset from end, if 0 then full length or infinite. AudioKey() { } }; @@ -235,10 +235,6 @@ private: Vector tracks; - /* - template - int _insert_pos(double p_time, T& p_keys);*/ - template void _clear(T &p_keys); @@ -247,7 +243,7 @@ private: template - inline int _find(const Vector &p_keys, double p_time, bool p_backward = false) const; + inline int _find(const Vector &p_keys, double p_time, bool p_backward = false, bool p_limit = false) const; _FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const; _FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const; @@ -327,7 +323,7 @@ private: struct Compression { enum { MAX_DATA_TRACK_SIZE = 16384, - BLEND_SHAPE_RANGE = 8, // - 8.0 to 8.0 + BLEND_SHAPE_RANGE = 8, // -8.0 to 8.0. FORMAT_VERSION = 1 }; struct Page { @@ -337,7 +333,7 @@ private: uint32_t fps = 120; LocalVector pages; - LocalVector bounds; //used by position and scale tracks (which contain index to track and index to bounds). + LocalVector bounds; // Used by position and scale tracks (which contain index to track and index to bounds). bool enabled = false; } compression; @@ -386,6 +382,7 @@ protected: Vector3 _scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const; float _blend_shape_track_interpolate_bind_compat_86629(int p_track, double p_time) const; Variant _value_track_interpolate_bind_compat_86629(int p_track, double p_time) const; + int _track_find_key_bind_compat_86661(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const; static void _bind_compatibility_methods(); #endif // DISABLE_DEPRECATED @@ -417,7 +414,7 @@ public: void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition); void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value); void track_set_key_time(int p_track, int p_key_idx, double p_time); - int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const; + int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST, bool p_limit = false) const; void track_remove_key(int p_track, int p_idx); void track_remove_key_at_time(int p_track, double p_time); int track_get_key_count(int p_track) const; @@ -503,7 +500,7 @@ public: void clear(); void optimize(real_t p_allowed_velocity_err = 0.01, real_t p_allowed_angular_err = 0.01, int p_precision = 3); - void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests + void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests. // Helper functions for Variant. static bool is_variant_interpolatable(const Variant p_value);