1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-10 13:00:37 +00:00

Add option to auto tangent new bezier points in animation editor

Co-authored-by: Mikael Hermansson <mikael@hermansson.io>
This commit is contained in:
Kasper Arnklit Frandsen
2024-09-17 16:13:21 +01:00
committed by Mikael Hermansson
parent e45cc68092
commit 5934804335
6 changed files with 211 additions and 86 deletions

View File

@@ -37,6 +37,7 @@
#include "editor/gui/editor_spin_slider.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/option_button.h"
#include "scene/gui/view_panner.h"
#include "scene/resources/text_line.h"
@@ -97,8 +98,6 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
}
}
out_handle += Vector2(offset, height);
float offset_n = animation->track_get_key_time(p_track, i_n);
float height_n = animation->bezier_track_get_key_value(p_track, i_n);
Vector2 in_handle = animation->bezier_track_get_key_in_handle(p_track, i_n);
@@ -116,6 +115,37 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
}
}
if (moving_inserted_key && moving_selection_from_track == p_track) {
if (moving_selection_from_key == i) {
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(p_track, i);
if (handle_mode != Animation::HANDLE_MODE_FREE) {
float offset_p = offset;
float height_p = height;
if (E->prev()) {
int i_p = E->prev()->get();
offset_p = animation->track_get_key_time(p_track, i_p);
height_p = animation->bezier_track_get_key_value(p_track, i_p);
}
animation->bezier_track_calculate_handles(offset, offset_p, height_p, offset_n, height_n, handle_mode, Animation::HANDLE_SET_MODE_AUTO, nullptr, &out_handle);
}
} else if (moving_selection_from_key == i_n) {
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(p_track, i_n);
if (handle_mode != Animation::HANDLE_MODE_FREE) {
float offset_nn = offset_n;
float height_nn = height_n;
if (E->next()->next()) {
int i_nn = E->next()->next()->get();
offset_nn = animation->track_get_key_time(p_track, i_nn);
height_nn = animation->bezier_track_get_key_value(p_track, i_nn);
}
animation->bezier_track_calculate_handles(offset_n, offset, height, offset_nn, height_nn, handle_mode, Animation::HANDLE_SET_MODE_AUTO, &in_handle, nullptr);
}
}
}
out_handle += Vector2(offset, height);
in_handle += Vector2(offset_n, height_n);
Vector2 start(offset, height);
@@ -570,18 +600,53 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j);
Vector2 out_vec = animation->bezier_track_get_key_out_handle(i, j);
if ((moving_handle == 1 || moving_handle == -1) && moving_handle_track == i && moving_handle_key == j) {
in_vec = moving_handle_left;
}
Vector2 pos_in(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y));
Vector2 out_vec = animation->bezier_track_get_key_out_handle(i, j);
if ((moving_handle == 1 || moving_handle == -1) && moving_handle_track == i && moving_handle_key == j) {
out_vec = moving_handle_right;
}
if (moving_inserted_key && moving_selection_from_key == j) {
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(i, j);
if (handle_mode != Animation::HANDLE_MODE_FREE) {
int key_prev = 0;
int key_next = moving_selection_from_key;
for (int k = 0; k < key_count; k++) {
if (k == moving_selection_from_key) {
continue;
}
if (animation->track_get_key_time(i, k) < offset) {
key_prev = k;
} else {
key_next = k;
break;
}
}
float prev_time = offset;
float prev_value = value;
if (key_prev != moving_selection_from_key) {
prev_time = animation->track_get_key_time(i, key_prev);
prev_value = animation->bezier_track_get_key_value(i, key_prev);
}
float next_time = offset;
float next_value = value;
if (key_next != moving_selection_from_key) {
next_time = animation->track_get_key_time(i, key_next);
next_value = animation->bezier_track_get_key_value(i, key_next);
}
animation->bezier_track_calculate_handles(offset, prev_time, prev_value, next_time, next_value, handle_mode, Animation::HANDLE_SET_MODE_AUTO, &in_vec, &out_vec);
}
}
Vector2 pos_in(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y));
Vector2 pos_out(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y));
if (i == selected_track || is_selected) {
@@ -1402,6 +1467,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Bezier Point"));
undo_redo->add_do_method(animation.ptr(), "bezier_track_insert_key", selected_track, time, new_point[0], Vector2(new_point[1], new_point[2]), Vector2(new_point[3], new_point[4]));
undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode_at_time", animation.ptr(), selected_track, time, (Animation::HandleMode)editor->bezier_key_mode->get_selected_id(), Animation::HANDLE_SET_MODE_AUTO);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
undo_redo->commit_action();
@@ -1412,6 +1478,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
_select_at_anim(animation, selected_track, animation->track_get_key_time(selected_track, index), true);
moving_selection_attempt = true;
moving_inserted_key = true;
moving_selection = false;
moving_selection_mouse_begin = mb->get_position();
moving_selection_from_key = index;
@@ -1539,6 +1606,14 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
real_t h = key[0];
h += moving_selection_offset.y;
key[0] = h;
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second);
Animation::HandleSetMode handle_set_mode = Animation::HANDLE_SET_MODE_NONE;
if (moving_inserted_key) {
handle_mode = (Animation::HandleMode)editor->bezier_key_mode->get_selected_id();
handle_set_mode = Animation::HANDLE_SET_MODE_AUTO;
}
undo_redo->add_do_method(
this,
"_bezier_track_insert_key_at_anim",
@@ -1548,7 +1623,8 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
key[0],
Vector2(key[1], key[2]),
Vector2(key[3], key[4]),
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
handle_mode,
handle_set_mode);
}
// 4 - (undo) Remove inserted keys.
@@ -1621,6 +1697,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
moving_selection = false;
moving_selection_attempt = false;
moving_inserted_key = false;
moving_selection_mouse_begin = Point2();
queue_redraw();
}
@@ -2058,9 +2135,11 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
}
float h = (get_size().height / 2.0 - menu_insert_key.y) * timeline_v_zoom + timeline_v_scroll;
Array new_point = animation->make_default_bezier_key(h);
Animation::HandleMode handle_mode = (Animation::HandleMode)editor->bezier_key_mode->get_selected_id();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add Bezier Point"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point);
undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode_at_time", animation.ptr(), selected_track, time, handle_mode, Animation::HANDLE_SET_MODE_AUTO);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
@@ -2343,9 +2422,9 @@ void AnimationBezierTrackEdit::delete_selection() {
}
}
void AnimationBezierTrackEdit::_bezier_track_insert_key_at_anim(const Ref<Animation> &p_anim, int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode) {
void AnimationBezierTrackEdit::_bezier_track_insert_key_at_anim(const Ref<Animation> &p_anim, int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode, Animation::HandleSetMode p_handle_set_mode) {
int idx = p_anim->bezier_track_insert_key(p_track, p_time, p_value, p_in_handle, p_out_handle);
p_anim->bezier_track_set_key_handle_mode(p_track, idx, p_handle_mode);
p_anim->bezier_track_set_key_handle_mode(p_track, idx, p_handle_mode, p_handle_set_mode);
}
void AnimationBezierTrackEdit::_bind_methods() {
@@ -2354,7 +2433,7 @@ void AnimationBezierTrackEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_select_at_anim"), &AnimationBezierTrackEdit::_select_at_anim);
ClassDB::bind_method(D_METHOD("_update_hidden_tracks_after"), &AnimationBezierTrackEdit::_update_hidden_tracks_after);
ClassDB::bind_method(D_METHOD("_update_locked_tracks_after"), &AnimationBezierTrackEdit::_update_locked_tracks_after);
ClassDB::bind_method(D_METHOD("_bezier_track_insert_key_at_anim"), &AnimationBezierTrackEdit::_bezier_track_insert_key_at_anim);
ClassDB::bind_method(D_METHOD("_bezier_track_insert_key_at_anim"), &AnimationBezierTrackEdit::_bezier_track_insert_key_at_anim, DEFVAL(Animation::HANDLE_SET_MODE_NONE));
ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single"), PropertyInfo(Variant::INT, "track")));
ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::INT, "track")));

