Skip to content

Commit

Permalink
Add auto fit timeline and bezier scale on animation editor
Browse files Browse the repository at this point in the history
Add a button at the bottom of the animation editor that change the zoom based on the animation length and the bezier scale based on the values and handles of the displayed tracks. The icon and the tooltip of the button change depending if the bezier editor is displayed or not.

Some refactor was made in animation_track_editor.cpp to remove code duplication with the visibility check of the tracks.

This should help with the issue #85826
  • Loading branch information
Hilderin committed Jan 11, 2024
1 parent 3524346 commit 0e06b1c
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 32 deletions.
121 changes: 90 additions & 31 deletions editor/animation_bezier_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,23 +264,11 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
RBMap<String, Vector<int>> track_indices;
int track_count = animation->get_track_count();
for (int i = 0; i < track_count; ++i) {
if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER) {
if (!_is_track_displayed(i)) {
continue;
}

String base_path = animation->track_get_path(i);
if (is_filtered) {
if (root && root->has_node(base_path)) {
Node *node = root->get_node(base_path);
if (!node) {
continue; // No node, no filter.
}
if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
continue; // Skip track due to not selected.
}
}
}

int end = base_path.find(":");
if (end != -1) {
base_path = base_path.substr(0, end + 1);
Expand Down Expand Up @@ -518,28 +506,11 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
float scale = timeline->get_zoom_scale();

for (int i = 0; i < track_count; ++i) {
if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER || hidden_tracks.has(i)) {
continue;
}

if (hidden_tracks.has(i) || locked_tracks.has(i)) {
if (!_is_track_curves_displayed(i) || locked_tracks.has(i)) {
continue;
}

int key_count = animation->track_get_key_count(i);
String path = animation->track_get_path(i);

if (is_filtered) {
if (root && root->has_node(path)) {
Node *node = root->get_node(path);
if (!node) {
continue; // No node, no filter.
}
if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
continue; // Skip track due to not selected.
}
}
}

for (int j = 0; j < key_count; ++j) {
float offset = animation->track_get_key_time(i, j);
Expand Down Expand Up @@ -646,6 +617,43 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
}

// Check if a track is displayed in the bezier editor (track type = bezier and track not filtered).
bool AnimationBezierTrackEdit::_is_track_displayed(int p_track_index) {
if (animation->track_get_type(p_track_index) != Animation::TrackType::TYPE_BEZIER) {
return false;
}

if (is_filtered) {
String path = animation->track_get_path(p_track_index);
if (root && root->has_node(path)) {
Node *node = root->get_node(path);
if (!node) {
return false; // No node, no filter.
}
if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
return false; // Skip track due to not selected.
}
}
}

return true;
}

// Check if the curves for a track are displayed in the editor (not hidden). Includes the check on the track visibility.
bool AnimationBezierTrackEdit::_is_track_curves_displayed(int p_track_index) {
//Is the track is visible in the editor?
if (!_is_track_displayed(p_track_index)) {
return false;
}

//And curves visible?
if (hidden_tracks.has(p_track_index)) {
return false;
}

return true;
}

Ref<Animation> AnimationBezierTrackEdit::get_animation() const {
return animation;
}
Expand Down Expand Up @@ -739,6 +747,57 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
queue_redraw();
}

void AnimationBezierTrackEdit::auto_fit_vertically() {
timeline_v_zoom = 0;
timeline_v_scroll = 0;

int track_count = animation->get_track_count();
real_t minimum_value = INFINITY;
real_t maximum_value = -INFINITY;

int nb_track_visible = 0;
for (int i = 0; i < track_count; ++i) {
if (!_is_track_curves_displayed(i) || locked_tracks.has(i)) {
continue;
}

int key_count = animation->track_get_key_count(i);

for (int j = 0; j < key_count; ++j) {
real_t value = animation->bezier_track_get_key_value(i, j);

minimum_value = MIN(value, minimum_value);
maximum_value = MAX(value, maximum_value);

//We also want to includes the handles...
Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j);
Vector2 out_vec = animation->bezier_track_get_key_out_handle(i, j);

minimum_value = MIN(value + in_vec.y, minimum_value);
maximum_value = MAX(value + in_vec.y, maximum_value);
minimum_value = MIN(value + out_vec.y, minimum_value);
maximum_value = MAX(value + out_vec.y, maximum_value);
}

nb_track_visible++;
}

if (nb_track_visible == 0) {
//No visible track... we will not adjust the vertical zoom
return;
}

if (Math::is_finite(minimum_value) && Math::is_finite(maximum_value)) {
timeline_v_scroll = (maximum_value + minimum_value) / 2.0;
if (maximum_value - minimum_value > CMP_EPSILON) {
timeline_v_zoom = (maximum_value - minimum_value) / ((get_size().height - timeline->get_size().height) * 0.9);
} else {
timeline_v_zoom = CMP_EPSILON;
}
queue_redraw();
}
}

void AnimationBezierTrackEdit::_zoom_changed() {
queue_redraw();
play_position->queue_redraw();
Expand Down
3 changes: 3 additions & 0 deletions editor/animation_bezier_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class AnimationBezierTrackEdit : public Control {
void _menu_selected(int p_index);

void _play_position_draw();
bool _is_track_displayed(int p_track_index);
bool _is_track_curves_displayed(int p_track_index);

Vector2 insert_at_pos;

Expand Down Expand Up @@ -201,6 +203,7 @@ class AnimationBezierTrackEdit : public Control {
void set_editor(AnimationTrackEditor *p_editor);
void set_root(Node *p_root);
void set_filtered(bool p_filtered);
void auto_fit_vertically();

void set_play_position(real_t p_pos);
void update_play_position();
Expand Down
77 changes: 76 additions & 1 deletion editor/animation_track_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,11 @@ void AnimationTimelineEdit::_zoom_changed(double) {
}

float AnimationTimelineEdit::get_zoom_scale() const {
float zv = zoom->get_max() - zoom->get_value();
return _get_zoom_scale(zoom->get_value());
}

float AnimationTimelineEdit::_get_zoom_scale(double p_zoom_value) const {
float zv = zoom->get_max() - p_zoom_value;
if (zv < 1) {
zv = 1.0 - zv;
return Math::pow(1.0f + zv, 8.0f) * 100;
Expand Down Expand Up @@ -1632,6 +1636,42 @@ void AnimationTimelineEdit::set_zoom(Range *p_zoom) {
zoom->connect("value_changed", callable_mp(this, &AnimationTimelineEdit::_zoom_changed));
}

void AnimationTimelineEdit::auto_fit() {
if (!animation.is_valid()) {
return;
}

float anim_length = animation->get_length();

int timeline_width_pixels = get_size().width - get_buttons_width() - get_name_limit();

//I want a little buffer at the end... (5% looks nice and we should keep some space for the bezier handles)
timeline_width_pixels *= 0.95;

//The technique is to reuse the _get_zoom_scale function directly to be sure that the auto_fit is always calculated
//the same way as the zoom slider. It's a little bit more calculation then doing the inverse of get_zoom_scale but
//it's really easier to inderstand and should always be accurate.
float new_zoom = zoom->get_max();
while (true) {
double test_zoom_scale = _get_zoom_scale(new_zoom);

if (anim_length * test_zoom_scale <= timeline_width_pixels) {
//It fits...
break;
}

new_zoom -= zoom->get_step();

if (new_zoom <= zoom->get_min()) {
new_zoom = zoom->get_min();
break;
}
}

//Set the zoom value... the signal value_changed will be emitted and the timeline will be refreshed correctly!
zoom->set_value(new_zoom);
}

void AnimationTimelineEdit::set_track_edit(AnimationTrackEdit *p_track_edit) {
track_edit = p_track_edit;
}
Expand Down Expand Up @@ -3400,6 +3440,8 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
step->set_read_only(false);
snap->set_disabled(false);
snap_mode->set_disabled(false);
auto_fit->set_disabled(false);
auto_fit_bezier->set_disabled(false);

imported_anim_warning->hide();
for (int i = 0; i < animation->get_track_count(); i++) {
Expand All @@ -3420,6 +3462,8 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
snap->set_disabled(true);
snap_mode->set_disabled(true);
bezier_edit_icon->set_disabled(true);
auto_fit->set_disabled(true);
auto_fit_bezier->set_disabled(true);
}
}

Expand Down Expand Up @@ -4708,6 +4752,8 @@ void AnimationTrackEditor::_notification(int p_what) {
inactive_player_warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning")));
main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), get_editor_theme_icon(SNAME("Reload")));
auto_fit->set_icon(get_editor_theme_icon(SNAME("AnimationAutoFit")));
auto_fit_bezier->set_icon(get_editor_theme_icon(SNAME("AnimationAutoFitBezier")));
} break;

case NOTIFICATION_READY: {
Expand Down Expand Up @@ -5560,6 +5606,8 @@ void AnimationTrackEditor::_cancel_bezier_edit() {
bezier_edit->hide();
scroll->show();
bezier_edit_icon->set_pressed(false);
auto_fit->show();
auto_fit_bezier->hide();
}

void AnimationTrackEditor::_bezier_edit(int p_for_track) {
Expand All @@ -5568,6 +5616,8 @@ void AnimationTrackEditor::_bezier_edit(int p_for_track) {
bezier_edit->set_animation_and_track(animation, p_for_track, read_only);
scroll->hide();
bezier_edit->show();
auto_fit->hide();
auto_fit_bezier->show();
// Search everything within the track and curve - edit it.
}

Expand Down Expand Up @@ -6405,6 +6455,18 @@ bool AnimationTrackEditor::is_grouping_tracks() {
return !view_group->is_pressed();
}

void AnimationTrackEditor::_auto_fit() {
timeline->auto_fit();
}

void AnimationTrackEditor::_auto_fit_bezier() {
timeline->auto_fit();

if (bezier_edit->is_visible()) {
bezier_edit->auto_fit_vertically();
}
}

void AnimationTrackEditor::_selection_changed() {
if (selected_filter->is_pressed()) {
_update_tracks(); // Needs updatin.
Expand Down Expand Up @@ -6721,6 +6783,19 @@ AnimationTrackEditor::AnimationTrackEditor() {
bottom_hb->add_child(zoom);
timeline->set_zoom(zoom);

auto_fit = memnew(Button);
auto_fit->set_flat(true);
auto_fit->connect("pressed", callable_mp(this, &AnimationTrackEditor::_auto_fit));
auto_fit->set_tooltip_text(TTR("Adjust the timeline based on the animation."));
bottom_hb->add_child(auto_fit);

auto_fit_bezier = memnew(Button);
auto_fit_bezier->set_flat(true);
auto_fit_bezier->set_visible(false);
auto_fit_bezier->connect("pressed", callable_mp(this, &AnimationTrackEditor::_auto_fit_bezier));
auto_fit_bezier->set_tooltip_text(TTR("Adjust the timeline and the scale based on the animation and visible tracks."));
bottom_hb->add_child(auto_fit_bezier);

edit = memnew(MenuButton);
edit->set_shortcut_context(this);
edit->set_text(TTR("Edit"));
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 @@ -181,6 +181,8 @@ class AnimationTimelineEdit : public Range {
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _track_added(int p_track);

float _get_zoom_scale(double p_zoom_value) const;

protected:
static void _bind_methods();
void _notification(int p_what);
Expand All @@ -196,6 +198,7 @@ class AnimationTimelineEdit : public Range {
void set_track_edit(AnimationTrackEdit *p_track_edit);
void set_zoom(Range *p_zoom);
Range *get_zoom() const { return zoom; }
void auto_fit();

void set_play_position(float p_pos);
float get_play_position() const;
Expand Down Expand Up @@ -397,6 +400,8 @@ class AnimationTrackEditor : public VBoxContainer {
Button *snap = nullptr;
Button *bezier_edit_icon = nullptr;
OptionButton *snap_mode = nullptr;
Button *auto_fit = nullptr;
Button *auto_fit_bezier = nullptr;

Button *imported_anim_warning = nullptr;
void _show_imported_anim_warning();
Expand Down Expand Up @@ -576,6 +581,9 @@ class AnimationTrackEditor : public VBoxContainer {
Button *view_group = nullptr;
Button *selected_filter = nullptr;

void _auto_fit();
void _auto_fit_bezier();

void _selection_changed();

ConfirmationDialog *track_copy_dialog = nullptr;
Expand Down
2 changes: 2 additions & 0 deletions editor/icons/AnimationAutoFit.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions editor/icons/AnimationAutoFitBezier.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 0e06b1c

Please sign in to comment.