diff options
Diffstat (limited to 'editor')
52 files changed, 2999 insertions, 2220 deletions
diff --git a/editor/audio_stream_preview.cpp b/editor/audio_stream_preview.cpp index 6ae5ec43a9..6e6a7d7935 100644 --- a/editor/audio_stream_preview.cpp +++ b/editor/audio_stream_preview.cpp @@ -169,7 +169,8 @@ Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref< preview->preview->preview = maxmin; preview->preview->length = len_s; - preview->thread = Thread::create(_preview_thread, preview); + if (preview->playback.is_valid()) + preview->thread = Thread::create(_preview_thread, preview); return preview->preview; } diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 5c911a00ca..721158cebb 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -661,6 +661,21 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & List<String> remaps; config->get_section_keys("remap", &remaps); + Set<String> remap_features; + + for (List<String>::Element *F = remaps.front(); F; F = F->next()) { + + String remap = F->get(); + String feature = remap.get_slice(".", 1); + if (feature == "fallback" || features.has(feature)) { + remap_features.insert(feature); + } + } + + if (remap_features.size() > 1) { + this->resolve_platform_feature_priorities(p_preset, remap_features); + } + for (List<String>::Element *F = remaps.front(); F; F = F->next()) { String remap = F->get(); @@ -670,7 +685,8 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & p_func(p_udata, remapped_path, array, idx, total); } else if (remap.begins_with("path.")) { String feature = remap.get_slice(".", 1); - if (features.has(feature)) { + + if (remap_features.has(feature)) { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path); p_func(p_udata, remapped_path, array, idx, total); @@ -1249,9 +1265,11 @@ void EditorExportPlatformPC::get_preset_features(const Ref<EditorExportPreset> & void EditorExportPlatformPC::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/bptc"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/no_bptc_fallbacks"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/64_bits"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE), "")); @@ -1434,6 +1452,16 @@ void EditorExportPlatformPC::get_platform_features(List<String> *r_features) { } } +void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { + + if (p_features.has("bptc")) { + if (p_preset->has("texture_format/no_bptc_fallbacks")) { + p_features.erase("s3tc"); + p_features.erase("fallback"); + } + } +} + int EditorExportPlatformPC::get_chmod_flags() const { return chmod_flags; diff --git a/editor/editor_export.h b/editor/editor_export.h index 1d0b89cf16..b984d66a1b 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -246,6 +246,7 @@ public: virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual void get_platform_features(List<String> *r_features) = 0; + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) = 0; EditorExportPlatform(); }; @@ -403,6 +404,7 @@ public: void add_platform_feature(const String &p_feature); virtual void get_platform_features(List<String> *r_features); + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features); int get_chmod_flags() const; void set_chmod_flags(int p_flags); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 60826aa81b..50b3810e52 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -624,6 +624,22 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) { class_desc->pop(); } +String EditorHelp::_fix_constant(const String &p_constant) const { + + if (p_constant.strip_edges() == "4294967295") { + return "0xFFFFFFFF"; + } + + if (p_constant.strip_edges() == "2147483647") { + return "0x7FFFFFFF"; + } + if (p_constant.strip_edges() == "1048575") { + return "0xfffff"; + } + + return p_constant; +} + void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview) { method_line[p_method.name] = class_desc->get_line_count() - 2; //gets overridden if description @@ -673,7 +689,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview class_desc->push_color(symbol_color); class_desc->add_text("="); class_desc->pop(); - _add_text(p_method.arguments[j].default_value); + _add_text(_fix_constant(p_method.arguments[j].default_value)); } class_desc->pop(); @@ -1781,7 +1797,7 @@ void EditorHelp::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - class_desc->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + class_desc->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); _update_doc(); } break; @@ -1863,7 +1879,7 @@ EditorHelp::EditorHelp() { class_desc = memnew(RichTextLabel); add_child(class_desc); class_desc->set_v_size_flags(SIZE_EXPAND_FILL); - class_desc->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + class_desc->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); class_desc->connect("meta_clicked", this, "_class_desc_select"); class_desc->connect("gui_input", this, "_class_desc_input"); @@ -1929,7 +1945,7 @@ void EditorHelpBit::_notification(int p_what) { switch (p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + rich_text->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); } break; default: break; @@ -1948,7 +1964,7 @@ EditorHelpBit::EditorHelpBit() { add_child(rich_text); //rich_text->set_anchors_and_margins_preset(Control::PRESET_WIDE); rich_text->connect("meta_clicked", this, "_meta_clicked"); - rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + rich_text->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); rich_text->set_override_selected_font_color(false); set_custom_minimum_size(Size2(0, 70 * EDSCALE)); } diff --git a/editor/editor_help.h b/editor/editor_help.h index dbea97e98b..ad81a39945 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -244,6 +244,8 @@ class EditorHelp : public VBoxContainer { void _unhandled_key_input(const Ref<InputEvent> &p_ev); + String _fix_constant(const String &p_constant) const; + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 99a2b2aa67..852e1930d2 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1533,9 +1533,10 @@ void EditorInspector::update_tree() { if (capitalize_paths) path_name = path_name.capitalize(); + Color c = sscolor; c.a /= level; - section->setup(path_name, acc_path, object, c, use_folding); + section->setup(acc_path, path_name, object, c, use_folding); item_path[acc_path] = section->get_vbox(); } @@ -2221,7 +2222,7 @@ EditorInspector::EditorInspector() { show_categories = false; hide_script = true; use_doc_hints = false; - capitalize_paths = false; + capitalize_paths = true; use_filter = false; autoclear = false; changing = 0; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0be6ee8759..0ca70c41fa 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -74,6 +74,7 @@ #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/animation_state_machine_editor.h" #include "editor/plugins/animation_tree_editor_plugin.h" +#include "editor/plugins/animation_tree_player_editor_plugin.h" #include "editor/plugins/asset_library_editor_plugin.h" #include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/baked_lightmap_editor_plugin.h" @@ -83,7 +84,6 @@ #include "editor/plugins/collision_polygon_editor_plugin.h" #include "editor/plugins/collision_shape_2d_editor_plugin.h" #include "editor/plugins/cpu_particles_editor_plugin.h" -#include "editor/plugins/cube_grid_theme_editor_plugin.h" #include "editor/plugins/curve_editor_plugin.h" #include "editor/plugins/editor_preview_plugins.h" #include "editor/plugins/gi_probe_editor_plugin.h" @@ -94,6 +94,7 @@ #include "editor/plugins/material_editor_plugin.h" #include "editor/plugins/mesh_editor_plugin.h" #include "editor/plugins/mesh_instance_editor_plugin.h" +#include "editor/plugins/mesh_library_editor_plugin.h" #include "editor/plugins/multimesh_editor_plugin.h" #include "editor/plugins/navigation_polygon_editor_plugin.h" #include "editor/plugins/particles_2d_editor_plugin.h" @@ -4591,6 +4592,10 @@ EditorNode::EditorNode() { VisualServer::get_singleton()->textures_keep_original(true); VisualServer::get_singleton()->set_debug_generate_wireframes(true); + PhysicsServer::get_singleton()->set_active(false); // no physics by default if editor + Physics2DServer::get_singleton()->set_active(false); // no physics by default if editor + ScriptServer::set_scripting_enabled(false); // no scripting by default if editor + EditorHelp::generate_doc(); //before any editor classes are crated SceneState::set_disable_placeholders(true); ResourceLoader::clear_translation_remaps(); //no remaps using during editor @@ -5589,16 +5594,13 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(ShaderEditorPlugin(this))); add_editor_plugin(memnew(VisualShaderEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendTreeEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendSpace1DEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendSpace2DEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeStateMachineEditorPlugin(this))); add_editor_plugin(memnew(CameraEditorPlugin(this))); add_editor_plugin(memnew(ThemeEditorPlugin(this))); add_editor_plugin(memnew(MultiMeshEditorPlugin(this))); add_editor_plugin(memnew(MeshInstanceEditorPlugin(this))); add_editor_plugin(memnew(AnimationTreeEditorPlugin(this))); + add_editor_plugin(memnew(AnimationTreePlayerEditorPlugin(this))); add_editor_plugin(memnew(MeshLibraryEditorPlugin(this))); add_editor_plugin(memnew(StyleBoxEditorPlugin(this))); add_editor_plugin(memnew(SpriteEditorPlugin(this))); @@ -5683,10 +5685,6 @@ EditorNode::EditorNode() { _edit_current(); current = NULL; - PhysicsServer::get_singleton()->set_active(false); // no physics by default if editor - Physics2DServer::get_singleton()->set_active(false); // no physics by default if editor - ScriptServer::set_scripting_enabled(false); // no scripting by default if editor - reference_resource_mem = true; save_external_resources_mem = true; diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h index 310cab699f..194fac6b92 100644 --- a/editor/editor_plugin_settings.h +++ b/editor/editor_plugin_settings.h @@ -31,7 +31,7 @@ #ifndef EDITORPLUGINSETTINGS_H #define EDITORPLUGINSETTINGS_H -#include "editor/plugin_config_dialog.h"; +#include "editor/plugin_config_dialog.h" #include "editor_data.h" #include "property_editor.h" #include "scene/gui/dialogs.h" diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 0cbd5f0bff..5f1e7273e5 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -61,7 +61,7 @@ void EditorPropertyText::_text_changed(const String &p_string) { if (updating) return; - emit_signal("property_changed", get_edited_property(), p_string); + emit_signal("property_changed", get_edited_property(), p_string, true); } void EditorPropertyText::update_property() { @@ -72,6 +72,10 @@ void EditorPropertyText::update_property() { updating = false; } +void EditorPropertyText::set_placeholder(const String &p_string) { + text->set_placeholder(p_string); +} + void EditorPropertyText::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_changed", "txt"), &EditorPropertyText::_text_changed); @@ -92,12 +96,11 @@ EditorPropertyText::EditorPropertyText() { void EditorPropertyMultilineText::_big_text_changed() { text->set_text(big_text->get_text()); - emit_signal("property_changed", get_edited_property(), big_text->get_text()); + emit_signal("property_changed", get_edited_property(), big_text->get_text(), true); } void EditorPropertyMultilineText::_text_changed() { - - emit_signal("property_changed", get_edited_property(), text->get_text()); + emit_signal("property_changed", get_edited_property(), text->get_text(), true); } void EditorPropertyMultilineText::_open_big_text() { @@ -1735,12 +1738,18 @@ EditorPropertyTransform::EditorPropertyTransform() { void EditorPropertyColor::_color_changed(const Color &p_color) { - emit_signal("property_changed", get_edited_property(), p_color); + emit_signal("property_changed", get_edited_property(), p_color, true); +} + +void EditorPropertyColor::_popup_closed() { + + emit_signal("property_changed", get_edited_property(), picker->get_pick_color(), false); } void EditorPropertyColor::_bind_methods() { ClassDB::bind_method(D_METHOD("_color_changed"), &EditorPropertyColor::_color_changed); + ClassDB::bind_method(D_METHOD("_popup_closed"), &EditorPropertyColor::_popup_closed); } void EditorPropertyColor::update_property() { @@ -1758,6 +1767,7 @@ EditorPropertyColor::EditorPropertyColor() { add_child(picker); picker->set_flat(true); picker->connect("color_changed", this, "_color_changed"); + picker->connect("popup_closed", this, "_popup_closed"); } ////////////// NODE PATH ////////////////////// @@ -2777,6 +2787,9 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } else { EditorPropertyText *editor = memnew(EditorPropertyText); + if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { + editor->set_placeholder(p_hint_text); + } add_property_editor(p_path, editor); } } break; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index d5fac9c1a0..5726ccfa41 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -61,6 +61,7 @@ protected: public: virtual void update_property(); + void set_placeholder(const String &p_string); EditorPropertyText(); }; @@ -476,6 +477,7 @@ class EditorPropertyColor : public EditorProperty { GDCLASS(EditorPropertyColor, EditorProperty) ColorPickerButton *picker; void _color_changed(const Color &p_color); + void _popup_closed(); protected: static void _bind_methods(); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 8203c85c6a..23dbb026dd 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -125,13 +125,13 @@ EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() { ///////////////////// ARRAY /////////////////////////// -void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value) { +void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value, bool changing) { if (p_prop.begins_with("indices")) { int idx = p_prop.get_slice("/", 1).to_int(); Variant array = object->get_array(); array.set(idx, p_value); - emit_signal("property_changed", get_edited_property(), array); + emit_signal("property_changed", get_edited_property(), array, true); if (array.get_type() == Variant::ARRAY) { array = array.call("duplicate"); //dupe, so undo/redo works better @@ -544,7 +544,7 @@ void EditorPropertyArray::_bind_methods() { ClassDB::bind_method("_edit_pressed", &EditorPropertyArray::_edit_pressed); ClassDB::bind_method("_page_changed", &EditorPropertyArray::_page_changed); ClassDB::bind_method("_length_changed", &EditorPropertyArray::_length_changed); - ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed, DEFVAL(false)); ClassDB::bind_method("_change_type", &EditorPropertyArray::_change_type); ClassDB::bind_method("_change_type_menu", &EditorPropertyArray::_change_type_menu); } @@ -579,7 +579,7 @@ EditorPropertyArray::EditorPropertyArray() { ///////////////////// DICTIONARY /////////////////////////// -void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value) { +void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value, bool changing) { if (p_prop == "new_item_key") { @@ -593,7 +593,7 @@ void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p Variant key = dict.get_key_at_index(idx); dict[key] = p_value; - emit_signal("property_changed", get_edited_property(), dict); + emit_signal("property_changed", get_edited_property(), dict, true); dict = dict.duplicate(); //dupe, so undo/redo works better object->set_dict(dict); @@ -1006,7 +1006,7 @@ void EditorPropertyDictionary::_page_changed(double p_page) { void EditorPropertyDictionary::_bind_methods() { ClassDB::bind_method("_edit_pressed", &EditorPropertyDictionary::_edit_pressed); ClassDB::bind_method("_page_changed", &EditorPropertyDictionary::_page_changed); - ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed, DEFVAL(false)); ClassDB::bind_method("_change_type", &EditorPropertyDictionary::_change_type); ClassDB::bind_method("_change_type_menu", &EditorPropertyDictionary::_change_type_menu); ClassDB::bind_method("_add_key_value", &EditorPropertyDictionary::_add_key_value); diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index 75c67d280d..a8ddb02e9d 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -67,7 +67,7 @@ class EditorPropertyArray : public EditorProperty { void _page_changed(double p_page); void _length_changed(double p_page); void _edit_pressed(); - void _property_changed(const String &p_prop, Variant p_value); + void _property_changed(const String &p_prop, Variant p_value, bool changing = false); void _change_type(Object *p_button, int p_index); void _change_type_menu(int p_index); @@ -99,7 +99,7 @@ class EditorPropertyDictionary : public EditorProperty { void _page_changed(double p_page); void _edit_pressed(); - void _property_changed(const String &p_prop, Variant p_value); + void _property_changed(const String &p_prop, Variant p_value, bool changing = false); void _change_type(Object *p_button, int p_index); void _change_type_menu(int p_index); diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 62870ab35c..bbd18306a2 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -196,8 +196,8 @@ Error EditorRun::run(const String &p_scene, const String p_custom_args, const Li void EditorRun::stop() { if (status != STATUS_STOP && pid != 0) { - - OS::get_singleton()->kill(pid); + const int max_wait_msec = GLOBAL_DEF("editor/stop_max_wait_msec", 10000); + OS::get_singleton()->kill(pid, max_wait_msec); } status = STATUS_STOP; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index e1ec48a9f9..9278d7676a 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -357,6 +357,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Adaptive,Default,Custom"); _initial_set("text_editor/theme/line_spacing", 4); + _initial_set("text_editor/theme/selection_color", Color::html("40808080")); _load_default_text_editor_theme(); @@ -473,6 +474,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["editors/3d/freelook/freelook_modifier_speed_factor"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_modifier_speed_factor", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.1"); _initial_set("editors/3d/freelook/freelook_speed_zoom_link", false); + _initial_set("editors/2d/grid_color", Color(1.0, 1.0, 1.0, 0.07)); _initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8)); _initial_set("editors/2d/bone_width", 5); _initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9)); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 69a013dd00..50d71f1c98 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -381,12 +381,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("error_color", "Editor", error_color); theme->set_color("property_color", "Editor", property_color); - // 2d grid color - const Color grid_minor_color = mono_color * Color(1.0, 1.0, 1.0, 0.07); - const Color grid_major_color = Color(font_color_disabled.r, font_color_disabled.g, font_color_disabled.b, 0.15); - theme->set_color("grid_major_color", "Editor", grid_major_color); - theme->set_color("grid_minor_color", "Editor", grid_minor_color); - const int thumb_size = EDITOR_DEF("filesystem/file_dialog/thumbnail_size", 64); theme->set_constant("scale", "Editor", EDSCALE); theme->set_constant("thumb_size", "Editor", thumb_size); @@ -971,8 +965,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // GraphEdit theme->set_stylebox("bg", "GraphEdit", style_tree_bg); - theme->set_color("grid_major", "GraphEdit", grid_major_color); - theme->set_color("grid_minor", "GraphEdit", grid_minor_color); + theme->set_color("grid_major", "GraphEdit", Color(1.0, 1.0, 1.0, 0.15)); + theme->set_color("grid_minor", "GraphEdit", Color(1.0, 1.0, 1.0, 0.07)); theme->set_color("activity", "GraphEdit", accent_color); theme->set_icon("minus", "GraphEdit", theme->get_icon("ZoomLess", "EditorIcons")); theme->set_icon("more", "GraphEdit", theme->get_icon("ZoomMore", "EditorIcons")); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 2be1f1644e..ef7409fd43 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -37,10 +37,10 @@ #include "scene/gui/check_box.h" #include "scene/gui/file_dialog.h" #include "scene/gui/grid_container.h" -#include "scene/gui/item_list.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/progress_bar.h" +#include "scene/gui/tree.h" #define ROOT_PREFIX "res://" @@ -58,6 +58,34 @@ static bool is_text_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } +static bool find_next(const String &line, String pattern, int from, bool match_case, bool whole_words, int &out_begin, int &out_end) { + + int end = from; + + while (true) { + int begin = match_case ? line.find(pattern, end) : line.findn(pattern, end); + + if (begin == -1) + return false; + + end = begin + pattern.length(); + out_begin = begin; + out_end = end; + + if (whole_words) { + if (begin > 0 && is_text_char(line[begin - 1])) { + continue; + } + if (end < line.size() && is_text_char(line[end])) { + continue; + } + } + + return true; + } +} + +//-------------------------------------------------------------------------------- FindInFiles::FindInFiles() { _root_prefix = ROOT_PREFIX; _extension_filter.insert("gd"); @@ -246,24 +274,7 @@ void FindInFiles::_scan_file(String fpath) { String line = f->get_line(); - // Find all occurrences in the current line - while (true) { - begin = _match_case ? line.find(_pattern, end) : line.findn(_pattern, end); - - if (begin == -1) - break; - - end = begin + _pattern.length(); - - if (_whole_words) { - if (begin > 0 && is_text_char(line[begin - 1])) { - continue; - } - if (end < line.size() && is_text_char(line[end])) { - continue; - } - } - + while (find_next(line, _pattern, end, _match_case, _whole_words, begin, end)) { emit_signal(SIGNAL_RESULT_FOUND, fpath, line_number, begin, end, line); } } @@ -567,14 +578,18 @@ FindInFilesPanel::FindInFilesPanel() { vbc->add_child(hbc); } - // In the future, this should be replaced by a more specific list container, - // which can highlight text regions and change opacity for enabled/disabled states - _results_display = memnew(ItemList); + _results_display = memnew(Tree); _results_display->add_font_override("font", get_font("source", "EditorFonts")); _results_display->set_v_size_flags(SIZE_EXPAND_FILL); _results_display->connect("item_selected", this, "_on_result_selected"); + _results_display->connect("item_edited", this, "_on_item_edited"); + _results_display->set_hide_root(true); + _results_display->set_select_mode(Tree::SELECT_ROW); + _results_display->create_item(); // Root vbc->add_child(_results_display); + _with_replace = false; + { _replace_container = memnew(HBoxContainer); @@ -600,12 +615,33 @@ FindInFilesPanel::FindInFilesPanel() { void FindInFilesPanel::set_with_replace(bool with_replace) { + _with_replace = with_replace; _replace_container->set_visible(with_replace); + + if (with_replace) { + // Results show checkboxes on their left so they can be opted out + _results_display->set_columns(2); + _results_display->set_column_expand(0, false); + _results_display->set_column_min_width(0, 48 * EDSCALE); + + } else { + // Results are single-cell items + _results_display->set_column_expand(0, true); + _results_display->set_columns(1); + } +} + +void FindInFilesPanel::clear() { + _file_items.clear(); + _result_items.clear(); + _results_display->clear(); + _results_display->create_item(); // Root } void FindInFilesPanel::start_search() { - _results_display->clear(); + clear(); + _status_label->set_text(TTR("Searching...")); _search_text_label->set_text(_finder->get_search_text()); @@ -636,9 +672,90 @@ void FindInFilesPanel::_notification(int p_what) { void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin, int end, String text) { - int i = _results_display->get_item_count(); - _results_display->add_item(fpath + ": " + String::num(line_number) + ": " + text.replace("\t", " ")); - _results_display->set_item_metadata(i, varray(fpath, line_number, begin, end)); + TreeItem *file_item; + Map<String, TreeItem *>::Element *E = _file_items.find(fpath); + + if (E == NULL) { + file_item = _results_display->create_item(); + file_item->set_text(0, fpath); + file_item->set_metadata(0, fpath); + + // The width of this column is restrained to checkboxes, but that doesn't make sense for the parent items, + // so we override their width so they can expand to full width + file_item->set_expand_right(0, true); + + _file_items[fpath] = file_item; + + } else { + file_item = E->value(); + } + + int text_index = _with_replace ? 1 : 0; + + TreeItem *item = _results_display->create_item(file_item); + + // Do this first because it resets properties of the cell... + item->set_cell_mode(text_index, TreeItem::CELL_MODE_CUSTOM); + + String item_text = String::num_int64(line_number) + ": " + text.replace("\t", " "); + + item->set_text(text_index, item_text); + item->set_custom_draw(text_index, this, "_draw_result_text"); + + Ref<Font> font = _results_display->get_font("font"); + + float raw_text_width = font->get_string_size(text).x; + float item_text_width = font->get_string_size(item_text).x; + + Result r; + r.line_number = line_number; + r.begin = begin; + r.end = end; + r.draw_begin = (item_text_width - raw_text_width) + font->get_string_size(text.left(r.begin)).x; + r.draw_width = font->get_string_size(text.substr(r.begin, r.end - r.begin + 1)).x; + _result_items[item] = r; + + if (_with_replace) { + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_checked(0, true); + item->set_editable(0, true); + } +} + +void FindInFilesPanel::draw_result_text(Object *item_obj, Rect2 rect) { + + TreeItem *item = Object::cast_to<TreeItem>(item_obj); + if (!item) + return; + + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + if (!E) + return; + Result r = E->value(); + + Rect2 match_rect = rect; + match_rect.position.x += r.draw_begin; + match_rect.size.x = r.draw_width; + match_rect.position.y += 1 * EDSCALE; + match_rect.size.y -= 2 * EDSCALE; + + _results_display->draw_rect(match_rect, Color(0, 0, 0, 0.5)); + // Text is drawn by Tree already +} + +void FindInFilesPanel::_on_item_edited() { + + TreeItem *item = _results_display->get_selected(); + + if (item->is_checked(0)) { + item->set_custom_color(1, _results_display->get_color("font_color")); + + } else { + // Grey out + Color color = _results_display->get_color("font_color"); + color.a /= 2.0; + item->set_custom_color(1, color); + } } void FindInFilesPanel::_on_finished() { @@ -653,10 +770,19 @@ void FindInFilesPanel::_on_cancel_button_clicked() { stop_search(); } -void FindInFilesPanel::_on_result_selected(int i) { +void FindInFilesPanel::_on_result_selected() { + + TreeItem *item = _results_display->get_selected(); + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + + if (E == NULL) + return; + Result r = E->value(); + + TreeItem *file_item = item->get_parent(); + String fpath = file_item->get_metadata(0); - Array meta = _results_display->get_item_metadata(i); - emit_signal(SIGNAL_RESULT_SELECTED, meta[0], meta[1], meta[2], meta[3]); + emit_signal(SIGNAL_RESULT_SELECTED, fpath, r.line_number, r.begin, r.end); } void FindInFilesPanel::_on_replace_text_changed(String text) { @@ -668,39 +794,33 @@ void FindInFilesPanel::_on_replace_all_clicked() { String replace_text = get_replace_text(); ERR_FAIL_COND(replace_text.empty()); - String last_fpath; - PoolIntArray locations; PoolStringArray modified_files; - for (int i = 0; i < _results_display->get_item_count(); ++i) { + for (Map<String, TreeItem *>::Element *E = _file_items.front(); E; E = E->next()) { - Array meta = _results_display->get_item_metadata(i); + TreeItem *file_item = E->value(); + String fpath = file_item->get_metadata(0); - String fpath = meta[0]; + Vector<Result> locations; + for (TreeItem *item = file_item->get_children(); item; item = item->get_next()) { - // Results are sorted by file, so we can batch replaces - if (fpath != last_fpath) { - if (locations.size() != 0) { - apply_replaces_in_file(last_fpath, locations, replace_text); - modified_files.append(last_fpath); - locations.resize(0); - } - } + if (!item->is_checked(0)) + continue; - locations.append(meta[1]); // line_number - locations.append(meta[2]); // begin - locations.append(meta[3]); // end - - last_fpath = fpath; - } + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + ERR_FAIL_COND(E == NULL); + locations.push_back(E->value()); + } - if (locations.size() != 0) { - apply_replaces_in_file(last_fpath, locations, replace_text); - modified_files.append(last_fpath); + if (locations.size() != 0) { + // Results are sorted by file, so we can batch replaces + apply_replaces_in_file(fpath, locations, replace_text); + modified_files.append(fpath); + } } // Hide replace bar so we can't trigger the action twice without doing a new search - set_with_replace(false); + _replace_container->hide(); emit_signal(SIGNAL_FILES_MODIFIED, modified_files); } @@ -740,11 +860,7 @@ private: Vector<char> _line_buffer; }; -void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locations, String text) { - - ERR_FAIL_COND(locations.size() % 3 != 0); - - //print_line(String("Replacing {0} occurrences in {1}").format(varray(fpath, locations.size() / 3))); +void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector<Result> &locations, String new_text) { // If the file is already open, I assume the editor will reload it. // If there are unsaved changes, the user will be asked on focus, @@ -759,21 +875,34 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locatio ConservativeGetLine conservative; String line = conservative.get_line(f); + String search_text = _finder->get_search_text(); + + int offset = 0; - PoolIntArray::Read locations_read = locations.read(); - for (int i = 0; i < locations.size(); i += 3) { + for (int i = 0; i < locations.size(); ++i) { - int repl_line_number = locations_read[i]; - int repl_begin = locations_read[i + 1]; - int repl_end = locations_read[i + 2]; + int repl_line_number = locations[i].line_number; while (current_line < repl_line_number) { buffer += line; line = conservative.get_line(f); ++current_line; + offset = 0; + } + + int repl_begin = locations[i].begin + offset; + int repl_end = locations[i].end + offset; + + int _; + if (!find_next(line, search_text, repl_begin, _finder->is_match_case(), _finder->is_whole_words(), _, _)) { + // Make sure the replace is still valid in case the file was tampered with. + print_line(String("Occurrence no longer matches, replace will be ignored in {0}: line {1}, col {2}").format(varray(fpath, repl_line_number, repl_begin))); + continue; } - line = line.left(repl_begin) + text + line.right(repl_end); + line = line.left(repl_begin) + new_text + line.right(repl_end); + // keep an offset in case there are successive replaces in the same line + offset += new_text.length() - (repl_end - repl_begin); } buffer += line; @@ -811,11 +940,13 @@ void FindInFilesPanel::set_progress_visible(bool visible) { void FindInFilesPanel::_bind_methods() { ClassDB::bind_method("_on_result_found", &FindInFilesPanel::_on_result_found); + ClassDB::bind_method("_on_item_edited", &FindInFilesPanel::_on_item_edited); ClassDB::bind_method("_on_finished", &FindInFilesPanel::_on_finished); ClassDB::bind_method("_on_cancel_button_clicked", &FindInFilesPanel::_on_cancel_button_clicked); ClassDB::bind_method("_on_result_selected", &FindInFilesPanel::_on_result_selected); ClassDB::bind_method("_on_replace_text_changed", &FindInFilesPanel::_on_replace_text_changed); ClassDB::bind_method("_on_replace_all_clicked", &FindInFilesPanel::_on_replace_all_clicked); + ClassDB::bind_method("_draw_result_text", &FindInFilesPanel::draw_result_text); ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_SELECTED, PropertyInfo(Variant::STRING, "path"), diff --git a/editor/find_in_files.h b/editor/find_in_files.h index d57184960b..75ea1c3161 100644 --- a/editor/find_in_files.h +++ b/editor/find_in_files.h @@ -131,7 +131,8 @@ private: }; class Button; -class ItemList; +class Tree; +class TreeItem; class ProgressBar; // Display search results @@ -159,22 +160,37 @@ private: void _on_result_found(String fpath, int line_number, int begin, int end, String text); void _on_finished(); void _on_cancel_button_clicked(); - void _on_result_selected(int i); + void _on_result_selected(); + void _on_item_edited(); void _on_replace_text_changed(String text); void _on_replace_all_clicked(); - void apply_replaces_in_file(String fpath, PoolIntArray locations, String text); + struct Result { + int line_number; + int begin; + int end; + float draw_begin; + float draw_width; + }; + void apply_replaces_in_file(String fpath, const Vector<Result> &locations, String new_text); void update_replace_buttons(); String get_replace_text(); + + void draw_result_text(Object *item_obj, Rect2 rect); + void set_progress_visible(bool visible); + void clear(); FindInFiles *_finder; Label *_search_text_label; - ItemList *_results_display; + Tree *_results_display; Label *_status_label; Button *_cancel_button; ProgressBar *_progress_bar; + Map<String, TreeItem *> _file_items; + Map<TreeItem *, Result> _result_items; + bool _with_replace; HBoxContainer *_replace_container; LineEdit *_replace_line_edit; diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 2f958a6fdd..211bb7bcc9 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -163,6 +163,7 @@ void ResourceImporterLayeredTexture::_save_tex(const Vector<Ref<Image> > &p_imag Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { int compress_mode = p_options["compress/mode"]; + int no_bptc_if_rgb = p_options["compress/no_bptc_if_rgb"]; int repeat = p_options["flags/repeat"]; bool filter = p_options["flags/filter"]; bool mipmaps = p_options["flags/mipmaps"]; @@ -226,6 +227,26 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const //Android, GLES 2.x bool ok_on_pc = false; + bool encode_bptc = false; + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) { + + encode_bptc = true; + + if (no_bptc_if_rgb) { + Image::DetectChannels channels = image->get_detected_channels(); + if (channels != Image::DETECTED_LA && channels != Image::DETECTED_RGBA) { + encode_bptc = false; + } + } + } + + if (encode_bptc) { + + _save_tex(slices, p_save_path + ".bptc." + extension, compress_mode, Image::COMPRESS_BPTC, mipmaps, tex_flags); + r_platform_variants->push_back("bptc"); + ok_on_pc = true; + } if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc")) { diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 17a9394b51..7feb506270 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -188,7 +188,8 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options, r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_mode", PROPERTY_HINT_ENUM, "Compress,Force RGBE"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/no_bptc_if_rgb"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_mode", PROPERTY_HINT_ENUM, "LDR Fallback,Force RGBE,RGBE Fallback"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), p_preset == PRESET_3D ? 1 : 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), p_preset == PRESET_2D_PIXEL ? false : true)); @@ -198,6 +199,7 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options, r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D ? true : false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/HDR_as_SRGB"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/invert_color"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "stream"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "detect_3d"), p_preset == PRESET_DETECT)); @@ -346,6 +348,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { int compress_mode = p_options["compress/mode"]; + int no_bptc_if_rgb = p_options["compress/no_bptc_if_rgb"]; float lossy = p_options["compress/lossy_quality"]; int repeat = p_options["flags/repeat"]; bool filter = p_options["flags/filter"]; @@ -354,9 +357,11 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String int srgb = p_options["flags/srgb"]; bool fix_alpha_border = p_options["process/fix_alpha_border"]; bool premult_alpha = p_options["process/premult_alpha"]; + bool invert_color = p_options["process/invert_color"]; bool stream = p_options["stream"]; int size_limit = p_options["size_limit"]; bool force_rgbe = int(p_options["compress/hdr_mode"]) == 1; + bool rgbe_fallback = int(p_options["compress/hdr_mode"]) == 2; bool hdr_as_srgb = p_options["process/HDR_as_SRGB"]; int normal = p_options["compress/normal_map"]; float scale = p_options["svg/scale"]; @@ -409,6 +414,19 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String image->premultiply_alpha(); } + if (invert_color) { + int height = image->get_height(); + int width = image->get_width(); + + image->lock(); + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + image->set_pixel(i, j, image->get_pixel(i, j).inverted()); + } + } + image->unlock(); + } + bool detect_3d = p_options["detect_3d"]; bool detect_srgb = srgb == 2; bool detect_normal = normal == 0; @@ -419,31 +437,59 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String //Android, GLES 2.x bool ok_on_pc = false; + bool encode_bptc = false; + bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); + bool no_ldr_compression = (is_hdr && rgbe_fallback); + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) { + + encode_bptc = true; + + if (no_bptc_if_rgb && !is_hdr) { + Image::DetectChannels channels = image->get_detected_channels(); + if (channels != Image::DETECTED_LA && channels != Image::DETECTED_RGBA) { + encode_bptc = false; + } + } + } + + if (encode_bptc) { - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc")) { + _save_stex(image, p_save_path + ".bptc.stex", compress_mode, lossy, Image::COMPRESS_BPTC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); + r_platform_variants->push_back("bptc"); + ok_on_pc = true; + } + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc") && !no_ldr_compression) { _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, Image::COMPRESS_S3TC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); r_platform_variants->push_back("s3tc"); ok_on_pc = true; } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2") && !no_ldr_compression) { _save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); r_platform_variants->push_back("etc2"); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc") && !no_ldr_compression) { _save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); r_platform_variants->push_back("etc"); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc") && !no_ldr_compression) { _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); r_platform_variants->push_back("pvrtc"); } + if (is_hdr && rgbe_fallback) { + _save_stex(image, p_save_path + ".fallback.stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags, stream, detect_3d, detect_srgb, true, detect_normal, force_normal); + r_platform_variants->push_back("fallback"); + ok_on_pc = true; + } + if (!ok_on_pc) { EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correcly on PC."); } diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 41f5a892eb..9e99dcc5c8 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -157,15 +157,18 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s //Consider revision for engine version 3.0 compression_code = file->get_16(); if (compression_code != 1 && compression_code != 3) { - ERR_PRINT("Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead."); - break; + file->close(); + memdelete(file); + ERR_EXPLAIN("Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead."); + ERR_FAIL_V(ERR_INVALID_DATA); } format_channels = file->get_16(); if (format_channels != 1 && format_channels != 2) { - - ERR_PRINT("Format not supported for WAVE file (not stereo or mono)"); - break; + file->close(); + memdelete(file); + ERR_EXPLAIN("Format not supported for WAVE file (not stereo or mono)."); + ERR_FAIL_V(ERR_INVALID_DATA); } format_freq = file->get_32(); //sampling rate @@ -174,10 +177,11 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s file->get_16(); // block align (unused) format_bits = file->get_16(); // bits per sample - if (format_bits % 8) { - - ERR_PRINT("Strange number of bits in sample (not 8,16,24,32)"); - break; + if (format_bits % 8 || format_bits == 0) { + file->close(); + memdelete(file); + ERR_EXPLAIN("Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32)."); + ERR_FAIL_V(ERR_INVALID_DATA); } /* Don't need anything else, continue */ @@ -185,7 +189,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s } if (chunkID[0] == 'd' && chunkID[1] == 'a' && chunkID[2] == 't' && chunkID[3] == 'a' && !data_found) { - /* IS FORMAT CHUNK */ + /* IS DATA CHUNK */ data_found = true; if (!format_found) { diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index f91802b352..31eb193461 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -420,10 +420,9 @@ ImportDock::ImportDock() { preset->get_popup()->connect("index_pressed", this, "_preset_selected"); hb->add_child(preset); - import_opts = memnew(PropertyEditor); + import_opts = memnew(EditorInspector); add_child(import_opts); import_opts->set_v_size_flags(SIZE_EXPAND_FILL); - import_opts->hide_top_label(); hb = memnew(HBoxContainer); add_child(hb); diff --git a/editor/import_dock.h b/editor/import_dock.h index a7a7eda8d8..41c7298d9a 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -31,10 +31,12 @@ #ifndef IMPORTDOCK_H #define IMPORTDOCK_H -#include "editor_file_system.h" -#include "io/resource_import.h" -#include "property_editor.h" +#include "core/io/config_file.h" +#include "core/io/resource_import.h" +#include "editor/editor_file_system.h" +#include "editor/editor_inspector.h" #include "scene/gui/box_container.h" +#include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/popup_menu.h" @@ -45,7 +47,7 @@ class ImportDock : public VBoxContainer { Label *imported; OptionButton *import_as; MenuButton *preset; - PropertyEditor *import_opts; + EditorInspector *import_opts; List<PropertyInfo> properties; Map<StringName, Variant> property_values; diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 615e9df043..0335053162 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -36,6 +36,16 @@ void InspectorDock::_menu_option(int p_option) { switch (p_option) { + case RESOURCE_MAKE_BUILT_IN: { + _unref_resource(); + } break; + case RESOURCE_COPY: { + _copy_resource(); + } break; + case RESOURCE_EDIT_CLIPBOARD: { + _paste_resource(); + } break; + case RESOURCE_SAVE: { _save_resource(false); } break; @@ -400,10 +410,11 @@ void InspectorDock::update(Object *p_object) { p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTR("Copy Params")), OBJECT_COPY_PARAMS); p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTR("Paste Params")), OBJECT_PASTE_PARAMS); p->add_separator(); - p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Paste Resource")), RESOURCE_PASTE); + + p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Edit Resource Clipboard")), RESOURCE_EDIT_CLIPBOARD); if (is_resource) { p->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY); - p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_UNREF); + p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_MAKE_BUILT_IN); } if (is_resource || is_node) { diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index f347056158..97ef6899dc 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -51,13 +51,12 @@ class InspectorDock : public VBoxContainer { GDCLASS(InspectorDock, VBoxContainer); enum MenuOptions { - RESOURCE_NEW, RESOURCE_LOAD, RESOURCE_SAVE, RESOURCE_SAVE_AS, - RESOURCE_UNREF, + RESOURCE_MAKE_BUILT_IN, RESOURCE_COPY, - RESOURCE_PASTE, + RESOURCE_EDIT_CLIPBOARD, OBJECT_COPY_PARAMS, OBJECT_PASTE_PARAMS, OBJECT_UNIQUE_RESOURCES, diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 2e128db883..5373015654 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -3,40 +3,9 @@ #include "os/keyboard.h" #include "scene/animation/animation_blend_tree.h" -void AnimationNodeBlendSpace1DEditorPlugin::edit(Object *p_object) { - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace1D>(p_object)); -} - -bool AnimationNodeBlendSpace1DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("AnimationNodeBlendSpace1D"); -} - -void AnimationNodeBlendSpace1DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - if (anim_tree_editor->is_visible_in_tree()) { - editor->hide_bottom_panel(); - } - - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeBlendSpace1DEditorPlugin::AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node) { - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendSpace1DEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 150 * EDSCALE)); - - button = editor->add_bottom_panel_item(TTR("BlendSpace1D"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendSpace1DEditorPlugin::~AnimationNodeBlendSpace1DEditorPlugin() { +StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { + StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + "blend_position"; + return path; } void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) { @@ -62,7 +31,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = blend_space->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp->has_node(gp->get_animation_player())) { @@ -85,10 +54,18 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven continue; int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name), idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -158,7 +135,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_pos(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(), blend_pos); blend_space_draw->update(); } @@ -181,7 +158,8 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_pos(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(), blend_pos); + blend_space_draw->update(); } } @@ -277,7 +255,8 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_draw() { color.a *= 0.5; } - float point = blend_space->get_blend_pos(); + float point = AnimationTreeEditor::get_singleton()->get_tree()->get(get_blend_position_path()); + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); point *= s.width; @@ -299,12 +278,6 @@ void AnimationNodeBlendSpace1DEditor::_update_space() { updating = true; - if (blend_space->get_parent().is_valid()) { - goto_parent_hb->show(); - } else { - goto_parent_hb->hide(); - } - max_value->set_value(blend_space->get_max_space()); min_value->set_value(blend_space->get_min_space()); @@ -355,15 +328,47 @@ void AnimationNodeBlendSpace1DEditor::_snap_toggled() { blend_space_draw->update(); } +void AnimationNodeBlendSpace1DEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + Ref<AnimationRootNode> node; + if (p_index == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + node = Ref<AnimationNode>(an); + } - Ref<AnimationNode> node(an); + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } updating = true; undo_redo->create_action("Add Node Point"); @@ -438,7 +443,7 @@ void AnimationNodeBlendSpace1DEditor::_update_tool_erase() { if (point_valid) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(an)) { open_editor->show(); } else { open_editor->hide(); @@ -490,18 +495,10 @@ void AnimationNodeBlendSpace1DEditor::_open_editor() { if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); ERR_FAIL_COND(an.is_null()); - EditorNode::get_singleton()->edit_item(an.ptr()); + AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); } } -void AnimationNodeBlendSpace1DEditor::_goto_parent() { - EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); -} - -void AnimationNodeBlendSpace1DEditor::_removed_from_graph() { - EditorNode::get_singleton()->edit_item(NULL); -} - void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); @@ -513,18 +510,15 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { tool_erase->set_icon(get_icon("Remove", "EditorIcons")); snap->set_icon(get_icon("SnapGrid", "EditorIcons")); open_editor->set_icon(get_icon("Edit", "EditorIcons")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); } if (p_what == NOTIFICATION_PROCESS) { String error; - if (!blend_space->get_tree()) { - error = TTR("BlendSpace1D does not belong to an AnimationTree node."); - } else if (!blend_space->get_tree()->is_active()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_tree()->is_state_invalid()) { - error = blend_space->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { @@ -536,6 +530,10 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { } } } + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); + } } void AnimationNodeBlendSpace1DEditor::_bind_methods() { @@ -556,28 +554,21 @@ void AnimationNodeBlendSpace1DEditor::_bind_methods() { ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace1DEditor::_update_edited_point_pos); ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace1DEditor::_open_editor); - ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace1DEditor::_goto_parent); - ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace1DEditor::_removed_from_graph); + ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace1DEditor::_file_opened); } -void AnimationNodeBlendSpace1DEditor::edit(AnimationNodeBlendSpace1D *p_blend_space) { +bool AnimationNodeBlendSpace1DEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeBlendSpace1D> b1d = p_node; + return b1d.is_valid(); +} - if (p_blend_space) { - blend_space = Ref<AnimationNodeBlendSpace1D>(p_blend_space); - } else { - blend_space.unref(); - } +void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + blend_space = p_node; + if (!blend_space.is_null()) { _update_space(); } } @@ -594,15 +585,6 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { Ref<ButtonGroup> bg; bg.instance(); - goto_parent_hb = memnew(HBoxContainer); - top_hb->add_child(goto_parent_hb); - - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hb->add_child(goto_parent); - goto_parent_hb->add_child(memnew(VSeparator)); - goto_parent_hb->hide(); - tool_blend = memnew(ToolButton); tool_blend->set_toggle_mode(true); tool_blend->set_button_group(bg); @@ -726,13 +708,20 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); animations_menu->set_name("animations"); animations_menu->connect("index_pressed", this, "_add_animation_type"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + selected_point = -1; dragging_selected = false; dragging_selected_attempt = false; diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index 52139626e6..278357b9c7 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_blend_space_1d.h" #include "scene/gui/button.h" @@ -10,9 +11,9 @@ #include "scene/gui/popup.h" #include "scene/gui/tree.h" -class AnimationNodeBlendSpace1DEditor : public VBoxContainer { +class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendSpace1DEditor, VBoxContainer) + GDCLASS(AnimationNodeBlendSpace1DEditor, AnimationTreeNodeEditorPlugin) Ref<AnimationNodeBlendSpace1D> blend_space; @@ -81,7 +82,17 @@ class AnimationNodeBlendSpace1DEditor : public VBoxContainer { void _goto_parent(); - void _removed_from_graph(); + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + + StringName get_blend_position_path() const; protected: void _notification(int p_what); @@ -89,29 +100,9 @@ protected: public: static AnimationNodeBlendSpace1DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace1D *p_blend_space); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeBlendSpace1DEditor(); }; -class AnimationNodeBlendSpace1DEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeBlendSpace1DEditorPlugin, EditorPlugin) - - AnimationNodeBlendSpace1DEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendSpace1D"; } - - bool has_main_screen() const { return false; } - - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendSpace1DEditorPlugin(); -}; - #endif // ANIMATION_BLEND_SPACE_1D_EDITOR_H diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 27df60f87a..e5476aaf08 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -11,27 +11,26 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeBlendSpace2DEditor::edit(AnimationNodeBlendSpace2D *p_blend_space) { +bool AnimationNodeBlendSpace2DEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeBlendSpace2D> bs2d = p_node; + return bs2d.is_valid(); +} - if (p_blend_space) { - blend_space = Ref<AnimationNodeBlendSpace2D>(p_blend_space); - } else { - blend_space.unref(); - } +void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + blend_space = p_node; + if (!blend_space.is_null()) { _update_space(); } } +StringName AnimationNodeBlendSpace2DEditor::get_blend_position_path() const { + StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + "blend_position"; + return path; +} + void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; @@ -54,7 +53,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = blend_space->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp && gp->has_node(gp->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); @@ -74,10 +73,18 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven if (name == "Animation") continue; // nope int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name), idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); add_point_pos = (mb->get_position() / blend_space_draw->get_size()); @@ -203,7 +210,8 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_position(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(), blend_pos); + blend_space_draw->update(); } @@ -237,21 +245,54 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_position(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(), blend_pos); + blend_space_draw->update(); } } +void AnimationNodeBlendSpace2DEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + Ref<AnimationRootNode> node; + if (p_index == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + node = Ref<AnimationNode>(an); + } - Ref<AnimationNode> node(an); + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } updating = true; undo_redo->create_action("Add Node Point"); @@ -288,7 +329,7 @@ void AnimationNodeBlendSpace2DEditor::_update_tool_erase() { tool_erase->set_disabled(!(selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) && !(selected_triangle >= 0 && selected_triangle < blend_space->get_triangle_count())); if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(an)) { open_editor->show(); } else { open_editor->hide(); @@ -490,13 +531,15 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { color.a *= 0.5; } - Vector2 point = blend_space->get_blend_position(); + Vector2 blend_pos = AnimationTreeEditor::get_singleton()->get_tree()->get(get_blend_position_path()); + Vector2 point = blend_pos; + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); point *= s; point.y = s.height - point.y; if (blend_space->get_triangle_count()) { - Vector2 closest = blend_space->get_closest_point(blend_space->get_blend_position()); + Vector2 closest = blend_space->get_closest_point(blend_pos); closest = (closest - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); closest *= s; closest.y = s.height - closest.y; @@ -527,12 +570,6 @@ void AnimationNodeBlendSpace2DEditor::_update_space() { updating = true; - if (blend_space->get_parent().is_valid()) { - goto_parent_hb->show(); - } else { - goto_parent_hb->hide(); - } - if (blend_space->get_auto_triangles()) { tool_triangle->hide(); } else { @@ -685,7 +722,6 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { tool_erase->set_icon(get_icon("Remove", "EditorIcons")); snap->set_icon(get_icon("SnapGrid", "EditorIcons")); open_editor->set_icon(get_icon("Edit", "EditorIcons")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); auto_triangles->set_icon(get_icon("AutoTriangle", "EditorIcons")); } @@ -693,12 +729,12 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { String error; - if (!blend_space->get_tree()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()) { error = TTR("BlendSpace2D does not belong to an AnimationTree node."); - } else if (!blend_space->get_tree()->is_active()) { + } else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_tree()->is_state_invalid()) { - error = blend_space->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } else if (blend_space->get_triangle_count() == 0) { error = TTR("No triangles exist, so no blending can take place."); } @@ -712,22 +748,21 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { } } } + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); + } } void AnimationNodeBlendSpace2DEditor::_open_editor() { if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); + ERR_FAIL_COND(an.is_null()); + AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); } } -void AnimationNodeBlendSpace2DEditor::_goto_parent() { - - EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); -} - void AnimationNodeBlendSpace2DEditor::_removed_from_graph() { EditorNode::get_singleton()->edit_item(NULL); } @@ -761,11 +796,12 @@ void AnimationNodeBlendSpace2DEditor::_bind_methods() { ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos); ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace2DEditor::_open_editor); - ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace2DEditor::_goto_parent); ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace2DEditor::_removed_from_graph); ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled); + + ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace2DEditor::_file_opened); } AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = NULL; @@ -781,14 +817,6 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { Ref<ButtonGroup> bg; bg.instance(); - goto_parent_hb = memnew(HBoxContainer); - top_hb->add_child(goto_parent_hb); - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hb->add_child(goto_parent); - goto_parent_hb->add_child(memnew(VSeparator)); - goto_parent_hb->hide(); - tool_blend = memnew(ToolButton); tool_blend->set_toggle_mode(true); tool_blend->set_button_group(bg); @@ -968,56 +996,23 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); animations_menu->set_name("animations"); animations_menu->connect("index_pressed", this, "_add_animation_type"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + selected_point = -1; selected_triangle = -1; dragging_selected = false; dragging_selected_attempt = false; } - -void AnimationNodeBlendSpace2DEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace2D>(p_object)); -} - -bool AnimationNodeBlendSpace2DEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeBlendSpace2D"); -} - -void AnimationNodeBlendSpace2DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - //editor->hide_animation_player_editors(); - //editor->animation_panel_make_visible(true); - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - - if (anim_tree_editor->is_visible_in_tree()) - editor->hide_bottom_panel(); - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeBlendSpace2DEditorPlugin::AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendSpace2DEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("BlendSpace2D"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendSpace2DEditorPlugin::~AnimationNodeBlendSpace2DEditorPlugin() { -} diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h index a0e497804e..0bf1e25d7a 100644 --- a/editor/plugins/animation_blend_space_2d_editor.h +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_blend_space_2d.h" #include "scene/gui/button.h" @@ -13,15 +14,12 @@ @author Juan Linietsky <reduzio@gmail.com> */ -class AnimationNodeBlendSpace2DEditor : public VBoxContainer { +class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendSpace2DEditor, VBoxContainer); + GDCLASS(AnimationNodeBlendSpace2DEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendSpace2D> blend_space; - HBoxContainer *goto_parent_hb; - ToolButton *goto_parent; - PanelContainer *panel; ToolButton *tool_blend; ToolButton *tool_select; @@ -93,38 +91,31 @@ class AnimationNodeBlendSpace2DEditor : public VBoxContainer { void _edit_point_pos(double); void _open_editor(); - void _goto_parent(); - void _removed_from_graph(); void _auto_triangles_toggled(); + StringName get_blend_position_path() const; + + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); public: static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace2D *p_blend_space); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeBlendSpace2DEditor(); }; -class AnimationNodeBlendSpace2DEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeBlendSpace2DEditorPlugin, EditorPlugin); - - AnimationNodeBlendSpace2DEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendSpace2D"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendSpace2DEditorPlugin(); -}; #endif // ANIMATION_BLEND_SPACE_2D_EDITOR_H diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index c00ad451fa..42e32b9788 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -2,6 +2,7 @@ #include "core/io/resource_loader.h" #include "core/project_settings.h" +#include "editor/editor_inspector.h" #include "os/input.h" #include "os/keyboard.h" #include "scene/animation/animation_player.h" @@ -9,27 +10,6 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeBlendTreeEditor::edit(AnimationNodeBlendTree *p_blend_tree) { - - if (blend_tree.is_valid()) { - blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph"); - } - - if (p_blend_tree) { - blend_tree = Ref<AnimationNodeBlendTree>(p_blend_tree); - } else { - blend_tree.unref(); - } - - if (blend_tree.is_null()) { - hide(); - } else { - blend_tree->connect("removed_from_graph", this, "_removed_from_graph"); - - _update_graph(); - } -} - void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script) { for (int i = 0; i < add_options.size(); i++) { @@ -58,10 +38,19 @@ void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_scrip void AnimationNodeBlendTreeEditor::_update_options_menu() { + print_line("update options"); add_node->get_popup()->clear(); for (int i = 0; i < add_options.size(); i++) { - add_node->get_popup()->add_item(add_options[i].name); + add_node->get_popup()->add_item(add_options[i].name, i); } + + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + add_node->get_popup()->add_separator(); + add_node->get_popup()->add_item(TTR("Paste"), MENU_PASTE); + } + add_node->get_popup()->add_separator(); + add_node->get_popup()->add_item(TTR("Load.."), MENU_LOAD_FILE); } Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { @@ -69,18 +58,28 @@ Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { return Size2(10, 200); } +void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_property, const Variant &p_value) { + + AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_tree(); + updating = true; + undo_redo->create_action("Parameter Changed: " + String(p_property), UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(tree, p_property, p_value); + undo_redo->add_undo_property(tree, p_property, tree->get(p_property)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + updating = false; +} + void AnimationNodeBlendTreeEditor::_update_graph() { if (updating) return; + visible_properties.clear(); + graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE); - if (blend_tree->get_parent().is_valid()) { - goto_parent->show(); - } else { - goto_parent->hide(); - } graph->clear_connections(); //erase all nodes for (int i = 0; i < graph->get_child_count(); i++) { @@ -107,7 +106,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED); } - node->set_offset(agnode->get_position() * EDSCALE); + node->set_offset(blend_tree->get_node_position(E->get()) * EDSCALE); node->set_title(agnode->get_caption()); node->set_name(E->get()); @@ -133,9 +132,28 @@ void AnimationNodeBlendTreeEditor::_update_graph() { node->set_slot(base + i, true, 0, get_color("font_color", "Label"), false, 0, Color()); } - node->connect("dragged", this, "_node_dragged", varray(agnode)); + List<PropertyInfo> pinfo; + agnode->get_parameter_list(&pinfo); + for (List<PropertyInfo>::Element *F = pinfo.front(); F; F = F->next()) { + + if (!(F->get().usage & PROPERTY_USAGE_EDITOR)) { + continue; + } + String base_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E->get()) + "/" + F->get().name; + EditorProperty *prop = EditorInspector::instantiate_property_editor(AnimationTreeEditor::get_singleton()->get_tree(), F->get().type, base_path, F->get().hint, F->get().hint_string, F->get().usage); + if (prop) { + prop->set_object_and_property(AnimationTreeEditor::get_singleton()->get_tree(), base_path); + prop->update_property(); + prop->set_name_split_ratio(0); + prop->connect("property_changed", this, "_property_changed"); + node->add_child(prop); + visible_properties.push_back(prop); + } + } + + node->connect("dragged", this, "_node_dragged", varray(E->get())); - if (EditorNode::get_singleton()->item_has_editor(agnode.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(agnode)) { node->add_child(memnew(HSeparator)); Button *open_in_editor = memnew(Button); open_in_editor->set_text(TTR("Open Editor")); @@ -169,7 +187,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { ProgressBar *pb = memnew(ProgressBar); - AnimationTree *player = anim->get_tree(); + AnimationTree *player = AnimationTreeEditor::get_singleton()->get_tree(); if (player->has_node(player->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(player->get_node(player->get_animation_player())); if (ap) { @@ -194,6 +212,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED); } + /* should be no longer necesary, as the boolean works Ref<AnimationNodeOneShot> oneshot = agnode; if (oneshot.is_valid()) { @@ -209,7 +228,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { play_stop->add_child(stop); play_stop->add_spacer(); node->add_child(play_stop); - } + } */ } List<AnimationNodeBlendTree::NodeConnection> connections; @@ -225,16 +244,44 @@ void AnimationNodeBlendTreeEditor::_update_graph() { } } -void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { +void AnimationNodeBlendTreeEditor::_file_opened(const String &p_file) { - ERR_FAIL_INDEX(p_idx, add_options.size()); + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_node(MENU_LOAD_FILE_CONFIRM); + } +} + +void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { Ref<AnimationNode> anode; - if (add_options[p_idx].type != String()) { + String base_name; + + if (p_idx == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_idx == MENU_LOAD_FILE_CONFIRM) { + anode = file_loaded; + file_loaded.unref(); + base_name = anode->get_class(); + } else if (p_idx == MENU_PASTE) { + + anode = EditorSettings::get_singleton()->get_resource_clipboard(); + ERR_FAIL_COND(!anode.is_valid()); + base_name = anode->get_class(); + } else if (add_options[p_idx].type != String()) { AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instance(add_options[p_idx].type)); ERR_FAIL_COND(!an); anode = Ref<AnimationNode>(an); + base_name = add_options[p_idx].name; } else { ERR_FAIL_COND(add_options[p_idx].script.is_null()); String base_type = add_options[p_idx].script->get_instance_base_type(); @@ -242,13 +289,16 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { ERR_FAIL_COND(!an); anode = Ref<AnimationNode>(an); anode->set_script(add_options[p_idx].script.get_ref_ptr()); + base_name = add_options[p_idx].name; } + Ref<AnimationNodeOutput> out = anode; + if (out.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("Output node can't be added to the blend tree.")); + return; + } Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5; - anode->set_position(instance_pos / EDSCALE); - - String base_name = add_options[p_idx].name; int base = 1; String name = base_name; while (blend_tree->has_node(name)) { @@ -257,19 +307,19 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { } undo_redo->create_action("Add Node to BlendTree"); - undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode); + undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE); undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); } -void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node) { +void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) { updating = true; undo_redo->create_action("Node Moved"); - undo_redo->add_do_method(p_node.ptr(), "set_position", p_to / EDSCALE); - undo_redo->add_undo_method(p_node.ptr(), "set_position", p_from / EDSCALE); + undo_redo->add_do_method(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE); + undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -342,20 +392,6 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { undo_redo->commit_action(); } -void AnimationNodeBlendTreeEditor::_oneshot_start(const StringName &p_name) { - - Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name); - ERR_FAIL_COND(!os.is_valid()); - os->start(); -} - -void AnimationNodeBlendTreeEditor::_oneshot_stop(const StringName &p_name) { - - Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name); - ERR_FAIL_COND(!os.is_valid()); - os->stop(); -} - void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) { GraphNode *gn = Object::cast_to<GraphNode>(p_node); @@ -373,13 +409,7 @@ void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) { Ref<AnimationNode> an = blend_tree->get_node(p_which); ERR_FAIL_COND(!an.is_valid()) - EditorNode::get_singleton()->edit_item(an.ptr()); -} - -void AnimationNodeBlendTreeEditor::_open_parent() { - if (blend_tree->get_parent().is_valid()) { - EditorNode::get_singleton()->edit_item(blend_tree->get_parent().ptr()); - } + AnimationTreeEditor::get_singleton()->enter_editor(p_which); } void AnimationNodeBlendTreeEditor::_filter_toggled() { @@ -417,14 +447,14 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano if (updating || _filter_edit != anode) return false; - NodePath player_path = anode->get_tree()->get_animation_player(); + NodePath player_path = AnimationTreeEditor::get_singleton()->get_tree()->get_animation_player(); - if (!anode->get_tree()->has_node(player_path)) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->has_node(player_path)) { EditorNode::get_singleton()->show_warning(TTR("No animation player set, so unable to retrieve track names.")); return false; } - AnimationPlayer *player = Object::cast_to<AnimationPlayer>(anode->get_tree()->get_node(player_path)); + AnimationPlayer *player = Object::cast_to<AnimationPlayer>(AnimationTreeEditor::get_singleton()->get_tree()->get_node(player_path)); if (!player) { EditorNode::get_singleton()->show_warning(TTR("Player path set is invalid, so unable to retrieve track names.")); return false; @@ -593,8 +623,6 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); - error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); } @@ -603,12 +631,10 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { String error; - if (!blend_tree->get_tree()) { - error = TTR("BlendTree does not belong to an AnimationTree node."); - } else if (!blend_tree->get_tree()->is_active()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_tree->get_tree()->is_state_invalid()) { - error = blend_tree->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { @@ -624,13 +650,13 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { blend_tree->get_node_connections(&conns); for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) { float activity = 0; - if (blend_tree->get_tree() && !blend_tree->get_tree()->is_state_invalid()) { + if (AnimationTreeEditor::get_singleton()->get_tree() && !AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { activity = blend_tree->get_connection_activity(E->get().input_node, E->get().input_index); } graph->set_connection_activity(E->get().output_node, 0, E->get().input_node, E->get().input_index, activity); } - AnimationTree *graph_player = blend_tree->get_tree(); + AnimationTree *graph_player = AnimationTreeEditor::get_singleton()->get_tree(); AnimationPlayer *player = NULL; if (graph_player->has_node(graph_player->get_animation_player())) { player = Object::cast_to<AnimationPlayer>(graph_player->get_node(graph_player->get_animation_player())); @@ -650,6 +676,14 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { } } } + + for (int i = 0; i < visible_properties.size(); i++) { + visible_properties[i]->update_property(); + } + } + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); } } @@ -664,9 +698,9 @@ void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) { void AnimationNodeBlendTreeEditor::_node_changed(ObjectID p_node) { AnimationNode *an = Object::cast_to<AnimationNode>(ObjectDB::get_instance(p_node)); - if (an && an->get_parent() == blend_tree) { - _update_graph(); - } + //if (an && an->get_parent() == blend_tree) { + _update_graph(); + //} } void AnimationNodeBlendTreeEditor::_bind_methods() { @@ -680,17 +714,17 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_disconnection_request", &AnimationNodeBlendTreeEditor::_disconnection_request); ClassDB::bind_method("_node_selected", &AnimationNodeBlendTreeEditor::_node_selected); ClassDB::bind_method("_open_in_editor", &AnimationNodeBlendTreeEditor::_open_in_editor); - ClassDB::bind_method("_open_parent", &AnimationNodeBlendTreeEditor::_open_parent); ClassDB::bind_method("_scroll_changed", &AnimationNodeBlendTreeEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &AnimationNodeBlendTreeEditor::_delete_request); ClassDB::bind_method("_edit_filters", &AnimationNodeBlendTreeEditor::_edit_filters); ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited); ClassDB::bind_method("_filter_toggled", &AnimationNodeBlendTreeEditor::_filter_toggled); - ClassDB::bind_method("_oneshot_start", &AnimationNodeBlendTreeEditor::_oneshot_start); - ClassDB::bind_method("_oneshot_stop", &AnimationNodeBlendTreeEditor::_oneshot_stop); ClassDB::bind_method("_node_changed", &AnimationNodeBlendTreeEditor::_node_changed); ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendTreeEditor::_removed_from_graph); + ClassDB::bind_method("_property_changed", &AnimationNodeBlendTreeEditor::_property_changed); + ClassDB::bind_method("_file_opened", &AnimationNodeBlendTreeEditor::_file_opened); + ClassDB::bind_method("_update_options_menu", &AnimationNodeBlendTreeEditor::_update_options_menu); ClassDB::bind_method("_anim_selected", &AnimationNodeBlendTreeEditor::_anim_selected); } @@ -708,7 +742,9 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1) - ERR_FAIL_COND(new_name == prev_name); + if (new_name == prev_name) { + return; //nothing to do + } String base_name = new_name; int base = 1; @@ -718,22 +754,61 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima name = base_name + " " + itos(base); } + String base_path = AnimationTreeEditor::get_singleton()->get_base_path(); + updating = true; undo_redo->create_action("Node Renamed"); undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name); undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name); + undo_redo->add_do_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + prev_name, base_path + name); + undo_redo->add_undo_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + name, base_path + prev_name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); updating = false; gn->set_name(new_name); gn->set_size(gn->get_minimum_size()); + + //change editors accordingly + for (int i = 0; i < visible_properties.size(); i++) { + String pname = visible_properties[i]->get_edited_property().operator String(); + if (pname.begins_with(base_path + prev_name)) { + String new_name = pname.replace_first(base_path + prev_name, base_path + name); + visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name); + } + } } void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) { _node_renamed(le->call("get_text"), p_node); } +bool AnimationNodeBlendTreeEditor::can_edit(const Ref<AnimationNode> &p_node) { + Ref<AnimationNodeBlendTree> bt = p_node; + return bt.is_valid(); +} + +void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { + + if (blend_tree.is_valid()) { + blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph"); + } + + if (p_node.is_valid()) { + blend_tree = p_node; + } else { + blend_tree.unref(); + } + + if (blend_tree.is_null()) { + hide(); + } else { + blend_tree->connect("removed_from_graph", this, "_removed_from_graph"); + + _update_graph(); + } +} + AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { singleton = this; @@ -757,13 +832,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { graph->get_zoom_hbox()->add_child(add_node); add_node->set_text(TTR("Add Node..")); graph->get_zoom_hbox()->move_child(add_node, 0); - add_node->get_popup()->connect("index_pressed", this, "_add_node"); - - goto_parent = memnew(Button); - graph->get_zoom_hbox()->add_child(goto_parent); - graph->get_zoom_hbox()->move_child(goto_parent, 0); - goto_parent->hide(); - goto_parent->connect("pressed", this, "_open_parent"); + add_node->get_popup()->connect("id_pressed", this, "_add_node"); + add_node->connect("about_to_show", this, "_update_options_menu"); add_options.push_back(AddOption("Animation", "AnimationNodeAnimation")); add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot")); @@ -804,45 +874,10 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { filters->set_hide_root(true); filters->connect("item_edited", this, "_filter_edited"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); undo_redo = EditorNode::get_singleton()->get_undo_redo(); } - -void AnimationNodeBlendTreeEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendTree>(p_object)); -} - -bool AnimationNodeBlendTreeEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeBlendTree"); -} - -void AnimationNodeBlendTreeEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - //editor->hide_animation_player_editors(); - //editor->animation_panel_make_visible(true); - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - - if (anim_tree_editor->is_visible_in_tree()) - editor->hide_bottom_panel(); - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeBlendTreeEditorPlugin::AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendTreeEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("BlendTree"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendTreeEditorPlugin::~AnimationNodeBlendTreeEditorPlugin() { -} diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index deba3b2b0e..35ecc32979 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_blend_tree.h" #include "scene/gui/button.h" @@ -13,14 +14,13 @@ @author Juan Linietsky <reduzio@gmail.com> */ -class AnimationNodeBlendTreeEditor : public VBoxContainer { +class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendTreeEditor, VBoxContainer); + GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendTree> blend_tree; GraphEdit *graph; MenuButton *add_node; - Button *goto_parent; PanelContainer *error_panel; Label *error_label; @@ -32,6 +32,7 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { CheckBox *filter_enabled; Map<StringName, ProgressBar *> animations; + Vector<EditorProperty *> visible_properties; void _update_graph(); @@ -52,7 +53,7 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { static AnimationNodeBlendTreeEditor *singleton; - void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node); + void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which); void _node_renamed(const String &p_text, Ref<AnimationNode> p_node); void _node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node); @@ -64,11 +65,8 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { void _scroll_changed(const Vector2 &p_scroll); void _node_selected(Object *p_node); void _open_in_editor(const String &p_which); - void _open_parent(); void _anim_selected(int p_index, Array p_options, const String &p_node); void _delete_request(const String &p_which); - void _oneshot_start(const StringName &p_name); - void _oneshot_stop(const StringName &p_name); bool _update_filters(const Ref<AnimationNode> &anode); void _edit_filters(const String &p_which); @@ -78,8 +76,19 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { void _node_changed(ObjectID p_node); + void _property_changed(const StringName &p_property, const Variant &p_value); void _removed_from_graph(); + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); @@ -91,27 +100,11 @@ public: void remove_custom_type(const Ref<Script> &p_script); virtual Size2 get_minimum_size() const; - void edit(AnimationNodeBlendTree *p_blend_tree); - AnimationNodeBlendTreeEditor(); -}; -class AnimationNodeBlendTreeEditorPlugin : public EditorPlugin { + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); - GDCLASS(AnimationNodeBlendTreeEditorPlugin, EditorPlugin); - - AnimationNodeBlendTreeEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendTree"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendTreeEditorPlugin(); + AnimationNodeBlendTreeEditor(); }; #endif // ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index ee450333c8..3a65cb9b38 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -11,22 +11,17 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_machine) { +bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (state_machine.is_valid()) { - state_machine->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeStateMachine> ansm = p_node; + return ansm.is_valid(); +} - if (p_state_machine) { - state_machine = Ref<AnimationNodeStateMachine>(p_state_machine); - } else { - state_machine.unref(); - } +void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) { - if (state_machine.is_null()) { - hide(); - } else { - state_machine->connect("removed_from_graph", this, "_removed_from_graph"); + state_machine = p_node; + + if (state_machine.is_valid()) { selected_transition_from = StringName(); selected_transition_to = StringName(); @@ -38,6 +33,10 @@ void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_ma void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) { + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (playback.is_null()) + return; + Ref<InputEventKey> k = p_event; if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) { if (selected_node != StringName() || selected_transition_to != StringName() || selected_transition_from != StringName()) { @@ -59,7 +58,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = state_machine->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp && gp->has_node(gp->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); @@ -79,9 +78,17 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv if (name == "Animation") continue; // nope int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name), idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); menu->set_global_position(state_machine_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -98,18 +105,12 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order if (node_rects[i].play.has_point(mb->get_position())) { //edit name - if (play_mode->get_selected() == 1 || !state_machine->is_playing()) { + if (play_mode->get_selected() == 1 || !playback->is_playing()) { //start - state_machine->start(node_rects[i].node_name); + playback->start(node_rects[i].node_name); } else { //travel - if (!state_machine->travel(node_rects[i].node_name)) { - - state_machine->start(node_rects[i].node_name); - //removing this due to usability.. - //error_time = 5; - //error_text = vformat(TTR("No path found from '%s' to '%s'."), state_machine->get_current_node(), node_rects[i].node_name); - } + playback->travel(node_rects[i].node_name); } state_machine_draw->update(); return; @@ -196,8 +197,8 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<AnimationNode> an = state_machine->get_node(selected_node); updating = true; undo_redo->create_action("Move Node"); - undo_redo->add_do_method(an.ptr(), "set_position", an->get_position() + drag_ofs / EDSCALE); - undo_redo->add_undo_method(an.ptr(), "set_position", an->get_position()); + undo_redo->add_do_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE); + undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node)); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -293,7 +294,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv snap_y = StringName(); { //snap - Vector2 cpos = state_machine->get_node(selected_node)->get_position() + drag_ofs / EDSCALE; + Vector2 cpos = state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE; List<StringName> nodes; state_machine->get_node_list(&nodes); @@ -303,7 +304,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { if (E->get() == selected_node) continue; - Vector2 npos = state_machine->get_node(E->get())->get_position(); + Vector2 npos = state_machine->get_node_position(E->get()); float d_x = ABS(npos.x - cpos.x); if (d_x < MIN(5, best_d_x)) { @@ -372,19 +373,58 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } } +void AnimationNodeStateMachineEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + String base_name; + Ref<AnimationRootNode> node; - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + if (p_index == MENU_LOAD_FILE) { - Ref<AnimationNode> node(an); - node->set_position(add_node_pos); + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); + + node = Ref<AnimationNode>(an); + base_name = type.replace_first("AnimationNode", ""); + } + + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } + + if (base_name == String()) { + + base_name = node->get_class().replace_first("AnimationNode", ""); + } - String base_name = type.replace_first("AnimationNode", ""); int base = 1; String name = base_name; while (state_machine->has_node(name)) { @@ -394,7 +434,7 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { updating = true; undo_redo->create_action("Add Node"); - undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node); + undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -419,11 +459,9 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) { name = base_name + " " + itos(base); } - anim->set_position(add_node_pos); - updating = true; undo_redo->create_action("Add Node"); - undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim); + undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -502,6 +540,8 @@ void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(Vector2 &r_from, Ve void AnimationNodeStateMachineEditor::_state_machine_draw() { + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + Ref<StyleBox> style = get_stylebox("frame", "GraphNode"); Ref<StyleBox> style_selected = get_stylebox("selectedframe", "GraphNode"); @@ -515,10 +555,17 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { linecolor.a *= 0.3; Ref<StyleBox> playing_overlay = get_stylebox("position", "GraphNode"); - bool playing = state_machine->is_playing(); - StringName current = state_machine->get_current_node(); - StringName blend_from = state_machine->get_blend_from_node(); - Vector<StringName> travel_path = state_machine->get_travel_path(); + bool playing = false; + StringName current; + StringName blend_from; + Vector<StringName> travel_path; + + if (playback.is_valid()) { + playing = playback->is_playing(); + current = playback->get_current_node(); + blend_from = playback->get_blend_from_node(); + travel_path = playback->get_travel_path(); + } if (state_machine_draw->has_focus()) { state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), accent, false); @@ -534,13 +581,13 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //snap lines if (dragging_selected) { - Vector2 from = (state_machine->get_node(selected_node)->get_position() * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE; + Vector2 from = (state_machine->get_node_position(selected_node) * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE; if (snap_x != StringName()) { - Vector2 to = (state_machine->get_node(snap_x)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 to = (state_machine->get_node_position(snap_x) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; state_machine_draw->draw_line(from, to, linecolor, 2); } if (snap_y != StringName()) { - Vector2 to = (state_machine->get_node(snap_y)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 to = (state_machine->get_node_position(snap_y) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; state_machine_draw->draw_line(from, to, linecolor, 2); } } @@ -563,7 +610,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } Vector2 offset; - offset += anode->get_position() * EDSCALE; + offset += state_machine->get_node_position(E->get()) * EDSCALE; if (selected_node == E->get() && dragging_selected) { offset += drag_ofs; } @@ -588,10 +635,10 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //draw conecting line for potential new transition if (connecting) { - Vector2 from = (state_machine->get_node(connecting_from)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 from = (state_machine->get_node_position(connecting_from) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; Vector2 to; if (connecting_to_node != StringName()) { - to = (state_machine->get_node(connecting_to_node)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + to = (state_machine->get_node_position(connecting_to_node) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; } else { to = connecting_to; } @@ -617,15 +664,17 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { TransitionLine tl; tl.from_node = state_machine->get_transition_from(i); Vector2 ofs_from = (dragging_selected && tl.from_node == selected_node) ? drag_ofs : Vector2(); - tl.from = (state_machine->get_node(tl.from_node)->get_position() * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; + tl.from = (state_machine->get_node_position(tl.from_node) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; tl.to_node = state_machine->get_transition_to(i); Vector2 ofs_to = (dragging_selected && tl.to_node == selected_node) ? drag_ofs : Vector2(); - tl.to = (state_machine->get_node(tl.to_node)->get_position() * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; + tl.to = (state_machine->get_node_position(tl.to_node) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); tl.disabled = tr->is_disabled(); tl.auto_advance = tr->has_auto_advance(); + tl.advance_condition_name = tr->get_advance_condition_name(); + tl.advance_condition_state = false; tl.mode = tr->get_switch_mode(); tl.width = tr_bidi_offset; @@ -665,7 +714,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } } - _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, tl.auto_advance); + + bool auto_advance = tl.auto_advance; + StringName fullpath = AnimationTreeEditor::get_singleton()->get_base_path() + String(tl.advance_condition_name); + if (tl.advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(fullpath))) { + tl.advance_condition_state = true; + auto_advance = true; + } + _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, auto_advance); transition_lines.push_back(tl); } @@ -675,7 +731,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { String name = node_rects[i].node_name; Ref<AnimationNode> anode = state_machine->get_node(name); - bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr()); + bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode); Ref<StyleBox> sb = name == selected_node ? style_selected : style; int strsize = font->get_string_size(name).width; @@ -757,12 +813,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { - if (!state_machine->is_playing()) + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + + if (!playback.is_valid() || !playback->is_playing()) return; int idx = -1; for (int i = 0; node_rects.size(); i++) { - if (node_rects[i].node_name == state_machine->get_current_node()) { + if (node_rects[i].node_name == playback->get_current_node()) { idx = i; break; } @@ -785,9 +843,9 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, state_machine->get_current_length()); + float len = MAX(0.0001, playback->get_current_length()); - float pos = CLAMP(state_machine->get_current_play_pos(), 0, len); + float pos = CLAMP(playback->get_current_play_pos(), 0, len); float c = pos / len; Color fg = get_color("font_color", "Label"); Color bg = fg; @@ -807,12 +865,6 @@ void AnimationNodeStateMachineEditor::_update_graph() { updating = true; - if (state_machine->get_parent().is_valid()) { - goto_parent_hbox->show(); - } else { - goto_parent_hbox->hide(); - } - state_machine_draw->update(); updating = false; @@ -824,7 +876,6 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); panel->add_style_override("panel", get_stylebox("bg", "Tree")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); tool_select->set_icon(get_icon("ToolSelect", "EditorIcons")); tool_create->set_icon(get_icon("ToolAddNode", "EditorIcons")); @@ -856,19 +907,21 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { String error; + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (error_time > 0) { error = error_text; error_time -= get_process_delta_time(); - } else if (!state_machine->get_tree()) { - error = TTR("StateMachine does not belong to an AnimationTree node."); - } else if (!state_machine->get_tree()->is_active()) { + } else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (state_machine->get_tree()->is_state_invalid()) { - error = state_machine->get_tree()->get_invalid_state_reason(); - } else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) { + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); + /*} else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) { if (state_machine->get_start_node() == StringName() || state_machine->get_end_node() == StringName()) { error = TTR("Start and end nodes are needed for a sub-transition."); - } + }*/ + } else if (playback.is_null()) { + error = vformat(TTR("No playback resource set at path: %s."), AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); } if (error != error_label->get_text()) { @@ -904,14 +957,38 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { break; } + if (transition_lines[i].advance_condition_name != state_machine->get_transition(tidx)->get_advance_condition_name()) { + state_machine_draw->update(); + break; + } + if (transition_lines[i].mode != state_machine->get_transition(tidx)->get_switch_mode()) { state_machine_draw->update(); break; } + + bool acstate = transition_lines[i].advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + String(transition_lines[i].advance_condition_name))); + + if (transition_lines[i].advance_condition_state != acstate) { + state_machine_draw->update(); + break; + } } bool same_travel_path = true; - Vector<StringName> tp = state_machine->get_travel_path(); + Vector<StringName> tp; + bool is_playing = false; + StringName current_node; + StringName blend_from_node; + float play_pos = 0; + + if (playback.is_valid()) { + tp = playback->get_travel_path(); + is_playing = playback->is_playing(); + current_node = playback->get_current_node(); + blend_from_node = playback->get_blend_from_node(); + play_pos = playback->get_current_play_pos(); + } { @@ -928,37 +1005,32 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { } //update if travel state changed - if (!same_travel_path || last_active != state_machine->is_playing() || last_current_node != state_machine->get_current_node() || last_blend_from_node != state_machine->get_blend_from_node()) { + if (!same_travel_path || last_active != is_playing || last_current_node != current_node || last_blend_from_node != blend_from_node) { state_machine_draw->update(); last_travel_path = tp; - last_current_node = state_machine->get_current_node(); - last_active = state_machine->is_playing(); - last_blend_from_node = state_machine->get_blend_from_node(); + last_current_node = current_node; + last_active = is_playing; + last_blend_from_node = blend_from_node; state_machine_play_pos->update(); } - if (last_play_pos != state_machine->get_current_play_pos()) { + if (last_play_pos != play_pos) { - last_play_pos = state_machine->get_current_play_pos(); + last_play_pos = play_pos; state_machine_play_pos->update(); } } if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { over_node = StringName(); + set_process(is_visible_in_tree()); } } void AnimationNodeStateMachineEditor::_open_editor(const String &p_name) { - Ref<AnimationNode> an = state_machine->get_node(p_name); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); -} - -void AnimationNodeStateMachineEditor::_goto_parent() { - EditorNode::get_singleton()->edit_item(state_machine->get_parent().ptr()); + AnimationTreeEditor::get_singleton()->enter_editor(p_name); } void AnimationNodeStateMachineEditor::_removed_from_graph() { @@ -1114,7 +1186,6 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_name_edited", &AnimationNodeStateMachineEditor::_name_edited); - ClassDB::bind_method("_goto_parent", &AnimationNodeStateMachineEditor::_goto_parent); ClassDB::bind_method("_removed_from_graph", &AnimationNodeStateMachineEditor::_removed_from_graph); ClassDB::bind_method("_open_editor", &AnimationNodeStateMachineEditor::_open_editor); @@ -1124,6 +1195,7 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_autoplay_selected", &AnimationNodeStateMachineEditor::_autoplay_selected); ClassDB::bind_method("_end_selected", &AnimationNodeStateMachineEditor::_end_selected); ClassDB::bind_method("_update_mode", &AnimationNodeStateMachineEditor::_update_mode); + ClassDB::bind_method("_file_opened", &AnimationNodeStateMachineEditor::_file_opened); } AnimationNodeStateMachineEditor *AnimationNodeStateMachineEditor::singleton = NULL; @@ -1136,13 +1208,6 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { HBoxContainer *top_hb = memnew(HBoxContainer); add_child(top_hb); - goto_parent_hbox = memnew(HBoxContainer); - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hbox->add_child(goto_parent); - goto_parent_hbox->add_child(memnew(VSeparator)); - top_hb->add_child(goto_parent_hbox); - Ref<ButtonGroup> bg; bg.instance(); @@ -1248,7 +1313,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); @@ -1261,6 +1326,13 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { name_edit->connect("text_entered", this, "_name_edited"); name_edit->set_as_toplevel(true); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + over_text = false; over_node_what = -1; @@ -1271,43 +1343,3 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { error_time = 0; } - -void AnimationNodeStateMachineEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeStateMachine>(p_object)); -} - -bool AnimationNodeStateMachineEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeStateMachine"); -} - -void AnimationNodeStateMachineEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - //editor->hide_animation_player_editors(); - //editor->animation_panel_make_visible(true); - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - - if (anim_tree_editor->is_visible_in_tree()) - editor->hide_bottom_panel(); - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeStateMachineEditorPlugin::AnimationNodeStateMachineEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeStateMachineEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("StateMachine"), anim_tree_editor); - button->hide(); -} - -AnimationNodeStateMachineEditorPlugin::~AnimationNodeStateMachineEditorPlugin() { -} diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index efd3de7415..49d08607cf 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_node_state_machine.h" #include "scene/gui/button.h" @@ -10,9 +11,9 @@ #include "scene/gui/popup.h" #include "scene/gui/tree.h" -class AnimationNodeStateMachineEditor : public VBoxContainer { +class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeStateMachineEditor, VBoxContainer); + GDCLASS(AnimationNodeStateMachineEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeStateMachine> state_machine; @@ -29,9 +30,6 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { OptionButton *transition_mode; OptionButton *play_mode; - HBoxContainer *goto_parent_hbox; - ToolButton *goto_parent; - PanelContainer *panel; StringName selected_node; @@ -79,8 +77,6 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { void _add_menu_type(int p_index); void _add_animation_type(int p_index); - void _goto_parent(); - void _removed_from_graph(); struct NodeRect { @@ -99,6 +95,8 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { Vector2 from; Vector2 to; AnimationNodeStateMachineTransition::SwitchMode mode; + StringName advance_condition_name; + bool advance_condition_state; bool disabled; bool auto_advance; float width; @@ -135,33 +133,25 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { float error_time; String error_text; + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); public: static AnimationNodeStateMachineEditor *get_singleton() { return singleton; } - void edit(AnimationNodeStateMachine *p_state_machine); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeStateMachineEditor(); }; -class AnimationNodeStateMachineEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeStateMachineEditorPlugin, EditorPlugin); - - AnimationNodeStateMachineEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "StateMachine"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeStateMachineEditorPlugin(EditorNode *p_node); - ~AnimationNodeStateMachineEditorPlugin(); -}; - #endif // ANIMATION_STATE_MACHINE_EDITOR_H diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 25582ae0b9..19921ef54f 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -1,1418 +1,247 @@ -/*************************************************************************/ -/* animation_tree_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - #include "animation_tree_editor_plugin.h" +#include "animation_blend_space_1d_editor.h" +#include "animation_blend_space_2d_editor.h" +#include "animation_blend_tree_editor_plugin.h" +#include "animation_state_machine_editor.h" #include "core/io/resource_loader.h" #include "core/project_settings.h" +#include "math/delaunay.h" #include "os/input.h" #include "os/keyboard.h" +#include "scene/animation/animation_blend_tree.h" +#include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/main/viewport.h" +#include "scene/scene_string_names.h" -void AnimationTreeEditor::edit(AnimationTreePlayer *p_anim_tree) { +void AnimationTreeEditor::edit(AnimationTree *p_tree) { - anim_tree = p_anim_tree; + if (tree == p_tree) + return; - if (!anim_tree) { - hide(); - } else { - order.clear(); - p_anim_tree->get_node_list(&order); - /* - for(List<StringName>::Element* E=order.front();E;E=E->next()) { + tree = p_tree; - if (E->get() >= (int)last_id) - last_id=E->get()+1; - }*/ - play_button->set_pressed(p_anim_tree->is_active()); - //read the orders + Vector<String> path; + if (tree->has_meta("_tree_edit_path")) { + path = tree->get_meta("_tree_edit_path"); + edit_path(path); + } else { + current_root = 0; } } -Size2 AnimationTreeEditor::_get_maximum_size() { - - Size2 max; - - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { +void AnimationTreeEditor::_path_button_pressed(int p_path) { - Point2 pos = anim_tree->node_get_position(E->get()); - - if (click_type == CLICK_NODE && click_node == E->get()) { + Ref<AnimationNode> node = tree->get_tree_root(); + if (node.is_null()) + return; - pos += click_motion - click_pos; + edited_path.clear(); + if (p_path >= 0) { + for (int i = 0; i <= p_path; i++) { + Ref<AnimationNode> child = node->get_child_by_name(button_path[i]); + ERR_BREAK(child.is_null()); + node = child; + edited_path.push_back(button_path[i]); } - pos += get_node_size(E->get()); - if (pos.x > max.x) - max.x = pos.x; - if (pos.y > max.y) - max.y = pos.y; } - return max; -} - -const char *AnimationTreeEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" }; - -Size2 AnimationTreeEditor::get_node_size(const StringName &p_node) const { - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - - Size2 size = style->get_minimum_size(); - - int count = 2; // title and name - int inputs = anim_tree->node_get_input_count(p_node); - count += inputs ? inputs : 1; - String name = p_node; - - float name_w = font->get_string_size(name).width; - float type_w = font->get_string_size(String(_node_type_names[type])).width; - float max_w = MAX(name_w, type_w); - - switch (type) { - case AnimationTreePlayer::NODE_TIMESEEK: - case AnimationTreePlayer::NODE_OUTPUT: { - } break; - case AnimationTreePlayer::NODE_ANIMATION: - case AnimationTreePlayer::NODE_ONESHOT: - case AnimationTreePlayer::NODE_MIX: - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_BLEND3: - case AnimationTreePlayer::NODE_BLEND4: - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TRANSITION: { - - size.height += font->get_height(); - } break; - case AnimationTreePlayer::NODE_MAX: { + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(node)) { + editors[i]->edit(node); + editors[i]->show(); + } else { + editors[i]->edit(Ref<AnimationNode>()); + editors[i]->hide(); } } - - size.x += max_w + 20; - size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu")); - - return size; } -void AnimationTreeEditor::_edit_dialog_changede(String) { - - edit_dialog->hide(); -} - -void AnimationTreeEditor::_edit_dialog_changeds(String s) { - - _edit_dialog_changed(); -} - -void AnimationTreeEditor::_edit_dialog_changedf(float) { - - _edit_dialog_changed(); -} - -void AnimationTreeEditor::_edit_dialog_changed() { - - if (updating_edit) - return; - - if (renaming_edit) { - - if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) { - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { - - if (E->get() == edited_node) - E->get() = edit_line[0]->get_text(); - } - edited_node = edit_line[0]->get_text(); - } - update(); - return; +void AnimationTreeEditor::_update_path() { + while (path_hb->get_child_count()) { + memdelete(path_hb->get_child(0)); } - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); - - switch (type) { - - case AnimationTreePlayer::NODE_TIMESCALE: - anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double()); - break; - case AnimationTreePlayer::NODE_ONESHOT: - anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double()); - anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed()); - anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected()); - - break; - - case AnimationTreePlayer::NODE_MIX: - - anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value()); - break; - case AnimationTreePlayer::NODE_BLEND2: - anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value()); - - break; - - case AnimationTreePlayer::NODE_BLEND3: - anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value()); - - break; - case AnimationTreePlayer::NODE_BLEND4: - - anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value())); - - break; - - case AnimationTreePlayer::NODE_TRANSITION: { - anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double()); - if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) - anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); - } break; - default: {} - } -} - -void AnimationTreeEditor::_edit_dialog_animation_changed() { - - Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); - anim_tree->animation_node_set_animation(edited_node, anim); - update(); -} - -void AnimationTreeEditor::_edit_dialog_edit_animation() { - - if (Engine::get_singleton()->is_editor_hint()) { - get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); - }; -}; - -void AnimationTreeEditor::_edit_oneshot_start() { - - anim_tree->oneshot_node_start(edited_node); -} - -void AnimationTreeEditor::_play_toggled() { - - anim_tree->set_active(play_button->is_pressed()); -} - -void AnimationTreeEditor::_master_anim_menu_item(int p_item) { - - if (p_item == 0) - _edit_filters(); - else { - - String str = master_anim_popup->get_item_text(p_item); - anim_tree->animation_node_set_master_animation(edited_node, str); + Ref<ButtonGroup> group; + group.instance(); + + Button *b = memnew(Button); + b->set_text("root"); + b->set_toggle_mode(true); + b->set_button_group(group); + b->set_pressed(true); + b->set_focus_mode(FOCUS_NONE); + b->connect("pressed", this, "_path_button_pressed", varray(-1)); + path_hb->add_child(b); + for (int i = 0; i < button_path.size(); i++) { + b = memnew(Button); + b->set_text(button_path[i]); + b->set_toggle_mode(true); + b->set_button_group(group); + path_hb->add_child(b); + b->set_pressed(true); + b->set_focus_mode(FOCUS_NONE); + b->connect("pressed", this, "_path_button_pressed", varray(i)); } - update(); } -void AnimationTreeEditor::_popup_edit_dialog() { - - updating_edit = true; - - for (int i = 0; i < 2; i++) - edit_scroll[i]->hide(); - - for (int i = 0; i < 4; i++) { - - edit_line[i]->hide(); - edit_label[i]->hide(); - } - - edit_option->hide(); - edit_button->hide(); - filter_button->hide(); - edit_check->hide(); - - Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value()); - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Size2 size = get_node_size(edited_node); - Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM)); - popup_pos += get_global_position(); - - if (renaming_edit) { - - edit_label[0]->set_text(TTR("New name:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(edited_node); - edit_line[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - - } else { - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); - - switch (type) { - - case AnimationTreePlayer::NODE_ANIMATION: - - if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) { - - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player())); - master_anim_popup->clear(); - master_anim_popup->add_item(TTR("Edit Filters")); - master_anim_popup->add_separator(); - List<StringName> sn; - ap->get_animation_list(&sn); - sn.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = sn.front(); E; E = E->next()) { - master_anim_popup->add_item(E->get()); - } - - master_anim_popup->set_position(popup_pos); - master_anim_popup->popup(); - } else { - property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation"); - property_editor->set_position(popup_pos); - property_editor->popup(); - updating_edit = false; - } - return; - case AnimationTreePlayer::NODE_TIMESCALE: - edit_label[0]->set_text(TTR("Scale:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); - edit_line[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - break; - case AnimationTreePlayer::NODE_ONESHOT: - edit_label[0]->set_text(TTR("Fade In (s):")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); - edit_line[0]->show(); - edit_label[1]->set_text(TTR("Fade Out (s):")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_line[1]->set_begin(Point2(15, 75)); - edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); - edit_line[1]->show(); - - edit_option->clear(); - edit_option->add_item(TTR("Blend"), 0); - edit_option->add_item(TTR("Mix"), 1); - edit_option->set_begin(Point2(15, 105)); - - edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node)); - edit_option->show(); - - edit_check->set_text(TTR("Auto Restart:")); - edit_check->set_begin(Point2(15, 125)); - edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); - edit_check->show(); - - edit_label[2]->set_text(TTR("Restart (s):")); - edit_label[2]->set_position(Point2(5, 145)); - edit_label[2]->show(); - edit_line[2]->set_begin(Point2(15, 165)); - edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); - edit_line[2]->show(); - edit_label[3]->set_text(TTR("Random Restart (s):")); - edit_label[3]->set_position(Point2(5, 195)); - edit_label[3]->show(); - edit_line[3]->set_begin(Point2(15, 215)); - edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); - edit_line[3]->show(); - - filter_button->set_begin(Point2(10, 245)); - filter_button->show(); - - edit_button->set_begin(Point2(10, 268)); - edit_button->set_text(TTR("Start!")); - - edit_button->show(); +void AnimationTreeEditor::edit_path(const Vector<String> &p_path) { - edit_dialog->set_size(Size2(180, 293)); + button_path.clear(); - break; + Ref<AnimationNode> node = tree->get_tree_root(); - case AnimationTreePlayer::NODE_MIX: + if (node.is_valid()) { + current_root = node->get_instance_id(); - edit_label[0]->set_text(TTR("Amount:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_dialog->set_size(Size2(150, 50)); + for (int i = 0; i < p_path.size(); i++) { - break; - case AnimationTreePlayer::NODE_BLEND2: - edit_label[0]->set_text(TTR("Blend:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - filter_button->set_begin(Point2(10, 47)); - filter_button->show(); - edit_dialog->set_size(Size2(150, 74)); - - break; - - case AnimationTreePlayer::NODE_BLEND3: - edit_label[0]->set_text(TTR("Blend:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(-1); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - - break; - case AnimationTreePlayer::NODE_BLEND4: - - edit_label[0]->set_text(TTR("Blend 0:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_label[1]->set_text(TTR("Blend 1:")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_scroll[1]->set_min(0); - edit_scroll[1]->set_max(1); - edit_scroll[1]->set_step(0.01); - edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y); - edit_scroll[1]->set_begin(Point2(15, 75)); - edit_scroll[1]->show(); - edit_dialog->set_size(Size2(150, 100)); - - break; - - case AnimationTreePlayer::NODE_TRANSITION: { - - edit_label[0]->set_text(TTR("X-Fade Time (s):")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); - edit_line[0]->show(); - - edit_label[1]->set_text(TTR("Current:")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_option->set_begin(Point2(15, 75)); - - edit_option->clear(); - - for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) { - edit_option->add_item(itos(i), i); - } - - edit_option->select(anim_tree->transition_node_get_current(edited_node)); - edit_option->show(); - edit_dialog->set_size(Size2(150, 100)); - - } break; - default: {} + Ref<AnimationNode> child = node->get_child_by_name(p_path[i]); + ERR_BREAK(child.is_null()); + node = child; + button_path.push_back(p_path[i]); } - } - - edit_dialog->set_position(popup_pos); - edit_dialog->popup(); - - updating_edit = false; -} - -void AnimationTreeEditor::_draw_node(const StringName &p_node) { - - RID ci = get_canvas_item(); - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - Color font_color = get_color("font_color", "PopupMenu"); - Color font_color_title = get_color("font_color_hover", "PopupMenu"); - font_color_title.a *= 0.8; - Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); - - Size2 size = get_node_size(p_node); - Point2 pos = anim_tree->node_get_position(p_node); - if (click_type == CLICK_NODE && click_node == p_node) { - - pos += click_motion - click_pos; - if (pos.x < 5) - pos.x = 5; - if (pos.y < 5) - pos.y = 5; - } - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - style->draw(ci, Rect2(pos, size)); - - float w = size.width - style->get_minimum_size().width; - float h = font->get_height() + get_constant("vseparation", "PopupMenu"); - - Point2 ofs = style->get_offset() + pos; - Point2 ascofs(0, font->get_ascent()); - - Color bx = font_color_title; - bx.a *= 0.1; - draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx); - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title); - - ofs.y += h; - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color); - ofs.y += h; - - int count = 2; // title and name - int inputs = anim_tree->node_get_input_count(p_node); - count += inputs ? inputs : 1; - - float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1; - - if (type != AnimationTreePlayer::NODE_OUTPUT) - slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output - - if (inputs) { - for (int i = 0; i < inputs; i++) { - - slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs)); - String text; - switch (type) { - - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break; - case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break; - case AnimationTreePlayer::NODE_ANIMATION: break; - case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break; - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break; - case AnimationTreePlayer::NODE_BLEND3: - switch (i) { - case 0: text = "b-"; break; - case 1: text = "a"; break; - case 2: text = "b+"; break; - } - break; - - case AnimationTreePlayer::NODE_BLEND4: - switch (i) { - case 0: text = "a0"; break; - case 1: text = "b0"; break; - case 2: text = "a1"; break; - case 3: text = "b1"; break; - } - break; - - case AnimationTreePlayer::NODE_TRANSITION: - text = itos(i); - if (anim_tree->transition_node_has_input_auto_advance(p_node, i)) - text += "->"; - - break; - default: {} + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(node)) { + editors[i]->edit(node); + editors[i]->show(); + } else { + editors[i]->edit(Ref<AnimationNode>()); + editors[i]->hide(); } - font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); - - ofs.y += h; } } else { - ofs.y += h; + current_root = 0; } - Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar"); - Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar"); - Rect2 pg_rect(ofs, Size2(w, h)); - - bool editable = true; - switch (type) { - case AnimationTreePlayer::NODE_ANIMATION: { - - Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); - String text; - if (anim_tree->animation_node_get_master_animation(p_node) != "") - text = anim_tree->animation_node_get_master_animation(p_node); - else if (anim.is_null()) - text = "load..."; - else - text = anim->get_name(); - - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title); + edited_path = button_path; - } break; - case AnimationTreePlayer::NODE_ONESHOT: - case AnimationTreePlayer::NODE_MIX: - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_BLEND3: - case AnimationTreePlayer::NODE_BLEND4: - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TRANSITION: { - - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); - } break; - default: editable = false; - } - - if (editable) { - - Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons"); - Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2)); - arrow->draw(ci, ofs + arrow_ofs); - } + _update_path(); } -AnimationTreeEditor::ClickType AnimationTreeEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const { - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - - float h = (font->get_height() + get_constant("vseparation", "PopupMenu")); - - for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) { - - StringName node = E->get(); - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node); - - Point2 pos = anim_tree->node_get_position(node); - Size2 size = get_node_size(node); - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - - if (!Rect2(pos, size).has_point(p_click)) - continue; - - if (p_node_id) - *p_node_id = node; - - pos = p_click - pos; - - float y = pos.y - style->get_offset().height; - - if (y < 2 * h) - return CLICK_NODE; - y -= 2 * h; - - int inputs = anim_tree->node_get_input_count(node); - int count = MAX(inputs, 1); - - if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) { - - if (y < count * h) { - - if (p_slot_index) - *p_slot_index = 0; - return CLICK_OUTPUT_SLOT; - } - } - - for (int i = 0; i < count; i++) { - - if (y < h) { - if (p_slot_index) - *p_slot_index = i; - return CLICK_INPUT_SLOT; - } - y -= h; - } - - bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK; - return has_parameters ? CLICK_PARAMETER : CLICK_NODE; - } - - return CLICK_NONE; -} - -Point2 AnimationTreeEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) { - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); - - Size2 size = get_node_size(p_node_id); - Point2 pos = anim_tree->node_get_position(p_node_id); - - if (click_type == CLICK_NODE && click_node == p_node_id) { - - pos += click_motion - click_pos; - if (pos.x < 5) - pos.x = 5; - if (pos.y < 5) - pos.y = 5; - } - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - - float w = size.width - style->get_minimum_size().width; - float h = font->get_height() + get_constant("vseparation", "PopupMenu"); - - pos += style->get_offset(); - - pos.y += h * 2; - - pos.y += h * p_slot; - - pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor(); - - if (!p_input) { - pos.x += w + slot_icon->get_width(); - } - - return pos; +Vector<String> AnimationTreeEditor::get_edited_path() const { + return button_path; } -void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) { - - Ref<InputEventMouseButton> mb = p_event; - - if (mb.is_valid()) { - - if (mb->is_pressed()) { +void AnimationTreeEditor::enter_editor(const String &p_path) { - if (mb->get_button_index() == 1) { - click_pos = Point2(mb->get_position().x, mb->get_position().y); - click_motion = click_pos; - click_type = _locate_click(click_pos, &click_node, &click_slot); - if (click_type != CLICK_NONE) { - - order.erase(click_node); - order.push_back(click_node); - update(); - } - - switch (click_type) { - case CLICK_INPUT_SLOT: { - click_pos = _get_slot_pos(click_node, true, click_slot); - } break; - case CLICK_OUTPUT_SLOT: { - click_pos = _get_slot_pos(click_node, false, click_slot); - } break; - case CLICK_PARAMETER: { - - edited_node = click_node; - renaming_edit = false; - _popup_edit_dialog(); - //open editor - //_node_edit_property(click_node); - } break; - default: {} - } - } - if (mb->get_button_index() == 2) { - - if (click_type != CLICK_NONE) { - click_type = CLICK_NONE; - update(); - } else { - // try to disconnect/remove - - Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y); - rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot); - if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { - - node_popup->clear(); - node_popup->set_size(Size2(1, 1)); - node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); - if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { - node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); - if (rclick_type == CLICK_INPUT_SLOT) { - if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot)) - node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE); - else - node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE); - node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT); - } - } - - node_popup->set_position(rclick_pos + get_global_position()); - node_popup->popup(); - } - - if (rclick_type == CLICK_NODE) { - node_popup->clear(); - node_popup->set_size(Size2(1, 1)); - node_popup->add_item(TTR("Rename"), NODE_RENAME); - node_popup->add_item(TTR("Remove"), NODE_ERASE); - if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) - node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); - node_popup->set_position(rclick_pos + get_global_position()); - node_popup->popup(); - } - } - } - } else { - - if (mb->get_button_index() == 1 && click_type != CLICK_NONE) { - - switch (click_type) { - case CLICK_INPUT_SLOT: - case CLICK_OUTPUT_SLOT: { - - Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y); - StringName id; - int slot; - ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot); - - if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) { - - anim_tree->connect_nodes(click_node, id, slot); - } - if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) { - - anim_tree->connect_nodes(id, click_node, click_slot); - } - - } break; - case CLICK_NODE: { - Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos); - if (new_pos.x < 5) - new_pos.x = 5; - if (new_pos.y < 5) - new_pos.y = 5; - anim_tree->node_set_position(click_node, new_pos); - - } break; - default: {} - } - - click_type = CLICK_NONE; - update(); - } - } - } - - Ref<InputEventMouseMotion> mm = p_event; - - if (mm.is_valid()) { - - if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) { - - click_motion = Point2(mm->get_position().x, mm->get_position().y); - update(); - } - if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { - - h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); - v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); - update(); - } - } + Vector<String> path = edited_path; + path.push_back(p_path); + edit_path(path); } -void AnimationTreeEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) { - - static const int steps = 20; - - Rect2 r; - r.position = p_from; - r.expand_to(p_to); - Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1); - bool flip = sign.x * sign.y < 0; - - Vector2 prev; - for (int i = 0; i <= steps; i++) { - - float d = i / float(steps); - float c = -Math::cos(d * Math_PI) * 0.5 + 0.5; - if (flip) - c = 1.0 - c; - Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height); - - if (i > 0) { - - draw_line(prev, p, p_color, 2); - } - - prev = p; - } +void AnimationTreeEditor::_about_to_show_root() { } void AnimationTreeEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_PROCESS) { + ObjectID root = 0; + if (tree && tree->get_tree_root().is_valid()) { + root = tree->get_tree_root()->get_instance_id(); + } - switch (p_what) { - - case NOTIFICATION_ENTER_TREE: { - - play_button->set_icon(get_icon("Play", "EditorIcons")); - add_menu->set_icon(get_icon("Add", "EditorIcons")); - } break; - case NOTIFICATION_DRAW: { - - _update_scrollbars(); - //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); - get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size())); - - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { - - _draw_node(E->get()); - } - - if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) { - - _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8)); - } - - List<AnimationTreePlayer::Connection> connections; - anim_tree->get_connection_list(&connections); - - for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { - - const AnimationTreePlayer::Connection &c = E->get(); - Point2 source = _get_slot_pos(c.src_node, false, 0); - Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input); - Color col = Color(1, 1, 0.5, 0.8); - /* - if (click_type==CLICK_NODE && click_node==c.src_node) { - - source+=click_motion-click_pos; - } - - if (click_type==CLICK_NODE && click_node==c.dst_node) { - - dest+=click_motion-click_pos; - }*/ - - _draw_cos_line(source, dest, col); - } - - switch (anim_tree->get_last_error()) { - - case AnimationTreePlayer::CONNECT_OK: { - - Ref<Font> f = get_font("font", "Label"); - f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8)); - } break; - default: { - - Ref<Font> f = get_font("font", "Label"); - f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8)); - } break; - } - - } break; + if (root != current_root) { + edit_path(Vector<String>()); + } } } -void AnimationTreeEditor::_update_scrollbars() { - - Size2 size = get_size(); - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); - - v_scroll->set_begin(Point2(size.width - vmin.width, 0)); - v_scroll->set_end(Point2(size.width, size.height)); - - h_scroll->set_begin(Point2(0, size.height - hmin.height)); - h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - - Size2 min = _get_maximum_size(); - - if (min.height < size.height - hmin.height) { - - v_scroll->hide(); - offset.y = 0; - } else { - - v_scroll->show(); - v_scroll->set_max(min.height); - v_scroll->set_page(size.height - hmin.height); - offset.y = v_scroll->get_value(); - } - - if (min.width < size.width - vmin.width) { - - h_scroll->hide(); - offset.x = 0; - } else { - - h_scroll->show(); - h_scroll->set_max(min.width); - h_scroll->set_page(size.width - vmin.width); - offset.x = h_scroll->get_value(); - } +void AnimationTreeEditor::_bind_methods() { + ClassDB::bind_method("_path_button_pressed", &AnimationTreeEditor::_path_button_pressed); } -void AnimationTreeEditor::_scroll_moved(float) { +AnimationTreeEditor *AnimationTreeEditor::singleton = NULL; - offset.x = h_scroll->get_value(); - offset.y = v_scroll->get_value(); - update(); +void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) { + ERR_FAIL_COND(p_editor->get_parent()); + editor_base->add_child(p_editor); + editors.push_back(p_editor); + p_editor->set_h_size_flags(SIZE_EXPAND_FILL); + p_editor->set_v_size_flags(SIZE_EXPAND_FILL); + p_editor->hide(); } -void AnimationTreeEditor::_node_menu_item(int p_item) { - - switch (p_item) { - - case NODE_DISCONNECT: { - - if (rclick_type == CLICK_INPUT_SLOT) { - - anim_tree->disconnect_nodes(rclick_node, rclick_slot); - update(); - } - - if (rclick_type == CLICK_OUTPUT_SLOT) { - - List<AnimationTreePlayer::Connection> connections; - anim_tree->get_connection_list(&connections); - - for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { - - const AnimationTreePlayer::Connection &c = E->get(); - if (c.dst_node == rclick_node) { - - anim_tree->disconnect_nodes(c.dst_node, c.dst_input); - } - } - update(); - } - - } break; - case NODE_RENAME: { - - renaming_edit = true; - edited_node = rclick_node; - _popup_edit_dialog(); - - } break; - case NODE_ADD_INPUT: { - - anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1); - update(); - } break; - case NODE_DELETE_INPUT: { - - anim_tree->transition_node_delete_input(rclick_node, rclick_slot); - update(); - } break; - case NODE_SET_AUTOADVANCE: { - - anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true); - update(); - - } break; - case NODE_CLEAR_AUTOADVANCE: { - - anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false); - update(); - - } break; - - case NODE_ERASE: { - - if (rclick_node == "out") - break; - order.erase(rclick_node); - anim_tree->remove_node(rclick_node); - update(); - } break; - } +void AnimationTreeEditor::remove_plugin(AnimationTreeNodeEditorPlugin *p_editor) { + ERR_FAIL_COND(p_editor->get_parent() != editor_base); + editor_base->remove_child(p_editor); + editors.erase(p_editor); } -StringName AnimationTreeEditor::_add_node(int p_item) { - - static const char *bname[] = { - "out", - "anim", - "oneshot", - "mix", - "blend2", - "blend3", - "blend4", - "scale", - "seek", - "transition" - }; - - String name; - int idx = 1; - - while (true) { - - name = bname[p_item]; - if (idx > 1) - name += " " + itos(idx); - if (anim_tree->node_exists(name)) - idx++; - else - break; +String AnimationTreeEditor::get_base_path() { + String path = SceneStringNames::get_singleton()->parameters_base_path; + for (int i = 0; i < edited_path.size(); i++) { + path += edited_path[i] + "/"; } - - anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name); - anim_tree->node_set_position(name, Point2(last_x, last_y)); - order.push_back(name); - last_x += 10; - last_y += 10; - last_x = last_x % (int)get_size().width; - last_y = last_y % (int)get_size().height; - update(); - - return name; -}; - -void AnimationTreeEditor::_file_dialog_selected(String p_path) { - - switch (file_op) { - - case MENU_IMPORT_ANIMATIONS: { - Vector<String> files = file_dialog->get_selected_files(); - - for (int i = 0; i < files.size(); i++) { - - StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); - - RES anim = ResourceLoader::load(files[i]); - anim_tree->animation_node_set_animation(node, anim); - //anim_tree->node_set_name(node, files[i].get_file()); - }; - } break; - - default: - break; - }; -}; - -void AnimationTreeEditor::_add_menu_item(int p_item) { - - if (p_item == MENU_GRAPH_CLEAR) { - - //clear - } else if (p_item == MENU_IMPORT_ANIMATIONS) { - - file_op = MENU_IMPORT_ANIMATIONS; - file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); - file_dialog->popup_centered_ratio(); - - } else { - - _add_node(p_item); - } -} - -Size2 AnimationTreeEditor::get_minimum_size() const { - - return Size2(10, 200); + return path; } -void AnimationTreeEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) { - - ERR_FAIL_COND(!anim_tree->node_exists(p_node)); - - for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) { - - StringName port = anim_tree->node_get_input_source(p_node, i); - if (port == StringName()) - continue; - _find_paths_for_filter(port, paths); - } - - if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) { - - Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); - if (anim.is_valid()) { - - for (int i = 0; i < anim->get_track_count(); i++) { - paths.insert(anim->track_get_path(i)); - } +bool AnimationTreeEditor::can_edit(const Ref<AnimationNode> &p_node) const { + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(p_node)) { + return true; } } + return false; } -void AnimationTreeEditor::_filter_edited() { - - TreeItem *ed = filter->get_edited(); - if (!ed) - return; +Vector<String> AnimationTreeEditor::get_animation_list() { - if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { - anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { - anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { - anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + if (!singleton->is_visible()) { + return Vector<String>(); } -} - -void AnimationTreeEditor::_edit_filters() { - - filter_dialog->popup_centered_ratio(); - filter->clear(); - - Set<String> npb; - _find_paths_for_filter(edited_node, npb); - - TreeItem *root = filter->create_item(); - filter->set_hide_root(true); - Map<String, TreeItem *> pm; - - Node *base = anim_tree->get_node(anim_tree->get_base_path()); - - for (Set<String>::Element *E = npb.front(); E; E = E->next()) { - - TreeItem *parent = root; - String descr = E->get(); - if (base) { - NodePath np = E->get(); - if (np.get_subname_count() == 1) { - Node *n = base->get_node(np); - Skeleton *s = Object::cast_to<Skeleton>(n); - if (s) { + AnimationTree *tree = singleton->tree; + if (!tree || !tree->has_node(tree->get_animation_player())) + return Vector<String>(); - String skelbase = E->get().substr(0, E->get().find(":")); + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player())); - int bidx = s->find_bone(np.get_subname(0)); + if (!ap) + return Vector<String>(); - if (bidx != -1) { - int bparent = s->get_bone_parent(bidx); - // - if (bparent != -1) { - - String bpn = skelbase + ":" + s->get_bone_name(bparent); - if (pm.has(bpn)) { - parent = pm[bpn]; - descr = np.get_subname(0); - } - } else { - - if (pm.has(skelbase)) { - parent = pm[skelbase]; - } - } - } - } - } - } - - TreeItem *it = filter->create_item(parent); - it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - it->set_text(0, descr); - it->set_metadata(0, NodePath(E->get())); - it->set_editable(0, true); - if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { - it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get())); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { - it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get())); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { - it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get())); - } - pm[E->get()] = it; + List<StringName> anims; + ap->get_animation_list(&anims); + Vector<String> ret; + for (List<StringName>::Element *E = anims.front(); E; E = E->next()) { + ret.push_back(E->get()); } -} - -void AnimationTreeEditor::_bind_methods() { - ClassDB::bind_method("_add_menu_item", &AnimationTreeEditor::_add_menu_item); - ClassDB::bind_method("_node_menu_item", &AnimationTreeEditor::_node_menu_item); - ClassDB::bind_method("_gui_input", &AnimationTreeEditor::_gui_input); - //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); - ClassDB::bind_method("_scroll_moved", &AnimationTreeEditor::_scroll_moved); - ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreeEditor::_edit_dialog_changeds); - ClassDB::bind_method("_edit_dialog_changede", &AnimationTreeEditor::_edit_dialog_changede); - ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreeEditor::_edit_dialog_changedf); - ClassDB::bind_method("_edit_dialog_changed", &AnimationTreeEditor::_edit_dialog_changed); - ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreeEditor::_edit_dialog_animation_changed); - ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreeEditor::_edit_dialog_edit_animation); - ClassDB::bind_method("_play_toggled", &AnimationTreeEditor::_play_toggled); - ClassDB::bind_method("_edit_oneshot_start", &AnimationTreeEditor::_edit_oneshot_start); - ClassDB::bind_method("_file_dialog_selected", &AnimationTreeEditor::_file_dialog_selected); - ClassDB::bind_method("_master_anim_menu_item", &AnimationTreeEditor::_master_anim_menu_item); - ClassDB::bind_method("_edit_filters", &AnimationTreeEditor::_edit_filters); - ClassDB::bind_method("_filter_edited", &AnimationTreeEditor::_filter_edited); + return ret; } AnimationTreeEditor::AnimationTreeEditor() { - set_focus_mode(FOCUS_ALL); - - PopupMenu *p; - List<PropertyInfo> defaults; - - add_menu = memnew(MenuButton); - //add_menu->set_ - add_menu->set_position(Point2(0, 0)); - add_menu->set_size(Point2(25, 15)); - add_child(add_menu); - - p = add_menu->get_popup(); - p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION); - p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT); - p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX); - p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2); - p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3); - p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4); - p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE); - p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); - p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); - p->add_separator(); - p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf - p->add_separator(); - p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); - - p->connect("id_pressed", this, "_add_menu_item"); - - play_button = memnew(Button); - play_button->set_position(Point2(25, 0)); - play_button->set_size(Point2(25, 15)); - add_child(play_button); - play_button->set_toggle_mode(true); - play_button->connect("pressed", this, "_play_toggled"); - - last_x = 50; - last_y = 50; - - property_editor = memnew(CustomPropertyEditor); - add_child(property_editor); - property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed"); - property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); - - h_scroll = memnew(HScrollBar); - v_scroll = memnew(VScrollBar); - - add_child(h_scroll); - add_child(v_scroll); - - h_scroll->connect("value_changed", this, "_scroll_moved"); - v_scroll->connect("value_changed", this, "_scroll_moved"); - - node_popup = memnew(PopupMenu); - add_child(node_popup); - node_popup->set_as_toplevel(true); - - master_anim_popup = memnew(PopupMenu); - add_child(master_anim_popup); - master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item"); - - node_popup->connect("id_pressed", this, "_node_menu_item"); - - updating_edit = false; - - edit_dialog = memnew(PopupPanel); - //edit_dialog->get_ok()->hide(); - //edit_dialog->get_cancel()->hide(); - add_child(edit_dialog); - - edit_option = memnew(OptionButton); - edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_option->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_option); - edit_option->connect("item_selected", this, "_edit_dialog_changedf"); - edit_option->hide(); - - for (int i = 0; i < 2; i++) { - edit_scroll[i] = memnew(HSlider); - edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_scroll[i]->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_scroll[i]); - edit_scroll[i]->hide(); - edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf"); - } - for (int i = 0; i < 4; i++) { - edit_line[i] = memnew(LineEdit); - edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_line[i]->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_line[i]); - edit_line[i]->hide(); - edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds"); - edit_line[i]->connect("text_entered", this, "_edit_dialog_changede"); - edit_label[i] = memnew(Label); - edit_dialog->add_child(edit_label[i]); - edit_label[i]->hide(); - } - - edit_button = memnew(Button); - edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_button->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_button); - edit_button->hide(); - edit_button->connect("pressed", this, "_edit_oneshot_start"); - - edit_check = memnew(CheckButton); - edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_check->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_check); - edit_check->hide(); - edit_check->connect("pressed", this, "_edit_dialog_changed"); - - file_dialog = memnew(EditorFileDialog); - file_dialog->set_enable_multiple_selection(true); - file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path()); - add_child(file_dialog); - file_dialog->connect("file_selected", this, "_file_dialog_selected"); - - filter_dialog = memnew(AcceptDialog); - filter_dialog->set_title(TTR("Edit Node Filters")); - add_child(filter_dialog); - - filter = memnew(Tree); - filter_dialog->add_child(filter); - //filter_dialog->set_child_rect(filter); - filter->connect("item_edited", this, "_filter_edited"); + AnimationNodeAnimation::get_editable_animation_list = get_animation_list; + path_edit = memnew(ScrollContainer); + add_child(path_edit); + path_edit->set_enable_h_scroll(true); + path_edit->set_enable_v_scroll(false); + path_hb = memnew(HBoxContainer); + path_edit->add_child(path_hb); - filter_button = memnew(Button); - filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); - filter_button->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(filter_button); - filter_button->hide(); - filter_button->set_text(TTR("Filters...")); - filter_button->connect("pressed", this, "_edit_filters"); + current_root = 0; + singleton = this; + editor_base = memnew(PanelContainer); + editor_base->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(editor_base); - set_clip_contents(true); + add_plugin(memnew(AnimationNodeBlendTreeEditor)); + add_plugin(memnew(AnimationNodeBlendSpace1DEditor)); + add_plugin(memnew(AnimationNodeBlendSpace2DEditor)); + add_plugin(memnew(AnimationNodeStateMachineEditor)); } void AnimationTreeEditorPlugin::edit(Object *p_object) { - anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object)); + anim_tree_editor->edit(Object::cast_to<AnimationTree>(p_object)); } bool AnimationTreeEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("AnimationTreePlayer"); + return p_object->is_class("AnimationTree"); } void AnimationTreeEditorPlugin::make_visible(bool p_visible) { @@ -1422,13 +251,13 @@ void AnimationTreeEditorPlugin::make_visible(bool p_visible) { //editor->animation_panel_make_visible(true); button->show(); editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_physics_process(true); + anim_tree_editor->set_process(true); } else { if (anim_tree_editor->is_visible_in_tree()) editor->hide_bottom_panel(); button->hide(); - anim_tree_editor->set_physics_process(false); + anim_tree_editor->set_process(false); } } diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index aeb5b1744f..b12054bb62 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -1,167 +1,65 @@ -/*************************************************************************/ -/* animation_tree_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - #ifndef ANIMATION_TREE_EDITOR_PLUGIN_H #define ANIMATION_TREE_EDITOR_PLUGIN_H #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "editor/property_editor.h" -#include "scene/animation/animation_tree_player.h" +#include "scene/animation/animation_tree.h" #include "scene/gui/button.h" +#include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - -class AnimationTreeEditor : public Control { - - GDCLASS(AnimationTreeEditor, Control); - - static const char *_node_type_names[]; - - enum ClickType { - CLICK_NONE, - CLICK_NAME, - CLICK_NODE, - CLICK_INPUT_SLOT, - CLICK_OUTPUT_SLOT, - CLICK_PARAMETER - }; - - enum { - - MENU_GRAPH_CLEAR = 100, - MENU_IMPORT_ANIMATIONS = 101, - NODE_DISCONNECT, - NODE_RENAME, - NODE_ERASE, - NODE_ADD_INPUT, - NODE_DELETE_INPUT, - NODE_SET_AUTOADVANCE, - NODE_CLEAR_AUTOADVANCE - }; - - bool renaming_edit; - StringName edited_node; - bool updating_edit; - Popup *edit_dialog; - HSlider *edit_scroll[2]; - LineEdit *edit_line[4]; - OptionButton *edit_option; - Label *edit_label[4]; - Button *edit_button; - Button *filter_button; - CheckButton *edit_check; - EditorFileDialog *file_dialog; - int file_op; - - void _popup_edit_dialog(); - - void _setup_edit_dialog(const StringName &p_node); - PopupMenu *master_anim_popup; - PopupMenu *node_popup; - PopupMenu *add_popup; - HScrollBar *h_scroll; - VScrollBar *v_scroll; - MenuButton *add_menu; - - CustomPropertyEditor *property_editor; - - AnimationTreePlayer *anim_tree; - List<StringName> order; - Set<StringName> active_nodes; - - int last_x, last_y; - - Point2 offset; - ClickType click_type; - Point2 click_pos; - StringName click_node; - int click_slot; - Point2 click_motion; - ClickType rclick_type; - StringName rclick_node; - int rclick_slot; - - Button *play_button; - - Size2 _get_maximum_size(); - Size2 get_node_size(const StringName &p_node) const; - void _draw_node(const StringName &p_node); - - AcceptDialog *filter_dialog; - Tree *filter; - - void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color); - void _update_scrollbars(); - void _scroll_moved(float); - void _play_toggled(); - /* - void _node_param_changed(); - void _node_add_callback(); - void _node_add(VisualServer::AnimationTreeNodeType p_type); - void _node_edit_property(const StringName& p_node); -*/ - - void _master_anim_menu_item(int p_item); - void _node_menu_item(int p_item); - void _add_menu_item(int p_item); - - void _filter_edited(); - void _find_paths_for_filter(const StringName &p_node, Set<String> &paths); - void _edit_filters(); - - void _edit_oneshot_start(); - void _edit_dialog_animation_changed(); - void _edit_dialog_edit_animation(); - void _edit_dialog_changeds(String); - void _edit_dialog_changede(String); - void _edit_dialog_changedf(float); - void _edit_dialog_changed(); - void _dialog_changed() const; - ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const; - Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot); - - StringName _add_node(int p_item); - void _file_dialog_selected(String p_path); + +class AnimationTreeNodeEditorPlugin : public VBoxContainer { + GDCLASS(AnimationTreeNodeEditorPlugin, VBoxContainer) +public: + virtual bool can_edit(const Ref<AnimationNode> &p_node) = 0; + virtual void edit(const Ref<AnimationNode> &p_node) = 0; +}; + +class AnimationTreeEditor : public VBoxContainer { + + GDCLASS(AnimationTreeEditor, VBoxContainer); + + ScrollContainer *path_edit; + HBoxContainer *path_hb; + + AnimationTree *tree; + PanelContainer *editor_base; + + Vector<String> button_path; + Vector<String> edited_path; + Vector<AnimationTreeNodeEditorPlugin *> editors; + + void _update_path(); + void _about_to_show_root(); + ObjectID current_root; + + void _path_button_pressed(int p_path); + + static Vector<String> get_animation_list(); protected: void _notification(int p_what); - void _gui_input(Ref<InputEvent> p_event); static void _bind_methods(); + static AnimationTreeEditor *singleton; + public: - virtual Size2 get_minimum_size() const; - void edit(AnimationTreePlayer *p_anim_tree); + AnimationTree *get_tree() { return tree; } + void add_plugin(AnimationTreeNodeEditorPlugin *p_editor); + void remove_plugin(AnimationTreeNodeEditorPlugin *p_editor); + + String get_base_path(); + + bool can_edit(const Ref<AnimationNode> &p_node) const; + + void edit_path(const Vector<String> &p_path); + Vector<String> get_edited_path() const; + + void enter_editor(const String &p_path = ""); + static AnimationTreeEditor *get_singleton() { return singleton; } + void edit(AnimationTree *p_tree); AnimationTreeEditor(); }; @@ -174,7 +72,7 @@ class AnimationTreeEditorPlugin : public EditorPlugin { Button *button; public: - virtual String get_name() const { return "AnimTree"; } + virtual String get_name() const { return "AnimationTree"; } bool has_main_screen() const { return false; } virtual void edit(Object *p_object); virtual bool handles(Object *p_object) const; diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp new file mode 100644 index 0000000000..36d10ab99e --- /dev/null +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -0,0 +1,1446 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "animation_tree_player_editor_plugin.h" + +#include "core/io/resource_loader.h" +#include "core/project_settings.h" +#include "os/input.h" +#include "os/keyboard.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" +#include "scene/main/viewport.h" + +void AnimationTreePlayerEditor::edit(AnimationTreePlayer *p_anim_tree) { + + anim_tree = p_anim_tree; + + if (!anim_tree) { + hide(); + } else { + order.clear(); + p_anim_tree->get_node_list(&order); + /* + for(List<StringName>::Element* E=order.front();E;E=E->next()) { + + if (E->get() >= (int)last_id) + last_id=E->get()+1; + }*/ + play_button->set_pressed(p_anim_tree->is_active()); + //read the orders + } +} + +Size2 AnimationTreePlayerEditor::_get_maximum_size() { + + Size2 max; + + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + Point2 pos = anim_tree->node_get_position(E->get()); + + if (click_type == CLICK_NODE && click_node == E->get()) { + + pos += click_motion - click_pos; + } + pos += get_node_size(E->get()); + if (pos.x > max.x) + max.x = pos.x; + if (pos.y > max.y) + max.y = pos.y; + } + + return max; +} + +const char *AnimationTreePlayerEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" }; + +Size2 AnimationTreePlayerEditor::get_node_size(const StringName &p_node) const { + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + + Size2 size = style->get_minimum_size(); + + int count = 2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs ? inputs : 1; + String name = p_node; + + float name_w = font->get_string_size(name).width; + float type_w = font->get_string_size(String(_node_type_names[type])).width; + float max_w = MAX(name_w, type_w); + + switch (type) { + case AnimationTreePlayer::NODE_TIMESEEK: + case AnimationTreePlayer::NODE_OUTPUT: { + } break; + case AnimationTreePlayer::NODE_ANIMATION: + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + size.height += font->get_height(); + } break; + case AnimationTreePlayer::NODE_MAX: { + } + } + + size.x += max_w + 20; + size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu")); + + return size; +} + +void AnimationTreePlayerEditor::_edit_dialog_changede(String) { + + edit_dialog->hide(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changeds(String s) { + + _edit_dialog_changed(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changedf(float) { + + _edit_dialog_changed(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changed() { + + if (updating_edit) + return; + + if (renaming_edit) { + + if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) { + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + if (E->get() == edited_node) + E->get() = edit_line[0]->get_text(); + } + edited_node = edit_line[0]->get_text(); + } + update(); + return; + } + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); + + switch (type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double()); + break; + case AnimationTreePlayer::NODE_ONESHOT: + anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double()); + anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed()); + anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected()); + + break; + + case AnimationTreePlayer::NODE_MIX: + + anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value()); + break; + case AnimationTreePlayer::NODE_BLEND2: + anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value()); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value()); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value())); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double()); + if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) + anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); + } break; + default: {} + } +} + +void AnimationTreePlayerEditor::_edit_dialog_animation_changed() { + + Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); + anim_tree->animation_node_set_animation(edited_node, anim); + update(); +} + +void AnimationTreePlayerEditor::_edit_dialog_edit_animation() { + + if (Engine::get_singleton()->is_editor_hint()) { + get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); + }; +}; + +void AnimationTreePlayerEditor::_edit_oneshot_start() { + + anim_tree->oneshot_node_start(edited_node); +} + +void AnimationTreePlayerEditor::_play_toggled() { + + anim_tree->set_active(play_button->is_pressed()); +} + +void AnimationTreePlayerEditor::_master_anim_menu_item(int p_item) { + + if (p_item == 0) + _edit_filters(); + else { + + String str = master_anim_popup->get_item_text(p_item); + anim_tree->animation_node_set_master_animation(edited_node, str); + } + update(); +} + +void AnimationTreePlayerEditor::_popup_edit_dialog() { + + updating_edit = true; + + for (int i = 0; i < 2; i++) + edit_scroll[i]->hide(); + + for (int i = 0; i < 4; i++) { + + edit_line[i]->hide(); + edit_label[i]->hide(); + } + + edit_option->hide(); + edit_button->hide(); + filter_button->hide(); + edit_check->hide(); + + Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value()); + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Size2 size = get_node_size(edited_node); + Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM)); + popup_pos += get_global_position(); + + if (renaming_edit) { + + edit_label[0]->set_text(TTR("New name:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(edited_node); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + } else { + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); + + switch (type) { + + case AnimationTreePlayer::NODE_ANIMATION: + + if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) { + + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player())); + master_anim_popup->clear(); + master_anim_popup->add_item(TTR("Edit Filters")); + master_anim_popup->add_separator(); + List<StringName> sn; + ap->get_animation_list(&sn); + sn.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = sn.front(); E; E = E->next()) { + master_anim_popup->add_item(E->get()); + } + + master_anim_popup->set_position(popup_pos); + master_anim_popup->popup(); + } else { + property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation"); + property_editor->set_position(popup_pos); + property_editor->popup(); + updating_edit = false; + } + return; + case AnimationTreePlayer::NODE_TIMESCALE: + edit_label[0]->set_text(TTR("Scale:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + break; + case AnimationTreePlayer::NODE_ONESHOT: + edit_label[0]->set_text(TTR("Fade In (s):")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); + edit_line[0]->show(); + edit_label[1]->set_text(TTR("Fade Out (s):")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_line[1]->set_begin(Point2(15, 75)); + edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); + edit_line[1]->show(); + + edit_option->clear(); + edit_option->add_item(TTR("Blend"), 0); + edit_option->add_item(TTR("Mix"), 1); + edit_option->set_begin(Point2(15, 105)); + + edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node)); + edit_option->show(); + + edit_check->set_text(TTR("Auto Restart:")); + edit_check->set_begin(Point2(15, 125)); + edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); + edit_check->show(); + + edit_label[2]->set_text(TTR("Restart (s):")); + edit_label[2]->set_position(Point2(5, 145)); + edit_label[2]->show(); + edit_line[2]->set_begin(Point2(15, 165)); + edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); + edit_line[2]->show(); + edit_label[3]->set_text(TTR("Random Restart (s):")); + edit_label[3]->set_position(Point2(5, 195)); + edit_label[3]->show(); + edit_line[3]->set_begin(Point2(15, 215)); + edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); + edit_line[3]->show(); + + filter_button->set_begin(Point2(10, 245)); + filter_button->show(); + + edit_button->set_begin(Point2(10, 268)); + edit_button->set_text(TTR("Start!")); + + edit_button->show(); + + edit_dialog->set_size(Size2(180, 293)); + + break; + + case AnimationTreePlayer::NODE_MIX: + + edit_label[0]->set_text(TTR("Amount:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + break; + case AnimationTreePlayer::NODE_BLEND2: + edit_label[0]->set_text(TTR("Blend:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + filter_button->set_begin(Point2(10, 47)); + filter_button->show(); + edit_dialog->set_size(Size2(150, 74)); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + edit_label[0]->set_text(TTR("Blend:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(-1); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + edit_label[0]->set_text(TTR("Blend 0:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_label[1]->set_text(TTR("Blend 1:")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_scroll[1]->set_min(0); + edit_scroll[1]->set_max(1); + edit_scroll[1]->set_step(0.01); + edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y); + edit_scroll[1]->set_begin(Point2(15, 75)); + edit_scroll[1]->show(); + edit_dialog->set_size(Size2(150, 100)); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + + edit_label[0]->set_text(TTR("X-Fade Time (s):")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); + edit_line[0]->show(); + + edit_label[1]->set_text(TTR("Current:")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_option->set_begin(Point2(15, 75)); + + edit_option->clear(); + + for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) { + edit_option->add_item(itos(i), i); + } + + edit_option->select(anim_tree->transition_node_get_current(edited_node)); + edit_option->show(); + edit_dialog->set_size(Size2(150, 100)); + + } break; + default: {} + } + } + + edit_dialog->set_position(popup_pos); + edit_dialog->popup(); + + updating_edit = false; +} + +void AnimationTreePlayerEditor::_draw_node(const StringName &p_node) { + + RID ci = get_canvas_item(); + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + Color font_color = get_color("font_color", "PopupMenu"); + Color font_color_title = get_color("font_color_hover", "PopupMenu"); + font_color_title.a *= 0.8; + Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); + + Size2 size = get_node_size(p_node); + Point2 pos = anim_tree->node_get_position(p_node); + if (click_type == CLICK_NODE && click_node == p_node) { + + pos += click_motion - click_pos; + if (pos.x < 5) + pos.x = 5; + if (pos.y < 5) + pos.y = 5; + } + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + style->draw(ci, Rect2(pos, size)); + + float w = size.width - style->get_minimum_size().width; + float h = font->get_height() + get_constant("vseparation", "PopupMenu"); + + Point2 ofs = style->get_offset() + pos; + Point2 ascofs(0, font->get_ascent()); + + Color bx = font_color_title; + bx.a *= 0.1; + draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx); + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title); + + ofs.y += h; + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color); + ofs.y += h; + + int count = 2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs ? inputs : 1; + + float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1; + + if (type != AnimationTreePlayer::NODE_OUTPUT) + slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output + + if (inputs) { + for (int i = 0; i < inputs; i++) { + + slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs)); + String text; + switch (type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break; + case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break; + case AnimationTreePlayer::NODE_ANIMATION: break; + case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break; + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break; + case AnimationTreePlayer::NODE_BLEND3: + switch (i) { + case 0: text = "b-"; break; + case 1: text = "a"; break; + case 2: text = "b+"; break; + } + break; + + case AnimationTreePlayer::NODE_BLEND4: + switch (i) { + case 0: text = "a0"; break; + case 1: text = "b0"; break; + case 2: text = "a1"; break; + case 3: text = "b1"; break; + } + break; + + case AnimationTreePlayer::NODE_TRANSITION: + text = itos(i); + if (anim_tree->transition_node_has_input_auto_advance(p_node, i)) + text += "->"; + + break; + default: {} + } + font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); + + ofs.y += h; + } + } else { + ofs.y += h; + } + + Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar"); + Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar"); + Rect2 pg_rect(ofs, Size2(w, h)); + + bool editable = true; + switch (type) { + case AnimationTreePlayer::NODE_ANIMATION: { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + String text; + if (anim_tree->animation_node_get_master_animation(p_node) != "") + text = anim_tree->animation_node_get_master_animation(p_node); + else if (anim.is_null()) + text = "load..."; + else + text = anim->get_name(); + + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title); + + } break; + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); + } break; + default: editable = false; + } + + if (editable) { + + Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons"); + Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2)); + arrow->draw(ci, ofs + arrow_ofs); + } +} + +AnimationTreePlayerEditor::ClickType AnimationTreePlayerEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const { + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + + float h = (font->get_height() + get_constant("vseparation", "PopupMenu")); + + for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) { + + StringName node = E->get(); + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node); + + Point2 pos = anim_tree->node_get_position(node); + Size2 size = get_node_size(node); + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + if (!Rect2(pos, size).has_point(p_click)) + continue; + + if (p_node_id) + *p_node_id = node; + + pos = p_click - pos; + + float y = pos.y - style->get_offset().height; + + if (y < 2 * h) + return CLICK_NODE; + y -= 2 * h; + + int inputs = anim_tree->node_get_input_count(node); + int count = MAX(inputs, 1); + + if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) { + + if (y < count * h) { + + if (p_slot_index) + *p_slot_index = 0; + return CLICK_OUTPUT_SLOT; + } + } + + for (int i = 0; i < count; i++) { + + if (y < h) { + if (p_slot_index) + *p_slot_index = i; + return CLICK_INPUT_SLOT; + } + y -= h; + } + + bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK; + return has_parameters ? CLICK_PARAMETER : CLICK_NODE; + } + + return CLICK_NONE; +} + +Point2 AnimationTreePlayerEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) { + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); + + Size2 size = get_node_size(p_node_id); + Point2 pos = anim_tree->node_get_position(p_node_id); + + if (click_type == CLICK_NODE && click_node == p_node_id) { + + pos += click_motion - click_pos; + if (pos.x < 5) + pos.x = 5; + if (pos.y < 5) + pos.y = 5; + } + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + float w = size.width - style->get_minimum_size().width; + float h = font->get_height() + get_constant("vseparation", "PopupMenu"); + + pos += style->get_offset(); + + pos.y += h * 2; + + pos.y += h * p_slot; + + pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor(); + + if (!p_input) { + pos.x += w + slot_icon->get_width(); + } + + return pos; +} + +void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid()) { + + if (mb->is_pressed()) { + + if (mb->get_button_index() == 1) { + click_pos = Point2(mb->get_position().x, mb->get_position().y); + click_motion = click_pos; + click_type = _locate_click(click_pos, &click_node, &click_slot); + if (click_type != CLICK_NONE) { + + order.erase(click_node); + order.push_back(click_node); + update(); + } + + switch (click_type) { + case CLICK_INPUT_SLOT: { + click_pos = _get_slot_pos(click_node, true, click_slot); + } break; + case CLICK_OUTPUT_SLOT: { + click_pos = _get_slot_pos(click_node, false, click_slot); + } break; + case CLICK_PARAMETER: { + + edited_node = click_node; + renaming_edit = false; + _popup_edit_dialog(); + //open editor + //_node_edit_property(click_node); + } break; + default: {} + } + } + if (mb->get_button_index() == 2) { + + if (click_type != CLICK_NONE) { + click_type = CLICK_NONE; + update(); + } else { + // try to disconnect/remove + + Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y); + rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot); + if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { + + node_popup->clear(); + node_popup->set_size(Size2(1, 1)); + node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); + if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { + node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); + if (rclick_type == CLICK_INPUT_SLOT) { + if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot)) + node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE); + else + node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE); + node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT); + } + } + + node_popup->set_position(rclick_pos + get_global_position()); + node_popup->popup(); + } + + if (rclick_type == CLICK_NODE) { + node_popup->clear(); + node_popup->set_size(Size2(1, 1)); + node_popup->add_item(TTR("Rename"), NODE_RENAME); + node_popup->add_item(TTR("Remove"), NODE_ERASE); + if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) + node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); + node_popup->set_position(rclick_pos + get_global_position()); + node_popup->popup(); + } + } + } + } else { + + if (mb->get_button_index() == 1 && click_type != CLICK_NONE) { + + switch (click_type) { + case CLICK_INPUT_SLOT: + case CLICK_OUTPUT_SLOT: { + + Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y); + StringName id; + int slot; + ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot); + + if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) { + + anim_tree->connect_nodes(click_node, id, slot); + } + if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) { + + anim_tree->connect_nodes(id, click_node, click_slot); + } + + } break; + case CLICK_NODE: { + Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos); + if (new_pos.x < 5) + new_pos.x = 5; + if (new_pos.y < 5) + new_pos.y = 5; + anim_tree->node_set_position(click_node, new_pos); + + } break; + default: {} + } + + click_type = CLICK_NONE; + update(); + } + } + } + + Ref<InputEventMouseMotion> mm = p_event; + + if (mm.is_valid()) { + + if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) { + + click_motion = Point2(mm->get_position().x, mm->get_position().y); + update(); + } + if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + + h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); + v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); + update(); + } + } +} + +void AnimationTreePlayerEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) { + + static const int steps = 20; + + Rect2 r; + r.position = p_from; + r.expand_to(p_to); + Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1); + bool flip = sign.x * sign.y < 0; + + Vector2 prev; + for (int i = 0; i <= steps; i++) { + + float d = i / float(steps); + float c = -Math::cos(d * Math_PI) * 0.5 + 0.5; + if (flip) + c = 1.0 - c; + Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height); + + if (i > 0) { + + draw_line(prev, p, p_color, 2); + } + + prev = p; + } +} + +void AnimationTreePlayerEditor::_notification(int p_what) { + + switch (p_what) { + + case NOTIFICATION_ENTER_TREE: { + + play_button->set_icon(get_icon("Play", "EditorIcons")); + add_menu->set_icon(get_icon("Add", "EditorIcons")); + } break; + case NOTIFICATION_DRAW: { + + _update_scrollbars(); + //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); + get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size())); + + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + _draw_node(E->get()); + } + + if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) { + + _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8)); + } + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { + + const AnimationTreePlayer::Connection &c = E->get(); + Point2 source = _get_slot_pos(c.src_node, false, 0); + Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input); + Color col = Color(1, 1, 0.5, 0.8); + /* + if (click_type==CLICK_NODE && click_node==c.src_node) { + + source+=click_motion-click_pos; + } + + if (click_type==CLICK_NODE && click_node==c.dst_node) { + + dest+=click_motion-click_pos; + }*/ + + _draw_cos_line(source, dest, col); + } + + switch (anim_tree->get_last_error()) { + + case AnimationTreePlayer::CONNECT_OK: { + + Ref<Font> f = get_font("font", "Label"); + f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8)); + } break; + default: { + + Ref<Font> f = get_font("font", "Label"); + f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8)); + } break; + } + + } break; + } +} + +void AnimationTreePlayerEditor::_update_scrollbars() { + + Size2 size = get_size(); + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); + + v_scroll->set_begin(Point2(size.width - vmin.width, 0)); + v_scroll->set_end(Point2(size.width, size.height)); + + h_scroll->set_begin(Point2(0, size.height - hmin.height)); + h_scroll->set_end(Point2(size.width - vmin.width, size.height)); + + Size2 min = _get_maximum_size(); + + if (min.height < size.height - hmin.height) { + + v_scroll->hide(); + offset.y = 0; + } else { + + v_scroll->show(); + v_scroll->set_max(min.height); + v_scroll->set_page(size.height - hmin.height); + offset.y = v_scroll->get_value(); + } + + if (min.width < size.width - vmin.width) { + + h_scroll->hide(); + offset.x = 0; + } else { + + h_scroll->show(); + h_scroll->set_max(min.width); + h_scroll->set_page(size.width - vmin.width); + offset.x = h_scroll->get_value(); + } +} + +void AnimationTreePlayerEditor::_scroll_moved(float) { + + offset.x = h_scroll->get_value(); + offset.y = v_scroll->get_value(); + update(); +} + +void AnimationTreePlayerEditor::_node_menu_item(int p_item) { + + switch (p_item) { + + case NODE_DISCONNECT: { + + if (rclick_type == CLICK_INPUT_SLOT) { + + anim_tree->disconnect_nodes(rclick_node, rclick_slot); + update(); + } + + if (rclick_type == CLICK_OUTPUT_SLOT) { + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { + + const AnimationTreePlayer::Connection &c = E->get(); + if (c.dst_node == rclick_node) { + + anim_tree->disconnect_nodes(c.dst_node, c.dst_input); + } + } + update(); + } + + } break; + case NODE_RENAME: { + + renaming_edit = true; + edited_node = rclick_node; + _popup_edit_dialog(); + + } break; + case NODE_ADD_INPUT: { + + anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1); + update(); + } break; + case NODE_DELETE_INPUT: { + + anim_tree->transition_node_delete_input(rclick_node, rclick_slot); + update(); + } break; + case NODE_SET_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true); + update(); + + } break; + case NODE_CLEAR_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false); + update(); + + } break; + + case NODE_ERASE: { + + if (rclick_node == "out") + break; + order.erase(rclick_node); + anim_tree->remove_node(rclick_node); + update(); + } break; + } +} + +StringName AnimationTreePlayerEditor::_add_node(int p_item) { + + static const char *bname[] = { + "out", + "anim", + "oneshot", + "mix", + "blend2", + "blend3", + "blend4", + "scale", + "seek", + "transition" + }; + + String name; + int idx = 1; + + while (true) { + + name = bname[p_item]; + if (idx > 1) + name += " " + itos(idx); + if (anim_tree->node_exists(name)) + idx++; + else + break; + } + + anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name); + anim_tree->node_set_position(name, Point2(last_x, last_y)); + order.push_back(name); + last_x += 10; + last_y += 10; + last_x = last_x % (int)get_size().width; + last_y = last_y % (int)get_size().height; + update(); + + return name; +}; + +void AnimationTreePlayerEditor::_file_dialog_selected(String p_path) { + + switch (file_op) { + + case MENU_IMPORT_ANIMATIONS: { + Vector<String> files = file_dialog->get_selected_files(); + + for (int i = 0; i < files.size(); i++) { + + StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); + + RES anim = ResourceLoader::load(files[i]); + anim_tree->animation_node_set_animation(node, anim); + //anim_tree->node_set_name(node, files[i].get_file()); + }; + } break; + + default: + break; + }; +}; + +void AnimationTreePlayerEditor::_add_menu_item(int p_item) { + + if (p_item == MENU_GRAPH_CLEAR) { + + //clear + } else if (p_item == MENU_IMPORT_ANIMATIONS) { + + file_op = MENU_IMPORT_ANIMATIONS; + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->popup_centered_ratio(); + + } else { + + _add_node(p_item); + } +} + +Size2 AnimationTreePlayerEditor::get_minimum_size() const { + + return Size2(10, 200); +} + +void AnimationTreePlayerEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) { + + ERR_FAIL_COND(!anim_tree->node_exists(p_node)); + + for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) { + + StringName port = anim_tree->node_get_input_source(p_node, i); + if (port == StringName()) + continue; + _find_paths_for_filter(port, paths); + } + + if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + if (anim.is_valid()) { + + for (int i = 0; i < anim->get_track_count(); i++) { + paths.insert(anim->track_get_path(i)); + } + } + } +} + +void AnimationTreePlayerEditor::_filter_edited() { + + TreeItem *ed = filter->get_edited(); + if (!ed) + return; + + if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { + anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { + anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { + anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } +} + +void AnimationTreePlayerEditor::_edit_filters() { + + filter_dialog->popup_centered_ratio(); + filter->clear(); + + Set<String> npb; + _find_paths_for_filter(edited_node, npb); + + TreeItem *root = filter->create_item(); + filter->set_hide_root(true); + Map<String, TreeItem *> pm; + + Node *base = anim_tree->get_node(anim_tree->get_base_path()); + + for (Set<String>::Element *E = npb.front(); E; E = E->next()) { + + TreeItem *parent = root; + String descr = E->get(); + if (base) { + NodePath np = E->get(); + + if (np.get_subname_count() == 1) { + Node *n = base->get_node(np); + Skeleton *s = Object::cast_to<Skeleton>(n); + if (s) { + + String skelbase = E->get().substr(0, E->get().find(":")); + + int bidx = s->find_bone(np.get_subname(0)); + + if (bidx != -1) { + int bparent = s->get_bone_parent(bidx); + // + if (bparent != -1) { + + String bpn = skelbase + ":" + s->get_bone_name(bparent); + if (pm.has(bpn)) { + parent = pm[bpn]; + descr = np.get_subname(0); + } + } else { + + if (pm.has(skelbase)) { + parent = pm[skelbase]; + } + } + } + } + } + } + + TreeItem *it = filter->create_item(parent); + it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + it->set_text(0, descr); + it->set_metadata(0, NodePath(E->get())); + it->set_editable(0, true); + if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { + it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get())); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { + it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get())); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { + it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get())); + } + pm[E->get()] = it; + } +} + +void AnimationTreePlayerEditor::_bind_methods() { + + ClassDB::bind_method("_add_menu_item", &AnimationTreePlayerEditor::_add_menu_item); + ClassDB::bind_method("_node_menu_item", &AnimationTreePlayerEditor::_node_menu_item); + ClassDB::bind_method("_gui_input", &AnimationTreePlayerEditor::_gui_input); + //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); + ClassDB::bind_method("_scroll_moved", &AnimationTreePlayerEditor::_scroll_moved); + ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreePlayerEditor::_edit_dialog_changeds); + ClassDB::bind_method("_edit_dialog_changede", &AnimationTreePlayerEditor::_edit_dialog_changede); + ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreePlayerEditor::_edit_dialog_changedf); + ClassDB::bind_method("_edit_dialog_changed", &AnimationTreePlayerEditor::_edit_dialog_changed); + ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreePlayerEditor::_edit_dialog_animation_changed); + ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreePlayerEditor::_edit_dialog_edit_animation); + ClassDB::bind_method("_play_toggled", &AnimationTreePlayerEditor::_play_toggled); + ClassDB::bind_method("_edit_oneshot_start", &AnimationTreePlayerEditor::_edit_oneshot_start); + ClassDB::bind_method("_file_dialog_selected", &AnimationTreePlayerEditor::_file_dialog_selected); + ClassDB::bind_method("_master_anim_menu_item", &AnimationTreePlayerEditor::_master_anim_menu_item); + ClassDB::bind_method("_edit_filters", &AnimationTreePlayerEditor::_edit_filters); + ClassDB::bind_method("_filter_edited", &AnimationTreePlayerEditor::_filter_edited); +} + +AnimationTreePlayerEditor::AnimationTreePlayerEditor() { + + set_focus_mode(FOCUS_ALL); + + PopupMenu *p; + List<PropertyInfo> defaults; + + add_menu = memnew(MenuButton); + //add_menu->set_ + add_menu->set_position(Point2(0, 0)); + add_menu->set_size(Point2(25, 15)); + add_child(add_menu); + + p = add_menu->get_popup(); + p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION); + p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT); + p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX); + p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2); + p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3); + p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4); + p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE); + p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); + p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); + p->add_separator(); + p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf + p->add_separator(); + p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); + + p->connect("id_pressed", this, "_add_menu_item"); + + play_button = memnew(Button); + play_button->set_position(Point2(25, 0)); + play_button->set_size(Point2(25, 15)); + add_child(play_button); + play_button->set_toggle_mode(true); + play_button->connect("pressed", this, "_play_toggled"); + + last_x = 50; + last_y = 50; + + property_editor = memnew(CustomPropertyEditor); + add_child(property_editor); + property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed"); + property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); + + h_scroll = memnew(HScrollBar); + v_scroll = memnew(VScrollBar); + + add_child(h_scroll); + add_child(v_scroll); + + h_scroll->connect("value_changed", this, "_scroll_moved"); + v_scroll->connect("value_changed", this, "_scroll_moved"); + + node_popup = memnew(PopupMenu); + add_child(node_popup); + node_popup->set_as_toplevel(true); + + master_anim_popup = memnew(PopupMenu); + add_child(master_anim_popup); + master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item"); + + node_popup->connect("id_pressed", this, "_node_menu_item"); + + updating_edit = false; + + edit_dialog = memnew(PopupPanel); + //edit_dialog->get_ok()->hide(); + //edit_dialog->get_cancel()->hide(); + add_child(edit_dialog); + + edit_option = memnew(OptionButton); + edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_option->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_option); + edit_option->connect("item_selected", this, "_edit_dialog_changedf"); + edit_option->hide(); + + for (int i = 0; i < 2; i++) { + edit_scroll[i] = memnew(HSlider); + edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_scroll[i]->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_scroll[i]); + edit_scroll[i]->hide(); + edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf"); + } + for (int i = 0; i < 4; i++) { + edit_line[i] = memnew(LineEdit); + edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_line[i]->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_line[i]); + edit_line[i]->hide(); + edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds"); + edit_line[i]->connect("text_entered", this, "_edit_dialog_changede"); + edit_label[i] = memnew(Label); + edit_dialog->add_child(edit_label[i]); + edit_label[i]->hide(); + } + + edit_button = memnew(Button); + edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_button->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_button); + edit_button->hide(); + edit_button->connect("pressed", this, "_edit_oneshot_start"); + + edit_check = memnew(CheckButton); + edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_check->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_check); + edit_check->hide(); + edit_check->connect("pressed", this, "_edit_dialog_changed"); + + file_dialog = memnew(EditorFileDialog); + file_dialog->set_enable_multiple_selection(true); + file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path()); + add_child(file_dialog); + file_dialog->connect("file_selected", this, "_file_dialog_selected"); + + filter_dialog = memnew(AcceptDialog); + filter_dialog->set_title(TTR("Edit Node Filters")); + add_child(filter_dialog); + + filter = memnew(Tree); + filter_dialog->add_child(filter); + //filter_dialog->set_child_rect(filter); + filter->connect("item_edited", this, "_filter_edited"); + + filter_button = memnew(Button); + filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); + filter_button->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(filter_button); + filter_button->hide(); + filter_button->set_text(TTR("Filters...")); + filter_button->connect("pressed", this, "_edit_filters"); + + set_clip_contents(true); +} + +void AnimationTreePlayerEditorPlugin::edit(Object *p_object) { + + anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object)); +} + +bool AnimationTreePlayerEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("AnimationTreePlayer"); +} + +void AnimationTreePlayerEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + //editor->hide_animation_player_editors(); + //editor->animation_panel_make_visible(true); + button->show(); + editor->make_bottom_panel_item_visible(anim_tree_editor); + anim_tree_editor->set_physics_process(true); + } else { + + if (anim_tree_editor->is_visible_in_tree()) + editor->hide_bottom_panel(); + button->hide(); + anim_tree_editor->set_physics_process(false); + } +} + +AnimationTreePlayerEditorPlugin::AnimationTreePlayerEditorPlugin(EditorNode *p_node) { + + editor = p_node; + anim_tree_editor = memnew(AnimationTreePlayerEditor); + anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); + + button = editor->add_bottom_panel_item(TTR("AnimationTree"), anim_tree_editor); + button->hide(); +} + +AnimationTreePlayerEditorPlugin::~AnimationTreePlayerEditorPlugin() { +} diff --git a/editor/plugins/animation_tree_player_editor_plugin.h b/editor/plugins/animation_tree_player_editor_plugin.h new file mode 100644 index 0000000000..d1c5f395e4 --- /dev/null +++ b/editor/plugins/animation_tree_player_editor_plugin.h @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H +#define ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/property_editor.h" +#include "scene/animation/animation_tree_player.h" +#include "scene/gui/button.h" +#include "scene/gui/popup.h" +#include "scene/gui/tree.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class AnimationTreePlayerEditor : public Control { + + GDCLASS(AnimationTreePlayerEditor, Control); + + static const char *_node_type_names[]; + + enum ClickType { + CLICK_NONE, + CLICK_NAME, + CLICK_NODE, + CLICK_INPUT_SLOT, + CLICK_OUTPUT_SLOT, + CLICK_PARAMETER + }; + + enum { + + MENU_GRAPH_CLEAR = 100, + MENU_IMPORT_ANIMATIONS = 101, + NODE_DISCONNECT, + NODE_RENAME, + NODE_ERASE, + NODE_ADD_INPUT, + NODE_DELETE_INPUT, + NODE_SET_AUTOADVANCE, + NODE_CLEAR_AUTOADVANCE + }; + + bool renaming_edit; + StringName edited_node; + bool updating_edit; + Popup *edit_dialog; + HSlider *edit_scroll[2]; + LineEdit *edit_line[4]; + OptionButton *edit_option; + Label *edit_label[4]; + Button *edit_button; + Button *filter_button; + CheckButton *edit_check; + EditorFileDialog *file_dialog; + int file_op; + + void _popup_edit_dialog(); + + void _setup_edit_dialog(const StringName &p_node); + PopupMenu *master_anim_popup; + PopupMenu *node_popup; + PopupMenu *add_popup; + HScrollBar *h_scroll; + VScrollBar *v_scroll; + MenuButton *add_menu; + + CustomPropertyEditor *property_editor; + + AnimationTreePlayer *anim_tree; + List<StringName> order; + Set<StringName> active_nodes; + + int last_x, last_y; + + Point2 offset; + ClickType click_type; + Point2 click_pos; + StringName click_node; + int click_slot; + Point2 click_motion; + ClickType rclick_type; + StringName rclick_node; + int rclick_slot; + + Button *play_button; + + Size2 _get_maximum_size(); + Size2 get_node_size(const StringName &p_node) const; + void _draw_node(const StringName &p_node); + + AcceptDialog *filter_dialog; + Tree *filter; + + void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color); + void _update_scrollbars(); + void _scroll_moved(float); + void _play_toggled(); + /* + void _node_param_changed(); + void _node_add_callback(); + void _node_add(VisualServer::AnimationTreeNodeType p_type); + void _node_edit_property(const StringName& p_node); +*/ + + void _master_anim_menu_item(int p_item); + void _node_menu_item(int p_item); + void _add_menu_item(int p_item); + + void _filter_edited(); + void _find_paths_for_filter(const StringName &p_node, Set<String> &paths); + void _edit_filters(); + + void _edit_oneshot_start(); + void _edit_dialog_animation_changed(); + void _edit_dialog_edit_animation(); + void _edit_dialog_changeds(String); + void _edit_dialog_changede(String); + void _edit_dialog_changedf(float); + void _edit_dialog_changed(); + void _dialog_changed() const; + ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const; + Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot); + + StringName _add_node(int p_item); + void _file_dialog_selected(String p_path); + +protected: + void _notification(int p_what); + void _gui_input(Ref<InputEvent> p_event); + static void _bind_methods(); + +public: + virtual Size2 get_minimum_size() const; + void edit(AnimationTreePlayer *p_anim_tree); + AnimationTreePlayerEditor(); +}; + +class AnimationTreePlayerEditorPlugin : public EditorPlugin { + + GDCLASS(AnimationTreePlayerEditorPlugin, EditorPlugin); + + AnimationTreePlayerEditor *anim_tree_editor; + EditorNode *editor; + Button *button; + +public: + virtual String get_name() const { return "AnimTree"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + AnimationTreePlayerEditorPlugin(EditorNode *p_node); + ~AnimationTreePlayerEditorPlugin(); +}; + +#endif // ANIMATION_TREE_EDITOR_PLUGIN_H diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 4ed2b051aa..66770d98e5 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -421,7 +421,16 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { int cstatus = download->get_http_client_status(); if (cstatus == HTTPClient::STATUS_BODY) { - status->set_text(vformat(TTR("Downloading (%s / %s)..."), String::humanize_size(download->get_downloaded_bytes()), String::humanize_size(download->get_body_size()))); + if (download->get_body_size() > 0) { + status->set_text( + vformat( + TTR("Downloading (%s / %s)..."), + String::humanize_size(download->get_downloaded_bytes()), + String::humanize_size(download->get_body_size()))); + } else { + // Total file size is unknown, so it cannot be displayed + status->set_text(TTR("Downloading...")); + } } if (cstatus != prev_status) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 145a2ef9bb..72248b62a3 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2276,14 +2276,14 @@ void CanvasItemEditor::_draw_grid() { real_grid_offset = grid_offset; } - const Color grid_minor_color = get_color("grid_minor_color", "Editor"); + const Color grid_color = EditorSettings::get_singleton()->get("editors/2d/grid_color"); if (grid_step.x != 0) { for (int i = 0; i < s.width; i++) { int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i, 0)).x - real_grid_offset.x) / (grid_step.x * Math::pow(2.0, grid_step_multiplier)))); if (i == 0) last_cell = cell; if (last_cell != cell) - viewport->draw_line(Point2(i, 0), Point2(i, s.height), grid_minor_color); + viewport->draw_line(Point2(i, 0), Point2(i, s.height), grid_color); last_cell = cell; } } @@ -2294,7 +2294,7 @@ void CanvasItemEditor::_draw_grid() { if (i == 0) last_cell = cell; if (last_cell != cell) - viewport->draw_line(Point2(0, i), Point2(s.width, i), grid_minor_color); + viewport->draw_line(Point2(0, i), Point2(s.width, i), grid_color); last_cell = cell; } } diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 49c54ad67d..f5bdf77973 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -616,8 +616,8 @@ void CurveEditor::_draw() { Vector2 min_edge = get_world_pos(Vector2(0, view_size.y)); Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0)); - const Color grid_color0 = get_color("grid_major_color", "Editor"); - const Color grid_color1 = get_color("grid_minor_color", "Editor"); + const Color grid_color0 = Color(1.0, 1.0, 1.0, 0.15); + const Color grid_color1 = Color(1.0, 1.0, 1.0, 0.07); draw_line(Vector2(min_edge.x, curve.get_min_value()), Vector2(max_edge.x, curve.get_min_value()), grid_color0); draw_line(Vector2(max_edge.x, curve.get_max_value()), Vector2(min_edge.x, curve.get_max_value()), grid_color0); draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color0); diff --git a/editor/plugins/cube_grid_theme_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 68d5ea5247..a4d2905c0e 100644 --- a/editor/plugins/cube_grid_theme_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* cube_grid_theme_editor_plugin.cpp */ +/* mesh_library_editor_plugin.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "cube_grid_theme_editor_plugin.h" +#include "mesh_library_editor_plugin.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" @@ -38,6 +38,7 @@ #include "scene/3d/physics_body.h" #include "scene/main/viewport.h" #include "scene/resources/packed_scene.h" +#include "spatial_editor_plugin.h" void MeshLibraryEditor::edit(const Ref<MeshLibrary> &p_theme) { @@ -241,21 +242,20 @@ MeshLibraryEditor::MeshLibraryEditor(EditorNode *p_editor) { add_child(file); file->connect("file_selected", this, "_import_scene_cbk"); - Panel *panel = memnew(Panel); - panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); - add_child(panel); - MenuButton *options = memnew(MenuButton); - panel->add_child(options); - options->set_position(Point2(1, 1)); - options->set_text("Theme"); - options->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM); - options->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Import from Scene"), MENU_OPTION_IMPORT_FROM_SCENE); - options->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE); - options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true); - options->get_popup()->connect("id_pressed", this, "_menu_cbk"); - menu = options; + menu = memnew(MenuButton); + SpatialEditor::get_singleton()->add_control_to_menu_panel(menu); + menu->set_position(Point2(1, 1)); + menu->set_text("Mesh Library"); + menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshLibrary", "EditorIcons")); + menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM); + menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM); + menu->get_popup()->add_separator(); + menu->get_popup()->add_item(TTR("Import from Scene"), MENU_OPTION_IMPORT_FROM_SCENE); + menu->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE); + menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true); + menu->get_popup()->connect("id_pressed", this, "_menu_cbk"); + menu->hide(); + editor = p_editor; cd = memnew(ConfirmationDialog); add_child(cd); @@ -278,10 +278,13 @@ bool MeshLibraryEditorPlugin::handles(Object *p_node) const { void MeshLibraryEditorPlugin::make_visible(bool p_visible) { - if (p_visible) + if (p_visible) { theme_editor->show(); - else + theme_editor->get_menu_button()->show(); + } else { theme_editor->hide(); + theme_editor->get_menu_button()->hide(); + } } MeshLibraryEditorPlugin::MeshLibraryEditorPlugin(EditorNode *p_node) { diff --git a/editor/plugins/cube_grid_theme_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h index 36a8f8f203..dc2d488bd6 100644 --- a/editor/plugins/cube_grid_theme_editor_plugin.h +++ b/editor/plugins/mesh_library_editor_plugin.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* cube_grid_theme_editor_plugin.h */ +/* mesh_library_editor_plugin.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CUBE_GRID_THEME_EDITOR_PLUGIN_H -#define CUBE_GRID_THEME_EDITOR_PLUGIN_H +#ifndef MESH_LIBRARY_EDITOR_PLUGIN_H +#define MESH_LIBRARY_EDITOR_PLUGIN_H #include "editor/editor_node.h" #include "scene/resources/mesh_library.h" @@ -65,6 +65,8 @@ protected: static void _bind_methods(); public: + MenuButton *get_menu_button() const { return menu; } + void edit(const Ref<MeshLibrary> &p_theme); static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true); @@ -88,4 +90,4 @@ public: MeshLibraryEditorPlugin(EditorNode *p_node); }; -#endif // CUBE_GRID_THEME_EDITOR_PLUGIN_H +#endif // MESH_LIBRARY_EDITOR_PLUGIN_H diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 9782b9d1f4..7ad117deb6 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -4352,10 +4352,13 @@ void SpatialEditor::_menu_item_pressed(int p_option) { bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option)); - is_checked = !is_checked; - VisualServer::get_singleton()->instance_set_visible(origin_instance, is_checked); + origin_enabled = !is_checked; + VisualServer::get_singleton()->instance_set_visible(origin_instance, origin_enabled); + // Update the grid since its appearance depends on whether the origin is enabled + _finish_grid(); + _init_grid(); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), is_checked); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), origin_enabled); } break; case MENU_VIEW_GRID: { @@ -4778,7 +4781,11 @@ void SpatialEditor::_init_grid() { Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); Color line_color = secondary_grid_color; - if (j % primary_grid_steps == 0) { + if (origin_enabled && j == 0) { + // Don't draw the center lines of the grid if the origin is enabled + // The origin would overlap the grid lines in this case, causing flickering + continue; + } else if (j % primary_grid_steps == 0) { line_color = primary_grid_color; } @@ -5187,9 +5194,10 @@ void SpatialEditor::_register_all_gizmos() { register_gizmo_plugin(Ref<MeshInstanceSpatialGizmoPlugin>(memnew(MeshInstanceSpatialGizmoPlugin))); register_gizmo_plugin(Ref<SoftBodySpatialGizmoPlugin>(memnew(SoftBodySpatialGizmoPlugin))); register_gizmo_plugin(Ref<Sprite3DSpatialGizmoPlugin>(memnew(Sprite3DSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin))); register_gizmo_plugin(Ref<SkeletonSpatialGizmoPlugin>(memnew(SkeletonSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin))); register_gizmo_plugin(Ref<RayCastSpatialGizmoPlugin>(memnew(RayCastSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<SpringArmSpatialGizmoPlugin>(memnew(SpringArmSpatialGizmoPlugin))); register_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin))); register_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin))); register_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin))); diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 4057145c2f..5850c0dbf1 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -508,6 +508,7 @@ private: RID origin; RID origin_instance; + bool origin_enabled; RID grid[3]; RID grid_instance[3]; bool grid_visible[3]; //currently visible diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index d13e01dc1e..4a9cbfe535 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -70,7 +70,7 @@ void TextureRegionEditor::_region_draw() { VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), Transform2D()); if (snap_mode == SNAP_GRID) { - Color grid_color = get_color("grid_major_color", "Editor"); + Color grid_color = Color(1.0, 1.0, 1.0, 0.15); Size2 s = edit_draw->get_size(); int last_cell = 0; diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 3d14db7d0e..0ba42cb101 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -426,6 +426,9 @@ void TileMapEditor::_update_palette() { Ref<Texture> tex = tileset->tile_get_texture(entries[i].id); if (tex.is_valid()) { + Color color = tileset->tile_get_modulate(entries[i].id); + palette->set_item_icon_modulate(palette->get_item_count() - 1, color); + Rect2 region = tileset->tile_get_region(entries[i].id); if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(entries[i].id) == TileSet::ATLAS_TILE) { @@ -759,10 +762,13 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h rect.position = p_xform.xform(rect.position); rect.size *= sc; + Color modulate = node->get_tileset()->tile_get_modulate(p_cell); + modulate.a = 0.5; + if (r.has_no_area()) - canvas_item_editor->draw_texture_rect(t, rect, false, Color(1, 1, 1, 0.5), p_transpose); + canvas_item_editor->draw_texture_rect(t, rect, false, modulate, p_transpose); else - canvas_item_editor->draw_texture_rect_region(t, rect, r, Color(1, 1, 1, 0.5), p_transpose); + canvas_item_editor->draw_texture_rect_region(t, rect, r, modulate, p_transpose); } void TileMapEditor::_draw_fill_preview(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 490ebeca26..1f891fe0fd 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -900,12 +900,13 @@ void TileSetEditor::_on_workspace_overlay_draw() { else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) c = COLOR_ATLAS; c.a = tile_names_opacity; + String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); Ref<Font> font = get_font("font", "Label"); - region.set_size(font->get_string_size(tileset->tile_get_name(t_id))); + region.set_size(font->get_string_size(tile_id_name)); workspace_overlay->draw_rect(region, c); region.position.y += region.size.y - 2; c = Color(0.1, 0.1, 0.1, tile_names_opacity); - workspace_overlay->draw_string(font, region.position, tileset->tile_get_name(t_id), c); + workspace_overlay->draw_string(font, region.position, tile_id_name, c); } } } diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 3cebbb5d21..019d5d382c 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "project_export.h" + #include "compressed_translation.h" #include "editor_data.h" #include "editor_node.h" @@ -389,7 +390,6 @@ void ProjectExportDialog::_patch_deleted() { void ProjectExportDialog::_update_parameters(const String &p_edited_property) { _edit_preset(presets->get_current()); - parameters->update_tree(); } void ProjectExportDialog::_runnable_pressed() { @@ -842,12 +842,10 @@ ProjectExportDialog::ProjectExportDialog() { settings_vb->add_child(sections); sections->set_v_size_flags(SIZE_EXPAND_FILL); - parameters = memnew(PropertyEditor); + parameters = memnew(EditorInspector); sections->add_child(parameters); parameters->set_name(TTR("Options")); - parameters->hide_top_label(); parameters->set_v_size_flags(SIZE_EXPAND_FILL); - parameters->connect("property_edited", this, "_update_parameters"); VBoxContainer *resources_vb = memnew(VBoxContainer); diff --git a/editor/project_export.h b/editor/project_export.h index b62254974d..1f8723febd 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -31,26 +31,27 @@ #ifndef PROJECT_EXPORT_SETTINGS_H #define PROJECT_EXPORT_SETTINGS_H +#include "core/os/dir_access.h" +#include "core/os/thread.h" +#include "editor/editor_export.h" #include "editor/editor_file_dialog.h" -#include "os/dir_access.h" -#include "os/thread.h" +#include "editor/editor_file_system.h" +#include "editor/editor_inspector.h" #include "scene/gui/button.h" +#include "scene/gui/check_button.h" #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" #include "scene/gui/label.h" #include "scene/gui/link_button.h" +#include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/rich_text_label.h" +#include "scene/gui/slider.h" #include "scene/gui/tab_container.h" #include "scene/gui/tree.h" #include "scene/main/timer.h" -#include "editor/editor_file_system.h" -#include "editor_export.h" -#include "property_editor.h" -#include "scene/gui/slider.h" - class EditorNode; class ProjectExportDialog : public ConfirmationDialog { @@ -64,12 +65,9 @@ private: ItemList *presets; LineEdit *name; - PropertyEditor *parameters; + EditorInspector *parameters; CheckButton *runnable; - //EditorFileDialog *pck_export; - //EditorFileDialog *file_export; - Button *button_export; bool updating; diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 970302e058..65b2e2301b 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -1007,6 +1007,7 @@ void ProjectSettingsEditor::_copy_to_platform_about_to_show() { Set<String> presets; + presets.insert("bptc"); presets.insert("s3tc"); presets.insert("etc"); presets.insert("etc2"); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 97147535c0..746e1cd28f 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -1240,6 +1240,9 @@ void ScriptEditorDebugger::stop() { if (connection.is_valid()) { EditorNode::get_log()->add_message("** Debug Process Stopped **"); connection.unref(); + + reason->set_text(""); + reason->set_tooltip(""); } pending_in_queue = 0; diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index 64fdc778d0..64638cdb1e 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -677,7 +677,7 @@ void EditorSpatialGizmo::_bind_methods() { ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorSpatialGizmo::add_collision_segments); ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorSpatialGizmo::add_collision_triangles); ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale"), &EditorSpatialGizmo::add_unscaled_billboard, DEFVAL(1)); - ClassDB::bind_method(D_METHOD("add_handles", "handles", "billboard", "secondary"), &EditorSpatialGizmo::add_handles, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "billboard", "secondary"), &EditorSpatialGizmo::add_handles, DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_spatial_node", "node"), &EditorSpatialGizmo::_set_spatial_node); ClassDB::bind_method(D_METHOD("clear"), &EditorSpatialGizmo::clear); ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorSpatialGizmo::set_hidden); @@ -1337,6 +1337,48 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { p_gizmo->add_collision_segments(lines); p_gizmo->add_unscaled_billboard(icon, 0.05); p_gizmo->add_handles(handles, get_material("handles")); + + ClippedCamera *clipcam = Object::cast_to<ClippedCamera>(camera); + if (clipcam) { + Spatial *parent = Object::cast_to<Spatial>(camera->get_parent()); + if (!parent) { + return; + } + Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized(); + Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized(); + Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); + Vector3 cam_pos = camera->get_global_transform().origin; + Vector3 parent_pos = parent->get_global_transform().origin; + + Plane parent_plane(parent_pos, cam_normal); + Vector3 ray_from = parent_plane.project(cam_pos); + + lines.clear(); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); + + lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); + lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); + + lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); + lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); + + lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); + + if (parent_plane.distance_to(cam_pos) < 0) { + lines.push_back(ray_from); + lines.push_back(cam_pos); + } + + Transform local = camera->get_global_transform().affine_inverse(); + for (int i = 0; i < lines.size(); i++) { + lines.write[i] = local.xform(lines[i]); + } + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + } } ////// @@ -1920,6 +1962,38 @@ void RayCastSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { ///// +void SpringArmSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + SpringArm *spring_arm = Object::cast_to<SpringArm>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector<Vector3> lines; + + lines.push_back(Vector3()); + lines.push_back(Vector3(0, 0, 1.0) * spring_arm->get_length()); + + Ref<SpatialMaterial> material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); +} + +SpringArmSpatialGizmoPlugin::SpringArmSpatialGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); +} + +bool SpringArmSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<SpringArm>(p_spatial) != NULL; +} + +String SpringArmSpatialGizmoPlugin::get_name() const { + return "SpringArm"; +} + +///// + VehicleWheelSpatialGizmoPlugin::VehicleWheelSpatialGizmoPlugin() { Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h index 877590b91d..6f29e9d999 100644 --- a/editor/spatial_editor_gizmos.h +++ b/editor/spatial_editor_gizmos.h @@ -49,6 +49,7 @@ #include "scene/3d/ray_cast.h" #include "scene/3d/reflection_probe.h" #include "scene/3d/room_instance.h" +#include "scene/3d/spring_arm.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body.h" #include "scene/3d/visibility_notifier.h" @@ -196,6 +197,18 @@ public: RayCastSpatialGizmoPlugin(); }; +class SpringArmSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { + + GDCLASS(SpringArmSpatialGizmoPlugin, EditorSpatialGizmoPlugin); + +public: + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + SpringArmSpatialGizmoPlugin(); +}; + class VehicleWheelSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { GDCLASS(VehicleWheelSpatialGizmoPlugin, EditorSpatialGizmoPlugin); @@ -330,14 +343,12 @@ public: }; class CollisionPolygonSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(CollisionPolygonSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: bool has_gizmo(Spatial *p_spatial); String get_name() const; void redraw(EditorSpatialGizmo *p_gizmo); - CollisionPolygonSpatialGizmoPlugin(); }; |