/*************************************************************************/ /* editor_node.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ /* */ /* 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 "version.h" #include "editor_node.h" #include "print_string.h" #include "editor_icons.h" #include "editor_fonts.h" #include "editor_help.h" #include "core/io/resource_saver.h" #include "core/io/resource_loader.h" #include "servers/physics_2d_server.h" #include "scene/resources/packed_scene.h" #include "editor_settings.h" #include "io_plugins/editor_import_collada.h" #include "io_plugins/editor_scene_importer_fbxconv.h" #include "globals.h" #include #include "object_type_db.h" #include "os/keyboard.h" #include "os/os.h" #include "os/file_access.h" #include "message_queue.h" #include "path_remap.h" #include "translation.h" #include "pvrtc_compress.h" #include "editor_file_system.h" #include "register_exporters.h" #include "bind/core_bind.h" #include "io/zip_io.h" #include "io/config_file.h" #include "animation_editor.h" #include "io/stream_peer_ssl.h" // plugins #include "plugins/sprite_frames_editor_plugin.h" #include "plugins/texture_region_editor_plugin.h" #include "plugins/canvas_item_editor_plugin.h" #include "asset_library_editor_plugin.h" #include "plugins/spatial_editor_plugin.h" #include "plugins/sample_editor_plugin.h" #include "plugins/texture_editor_plugin.h" #include "plugins/material_editor_plugin.h" #include "plugins/sample_library_editor_plugin.h" #include "plugins/sample_player_editor_plugin.h" #include "plugins/camera_editor_plugin.h" #include "plugins/style_box_editor_plugin.h" #include "plugins/resource_preloader_editor_plugin.h" #include "plugins/item_list_editor_plugin.h" #include "plugins/stream_editor_plugin.h" #include "plugins/multimesh_editor_plugin.h" #include "plugins/mesh_instance_editor_plugin.h" #include "plugins/mesh_editor_plugin.h" #include "plugins/theme_editor_plugin.h" #include "plugins/tile_map_editor_plugin.h" #include "plugins/cube_grid_theme_editor_plugin.h" #include "plugins/shader_editor_plugin.h" #include "plugins/shader_graph_editor_plugin.h" #include "plugins/path_editor_plugin.h" #include "plugins/rich_text_editor_plugin.h" #include "plugins/collision_polygon_editor_plugin.h" #include "plugins/collision_polygon_2d_editor_plugin.h" #include "plugins/script_editor_plugin.h" #include "plugins/path_2d_editor_plugin.h" #include "plugins/particles_editor_plugin.h" #include "plugins/particles_2d_editor_plugin.h" #include "plugins/animation_tree_editor_plugin.h" #include "plugins/tile_set_editor_plugin.h" #include "plugins/animation_player_editor_plugin.h" #include "plugins/baked_light_editor_plugin.h" #include "plugins/polygon_2d_editor_plugin.h" #include "plugins/navigation_polygon_editor_plugin.h" #include "plugins/light_occluder_2d_editor_plugin.h" #include "plugins/color_ramp_editor_plugin.h" #include "plugins/collision_shape_2d_editor_plugin.h" #include "main/input_default.h" // end #include "tools/editor/io_plugins/editor_texture_import_plugin.h" #include "tools/editor/io_plugins/editor_scene_import_plugin.h" #include "tools/editor/io_plugins/editor_font_import_plugin.h" #include "tools/editor/io_plugins/editor_sample_import_plugin.h" #include "tools/editor/io_plugins/editor_translation_import_plugin.h" #include "tools/editor/io_plugins/editor_bitmask_import_plugin.h" #include "tools/editor/io_plugins/editor_mesh_import_plugin.h" #include "tools/editor/io_plugins/editor_export_scene.h" #include "plugins/editor_preview_plugins.h" #include "editor_initialize_ssl.h" #include "script_editor_debugger.h" EditorNode *EditorNode::singleton=NULL; void EditorNode::_update_scene_tabs() { bool show_rb = EditorSettings::get_singleton()->get("global/show_script_in_scene_tabs"); scene_tabs->clear_tabs(); Ref script_icon = gui_base->get_icon("Script","EditorIcons"); for(int i=0;i icon; if (type!=String()) { if (!gui_base->has_icon(type,"EditorIcons")) { type="Node"; } icon=gui_base->get_icon(type,"EditorIcons"); } int current = editor_data.get_edited_scene(); bool unsaved = (i==current)?saved_version!=editor_data.get_undo_redo().get_version():editor_data.get_scene_version(i)!=0; scene_tabs->add_tab(editor_data.get_scene_title(i)+(unsaved?"(*)":""),icon); if (show_rb && editor_data.get_scene_root_script(i).is_valid()) { scene_tabs->set_tab_right_button(i,script_icon); } } scene_tabs->set_current_tab(editor_data.get_edited_scene()); scene_tabs->ensure_tab_visible(editor_data.get_edited_scene()); } void EditorNode::_update_title() { String appname = Globals::get_singleton()->get("application/name"); String title = appname.empty()?String(VERSION_FULL_NAME):String(_MKSTR(VERSION_NAME) + String(" - ") + appname); String edited = editor_data.get_edited_scene_root()?editor_data.get_edited_scene_root()->get_filename():String(); if (!edited.empty()) title+=" - " + String(edited.get_file()); if (unsaved_cache) title+=" (*)"; OS::get_singleton()->set_window_title(title); } void EditorNode::_unhandled_input(const InputEvent& p_event) { if (p_event.type==InputEvent::KEY && p_event.key.pressed && !p_event.key.echo && !gui_base->get_viewport()->gui_has_modal_stack()) { if (ED_IS_SHORTCUT("editor/fullscreen_mode", p_event)) { if (distraction_free_mode) { distraction_free_mode = false; _update_top_menu_visibility(); } else { set_docks_visible(!get_docks_visible()); } } if (ED_IS_SHORTCUT("editor/distraction_free_mode", p_event)) { set_distraction_free_mode(!get_distraction_free_mode()); } switch(p_event.key.scancode) { /*case KEY_F1: if (!p_event.key.mod.shift && !p_event.key.mod.command) _editor_select(EDITOR_SCRIPT); break;*/ case KEY_F1: if (!p_event.key.mod.shift && !p_event.key.mod.command) _editor_select(EDITOR_2D); break; case KEY_F2: if (!p_event.key.mod.shift && !p_event.key.mod.command) _editor_select(EDITOR_3D); break; case KEY_F3: if (!p_event.key.mod.shift && !p_event.key.mod.command) _editor_select(EDITOR_SCRIPT); break; /* case KEY_F5: _menu_option_confirm((p_event.key.mod.control&&p_event.key.mod.shift)?RUN_PLAY_CUSTOM_SCENE:RUN_PLAY,true); break; case KEY_F6: _menu_option_confirm(RUN_PLAY_SCENE,true); break; //case KEY_F7: _menu_option_confirm(RUN_PAUSE,true); break; case KEY_F8: _menu_option_confirm(RUN_STOP,true); break;*/ } } } void EditorNode::_notification(int p_what) { if (p_what==NOTIFICATION_EXIT_TREE) { editor_data.save_editor_external_data(); FileAccess::set_file_close_fail_notify_callback(NULL); log->deinit(); // do not get messages anymore } if (p_what==NOTIFICATION_PROCESS) { //force the whole tree viewport #if 0 { Rect2 grect = scene_root_base->get_global_rect(); Rect2 grectsrp = scene_root_parent->get_global_rect(); if (grect!=grectsrp) { scene_root_parent->set_pos(grect.pos); scene_root_parent->set_size(grect.size); } } #endif if (opening_prev && confirmation->is_hidden()) opening_prev=false; if (unsaved_cache != (saved_version!=editor_data.get_undo_redo().get_version())) { unsaved_cache = (saved_version!=editor_data.get_undo_redo().get_version()); _update_title(); } if (last_checked_version!=editor_data.get_undo_redo().get_version()) { _update_scene_tabs(); last_checked_version=editor_data.get_undo_redo().get_version(); } //get_root_node()->set_rect(viewport->get_global_rect()); //update the circle uint64_t frame = OS::get_singleton()->get_frames_drawn(); uint32_t tick = OS::get_singleton()->get_ticks_msec(); if (frame!=circle_step_frame && (tick-circle_step_msec)>(1000/8)) { circle_step++; if (circle_step>=8) circle_step=0; circle_step_msec=tick; circle_step_frame=frame+1; update_menu->set_icon(gui_base->get_icon("Progress"+itos(circle_step+1),"EditorIcons")); } scene_root->set_size_override(true,Size2(Globals::get_singleton()->get("display/width"),Globals::get_singleton()->get("display/height"))); editor_selection->update(); { uint32_t p32 = AudioServer::get_singleton()->read_output_peak()>>8; float peak = p32==0? -80 : Math::linear2db(p32 / 65535.0); if (peak<-80) peak=-80; float vu = audio_vu->get_val(); if (peak > vu) { audio_vu->set_val(peak); } else { float new_vu = vu - get_process_delta_time()*70.0; if (new_vu<-80) new_vu=-80; if (new_vu !=-80 && vu !=-80) audio_vu->set_val(new_vu); } } } if (p_what==NOTIFICATION_ENTER_TREE) { //MessageQueue::get_singleton()->push_call(this,"_get_scene_metadata"); get_tree()->set_editor_hint(true); get_tree()->get_root()->set_as_audio_listener(false); get_tree()->get_root()->set_as_audio_listener_2d(false); get_tree()->set_auto_accept_quit(false); get_tree()->connect("files_dropped",this,"_dropped_files"); //VisualServer::get_singleton()->viewport_set_hide_canvas(editor->get_scene_root()->get_viewport(),false); //import_monitor->scan_changes(); } if (p_what==NOTIFICATION_EXIT_TREE) { editor_data.clear_edited_scenes(); } if (p_what==NOTIFICATION_READY) { VisualServer::get_singleton()->viewport_set_hide_scenario(get_scene_root()->get_viewport(),true); VisualServer::get_singleton()->viewport_set_hide_canvas(get_scene_root()->get_viewport(),true); VisualServer::get_singleton()->viewport_set_disable_environment(get_viewport()->get_viewport_rid(),true); _editor_select(EDITOR_3D); if (defer_load_scene!="") { load_scene(defer_load_scene); defer_load_scene=""; } if (defer_translatable!="") { Error ok = save_translatable_strings(defer_translatable); if (ok!=OK) OS::get_singleton()->set_exit_code(255); defer_translatable=""; get_tree()->quit(); } /* if (defer_optimize!="") { Error ok = save_optimized_copy(defer_optimize,defer_optimize_preset); defer_optimize_preset=""; if (ok!=OK) OS::get_singleton()->set_exit_code(255); get_scene()->quit(); } */ /* // moved to "_sources_changed" if (export_defer.platform!="") { project_export_settings->export_platform(export_defer.platform,export_defer.path,export_defer.debug,export_defer.password,true); export_defer.platform=""; } */ } if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_IN) { /* List > cached; ResourceCache::get_cached_resources(&cached); bool changes=false; for(List >::Element *E=cached.front();E;E=E->next()) { if (!E->get()->can_reload_from_file()) continue; if (E->get()->get_path().find("::")!=-1) continue; uint64_t mt = FileAccess::get_modified_time(E->get()->get_path()); if (mt!=E->get()->get_last_modified_time()) { changes=true; break; } } sources_button->get_popup()->set_item_disabled(sources_button->get_popup()->get_item_index(DEPENDENCY_UPDATE_LOCAL),!changes); if (changes && sources_button->get_popup()->is_item_disabled(sources_button->get_popup()->get_item_index(DEPENDENCY_UPDATE_IMPORTED))) { sources_button->set_icon(gui_base->get_icon("DependencyLocalChanged","EditorIcons")); } */ if (bool(EDITOR_DEF("resources/auto_reload_modified_images",true))) { _menu_option_confirm(DEPENDENCY_LOAD_CHANGED_IMAGES,true); } waiting_for_sources_changed=true; EditorFileSystem::get_singleton()->scan_sources(); } if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST) { _menu_option_confirm(FILE_QUIT, false); }; if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { scene_tabs->set_tab_close_display_policy( (bool(EDITOR_DEF("global/always_show_close_button_in_scene_tabs", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY) ); } } void EditorNode::_fs_changed() { for(Set::Element *E=file_dialogs.front();E;E=E->next()) { E->get()->invalidate(); } for(Set::Element *E=editor_file_dialogs.front();E;E=E->next()) { E->get()->invalidate(); } if (export_defer.platform!="") { project_export_settings->export_platform(export_defer.platform,export_defer.path,export_defer.debug,export_defer.password,true); export_defer.platform=""; } } void EditorNode::_sources_changed(bool p_exist) { if (p_exist && bool(EditorSettings::get_singleton()->get("import/automatic_reimport_on_sources_changed"))) { p_exist=false; List changed_sources; EditorFileSystem::get_singleton()->get_changed_sources(&changed_sources); EditorProgress ep("reimport",TTR("Re-Importing"),changed_sources.size()); int step_idx=0; for(List::Element *E=changed_sources.front();E;E=E->next()) { ep.step(TTR("Importing:")+" "+E->get(),step_idx++); Ref rimd = ResourceLoader::load_import_metadata(E->get()); ERR_CONTINUE(rimd.is_null()); String editor = rimd->get_editor(); if (editor.begins_with("texture_")) { editor="texture"; //compatibility fix for old versions } Ref eip = EditorImportExport::get_singleton()->get_import_plugin_by_name(editor); ERR_CONTINUE(eip.is_null()); Error err = eip->import(E->get(),rimd); if (err!=OK) { EditorNode::add_io_error("Error Re Importing:\n "+E->get()); } } EditorFileSystem::get_singleton()->scan_sources(); waiting_for_sources_changed=false; return; } if (p_exist) { sources_button->set_icon(gui_base->get_icon("DependencyChanged","EditorIcons")); sources_button->set_disabled(false); } else { sources_button->set_icon(gui_base->get_icon("DependencyOk","EditorIcons")); sources_button->set_disabled(true); } waiting_for_sources_changed=false; } void EditorNode::_vp_resized() { } void EditorNode::_rebuild_import_menu() { PopupMenu* p = import_menu->get_popup(); p->clear(); //p->add_item(TTR("Node From Scene"), FILE_IMPORT_SUBSCENE); //p->add_separator(); for (int i = 0; i < editor_import_export->get_import_plugin_count(); i++) { p->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(), IMPORT_PLUGIN_BASE + i); } //p->add_separator(); //p->add_item(TTR("Re-Import.."), SETTINGS_IMPORT); } void EditorNode::_node_renamed() { if (property_editor) property_editor->update_tree(); } Error EditorNode::load_resource(const String& p_scene) { RES res = ResourceLoader::load(p_scene); ERR_FAIL_COND_V(!res.is_valid(),ERR_CANT_OPEN); edit_resource(res); return OK; } void EditorNode::edit_resource(const Ref& p_resource) { _resource_selected(p_resource,""); } void EditorNode::edit_node(Node *p_node) { push_item(p_node); } void EditorNode::open_resource(const String& p_type) { file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List extensions; ResourceLoader::get_recognized_extensions_for_type(p_type,&extensions); file->clear_filters(); for(int i=0;iadd_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); } //file->set_current_path(current_path); file->popup_centered_ratio(); current_option=RESOURCE_LOAD; } void EditorNode::save_resource_in_path(const Ref& p_resource,const String& p_path) { editor_data.apply_changes_in_editors(); int flg=0; if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources")) flg|=ResourceSaver::FLAG_COMPRESS; if (EditorSettings::get_singleton()->get("on_save/save_paths_as_relative")) flg|=ResourceSaver::FLAG_RELATIVE_PATHS; String path = Globals::get_singleton()->localize_path(p_path); Error err = ResourceSaver::save(path,p_resource,flg|ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS); if (err!=OK) { accept->set_text(TTR("Error saving resource!")); accept->popup_centered_minsize(); return; } // EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type()); ((Resource*)p_resource.ptr())->set_path(path); emit_signal("resource_saved",p_resource); } void EditorNode::save_resource(const Ref& p_resource) { if (p_resource->get_path().is_resource_file()) { save_resource_in_path(p_resource,p_resource->get_path()); } else { save_resource_as(p_resource); } } void EditorNode::save_resource_as(const Ref& p_resource,const String& p_at_path) { file->set_mode(EditorFileDialog::MODE_SAVE_FILE); bool relpaths = (p_resource->has_meta("__editor_relpaths__") && p_resource->get_meta("__editor_relpaths__").operator bool()); current_option=RESOURCE_SAVE_AS; List extensions; Ref sd = memnew( PackedScene ); ResourceSaver::get_recognized_extensions(p_resource,&extensions); file->clear_filters(); List preferred; for(int i=0;iis_type("Script") && (extensions[i]=="tres" || extensions[i]=="res" || extensions[i]=="xml")) { //this serves no purpose and confused people continue; } file->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); preferred.push_back(extensions[i]); } //file->set_current_path(current_path); if (p_at_path!=String()) { file->set_current_dir(p_at_path); if (p_resource->get_path().is_resource_file()) { file->set_current_file(p_resource->get_path().get_file()); } else { if (extensions.size()) { file->set_current_file("new_"+p_resource->get_type().to_lower()+"."+preferred.front()->get().to_lower()); } else { file->set_current_file(String()); } } } else if (p_resource->get_path()!="") { file->set_current_path(p_resource->get_path()); if (extensions.size()) { String ext=p_resource->get_path().extension().to_lower(); if (extensions.find(ext)==NULL) { file->set_current_path(p_resource->get_path().replacen("."+ext,"."+extensions.front()->get())); } } } else if (preferred.size()) { String existing; if (extensions.size()) { existing="new_"+p_resource->get_type().to_lower()+"."+preferred.front()->get().to_lower(); } file->set_current_path(existing); } file->popup_centered_ratio(); file->set_title(TTR("Save Resource As..")); } void EditorNode::_menu_option(int p_option) { _menu_option_confirm(p_option,false); } void EditorNode::_menu_confirm_current() { _menu_option_confirm(current_option,true); } void EditorNode::_dialog_display_file_error(String p_file,Error p_error) { if (p_error) { current_option=-1; //accept->"()->hide(); accept->get_ok()->set_text(TTR("I see..")); switch(p_error) { case ERR_FILE_CANT_WRITE: { accept->set_text(TTR("Can't open file for writing:")+" "+p_file.extension()); } break; case ERR_FILE_UNRECOGNIZED: { accept->set_text(TTR("Requested file format unknown:")+" "+p_file.extension()); } break; default: { accept->set_text(TTR("Error while saving.")); }break; } accept->popup_centered_minsize(); } } void EditorNode::_get_scene_metadata(const String& p_file) { Node *scene = editor_data.get_edited_scene_root(); if (!scene) return; String path = EditorSettings::get_singleton()->get_project_settings_path().plus_file(p_file.get_file()+"-editstate-"+p_file.md5_text()+".cfg"); Ref cf; cf.instance(); Error err = cf->load(path); if (err!=OK) return; //must not exist List esl; cf->get_section_keys("editor_states",&esl); Dictionary md; for (List::Element *E=esl.front();E;E=E->next()) { Variant st=cf->get_value("editor_states",E->get()); if (st.get_type()) { md[E->get()]=st; } } editor_data.set_editor_states(md); } void EditorNode::_set_scene_metadata(const String& p_file) { Node *scene = editor_data.get_edited_scene_root(); if (!scene) return; scene->set_meta("__editor_run_settings__",Variant()); //clear it (no point in keeping it) scene->set_meta("__editor_plugin_states__",Variant()); String path = EditorSettings::get_singleton()->get_project_settings_path().plus_file(p_file.get_file()+"-editstate-"+p_file.md5_text()+".cfg"); Ref cf; cf.instance(); Dictionary md = editor_data.get_editor_states(); List keys; md.get_key_list(&keys); for(List::Element *E=keys.front();E;E=E->next()) { cf->set_value("editor_states",E->get(),md[E->get()]); } Error err = cf->save(path); ERR_FAIL_COND(err!=OK); } static Error _fix_object_paths(Object* obj, Node* root, String save_path) { Globals* g = Globals::get_singleton(); String import_dir = root->get_meta("__editor_import_file__"); import_dir = import_dir.get_base_dir(); import_dir = DirAccess::normalize_path(import_dir); if (import_dir[import_dir.length()-1] != '/') { import_dir = import_dir + "/"; }; String resource_dir = DirAccess::normalize_path(g->get_resource_path()); if (resource_dir[resource_dir.length()-1] != '/') { resource_dir = resource_dir + "/"; }; List list; obj->get_property_list(&list, false); List::Element *E = list.front(); while (E) { Variant v = obj->get(E->get().name); if (v.get_type() == Variant::OBJECT) { Ref res = (RefPtr)v; if (res.is_null()) { E = E->next(); continue; } if (res->get_path() != "") { String res_path = res->get_path(); res_path = Globals::get_singleton()->globalize_path(res_path); res_path = DirAccess::normalize_path(res_path); if (res_path.find(resource_dir) != 0) { // path of resource is not inside engine's resource path String new_path; if (res_path.find(import_dir) == 0) { // path of resource is relative to path of import file new_path = save_path + "/" + res_path.substr(import_dir.length(), res_path.length() - import_dir.length()); } else { // path of resource is not relative to import file new_path = save_path + "/" + res_path.get_file(); }; res->set_path(g->localize_path(new_path)); DirAccess* d = DirAccess::create(DirAccess::ACCESS_RESOURCES); d->make_dir_recursive(new_path.get_base_dir()); printf("copying from %ls to %ls\n", res_path.c_str(), new_path.c_str()); Error err = d->copy(res_path, new_path); memdelete(d); ERR_FAIL_COND_V(err != OK, err); } } else { _fix_object_paths(res.operator->(), root, save_path); }; }; E = E->next(); }; return OK; }; static Error _fix_imported_scene_paths(Node* node, Node* root, String save_path) { if (node == root || node->get_owner() == root) { Error e = _fix_object_paths(node, root, save_path); ERR_FAIL_COND_V(e != OK, e); }; for (int i=0; iget_child_count(); i++) { Error e = _fix_imported_scene_paths(node->get_child(i), root, save_path); ERR_FAIL_COND_V(e != OK, e); }; return OK; }; bool EditorNode::_find_and_save_resource(RES res,Map& processed,int32_t flags) { if (res.is_null()) return false; if (processed.has(res)) { return processed[res]; } bool changed = res->is_edited(); res->set_edited(false); bool subchanged = _find_and_save_edited_subresources(res.ptr(),processed,flags); // print_line("checking if edited: "+res->get_type()+" :: "+res->get_name()+" :: "+res->get_path()+" :: "+itos(changed)+" :: SR "+itos(subchanged)); if (res->get_path().is_resource_file()) { if (changed || subchanged) { //save print_line("Also saving modified external resource: "+res->get_path()); Error err = ResourceSaver::save(res->get_path(),res,flags); } processed[res]=false; //because it's a file return false; } else { processed[res]=changed; return changed; } } bool EditorNode::_find_and_save_edited_subresources(Object *obj,Map& processed,int32_t flags) { bool ret_changed=false; List pi; obj->get_property_list(&pi); for (List::Element *E=pi.front();E;E=E->next()) { if (!(E->get().usage&PROPERTY_USAGE_STORAGE)) continue; switch(E->get().type) { case Variant::OBJECT: { RES res = obj->get(E->get().name); if (_find_and_save_resource(res,processed,flags)) ret_changed=true; } break; case Variant::ARRAY: { Array varray= obj->get(E->get().name); int len=varray.size(); for(int i=0;iget(E->get().name);; List keys; d.get_key_list(&keys); for(List::Element *E=keys.front();E;E=E->next()) { Variant v = d[E->get()]; RES res=v; if (_find_and_save_resource(res,processed,flags)) ret_changed=true; } } break; default: {} } } return ret_changed; } void EditorNode::_save_edited_subresources(Node* scene,Map& processed,int32_t flags) { _find_and_save_edited_subresources(scene,processed,flags); for(int i=0;iget_child_count();i++) { Node *n = scene->get_child(i); if (n->get_owner()!=editor_data.get_edited_scene_root()) continue; _save_edited_subresources(n,processed,flags); } } void EditorNode::_find_node_types(Node* p_node, int&count_2d, int&count_3d) { if (p_node->is_type("Viewport") || (p_node!=editor_data.get_edited_scene_root() && p_node->get_owner()!=editor_data.get_edited_scene_root())) return; if (p_node->is_type("CanvasItem")) count_2d++; else if (p_node->is_type("Spatial")) count_3d++; for(int i=0;iget_child_count();i++) _find_node_types(p_node->get_child(i),count_2d,count_3d); } void EditorNode::_save_scene_with_preview(String p_file) { int c2d=0; int c3d=0; EditorProgress save("save",TTR("Saving Scene"),4); save.step(TTR("Analyzing"),0); _find_node_types(editor_data.get_edited_scene_root(),c2d,c3d); RID viewport; bool is2d; if (c3dget_viewport(); is2d=true; } else { viewport=SpatialEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_viewport(); is2d=false; } save.step(TTR("Creating Thumbnail"),1); //current view? int screen =-1; for(int i=0;iviewport_queue_screen_capture(viewport); save.step(TTR("Creating Thumbnail"),2); save.step(TTR("Creating Thumbnail"),3); Image img = VS::get_singleton()->viewport_get_screen_capture(viewport); int preview_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");; preview_size*=EDSCALE; int width,height; if (img.get_width() > preview_size && img.get_width() >= img.get_height()) { width=preview_size; height = img.get_height() * preview_size / img.get_width(); } else if (img.get_height() > preview_size && img.get_height() >= img.get_width()) { height=preview_size; width = img.get_width() * preview_size / img.get_height(); } else { width=img.get_width(); height=img.get_height(); } img.convert(Image::FORMAT_RGB); img.resize(width,height); String pfile = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/last_scene_preview.png"); img.save_png(pfile); Vector imgdata = FileAccess::get_file_as_array(pfile); //print_line("img data is "+itos(imgdata.size())); if (editor_data.get_edited_scene_import_metadata().is_null()) editor_data.set_edited_scene_import_metadata(Ref( memnew( ResourceImportMetadata ) ) ); editor_data.get_edited_scene_import_metadata()->set_option("thumbnail",imgdata); //tamanio tel thumbnail if (screen!=-1) { _editor_select(screen); } save.step(TTR("Saving Scene"),4); _save_scene(p_file); } void EditorNode::_save_scene(String p_file) { Node *scene = editor_data.get_edited_scene_root(); if (!scene) { current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text(TTR("I see..")); accept->set_text("This operation can't be done without a tree root."); accept->popup_centered_minsize(); return; } editor_data.apply_changes_in_editors(); _set_scene_metadata(p_file); Ref sdata; if (ResourceCache::has(p_file)) { // something may be referencing this resource and we are good with that. // we must update it, but also let the previous scene state go, as // old version still work for referencing changes in instanced or inherited scenes sdata = Ref( ResourceCache::get(p_file)->cast_to() ); if (sdata.is_valid()) sdata->recreate_state(); else sdata.instance(); } else { sdata.instance(); } Error err = sdata->pack(scene); if (err!=OK) { current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text(TTR("I see..")); accept->set_text(TTR("Couldn't save scene. Likely dependencies (instances) couldn't be satisfied.")); accept->popup_centered_minsize(); return; } sdata->set_import_metadata(editor_data.get_edited_scene_import_metadata()); int flg=0; if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources")) flg|=ResourceSaver::FLAG_COMPRESS; if (EditorSettings::get_singleton()->get("on_save/save_paths_as_relative")) flg|=ResourceSaver::FLAG_RELATIVE_PATHS; flg|=ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS; err = ResourceSaver::save(p_file,sdata,flg); Map processed; _save_edited_subresources(scene,processed,flg); editor_data.save_editor_external_data(); if (err==OK) { scene->set_filename( Globals::get_singleton()->localize_path(p_file) ); //EditorFileSystem::get_singleton()->update_file(p_file,sdata->get_type()); set_current_version(editor_data.get_undo_redo().get_version()); _update_title(); _update_scene_tabs(); } else { _dialog_display_file_error(p_file,err); } }; void EditorNode::_import_action(const String& p_action) { #if 0 import_confirmation->hide(); if (p_action=="re-import") { _import(_tmp_import_path); } if (p_action=="update") { Node *src = EditorImport::import_scene(_tmp_import_path); if (!src) { current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ugh"); accept->set_text("Error importing scene."); accept->popup_centered(Size2(300,70));; return; } //as soon as the scene is imported, version hashes must be generated for comparison against saved scene EditorImport::generate_version_hashes(src); Node *dst = SceneLoader::load(editor_data.get_imported_scene(Globals::get_singleton()->localize_path(_tmp_import_path))); if (!dst) { memdelete(src); //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ugh"); accept->set_text("Error load scene to update."); accept->popup_centered(Size2(300,70));; return; } List conflicts; EditorImport::check_conflicts(src,dst,&conflicts); bool conflicted=false; for (List::Element *E=conflicts.front();E;E=E->next()) { if (E->get().status==EditorImport::Conflict::STATUS_CONFLICT) { conflicted=true; break; } } if (conflicted) { import_conflicts_dialog->popup(src,dst,conflicts); return; } _import_with_conflicts(src,dst,conflicts); //not conflicted, just reimport! } #endif } void EditorNode::_import(const String &p_file) { #if 0 Node *new_scene = EditorImport::import_scene(p_file); if (!new_scene) { current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ugh"); accept->set_text("Error importing scene."); accept->popup_centered(Size2(300,70));; return; } //as soon as the scene is imported, version hashes must be generated for comparison against saved scene EditorImport::generate_version_hashes(new_scene); Node *old_scene = edited_scene; _hide_top_editors(); set_edited_scene(NULL); editor_data.clear_editor_states(); if (old_scene) { memdelete(old_scene); } set_edited_scene(new_scene); scene_tree_dock->set_selected(new_scene); //_get_scene_metadata(); editor_data.get_undo_redo().clear_history(); saved_version=editor_data.get_undo_redo().get_version(); _update_title(); #endif } void EditorNode::_dialog_action(String p_file) { switch(current_option) { case RESOURCE_LOAD: { RES res = ResourceLoader::load(p_file); if (res.is_null()) { current_option=-1; //accept->get_cancel()->hide(); accept->get_ok()->set_text("ok :("); accept->set_text(TTR("Failed to load resource.")); return; }; push_item(res.operator->() ); } break; case FILE_NEW_INHERITED_SCENE: { load_scene(p_file,false,true); } break; case FILE_OPEN_SCENE: { load_scene(p_file); } break; case FILE_SAVE_OPTIMIZED: { } break; case FILE_RUN_SCRIPT: { print_line("RUN: "+p_file); Ref