View File

@@ -108,6 +108,7 @@ class AnimationBezierTrackEdit : public Control {
typedef Pair<int, int> IntPair;
bool moving_selection_attempt = false;
bool moving_inserted_key = false;
Point2 moving_selection_mouse_begin;
IntPair select_single_attempt;
bool moving_selection = false;
@@ -230,7 +231,7 @@ public:
void paste_keys(real_t p_ofs, bool p_ofs_valid);
void delete_selection();
void _bezier_track_insert_key_at_anim(const Ref<Animation> &p_anim, int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode);
void _bezier_track_insert_key_at_anim(const Ref<Animation> &p_anim, int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode, Animation::HandleSetMode p_handle_set_mode = Animation::HANDLE_SET_MODE_NONE);
AnimationBezierTrackEdit();
};

View File

@@ -4880,6 +4880,9 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
}
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, value);
if (!created && p_id.type == Animation::TYPE_BEZIER) {
undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode_at_time", animation.ptr(), p_id.track_idx, time, (Animation::HandleMode)bezier_key_mode->get_selected_id(), Animation::HANDLE_SET_MODE_AUTO);
}
if (created) {
// Just remove the track.
@@ -6316,12 +6319,17 @@ void AnimationTrackEditor::_bezier_edit(int p_for_track) {
}
void AnimationTrackEditor::_bezier_track_set_key_handle_mode(Animation *p_anim, int p_track, int p_index, Animation::HandleMode p_mode, Animation::HandleSetMode p_set_mode) {
if (!p_anim) {
return;
}
ERR_FAIL_NULL(p_anim);
p_anim->bezier_track_set_key_handle_mode(p_track, p_index, p_mode, p_set_mode);
}
void AnimationTrackEditor::_bezier_track_set_key_handle_mode_at_time(Animation *p_anim, int p_track, float p_time, Animation::HandleMode p_mode, Animation::HandleSetMode p_set_mode) {
ERR_FAIL_NULL(p_anim);
int index = p_anim->track_find_key(p_track, p_time, Animation::FIND_MODE_APPROX);
ERR_FAIL_COND(index < 0);
_bezier_track_set_key_handle_mode(p_anim, p_track, index, p_mode, p_set_mode);
}
void AnimationTrackEditor::_anim_duplicate_keys(float p_ofs, bool p_ofs_valid, int p_track) {
if (selection.size() && animation.is_valid()) {
int top_track = 0x7FFFFFFF;
@@ -7699,6 +7707,7 @@ void AnimationTrackEditor::_bind_methods() {
ClassDB::bind_method("_clear_selection", &AnimationTrackEditor::_clear_selection);
ClassDB::bind_method(D_METHOD("_bezier_track_set_key_handle_mode", "animation", "track_idx", "key_idx", "key_handle_mode", "key_handle_set_mode"), &AnimationTrackEditor::_bezier_track_set_key_handle_mode, DEFVAL(Animation::HANDLE_SET_MODE_NONE));
ClassDB::bind_method(D_METHOD("_bezier_track_set_key_handle_mode_at_time", "animation", "track_idx", "time", "key_handle_mode", "key_handle_set_mode"), &AnimationTrackEditor::_bezier_track_set_key_handle_mode_at_time, DEFVAL(Animation::HANDLE_SET_MODE_NONE));
ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "timeline_only"), PropertyInfo(Variant::BOOL, "update_position_only")));
ADD_SIGNAL(MethodInfo("keying_changed"));
@@ -7870,6 +7879,21 @@ AnimationTrackEditor::AnimationTrackEditor() {
spacer->set_h_size_flags(SIZE_EXPAND_FILL);
bottom_hf->add_child(spacer);
Label *bezier_key_default_label = memnew(Label);
bezier_key_default_label->set_text(TTR("Bezier Default Mode:"));
bottom_hf->add_child(bezier_key_default_label);
bezier_key_mode = memnew(OptionButton);
bezier_key_mode->add_item(TTR("Free"));
bezier_key_mode->add_item(TTR("Linear"));
bezier_key_mode->add_item(TTR("Balanced"));
bezier_key_mode->add_item(TTR("Mirrored"));
bezier_key_mode->set_tooltip_text(TTR("Set the default behavior of new bezier keys."));
bezier_key_mode->select(2);
bottom_hf->add_child(bezier_key_mode);
bottom_hf->add_child(memnew(VSeparator));
bezier_edit_icon = memnew(Button);
bezier_edit_icon->set_flat(true);
bezier_edit_icon->set_disabled(true);

View File

@@ -618,6 +618,7 @@ class AnimationTrackEditor : public VBoxContainer {
OptionButton *snap_mode = nullptr;
Button *auto_fit = nullptr;
Button *auto_fit_bezier = nullptr;
OptionButton *bezier_key_mode = nullptr;
Button *imported_anim_warning = nullptr;
void _show_imported_anim_warning();
@@ -767,6 +768,7 @@ class AnimationTrackEditor : public VBoxContainer {
void _cancel_bezier_edit();
void _bezier_edit(int p_for_track);
void _bezier_track_set_key_handle_mode(Animation *p_anim, int p_track, int p_index, Animation::HandleMode p_mode, Animation::HandleSetMode p_set_mode = Animation::HANDLE_SET_MODE_NONE);
void _bezier_track_set_key_handle_mode_at_time(Animation *p_anim, int p_track, float p_time, Animation::HandleMode p_mode, Animation::HandleSetMode p_set_mode = Animation::HANDLE_SET_MODE_NONE);
////////////// edit menu stuff

View File

@@ -3492,79 +3492,10 @@ void Animation::bezier_track_set_key_handle_mode(int p_track, int p_index, Handl
bt->values.write[p_index].value.handle_mode = p_mode;
switch (p_mode) {
case HANDLE_MODE_LINEAR: {
bt->values.write[p_index].value.in_handle = Vector2(0, 0);
bt->values.write[p_index].value.out_handle = Vector2(0, 0);
} break;
case HANDLE_MODE_BALANCED:
case HANDLE_MODE_MIRRORED: {
int prev_key = MAX(0, p_index - 1);
int next_key = MIN(bt->values.size() - 1, p_index + 1);
if (prev_key == next_key) {
break; // Exists only one key.
}
real_t in_handle_x = 0;
real_t in_handle_y = 0;
real_t out_handle_x = 0;
real_t out_handle_y = 0;
if (p_mode == HANDLE_MODE_BALANCED) {
// Note:
// If p_set_mode == HANDLE_SET_MODE_NONE, I don't know if it should change the Tangent implicitly.
// At the least, we need to avoid corrupting the handles when loading animation from the resource.
// However, changes made by the Inspector do not go through the BezierEditor,
// so if you change from Free to Balanced or Mirrored in Inspector, there is no guarantee that
// it is Balanced or Mirrored until there is a handle operation.
if (p_set_mode == HANDLE_SET_MODE_RESET) {
real_t handle_length = 1.0 / 3.0;
in_handle_x = (bt->values[prev_key].time - bt->values[p_index].time) * handle_length;
in_handle_y = 0;
out_handle_x = (bt->values[next_key].time - bt->values[p_index].time) * handle_length;
out_handle_y = 0;
bt->values.write[p_index].value.in_handle = Vector2(in_handle_x, in_handle_y);
bt->values.write[p_index].value.out_handle = Vector2(out_handle_x, out_handle_y);
} else if (p_set_mode == HANDLE_SET_MODE_AUTO) {
real_t handle_length = 1.0 / 6.0;
real_t tangent = (bt->values[next_key].value.value - bt->values[prev_key].value.value) / (bt->values[next_key].time - bt->values[prev_key].time);
in_handle_x = (bt->values[prev_key].time - bt->values[p_index].time) * handle_length;
in_handle_y = in_handle_x * tangent;
out_handle_x = (bt->values[next_key].time - bt->values[p_index].time) * handle_length;
out_handle_y = out_handle_x * tangent;
bt->values.write[p_index].value.in_handle = Vector2(in_handle_x, in_handle_y);
bt->values.write[p_index].value.out_handle = Vector2(out_handle_x, out_handle_y);
}
} else {
real_t handle_length = 1.0 / 4.0;
real_t prev_interval = Math::abs(bt->values[p_index].time - bt->values[prev_key].time);
real_t next_interval = Math::abs(bt->values[p_index].time - bt->values[next_key].time);
real_t min_time = 0;
if (Math::is_zero_approx(prev_interval)) {
min_time = next_interval;
} else if (Math::is_zero_approx(next_interval)) {
min_time = prev_interval;
} else {
min_time = MIN(prev_interval, next_interval);
}
if (p_set_mode == HANDLE_SET_MODE_RESET) {
in_handle_x = -min_time * handle_length;
in_handle_y = 0;
out_handle_x = min_time * handle_length;
out_handle_y = 0;
bt->values.write[p_index].value.in_handle = Vector2(in_handle_x, in_handle_y);
bt->values.write[p_index].value.out_handle = Vector2(out_handle_x, out_handle_y);
} else if (p_set_mode == HANDLE_SET_MODE_AUTO) {
real_t tangent = (bt->values[next_key].value.value - bt->values[prev_key].value.value) / min_time;
in_handle_x = -min_time * handle_length;
in_handle_y = in_handle_x * tangent;
out_handle_x = min_time * handle_length;
out_handle_y = out_handle_x * tangent;
bt->values.write[p_index].value.in_handle = Vector2(in_handle_x, in_handle_y);
bt->values.write[p_index].value.out_handle = Vector2(out_handle_x, out_handle_y);
}
}
} break;
default: {
} break;
if (p_mode != HANDLE_MODE_FREE && p_set_mode != HANDLE_SET_MODE_NONE) {
Vector2 &in_handle = bt->values.write[p_index].value.in_handle;
Vector2 &out_handle = bt->values.write[p_index].value.out_handle;
bezier_track_calculate_handles(p_track, p_index, p_mode, p_set_mode, &in_handle, &out_handle);
}
emit_changed();
@@ -3581,6 +3512,92 @@ Animation::HandleMode Animation::bezier_track_get_key_handle_mode(int p_track, i
return bt->values[p_index].value.handle_mode;
}
bool Animation::bezier_track_calculate_handles(int p_track, int p_index, HandleMode p_mode, HandleSetMode p_set_mode, Vector2 *r_in_handle, Vector2 *r_out_handle) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), false);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BEZIER, false);
BezierTrack *bt = static_cast<BezierTrack *>(t);
ERR_FAIL_INDEX_V(p_index, bt->values.size(), false);
int prev_key = MAX(0, p_index - 1);
int next_key = MIN(bt->values.size() - 1, p_index + 1);
if (prev_key == next_key) {
return false;
}
float time = bt->values[p_index].time;
float prev_time = bt->values[prev_key].time;
float prev_value = bt->values[prev_key].value.value;
float next_time = bt->values[next_key].time;
float next_value = bt->values[next_key].value.value;
return bezier_track_calculate_handles(time, prev_time, prev_value, next_time, next_value, p_mode, p_set_mode, r_in_handle, r_out_handle);
}
bool Animation::bezier_track_calculate_handles(float p_time, float p_prev_time, float p_prev_value, float p_next_time, float p_next_value, HandleMode p_mode, HandleSetMode p_set_mode, Vector2 *r_in_handle, Vector2 *r_out_handle) {
ERR_FAIL_COND_V(p_mode == HANDLE_MODE_FREE, false);
ERR_FAIL_COND_V(p_set_mode == HANDLE_SET_MODE_NONE, false);
Vector2 in_handle;
Vector2 out_handle;
if (p_mode == HANDLE_MODE_LINEAR) {
in_handle = Vector2(0, 0);
out_handle = Vector2(0, 0);
} else if (p_mode == HANDLE_MODE_BALANCED) {
if (p_set_mode == HANDLE_SET_MODE_RESET) {
real_t handle_length = 1.0 / 3.0;
in_handle.x = (p_prev_time - p_time) * handle_length;
in_handle.y = 0;
out_handle.x = (p_next_time - p_time) * handle_length;
out_handle.y = 0;
} else if (p_set_mode == HANDLE_SET_MODE_AUTO) {
real_t handle_length = 1.0 / 6.0;
real_t tangent = (p_next_value - p_prev_value) / (p_next_time - p_prev_time);
in_handle.x = (p_prev_time - p_time) * handle_length;
in_handle.y = in_handle.x * tangent;
out_handle.x = (p_next_time - p_time) * handle_length;
out_handle.y = out_handle.x * tangent;
}
} else if (p_mode == HANDLE_MODE_MIRRORED) {
real_t handle_length = 1.0 / 4.0;
real_t prev_interval = Math::abs(p_time - p_prev_time);
real_t next_interval = Math::abs(p_time - p_next_time);
real_t min_time = 0;
if (Math::is_zero_approx(prev_interval)) {
min_time = next_interval;
} else if (Math::is_zero_approx(next_interval)) {
min_time = prev_interval;
} else {
min_time = MIN(prev_interval, next_interval);
}
if (p_set_mode == HANDLE_SET_MODE_RESET) {
in_handle.x = -min_time * handle_length;
in_handle.y = 0;
out_handle.x = min_time * handle_length;
out_handle.y = 0;
} else if (p_set_mode == HANDLE_SET_MODE_AUTO) {
real_t tangent = (p_next_value - p_prev_value) / min_time;
in_handle.x = -min_time * handle_length;
in_handle.y = in_handle.x * tangent;
out_handle.x = min_time * handle_length;
out_handle.y = out_handle.x * tangent;
}
}
if (r_in_handle != nullptr) {
*r_in_handle = in_handle;
}
if (r_out_handle != nullptr) {
*r_out_handle = out_handle;
}
return true;
}
#endif // TOOLS_ENABLED
real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {

View File

@@ -481,6 +481,8 @@ public:
#ifdef TOOLS_ENABLED
void bezier_track_set_key_handle_mode(int p_track, int p_index, HandleMode p_mode, HandleSetMode p_set_mode = HANDLE_SET_MODE_NONE);
HandleMode bezier_track_get_key_handle_mode(int p_track, int p_index) const;
bool bezier_track_calculate_handles(int p_track, int p_index, HandleMode p_mode, HandleSetMode p_set_mode, Vector2 *r_in_handle, Vector2 *r_out_handle);
bool bezier_track_calculate_handles(float p_time, float p_prev_time, float p_prev_value, float p_next_time, float p_next_value, HandleMode p_mode, HandleSetMode p_set_mode, Vector2 *r_in_handle, Vector2 *r_out_handle);
#endif // TOOLS_ENABLED
real_t bezier_track_interpolate(int p_track, double p_time) const;