Fix Quaternion Tween and implement ease baker
This commit is contained in:
@ -37,6 +37,7 @@
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/animation/tween.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/view_panner.h"
|
||||
#include "scene/main/window.h"
|
||||
@ -5963,6 +5964,89 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
||||
#undef NEW_POS
|
||||
undo_redo->commit_action();
|
||||
} break;
|
||||
|
||||
case EDIT_EASE_SELECTION: {
|
||||
ease_dialog->popup_centered(Size2(200, 100) * EDSCALE);
|
||||
} break;
|
||||
case EDIT_EASE_CONFIRM: {
|
||||
undo_redo->create_action(TTR("Make Easing Keys"));
|
||||
|
||||
Tween::TransitionType transition_type = static_cast<Tween::TransitionType>(transition_selection->get_selected_id());
|
||||
Tween::EaseType ease_type = static_cast<Tween::EaseType>(ease_selection->get_selected_id());
|
||||
float fps = ease_fps->get_value();
|
||||
double dur_step = 1.0 / fps;
|
||||
|
||||
// Organize track and key.
|
||||
HashMap<int, Vector<int>> keymap;
|
||||
Vector<int> tracks;
|
||||
for (const KeyValue<SelectedKey, KeyInfo> &E : selection) {
|
||||
if (!tracks.has(E.key.track)) {
|
||||
tracks.append(E.key.track);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < tracks.size(); i++) {
|
||||
switch (animation->track_get_type(tracks[i])) {
|
||||
case Animation::TYPE_VALUE:
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
case Animation::TYPE_SCALE_3D:
|
||||
case Animation::TYPE_BLEND_SHAPE: {
|
||||
Vector<int> keys;
|
||||
for (const KeyValue<SelectedKey, KeyInfo> &E : selection) {
|
||||
if (E.key.track == tracks[i]) {
|
||||
keys.append(E.key.key);
|
||||
}
|
||||
}
|
||||
keys.sort();
|
||||
keymap.insert(tracks[i], keys);
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make easing.
|
||||
HashMap<int, Vector<int>>::Iterator E = keymap.begin();
|
||||
while (E) {
|
||||
int track = E->key;
|
||||
Vector<int> keys = E->value;
|
||||
int len = keys.size() - 1;
|
||||
|
||||
// Make insert queue.
|
||||
Vector<Pair<double, Variant>> insert_queue;
|
||||
for (int i = 0; i < len; i++) {
|
||||
// Check neighboring keys.
|
||||
if (keys[i] + 1 == keys[i + 1]) {
|
||||
double from_t = animation->track_get_key_time(track, keys[i]);
|
||||
double to_t = animation->track_get_key_time(track, keys[i + 1]);
|
||||
Variant from_v = animation->track_get_key_value(track, keys[i]);
|
||||
Variant to_v = animation->track_get_key_value(track, keys[i + 1]);
|
||||
Variant delta_v;
|
||||
Variant::sub(to_v, from_v, delta_v);
|
||||
double duration = to_t - from_t;
|
||||
double fixed_duration = duration - 0.01; // Prevent to overwrap keys...
|
||||
for (double delta_t = dur_step; delta_t < fixed_duration; delta_t += dur_step) {
|
||||
Pair<double, Variant> keydata;
|
||||
keydata.first = from_t + delta_t;
|
||||
keydata.second = Tween::interpolate_variant(from_v, delta_v, delta_t, duration, transition_type, ease_type);
|
||||
insert_queue.append(keydata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do insertion.
|
||||
for (int i = 0; i < insert_queue.size(); i++) {
|
||||
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, insert_queue[i].first, insert_queue[i].second);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, insert_queue[i].first);
|
||||
}
|
||||
|
||||
++E;
|
||||
}
|
||||
|
||||
undo_redo->commit_action();
|
||||
|
||||
} break;
|
||||
|
||||
case EDIT_DUPLICATE_SELECTION: {
|
||||
if (bezier_edit->is_visible()) {
|
||||
bezier_edit->duplicate_selection();
|
||||
@ -6054,8 +6138,115 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
||||
} break;
|
||||
case EDIT_APPLY_RESET: {
|
||||
AnimationPlayerEditor::get_singleton()->get_player()->apply_reset(true);
|
||||
} break;
|
||||
|
||||
case EDIT_BAKE_ANIMATION: {
|
||||
bake_dialog->popup_centered(Size2(200, 100) * EDSCALE);
|
||||
} break;
|
||||
case EDIT_BAKE_ANIMATION_CONFIRM: {
|
||||
undo_redo->create_action(TTR("Bake Animation as Linear keys."));
|
||||
|
||||
int track_len = animation->get_track_count();
|
||||
bool b_trs = bake_trs->is_pressed();
|
||||
bool b_bs = bake_blendshape->is_pressed();
|
||||
bool b_v = bake_value->is_pressed();
|
||||
|
||||
double anim_len = animation->get_length() + CMP_EPSILON; // For end key.
|
||||
float fps = bake_fps->get_value();
|
||||
double dur_step = 1.0 / fps;
|
||||
|
||||
for (int i = 0; i < track_len; i++) {
|
||||
bool do_bake = false;
|
||||
Animation::TrackType type = animation->track_get_type(i);
|
||||
do_bake |= b_trs && (type == Animation::TYPE_POSITION_3D || type == Animation::TYPE_ROTATION_3D || type == Animation::TYPE_SCALE_3D);
|
||||
do_bake |= b_bs && type == Animation::TYPE_BLEND_SHAPE;
|
||||
do_bake |= b_v && type == Animation::TYPE_VALUE;
|
||||
if (do_bake && !animation->track_is_compressed(i)) {
|
||||
if (animation->track_get_interpolation_type(i) == Animation::INTERPOLATION_NEAREST) {
|
||||
continue; // Nearest interpolation cannot be baked.
|
||||
}
|
||||
|
||||
// Make insert queue.
|
||||
Vector<Pair<double, Variant>> insert_queue;
|
||||
|
||||
switch (type) {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
for (double delta_t = 0.0; delta_t <= anim_len; delta_t += dur_step) {
|
||||
Pair<double, Variant> keydata;
|
||||
keydata.first = delta_t;
|
||||
Vector3 v;
|
||||
animation->position_track_interpolate(i, delta_t, &v);
|
||||
keydata.second = v;
|
||||
insert_queue.append(keydata);
|
||||
}
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
for (double delta_t = 0.0; delta_t <= anim_len; delta_t += dur_step) {
|
||||
Pair<double, Variant> keydata;
|
||||
keydata.first = delta_t;
|
||||
Quaternion v;
|
||||
animation->rotation_track_interpolate(i, delta_t, &v);
|
||||
keydata.second = v;
|
||||
insert_queue.append(keydata);
|
||||
}
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
for (double delta_t = 0.0; delta_t <= anim_len; delta_t += dur_step) {
|
||||
Pair<double, Variant> keydata;
|
||||
keydata.first = delta_t;
|
||||
Vector3 v;
|
||||
animation->scale_track_interpolate(i, delta_t, &v);
|
||||
keydata.second = v;
|
||||
insert_queue.append(keydata);
|
||||
}
|
||||
} break;
|
||||
case Animation::TYPE_BLEND_SHAPE: {
|
||||
for (double delta_t = 0.0; delta_t <= anim_len; delta_t += dur_step) {
|
||||
Pair<double, Variant> keydata;
|
||||
keydata.first = delta_t;
|
||||
float v;
|
||||
animation->blend_shape_track_interpolate(i, delta_t, &v);
|
||||
keydata.second = v;
|
||||
insert_queue.append(keydata);
|
||||
}
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
for (double delta_t = 0.0; delta_t < anim_len; delta_t += dur_step) {
|
||||
Pair<double, Variant> keydata;
|
||||
keydata.first = delta_t;
|
||||
keydata.second = animation->value_track_interpolate(i, delta_t);
|
||||
insert_queue.append(keydata);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
|
||||
// Cleanup keys.
|
||||
int key_len = animation->track_get_key_count(i);
|
||||
for (int j = key_len - 1; j >= 0; j--) {
|
||||
undo_redo->add_do_method(animation.ptr(), "track_remove_key", i, j);
|
||||
}
|
||||
|
||||
// Insert keys.
|
||||
undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", i, Animation::INTERPOLATION_LINEAR);
|
||||
for (int j = insert_queue.size() - 1; j >= 0; j--) {
|
||||
undo_redo->add_do_method(animation.ptr(), "track_insert_key", i, insert_queue[j].first, insert_queue[j].second);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_remove_key", i, j);
|
||||
}
|
||||
|
||||
// Undo methods.
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", i, animation->track_get_interpolation_type(i));
|
||||
for (int j = key_len - 1; j >= 0; j--) {
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", i, animation->track_get_key_time(i, j), animation->track_get_key_value(i, j), animation->track_get_key_transition(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
undo_redo->commit_action();
|
||||
|
||||
} break;
|
||||
|
||||
case EDIT_OPTIMIZE_ANIMATION: {
|
||||
optimize_dialog->popup_centered(Size2(250, 180) * EDSCALE);
|
||||
|
||||
@ -6463,6 +6654,8 @@ 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_item(TTR("Make Easing Selection"), EDIT_EASE_SELECTION);
|
||||
edit->get_popup()->add_separator();
|
||||
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CMD | Key::D), EDIT_DUPLICATE_SELECTION);
|
||||
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/duplicate_selection_transposed", TTR("Duplicate Transposed"), KeyModifierMask::SHIFT | KeyModifierMask::CMD | Key::D), EDIT_DUPLICATE_TRANSPOSED);
|
||||
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/add_reset_value", TTR("Add RESET Value(s)")));
|
||||
@ -6475,6 +6668,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
|
||||
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("Bake Animation"), EDIT_BAKE_ANIMATION);
|
||||
edit->get_popup()->add_item(TTR("Optimize Animation"), EDIT_OPTIMIZE_ANIMATION);
|
||||
edit->get_popup()->add_item(TTR("Clean-Up Animation"), EDIT_CLEAN_UP_ANIMATION);
|
||||
|
||||
@ -6598,6 +6792,88 @@ AnimationTrackEditor::AnimationTrackEditor() {
|
||||
scale_dialog->connect("confirmed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_SCALE_CONFIRM));
|
||||
add_child(scale_dialog);
|
||||
|
||||
//
|
||||
ease_dialog = memnew(ConfirmationDialog);
|
||||
ease_dialog->set_title(TTR("Select Transition and Easing"));
|
||||
ease_dialog->connect("confirmed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_EASE_CONFIRM));
|
||||
add_child(ease_dialog);
|
||||
GridContainer *ease_grid = memnew(GridContainer);
|
||||
ease_grid->set_columns(2);
|
||||
ease_dialog->add_child(ease_grid);
|
||||
transition_selection = memnew(OptionButton);
|
||||
transition_selection->add_item("Linear", Tween::TRANS_LINEAR);
|
||||
transition_selection->add_item("Sine", Tween::TRANS_SINE);
|
||||
transition_selection->add_item("Quint", Tween::TRANS_QUINT);
|
||||
transition_selection->add_item("Quart", Tween::TRANS_QUART);
|
||||
transition_selection->add_item("Quad", Tween::TRANS_QUAD);
|
||||
transition_selection->add_item("Expo", Tween::TRANS_EXPO);
|
||||
transition_selection->add_item("Elastic", Tween::TRANS_ELASTIC);
|
||||
transition_selection->add_item("Cubic", Tween::TRANS_CUBIC);
|
||||
transition_selection->add_item("Circ", Tween::TRANS_CIRC);
|
||||
transition_selection->add_item("Bounce", Tween::TRANS_BOUNCE);
|
||||
transition_selection->add_item("Back", Tween::TRANS_BACK);
|
||||
transition_selection->select(Tween::TRANS_LINEAR); // Default
|
||||
ease_selection = memnew(OptionButton);
|
||||
ease_selection->add_item("In", Tween::EASE_IN);
|
||||
ease_selection->add_item("Out", Tween::EASE_OUT);
|
||||
ease_selection->add_item("InOut", Tween::EASE_IN_OUT);
|
||||
ease_selection->add_item("OutIn", Tween::EASE_OUT_IN);
|
||||
ease_selection->select(Tween::EASE_IN_OUT); // Default
|
||||
ease_fps = memnew(SpinBox);
|
||||
ease_fps->set_min(1);
|
||||
ease_fps->set_max(999);
|
||||
ease_fps->set_step(1);
|
||||
ease_fps->set_value(30); // Default
|
||||
Label *ease_label1 = memnew(Label);
|
||||
Label *ease_label2 = memnew(Label);
|
||||
Label *ease_label3 = memnew(Label);
|
||||
ease_label1->set_text("Transition Type:");
|
||||
ease_label2->set_text("Ease Type:");
|
||||
ease_label3->set_text("FPS:");
|
||||
ease_grid->add_child(ease_label1);
|
||||
ease_grid->add_child(transition_selection);
|
||||
ease_grid->add_child(ease_label2);
|
||||
ease_grid->add_child(ease_selection);
|
||||
ease_grid->add_child(ease_label3);
|
||||
ease_grid->add_child(ease_fps);
|
||||
|
||||
//
|
||||
bake_dialog = memnew(ConfirmationDialog);
|
||||
bake_dialog->set_title(TTR("Anim. Baker"));
|
||||
bake_dialog->connect("confirmed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_BAKE_ANIMATION_CONFIRM));
|
||||
add_child(bake_dialog);
|
||||
GridContainer *bake_grid = memnew(GridContainer);
|
||||
bake_grid->set_columns(2);
|
||||
bake_dialog->add_child(bake_grid);
|
||||
bake_trs = memnew(CheckBox);
|
||||
bake_trs->set_pressed(true);
|
||||
bake_blendshape = memnew(CheckBox);
|
||||
bake_blendshape->set_pressed(true);
|
||||
bake_value = memnew(CheckBox);
|
||||
bake_value->set_pressed(true);
|
||||
bake_fps = memnew(SpinBox);
|
||||
bake_fps->set_min(1);
|
||||
bake_fps->set_max(999);
|
||||
bake_fps->set_step(1);
|
||||
bake_fps->set_value(30); // Default
|
||||
Label *bake_label1 = memnew(Label);
|
||||
Label *bake_label2 = memnew(Label);
|
||||
Label *bake_label3 = memnew(Label);
|
||||
Label *bake_label4 = memnew(Label);
|
||||
bake_label1->set_text("Pos/Rot/Scl3D Track:");
|
||||
bake_label2->set_text("Blendshape Track:");
|
||||
bake_label3->set_text("Value Track:");
|
||||
bake_label4->set_text("FPS:");
|
||||
bake_grid->add_child(bake_label1);
|
||||
bake_grid->add_child(bake_trs);
|
||||
bake_grid->add_child(bake_label2);
|
||||
bake_grid->add_child(bake_blendshape);
|
||||
bake_grid->add_child(bake_label3);
|
||||
bake_grid->add_child(bake_value);
|
||||
bake_grid->add_child(bake_label4);
|
||||
bake_grid->add_child(bake_fps);
|
||||
|
||||
//
|
||||
track_copy_dialog = memnew(ConfirmationDialog);
|
||||
add_child(track_copy_dialog);
|
||||
track_copy_dialog->set_title(TTR("Select Tracks to Copy"));
|
||||
|
||||
Reference in New Issue
Block a user