diff options
Diffstat (limited to 'editor/animation_track_editor.cpp')
-rw-r--r-- | editor/animation_track_editor.cpp | 153 |
1 files changed, 118 insertions, 35 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index c02efc445f..55ea7c0082 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -63,7 +63,6 @@ constexpr double FPS_DECIMAL = 1.0; constexpr double SECOND_DECIMAL = 0.0001; -constexpr double FACTOR = 0.0625; void AnimationTrackKeyEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); @@ -1440,8 +1439,8 @@ void AnimationTimelineEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - add_track->set_icon(get_editor_theme_icon(SNAME("Add"))); - loop->set_icon(get_editor_theme_icon(SNAME("Loop"))); + add_track->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + loop->set_button_icon(get_editor_theme_icon(SNAME("Loop"))); time_icon->set_texture(get_editor_theme_icon(SNAME("Time"))); add_track->get_popup()->clear(); @@ -1818,15 +1817,15 @@ void AnimationTimelineEdit::update_values() { switch (animation->get_loop_mode()) { case Animation::LOOP_NONE: { - loop->set_icon(get_editor_theme_icon(SNAME("Loop"))); + loop->set_button_icon(get_editor_theme_icon(SNAME("Loop"))); loop->set_pressed(false); } break; case Animation::LOOP_LINEAR: { - loop->set_icon(get_editor_theme_icon(SNAME("Loop"))); + loop->set_button_icon(get_editor_theme_icon(SNAME("Loop"))); loop->set_pressed(true); } break; case Animation::LOOP_PINGPONG: { - loop->set_icon(get_editor_theme_icon(SNAME("PingPongLoop"))); + loop->set_button_icon(get_editor_theme_icon(SNAME("PingPongLoop"))); loop->set_pressed(true); } break; default: @@ -3313,7 +3312,7 @@ Variant AnimationTrackEdit::get_drag_data(const Point2 &p_point) { Button *tb = memnew(Button); tb->set_flat(true); tb->set_text(path_cache); - tb->set_icon(icon_cache); + tb->set_button_icon(icon_cache); tb->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); tb->add_theme_constant_override("icon_max_width", get_theme_constant("class_icon_size", EditorStringName(Editor))); set_drag_preview(tb); @@ -3776,6 +3775,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re step->set_read_only(false); snap_keys->set_disabled(false); snap_timeline->set_disabled(false); + fps_compat->set_disabled(false); snap_mode->set_disabled(false); auto_fit->set_disabled(false); auto_fit_bezier->set_disabled(false); @@ -3798,6 +3798,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re step->set_read_only(true); snap_keys->set_disabled(true); snap_timeline->set_disabled(true); + fps_compat->set_disabled(true); snap_mode->set_disabled(true); bezier_edit_icon->set_disabled(true); auto_fit->set_disabled(true); @@ -4279,7 +4280,18 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p // Let's build a node path. String path = root->get_path_to(p_node, true); - Variant value = p_node->get(p_property); + // Get the value from the subpath. + Variant value = p_node; + Vector<String> property_path = p_property.split(":"); + for (const String &E : property_path) { + if (value.get_type() == Variant::OBJECT) { + Object *obj = value; + value = obj->get(E); + } else { + value = Variant(); + break; + } + } if (Object::cast_to<AnimationPlayer>(p_node) && p_property == "current_animation") { if (p_node == AnimationPlayerEditor::get_singleton()->get_player()) { @@ -5017,6 +5029,13 @@ void AnimationTrackEditor::_snap_mode_changed(int p_mode) { key_edit->set_use_fps(use_fps); } marker_edit->set_use_fps(use_fps); + // To ensure that the conversion results are consistent between serialization and load, the value is snapped with 0.0625 to be a rational number when FPS mode is used. + step->set_step(use_fps ? FPS_DECIMAL : SECOND_DECIMAL); + if (use_fps) { + fps_compat->hide(); + } else { + fps_compat->show(); + } _update_step_spinbox(); } @@ -5032,7 +5051,6 @@ void AnimationTrackEditor::_update_step_spinbox() { } else { step->set_value(1.0 / animation->get_step()); } - } else { step->set_value(animation->get_step()); } @@ -5041,6 +5059,20 @@ void AnimationTrackEditor::_update_step_spinbox() { _update_snap_unit(); } +void AnimationTrackEditor::_update_fps_compat_mode(bool p_enabled) { + _update_snap_unit(); +} + +void AnimationTrackEditor::_update_nearest_fps_label() { + bool is_fps_invalid = nearest_fps == 0; + if (is_fps_invalid) { + nearest_fps_label->hide(); + } else { + nearest_fps_label->show(); + nearest_fps_label->set_text("Nearest FPS: " + itos(nearest_fps)); + } +} + void AnimationTrackEditor::_animation_update() { timeline->queue_redraw(); timeline->update_values(); @@ -5099,18 +5131,19 @@ void AnimationTrackEditor::_notification(int p_what) { } case NOTIFICATION_THEME_CHANGED: { zoom_icon->set_texture(get_editor_theme_icon(SNAME("Zoom"))); - bezier_edit_icon->set_icon(get_editor_theme_icon(SNAME("EditBezier"))); - snap_timeline->set_icon(get_editor_theme_icon(SNAME("SnapTimeline"))); - snap_keys->set_icon(get_editor_theme_icon(SNAME("SnapKeys"))); - view_group->set_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); - selected_filter->set_icon(get_editor_theme_icon(SNAME("AnimationFilter"))); - imported_anim_warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); - dummy_player_warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); - inactive_player_warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + bezier_edit_icon->set_button_icon(get_editor_theme_icon(SNAME("EditBezier"))); + snap_timeline->set_button_icon(get_editor_theme_icon(SNAME("SnapTimeline"))); + snap_keys->set_button_icon(get_editor_theme_icon(SNAME("SnapKeys"))); + fps_compat->set_button_icon(get_editor_theme_icon(SNAME("FPS"))); + view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); + selected_filter->set_button_icon(get_editor_theme_icon(SNAME("AnimationFilter"))); + imported_anim_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + dummy_player_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + inactive_player_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), get_editor_theme_icon(SNAME("Reload"))); - auto_fit->set_icon(get_editor_theme_icon(SNAME("AnimationAutoFit"))); - auto_fit_bezier->set_icon(get_editor_theme_icon(SNAME("AnimationAutoFitBezier"))); + auto_fit->set_button_icon(get_editor_theme_icon(SNAME("AnimationAutoFit"))); + auto_fit_bezier->set_button_icon(get_editor_theme_icon(SNAME("AnimationAutoFitBezier"))); const int timeline_separation = get_theme_constant(SNAME("timeline_v_separation"), SNAME("AnimationTrackEditor")); timeline_vbox->add_theme_constant_override("separation", timeline_separation); @@ -5144,12 +5177,11 @@ void AnimationTrackEditor::_update_step(double p_new_step) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Change Animation Step")); - // To ensure that the conversion results are consistent between serialization and load, the value is snapped with 0.0625 to be a rational number. - // step_value must also be less than or equal to 1000 to ensure that no error accumulates due to interactions with retrieving values from inner range. - double step_value = MIN(1000.0, Math::snapped(p_new_step, FACTOR)); + double step_value = p_new_step; if (timeline->is_using_fps()) { if (step_value != 0.0) { - step_value = 1.0 / step_value; + // A step_value should be less than or equal to 1000 to ensure that no error accumulates due to interactions with retrieving values from inner range. + step_value = 1.0 / MIN(1000.0, p_new_step); } timeline->queue_redraw(); } @@ -5284,6 +5316,28 @@ void AnimationTrackEditor::_add_track(int p_type) { return; } adding_track_type = p_type; + Vector<StringName> valid_types; + switch (adding_track_type) { + case Animation::TYPE_BLEND_SHAPE: { + // Blend Shape is a property of MeshInstance3D. + valid_types.push_back(SNAME("MeshInstance3D")); + } break; + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + // 3D Properties come from nodes inheriting Node3D. + valid_types.push_back(SNAME("Node3D")); + } break; + case Animation::TYPE_AUDIO: { + valid_types.push_back(SNAME("AudioStreamPlayer")); + valid_types.push_back(SNAME("AudioStreamPlayer2D")); + valid_types.push_back(SNAME("AudioStreamPlayer3D")); + } break; + case Animation::TYPE_ANIMATION: { + valid_types.push_back(SNAME("AnimationPlayer")); + } break; + } + pick_track->set_valid_types(valid_types); pick_track->popup_scenetree_dialog(nullptr, root_node); pick_track->get_filter_line_edit()->clear(); pick_track->get_filter_line_edit()->grab_focus(); @@ -6324,8 +6378,9 @@ bool AnimationTrackEditor::_is_track_compatible(int p_target_track_idx, Variant: } if (path_valid) { - if (is_source_bezier) + if (is_source_bezier) { p_source_value_type = Variant::FLOAT; + } return property_type == p_source_value_type; } else { if (animation->track_get_key_count(p_target_track_idx) > 0) { @@ -7266,7 +7321,7 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) { void AnimationTrackEditor::_view_group_toggle() { _update_tracks(); - view_group->set_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); + view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); bezier_edit->set_filtered(selected_filter->is_pressed()); } @@ -7300,39 +7355,55 @@ void AnimationTrackEditor::_selection_changed() { } void AnimationTrackEditor::_update_snap_unit() { + nearest_fps = 0; + if (step->get_value() <= 0) { snap_unit = 0; + _update_nearest_fps_label(); return; // Avoid zero div. } if (timeline->is_using_fps()) { + _clear_selection(true); // Needs to recreate a spinbox of the KeyEdit. snap_unit = 1.0 / step->get_value(); } else { - double integer; - double fraction = Math::modf(step->get_value(), &integer); - fraction = 1.0 / Math::round(1.0 / fraction); - snap_unit = integer + fraction; + if (fps_compat->is_pressed()) { + snap_unit = CLAMP(step->get_value(), 0.0, 1.0); + if (!Math::is_zero_approx(snap_unit)) { + real_t fps = Math::round(1.0 / snap_unit); + nearest_fps = int(fps); + snap_unit = 1.0 / fps; + } + } else { + snap_unit = step->get_value(); + } } + _update_nearest_fps_label(); } float AnimationTrackEditor::snap_time(float p_value, bool p_relative) { if (is_snap_keys_enabled()) { + double current_snap = snap_unit; if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) { // Use more precise snapping when holding Shift. - snap_unit *= 0.25; + current_snap *= 0.25; } if (p_relative) { - double rel = Math::fmod(timeline->get_value(), snap_unit); - p_value = Math::snapped(p_value + rel, snap_unit) - rel; + double rel = Math::fmod(timeline->get_value(), current_snap); + p_value = Math::snapped(p_value + rel, current_snap) - rel; } else { - p_value = Math::snapped(p_value, snap_unit); + p_value = Math::snapped(p_value, current_snap); } } return p_value; } +float AnimationTrackEditor::get_snap_unit() { + return snap_unit; +} + void AnimationTrackEditor::_show_imported_anim_warning() { // It looks terrible on a single line but the TTR extractor doesn't support line breaks yet. EditorNode::get_singleton()->show_warning( @@ -7586,6 +7657,18 @@ AnimationTrackEditor::AnimationTrackEditor() { snap_keys->set_pressed(true); snap_keys->set_tooltip_text(TTR("Apply snapping to selected key(s).")); + fps_compat = memnew(Button); + fps_compat->set_flat(true); + bottom_hb->add_child(fps_compat); + fps_compat->set_disabled(true); + fps_compat->set_toggle_mode(true); + fps_compat->set_pressed(true); + fps_compat->set_tooltip_text(TTR("Apply snapping to the nearest integer FPS.")); + fps_compat->connect(SceneStringName(toggled), callable_mp(this, &AnimationTrackEditor::_update_fps_compat_mode)); + + nearest_fps_label = memnew(Label); + bottom_hb->add_child(nearest_fps_label); + step = memnew(EditorSpinSlider); step->set_min(0); step->set_max(1000000); @@ -8750,7 +8833,7 @@ void AnimationMarkerEdit::_move_selection_commit() { void AnimationMarkerEdit::_delete_selected_markers() { if (selection.size()) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Animation Delete Keys")); + undo_redo->create_action(TTR("Animation Delete Markers")); for (const StringName &name : selection) { double time = animation->get_marker_time(name); undo_redo->add_do_method(animation.ptr(), "remove_marker", name); @@ -8954,7 +9037,7 @@ AnimationMarkerEdit::AnimationMarkerEdit() { add_child(menu); menu->connect(SceneStringName(id_pressed), callable_mp(this, &AnimationMarkerEdit::_menu_selected)); menu->add_shortcut(ED_SHORTCUT("animation_marker_edit/rename_marker", TTR("Rename Marker"), Key::R), MENU_KEY_RENAME); - menu->add_shortcut(ED_SHORTCUT("animation_marker_edit/delete_selection", TTR("Delete Markers (s)"), Key::KEY_DELETE), MENU_KEY_DELETE); + menu->add_shortcut(ED_SHORTCUT("animation_marker_edit/delete_selection", TTR("Delete Marker(s)"), Key::KEY_DELETE), MENU_KEY_DELETE); menu->add_shortcut(ED_SHORTCUT("animation_marker_edit/toggle_marker_names", TTR("Show All Marker Names"), Key::M), MENU_KEY_TOGGLE_MARKER_NAMES); marker_insert_confirm = memnew(ConfirmationDialog); |