/*************************************************************************/ /* editor_scene_import_plugin.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2017 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 "editor_scene_import_plugin.h" #include "editor/create_dialog.h" #include "editor/editor_node.h" #include "globals.h" #include "io/resource_saver.h" #include "os/file_access.h" #include "os/os.h" #include "scene/3d/body_shape.h" #include "scene/3d/mesh_instance.h" #include "scene/3d/navigation.h" #include "scene/3d/path.h" #include "scene/3d/physics_body.h" #include "scene/3d/portal.h" #include "scene/3d/room_instance.h" #include "scene/3d/vehicle_body.h" #include "scene/animation/animation_player.h" #include "scene/resources/box_shape.h" #include "scene/resources/packed_scene.h" #include "scene/resources/sphere_shape.h" #include #include #include EditorSceneImporter::EditorSceneImporter() { } void EditorScenePostImport::_bind_methods() { BIND_VMETHOD(MethodInfo("post_import", PropertyInfo(Variant::OBJECT, "scene"))); } Node *EditorScenePostImport::post_import(Node *p_scene) { if (get_script_instance()) return get_script_instance()->call("post_import", p_scene); return p_scene; } EditorScenePostImport::EditorScenePostImport() { } ///////////////////////////// class EditorImportAnimationOptions : public VBoxContainer { OBJ_TYPE(EditorImportAnimationOptions, VBoxContainer); TreeItem *fps; TreeItem *optimize_linear_error; TreeItem *optimize_angular_error; TreeItem *optimize_max_angle; TreeItem *clips_base; TextEdit *filters; Vector clips; Tree *flags; Tree *clips_tree; Tree *optimization_tree; Vector items; bool updating; bool validating; void _changed(); void _item_edited(); void _button_action(Object *p_obj, int p_col, int p_id); protected: static void _bind_methods(); void _notification(int p_what); public: void set_flags(uint32_t p_flags); uint32_t get_flags() const; void set_fps(int p_fps); int get_fps() const; void set_optimize_linear_error(float p_error); float get_optimize_linear_error() const; void set_optimize_angular_error(float p_error); float get_optimize_angular_error() const; void set_optimize_max_angle(float p_error); float get_optimize_max_angle() const; void setup_clips(const Array &p_clips); Array get_clips() const; void set_filter(const String &p_filter); String get_filter() const; EditorImportAnimationOptions(); }; //////////////////////////// class EditorSceneImportDialog : public ConfirmationDialog { OBJ_TYPE(EditorSceneImportDialog, ConfirmationDialog); struct FlagInfo { int value; const char *category; const char *text; bool defval; }; static const FlagInfo scene_flag_names[]; EditorImportTextureOptions *texture_options; EditorImportAnimationOptions *animation_options; EditorSceneImportPlugin *plugin; EditorNode *editor; LineEdit *import_path; LineEdit *save_path; LineEdit *script_path; Tree *import_options; EditorFileDialog *file_select; EditorFileDialog *script_select; EditorDirDialog *save_select; OptionButton *texture_action; CreateDialog *root_type_choose; LineEdit *root_node_name; ConfirmationDialog *confirm_open; ConfirmationDialog *confirm_import; RichTextLabel *missing_files; Vector scene_flags; Map, Ref > collision_map; ConfirmationDialog *error_dialog; Button *root_type; CheckBox *root_default; void _root_default_pressed(); void _root_type_pressed(); void _set_root_type(); void _choose_file(const String &p_path); void _choose_save_file(const String &p_path); void _choose_script(const String &p_path); void _browse(); void _browse_target(); void _browse_script(); void _import(bool p_and_open = false); void _import_confirm(); Ref wip_rimd; Node *wip_import; String wip_save_file; bool wip_blocked; bool wip_open; void _dialog_hid(); void _open_and_import(); protected: void _notification(int p_what); static void _bind_methods(); public: void setup_popup(const String &p_from, const String &p_to_path) { _choose_file(p_from); _choose_save_file(p_to_path); } Error import(const String &p_from, const String &p_to, const String &p_preset); void popup_import(const String &p_from); EditorSceneImportDialog(EditorNode *p_editor, EditorSceneImportPlugin *p_plugin); }; /////////////////////////////////// static const char *anim_flag_names[] = { "Detect Loop (-loop,-cycle)", "Keep Value Tracks", "Optimize", "Force All Tracks in All Clips", NULL }; static const char *anim_flag_descript[] = { "Set loop flag for animation names that\ncontain 'cycle' or 'loop' in the name.", "When merging an existing aimation,\nkeep the user-created value-tracks.", "Remove redundant keyframes in\n transform tacks.", "Some exporters will rely on default pose for some bones.\nThis forces those bones to have at least one animation key.", NULL }; void EditorImportAnimationOptions::set_flags(uint32_t p_flags) { updating = true; for (int i = 0; i < items.size(); i++) { items[i]->set_checked(0, p_flags & (1 << i)); } updating = false; } uint32_t EditorImportAnimationOptions::get_flags() const { uint32_t f = 0; for (int i = 0; i < items.size(); i++) { if (items[i]->is_checked(0)) f |= (1 << i); } return f; } void EditorImportAnimationOptions::_changed() { if (updating) return; emit_signal("changed"); } void EditorImportAnimationOptions::_button_action(Object *p_obj, int p_col, int p_id) { memdelete(p_obj); } void EditorImportAnimationOptions::_item_edited() { if (validating) return; if (clips.size() == 0) return; validating = true; print_line("edited"); TreeItem *item = clips_tree->get_edited(); if (item == clips[clips.size() - 1]) { //add new print_line("islast"); if (item->get_text(0).find("<") != -1 || item->get_text(0).find(">") != -1) { validating = false; return; //fuckit } item->set_editable(1, true); item->set_editable(2, true); item->add_button(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Del", "EditorIcons")); item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); item->set_range_config(1, 0, 3600, 0.01); item->set_range(1, 0); item->set_editable(1, true); item->set_cell_mode(2, TreeItem::CELL_MODE_RANGE); item->set_range_config(2, 0, 3600, 0.01); item->set_range(2, 0); item->set_cell_mode(3, TreeItem::CELL_MODE_CHECK); item->set_editable(3, true); TreeItem *newclip = clips_tree->create_item(clips_base); newclip->set_text(0, ""); newclip->set_editable(0, true); newclip->set_editable(1, false); newclip->set_editable(2, false); clips.push_back(newclip); } //make name unique JUST IN CASE String name = item->get_text(0); name = name.replace("/", "_").replace(":", "_").strip_edges(); if (name == "") name = TTR("New Clip"); if (clips.size() > 2) { int index = 1; while (true) { bool valid = true; String try_name = name; if (index > 1) try_name += " " + itos(index); for (int i = 0; i < clips.size() - 1; i++) { if (clips[i] == item) continue; if (clips[i]->get_text(0) == try_name) { index++; valid = false; break; } } if (valid) { name = try_name; break; } } } if (item->get_text(0) != name) item->set_text(0, name); validating = false; } void EditorImportAnimationOptions::_bind_methods() { ObjectTypeDB::bind_method("_changed", &EditorImportAnimationOptions::_changed); ObjectTypeDB::bind_method("_item_edited", &EditorImportAnimationOptions::_item_edited); ObjectTypeDB::bind_method("_button_action", &EditorImportAnimationOptions::_button_action); // ObjectTypeDB::bind_method("_changedp",&EditorImportAnimationOptions::_changedp); ADD_SIGNAL(MethodInfo("changed")); } void EditorImportAnimationOptions::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { flags->connect("item_edited", this, "_changed"); clips_tree->connect("item_edited", this, "_item_edited"); clips_tree->connect("button_pressed", this, "_button_action", varray(), CONNECT_DEFERRED); // format->connect("item_selected",this,"_changedp"); } } Array EditorImportAnimationOptions::get_clips() const { Array arr; for (int i = 0; i < clips.size() - 1; i++) { arr.push_back(clips[i]->get_text(0)); arr.push_back(clips[i]->get_range(1)); arr.push_back(clips[i]->get_range(2)); arr.push_back(clips[i]->is_checked(3)); } return arr; } void EditorImportAnimationOptions::setup_clips(const Array &p_clips) { ERR_FAIL_COND(p_clips.size() % 4 != 0); for (int i = 0; i < clips.size(); i++) { memdelete(clips[i]); } clips.clear(); for (int i = 0; i < p_clips.size(); i += 4) { TreeItem *clip = clips_tree->create_item(clips_base); clip->set_text(0, p_clips[i]); clip->add_button(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Del", "EditorIcons")); clip->set_editable(0, true); clip->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); clip->set_range_config(1, 0, 3600, 0.01); clip->set_range(1, p_clips[i + 1]); clip->set_editable(1, true); clip->set_cell_mode(2, TreeItem::CELL_MODE_RANGE); clip->set_range_config(2, 0, 3600, 0.01); clip->set_range(2, p_clips[i + 2]); clip->set_editable(2, true); clip->set_cell_mode(3, TreeItem::CELL_MODE_CHECK); clip->set_editable(3, true); clip->set_checked(3, p_clips[i + 3]); clips.push_back(clip); } TreeItem *newclip = clips_tree->create_item(clips_base); newclip->set_text(0, ""); newclip->set_editable(0, true); newclip->set_editable(1, false); newclip->set_editable(2, false); newclip->set_editable(3, false); clips.push_back(newclip); } EditorImportAnimationOptions::EditorImportAnimationOptions() { updating = false; validating = false; TabContainer *tab = memnew(TabContainer); add_margin_child(TTR("Animation Options"), tab, true); flags = memnew(Tree); flags->set_hide_root(true); tab->add_child(flags); flags->set_name(TTR("Flags")); TreeItem *root = flags->create_item(); const char **fname = anim_flag_names; const char **fdescr = anim_flag_descript; while (*fname) { TreeItem *ti = flags->create_item(root); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, *fname); ti->set_editable(0, true); ti->set_tooltip(0, *fdescr); items.push_back(ti); fname++; fdescr++; } TreeItem *fps_base = flags->create_item(root); fps_base->set_text(0, TTR("Bake FPS:")); fps_base->set_editable(0, false); fps = flags->create_item(fps_base); fps->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); fps->set_editable(0, true); fps->set_range_config(0, 1, 120, 1); fps->set_range(0, 15); optimization_tree = memnew(Tree); optimization_tree->set_columns(2); tab->add_child(optimization_tree); optimization_tree->set_name(TTR("Optimizer")); optimization_tree->set_column_expand(0, true); optimization_tree->set_column_expand(1, false); optimization_tree->set_column_min_width(1, 80); optimization_tree->set_hide_root(true); TreeItem *optimize_root = optimization_tree->create_item(); optimize_linear_error = optimization_tree->create_item(optimize_root); optimize_linear_error->set_text(0, TTR("Max Linear Error")); optimize_linear_error->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); optimize_linear_error->set_editable(1, true); optimize_linear_error->set_range_config(1, 0, 1, 0.001); optimize_linear_error->set_range(1, 0.05); optimize_angular_error = optimization_tree->create_item(optimize_root); optimize_angular_error->set_text(0, TTR("Max Angular Error")); optimize_angular_error->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); optimize_angular_error->set_editable(1, true); optimize_angular_error->set_range_config(1, 0, 1, 0.001); optimize_angular_error->set_range(1, 0.01); optimize_max_angle = optimization_tree->create_item(optimize_root); optimize_max_angle->set_text(0, TTR("Max Angle")); optimize_max_angle->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); optimize_max_angle->set_editable(1, true); optimize_max_angle->set_range_config(1, 0, 360, 0.001); optimize_max_angle->set_range(1, int(180 * 0.125)); clips_tree = memnew(Tree); clips_tree->set_hide_root(true); tab->add_child(clips_tree); clips_tree->set_name(TTR("Clips")); clips_tree->set_columns(4); clips_tree->set_column_expand(0, 1); clips_tree->set_column_expand(1, 0); clips_tree->set_column_expand(2, 0); clips_tree->set_column_expand(3, 0); clips_tree->set_column_min_width(1, 60); clips_tree->set_column_min_width(2, 60); clips_tree->set_column_min_width(3, 40); clips_tree->set_column_titles_visible(true); clips_tree->set_column_title(0, TTR("Name")); clips_tree->set_column_title(1, TTR("Start(s)")); clips_tree->set_column_title(2, TTR("End(s)")); clips_tree->set_column_title(3, TTR("Loop")); clips_base = clips_tree->create_item(0); setup_clips(Array()); filters = memnew(TextEdit); tab->add_child(filters); filters->set_name(TTR("Filters")); } void EditorImportAnimationOptions::set_fps(int p_fps) { fps->set_range(0, p_fps); } int EditorImportAnimationOptions::get_fps() const { return fps->get_range(0); } void EditorImportAnimationOptions::set_optimize_linear_error(float p_optimize_linear_error) { optimize_linear_error->set_range(1, p_optimize_linear_error); } float EditorImportAnimationOptions::get_optimize_linear_error() const { return optimize_linear_error->get_range(1); } void EditorImportAnimationOptions::set_optimize_angular_error(float p_optimize_angular_error) { optimize_angular_error->set_range(1, p_optimize_angular_error); } float EditorImportAnimationOptions::get_optimize_angular_error() const { return optimize_angular_error->get_range(1); } void EditorImportAnimationOptions::set_optimize_max_angle(float p_optimize_max_angle) { optimize_max_angle->set_range(1, p_optimize_max_angle); } float EditorImportAnimationOptions::get_optimize_max_angle() const { return optimize_max_angle->get_range(1); } void EditorImportAnimationOptions::set_filter(const String &p_filter) { filters->set_text(p_filter); } String EditorImportAnimationOptions::get_filter() const { return filters->get_text(); } //////////////////////////////////////////////////////// void EditorSceneImportDialog::_choose_file(const String &p_path) { #if 0 StringName sn = EditorImportDB::get_singleton()->find_source_path(p_path); if (sn!=StringName()) { EditorImportDB::ImportScene isc; if (EditorImportDB::get_singleton()->get_scene(sn,isc)==OK) { save_path->set_text(String(sn).get_base_dir()); texture_options->set_flags( isc.image_flags ); texture_options->set_quality( isc.image_quality ); texture_options->set_format( isc.image_format ); animation_options->set_flags( isc.anim_flags ); script_path->set_text( isc.import_script ); uint32_t f = isc.flags; for(int i=0;iset_checked(0,f&(1<set_text(""); root_node_name->set_text(""); //save_path->set_text(p_path.get_file().basename()+".scn"); #if 0 } #endif if (p_path != String()) { String from_path = EditorFileSystem::get_singleton()->find_resource_from_source(EditorImportPlugin::validate_source_path(p_path)); print_line("from path.." + from_path); if (from_path != String()) { popup_import(from_path); } } import_path->set_text(p_path); if (root_node_name->get_text().size() == 0) { root_node_name->set_text(import_path->get_text().get_file().basename()); } } void EditorSceneImportDialog::_choose_save_file(const String &p_path) { save_path->set_text(p_path); } void EditorSceneImportDialog::_choose_script(const String &p_path) { String p = Globals::get_singleton()->localize_path(p_path); if (!p.is_resource_file()) p = Globals::get_singleton()->get_resource_path().path_to(p_path.get_base_dir()) + p_path.get_file(); script_path->set_text(p); } void EditorSceneImportDialog::_open_and_import() { bool unsaved = EditorNode::has_unsaved_changes(); if (unsaved) { confirm_open->popup_centered_minsize(Size2(300, 80) * EDSCALE); } else { _import(true); } } void EditorSceneImportDialog::_import(bool p_and_open) { wip_open = p_and_open; //' ImportMonitorBlock imb; if (import_path->get_text().strip_edges() == "") { error_dialog->set_text(TTR("Source path is empty.")); error_dialog->popup_centered_minsize(); return; } if (save_path->get_text().strip_edges() == "") { error_dialog->set_text(TTR("Target path is empty.")); error_dialog->popup_centered_minsize(); return; } if (!save_path->get_text().begins_with("res://")) { error_dialog->set_text(TTR("Target path must be a complete resource path.")); error_dialog->popup_centered_minsize(); return; } if (!DirAccess::exists(save_path->get_text())) { error_dialog->set_text(TTR("Target path must exist.")); error_dialog->popup_centered_minsize(); return; } uint32_t flags = 0; for (int i = 0; i < scene_flags.size(); i++) { if (scene_flags[i]->is_checked(0)) { int md = scene_flags[i]->get_metadata(0); flags |= md; } } if (script_path->get_text() != "") { Ref