Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add persistent folding to Animation Library Editor #86481

Merged
merged 1 commit into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 202 additions & 1 deletion editor/plugins/animation_library_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@

#include "animation_library_editor.h"

#include "core/string/print_string.h"
#include "core/string/ustring.h"
#include "core/templates/vector.h"
#include "core/variant/variant.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
Expand Down Expand Up @@ -518,6 +523,8 @@ void AnimationLibraryEditor::_item_renamed() {
if (restore_text) {
ti->set_text(0, old_text);
}

_save_mixer_lib_folding(ti);
}

void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button) {
Expand Down Expand Up @@ -670,6 +677,8 @@ void AnimationLibraryEditor::update_tree() {

TreeItem *root = tree->create_item();
List<StringName> libs;
Vector<uint64_t> collapsed_lib_ids = _load_mixer_libs_folding();

mixer->get_animation_library_list(&libs);

for (const StringName &K : libs) {
Expand Down Expand Up @@ -759,12 +768,203 @@ void AnimationLibraryEditor::update_tree() {
anitem->set_text(1, anim_path.get_file());
}
}

anitem->add_button(1, get_editor_theme_icon("Save"), ANIM_BUTTON_FILE, animation_library_is_foreign, TTR("Save animation to resource on disk."));
anitem->add_button(1, get_editor_theme_icon("Remove"), ANIM_BUTTON_DELETE, animation_library_is_foreign, TTR("Remove animation from Library."));

for (const uint64_t &lib_id : collapsed_lib_ids) {
Object *lib_obj = ObjectDB::get_instance(ObjectID(lib_id));
AnimationLibrary *cur_lib = Object::cast_to<AnimationLibrary>(lib_obj);
StringName M = mixer->get_animation_library_name(cur_lib);

if (M == K) {
libitem->set_collapsed_recursive(true);
}
}
}
}
}

void AnimationLibraryEditor::_save_mixer_lib_folding(TreeItem *p_item) {
//Check if ti is a library or animation
if (p_item->get_parent()->get_parent() != nullptr) {
return;
}

Ref<ConfigFile> config;
config.instantiate();

String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("lib_folding.cfg");
Error err = config->load(path);
if (err != OK && err != ERR_FILE_NOT_FOUND) {
ERR_PRINT("Error loading lib_folding.cfg: " + itos(err));
}

// Get unique identifier for this scene+mixer combination
String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text();

PackedStringArray collapsed_lib_names;
PackedStringArray collapsed_lib_ids;

if (config->has_section(md)) {
collapsed_lib_names = String(config->get_value(md, "folding")).split("\n");
collapsed_lib_ids = String(config->get_value(md, "id")).split("\n");
}

String lib_name = p_item->get_text(0);

// Get library reference and check validity
Ref<AnimationLibrary> al;
uint64_t lib_id = 0;

if (mixer->has_animation_library(lib_name)) {
al = mixer->get_animation_library(lib_name);
ERR_FAIL_COND(al.is_null());
lib_id = uint64_t(al->get_instance_id());
} else {
ERR_PRINT("Library not found: " + lib_name);
}

int at = collapsed_lib_names.find(lib_name);
if (p_item->is_collapsed()) {
if (at != -1) {
//Entry exists and needs updating
collapsed_lib_ids.set(at, String::num_int64(lib_id + INT64_MIN));
} else {
//Check if it's a rename
int id_at = collapsed_lib_ids.find(String::num_int64(lib_id + INT64_MIN));
if (id_at != -1) {
//It's actually a rename
collapsed_lib_names.set(id_at, lib_name);
} else {
//It's a new entry
collapsed_lib_names.append(lib_name);
collapsed_lib_ids.append(String::num_int64(lib_id + INT64_MIN));
}
}
} else {
if (at != -1) {
collapsed_lib_names.remove_at(at);
collapsed_lib_ids.remove_at(at);
}
}

//Runtime IDs
config->set_value(md, "root", uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id()));
config->set_value(md, "mixer", uint64_t(mixer->get_instance_id()));

//Plan B recovery mechanism
config->set_value(md, "mixer_signature", _get_mixer_signature());

//Save folding state as text and runtime ID
config->set_value(md, "folding", String("\n").join(collapsed_lib_names));
config->set_value(md, "id", String("\n").join(collapsed_lib_ids));

err = config->save(path);
if (err != OK) {
ERR_PRINT("Error saving lib_folding.cfg: " + itos(err));
}
}

Vector<uint64_t> AnimationLibraryEditor::_load_mixer_libs_folding() {
Ref<ConfigFile> config;
config.instantiate();

String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("lib_folding.cfg");
Error err = config->load(path);
if (err != OK && err != ERR_FILE_NOT_FOUND) {
ERR_PRINT("Error loading lib_folding.cfg: " + itos(err));
return Vector<uint64_t>();
}

// Get unique identifier for this scene+mixer combination
String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text();

Vector<uint64_t> collapsed_lib_ids;

if (config->has_section(md)) {
_load_config_libs_folding(collapsed_lib_ids, config.ptr(), md);

} else {
//The scene/mixer combination is no longer valid and we'll try to recover
uint64_t current_mixer_id = uint64_t(mixer->get_instance_id());
String current_mixer_signature = _get_mixer_signature();
List<String> sections;
config->get_sections(&sections);

for (const String &section : sections) {
Variant mixer_id = config->get_value(section, "mixer");
if ((mixer_id.get_type() == Variant::INT && uint64_t(mixer_id) == current_mixer_id) || config->get_value(section, "mixer_signature") == current_mixer_signature) { // Ensure value exists and is correct type
// Found the mixer in a different section!
_load_config_libs_folding(collapsed_lib_ids, config.ptr(), section);

//Cleanup old entry and copy fold data into new one!
String collapsed_lib_names_str = String(config->get_value(section, "folding"));
String collapsed_lib_ids_str = String(config->get_value(section, "id"));
config->erase_section(section);

config->set_value(md, "root", uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id()));
config->set_value(md, "mixer", uint64_t(mixer->get_instance_id()));
config->set_value(md, "mixer_signature", _get_mixer_signature());
config->set_value(md, "folding", collapsed_lib_names_str);
config->set_value(md, "id", collapsed_lib_ids_str);

err = config->save(path);
if (err != OK) {
ERR_PRINT("Error saving lib_folding.cfg: " + itos(err));
}
break;
}
}
}

