Skip to content

Commit

Permalink
Add animation reset track feature
Browse files Browse the repository at this point in the history
As a bonus, to have consistency between use Beziers and create insert tracks, use Beziers also gets a default via editor settings that is used when the confirmation dialog is disabled, instead of just falling back to creating non-Bezier tracks.
  • Loading branch information
RandomShaper committed Dec 20, 2020
1 parent 1f52782 commit b7367ac
Show file tree
Hide file tree
Showing 12 changed files with 296 additions and 77 deletions.
2 changes: 1 addition & 1 deletion core/object/undo_redo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@

#include "undo_redo.h"

#include "core/io/resource.h"
#include "core/os/os.h"
#include "core/resource.h"

void UndoRedo::_discard_redo() {
if (current_action == actions.size() - 1) {
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/AnimationPlayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@
<member name="playback_speed" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0">
The speed scaling ratio. For instance, if this value is 1, then the animation plays at normal speed. If it's 0.5, then it plays at half speed. If it's 2, then it plays at double speed.
</member>
<member name="reset_on_save" type="bool" setter="set_reset_on_save_enabled" getter="is_reset_on_save_enabled" default="true">
This is used by the editor. If set to [code]true[/code], the scene will be saved with the effects of the reset animation applied (as if it had been seeked to time 0), then reverted after saving.
In other words, the saved scene file will contain the "default pose", as defined by the reset animation, if any, with the editor keeping the values that the nodes had before saving.
</member>
<member name="root_node" type="NodePath" setter="set_root" getter="get_root" default="NodePath(&quot;..&quot;)">
The node from which node path references will travel.
</member>
Expand Down
186 changes: 141 additions & 45 deletions editor/animation_track_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor_node.h"
#include "editor_scale.h"
#include "scene/animation/animation_player.h"
#include "scene/main/window.h"
#include "servers/audio/audio_stream.h"

Expand Down Expand Up @@ -3299,6 +3300,19 @@ void AnimationTrackEditor::set_anim_pos(float p_pos) {
bezier_edit->set_play_position(p_pos);
}

static bool track_type_is_resettable(Animation::TrackType p_type) {
switch (p_type) {
case Animation::TYPE_VALUE:
[[fallthrough]];
case Animation::TYPE_BEZIER:
[[fallthrough]];
case Animation::TYPE_TRANSFORM:
return true;
default:
return false;
}
}

void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
if (insert_frame != Engine::get_singleton()->get_frames_drawn()) {
//clear insert list for the frame if frame changed
Expand All @@ -3319,64 +3333,85 @@ void AnimationTrackEditor::_query_insert(const InsertData &p_id) {

insert_data.push_back(p_id);

bool reset_allowed = true;
AnimationPlayer *player = AnimationPlayerEditor::singleton->get_player();
if (player->has_animation("RESET") && player->get_animation("RESET") == animation) {
// Avoid messing with the reset animation itself
reset_allowed = false;
} else {
bool some_resettable = false;
for (int i = 0; i < insert_data.size(); i++) {
if (track_type_is_resettable(insert_data[i].type)) {
some_resettable = true;
break;
}
}
if (!some_resettable) {
reset_allowed = false;
}
}

if (p_id.track_idx == -1) {
if (bool(EDITOR_DEF("editors/animation/confirm_insert_track", true))) {
//potential new key, does not exist
int num_tracks = 0;
bool all_bezier = true;
for (int i = 0; i < insert_data.size(); i++) {
if (insert_data[i].type != Animation::TYPE_VALUE && insert_data[i].type != Animation::TYPE_BEZIER) {
all_bezier = false;
}
int num_tracks = 0;
bool all_bezier = true;
for (int i = 0; i < insert_data.size(); i++) {
if (insert_data[i].type != Animation::TYPE_VALUE && insert_data[i].type != Animation::TYPE_BEZIER) {
all_bezier = false;
}

if (insert_data[i].track_idx == -1) {
++num_tracks;
}
if (insert_data[i].track_idx == -1) {
++num_tracks;
}

if (insert_data[i].type != Animation::TYPE_VALUE) {
continue;
}
if (insert_data[i].type != Animation::TYPE_VALUE) {
continue;
}

switch (insert_data[i].value.get_type()) {
case Variant::INT:
case Variant::FLOAT:
case Variant::VECTOR2:
case Variant::VECTOR3:
case Variant::QUAT:
case Variant::PLANE:
case Variant::COLOR: {
// Valid.
} break;
default: {
all_bezier = false;
}
switch (insert_data[i].value.get_type()) {
case Variant::INT:
case Variant::FLOAT:
case Variant::VECTOR2:
case Variant::VECTOR3:
case Variant::QUAT:
case Variant::PLANE:
case Variant::COLOR: {
// Valid.
} break;
default: {
all_bezier = false;
}
}
}

if (bool(EDITOR_DEF("editors/animation/confirm_insert_track", true))) {
//potential new key, does not exist
if (num_tracks == 1) {
insert_confirm_text->set_text(vformat(TTR("Create new track for %s and insert key?"), p_id.query));
} else {
insert_confirm_text->set_text(vformat(TTR("Create %d new tracks and insert keys?"), num_tracks));
}

insert_confirm_bezier->set_visible(all_bezier);
insert_confirm_reset->set_visible(reset_allowed);

insert_confirm->get_ok_button()->set_text(TTR("Create"));
insert_confirm->popup_centered();
insert_query = true;
} else {
call_deferred("_insert_delay");
call_deferred("_insert_delay", reset_allowed && EDITOR_GET("editors/animation/default_create_reset_tracks"), all_bezier && EDITOR_GET("editors/animation/default_create_bezier_tracks"));
insert_queue = true;
}

} else {
if (!insert_query && !insert_queue) {
call_deferred("_insert_delay");
// Create Beziers wouldn't make sense in this case, where no tracks are being created
call_deferred("_insert_delay", reset_allowed && EDITOR_GET("editors/animation/default_create_reset_tracks"), false);
insert_queue = true;
}
}
}

void AnimationTrackEditor::_insert_delay() {
void AnimationTrackEditor::_insert_delay(bool p_create_reset, bool p_create_beziers) {
if (insert_query) {
//discard since it's entered into query mode
insert_queue = false;
Expand All @@ -3385,13 +3420,18 @@ void AnimationTrackEditor::_insert_delay() {

undo_redo->create_action(TTR("Anim Insert"));

int last_track = animation->get_track_count();
Ref<Animation> reset_anim;
if (p_create_reset) {
reset_anim = _create_and_get_reset_animation();
}

TrackIndices next_tracks(animation.ptr(), reset_anim.ptr());
bool advance = false;
while (insert_data.size()) {
if (insert_data.front()->get().advance) {
advance = true;
}
last_track = _confirm_insert(insert_data.front()->get(), last_track);
next_tracks = _confirm_insert(insert_data.front()->get(), next_tracks, p_create_reset, p_create_beziers);
insert_data.pop_front();
}

Expand Down Expand Up @@ -3682,12 +3722,34 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari
}
}

Ref<Animation> AnimationTrackEditor::_create_and_get_reset_animation() {
AnimationPlayer *player = AnimationPlayerEditor::singleton->get_player();
if (player->has_animation("RESET")) {
return player->get_animation("RESET");
} else {
Ref<Animation> reset_anim;
reset_anim.instance();
reset_anim->set_length(ANIM_MIN_LENGTH);
undo_redo->add_do_method(player, "add_animation", "RESET", reset_anim);
undo_redo->add_do_method(AnimationPlayerEditor::singleton, "_animation_player_changed", player);
undo_redo->add_undo_method(player, "remove_animation", "RESET");
undo_redo->add_undo_method(AnimationPlayerEditor::singleton, "_animation_player_changed", player);
return reset_anim;
}
}

void AnimationTrackEditor::_confirm_insert_list() {
undo_redo->create_action(TTR("Anim Create & Insert"));

int last_track = animation->get_track_count();
bool create_reset = insert_confirm_reset->is_visible() && insert_confirm_reset->is_pressed();
Ref<Animation> reset_anim;
if (create_reset) {
reset_anim = _create_and_get_reset_animation();
}

TrackIndices next_tracks(animation.ptr(), reset_anim.ptr());
while (insert_data.size()) {
last_track = _confirm_insert(insert_data.front()->get(), last_track, insert_confirm_bezier->is_pressed());
next_tracks = _confirm_insert(insert_data.front()->get(), next_tracks, create_reset, insert_confirm_bezier->is_pressed());
insert_data.pop_front();
}

Expand Down Expand Up @@ -3807,11 +3869,7 @@ static Vector<String> _get_bezier_subindices_for_type(Variant::Type p_type, bool
return subindices;
}

int AnimationTrackEditor::_confirm_insert(InsertData p_id, int p_last_track, bool p_create_beziers) {
if (p_last_track == -1) {
p_last_track = animation->get_track_count();
}

AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertData p_id, TrackIndices p_next_tracks, bool p_create_reset, bool p_create_beziers) {
bool created = false;
if (p_id.track_idx < 0) {
if (p_create_beziers) {
Expand All @@ -3823,10 +3881,10 @@ int AnimationTrackEditor::_confirm_insert(InsertData p_id, int p_last_track, boo
id.type = Animation::TYPE_BEZIER;
id.value = p_id.value.get(subindices[i].substr(1, subindices[i].length()));
id.path = String(p_id.path) + subindices[i];
_confirm_insert(id, p_last_track + i);
p_next_tracks = _confirm_insert(id, p_next_tracks, p_create_reset, false);
}

return p_last_track + subindices.size();
return p_next_tracks;
}
}
created = true;
Expand Down Expand Up @@ -3863,7 +3921,7 @@ int AnimationTrackEditor::_confirm_insert(InsertData p_id, int p_last_track, boo
}
}

p_id.track_idx = p_last_track;
p_id.track_idx = p_next_tracks.normal;

undo_redo->add_do_method(animation.ptr(), "add_track", p_id.type);
undo_redo->add_do_method(animation.ptr(), "track_set_path", p_id.track_idx, p_id.path);
Expand Down Expand Up @@ -3915,7 +3973,7 @@ int AnimationTrackEditor::_confirm_insert(InsertData p_id, int p_last_track, boo
// Just remove the track.
undo_redo->add_undo_method(this, "_clear_selection", false);
undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
p_last_track++;
p_next_tracks.normal++;
} else {
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_id.track_idx, time);
int existing = animation->track_find_key(p_id.track_idx, time, true);
Expand All @@ -3926,9 +3984,27 @@ int AnimationTrackEditor::_confirm_insert(InsertData p_id, int p_last_track, boo
}
}

if (p_create_reset && track_type_is_resettable(p_id.type)) {
bool create_reset_track = true;
Animation *reset_anim = AnimationPlayerEditor::singleton->get_player()->get_animation("RESET").ptr();
for (int i = 0; i < reset_anim->get_track_count(); i++) {
if (reset_anim->track_get_path(i) == p_id.path) {
create_reset_track = false;
break;
}
}
if (create_reset_track) {
undo_redo->add_do_method(reset_anim, "add_track", p_id.type);
undo_redo->add_do_method(reset_anim, "track_set_path", p_next_tracks.reset, p_id.path);
undo_redo->add_do_method(reset_anim, "track_insert_key", p_next_tracks.reset, 0.0f, value);
undo_redo->add_undo_method(reset_anim, "remove_track", reset_anim->get_track_count());
p_next_tracks.reset++;
}
}

undo_redo->commit_action();

return p_last_track;
return p_next_tracks;
}

void AnimationTrackEditor::show_select_node_warning(bool p_show) {
Expand Down Expand Up @@ -4224,6 +4300,7 @@ void AnimationTrackEditor::_notification(int p_what) {
selected_filter->set_icon(get_theme_icon("AnimationFilter", "EditorIcons"));
imported_anim_warning->set_icon(get_theme_icon("NodeWarning", "EditorIcons"));
main_panel->add_theme_style_override("panel", get_theme_stylebox("bg", "Tree"));
edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), get_theme_icon("Reload", "EditorIcons"));
}

if (p_what == NOTIFICATION_READY) {
Expand Down Expand Up @@ -5056,6 +5133,11 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) {
}
}

void AnimationTrackEditor::_edit_menu_about_to_popup() {
AnimationPlayer *player = AnimationPlayerEditor::singleton->get_player();
edit->get_popup()->set_item_disabled(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), !player->can_apply_reset());
}

void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
last_menu_track_opt = p_option;
switch (p_option) {
Expand Down Expand Up @@ -5377,6 +5459,10 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
set_anim_pos(pos);
emit_signal("timeline_changed", pos, true);

} break;
case EDIT_APPLY_RESET: {
AnimationPlayerEditor::singleton->get_player()->apply_reset(true);

} break;
case EDIT_OPTIMIZE_ANIMATION: {
optimize_dialog->popup_centered(Size2(250, 180) * EDSCALE);
Expand Down Expand Up @@ -5710,10 +5796,13 @@ AnimationTrackEditor::AnimationTrackEditor() {
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_next_step", TTR("Go to Next Step"), KEY_MASK_CMD | KEY_RIGHT), EDIT_GOTO_NEXT_STEP);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_prev_step", TTR("Go to Previous Step"), KEY_MASK_CMD | KEY_LEFT), EDIT_GOTO_PREV_STEP);
edit->get_popup()->add_separator();
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/apply_reset", TTR("Apply Reset")), EDIT_APPLY_RESET);
edit->get_popup()->add_separator();
edit->get_popup()->add_item(TTR("Optimize Animation"), EDIT_OPTIMIZE_ANIMATION);
edit->get_popup()->add_item(TTR("Clean-Up Animation"), EDIT_CLEAN_UP_ANIMATION);

edit->get_popup()->connect("id_pressed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed));
edit->get_popup()->connect("about_to_popup", callable_mp(this, &AnimationTrackEditor::_edit_menu_about_to_popup));

pick_track = memnew(SceneTreeDialog);
add_child(pick_track);
Expand All @@ -5739,9 +5828,16 @@ AnimationTrackEditor::AnimationTrackEditor() {
insert_confirm->add_child(icvb);
insert_confirm_text = memnew(Label);
icvb->add_child(insert_confirm_text);
HBoxContainer *ichb = memnew(HBoxContainer);
icvb->add_child(ichb);
insert_confirm_bezier = memnew(CheckBox);
insert_confirm_bezier->set_text(TTR("Use Bezier Curves"));
icvb->add_child(insert_confirm_bezier);
insert_confirm_bezier->set_pressed(EDITOR_GET("editors/animation/default_create_bezier_tracks"));
ichb->add_child(insert_confirm_bezier);
insert_confirm_reset = memnew(CheckBox);
insert_confirm_reset->set_text(TTR("Create RESET Track(s)", ""));
insert_confirm_reset->set_pressed(EDITOR_GET("editors/animation/default_create_reset_tracks"));
ichb->add_child(insert_confirm_reset);
keying = false;
moving_selection = false;
key_edit = nullptr;
Expand Down
Loading

0 comments on commit b7367ac

Please sign in to comment.