Merge pull request #113479 from Meorge/feat/collapse-anim-groups
Collapse groups in animation track editor
This commit is contained in:
commit
15a4311583
6 changed files with 190 additions and 12 deletions
|
|
@ -3911,7 +3911,14 @@ void AnimationTrackEditGroup::_notification(int p_what) {
|
|||
draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), v_line_color, Math::round(EDSCALE));
|
||||
|
||||
int ofs = stylebox_header->get_margin(SIDE_LEFT);
|
||||
bool is_group_folded = editor->get_current_animation()->editor_is_group_folded(node_name);
|
||||
Ref<Texture2D> fold_icon = get_theme_icon(is_group_folded ? SNAME("arrow_collapsed") : SNAME("arrow"), SNAME("Tree"));
|
||||
Size2 fold_icon_size = fold_icon->get_size();
|
||||
draw_texture_rect(fold_icon, Rect2(Point2(ofs, (get_size().height - fold_icon_size.y) / 2 + v_margin_offset).round(), fold_icon_size));
|
||||
|
||||
ofs += h_separation + fold_icon_size.x;
|
||||
draw_texture_rect(icon, Rect2(Point2(ofs, (get_size().height - icon_size.y) / 2 + v_margin_offset).round(), icon_size));
|
||||
|
||||
ofs += h_separation + icon_size.x;
|
||||
draw_string(font, Point2(ofs, (get_size().height - font->get_height(font_size)) / 2 + font->get_ascent(font_size) + v_margin_offset).round(), node_name, HORIZONTAL_ALIGNMENT_LEFT, timeline->get_name_limit() - ofs, font_size, color);
|
||||
|
||||
|
|
@ -3938,14 +3945,41 @@ void AnimationTrackEditGroup::gui_input(const Ref<InputEvent> &p_event) {
|
|||
Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
Point2 pos = mb->get_position();
|
||||
Rect2 node_name_rect = Rect2(0, 0, timeline->get_name_limit(), get_size().height);
|
||||
|
||||
if (node_name_rect.has_point(pos)) {
|
||||
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
|
||||
editor_selection->clear();
|
||||
Node *n = root->get_node_or_null(node);
|
||||
if (n) {
|
||||
editor_selection->add_node(n);
|
||||
int left_ofs = get_theme_stylebox(SNAME("header"), SNAME("AnimationTrackEditGroup"))->get_margin(SIDE_LEFT);
|
||||
bool is_group_folded = editor->get_current_animation()->editor_is_group_folded(node_name);
|
||||
Ref<Texture2D> fold_icon = get_theme_icon(is_group_folded ? SNAME("arrow_collapsed") : SNAME("arrow"), SNAME("Tree"));
|
||||
int fold_icon_width = fold_icon->get_size().width;
|
||||
Rect2 fold_area_rect = Rect2(0, 0, left_ofs + fold_icon_width, get_size().height);
|
||||
|
||||
if (fold_area_rect.has_point(pos)) {
|
||||
bool current_group_folded = !editor->get_current_animation()->editor_is_group_folded(node_name);
|
||||
editor->get_current_animation()->editor_set_group_folded(node_name, current_group_folded);
|
||||
|
||||
if (!editor->get_current_animation()->get_path().is_resource_file()) {
|
||||
EditorNode::get_editor_folding().save_scene_folding(
|
||||
EditorNode::get_singleton()->get_edited_scene(),
|
||||
EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path());
|
||||
} else {
|
||||
EditorNode::get_editor_folding().save_resource_folding(
|
||||
editor->get_current_animation(),
|
||||
editor->get_current_animation()->get_path());
|
||||
}
|
||||
|
||||
for (AnimationTrackEdit *i : track_edits) {
|
||||
i->set_visible(!current_group_folded);
|
||||
}
|
||||
|
||||
queue_redraw();
|
||||
} else {
|
||||
Rect2 node_name_rect = Rect2(0, 0, timeline->get_name_limit(), get_size().height);
|
||||
if (node_name_rect.has_point(pos)) {
|
||||
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
|
||||
editor_selection->clear();
|
||||
Node *n = root->get_node_or_null(node);
|
||||
if (n) {
|
||||
editor_selection->add_node(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4079,6 +4113,12 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animation->get_path().is_resource_file()) {
|
||||
EditorNode::get_editor_folding().load_resource_folding(
|
||||
animation,
|
||||
animation->get_path());
|
||||
}
|
||||
} else {
|
||||
hscroll->hide();
|
||||
edit->set_disabled(true);
|
||||
|
|
@ -5305,6 +5345,10 @@ void AnimationTrackEditor::_update_tracks() {
|
|||
track_edit->set_in_group(true);
|
||||
group_sort[base_path]->add_child(track_edit);
|
||||
|
||||
AnimationTrackEditGroup *g = Object::cast_to<AnimationTrackEditGroup>(group_sort[base_path]->get_child(0));
|
||||
ERR_FAIL_NULL_MSG(g, "The first child of this group's VBoxContainer isn't an AnimationTrackEditGroup. Collapsing this animation group may not work.");
|
||||
g->track_edits.push_back(track_edit);
|
||||
|
||||
} else {
|
||||
track_edit->set_in_group(false);
|
||||
}
|
||||
|
|
@ -5353,6 +5397,13 @@ void AnimationTrackEditor::_update_tracks() {
|
|||
|
||||
for (VBoxContainer *vb : group_containers) {
|
||||
track_vbox->add_child(vb);
|
||||
|
||||
AnimationTrackEditGroup *g = Object::cast_to<AnimationTrackEditGroup>(vb->get_child(0));
|
||||
if (g) {
|
||||
for (AnimationTrackEdit *i : g->track_edits) {
|
||||
i->set_visible(!animation->editor_is_group_folded(g->node_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -6399,6 +6450,9 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
|
|||
if (box_selection->is_visible_in_tree()) {
|
||||
// Only if moved.
|
||||
for (int i = 0; i < track_edits.size(); i++) {
|
||||
if (!track_edits[i]->is_visible_in_tree()) {
|
||||
continue; // Skip collapsed track edits.
|
||||
}
|
||||
Rect2 local_rect = box_select_rect;
|
||||
local_rect.position -= track_edits[i]->get_global_position();
|
||||
track_edits[i]->append_to_selection(local_rect, mb->is_command_or_control_pressed());
|
||||
|
|
|
|||
|
|
@ -568,6 +568,8 @@ class AnimationMultiTrackKeyEdit;
|
|||
class AnimationBezierTrackEdit;
|
||||
|
||||
class AnimationTrackEditGroup : public Control {
|
||||
friend class AnimationTrackEditor;
|
||||
|
||||
GDCLASS(AnimationTrackEditGroup, Control);
|
||||
Ref<Texture2D> icon;
|
||||
Vector2 icon_size;
|
||||
|
|
@ -578,7 +580,7 @@ class AnimationTrackEditGroup : public Control {
|
|||
AnimationTrackEditor *editor = nullptr;
|
||||
|
||||
bool hovered = false;
|
||||
|
||||
LocalVector<AnimationTrackEdit *> track_edits;
|
||||
void _zoom_changed();
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -2316,6 +2316,32 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath>
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// key.get_path() of p_renames is like:
|
||||
// /root/@EditorNode@18033/@Panel@14/.../Scene/TheOldName
|
||||
// value of p_renames is like:
|
||||
// /root/@EditorNode@18033/@Panel@14/.../Scene/TheNewName
|
||||
for (const KeyValue<Node *, NodePath> &rename : *p_renames) {
|
||||
NodePath old_path = rename.key->get_path();
|
||||
NodePath new_path = rename.value;
|
||||
Vector<StringName> rel_path = old_path.rel_path_to(new_path).get_names();
|
||||
|
||||
StringName old_node_name = rename.key->get_name();
|
||||
StringName new_node_name = rel_path[rel_path.size() - 1];
|
||||
|
||||
anim->editor_set_group_folded(new_node_name, anim->editor_is_group_folded(old_node_name));
|
||||
anim->editor_set_group_folded(old_node_name, false);
|
||||
}
|
||||
|
||||
if (!anim->get_path().is_resource_file()) {
|
||||
EditorNode::get_editor_folding().save_scene_folding(
|
||||
EditorNode::get_singleton()->get_edited_scene(),
|
||||
EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path());
|
||||
} else {
|
||||
EditorNode::get_editor_folding().save_resource_folding(
|
||||
anim,
|
||||
anim->get_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
#include "core/io/file_access.h"
|
||||
#include "editor/file_system/editor_paths.h"
|
||||
#include "editor/inspector/editor_inspector.h"
|
||||
#include "scene/animation/animation_mixer.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
||||
Vector<String> EditorFolding::_get_unfolds(const Object *p_object) {
|
||||
Vector<String> sections;
|
||||
|
|
@ -55,6 +57,12 @@ void EditorFolding::save_resource_folding(const Ref<Resource> &p_resource, const
|
|||
Vector<String> unfolds = _get_unfolds(p_resource.ptr());
|
||||
config->set_value("folding", "sections_unfolded", unfolds);
|
||||
|
||||
Ref<Animation> as_anim = p_resource;
|
||||
if (as_anim.is_valid()) {
|
||||
Vector<String> folded_groups = _get_animation_folds(as_anim.ptr());
|
||||
config->set_value("folding", "animation_groups_folded", folded_groups);
|
||||
}
|
||||
|
||||
String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg";
|
||||
file = EditorPaths::get_singleton()->get_project_settings_dir().path_join(file);
|
||||
config->save(file);
|
||||
|
|
@ -86,9 +94,15 @@ void EditorFolding::load_resource_folding(Ref<Resource> p_resource, const String
|
|||
unfolds = config->get_value("folding", "sections_unfolded");
|
||||
}
|
||||
_set_unfolds(p_resource.ptr(), unfolds);
|
||||
|
||||
Ref<Animation> anim = p_resource;
|
||||
if (anim.is_valid() && config->has_section_key("folding", "animation_groups_folded")) {
|
||||
Vector<String> folded_groups = config->get_value("folding", "animation_groups_folded");
|
||||
_set_animation_folds(anim.ptr(), folded_groups);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array &nodes_folded, HashSet<Ref<Resource>> &resources) {
|
||||
void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array &nodes_folded, HashSet<Ref<Resource>> &resources, HashSet<Ref<Animation>> &animations, Array &anim_groups_folded) {
|
||||
if (p_root != p_node) {
|
||||
if (!p_node->get_owner()) {
|
||||
return; //not owned, bye
|
||||
|
|
@ -108,6 +122,21 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p
|
|||
p_folds.push_back(unfolds);
|
||||
}
|
||||
|
||||
const AnimationMixer *anim_mixer = Object::cast_to<AnimationMixer>(p_node);
|
||||
if (anim_mixer) {
|
||||
List<StringName> anim_names;
|
||||
anim_mixer->get_animation_list(&anim_names);
|
||||
for (const StringName &anim_name : anim_names) {
|
||||
Ref<Animation> anim = anim_mixer->get_animation(anim_name);
|
||||
if (anim.is_valid() && !animations.has(anim) && !anim->get_path().is_empty() && !anim->get_path().is_resource_file()) {
|
||||
Vector<String> anim_folds = _get_animation_folds(anim.ptr());
|
||||
anim_groups_folded.push_back(anim->get_path());
|
||||
anim_groups_folded.push_back(anim_folds);
|
||||
animations.insert(anim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<PropertyInfo> plist;
|
||||
p_node->get_property_list(&plist);
|
||||
for (const PropertyInfo &E : plist) {
|
||||
|
|
@ -125,7 +154,7 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p
|
|||
}
|
||||
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
_fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, nodes_folded, resources);
|
||||
_fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, nodes_folded, resources, animations, anim_groups_folded);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,11 +172,14 @@ void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path
|
|||
Array unfolds, res_unfolds;
|
||||
HashSet<Ref<Resource>> resources;
|
||||
Array nodes_folded;
|
||||
_fill_folds(p_scene, p_scene, unfolds, res_unfolds, nodes_folded, resources);
|
||||
HashSet<Ref<Animation>> animations;
|
||||
Array anim_groups_folded;
|
||||
_fill_folds(p_scene, p_scene, unfolds, res_unfolds, nodes_folded, resources, animations, anim_groups_folded);
|
||||
|
||||
config->set_value("folding", "node_unfolds", unfolds);
|
||||
config->set_value("folding", "resource_unfolds", res_unfolds);
|
||||
config->set_value("folding", "nodes_folded", nodes_folded);
|
||||
config->set_value("folding", "animation_groups_folded", anim_groups_folded);
|
||||
|
||||
String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg";
|
||||
file = EditorPaths::get_singleton()->get_project_settings_dir().path_join(file);
|
||||
|
|
@ -178,9 +210,14 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) {
|
|||
if (config->has_section_key("folding", "nodes_folded")) {
|
||||
nodes_folded = config->get_value("folding", "nodes_folded");
|
||||
}
|
||||
Array animation_groups_folded;
|
||||
if (config->has_section_key("folding", "animation_groups_folded")) {
|
||||
animation_groups_folded = config->get_value("folding", "animation_groups_folded");
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(unfolds.size() & 1);
|
||||
ERR_FAIL_COND(res_unfolds.size() & 1);
|
||||
ERR_FAIL_COND(animation_groups_folded.size() & 1);
|
||||
|
||||
for (int i = 0; i < unfolds.size(); i += 2) {
|
||||
NodePath path2 = unfolds[i];
|
||||
|
|
@ -210,6 +247,17 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) {
|
|||
node->set_display_folded(true);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < animation_groups_folded.size(); i += 2) {
|
||||
String path2 = animation_groups_folded[i];
|
||||
Ref<Animation> anim = ResourceCache::get_ref(path2);
|
||||
if (anim.is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<String> folded_groups = animation_groups_folded[i + 1];
|
||||
_set_animation_folds(anim.ptr(), folded_groups);
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorFolding::has_folding_data(const String &p_path) {
|
||||
|
|
@ -294,3 +342,24 @@ void EditorFolding::unfold_scene(Node *p_scene) {
|
|||
HashSet<Ref<Resource>> resources;
|
||||
_do_node_unfolds(p_scene, p_scene, resources);
|
||||
}
|
||||
|
||||
Vector<String> EditorFolding::_get_animation_folds(const Animation *p_animation) {
|
||||
Vector<String> folded_groups;
|
||||
folded_groups.resize(p_animation->editor_get_folded_groups().size());
|
||||
if (folded_groups.size()) {
|
||||
String *w = folded_groups.ptrw();
|
||||
int idx = 0;
|
||||
for (const StringName &group_name : p_animation->editor_get_folded_groups()) {
|
||||
w[idx++] = group_name;
|
||||
}
|
||||
}
|
||||
|
||||
return folded_groups;
|
||||
}
|
||||
|
||||
void EditorFolding::_set_animation_folds(Animation *p_animation, const Vector<String> &p_folds) {
|
||||
p_animation->editor_clear_folded_groups();
|
||||
for (const String &group_name : p_folds) {
|
||||
p_animation->editor_add_folded_group(group_name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,15 +32,20 @@
|
|||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Animation;
|
||||
|
||||
class EditorFolding {
|
||||
Vector<String> _get_unfolds(const Object *p_object);
|
||||
void _set_unfolds(Object *p_object, const Vector<String> &p_unfolds);
|
||||
|
||||
void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array &nodes_folded, HashSet<Ref<Resource>> &resources);
|
||||
void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array &nodes_folded, HashSet<Ref<Resource>> &resources, HashSet<Ref<Animation>> &animations, Array &anim_groups_folded);
|
||||
|
||||
void _do_object_unfolds(Object *p_object, HashSet<Ref<Resource>> &resources);
|
||||
void _do_node_unfolds(Node *p_root, Node *p_node, HashSet<Ref<Resource>> &resources);
|
||||
|
||||
Vector<String> _get_animation_folds(const Animation *p_animation);
|
||||
void _set_animation_folds(Animation *p_animation, const Vector<String> &p_unfolds);
|
||||
|
||||
public:
|
||||
void save_resource_folding(const Ref<Resource> &p_resource, const String &p_path);
|
||||
void load_resource_folding(Ref<Resource> p_resource, const String &p_path);
|
||||
|
|
@ -48,6 +53,9 @@ public:
|
|||
void save_scene_folding(const Node *p_scene, const String &p_path);
|
||||
void load_scene_folding(Node *p_scene, const String &p_path);
|
||||
|
||||
void save_animation_folding(const Ref<Animation> &p_animation, const String &p_path);
|
||||
void load_animation_folding(Ref<Animation> p_animation, const String &p_path);
|
||||
|
||||
void unfold_scene(Node *p_scene);
|
||||
|
||||
bool has_folding_data(const String &p_path);
|
||||
|
|
|
|||
|
|
@ -251,6 +251,10 @@ private:
|
|||
|
||||
LocalVector<Track *> tracks;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
HashSet<StringName> folded_groups;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
template <typename T, typename V>
|
||||
int _insert(double p_time, T &p_keys, const V &p_value);
|
||||
|
||||
|
|
@ -539,6 +543,21 @@ public:
|
|||
void optimize(real_t p_allowed_velocity_err = 0.01, real_t p_allowed_angular_err = 0.01, int p_precision = 3);
|
||||
void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests.
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
const HashSet<StringName> &editor_get_folded_groups() const { return folded_groups; }
|
||||
void editor_clear_folded_groups() { folded_groups.clear(); }
|
||||
void editor_add_folded_group(const StringName &p_group_name) { folded_groups.insert(p_group_name); }
|
||||
void editor_remove_folded_group(const StringName &p_group_name) { folded_groups.erase(p_group_name); }
|
||||
bool editor_is_group_folded(const StringName &p_group_name) const { return folded_groups.has(p_group_name); }
|
||||
void editor_set_group_folded(const StringName &p_group_name, bool p_folded) {
|
||||
if (p_folded) {
|
||||
editor_add_folded_group(p_group_name);
|
||||
} else {
|
||||
editor_remove_folded_group(p_group_name);
|
||||
}
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
// Helper functions for Rotation.
|
||||
static double interpolate_via_rest(double p_from, double p_to, double p_weight, double p_rest = 0.0); // Deterministic slerp to prevent to cross the inverted rest axis.
|
||||
static Quaternion interpolate_via_rest(const Quaternion &p_from, const Quaternion &p_to, real_t p_weight, const Quaternion &p_rest = Quaternion()); // Deterministic slerp to prevent to cross the inverted rest axis.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue