diff options
author | Nathan Lovato <nathan@gdquest.com> | 2021-09-03 10:02:53 -0400 |
---|---|---|
committer | Francois Belair <razoric480@gmail.com> | 2021-11-16 09:26:35 -0500 |
commit | a5d0a74b651607cbc95fc61859ab236442b73502 (patch) | |
tree | 0f65051b5f96cdec010032a30ab755d6bec3db6a | |
parent | 098e3cf8f7e56aaaf3715161b72b0a4882e3e9da (diff) |
Make bezier handle type a property of keyframes, update interface
- Replaced unused code related to old close icon with a button
- Add bezier handle options to right-click menu
- Remove mirror handle mode, only keep balanced
- Update animation reference
-rw-r--r-- | doc/classes/Animation.xml | 27 | ||||
-rw-r--r-- | editor/animation_bezier_editor.cpp | 102 | ||||
-rw-r--r-- | editor/animation_bezier_editor.h | 17 | ||||
-rw-r--r-- | editor/animation_track_editor.cpp | 53 | ||||
-rw-r--r-- | scene/resources/animation.cpp | 126 | ||||
-rw-r--r-- | scene/resources/animation.h | 16 |
6 files changed, 259 insertions, 82 deletions
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index ac04149c81..bb4089d67e 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -130,6 +130,14 @@ Sets the stream of the key identified by [code]key_idx[/code] to value [code]stream[/code]. The [code]track_idx[/code] must be the index of an Audio Track. </description> </method> + <method name="bezier_track_get_key_handle_mode" qualifiers="const"> + <return type="int" /> + <argument index="0" name="track_idx" type="int" /> + <argument index="1" name="key_idx" type="int" /> + <description> + Returns the handle mode of the key identified by [code]index[/code]. See [enum HandleMode] for possible values. The [code]track_idx[/code] must be the index of a Bezier Track. + </description> + </method> <method name="bezier_track_get_key_in_handle" qualifiers="const"> <return type="Vector2" /> <argument index="0" name="track_idx" type="int" /> @@ -161,6 +169,7 @@ <argument index="2" name="value" type="float" /> <argument index="3" name="in_handle" type="Vector2" default="Vector2(0, 0)" /> <argument index="4" name="out_handle" type="Vector2" default="Vector2(0, 0)" /> + <argument index="5" name="handle_mode" type="int" enum="Animation.HandleMode" default="1" /> <description> Inserts a Bezier Track key at the given [code]time[/code] in seconds. The [code]track_idx[/code] must be the index of a Bezier Track. [code]in_handle[/code] is the left-side weight of the added Bezier curve point, [code]out_handle[/code] is the right-side one, while [code]value[/code] is the actual value at this point. @@ -174,11 +183,22 @@ Returns the interpolated value at the given [code]time[/code] (in seconds). The [code]track_idx[/code] must be the index of a Bezier Track. </description> </method> + <method name="bezier_track_set_key_handle_mode"> + <return type="void" /> + <argument index="0" name="track_idx" type="int" /> + <argument index="1" name="key_idx" type="int" /> + <argument index="2" name="key_handle_mode" type="int" enum="Animation.HandleMode" /> + <argument index="3" name="balanced_value_time_ratio" type="float" default="1.0" /> + <description> + Changes the handle mode of the keyframe at the given [code]index[/code]. See [enum HandleMode] for possible values. The [code]track_idx[/code] must be the index of a Bezier Track. + </description> + </method> <method name="bezier_track_set_key_in_handle"> <return type="void" /> <argument index="0" name="track_idx" type="int" /> <argument index="1" name="key_idx" type="int" /> <argument index="2" name="in_handle" type="Vector2" /> + <argument index="3" name="balanced_value_time_ratio" type="float" default="1.0" /> <description> Sets the in handle of the key identified by [code]key_idx[/code] to value [code]in_handle[/code]. The [code]track_idx[/code] must be the index of a Bezier Track. </description> @@ -188,6 +208,7 @@ <argument index="0" name="track_idx" type="int" /> <argument index="1" name="key_idx" type="int" /> <argument index="2" name="out_handle" type="Vector2" /> + <argument index="3" name="balanced_value_time_ratio" type="float" default="1.0" /> <description> Sets the out handle of the key identified by [code]key_idx[/code] to value [code]out_handle[/code]. The [code]track_idx[/code] must be the index of a Bezier Track. </description> @@ -619,5 +640,11 @@ <constant name="LOOP_PINGPONG" value="2" enum="LoopMode"> Repeats playback and reverse playback at both ends of the animation. </constant> + <constant name="HANDLE_MODE_FREE" value="0" enum="HandleMode"> + Assigning the free handle mode to a Bezier Track's keyframe allows you to edit the keyframe's left and right handles independently from one another. + </constant> + <constant name="HANDLE_MODE_BALANCED" value="1" enum="HandleMode"> + Assigning the balanced handle mode to a Bezier Track's keyframe makes it so the two handles of the keyframe always stay aligned when changing either the keyframe's left or right handle. + </constant> </constants> </class> diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index e9cf22af85..f7f88ad0d5 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -187,7 +187,7 @@ void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const V Vector2 from = p_from; Vector2 to = p_to; - if (from.x == to.x) { + if (from.x == to.x && from.y == to.y) { return; } if (to.x < from.x) { @@ -222,11 +222,6 @@ void AnimationBezierTrackEdit::_notification(int p_what) { bezier_icon = get_theme_icon(SNAME("KeyBezierPoint"), SNAME("EditorIcons")); bezier_handle_icon = get_theme_icon(SNAME("KeyBezierHandle"), SNAME("EditorIcons")); selected_icon = get_theme_icon(SNAME("KeyBezierSelected"), SNAME("EditorIcons")); - if (handle_mode_option->get_item_count() == 0) { - handle_mode_option->add_icon_item(get_theme_icon(SNAME("BezierHandlesFree"), SNAME("EditorIcons")), TTR("Free"), HANDLE_MODE_FREE); - handle_mode_option->add_icon_item(get_theme_icon(SNAME("BezierHandlesBalanced"), SNAME("EditorIcons")), TTR("Balanced"), HANDLE_MODE_BALANCED); - handle_mode_option->add_icon_item(get_theme_icon(SNAME("BezierHandlesMirror"), SNAME("EditorIcons")), TTR("Mirror"), HANDLE_MODE_MIRROR); - } } if (p_what == NOTIFICATION_RESIZED) { int right_limit = get_size().width - timeline->get_buttons_width(); @@ -420,9 +415,9 @@ void AnimationBezierTrackEdit::_notification(int p_what) { //draw editor handles { - float scale = timeline->get_zoom_scale(); edit_points.clear(); + float scale = timeline->get_zoom_scale(); for (int i = 0; i < animation->track_get_key_count(track); i++) { float offset = animation->track_get_key_time(track, i); float value = animation->bezier_track_get_key_value(track, i); @@ -438,7 +433,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { if (moving_handle != 0 && moving_handle_key == i) { in_vec = moving_handle_left; } - Vector2 pos_in = Vector2(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y)); + 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(track, i); @@ -446,7 +441,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { out_vec = moving_handle_right; } - Vector2 pos_out = Vector2(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y)); + Vector2 pos_out(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y)); _draw_line_clipped(pos, pos_in, accent, limit, right_limit); _draw_line_clipped(pos, pos_out, accent, limit, right_limit); @@ -581,11 +576,21 @@ void AnimationBezierTrackEdit::_clear_selection() { update(); } +void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::HandleMode p_mode) { + undo_redo->create_action(TTR("Update Selected Key Handles")); + double ratio = timeline->get_zoom_scale() * v_zoom; + for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { + const int key_index = E->get(); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key_index, animation->bezier_track_get_key_handle_mode(track, key_index), ratio); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key_index, p_mode, ratio); + } + undo_redo->commit_action(); +} + void AnimationBezierTrackEdit::_clear_selection_for_anim(const Ref<Animation> &p_anim) { if (!(animation == p_anim)) { return; } - //selection.clear(); _clear_selection(); } @@ -667,6 +672,9 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Duplicate Selected Key(s)"), MENU_KEY_DUPLICATE); menu->add_separator(); menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete Selected Key(s)"), MENU_KEY_DELETE); + menu->add_separator(); + menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesFree"), SNAME("EditorIcons")), TTR("Make Handles Free"), MENU_KEY_SET_HANDLE_FREE); + menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesBalanced"), SNAME("EditorIcons")), TTR("Make Handles Balanced"), MENU_KEY_SET_HANDLE_BALANCED); } menu->set_as_minsize(); @@ -676,10 +684,6 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { - if (close_icon_rect.has_point(mb->get_position())) { - emit_signal(SNAME("close_request")); - return; - } for (const KeyValue<int, Rect2> &E : subtracks) { if (E.value.has_point(mb->get_position())) { set_animation_and_track(animation, E.key); @@ -746,7 +750,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { //insert new point if (mb->is_command_pressed() && mb->get_position().x >= timeline->get_name_limit() && mb->get_position().x < get_size().width - timeline->get_buttons_width()) { Array new_point; - new_point.resize(5); + new_point.resize(6); float h = (get_size().height / 2 - mb->get_position().y) * v_zoom + v_scroll; @@ -755,6 +759,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { new_point[2] = 0; new_point[3] = 0.25; new_point[4] = 0; + new_point[5] = 0; float time = ((mb->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value(); while (animation->track_find_key(track, time, true) != -1) { @@ -986,33 +991,49 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { if (moving_handle == -1) { moving_handle_left = moving_handle_value; - if (moving_handle_left.x > 0) { - moving_handle_left.x = 0; - } - if (handle_mode_option->get_selected() == HANDLE_MODE_BALANCED) { - Vector2 scale = Vector2(timeline->get_zoom_scale(), v_zoom); - moving_handle_right = (-(moving_handle_left * scale).normalized() * (moving_handle_right * scale).length()) / scale; + if (animation->bezier_track_get_key_handle_mode(track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) { + double ratio = timeline->get_zoom_scale() * v_zoom; + Transform2D xform; + xform.set_scale(Vector2(1.0, 1.0 / ratio)); - } else if (handle_mode_option->get_selected() == HANDLE_MODE_MIRROR) { - moving_handle_right = -moving_handle_left; - } - } + Vector2 vec_out = xform.xform(moving_handle_right); + Vector2 vec_in = xform.xform(moving_handle_left); - if (moving_handle == 1) { - moving_handle_right = moving_handle_value; - if (moving_handle_right.x < 0) { - moving_handle_right.x = 0; + moving_handle_right = xform.affine_inverse().xform(-vec_in.normalized() * vec_out.length()); } + } else if (moving_handle == 1) { + moving_handle_right = moving_handle_value; + + if (animation->bezier_track_get_key_handle_mode(track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) { + double ratio = timeline->get_zoom_scale() * v_zoom; + Transform2D xform; + xform.set_scale(Vector2(1.0, 1.0 / ratio)); - if (handle_mode_option->get_selected() == HANDLE_MODE_BALANCED) { - Vector2 scale = Vector2(timeline->get_zoom_scale(), v_zoom); - moving_handle_left = (-(moving_handle_right * scale).normalized() * (moving_handle_left * scale).length()) / scale; - } else if (handle_mode_option->get_selected() == HANDLE_MODE_MIRROR) { - moving_handle_left = -moving_handle_right; + Vector2 vec_in = xform.xform(moving_handle_left); + Vector2 vec_out = xform.xform(moving_handle_right); + + moving_handle_left = xform.affine_inverse().xform(-vec_out.normalized() * vec_in.length()); } } + update(); + } + + bool is_finishing_key_handle_drag = moving_handle != 0 && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT; + if (is_finishing_key_handle_drag) { + undo_redo->create_action(TTR("Move Bezier Points")); + if (moving_handle == -1) { + double ratio = timeline->get_zoom_scale() * v_zoom; + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, moving_handle_left, ratio); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, animation->bezier_track_get_key_in_handle(track, moving_handle_key), ratio); + } else if (moving_handle == 1) { + double ratio = timeline->get_zoom_scale() * v_zoom; + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, moving_handle_right, ratio); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, animation->bezier_track_get_key_out_handle(track, moving_handle_key), ratio); + } + undo_redo->commit_action(); + moving_handle = 0; update(); } } @@ -1021,7 +1042,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { switch (p_index) { case MENU_KEY_INSERT: { Array new_point; - new_point.resize(5); + new_point.resize(6); float h = (get_size().height / 2 - menu_insert_key.y) * v_zoom + v_scroll; @@ -1030,6 +1051,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { new_point[2] = 0; new_point[3] = 0.25; new_point[4] = 0; + new_point[5] = Animation::HANDLE_MODE_BALANCED; float time = ((menu_insert_key.x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value(); while (animation->track_find_key(track, time, true) != -1) { @@ -1048,6 +1070,12 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { case MENU_KEY_DELETE: { delete_selection(); } break; + case MENU_KEY_SET_HANDLE_FREE: { + _change_selected_keys_handle_mode(Animation::HANDLE_MODE_FREE); + } break; + case MENU_KEY_SET_HANDLE_BALANCED: { + _change_selected_keys_handle_mode(Animation::HANDLE_MODE_BALANCED); + } break; } } @@ -1118,6 +1146,7 @@ void AnimationBezierTrackEdit::delete_selection() { undo_redo->add_do_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->commit_action(); + //selection.clear(); } } @@ -1150,8 +1179,6 @@ AnimationBezierTrackEdit::AnimationBezierTrackEdit() { set_focus_mode(FOCUS_CLICK); set_clip_contents(true); - handle_mode = HANDLE_MODE_FREE; - handle_mode_option = memnew(OptionButton); close_button = memnew(Button); close_button->connect("pressed", Callable(this, SNAME("emit_signal")), varray(SNAME("close_request"))); @@ -1160,7 +1187,6 @@ AnimationBezierTrackEdit::AnimationBezierTrackEdit() { right_column = memnew(VBoxContainer); right_column->add_child(close_button); right_column->add_spacer(); - right_column->add_child(handle_mode_option); add_child(right_column); menu = memnew(PopupMenu); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index 578c6f9337..4b46777cfe 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -36,21 +36,14 @@ class AnimationBezierTrackEdit : public Control { GDCLASS(AnimationBezierTrackEdit, Control); - enum HandleMode { - HANDLE_MODE_FREE, - HANDLE_MODE_BALANCED, - HANDLE_MODE_MIRROR - }; - enum { MENU_KEY_INSERT, MENU_KEY_DUPLICATE, - MENU_KEY_DELETE + MENU_KEY_DELETE, + MENU_KEY_SET_HANDLE_FREE, + MENU_KEY_SET_HANDLE_BALANCED, }; - HandleMode handle_mode; - OptionButton *handle_mode_option; - VBoxContainer *right_column; Button *close_button; @@ -69,8 +62,6 @@ class AnimationBezierTrackEdit : public Control { Ref<Texture2D> bezier_handle_icon; Ref<Texture2D> selected_icon; - Rect2 close_icon_rect; - Map<int, Rect2> subtracks; float v_scroll = 0; @@ -104,10 +95,12 @@ class AnimationBezierTrackEdit : public Control { int moving_handle_key = 0; Vector2 moving_handle_left; Vector2 moving_handle_right; + int moving_handle_mode; // value from Animation::HandleMode void _clear_selection(); void _clear_selection_for_anim(const Ref<Animation> &p_anim); void _select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos); + void _change_selected_keys_handle_mode(Animation::HandleMode p_mode); Vector2 menu_insert_key; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index f8f66d08d4..6fce55f8e3 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -334,6 +334,22 @@ public: setting = false; return true; } + + if (name == "handle_mode") { + const Variant &value = p_value; + + setting = true; + undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS); + int prev = animation->bezier_track_get_key_handle_mode(track, key); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key, prev); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); + + setting = false; + return true; + } } break; case Animation::TYPE_AUDIO: { if (name == "stream") { @@ -498,6 +514,11 @@ public: return true; } + if (name == "handle_mode") { + r_ret = animation->bezier_track_get_key_handle_mode(track, key); + return true; + } + } break; case Animation::TYPE_AUDIO: { if (name == "stream") { @@ -610,6 +631,7 @@ public: p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle")); + p_list->push_back(PropertyInfo(Variant::INT, "handle_mode", PROPERTY_HINT_ENUM, "Free,Balanced")); } break; case Animation::TYPE_AUDIO: { @@ -949,6 +971,17 @@ public: undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, value); undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev); update_obj = true; + } else if (name == "handle_mode") { + const Variant &value = p_value; + + if (!setting) { + setting = true; + undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); + } + int prev = animation->bezier_track_get_key_handle_mode(track, key); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key, value); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key, prev); + update_obj = true; } } break; case Animation::TYPE_AUDIO: { @@ -1120,6 +1153,11 @@ public: return true; } + if (name == "handle_mode") { + r_ret = animation->bezier_track_get_key_handle_mode(track, key); + return true; + } + } break; case Animation::TYPE_AUDIO: { if (name == "stream") { @@ -1273,6 +1311,7 @@ public: p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle")); + p_list->push_back(PropertyInfo(Variant::INT, "handle_mode", PROPERTY_HINT_ENUM, "Free,Balanced")); } break; case Animation::TYPE_AUDIO: { p_list->push_back(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); @@ -2607,6 +2646,17 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { text += "In-Handle: " + ih + "\n"; Vector2 oh = animation->bezier_track_get_key_out_handle(track, key_idx); text += "Out-Handle: " + oh + "\n"; + int hm = animation->bezier_track_get_key_handle_mode(track, key_idx); + text += "Handle mode: "; + switch (hm) { + case Animation::HANDLE_MODE_FREE: { + text += "Free"; + } break; + case Animation::HANDLE_MODE_BALANCED: { + text += "Balanced"; + } break; + } + text += "\n"; } break; case Animation::TYPE_AUDIO: { String stream_name = "null"; @@ -4796,12 +4846,13 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { Variant value; _find_hint_for_track(p_track, bp, &value); Array arr; - arr.resize(5); + arr.resize(6); arr[0] = value; arr[1] = -0.25; arr[2] = 0; arr[3] = 0.25; arr[4] = 0; + arr[5] = 0; undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, arr); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 3700e87839..29aca6b321 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -317,7 +317,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { Vector<real_t> times = d["times"]; Vector<real_t> values = d["points"]; - ERR_FAIL_COND_V(times.size() * 5 != values.size(), false); + ERR_FAIL_COND_V(times.size() * 6 != values.size(), false); if (times.size()) { int valcount = times.size(); @@ -330,11 +330,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < valcount; i++) { bt->values.write[i].time = rt[i]; bt->values.write[i].transition = 0; //unused in bezier - bt->values.write[i].value.value = rv[i * 5 + 0]; - bt->values.write[i].value.in_handle.x = rv[i * 5 + 1]; - bt->values.write[i].value.in_handle.y = rv[i * 5 + 2]; - bt->values.write[i].value.out_handle.x = rv[i * 5 + 3]; - bt->values.write[i].value.out_handle.y = rv[i * 5 + 4]; + bt->values.write[i].value.value = rv[i * 6 + 0]; + bt->values.write[i].value.in_handle.x = rv[i * 6 + 1]; + bt->values.write[i].value.in_handle.y = rv[i * 6 + 2]; + bt->values.write[i].value.out_handle.x = rv[i * 6 + 3]; + bt->values.write[i].value.out_handle.y = rv[i * 6 + 4]; + bt->values.write[i].value.handle_mode = (HandleMode)rv[i * 6 + 5]; } } @@ -698,7 +699,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { int kk = bt->values.size(); key_times.resize(kk); - key_points.resize(kk * 5); + key_points.resize(kk * 6); real_t *wti = key_times.ptrw(); real_t *wpo = key_points.ptrw(); @@ -709,11 +710,12 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { for (int i = 0; i < kk; i++) { wti[idx] = vls[i].time; - wpo[idx * 5 + 0] = vls[i].value.value; - wpo[idx * 5 + 1] = vls[i].value.in_handle.x; - wpo[idx * 5 + 2] = vls[i].value.in_handle.y; - wpo[idx * 5 + 3] = vls[i].value.out_handle.x; - wpo[idx * 5 + 4] = vls[i].value.out_handle.y; + wpo[idx * 6 + 0] = vls[i].value.value; + wpo[idx * 6 + 1] = vls[i].value.in_handle.x; + wpo[idx * 6 + 2] = vls[i].value.in_handle.y; + wpo[idx * 6 + 3] = vls[i].value.out_handle.x; + wpo[idx * 6 + 4] = vls[i].value.out_handle.y; + wpo[idx * 6 + 5] = (double)vls[i].value.handle_mode; idx++; } @@ -1623,7 +1625,7 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke BezierTrack *bt = static_cast<BezierTrack *>(t); Array arr = p_key; - ERR_FAIL_COND(arr.size() != 5); + ERR_FAIL_COND(arr.size() != 6); TKey<BezierKey> k; k.time = p_time; @@ -1632,6 +1634,7 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke k.value.in_handle.y = arr[2]; k.value.out_handle.x = arr[3]; k.value.out_handle.y = arr[4]; + k.value.handle_mode = (HandleMode) int(arr[5]); _insert(p_time, bt->values, k); } break; @@ -1770,12 +1773,13 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const { ERR_FAIL_INDEX_V(p_key_idx, bt->values.size(), Variant()); Array arr; - arr.resize(5); + arr.resize(6); arr[0] = bt->values[p_key_idx].value.value; arr[1] = bt->values[p_key_idx].value.in_handle.x; arr[2] = bt->values[p_key_idx].value.in_handle.y; arr[3] = bt->values[p_key_idx].value.out_handle.x; arr[4] = bt->values[p_key_idx].value.out_handle.y; + arr[5] = (double)bt->values[p_key_idx].value.handle_mode; return arr; } break; @@ -2144,13 +2148,14 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p ERR_FAIL_INDEX(p_key_idx, bt->values.size()); Array arr = p_value; - ERR_FAIL_COND(arr.size() != 5); + ERR_FAIL_COND(arr.size() != 6); bt->values.write[p_key_idx].value.value = arr[0]; bt->values.write[p_key_idx].value.in_handle.x = arr[1]; bt->values.write[p_key_idx].value.in_handle.y = arr[2]; bt->values.write[p_key_idx].value.out_handle.x = arr[3]; bt->values.write[p_key_idx].value.out_handle.y = arr[4]; + bt->values.write[p_key_idx].value.handle_mode = (HandleMode) int(arr[5]); } break; case TYPE_AUDIO: { @@ -3203,7 +3208,7 @@ StringName Animation::method_track_get_name(int p_track, int p_key_idx) const { return pm->methods[p_key_idx].method; } -int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle) { +int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const HandleMode p_handle_mode) { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; ERR_FAIL_COND_V(t->type != TYPE_BEZIER, -1); @@ -3221,6 +3226,7 @@ int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_valu if (k.value.out_handle.x < 0) { k.value.out_handle.x = 0; } + k.value.handle_mode = p_handle_mode; int key = _insert(p_time, bt->values, k); @@ -3229,6 +3235,30 @@ int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_valu return key; } +void Animation::bezier_track_set_key_handle_mode(int p_track, int p_index, HandleMode p_mode, double p_balanced_value_time_ratio) { + ERR_FAIL_INDEX(p_track, tracks.size()); + Track *t = tracks[p_track]; + ERR_FAIL_COND(t->type != TYPE_BEZIER); + + BezierTrack *bt = static_cast<BezierTrack *>(t); + + ERR_FAIL_INDEX(p_index, bt->values.size()); + + bt->values.write[p_index].value.handle_mode = p_mode; + + if (p_mode == HANDLE_MODE_BALANCED) { + Transform2D xform; + xform.set_scale(Vector2(1.0, 1.0 / p_balanced_value_time_ratio)); + + Vector2 vec_in = xform.xform(bt->values[p_index].value.in_handle); + Vector2 vec_out = xform.xform(bt->values[p_index].value.out_handle); + + bt->values.write[p_index].value.in_handle = xform.affine_inverse().xform(-vec_out.normalized() * vec_in.length()); + } + + emit_changed(); +} + void Animation::bezier_track_set_key_value(int p_track, int p_index, real_t p_value) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; @@ -3242,7 +3272,7 @@ void Animation::bezier_track_set_key_value(int p_track, int p_index, real_t p_va emit_changed(); } -void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle) { +void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle, double p_balanced_value_time_ratio) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; ERR_FAIL_COND(t->type != TYPE_BEZIER); @@ -3251,14 +3281,26 @@ void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const V ERR_FAIL_INDEX(p_index, bt->values.size()); - bt->values.write[p_index].value.in_handle = p_handle; - if (bt->values[p_index].value.in_handle.x > 0) { - bt->values.write[p_index].value.in_handle.x = 0; + Vector2 in_handle = p_handle; + if (in_handle.x > 0) { + in_handle.x = 0; + } + bt->values.write[p_index].value.in_handle = in_handle; + + if (bt->values[p_index].value.handle_mode == HANDLE_MODE_BALANCED) { + Transform2D xform; + xform.set_scale(Vector2(1.0, 1.0 / p_balanced_value_time_ratio)); + + Vector2 vec_out = xform.xform(bt->values[p_index].value.out_handle); + Vector2 vec_in = xform.xform(in_handle); + + bt->values.write[p_index].value.out_handle = xform.affine_inverse().xform(-vec_in.normalized() * vec_out.length()); } + emit_changed(); } -void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle) { +void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle, double p_balanced_value_time_ratio) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; ERR_FAIL_COND(t->type != TYPE_BEZIER); @@ -3267,10 +3309,22 @@ void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const ERR_FAIL_INDEX(p_index, bt->values.size()); - bt->values.write[p_index].value.out_handle = p_handle; - if (bt->values[p_index].value.out_handle.x < 0) { - bt->values.write[p_index].value.out_handle.x = 0; + Vector2 out_handle = p_handle; + if (out_handle.x < 0) { + out_handle.x = 0; + } + bt->values.write[p_index].value.out_handle = out_handle; + + if (bt->values[p_index].value.handle_mode == HANDLE_MODE_BALANCED) { + Transform2D xform; + xform.set_scale(Vector2(1.0, 1.0 / p_balanced_value_time_ratio)); + + Vector2 vec_in = xform.xform(bt->values[p_index].value.in_handle); + Vector2 vec_out = xform.xform(out_handle); + + bt->values.write[p_index].value.in_handle = xform.affine_inverse().xform(-vec_out.normalized() * vec_in.length()); } + emit_changed(); } @@ -3286,6 +3340,18 @@ real_t Animation::bezier_track_get_key_value(int p_track, int p_index) const { return bt->values[p_index].value.value; } +int Animation::bezier_track_get_key_handle_mode(int p_track, int p_index) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), 0); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_BEZIER, 0); + + BezierTrack *bt = static_cast<BezierTrack *>(t); + + ERR_FAIL_INDEX_V(p_index, bt->values.size(), 0); + + return bt->values[p_index].value.handle_mode; +} + Vector2 Animation::bezier_track_get_key_in_handle(int p_track, int p_index) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector2()); Track *t = tracks[p_track]; @@ -3718,11 +3784,11 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("method_track_get_name", "track_idx", "key_idx"), &Animation::method_track_get_name); ClassDB::bind_method(D_METHOD("method_track_get_params", "track_idx", "key_idx"), &Animation::method_track_get_params); - ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track_idx", "time", "value", "in_handle", "out_handle"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2())); + ClassDB::bind_method(D_METHOD("bezier_track_insert_key", "track_idx", "time", "value", "in_handle", "out_handle", "handle_mode"), &Animation::bezier_track_insert_key, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(Animation::HandleMode::HANDLE_MODE_BALANCED)); ClassDB::bind_method(D_METHOD("bezier_track_set_key_value", "track_idx", "key_idx", "value"), &Animation::bezier_track_set_key_value); - ClassDB::bind_method(D_METHOD("bezier_track_set_key_in_handle", "track_idx", "key_idx", "in_handle"), &Animation::bezier_track_set_key_in_handle); - ClassDB::bind_method(D_METHOD("bezier_track_set_key_out_handle", "track_idx", "key_idx", "out_handle"), &Animation::bezier_track_set_key_out_handle); + ClassDB::bind_method(D_METHOD("bezier_track_set_key_in_handle", "track_idx", "key_idx", "in_handle", "balanced_value_time_ratio"), &Animation::bezier_track_set_key_in_handle, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("bezier_track_set_key_out_handle", "track_idx", "key_idx", "out_handle", "balanced_value_time_ratio"), &Animation::bezier_track_set_key_out_handle, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("bezier_track_get_key_value", "track_idx", "key_idx"), &Animation::bezier_track_get_key_value); ClassDB::bind_method(D_METHOD("bezier_track_get_key_in_handle", "track_idx", "key_idx"), &Animation::bezier_track_get_key_in_handle); @@ -3738,6 +3804,9 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("audio_track_get_key_start_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_start_offset); ClassDB::bind_method(D_METHOD("audio_track_get_key_end_offset", "track_idx", "key_idx"), &Animation::audio_track_get_key_end_offset); + ClassDB::bind_method(D_METHOD("bezier_track_set_key_handle_mode", "track_idx", "key_idx", "key_handle_mode", "balanced_value_time_ratio"), &Animation::bezier_track_set_key_handle_mode, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("bezier_track_get_key_handle_mode", "track_idx", "key_idx"), &Animation::bezier_track_get_key_handle_mode); + ClassDB::bind_method(D_METHOD("animation_track_insert_key", "track_idx", "time", "animation"), &Animation::animation_track_insert_key); ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation); ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "track_idx", "key_idx"), &Animation::animation_track_get_key_animation); @@ -3784,6 +3853,9 @@ void Animation::_bind_methods() { BIND_ENUM_CONSTANT(LOOP_NONE); BIND_ENUM_CONSTANT(LOOP_LINEAR); BIND_ENUM_CONSTANT(LOOP_PINGPONG); + + BIND_ENUM_CONSTANT(HANDLE_MODE_FREE); + BIND_ENUM_CONSTANT(HANDLE_MODE_BALANCED); } void Animation::clear() { diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 510d6c8323..8e4287e4fb 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -72,6 +72,11 @@ public: LOOP_PINGPONG, }; + enum HandleMode { + HANDLE_MODE_FREE, + HANDLE_MODE_BALANCED, + }; + private: struct Track { TrackType type = TrackType::TYPE_ANIMATION; @@ -157,10 +162,10 @@ private: }; /* BEZIER TRACK */ - struct BezierKey { Vector2 in_handle; //relative (x always <0) Vector2 out_handle; //relative (x always >0) + HandleMode handle_mode = HANDLE_MODE_BALANCED; real_t value = 0.0; }; @@ -419,11 +424,13 @@ public: void track_set_interpolation_type(int p_track, InterpolationType p_interp); InterpolationType track_get_interpolation_type(int p_track) const; - int bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle); + int bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const HandleMode p_handle_mode = HandleMode::HANDLE_MODE_BALANCED); + void bezier_track_set_key_handle_mode(int p_track, int p_index, HandleMode p_mode, double p_balanced_value_time_ratio = 1.0); void bezier_track_set_key_value(int p_track, int p_index, real_t p_value); - void bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle); - void bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle); + void bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle, double p_balanced_value_time_ratio = 1.0); + void bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle, double p_balanced_value_time_ratio = 1.0); real_t bezier_track_get_key_value(int p_track, int p_index) const; + int bezier_track_get_key_handle_mode(int p_track, int p_index) const; Vector2 bezier_track_get_key_in_handle(int p_track, int p_index) const; Vector2 bezier_track_get_key_out_handle(int p_track, int p_index) const; @@ -478,6 +485,7 @@ public: VARIANT_ENUM_CAST(Animation::TrackType); VARIANT_ENUM_CAST(Animation::InterpolationType); VARIANT_ENUM_CAST(Animation::UpdateMode); +VARIANT_ENUM_CAST(Animation::HandleMode); VARIANT_ENUM_CAST(Animation::LoopMode); #endif |