diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index d941de86581..5db3075da45 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -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 &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 &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 &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 &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 &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 &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 &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"))); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index 5297d617e9c..06e1795bae0 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -108,6 +108,7 @@ class AnimationBezierTrackEdit : public Control { typedef Pair 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 &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 &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(); }; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 0e25158535f..6f2e898a0df 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -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); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index e55fd53c752..d830ea3a868 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -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 diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 021d2346142..e9aeae187ea 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -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(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 { diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 174fca8d816..592e47189e2 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -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;