return collapsed_lib_ids;
}

void AnimationLibraryEditor::_load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section) {
if (uint64_t(p_config->get_value(p_section, "root", 0)) != uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id())) {
// Root changed - tries to match by library names
PackedStringArray collapsed_lib_names = String(p_config->get_value(p_section, "folding", "")).split("\n");
for (const String &lib_name : collapsed_lib_names) {
if (mixer->has_animation_library(lib_name)) {
p_lib_ids.append(mixer->get_animation_library(lib_name)->get_instance_id());
} else {
print_line("Can't find ", lib_name, " in mixer");
}
}
} else {
// Root same - uses saved instance IDs
for (const String &saved_id : String(p_config->get_value(p_section, "id")).split("\n")) {
p_lib_ids.append(uint64_t(saved_id.to_int() - INT64_MIN));
}
}
}

String AnimationLibraryEditor::_get_mixer_signature() const {
String signature = String();

// Get all libraries sorted for consistency
List<StringName> libs;
mixer->get_animation_library_list(&libs);
libs.sort_custom<StringName::AlphCompare>();

// Add libraries and their animations to signature
for (const StringName &lib_name : libs) {
signature += "::" + String(lib_name);
Ref<AnimationLibrary> lib = mixer->get_animation_library(lib_name);
if (lib.is_valid()) {
List<StringName> anims;
lib->get_animation_list(&anims);
anims.sort_custom<StringName::AlphCompare>();
for (const StringName &anim_name : anims) {
signature += "," + String(anim_name);
}
}
}

return signature.md5_text();
}

void AnimationLibraryEditor::show_dialog() {
update_tree();
popup_centered_ratio(0.5);
Expand Down Expand Up @@ -855,11 +1055,12 @@ AnimationLibraryEditor::AnimationLibraryEditor() {
tree->set_column_custom_minimum_width(1, EDSCALE * 250);
tree->set_column_expand(1, false);
tree->set_hide_root(true);
tree->set_hide_folding(true);
tree->set_hide_folding(false);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);

tree->connect("item_edited", callable_mp(this, &AnimationLibraryEditor::_item_renamed));
tree->connect("button_clicked", callable_mp(this, &AnimationLibraryEditor::_button_pressed));
tree->connect("item_collapsed", callable_mp(this, &AnimationLibraryEditor::_save_mixer_lib_folding));

file_popup = memnew(PopupMenu);
add_child(file_popup);
Expand Down
7 changes: 7 additions & 0 deletions editor/plugins/animation_library_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#ifndef ANIMATION_LIBRARY_EDITOR_H
#define ANIMATION_LIBRARY_EDITOR_H

#include "core/io/config_file.h"
#include "core/templates/vector.h"
#include "editor/animation_track_editor.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_mixer.h"
Expand Down Expand Up @@ -103,6 +105,11 @@ class AnimationLibraryEditor : public AcceptDialog {
void _load_file(const String &p_path);
void _load_files(const PackedStringArray &p_paths);

void _save_mixer_lib_folding(TreeItem *p_item);
Vector<uint64_t> _load_mixer_libs_folding();
void _load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section);
String _get_mixer_signature() const;

void _item_renamed();
void _button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button);

Expand Down
12 changes: 12 additions & 0 deletions scene/animation/animation_mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/string/print_string.h"
#include "core/string/string_name.h"
#include "scene/2d/audio_stream_player_2d.h"
#include "scene/animation/animation_player.h"
#include "scene/audio/audio_stream_player.h"
Expand Down Expand Up @@ -267,6 +269,16 @@ bool AnimationMixer::has_animation_library(const StringName &p_name) const {
return false;
}

StringName AnimationMixer::get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const {
ERR_FAIL_COND_V(p_animation_library.is_null(), StringName());
for (const AnimationLibraryData &lib : animation_libraries) {
if (lib.library == p_animation_library) {
return lib.name;
}
}
return StringName();
}

StringName AnimationMixer::find_animation_library(const Ref<Animation> &p_animation) const {
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
if (E.value.animation == p_animation) {
Expand Down
1 change: 1 addition & 0 deletions scene/animation/animation_mixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ class AnimationMixer : public Node {
void get_animation_library_list(List<StringName> *p_animations) const;
Ref<AnimationLibrary> get_animation_library(const StringName &p_name) const;
bool has_animation_library(const StringName &p_name) const;
StringName get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const;
StringName find_animation_library(const Ref<Animation> &p_animation) const;
Error add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library);
void remove_animation_library(const StringName &p_name);
Expand Down