diff options
author | Rémi Verschelde <rverschelde@gmail.com> | 2017-03-05 14:21:25 +0100 |
---|---|---|
committer | Rémi Verschelde <rverschelde@gmail.com> | 2017-03-05 14:21:25 +0100 |
commit | 49c065d29ca07040c3fd810026121164ad86b247 (patch) | |
tree | 285176e0c80a41c22c3e8f171024472cfdc7d765 /editor/io_plugins | |
parent | 532f6d4b431f940432e82b7fc7826652b7a4520d (diff) |
Refactoring: rename tools/editor/ to editor/
The other subfolders of tools/ had already been moved to either
editor/, misc/ or thirdparty/, so the hiding the editor code that
deep was no longer meaningful.
Diffstat (limited to 'editor/io_plugins')
21 files changed, 11314 insertions, 0 deletions
diff --git a/editor/io_plugins/SCsub b/editor/io_plugins/SCsub new file mode 100644 index 0000000000..f1fa50148f --- /dev/null +++ b/editor/io_plugins/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import('env') +Export('env') +env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/io_plugins/editor_atlas.cpp b/editor/io_plugins/editor_atlas.cpp new file mode 100644 index 0000000000..c5f1ee73cf --- /dev/null +++ b/editor/io_plugins/editor_atlas.cpp @@ -0,0 +1,160 @@ +/*************************************************************************/ +/* editor_atlas.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_atlas.h" + +#include "print_string.h" + +struct _EditorAtlasWorkRect { + + Size2i s; + Point2i p; + int idx; + _FORCE_INLINE_ bool operator<(const _EditorAtlasWorkRect& p_r) const { return s.width > p_r.s.width; }; +}; + +struct _EditorAtlasWorkRectResult { + + Vector<_EditorAtlasWorkRect> result; + int max_w; + int max_h; +}; + +void EditorAtlas::fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, Size2i& r_size) { + + //super simple, almost brute force scanline stacking fitter + //it's pretty basic for now, but it tries to make sure that the aspect ratio of the + //resulting atlas is somehow square. This is necesary because video cards have limits + //on texture size (usually 2048 or 4096), so the more square a texture, the more chances + //it will work in every hardware. + // for example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a + // 256x8192 atlas (won't work anywhere). + + ERR_FAIL_COND(p_rects.size()==0); + + Vector<_EditorAtlasWorkRect> wrects; + wrects.resize(p_rects.size()); + for(int i=0;i<p_rects.size();i++) { + wrects[i].s=p_rects[i]; + wrects[i].idx=i; + } + wrects.sort(); + int widest = wrects[0].s.width; + + Vector<_EditorAtlasWorkRectResult> results; + + for(int i=0;i<=12;i++) { + + int w = 1<<i; + int max_h=0; + int max_w=0; + if ( w < widest ) + continue; + + Vector<int> hmax; + hmax.resize(w); + for(int j=0;j<w;j++) + hmax[j]=0; + + //place them + int ofs=0; + + for(int j=0;j<wrects.size();j++) { + + + if (ofs+wrects[j].s.width > w) { + + ofs=0; + } + + int from_y=0; + for(int k=0;k<wrects[j].s.width;k++) { + + if (hmax[ofs+k] > from_y) + from_y=hmax[ofs+k]; + } + + wrects[j].p.x=ofs; + wrects[j].p.y=from_y; + + + + int end_h = from_y+wrects[j].s.height; + int end_w = ofs+wrects[j].s.width; + + for(int k=0;k<wrects[j].s.width;k++) { + + hmax[ofs+k]=end_h; + } + + if (end_h > max_h) + max_h=end_h; + + if (end_w > max_w) + max_w=end_w; + + ofs+=wrects[j].s.width; + + } + + _EditorAtlasWorkRectResult result; + result.result=wrects; + result.max_h=max_h; + result.max_w=max_w; + results.push_back(result); + + } + + //find the result with the best aspect ratio + + int best=-1; + float best_aspect=1e20; + + for(int i=0;i<results.size();i++) { + + float h = results[i].max_h; + float w = results[i].max_w; + float aspect = h>w ? h/w : w/h; + if (aspect < best_aspect) { + best=i; + best_aspect=aspect; + } + } + + r_result.resize(p_rects.size()); + + for(int i=0;i<p_rects.size();i++) { + + r_result[ results[best].result[i].idx ]=results[best].result[i].p; + } + + r_size=Size2(results[best].max_w,results[best].max_h ); + +} + + diff --git a/editor/io_plugins/editor_atlas.h b/editor/io_plugins/editor_atlas.h new file mode 100644 index 0000000000..e0cf76576e --- /dev/null +++ b/editor/io_plugins/editor_atlas.h @@ -0,0 +1,43 @@ +/*************************************************************************/ +/* editor_atlas.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_ATLAS_H +#define EDITOR_ATLAS_H + +#include "math_2d.h" +#include "vector.h" + +class EditorAtlas { +public: + + static void fit(const Vector<Size2i>& p_rects,Vector<Point2i>& r_result, Size2i& r_size); + + +}; + +#endif // EDITOR_ATLAS_H diff --git a/editor/io_plugins/editor_bitmask_import_plugin.cpp b/editor/io_plugins/editor_bitmask_import_plugin.cpp new file mode 100644 index 0000000000..7282cbe4e7 --- /dev/null +++ b/editor/io_plugins/editor_bitmask_import_plugin.cpp @@ -0,0 +1,387 @@ +/*************************************************************************/ +/* editor_bitmask_import_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_bitmask_import_plugin.h" +#if 0 +#include "io/image_loader.h" +#include "editor/editor_file_dialog.h" +#include "editor/editor_dir_dialog.h" +#include "editor/editor_node.h" +#include "editor/property_editor.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "io/marshalls.h" +#include "editor/editor_settings.h" + +class _EditorBitMaskImportOptions : public Object { + + GDCLASS(_EditorBitMaskImportOptions, Object); +public: + + bool _set(const StringName& p_name, const Variant& p_value) { + + return false; + } + + bool _get(const StringName& p_name, Variant &r_ret) const{ + + return false; + } + + void _get_property_list(List<PropertyInfo> *p_list) const{ + + } + + static void _bind_methods() { + + ADD_SIGNAL(MethodInfo("changed")); + } + + + _EditorBitMaskImportOptions() { + + } + +}; + +class EditorBitMaskImportDialog : public ConfirmationDialog { + + GDCLASS(EditorBitMaskImportDialog, ConfirmationDialog); + + EditorBitMaskImportPlugin *plugin; + + LineEdit *import_path; + LineEdit *save_path; + EditorFileDialog *file_select; + EditorDirDialog *save_select; + ConfirmationDialog *error_dialog; + PropertyEditor *option_editor; + +public: + + void _choose_files(const Vector<String>& p_path) { + + String files; + for (int i = 0; i<p_path.size(); i++) { + + if (i>0) + files += ","; + files += p_path[i]; + } + + import_path->set_text(files); + + } + void _choose_save_dir(const String& p_path) { + + save_path->set_text(p_path); + } + + void _browse() { + + file_select->popup_centered_ratio(); + } + + void _browse_target() { + + save_select->popup_centered_ratio(); + + } + + + void popup_import(const String& p_path) { + + popup_centered(Size2(400, 100)*EDSCALE); + if (p_path != "") { + + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_path); + ERR_FAIL_COND(!rimd.is_valid()); + + save_path->set_text(p_path.get_base_dir()); + + String src = ""; + for (int i = 0; i<rimd->get_source_count(); i++) { + if (i>0) + src += ","; + src += EditorImportPlugin::expand_source_path(rimd->get_source_path(i)); + } + import_path->set_text(src); + } + } + + + void _import() { + + Vector<String> bitmasks = import_path->get_text().split(","); + + if (bitmasks.size() == 0) { + error_dialog->set_text(TTR("No bit masks to import!")); + error_dialog->popup_centered(Size2(200, 100)*EDSCALE); + } + + 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; + } + + for (int i = 0; i<bitmasks.size(); i++) { + + Ref<ResourceImportMetadata> imd = memnew(ResourceImportMetadata); + + imd->add_source(EditorImportPlugin::validate_source_path(bitmasks[i])); + + String dst = save_path->get_text(); + if (dst == "") { + error_dialog->set_text(TTR("Save path is empty!")); + error_dialog->popup_centered(Size2(200, 100)*EDSCALE); + } + + dst = dst.plus_file(bitmasks[i].get_file().get_basename() + ".pbm"); + + plugin->import(dst, imd); + } + + hide(); + + } + + + void _notification(int p_what) { + + } + + static void _bind_methods() { + + + ClassDB::bind_method("_choose_files", &EditorBitMaskImportDialog::_choose_files); + ClassDB::bind_method("_choose_save_dir", &EditorBitMaskImportDialog::_choose_save_dir); + ClassDB::bind_method("_import", &EditorBitMaskImportDialog::_import); + ClassDB::bind_method("_browse", &EditorBitMaskImportDialog::_browse); + ClassDB::bind_method("_browse_target", &EditorBitMaskImportDialog::_browse_target); + //ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) ); + } + + EditorBitMaskImportDialog(EditorBitMaskImportPlugin *p_plugin) { + + plugin = p_plugin; + + + set_title(TTR("Import BitMasks")); + + VBoxContainer *vbc = memnew(VBoxContainer); + add_child(vbc); + //set_child_rect(vbc); + + + HBoxContainer *hbc = memnew(HBoxContainer); + vbc->add_margin_child(TTR("Source Texture(s):"), hbc); + + import_path = memnew(LineEdit); + import_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(import_path); + + Button * import_choose = memnew(Button); + import_choose->set_text(" .. "); + hbc->add_child(import_choose); + + import_choose->connect("pressed", this, "_browse"); + + hbc = memnew(HBoxContainer); + vbc->add_margin_child(TTR("Target Path:"), hbc); + + save_path = memnew(LineEdit); + save_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(save_path); + + Button * save_choose = memnew(Button); + save_choose->set_text(" .. "); + hbc->add_child(save_choose); + + save_choose->connect("pressed", this, "_browse_target"); + + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + add_child(file_select); + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES); + file_select->connect("files_selected", this, "_choose_files"); + + List<String> extensions; + ImageLoader::get_recognized_extensions(&extensions); + file_select->clear_filters(); + for (int i = 0; i<extensions.size(); i++) { + + file_select->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); + } + + save_select = memnew(EditorDirDialog); + add_child(save_select); + + //save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR); + save_select->connect("dir_selected", this, "_choose_save_dir"); + + get_ok()->connect("pressed", this, "_import"); + get_ok()->set_text(TTR("Import")); + + + error_dialog = memnew(ConfirmationDialog); + add_child(error_dialog); + error_dialog->get_ok()->set_text(TTR("Accept")); + //error_dialog->get_cancel()->hide(); + + set_hide_on_ok(false); + } + + ~EditorBitMaskImportDialog() { + } + +}; + + +String EditorBitMaskImportPlugin::get_name() const { + + return "bitmask"; +} +String EditorBitMaskImportPlugin::get_visible_name() const{ + + return TTR("Bit Mask"); +} +void EditorBitMaskImportPlugin::import_dialog(const String& p_from){ + + dialog->popup_import(p_from); +} +Error EditorBitMaskImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from){ + + ERR_FAIL_COND_V(p_from->get_source_count() != 1, ERR_INVALID_PARAMETER); + + Ref<ResourceImportMetadata> from = p_from; + + String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0)); + Ref<ImageTexture> it = ResourceLoader::load(src_path); + ERR_FAIL_COND_V(it.is_null(), ERR_CANT_OPEN); + + Ref<BitMap> target = memnew(BitMap); + target->create_from_image_alpha(it.ptr()->get_data()); + + from->set_source_md5(0, FileAccess::get_md5(src_path)); + from->set_editor(get_name()); + target->set_import_metadata(from); + + + Error err = ResourceSaver::save(p_path, target); + + return err; + +} + + +EditorBitMaskImportPlugin* EditorBitMaskImportPlugin::singleton = NULL; + + +void EditorBitMaskImportPlugin::import_from_drop(const Vector<String>& p_drop, const String &p_dest_path) { + + Vector<String> files; + + List<String> valid_extensions; + ImageLoader::get_recognized_extensions(&valid_extensions); + for(int i=0;i<p_drop.size();i++) { + + String extension=p_drop[i].get_extension().to_lower(); + + for (List<String>::Element *E=valid_extensions.front();E;E=E->next()) { + + if (E->get()==extension) { + files.push_back(p_drop[i]); + break; + } + } + } + + if (files.size()) { + import_dialog(); + dialog->_choose_files(files); + dialog->_choose_save_dir(p_dest_path); + } +} + +void EditorBitMaskImportPlugin::reimport_multiple_files(const Vector<String>& p_list) { + + if (p_list.size() == 0) + return; + + Vector<String> sources; + for (int i = 0; i<p_list.size(); i++) { + int idx; + EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->find_file(p_list[i], &idx); + if (efsd) { + for (int j = 0; j<efsd->get_source_count(idx); j++) { + String file = expand_source_path(efsd->get_source_file(idx, j)); + if (sources.find(file) == -1) { + sources.push_back(file); + } + + } + } + } + + if (sources.size()) { + + dialog->popup_import(p_list[0]); + dialog->_choose_files(sources); + dialog->_choose_save_dir(p_list[0].get_base_dir()); + } +} + +bool EditorBitMaskImportPlugin::can_reimport_multiple_files() const { + + return true; +} + +EditorBitMaskImportPlugin::EditorBitMaskImportPlugin(EditorNode* p_editor) { + + singleton = this; + dialog = memnew(EditorBitMaskImportDialog(this)); + p_editor->get_gui_base()->add_child(dialog); +} + +EditorBitMaskExportPlugin::EditorBitMaskExportPlugin() { + +} +#endif diff --git a/editor/io_plugins/editor_bitmask_import_plugin.h b/editor/io_plugins/editor_bitmask_import_plugin.h new file mode 100644 index 0000000000..d1618d7843 --- /dev/null +++ b/editor/io_plugins/editor_bitmask_import_plugin.h @@ -0,0 +1,70 @@ +/*************************************************************************/ +/* editor_bitmask_import_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_BITMASK_IMPORT_PLUGIN_H +#define EDITOR_BITMASK_IMPORT_PLUGIN_H +#if 0 +#include "editor/editor_import_export.h" +#include "scene/resources/font.h" + +class EditorNode; +class EditorBitMaskImportDialog; + +class EditorBitMaskImportPlugin : public EditorImportPlugin { + + GDCLASS(EditorBitMaskImportPlugin, EditorImportPlugin); + + EditorBitMaskImportDialog *dialog; +public: + + static EditorBitMaskImportPlugin *singleton; + + virtual String get_name() const; + virtual String get_visible_name() const; + virtual void import_dialog(const String& p_from = ""); + virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from); + void import_from_drop(const Vector<String>& p_drop, const String &p_dest_path); + virtual void reimport_multiple_files(const Vector<String>& p_list); + virtual bool can_reimport_multiple_files() const; + + + EditorBitMaskImportPlugin(EditorNode* p_editor); +}; + +class EditorBitMaskExportPlugin : public EditorExportPlugin { + + GDCLASS(EditorBitMaskExportPlugin, EditorExportPlugin); + + +public: + + EditorBitMaskExportPlugin(); +}; + +#endif +#endif // EDITOR_SAMPLE_IMPORT_PLUGIN_H diff --git a/editor/io_plugins/editor_export_scene.cpp b/editor/io_plugins/editor_export_scene.cpp new file mode 100644 index 0000000000..a593b870f9 --- /dev/null +++ b/editor/io_plugins/editor_export_scene.cpp @@ -0,0 +1,142 @@ +/*************************************************************************/ +/* editor_export_scene.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_export_scene.h" +#if 0 +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/dir_access.h" +#include "os/file_access.h" +#include "editor/editor_settings.h" +#include "scene/resources/packed_scene.h" +#include "global_config.h" + +Vector<uint8_t> EditorSceneExportPlugin::custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform) { + + if (!EditorImportExport::get_singleton()->get_convert_text_scenes()) { + return Vector<uint8_t>(); + } + + + String extension = p_path.get_extension(); + + //step 1 check if scene + + if (extension=="xml" || extension=="xres") { + + String type = ResourceLoader::get_resource_type(p_path); + + if (type!="PackedScene") + return Vector<uint8_t>(); + + } else if (extension!="tscn" && extension!="xscn") { + return Vector<uint8_t>(); + } + + //step 2 check if cached + + uint64_t sd=0; + String smd5; + String gp = GlobalConfig::get_singleton()->globalize_path(p_path); + String md5=gp.md5_text(); + String tmp_path = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/"); + + bool valid=false; + { + //if existing, make sure it's valid + FileAccessRef f = FileAccess::open(tmp_path+"scnexp-"+md5+".txt",FileAccess::READ); + if (f) { + + uint64_t d = f->get_line().strip_edges().to_int64(); + sd = FileAccess::get_modified_time(p_path); + + if (d==sd) { + valid=true; + } else { + String cmd5 = f->get_line().strip_edges(); + smd5 = FileAccess::get_md5(p_path); + if (cmd5==smd5) { + valid=true; + } + } + + + } + } + + if (!valid) { + //cache failed, convert + DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + + String copy = p_path+".convert."+extension; + + // a copy will allow loading the internal resources without conflicting with opened scenes + da->copy(p_path,copy); + + //@todo for tscn use something more efficient + + Ref<PackedScene> copyres = ResourceLoader::load(copy,"PackedScene"); + + da->remove(copy); + + memdelete(da); + + ERR_FAIL_COND_V(!copyres.is_valid(),Vector<uint8_t>()); + + Error err = ResourceSaver::save(tmp_path+"scnexp-"+md5+".scn",copyres); + + copyres=Ref<PackedScene>(); + + ERR_FAIL_COND_V(err!=OK,Vector<uint8_t>()); + + FileAccessRef f = FileAccess::open(tmp_path+"scnexp-"+md5+".txt",FileAccess::WRITE); + + if (sd==0) + sd = FileAccess::get_modified_time(p_path); + if (smd5==String()) + smd5 = FileAccess::get_md5(p_path); + + f->store_line(String::num(sd)); + f->store_line(smd5); + f->store_line(gp); //source path for reference + } + + + Vector<uint8_t> ret = FileAccess::get_file_as_array(tmp_path+"scnexp-"+md5+".scn"); + + p_path+=".converted.scn"; + + return ret; + +} + + +EditorSceneExportPlugin::EditorSceneExportPlugin() +{ +} +#endif diff --git a/editor/io_plugins/editor_export_scene.h b/editor/io_plugins/editor_export_scene.h new file mode 100644 index 0000000000..ac425fbedd --- /dev/null +++ b/editor/io_plugins/editor_export_scene.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* editor_export_scene.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_EXPORT_SCENE_H +#define EDITOR_EXPORT_SCENE_H + +#include "editor/editor_export.h" + +#if 0 +class EditorSceneExportPlugin : public EditorExportPlugin { + GDCLASS( EditorSceneExportPlugin, EditorExportPlugin ); +public: + + virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform); + + EditorSceneExportPlugin(); +}; +#endif +#endif // EDITOR_EXPORT_SCENE_H diff --git a/editor/io_plugins/editor_font_import_plugin.cpp b/editor/io_plugins/editor_font_import_plugin.cpp new file mode 100644 index 0000000000..c792ad717a --- /dev/null +++ b/editor/io_plugins/editor_font_import_plugin.cpp @@ -0,0 +1,1704 @@ +/*************************************************************************/ +/* editor_font_import_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_font_import_plugin.h" +#if 0 +#include "scene/gui/dialogs.h" +#include "editor/editor_file_dialog.h" +#include "editor/editor_node.h" +#include "os/file_access.h" +#include "editor_atlas.h" +#include "io/image_loader.h" +#include "io/resource_saver.h" + +#ifdef FREETYPE_ENABLED +#include <ft2build.h> +#include FT_FREETYPE_H +#endif + + +class _EditorFontImportOptions : public Object { + + GDCLASS(_EditorFontImportOptions,Object); +public: + + enum FontMode { + + FONT_BITMAP, + FONT_DISTANCE_FIELD + }; + + enum ColorType { + COLOR_WHITE, + COLOR_CUSTOM, + COLOR_GRADIENT_RANGE, + COLOR_GRADIENT_IMAGE + }; + + + int char_extra_spacing; + int top_extra_spacing; + int bottom_extra_spacing; + int space_extra_spacing; + + enum CharacterSet { + + CHARSET_ASCII, + CHARSET_LATIN, + CHARSET_UNICODE, + CHARSET_CUSTOM, + CHARSET_CUSTOM_LATIN + }; + + + FontMode font_mode; + + CharacterSet character_set; + String custom_file; + + bool shadow; + Vector2 shadow_offset; + int shadow_radius; + Color shadow_color; + float shadow_transition; + + bool shadow2; + Vector2 shadow2_offset; + int shadow2_radius; + Color shadow2_color; + float shadow2_transition; + + ColorType color_type; + Color color; + Color gradient_begin; + Color gradient_end; + bool color_use_monochrome; + String gradient_image; + + bool enable_filter; + bool round_advance; + bool premultiply_alpha; + + + + bool _set(const StringName& p_name, const Variant& p_value) { + + String n = p_name; + if (n=="mode/mode") { + font_mode=FontMode(int(p_value)); + _change_notify(); + } else if (n=="extra_space/char") + char_extra_spacing=p_value; + else if (n=="extra_space/space") + space_extra_spacing=p_value; + else if (n=="extra_space/top") + top_extra_spacing=p_value; + else if (n=="extra_space/bottom") + bottom_extra_spacing=p_value; + + else if (n=="character_set/mode") { + character_set=CharacterSet(int(p_value)); + _change_notify(); + } else if (n=="character_set/custom") + custom_file=p_value; + + else if (n=="shadow/enabled") { + shadow=p_value; + _change_notify(); + }else if (n=="shadow/radius") + shadow_radius=p_value; + else if (n=="shadow/offset") + shadow_offset=p_value; + else if (n=="shadow/color") + shadow_color=p_value; + else if (n=="shadow/transition") + shadow_transition=p_value; + + else if (n=="shadow2/enabled") { + shadow2=p_value; + _change_notify(); + }else if (n=="shadow2/radius") + shadow2_radius=p_value; + else if (n=="shadow2/offset") + shadow2_offset=p_value; + else if (n=="shadow2/color") + shadow2_color=p_value; + else if (n=="shadow2/transition") + shadow2_transition=p_value; + + else if (n=="color/mode") { + color_type=ColorType(int(p_value)); + _change_notify(); + }else if (n=="color/color") + color=p_value; + else if (n=="color/begin") + gradient_begin=p_value; + else if (n=="color/end") + gradient_end=p_value; + else if (n=="color/image") + gradient_image=p_value; + else if (n=="color/monochrome") + color_use_monochrome=p_value; + else if (n=="advanced/round_advance") + round_advance=p_value; + else if (n=="advanced/enable_filter") + enable_filter=p_value; + else if (n=="advanced/premultiply_alpha") + premultiply_alpha=p_value; + else + return false; + + emit_signal("changed"); + + + return true; + + } + + bool _get(const StringName& p_name,Variant &r_ret) const{ + + String n = p_name; + if (n=="mode/mode") + r_ret=font_mode; + else if (n=="extra_space/char") + r_ret=char_extra_spacing; + else if (n=="extra_space/space") + r_ret=space_extra_spacing; + else if (n=="extra_space/top") + r_ret=top_extra_spacing; + else if (n=="extra_space/bottom") + r_ret=bottom_extra_spacing; + + else if (n=="character_set/mode") + r_ret=character_set; + else if (n=="character_set/custom") + r_ret=custom_file; + + else if (n=="shadow/enabled") + r_ret=shadow; + else if (n=="shadow/radius") + r_ret=shadow_radius; + else if (n=="shadow/offset") + r_ret=shadow_offset; + else if (n=="shadow/color") + r_ret=shadow_color; + else if (n=="shadow/transition") + r_ret=shadow_transition; + + else if (n=="shadow2/enabled") + r_ret=shadow2; + else if (n=="shadow2/radius") + r_ret=shadow2_radius; + else if (n=="shadow2/offset") + r_ret=shadow2_offset; + else if (n=="shadow2/color") + r_ret=shadow2_color; + else if (n=="shadow2/transition") + r_ret=shadow2_transition; + + + else if (n=="color/mode") + r_ret=color_type; + else if (n=="color/color") + r_ret=color; + else if (n=="color/begin") + r_ret=gradient_begin; + else if (n=="color/end") + r_ret=gradient_end; + else if (n=="color/image") + r_ret=gradient_image; + else if (n=="color/monochrome") + r_ret=color_use_monochrome; + else if (n=="advanced/round_advance") + r_ret=round_advance; + else if (n=="advanced/enable_filter") + r_ret=enable_filter; + else if (n=="advanced/premultiply_alpha") + r_ret=premultiply_alpha; + else + return false; + + return true; + + } + + void _get_property_list( List<PropertyInfo> *p_list) const{ + + + p_list->push_back(PropertyInfo(Variant::INT,"mode/mode",PROPERTY_HINT_ENUM,"Bitmap,Distance Field")); + + p_list->push_back(PropertyInfo(Variant::INT,"extra_space/char",PROPERTY_HINT_RANGE,"-64,64,1")); + p_list->push_back(PropertyInfo(Variant::INT,"extra_space/space",PROPERTY_HINT_RANGE,"-64,64,1")); + p_list->push_back(PropertyInfo(Variant::INT,"extra_space/top",PROPERTY_HINT_RANGE,"-64,64,1")); + p_list->push_back(PropertyInfo(Variant::INT,"extra_space/bottom",PROPERTY_HINT_RANGE,"-64,64,1")); + p_list->push_back(PropertyInfo(Variant::INT,"character_set/mode",PROPERTY_HINT_ENUM,"Ascii,Latin,Unicode,Custom,Custom&Latin")); + + if (character_set>=CHARSET_CUSTOM) + p_list->push_back(PropertyInfo(Variant::STRING,"character_set/custom",PROPERTY_HINT_GLOBAL_FILE)); + + int usage = PROPERTY_USAGE_DEFAULT; + + if (font_mode==FONT_DISTANCE_FIELD) { + usage = PROPERTY_USAGE_NOEDITOR; + } + + { + + p_list->push_back(PropertyInfo(Variant::BOOL,"shadow/enabled",PROPERTY_HINT_NONE,"",usage)); + if (shadow) { + p_list->push_back(PropertyInfo(Variant::INT,"shadow/radius",PROPERTY_HINT_RANGE,"-64,64,1",usage)); + p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow/offset",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::COLOR,"shadow/color",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::REAL,"shadow/transition",PROPERTY_HINT_EXP_EASING,"",usage)); + } + + p_list->push_back(PropertyInfo(Variant::BOOL,"shadow2/enabled",PROPERTY_HINT_NONE,"",usage)); + if (shadow2) { + p_list->push_back(PropertyInfo(Variant::INT,"shadow2/radius",PROPERTY_HINT_RANGE,"-64,64,1",usage)); + p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow2/offset",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::COLOR,"shadow2/color",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::REAL,"shadow2/transition",PROPERTY_HINT_EXP_EASING,"",usage)); + } + + p_list->push_back(PropertyInfo(Variant::INT,"color/mode",PROPERTY_HINT_ENUM,"White,Color,Gradient,Gradient Image",usage)); + if (color_type==COLOR_CUSTOM) { + p_list->push_back(PropertyInfo(Variant::COLOR,"color/color",PROPERTY_HINT_NONE,"",usage)); + + } + if (color_type==COLOR_GRADIENT_RANGE) { + p_list->push_back(PropertyInfo(Variant::COLOR,"color/begin",PROPERTY_HINT_NONE,"",usage)); + p_list->push_back(PropertyInfo(Variant::COLOR,"color/end",PROPERTY_HINT_NONE,"",usage)); + } + if (color_type==COLOR_GRADIENT_IMAGE) { + p_list->push_back(PropertyInfo(Variant::STRING,"color/image",PROPERTY_HINT_GLOBAL_FILE,"",usage)); + } + p_list->push_back(PropertyInfo(Variant::BOOL,"color/monochrome",PROPERTY_HINT_NONE,"",usage)); + } + + p_list->push_back(PropertyInfo(Variant::BOOL,"advanced/round_advance")); + p_list->push_back(PropertyInfo(Variant::BOOL,"advanced/enable_filter")); + p_list->push_back(PropertyInfo(Variant::BOOL,"advanced/premultiply_alpha")); + + } + + + static void _bind_methods() { + + + ADD_SIGNAL( MethodInfo("changed")); + } + + + void reset() { + + char_extra_spacing=0; + top_extra_spacing=0; + bottom_extra_spacing=0; + space_extra_spacing=0; + + character_set=CHARSET_LATIN; + + shadow=false; + shadow_radius=2; + shadow_color=Color(0,0,0,0.3); + shadow_transition=1.0; + + shadow2=false; + shadow2_radius=2; + shadow2_color=Color(0,0,0,0.3); + shadow2_transition=1.0; + + color_type=COLOR_WHITE; + color=Color(1,1,1,1); + gradient_begin=Color(1,1,1,1); + gradient_end=Color(0.5,0.5,0.5,1); + color_use_monochrome=false; + + font_mode=FONT_BITMAP; + round_advance=true; + enable_filter=true; + premultiply_alpha=false; + + } + + _EditorFontImportOptions() { + + font_mode=FONT_BITMAP; + + char_extra_spacing=0; + top_extra_spacing=0; + bottom_extra_spacing=0; + space_extra_spacing=0; + + character_set=CHARSET_LATIN; + + shadow=false; + shadow_radius=2; + shadow_color=Color(0,0,0,0.3); + shadow_transition=1.0; + + shadow2=false; + shadow2_radius=2; + shadow2_color=Color(0,0,0,0.3); + shadow2_transition=1.0; + + color_type=COLOR_WHITE; + color=Color(1,1,1,1); + gradient_begin=Color(1,1,1,1); + gradient_end=Color(0.5,0.5,0.5,1); + color_use_monochrome=false; + + round_advance=true; + enable_filter=true; + premultiply_alpha=false; + } + + +}; + + +class EditorFontImportDialog : public ConfirmationDialog { + + GDCLASS(EditorFontImportDialog, ConfirmationDialog); + + + EditorLineEditFileChooser *source; + EditorLineEditFileChooser *dest; + SpinBox *font_size; + LineEdit *test_string; + ColorPickerButton *test_color; + Label *test_label; + PropertyEditor *prop_edit; + Timer *timer; + ConfirmationDialog *error_dialog; + + + Ref<ResourceImportMetadata> get_rimd() { + + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + List<PropertyInfo> pl; + options->_get_property_list(&pl); + for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { + + Variant v; + String opt=E->get().name; + options->_get(opt,v); + if (opt=="color/image" || opt=="character_set/custom") { + v = EditorImportPlugin::validate_source_path(v); + } + imd->set_option(opt,v); + } + + String src_path = EditorImportPlugin::validate_source_path(source->get_line_edit()->get_text()); + //print_line("pre src path "+source->get_line_edit()->get_text()); + //print_line("src path "+src_path); + imd->add_source(src_path); + imd->set_option("font/size",font_size->get_value()); + + return imd; + + } + + void _src_changed(String) { + _prop_changed(); + } + + void _update_text2(String) { + _update_text(); + } + void _update_text3(Color) { + _update_text(); + } + + void _update_text() { + + test_label->set_text(""); + test_label->set_text(test_string->get_text()); + test_label->add_color_override("font_color",test_color->get_pick_color()); + } + + void _update() { + + Ref<ResourceImportMetadata> imd = get_rimd(); + Ref<BitmapFont> font = plugin->generate_font(imd); + test_label->add_font_override("font",font); + _update_text(); + } + + void _font_size_changed(double) { + + _prop_changed(); + } + + void _prop_changed() { + + timer->start(); + } + + void _import_inc(String p_font) { + + Ref<BitmapFont> font = ResourceLoader::load(p_font); + if (!font.is_valid()) + return; + Ref<ImageTexture> tex = font->get_texture(0); + if (tex.is_null()) + return; + FileAccessRef f=FileAccess::open(p_font.get_basename()+".inc",FileAccess::WRITE); + Vector<CharType> ck = font->get_char_keys(); + + f->store_line("static const int _builtin_font_height="+itos(font->get_height())+";"); + f->store_line("static const int _builtin_font_ascent="+itos(font->get_ascent())+";"); + f->store_line("static const int _builtin_font_charcount="+itos(ck.size())+";"); + f->store_line("static const int _builtin_font_charrects["+itos(ck.size())+"][8]={"); + f->store_line("/* charidx , ofs_x, ofs_y, size_x, size_y, valign, halign, advance */"); + + for(int i=0;i<ck.size();i++) { + CharType k=ck[i]; + BitmapFont::Character c=font->get_character(k); + f->store_line("{"+itos(k)+","+rtos(c.rect.pos.x)+","+rtos(c.rect.pos.y)+","+rtos(c.rect.size.x)+","+rtos(c.rect.size.y)+","+rtos(c.v_align)+","+rtos(c.h_align)+","+rtos(c.advance)+"},"); + } + f->store_line("};"); + + Vector<BitmapFont::KerningPairKey> kp=font->get_kerning_pair_keys(); + f->store_line("static const int _builtin_font_kerning_pair_count="+itos(kp.size())+";"); + f->store_line("static const int _builtin_font_kerning_pairs["+itos(kp.size())+"][3]={"); + for(int i=0;i<kp.size();i++) { + + int d = font->get_kerning_pair(kp[i].A,kp[i].B); + f->store_line("{"+itos(kp[i].A)+","+itos(kp[i].B)+","+itos(d)+"},"); + } + + f->store_line("};"); + Image img = tex->get_data(); + + f->store_line("static const int _builtin_font_img_width="+itos(img.get_width())+";"); + f->store_line("static const int _builtin_font_img_height="+itos(img.get_height())+";"); + + String fname = p_font.get_basename()+".sv.png"; + ResourceSaver::save(fname,tex); + Vector<uint8_t> data=FileAccess::get_file_as_array(fname); + + + f->store_line("static const int _builtin_font_img_data_size="+itos(data.size())+";"); + f->store_line("static const unsigned char _builtin_font_img_data["+itos(data.size())+"]={"); + + + + for(int i=0;i<data.size();i++) { + + f->store_line(itos(data[i])+","); + + } + f->store_line("};"); + + } + + void _import() { + + if (source->get_line_edit()->get_text()=="") { + error_dialog->set_text(TTR("No source font file!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + if (dest->get_line_edit()->get_text()=="") { + error_dialog->set_text(TTR("No target font resource!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + if (dest->get_line_edit()->get_text().get_file()==".fnt") { + dest->get_line_edit()->set_text(dest->get_line_edit()->get_text().get_base_dir() + "/" + source->get_line_edit()->get_text().get_file().get_basename() + ".fnt" ); + } + + if (dest->get_line_edit()->get_text().get_extension() == dest->get_line_edit()->get_text()) { + dest->get_line_edit()->set_text(dest->get_line_edit()->get_text() + ".fnt"); + } + + if (dest->get_line_edit()->get_text().get_extension().to_lower() != "fnt") { + error_dialog->set_text(TTR("Invalid file extension.\nPlease use .fnt.")); + error_dialog->popup_centered(Size2(200,100)); + return; + } + + Ref<ResourceImportMetadata> rimd = get_rimd(); + + if (rimd.is_null()) { + error_dialog->set_text(TTR("Can't load/process source font.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + Error err = plugin->import(dest->get_line_edit()->get_text(),rimd); + + if (err!=OK) { + error_dialog->set_text(TTR("Couldn't save font.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + _import_inc(dest->get_line_edit()->get_text()); + + hide(); + } + + EditorFontImportPlugin *plugin; + _EditorFontImportOptions *options; + + static void _bind_methods() { + + ClassDB::bind_method("_update",&EditorFontImportDialog::_update); + ClassDB::bind_method("_update_text",&EditorFontImportDialog::_update_text); + ClassDB::bind_method("_update_text2",&EditorFontImportDialog::_update_text2); + ClassDB::bind_method("_update_text3",&EditorFontImportDialog::_update_text3); + ClassDB::bind_method("_prop_changed",&EditorFontImportDialog::_prop_changed); + ClassDB::bind_method("_src_changed",&EditorFontImportDialog::_src_changed); + ClassDB::bind_method("_font_size_changed",&EditorFontImportDialog::_font_size_changed); + ClassDB::bind_method("_import",&EditorFontImportDialog::_import); + + } + +public: + + void _notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_TREE) { + prop_edit->edit(options); + _update_text(); + } + } + + void popup_import(const String& p_path) { + + popup_centered(Size2(600,500)*EDSCALE); + + if (p_path!="") { + + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_path); + ERR_FAIL_COND(!rimd.is_valid()); + + dest->get_line_edit()->set_text(p_path); + List<String> opts; + rimd->get_options(&opts); + options->reset(); + for(List<String>::Element *E=opts.front();E;E=E->next()) { + + options->_set(E->get(),rimd->get_option(E->get())); + } + + String src = ""; + for(int i=0;i<rimd->get_source_count();i++) { + if (i>0) + src+=","; + src+=EditorImportPlugin::expand_source_path(rimd->get_source_path(i)); + } + source->get_line_edit()->set_text(src); + + font_size->set_value(rimd->get_option("font/size")); + } + } + + + void set_source_and_dest(const String& p_font,const String& p_dest) { + source->get_line_edit()->set_text(p_font); + dest->get_line_edit()->set_text(p_dest); + _prop_changed(); + } + + EditorFontImportDialog(EditorFontImportPlugin *p_plugin) { + plugin=p_plugin; + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + //set_child_rect(vbc); + HBoxContainer *hbc = memnew( HBoxContainer); + vbc->add_child(hbc); + VBoxContainer *vbl = memnew( VBoxContainer ); + hbc->add_child(vbl); + hbc->set_v_size_flags(SIZE_EXPAND_FILL); + vbl->set_h_size_flags(SIZE_EXPAND_FILL); + VBoxContainer *vbr = memnew( VBoxContainer ); + hbc->add_child(vbr); + vbr->set_h_size_flags(SIZE_EXPAND_FILL); + + source = memnew( EditorLineEditFileChooser ); + source->get_file_dialog()->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + source->get_file_dialog()->set_mode(EditorFileDialog::MODE_OPEN_FILE); + source->get_file_dialog()->add_filter("*.ttf;TrueType"); + source->get_file_dialog()->add_filter("*.otf;OpenType"); + source->get_file_dialog()->add_filter("*.fnt;BMFont"); + source->get_line_edit()->connect("text_entered",this,"_src_changed"); + + vbl->add_margin_child(TTR("Source Font:"),source); + font_size = memnew( SpinBox ); + vbl->add_margin_child(TTR("Source Font Size:"),font_size); + font_size->set_min(3); + font_size->set_max(256); + font_size->set_value(16); + font_size->connect("value_changed",this,"_font_size_changed"); + dest = memnew( EditorLineEditFileChooser ); + // + List<String> fl; + Ref<BitmapFont> font= memnew(BitmapFont); + dest->get_file_dialog()->add_filter("*.fnt ; Font" ); + /* + ResourceSaver::get_recognized_extensions(font,&fl); + for(List<String>::Element *E=fl.front();E;E=E->next()) { + dest->get_file_dialog()->add_filter("*."+E->get()); + } + */ + + vbl->add_margin_child(TTR("Dest Resource:"),dest); + HBoxContainer *testhb = memnew( HBoxContainer ); + test_string = memnew( LineEdit ); + test_string->set_text(TTR("The quick brown fox jumps over the lazy dog.")); + test_string->set_h_size_flags(SIZE_EXPAND_FILL); + test_string->set_stretch_ratio(5); + + testhb->add_child(test_string); + test_color = memnew( ColorPickerButton ); + test_color->set_pick_color(get_color("font_color","Label")); + test_color->set_h_size_flags(SIZE_EXPAND_FILL); + test_color->set_stretch_ratio(1); + test_color->connect("color_changed",this,"_update_text3"); + testhb->add_child(test_color); + + vbl->add_spacer(); + vbl->add_margin_child(TTR("Test:")+" ",testhb); + /* + HBoxContainer *upd_hb = memnew( HBoxContainer ); + //vbl->add_child(upd_hb); + upd_hb->add_spacer(); + Button *update = memnew( Button); + upd_hb->add_child(update); + update->set_text("Update"); + update->connect("pressed",this,"_update"); +*/ + options = memnew( _EditorFontImportOptions ); + prop_edit = memnew( PropertyEditor() ); + vbr->add_margin_child(TTR("Options:"),prop_edit,true); + options->connect("changed",this,"_prop_changed"); + + prop_edit->hide_top_label(); + + Panel *panel = memnew( Panel ); + vbc->add_child(panel); + test_label = memnew( Label ); + test_label->set_autowrap(true); + panel->add_child(test_label); + test_label->set_area_as_parent_rect(); + panel->set_v_size_flags(SIZE_EXPAND_FILL); + test_string->connect("text_changed",this,"_update_text2"); + set_title(TTR("Font Import")); + timer = memnew( Timer ); + add_child(timer); + timer->connect("timeout",this,"_update"); + timer->set_wait_time(0.4); + timer->set_one_shot(true); + + get_ok()->connect("pressed", this,"_import"); + get_ok()->set_text(TTR("Import")); + + error_dialog = memnew ( ConfirmationDialog ); + add_child(error_dialog); + error_dialog->get_ok()->set_text(TTR("Accept")); + set_hide_on_ok(false); + + + } + + ~EditorFontImportDialog() { + memdelete(options); + } +}; + + +/////////////////////////////////////// + + + +struct _EditorFontData { + + Vector<uint8_t> bitmap; + int width,height; + int ofs_x; //ofset to center, from ABOVE + int ofs_y; //ofset to begining, from LEFT + int valign; //vertical alignment + int halign; + float advance; + int character; + int glyph; + + int texture; + Image blit; + Point2i blit_ofs; + //bool printable; + +}; + + +struct _EditorFontDataSort { + + bool operator()(const _EditorFontData *p_A,const _EditorFontData *p_B) const { + return p_A->height > p_B->height; + }; +}; + +struct _EditorKerningKey { + + CharType A,B; + bool operator<(const _EditorKerningKey& p_k) const { return (A==p_k.A)?(B<p_k.B):(A<p_k.A); } + +}; + + +static unsigned char get_SDF_radial( + unsigned char *fontmap, + int w, int h, + int x, int y, + int max_radius ) +{ + //hideous brute force method + float d2 = max_radius*max_radius+1.0; + unsigned char v = fontmap[x+y*w]; + for( int radius = 1; (radius <= max_radius) && (radius*radius < d2); ++radius ) + { + int line, lo, hi; + //north + line = y - radius; + if( (line >= 0) && (line < h) ) + { + lo = x - radius; + hi = x + radius; + if( lo < 0 ) { lo = 0; } + if( hi >= w ) { hi = w-1; } + int idx = line * w + lo; + for( int i = lo; i <= hi; ++i ) + { + //check this pixel + if( fontmap[idx] != v ) + { + float nx = i - x; + float ny = line - y; + float nd2 = nx*nx+ny*ny; + if( nd2 < d2 ) + { + d2 = nd2; + } + } + //move on + ++idx; + } + } + //south + line = y + radius; + if( (line >= 0) && (line < h) ) + { + lo = x - radius; + hi = x + radius; + if( lo < 0 ) { lo = 0; } + if( hi >= w ) { hi = w-1; } + int idx = line * w + lo; + for( int i = lo; i <= hi; ++i ) + { + //check this pixel + if( fontmap[idx] != v ) + { + float nx = i - x; + float ny = line - y; + float nd2 = nx*nx+ny*ny; + if( nd2 < d2 ) + { + d2 = nd2; + } + } + //move on + ++idx; + } + } + //west + line = x - radius; + if( (line >= 0) && (line < w) ) + { + lo = y - radius + 1; + hi = y + radius - 1; + if( lo < 0 ) { lo = 0; } + if( hi >= h ) { hi = h-1; } + int idx = lo * w + line; + for( int i = lo; i <= hi; ++i ) + { + //check this pixel + if( fontmap[idx] != v ) + { + float nx = line - x; + float ny = i - y; + float nd2 = nx*nx+ny*ny; + if( nd2 < d2 ) + { + d2 = nd2; + } + } + //move on + idx += w; + } + } + //east + line = x + radius; + if( (line >= 0) && (line < w) ) + { + lo = y - radius + 1; + hi = y + radius - 1; + if( lo < 0 ) { lo = 0; } + if( hi >= h ) { hi = h-1; } + int idx = lo * w + line; + for( int i = lo; i <= hi; ++i ) + { + //check this pixel + if( fontmap[idx] != v ) + { + float nx = line - x; + float ny = i - y; + float nd2 = nx*nx+ny*ny; + if( nd2 < d2 ) + { + d2 = nd2; + } + } + //move on + idx += w; + } + } + } + d2 = sqrtf( d2 ); + if( v==0 ) + { + d2 = -d2; + } + d2 *= 127.5 / max_radius; + d2 += 127.5; + if( d2 < 0.0 ) d2 = 0.0; + if( d2 > 255.0 ) d2 = 255.0; + return (unsigned char)(d2 + 0.5); +} + + +Ref<BitmapFont> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata>& p_from, const String &p_existing) { + + + + Ref<ResourceImportMetadata> from = p_from; + ERR_FAIL_COND_V(from->get_source_count()!=1,Ref<BitmapFont>()); + + String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0)); + + if (src_path.get_extension().to_lower()=="fnt") { + + if (ResourceLoader::load(src_path).is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("Path:")+" "+src_path+"\n"+TTR("This file is already a Godot font file, please supply a BMFont type file instead.")); + return Ref<BitmapFont>(); + } + + Ref<BitmapFont> font; + font.instance(); + Error err = font->create_from_fnt(src_path); + if (err) { + EditorNode::get_singleton()->show_warning(TTR("Path:")+" "+src_path+"\n"+TTR("Failed opening as BMFont file.")); + return Ref<BitmapFont>(); + } + + return font; + } + + int size = from->get_option("font/size"); + +#ifdef FREETYPE_ENABLED + FT_Library library; /* handle to library */ + FT_Face face; /* handle to face object */ + + Vector<_EditorFontData*> font_data_list; + + int error = FT_Init_FreeType( &library ); + + ERR_EXPLAIN(TTR("Error initializing FreeType.")); + ERR_FAIL_COND_V( error !=0, Ref<BitmapFont>() ); + + print_line("loadfrom: "+src_path); + error = FT_New_Face( library, src_path.utf8().get_data(),0,&face ); + + if ( error == FT_Err_Unknown_File_Format ) { + ERR_EXPLAIN(TTR("Unknown font format.")); + FT_Done_FreeType( library ); + } else if ( error ) { + + ERR_EXPLAIN(TTR("Error loading font.")); + FT_Done_FreeType( library ); + + } + + ERR_FAIL_COND_V(error,Ref<BitmapFont>()); + + + int height=0; + int ascent=0; + int font_spacing=0; + + error = FT_Set_Char_Size(face,0,64*size,512,512); + + if ( error ) { + FT_Done_FreeType( library ); + ERR_EXPLAIN(TTR("Invalid font size.")); + ERR_FAIL_COND_V( error,Ref<BitmapFont>() ); + + } + + int font_mode = from->get_option("mode/mode"); + + int scaler=(font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD)?16:1; + + error = FT_Set_Pixel_Sizes(face,0,size*scaler); + + FT_GlyphSlot slot = face->glyph; + + //error = FT_Set_Charmap(face,ft_encoding_unicode ); /* encoding.. */ + + + /* PRINT CHARACTERS TO INDIVIDUAL BITMAPS */ + + + //int space_size=5; //size for space, if none found.. 5! + //int min_valign=500; //some ridiculous number + + FT_ULong charcode; + FT_UInt gindex; + + int max_up=-1324345; ///gibberish + int max_down=124232; + + Map<_EditorKerningKey,int> kerning_map; + + charcode = FT_Get_First_Char( face, &gindex ); + + Set<CharType> import_chars; + + int import_mode = from->get_option("character_set/mode"); + bool round_advance = from->get_option("advanced/round_advance"); + + if (import_mode>=_EditorFontImportOptions::CHARSET_CUSTOM) { + + //load from custom text + String path = from->get_option("character_set/custom"); + + FileAccess *fa = FileAccess::open(EditorImportPlugin::expand_source_path(path),FileAccess::READ); + + if ( !fa ) { + + FT_Done_FreeType( library ); + ERR_EXPLAIN(TTR("Invalid font custom source.")); + ERR_FAIL_COND_V( !fa,Ref<BitmapFont>() ); + + } + + + while(!fa->eof_reached()) { + + String line = fa->get_line(); + for(int i=0;i<line.length();i++) { + import_chars.insert(line[i]); + } + } + + if (import_mode==_EditorFontImportOptions::CHARSET_CUSTOM_LATIN) { + + for(int i=32;i<128;i++) + import_chars.insert(i); + } + + memdelete(fa); + } + + int xsize=0; + while ( gindex != 0 ) + { + + bool skip=false; + error = FT_Load_Char( face, charcode, font_mode==_EditorFontImportOptions::FONT_BITMAP?FT_LOAD_RENDER:FT_LOAD_MONOCHROME ); + if (error) skip=true; + else error = FT_Render_Glyph( face->glyph, font_mode==_EditorFontImportOptions::FONT_BITMAP?ft_render_mode_normal:ft_render_mode_mono ); + if (error) { + skip=true; + } else if (!skip) { + + switch(import_mode) { + + case _EditorFontImportOptions::CHARSET_ASCII: skip = charcode>127; break; + case _EditorFontImportOptions::CHARSET_LATIN: skip = charcode>255 ;break; + case _EditorFontImportOptions::CHARSET_UNICODE: break; //none + case _EditorFontImportOptions::CHARSET_CUSTOM: + case _EditorFontImportOptions::CHARSET_CUSTOM_LATIN: skip = !import_chars.has(charcode); break; + + } + } + + if (charcode<=32) //?? + skip=true; + + if (skip) { + charcode=FT_Get_Next_Char(face,charcode,&gindex); + continue; + } + + _EditorFontData * fdata = memnew( _EditorFontData ); + + + int w = slot->bitmap.width; + int h = slot->bitmap.rows; + int p = slot->bitmap.pitch; + + //print_line("W: "+itos(w)+" P: "+itos(slot->bitmap.pitch)); + + if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) { + + //oversize the holding buffer so I can smooth it! + int sw = w + scaler * 4; + int sh = h + scaler * 4; + //do the SDF + int sdfw = sw / scaler; + int sdfh = sh / scaler; + + fdata->width=sdfw; + fdata->height=sdfh; + } else { + fdata->width=w; + fdata->height=h; + } + + fdata->character=charcode; + fdata->glyph=FT_Get_Char_Index(face,charcode); + if (charcode=='x') + xsize=w/scaler; + + + + fdata->valign=slot->bitmap_top; + fdata->halign=slot->bitmap_left; + + if (round_advance) + fdata->advance=(slot->advance.x+(1<<5))>>6; + else + fdata->advance=slot->advance.x/float(1<<6); + + if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) { + + fdata->halign = fdata->halign / scaler - 1.5; + fdata->valign = fdata->valign / scaler + 1.5; + fdata->advance/=scaler; + + } + + fdata->advance+=font_spacing; + + + if (charcode<127) { + int top = fdata->valign; + int hmax = h/scaler; + + if (top>max_up) { + + max_up=top; + } + + + if ( (top - hmax)<max_down ) { + + max_down=top - hmax; + } + } + + if (font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD) { + + + //oversize the holding buffer so I can smooth it! + int sw = w + scaler * 4; + int sh = h + scaler * 4; + + unsigned char *smooth_buf = new unsigned char[sw*sh]; + + for( int i = 0; i < sw*sh; ++i ) { + smooth_buf[i] = 0; + } + + // copy the glyph into the buffer to be smoothed + unsigned char *buf = slot->bitmap.buffer; + for( int j = 0; j < h; ++j ) { + for( int i = 0; i < w; ++i ) { + smooth_buf[scaler*2+i+(j+scaler*2)*sw] = 255 * ((buf[j*p+(i>>3)] >> (7 - (i & 7))) & 1); + } + } + + // do the SDF + int sdfw = fdata->width; + int sdfh = fdata->height; + + fdata->bitmap.resize( sdfw*sdfh ); + + for( int j = 0; j < sdfh; ++j ) { + for( int i = 0; i < sdfw; ++i ) { + int pd_idx = j*sdfw+i; + + //fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i]; + + fdata->bitmap[pd_idx] = + //get_SDF + get_SDF_radial + ( smooth_buf, sw, sh, + i*scaler + (scaler >>1), j*scaler + (scaler >>1), + 2*scaler ); + + } + } + + delete [] smooth_buf; + + } else { + fdata->bitmap.resize( slot->bitmap.width*slot->bitmap.rows ); + for (int i=0;i<slot->bitmap.width;i++) { + for (int j=0;j<slot->bitmap.rows;j++) { + + fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i]; + } + } + } + + font_data_list.push_back(fdata); + charcode=FT_Get_Next_Char(face,charcode,&gindex); +// printf("reading char %i\n",charcode); + } + + /* SPACE */ + + _EditorFontData *spd = memnew( _EditorFontData ); + spd->advance=0; + spd->character=' '; + spd->halign=0; + spd->valign=0; + spd->width=0; + spd->height=0; + spd->ofs_x=0; + spd->ofs_y=0; + + if (!FT_Load_Char( face, ' ', FT_LOAD_RENDER ) && !FT_Render_Glyph( face->glyph, font_mode==_EditorFontImportOptions::FONT_BITMAP?ft_render_mode_normal:ft_render_mode_mono )) { + + spd->advance = slot->advance.x>>6; //round to nearest or store as float + spd->advance/=scaler; + spd->advance+=font_spacing; + } else { + + spd->advance=xsize; + spd->advance+=font_spacing; + } + + font_data_list.push_back(spd); + + Set<CharType> exported; + for (int i=0; i<font_data_list.size(); i++) { + exported.insert(font_data_list[i]->character); + }; + int missing = 0; + for(Set<CharType>::Element *E=import_chars.front();E;E=E->next()) { + CharType c = E->get(); + if (!exported.has(c)) { + CharType str[2] = {c, 0}; + printf("** Warning: character %i (%ls) not exported\n", (int)c, str); + ++missing; + }; + }; + print_line("total_chars: "+itos(font_data_list.size())); + + /* KERNING */ + + + for(int i=0;i<font_data_list.size();i++) { + + if (font_data_list[i]->character>512) + continue; + for(int j=0;j<font_data_list.size();j++) { + + if (font_data_list[j]->character>512) + continue; + + FT_Vector delta; + FT_Get_Kerning( face, font_data_list[i]->glyph,font_data_list[j]->glyph, FT_KERNING_DEFAULT, &delta ); + + if (delta.x!=0) { + + _EditorKerningKey kpk; + kpk.A = font_data_list[i]->character; + kpk.B = font_data_list[j]->character; + int kern = ((-delta.x)+(1<<5))>>6; + + if (kern==0) + continue; + kerning_map[kpk]=kern/scaler; + } + } + } + + height=max_up-max_down; + ascent=max_up; + + /* FIND OUT WHAT THE FONT HEIGHT FOR THIS IS */ + + /* ADJUST THE VALIGN FOR EACH CHARACTER */ + + for (int i=0;i<(int)font_data_list.size();i++) { + + font_data_list[i]->valign=max_up-font_data_list[i]->valign; + } + + + + /* ADD THE SPACEBAR CHARACTER */ +/* + _EditorFontData * fdata = new _EditorFontData; + + fdata->character=32; + fdata->bitmap=0; + fdata->width=xsize; + fdata->height=1; + fdata->valign=0; + + font_data_list.push_back(fdata); +*/ + /* SORT BY HEIGHT, SO THEY FIT BETTER ON THE TEXTURE */ + + font_data_list.sort_custom<_EditorFontDataSort>(); + Color *color=memnew_arr(Color,height); + + int gradient_type=from->get_option("color/mode"); + switch(gradient_type) { + case _EditorFontImportOptions::COLOR_WHITE: { + + for(int i=0;i<height;i++){ + color[i]=Color(1,1,1,1); + } + + } break; + case _EditorFontImportOptions::COLOR_CUSTOM: { + + Color cc = from->get_option("color/color"); + for(int i=0;i<height;i++){ + color[i]=cc; + } + + } break; + case _EditorFontImportOptions::COLOR_GRADIENT_RANGE: { + + Color src=from->get_option("color/begin"); + Color to=from->get_option("color/end"); + for(int i=0;i<height;i++){ + color[i]=src.linear_interpolate(to,i/float(height)); + } + + } break; + case _EditorFontImportOptions::COLOR_GRADIENT_IMAGE: { + + String fp = EditorImportPlugin::expand_source_path(from->get_option("color/image")); + Image img; + Error err = ImageLoader::load_image(fp,&img); + if (err==OK) { + + for(int i=0;i<height;i++){ + //color[i]=img.get_pixel(0,i*img.get_height()/height); + } + } else { + + for(int i=0;i<height;i++){ + color[i]=Color(1,1,1,1); + } + } + + } break; + } + + + for(int i=0;i<font_data_list.size();i++) { + + if (font_data_list[i]->bitmap.size()==0) + continue; + + int margin[4]={0,0,0,0}; + + if (from->get_option("shadow/enabled").operator bool()) { + int r=from->get_option("shadow/radius"); + Point2i ofs=Point2(from->get_option("shadow/offset")); + margin[ MARGIN_LEFT ] = MAX( r - ofs.x, 0); + margin[ MARGIN_RIGHT ] = MAX( r + ofs.x, 0); + margin[ MARGIN_TOP ] = MAX( r - ofs.y, 0); + margin[ MARGIN_BOTTOM ] = MAX( r + ofs.y, 0); + + } + + if (from->get_option("shadow2/enabled").operator bool()) { + int r=from->get_option("shadow2/radius"); + Point2i ofs=Point2(from->get_option("shadow2/offset")); + margin[ MARGIN_LEFT ] = MAX( r - ofs.x, margin[ MARGIN_LEFT ]); + margin[ MARGIN_RIGHT ] = MAX( r + ofs.x, margin[ MARGIN_RIGHT ]); + margin[ MARGIN_TOP ] = MAX( r - ofs.y, margin[ MARGIN_TOP ]); + margin[ MARGIN_BOTTOM ] = MAX( r + ofs.y, margin[ MARGIN_BOTTOM ]); + + } + + Size2i s; + s.width=font_data_list[i]->width+margin[MARGIN_LEFT]+margin[MARGIN_RIGHT]; + s.height=font_data_list[i]->height+margin[MARGIN_TOP]+margin[MARGIN_BOTTOM]; + Point2i o; + o.x=margin[MARGIN_LEFT]; + o.y=margin[MARGIN_TOP]; + + int ow=font_data_list[i]->width; + int oh=font_data_list[i]->height; + + PoolVector<uint8_t> pixels; + pixels.resize(s.x*s.y*4); + + PoolVector<uint8_t>::Write w = pixels.write(); + //print_line("val: "+itos(font_data_list[i]->valign)); + for(int y=0;y<s.height;y++) { + + int yc=CLAMP(y-o.y+font_data_list[i]->valign,0,height-1); + Color c=color[yc]; + c.a=0; + + for(int x=0;x<s.width;x++) { + + int ofs=y*s.x+x; + w[ofs*4+0]=c.r*255.0; + w[ofs*4+1]=c.g*255.0; + w[ofs*4+2]=c.b*255.0; + w[ofs*4+3]=c.a*255.0; + } + } + + + for(int si=0;si<2;si++) { + +#define S_VAR(m_v) (String(si==0?"shadow/":"shadow2/")+m_v) + if (from->get_option(S_VAR("enabled")).operator bool()) { + int r = from->get_option(S_VAR("radius")); + + Color sc = from->get_option(S_VAR("color")); + Point2i so=Point2(from->get_option(S_VAR("offset"))); + + float tr = from->get_option(S_VAR("transition")); + print_line("shadow enabled: "+itos(si)); + + Vector<uint8_t> s2buf; + s2buf.resize(s.x*s.y); + uint8_t *wa=s2buf.ptr(); + + for(int j=0;j<s.x*s.y;j++){ + + wa[j]=0; + } + + // blit shadowa + for(int x=0;x<ow;x++) { + for(int y=0;y<oh;y++) { + int ofs = (o.y+y+so.y)*s.x+x+o.x+so.x; + wa[ofs]=font_data_list[i]->bitmap[y*ow+x]; + } + } + //blur shadow2 with separatable convolution + + if (r>0) { + + Vector<uint8_t> pixels2; + pixels2.resize(s2buf.size()); + uint8_t *w2=pixels2.ptr(); + //vert + for(int x=0;x<s.width;x++) { + for(int y=0;y<s.height;y++) { + + int ofs = y*s.width+x; + int sum=wa[ofs]; + + for(int k=1;k<=r;k++) { + + int ofs_d=MIN(y+k,s.height-1)*s.width+x; + int ofs_u=MAX(y-k,0)*s.width+x; + sum+=wa[ofs_d]; + sum+=wa[ofs_u]; + } + + w2[ofs]=sum/(r*2+1); + + } + } + //horiz + for(int x=0;x<s.width;x++) { + for(int y=0;y<s.height;y++) { + + int ofs = y*s.width+x; + int sum=w2[ofs]; + + for(int k=1;k<=r;k++) { + + int ofs_r=MIN(x+k,s.width-1)+s.width*y; + int ofs_l=MAX(x-k,0)+s.width*y; + sum+=w2[ofs_r]; + sum+=w2[ofs_l]; + } + + wa[ofs]=Math::pow(float(sum/(r*2+1))/255.0f,tr)*255.0f; + + } + } + + } + + //blend back + + for(int j=0;j<s.x*s.y;j++){ + Color wd(w[j*4+0]/255.0,w[j*4+1]/255.0,w[j*4+2]/255.0,w[j*4+3]/255.0); + Color ws(sc.r,sc.g,sc.b,sc.a*(wa[j]/255.0)); + Color b = wd.blend(ws); + + w[j*4+0]=b.r*255.0; + w[j*4+1]=b.g*255.0; + w[j*4+2]=b.b*255.0; + w[j*4+3]=b.a*255.0; + + } + } + } + + for(int y=0;y<oh;y++) { + int yc=CLAMP(y+font_data_list[i]->valign,0,height-1); + Color sc=color[yc]; + for(int x=0;x<ow;x++) { + int ofs = (o.y+y)*s.x+x+o.x; + float c = font_data_list[i]->bitmap[y*ow+x]/255.0; + Color src_col=sc; + src_col.a*=c; + Color dst_col(w[ofs*4+0]/255.0,w[ofs*4+1]/255.0,w[ofs*4+2]/255.0,w[ofs*4+3]/255.0); + dst_col = dst_col.blend(src_col); + w[ofs*4+0]=dst_col.r*255.0; + w[ofs*4+1]=dst_col.g*255.0; + w[ofs*4+2]=dst_col.b*255.0; + w[ofs*4+3]=dst_col.a*255.0; + } + } + + + w=PoolVector<uint8_t>::Write(); + + Image img(s.width,s.height,0,Image::FORMAT_RGBA8,pixels); + + font_data_list[i]->blit=img; + font_data_list[i]->blit_ofs=o; + + } + + //make atlas + int spacing=2; + Vector<Size2i> sizes; + sizes.resize(font_data_list.size()); + for(int i=0;i<font_data_list.size();i++) { + + sizes[i]=Size2(font_data_list[i]->blit.get_width()+spacing*2,font_data_list[i]->blit.get_height()+spacing*2); + + } + Vector<Point2i> res; + Size2i res_size; + EditorAtlas::fit(sizes,res,res_size); + res_size.x=nearest_power_of_2(res_size.x); + res_size.y=nearest_power_of_2(res_size.y); + print_line("Atlas size: "+res_size); + + Image atlas(res_size.x,res_size.y,0,Image::FORMAT_RGBA8); + + for(int i=0;i<font_data_list.size();i++) { + + if (font_data_list[i]->bitmap.size()==0) + continue; + atlas.blit_rect(font_data_list[i]->blit,Rect2(0,0,font_data_list[i]->blit.get_width(),font_data_list[i]->blit.get_height()),res[i]+Size2(spacing,spacing)); + font_data_list[i]->ofs_x=res[i].x+spacing; + font_data_list[i]->ofs_y=res[i].y+spacing; + + + } + + if (from->has_option("advanced/premultiply_alpha") && bool(from->get_option("advanced/premultiply_alpha"))) { + + PoolVector<uint8_t> data = atlas.get_data(); + int dl = data.size(); + { + PoolVector<uint8_t>::Write w = data.write(); + + for(int i=0;i<dl;i+=4) { + + w[i+0]= uint8_t(int(w[i+0])*int(w[i+3])/255); + w[i+1]= uint8_t(int(w[i+1])*int(w[i+3])/255); + w[i+2]= uint8_t(int(w[i+2])*int(w[i+3])/255); + } + } + + atlas=Image(res_size.x,res_size.y,0,Image::FORMAT_RGBA8,data); + } + + if (from->has_option("color/monochrome") && bool(from->get_option("color/monochrome"))) { + + atlas.convert(Image::FORMAT_LA8); + } + + + if (0) { + //debug the texture + Ref<ImageTexture> atlast = memnew( ImageTexture ); + atlast->create_from_image(atlas); + //atlast->create_from_image(font_data_list[5]->blit); + TextureRect *tf = memnew( TextureRect ); + tf->set_texture(atlast); + dialog->add_child(tf); + } + + + /* CREATE FONT */ + + int char_space = from->get_option("extra_space/char"); + int space_space = from->get_option("extra_space/space"); + int top_space = from->get_option("extra_space/top"); + int bottom_space = from->get_option("extra_space/bottom"); + bool enable_filter = from->get_option("advanced/enable_filter"); + if (from->has_option("advanced/disable_filter")){ // this is a compatibility check for a deprecated option + enable_filter = !from->get_option("advanced/disable_filter"); + } + + Ref<BitmapFont> font; + + if (p_existing!=String() && ResourceCache::has(p_existing)) { + + font = Ref<BitmapFont>( ResourceCache::get(p_existing)->cast_to<BitmapFont>()); + } + + if (font.is_null()) { + font = Ref<BitmapFont>( memnew( BitmapFont ) ); + } + + font->clear(); + font->set_height(height+bottom_space+top_space); + font->set_ascent(ascent+top_space); + font->set_distance_field_hint(font_mode==_EditorFontImportOptions::FONT_DISTANCE_FIELD); + + //register texures + { + Ref<ImageTexture> t = memnew(ImageTexture); + int flags; + if (!enable_filter) + flags=0; + else + flags=Texture::FLAG_FILTER; + t->create_from_image(atlas,flags); + t->set_storage( ImageTexture::STORAGE_COMPRESS_LOSSLESS ); + font->add_texture(t); + + } + //register characters + + + for(int i=0;i<font_data_list.size();i++) { + _EditorFontData *fd=font_data_list[i]; + int tex_idx=0; + + font->add_char(fd->character,tex_idx,Rect2( fd->ofs_x, fd->ofs_y, fd->blit.get_width(), fd->blit.get_height()),Point2(fd->halign-fd->blit_ofs.x,fd->valign-fd->blit_ofs.y+top_space), fd->advance+char_space+(fd->character==' '?space_space:0)); + memdelete(fd); + } + + for(Map<_EditorKerningKey,int>::Element *E=kerning_map.front();E;E=E->next()) { + + font->add_kerning_pair(E->key().A,E->key().B,E->get()); + } + + FT_Done_FreeType( library ); + + return font; +#else + + return Ref<BitmapFont>(); +#endif +} + + +String EditorFontImportPlugin::get_name() const { + + return "font"; +} +String EditorFontImportPlugin::get_visible_name() const{ + + return TTR("Font"); +} +void EditorFontImportPlugin::import_dialog(const String& p_from){ + + dialog->popup_import(p_from); +} +Error EditorFontImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from){ + + + Ref<BitmapFont> font = EditorFontImportPlugin::generate_font(p_from,p_path); + if (!font.is_valid()) + return ERR_CANT_CREATE; + + Ref<ResourceImportMetadata> from=p_from; + from->set_source_md5(0,FileAccess::get_md5(EditorImportPlugin::expand_source_path(from->get_source_path(0)))); + from->set_editor(get_name()); + font->set_import_metadata(from); + + return ResourceSaver::save(p_path,font); + +} + +void EditorFontImportPlugin::import_from_drop(const Vector<String>& p_drop, const String &p_dest_path) { + + for(int i=0;i<p_drop.size();i++) { + String ext = p_drop[i].get_extension().to_lower(); + String file = p_drop[i].get_file(); + if (ext=="ttf" || ext=="otf" || ext=="fnt") { + + import_dialog(); + dialog->set_source_and_dest(p_drop[i],p_dest_path.plus_file(file.get_basename()+".fnt")); + break; + } + } +} + + +EditorFontImportPlugin::EditorFontImportPlugin(EditorNode* p_editor) { + + dialog = memnew( EditorFontImportDialog(this) ); + p_editor->get_gui_base()->add_child(dialog); +} +#endif diff --git a/editor/io_plugins/editor_font_import_plugin.h b/editor/io_plugins/editor_font_import_plugin.h new file mode 100644 index 0000000000..315a80e8cd --- /dev/null +++ b/editor/io_plugins/editor_font_import_plugin.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* editor_font_import_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_FONT_IMPORT_PLUGIN_H +#define EDITOR_FONT_IMPORT_PLUGIN_H + +#include "editor/editor_export.h" +#include "scene/resources/font.h" +#if 0 +class EditorNode; +class EditorFontImportDialog; + +class EditorFontImportPlugin : public EditorImportPlugin { + + GDCLASS(EditorFontImportPlugin,EditorImportPlugin); + + EditorFontImportDialog *dialog; +public: + + Ref<BitmapFont> generate_font(const Ref<ResourceImportMetadata>& p_from,const String& p_existing=String()); //used by editor + + virtual String get_name() const; + virtual String get_visible_name() const; + virtual void import_dialog(const String& p_from=""); + virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from); + virtual void import_from_drop(const Vector<String>& p_drop,const String& p_dest_path); + + + EditorFontImportPlugin(EditorNode* p_editor); +}; + +#endif // EDITOR_FONT_IMPORT_PLUGIN_H +#endif diff --git a/editor/io_plugins/editor_mesh_import_plugin.cpp b/editor/io_plugins/editor_mesh_import_plugin.cpp new file mode 100644 index 0000000000..df9d0a62e6 --- /dev/null +++ b/editor/io_plugins/editor_mesh_import_plugin.cpp @@ -0,0 +1,593 @@ +/*************************************************************************/ +/* editor_mesh_import_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_mesh_import_plugin.h" + +#if 0 + +#include "editor/editor_file_dialog.h" +#include "editor/editor_dir_dialog.h" +#include "editor/editor_node.h" +#include "editor/property_editor.h" +//#include "scene/resources/sample.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "io/marshalls.h" +#include "scene/resources/surface_tool.h" + +class _EditorMeshImportOptions : public Object { + + GDCLASS(_EditorMeshImportOptions,Object); +public: + + + bool generate_tangents; + bool generate_normals; + bool flip_faces; + bool smooth_shading; + bool weld_vertices; + bool import_material; + bool import_textures; + float weld_tolerance; + + + bool _set(const StringName& p_name, const Variant& p_value) { + + String n = p_name; + if (n=="generate/tangents") + generate_tangents=p_value; + else if (n=="generate/normals") + generate_normals=p_value; + else if (n=="import/materials") + import_material=p_value; + else if (n=="import/textures") + import_textures=p_value; + else if (n=="force/flip_faces") + flip_faces=p_value; + else if (n=="force/smooth_shading") + smooth_shading=p_value; + else if (n=="force/weld_vertices") + weld_vertices=p_value; + else if (n=="force/weld_tolerance") + weld_tolerance=p_value; + else + return false; + + return true; + + } + + bool _get(const StringName& p_name,Variant &r_ret) const{ + + String n = p_name; + if (n=="generate/tangents") + r_ret=generate_tangents; + else if (n=="generate/normals") + r_ret=generate_normals; + else if (n=="import/materials") + r_ret=import_material; + else if (n=="import/textures") + r_ret=import_textures; + else if (n=="force/flip_faces") + r_ret=flip_faces; + else if (n=="force/smooth_shading") + r_ret=smooth_shading; + else if (n=="force/weld_vertices") + r_ret=weld_vertices; + else if (n=="force/weld_tolerance") + r_ret=weld_tolerance; + else + return false; + + return true; + + } + void _get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back(PropertyInfo(Variant::BOOL,"generate/tangents")); + p_list->push_back(PropertyInfo(Variant::BOOL,"generate/normals")); + //not for nowp + //p_list->push_back(PropertyInfo(Variant::BOOL,"import/materials")); + //p_list->push_back(PropertyInfo(Variant::BOOL,"import/textures")); + p_list->push_back(PropertyInfo(Variant::BOOL,"force/flip_faces")); + p_list->push_back(PropertyInfo(Variant::BOOL,"force/smooth_shading")); + p_list->push_back(PropertyInfo(Variant::BOOL,"force/weld_vertices")); + p_list->push_back(PropertyInfo(Variant::REAL,"force/weld_tolerance",PROPERTY_HINT_RANGE,"0.00001,16,0.00001")); + //p_list->push_back(PropertyInfo(Variant::BOOL,"compress/enable")); + //p_list->push_back(PropertyInfo(Variant::INT,"compress/bitrate",PROPERTY_HINT_ENUM,"64,96,128,192")); + + + } + + + static void _bind_methods() { + + + ADD_SIGNAL( MethodInfo("changed")); + } + + + _EditorMeshImportOptions() { + + generate_tangents=true; + generate_normals=false; + flip_faces=false; + smooth_shading=false; + weld_vertices=true; + weld_tolerance=0.0001; + import_material=false; + import_textures=false; + + } + + +}; + +class EditorMeshImportDialog : public ConfirmationDialog { + + GDCLASS(EditorMeshImportDialog,ConfirmationDialog); + + EditorMeshImportPlugin *plugin; + + LineEdit *import_path; + LineEdit *save_path; + EditorFileDialog *file_select; + EditorDirDialog *save_select; + AcceptDialog *error_dialog; + PropertyEditor *option_editor; + + _EditorMeshImportOptions *options; + + +public: + + void _choose_files(const Vector<String>& p_path) { + + String files; + for(int i=0;i<p_path.size();i++) { + + if (i>0) + files+=","; + files+=p_path[i]; + } + /* + if (p_path.size()) { + String srctex=p_path[0]; + String ipath = EditorImportDB::get_singleton()->find_source_path(srctex); + + if (ipath!="") + save_path->set_text(ipath.get_base_dir()); + }*/ + import_path->set_text(files); + + } + void _choose_save_dir(const String& p_path) { + + save_path->set_text(p_path); + } + + void _browse() { + + file_select->popup_centered_ratio(); + } + + void _browse_target() { + + save_select->popup_centered_ratio(); + } + + void popup_import(const String& p_path) { + + popup_centered(Size2(400,400)*EDSCALE); + + if (p_path!="") { + + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_path); + ERR_FAIL_COND(!rimd.is_valid()); + + save_path->set_text(p_path.get_base_dir()); + List<String> opts; + rimd->get_options(&opts); + for(List<String>::Element *E=opts.front();E;E=E->next()) { + + options->_set(E->get(),rimd->get_option(E->get())); + } + + String src = ""; + for(int i=0;i<rimd->get_source_count();i++) { + if (i>0) + src+=","; + src+=EditorImportPlugin::expand_source_path(rimd->get_source_path(i)); + } + import_path->set_text(src); + } + } + + void _import() { + + Vector<String> meshes = import_path->get_text().split(","); + if (meshes.size()==0) { + error_dialog->set_text(TTR("No meshes to import!")); + error_dialog->popup_centered_minsize(); + return; + } + + String dst = save_path->get_text(); + if (dst=="") { + error_dialog->set_text(TTR("Save path is empty!")); + error_dialog->popup_centered_minsize(); + return; + } + + for(int i=0;i<meshes.size();i++) { + + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + + List<PropertyInfo> pl; + options->_get_property_list(&pl); + for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { + + Variant v; + String opt=E->get().name; + options->_get(opt,v); + imd->set_option(opt,v); + + } + + imd->add_source(EditorImportPlugin::validate_source_path(meshes[i])); + + String file_path = dst.plus_file(meshes[i].get_file().get_basename()+".msh"); + + plugin->import(file_path,imd); + } + + hide(); + } + + void _notification(int p_what) { + + + if (p_what==NOTIFICATION_ENTER_TREE) { + + option_editor->edit(options); + } + } + + static void _bind_methods() { + + ClassDB::bind_method("_choose_files",&EditorMeshImportDialog::_choose_files); + ClassDB::bind_method("_choose_save_dir",&EditorMeshImportDialog::_choose_save_dir); + ClassDB::bind_method("_import",&EditorMeshImportDialog::_import); + ClassDB::bind_method("_browse",&EditorMeshImportDialog::_browse); + ClassDB::bind_method("_browse_target",&EditorMeshImportDialog::_browse_target); + } + + EditorMeshImportDialog(EditorMeshImportPlugin *p_plugin) { + + plugin=p_plugin; + + set_title(TTR("Single Mesh Import")); + set_hide_on_ok(false); + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + //set_child_rect(vbc); + + HBoxContainer *hbc = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Source Mesh(es):"),hbc); + + import_path = memnew( LineEdit ); + import_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(import_path); + + Button * import_choose = memnew( Button ); + import_choose->set_text(" .. "); + hbc->add_child(import_choose); + + import_choose->connect("pressed", this,"_browse"); + + hbc = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Target Path:"),hbc); + + save_path = memnew( LineEdit ); + save_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(save_path); + + Button * save_choose = memnew( Button ); + save_choose->set_text(" .. "); + hbc->add_child(save_choose); + + save_choose->connect("pressed", this,"_browse_target"); + + file_select = memnew( EditorFileDialog ); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES); + file_select->add_filter("*.obj ; Wavefront OBJ"); + add_child(file_select); + file_select->connect("files_selected", this,"_choose_files"); + + save_select = memnew( EditorDirDialog ); + add_child(save_select); + save_select->connect("dir_selected", this,"_choose_save_dir"); + + get_ok()->connect("pressed", this,"_import"); + get_ok()->set_text(TTR("Import")); + + error_dialog = memnew( AcceptDialog ); + add_child(error_dialog); + + options = memnew( _EditorMeshImportOptions ); + + option_editor = memnew( PropertyEditor ); + option_editor->hide_top_label(); + vbc->add_margin_child(TTR("Options:"),option_editor,true); + } + + ~EditorMeshImportDialog() { + memdelete(options); + } + +}; + + +String EditorMeshImportPlugin::get_name() const { + + return "mesh"; +} +String EditorMeshImportPlugin::get_visible_name() const{ + + return TTR("Mesh"); +} +void EditorMeshImportPlugin::import_dialog(const String& p_from){ + + dialog->popup_import(p_from); +} +Error EditorMeshImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from){ + + + ERR_FAIL_COND_V(p_from->get_source_count()!=1,ERR_INVALID_PARAMETER); + + Ref<ResourceImportMetadata> from=p_from; + + String src_path=EditorImportPlugin::expand_source_path(from->get_source_path(0)); + FileAccessRef f = FileAccess::open(src_path,FileAccess::READ); + ERR_FAIL_COND_V(!f,ERR_CANT_OPEN); + + Ref<Mesh> mesh; + Map<String,Ref<Material> > name_map; + + if (FileAccess::exists(p_path)) { + mesh=ResourceLoader::load(p_path,"Mesh"); + if (mesh.is_valid()) { + for(int i=0;i<mesh->get_surface_count();i++) { + + if (!mesh->surface_get_material(i).is_valid()) + continue; + String name; + if (mesh->surface_get_name(i)!="") + name=mesh->surface_get_name(i); + else + name=vformat(TTR("Surface %d"),i+1); + + name_map[name]=mesh->surface_get_material(i); + } + + while(mesh->get_surface_count()) { + mesh->surface_remove(0); + } + } + } + + if (!mesh.is_valid()) + mesh = Ref<Mesh>( memnew( Mesh ) ); + + + bool generate_normals=from->get_option("generate/normals"); + bool generate_tangents=from->get_option("generate/tangents"); + bool flip_faces=from->get_option("force/flip_faces"); + bool force_smooth=from->get_option("force/smooth_shading"); + bool weld_vertices=from->get_option("force/weld_vertices"); + float weld_tolerance=from->get_option("force/weld_tolerance"); + Vector<Vector3> vertices; + Vector<Vector3> normals; + Vector<Vector2> uvs; + String name; + + Ref<SurfaceTool> surf_tool = memnew( SurfaceTool) ; + surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES); + if (force_smooth) + surf_tool->add_smooth_group(true); + int has_index_data=false; + + while(true) { + + + String l = f->get_line().strip_edges(); + + if (l.begins_with("v ")) { + //vertex + Vector<String> v = l.split(" ",false); + ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA); + Vector3 vtx; + vtx.x=v[1].to_float(); + vtx.y=v[2].to_float(); + vtx.z=v[3].to_float(); + vertices.push_back(vtx); + } else if (l.begins_with("vt ")) { + //uv + Vector<String> v = l.split(" ",false); + ERR_FAIL_COND_V(v.size()<3,ERR_INVALID_DATA); + Vector2 uv; + uv.x=v[1].to_float(); + uv.y=1.0-v[2].to_float(); + uvs.push_back(uv); + + } else if (l.begins_with("vn ")) { + //normal + Vector<String> v = l.split(" ",false); + ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA); + Vector3 nrm; + nrm.x=v[1].to_float(); + nrm.y=v[2].to_float(); + nrm.z=v[3].to_float(); + normals.push_back(nrm); + } if (l.begins_with("f ")) { + //vertex + + has_index_data=true; + Vector<String> v = l.split(" ",false); + ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA); + + //not very fast, could be sped up + + + Vector<String> face[3]; + face[0] = v[1].split("/"); + face[1] = v[2].split("/"); + ERR_FAIL_COND_V(face[0].size()==0,ERR_PARSE_ERROR); + ERR_FAIL_COND_V(face[0].size()!=face[1].size(),ERR_PARSE_ERROR); + for(int i=2;i<v.size()-1;i++) { + + face[2] = v[i+1].split("/"); + ERR_FAIL_COND_V(face[0].size()!=face[2].size(),ERR_PARSE_ERROR); + for(int j=0;j<3;j++) { + + int idx=j; + + if (!flip_faces && idx<2) { + idx=1^idx; + } + + + if (face[idx].size()==3) { + int norm = face[idx][2].to_int()-1; + ERR_FAIL_INDEX_V(norm,normals.size(),ERR_PARSE_ERROR); + surf_tool->add_normal(normals[norm]); + } + + if (face[idx].size()>=2 && face[idx][1]!=String()) { + + int uv = face[idx][1].to_int()-1; + ERR_FAIL_INDEX_V(uv,uvs.size(),ERR_PARSE_ERROR); + surf_tool->add_uv(uvs[uv]); + } + + int vtx = face[idx][0].to_int()-1; + ERR_FAIL_INDEX_V(vtx,vertices.size(),ERR_PARSE_ERROR); + + Vector3 vertex = vertices[vtx]; + if (weld_vertices) + vertex=vertex.snapped(weld_tolerance); + surf_tool->add_vertex(vertex); + } + + face[1]=face[2]; + } + } else if (l.begins_with("s ") && !force_smooth) { //smoothing + String what = l.substr(2,l.length()).strip_edges(); + if (what=="off") + surf_tool->add_smooth_group(false); + else + surf_tool->add_smooth_group(true); + + } else if (l.begins_with("o ") || f->eof_reached()) { //new surface or done + + if (has_index_data) { + //new object/surface + if (generate_normals || force_smooth) + surf_tool->generate_normals(); + if (uvs.size() && (normals.size() || generate_normals) && generate_tangents) + surf_tool->generate_tangents(); + + surf_tool->index(); + mesh = surf_tool->commit(mesh); + if (name=="") + name=vformat(TTR("Surface %d"),mesh->get_surface_count()-1); + mesh->surface_set_name(mesh->get_surface_count()-1,name); + name=""; + surf_tool->clear(); + surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES); + if (force_smooth) + surf_tool->add_smooth_group(true); + + has_index_data=false; + + if (f->eof_reached()) + break; + } + + if (l.begins_with("o ")) //name + name=l.substr(2,l.length()).strip_edges(); + } + } + + + from->set_source_md5(0,FileAccess::get_md5(src_path)); + from->set_editor(get_name()); + mesh->set_import_metadata(from); + + //re-apply materials if exist + for(int i=0;i<mesh->get_surface_count();i++) { + + String n = mesh->surface_get_name(i); + if (name_map.has(n)) + mesh->surface_set_material(i,name_map[n]); + } + + Error err = ResourceSaver::save(p_path,mesh); + + return err; +} + + +void EditorMeshImportPlugin::import_from_drop(const Vector<String>& p_drop, const String &p_dest_path) { + + + Vector<String> files; + for(int i=0;i<p_drop.size();i++) { + String ext = p_drop[i].get_extension().to_lower(); + String file = p_drop[i].get_file(); + if (ext=="obj") { + + files.push_back(p_drop[i]); + } + } + + if (files.size()) { + import_dialog(); + dialog->_choose_files(files); + dialog->_choose_save_dir(p_dest_path); + } +} + +EditorMeshImportPlugin::EditorMeshImportPlugin(EditorNode* p_editor) { + + dialog = memnew( EditorMeshImportDialog(this)); + p_editor->get_gui_base()->add_child(dialog); +} +#endif diff --git a/editor/io_plugins/editor_mesh_import_plugin.h b/editor/io_plugins/editor_mesh_import_plugin.h new file mode 100644 index 0000000000..df374549d4 --- /dev/null +++ b/editor/io_plugins/editor_mesh_import_plugin.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* editor_mesh_import_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_MESH_IMPORT_PLUGIN_H +#define EDITOR_MESH_IMPORT_PLUGIN_H + +#if 0 +#include "editor/editor_import_export.h" +#include "scene/resources/font.h" + +class EditorNode; +class EditorMeshImportDialog; + +class EditorMeshImportPlugin : public EditorImportPlugin { + + GDCLASS(EditorMeshImportPlugin,EditorImportPlugin); + + EditorMeshImportDialog *dialog; + + +public: + + virtual String get_name() const; + virtual String get_visible_name() const; + virtual void import_dialog(const String& p_from=""); + virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from); + void import_from_drop(const Vector<String>& p_drop, const String &p_dest_path); + + + EditorMeshImportPlugin(EditorNode* p_editor); +}; + +#endif +#endif // EDITOR_MESH_IMPORT_PLUGIN_H diff --git a/editor/io_plugins/editor_sample_import_plugin.cpp b/editor/io_plugins/editor_sample_import_plugin.cpp new file mode 100644 index 0000000000..d446d39027 --- /dev/null +++ b/editor/io_plugins/editor_sample_import_plugin.cpp @@ -0,0 +1,929 @@ +/*************************************************************************/ +/* editor_sample_import_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_sample_import_plugin.h" + +#include "editor/editor_file_dialog.h" +#include "editor/editor_dir_dialog.h" +#include "editor/editor_node.h" +#include "editor/property_editor.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "io/marshalls.h" +#include "editor/editor_settings.h" + +#if 0 + +class _EditorSampleImportOptions : public Object { + + GDCLASS(_EditorSampleImportOptions,Object); +public: + + enum CompressMode { + COMPRESS_MODE_DISABLED, + COMPRESS_MODE_RAM, + COMPRESS_MODE_DISK + }; + + enum CompressBitrate { + COMPRESS_64, + COMPRESS_96, + COMPRESS_128, + COMPRESS_192 + }; + + bool force_8_bit; + bool force_mono; + bool force_rate; + float force_rate_hz; + + bool edit_trim; + bool edit_normalize; + bool edit_loop; + + CompressMode compress_mode; + CompressBitrate compress_bitrate; + + + bool _set(const StringName& p_name, const Variant& p_value) { + + String n = p_name; + if (n=="force/8_bit") + force_8_bit=p_value; + else if (n=="force/mono") + force_mono=p_value; + else if (n=="force/max_rate") + force_rate=p_value; + else if (n=="force/max_rate_hz") + force_rate_hz=p_value; + else if (n=="edit/trim") + edit_trim=p_value; + else if (n=="edit/normalize") + edit_normalize=p_value; + else if (n=="edit/loop") + edit_loop=p_value; + else if (n=="compress/mode") + compress_mode=CompressMode(int(p_value)); + else if (n=="compress/bitrate") + compress_bitrate=CompressBitrate(int(p_value)); + else + return false; + + return true; + + } + + bool _get(const StringName& p_name,Variant &r_ret) const{ + + String n = p_name; + if (n=="force/8_bit") + r_ret=force_8_bit; + else if (n=="force/mono") + r_ret=force_mono; + else if (n=="force/max_rate") + r_ret=force_rate; + else if (n=="force/max_rate_hz") + r_ret=force_rate_hz; + else if (n=="edit/trim") + r_ret=edit_trim; + else if (n=="edit/normalize") + r_ret=edit_normalize; + else if (n=="edit/loop") + r_ret=edit_loop; + else if (n=="compress/mode") + r_ret=compress_mode; + else if (n=="compress/bitrate") + r_ret=compress_bitrate; + else + return false; + + return true; + + } + void _get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back(PropertyInfo(Variant::BOOL,"force/8_bit")); + p_list->push_back(PropertyInfo(Variant::BOOL,"force/mono")); + p_list->push_back(PropertyInfo(Variant::BOOL,"force/max_rate")); + p_list->push_back(PropertyInfo(Variant::REAL,"force/max_rate_hz",PROPERTY_HINT_EXP_RANGE,"11025,192000,1")); + p_list->push_back(PropertyInfo(Variant::BOOL,"edit/trim")); + p_list->push_back(PropertyInfo(Variant::BOOL,"edit/normalize")); + p_list->push_back(PropertyInfo(Variant::BOOL,"edit/loop")); + p_list->push_back(PropertyInfo(Variant::INT,"compress/mode",PROPERTY_HINT_ENUM,"Disabled,RAM (Ima-ADPCM)")); + //p_list->push_back(PropertyInfo(Variant::INT,"compress/bitrate",PROPERTY_HINT_ENUM,"64,96,128,192")); + + + } + + + static void _bind_methods() { + + + ADD_SIGNAL( MethodInfo("changed")); + } + + + _EditorSampleImportOptions() { + + force_8_bit=false; + force_mono=false; + force_rate=true; + force_rate_hz=44100; + + edit_trim=true; + edit_normalize=true; + edit_loop=false; + + compress_mode=COMPRESS_MODE_RAM; + compress_bitrate=COMPRESS_128; + } + + +}; + +class EditorSampleImportDialog : public ConfirmationDialog { + + GDCLASS(EditorSampleImportDialog,ConfirmationDialog); + + EditorSampleImportPlugin *plugin; + + LineEdit *import_path; + LineEdit *save_path; + EditorFileDialog *file_select; + EditorDirDialog *save_select; + ConfirmationDialog *error_dialog; + PropertyEditor *option_editor; + + _EditorSampleImportOptions *options; + + +public: + + void _choose_files(const Vector<String>& p_path) { + + String files; + for(int i=0;i<p_path.size();i++) { + + if (i>0) + files+=","; + files+=p_path[i]; + } + /* + if (p_path.size()) { + String srctex=p_path[0]; + String ipath = EditorImportDB::get_singleton()->find_source_path(srctex); + + if (ipath!="") + save_path->set_text(ipath.get_base_dir()); + }*/ + import_path->set_text(files); + + } + void _choose_save_dir(const String& p_path) { + + save_path->set_text(p_path); + } + + void _browse() { + + file_select->popup_centered_ratio(); + } + + void _browse_target() { + + save_select->popup_centered_ratio(); + + } + + + void popup_import(const String& p_path) { + + popup_centered(Size2(400,400)*EDSCALE); + if (p_path!="") { + + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_path); + ERR_FAIL_COND(!rimd.is_valid()); + + save_path->set_text(p_path.get_base_dir()); + List<String> opts; + rimd->get_options(&opts); + for(List<String>::Element *E=opts.front();E;E=E->next()) { + + options->_set(E->get(),rimd->get_option(E->get())); + } + + String src = ""; + for(int i=0;i<rimd->get_source_count();i++) { + if (i>0) + src+=","; + src+=EditorImportPlugin::expand_source_path(rimd->get_source_path(i)); + } + import_path->set_text(src); + } + } + + + void _import() { + + Vector<String> samples = import_path->get_text().split(","); + + if (samples.size()==0) { + error_dialog->set_text(TTR("No samples to import!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + } + + 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; + } + + for(int i=0;i<samples.size();i++) { + + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + + List<PropertyInfo> pl; + options->_get_property_list(&pl); + for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { + + Variant v; + String opt=E->get().name; + options->_get(opt,v); + imd->set_option(opt,v); + + } + + imd->add_source(EditorImportPlugin::validate_source_path(samples[i])); + + String dst = save_path->get_text(); + if (dst=="") { + error_dialog->set_text(TTR("Save path is empty!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + } + + dst = dst.plus_file(samples[i].get_file().get_basename()+".smp"); + + plugin->import(dst,imd); + } + + hide(); + + } + + + void _notification(int p_what) { + + + if (p_what==NOTIFICATION_ENTER_TREE) { + + option_editor->edit(options); + } + } + + static void _bind_methods() { + + + ClassDB::bind_method("_choose_files",&EditorSampleImportDialog::_choose_files); + ClassDB::bind_method("_choose_save_dir",&EditorSampleImportDialog::_choose_save_dir); + ClassDB::bind_method("_import",&EditorSampleImportDialog::_import); + ClassDB::bind_method("_browse",&EditorSampleImportDialog::_browse); + ClassDB::bind_method("_browse_target",&EditorSampleImportDialog::_browse_target); + //ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) ); + } + + EditorSampleImportDialog(EditorSampleImportPlugin *p_plugin) { + + plugin=p_plugin; + + + set_title(TTR("Import Audio Samples")); + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + //set_child_rect(vbc); + + + HBoxContainer *hbc = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Source Sample(s):"),hbc); + + import_path = memnew( LineEdit ); + import_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(import_path); + + Button * import_choose = memnew( Button ); + import_choose->set_text(" .. "); + hbc->add_child(import_choose); + + import_choose->connect("pressed", this,"_browse"); + + hbc = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Target Path:"),hbc); + + save_path = memnew( LineEdit ); + save_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(save_path); + + Button * save_choose = memnew( Button ); + save_choose->set_text(" .. "); + hbc->add_child(save_choose); + + save_choose->connect("pressed", this,"_browse_target"); + + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + add_child(file_select); + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES); + file_select->connect("files_selected", this,"_choose_files"); + file_select->add_filter("*.wav ; MS Waveform"); + save_select = memnew( EditorDirDialog ); + add_child(save_select); + + //save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR); + save_select->connect("dir_selected", this,"_choose_save_dir"); + + get_ok()->connect("pressed", this,"_import"); + get_ok()->set_text(TTR("Import")); + + + error_dialog = memnew ( ConfirmationDialog ); + add_child(error_dialog); + error_dialog->get_ok()->set_text(TTR("Accept")); + //error_dialog->get_cancel()->hide(); + + set_hide_on_ok(false); + options = memnew( _EditorSampleImportOptions ); + + option_editor = memnew( PropertyEditor ); + option_editor->hide_top_label(); + vbc->add_margin_child(TTR("Options:"),option_editor,true); + } + + ~EditorSampleImportDialog() { + memdelete(options); + } + +}; + + +String EditorSampleImportPlugin::get_name() const { + + return "sample"; +} +String EditorSampleImportPlugin::get_visible_name() const{ + + return TTR("Audio Sample"); +} +void EditorSampleImportPlugin::import_dialog(const String& p_from){ + + dialog->popup_import(p_from); +} +Error EditorSampleImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from){ + + ERR_FAIL_COND_V(p_from->get_source_count()!=1,ERR_INVALID_PARAMETER); + + Ref<ResourceImportMetadata> from=p_from; + + String src_path=EditorImportPlugin::expand_source_path(from->get_source_path(0)); + Ref<Sample> smp = ResourceLoader::load(src_path); + ERR_FAIL_COND_V(smp.is_null(),ERR_CANT_OPEN); + + + float rate = smp->get_mix_rate(); + bool is16 = smp->get_format()==Sample::FORMAT_PCM16; + int chans = smp->is_stereo()?2:1; + int len = smp->get_length(); + Sample::LoopFormat loop= smp->get_loop_format(); + int loop_beg = smp->get_loop_begin(); + int loop_end = smp->get_loop_end(); + + print_line("Input Sample: "); + print_line("\tlen: "+itos(len)); + print_line("\tchans: "+itos(chans)); + print_line("\t16bits: "+itos(is16)); + print_line("\trate: "+itos(rate)); + print_line("\tloop: "+itos(loop)); + print_line("\tloop begin: "+itos(loop_beg)); + print_line("\tloop end: "+itos(loop_end)); + Vector<float> data; + data.resize(len*chans); + + { + PoolVector<uint8_t> src_data = smp->get_data(); + PoolVector<uint8_t>::Read sr = src_data.read(); + + + for(int i=0;i<len*chans;i++) { + + float s=0; + if (is16) { + + int16_t i16 = decode_uint16(&sr[i*2]); + s=i16/32767.0; + } else { + + int8_t i8 = sr[i]; + s=i8/127.0; + } + data[i]=s; + } + } + + //apply frequency limit + + bool limit_rate = from->get_option("force/max_rate"); + int limit_rate_hz = from->get_option("force/max_rate_hz"); + if (limit_rate && rate > limit_rate_hz) { + //resampleeee!!! + int new_data_len = len * limit_rate_hz / rate; + Vector<float> new_data; + new_data.resize( new_data_len * chans ); + for(int c=0;c<chans;c++) { + + for(int i=0;i<new_data_len;i++) { + + //simple cubic interpolation should be enough. + float pos = float(i) * len / new_data_len; + float mu = pos-Math::floor(pos); + int ipos = int(Math::floor(pos)); + + float y0=data[MAX(0,ipos-1)*chans+c]; + float y1=data[ipos*chans+c]; + float y2=data[MIN(len-1,ipos+1)*chans+c]; + float y3=data[MIN(len-1,ipos+2)*chans+c]; + + float mu2 = mu*mu; + float a0 = y3 - y2 - y0 + y1; + float a1 = y0 - y1 - a0; + float a2 = y2 - y0; + float a3 = y1; + + float res=(a0*mu*mu2+a1*mu2+a2*mu+a3); + + new_data[i*chans+c]=res; + } + } + + if (loop) { + + loop_beg=loop_beg*new_data_len/len; + loop_end=loop_end*new_data_len/len; + } + data=new_data; + rate=limit_rate_hz; + len=new_data_len; + } + + + bool normalize = from->get_option("edit/normalize"); + + if (normalize) { + + float max=0; + for(int i=0;i<data.size();i++) { + + float amp = Math::abs(data[i]); + if (amp>max) + max=amp; + } + + if (max>0) { + + float mult=1.0/max; + for(int i=0;i<data.size();i++) { + + data[i]*=mult; + } + + } + } + + bool trim = from->get_option("edit/trim"); + + if (trim && !loop) { + + int first=0; + int last=(len*chans)-1; + bool found=false; + float limit = Math::db2linear((float)-30); + for(int i=0;i<data.size();i++) { + float amp = Math::abs(data[i]); + + if (!found && amp > limit) { + first=i; + found=true; + } + + if (found && amp > limit) { + last=i; + } + } + + first/=chans; + last/=chans; + + if (first<last) { + + Vector<float> new_data; + new_data.resize((last-first+1)*chans); + for(int i=first*chans;i<=last*chans;i++) { + new_data[i-first*chans]=data[i]; + } + + data=new_data; + len=data.size()/chans; + } + + } + + bool make_loop = from->get_option("edit/loop"); + + if (make_loop && !loop) { + + loop=Sample::LOOP_FORWARD; + loop_beg=0; + loop_end=len; + } + + int compression = from->get_option("compress/mode"); + bool force_mono = from->get_option("force/mono"); + + + if (force_mono && chans==2) { + + Vector<float> new_data; + new_data.resize(data.size()/2); + for(int i=0;i<len;i++) { + new_data[i]=(data[i*2+0]+data[i*2+1])/2.0; + } + + data=new_data; + chans=1; + } + + bool force_8_bit = from->get_option("force/8_bit"); + if (force_8_bit) { + + is16=false; + } + + + PoolVector<uint8_t> dst_data; + Sample::Format dst_format; + + if ( compression == _EditorSampleImportOptions::COMPRESS_MODE_RAM) { + + dst_format=Sample::FORMAT_IMA_ADPCM; + if (chans==1) { + _compress_ima_adpcm(data,dst_data); + } else { + + print_line("INTERLEAAVE!"); + + + + //byte interleave + Vector<float> left; + Vector<float> right; + + int tlen = data.size()/2; + left.resize(tlen); + right.resize(tlen); + + for(int i=0;i<tlen;i++) { + left[i]=data[i*2+0]; + right[i]=data[i*2+1]; + } + + PoolVector<uint8_t> bleft; + PoolVector<uint8_t> bright; + + _compress_ima_adpcm(left,bleft); + _compress_ima_adpcm(right,bright); + + int dl = bleft.size(); + dst_data.resize( dl *2 ); + + PoolVector<uint8_t>::Write w=dst_data.write(); + PoolVector<uint8_t>::Read rl=bleft.read(); + PoolVector<uint8_t>::Read rr=bright.read(); + + for(int i=0;i<dl;i++) { + w[i*2+0]=rl[i]; + w[i*2+1]=rr[i]; + } + } + + //print_line("compressing ima-adpcm, resulting buffersize is "+itos(dst_data.size())+" from "+itos(data.size())); + + } else { + + dst_format=is16?Sample::FORMAT_PCM16:Sample::FORMAT_PCM8; + dst_data.resize( data.size() * (is16?2:1)); + { + PoolVector<uint8_t>::Write w = dst_data.write(); + + int ds=data.size(); + for(int i=0;i<ds;i++) { + + if (is16) { + int16_t v = CLAMP(data[i]*32767,-32768,32767); + encode_uint16(v,&w[i*2]); + } else { + int8_t v = CLAMP(data[i]*127,-128,127); + w[i]=v; + } + } + } + } + + + Ref<Sample> target; + + if (ResourceCache::has(p_path)) { + + target = Ref<Sample>( ResourceCache::get(p_path)->cast_to<Sample>() ); + } else { + + target = smp; + } + + target->create(dst_format,chans==2?true:false,len); + target->set_data(dst_data); + target->set_mix_rate(rate); + target->set_loop_format(loop); + target->set_loop_begin(loop_beg); + target->set_loop_end(loop_end); + + from->set_source_md5(0,FileAccess::get_md5(src_path)); + from->set_editor(get_name()); + target->set_import_metadata(from); + + + Error err = ResourceSaver::save(p_path,smp); + + return err; + +} + +void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector<float>& p_data,PoolVector<uint8_t>& dst_data) { + + + /*p_sample_data->data = (void*)malloc(len); + xm_s8 *dataptr=(xm_s8*)p_sample_data->data;*/ + + static const int16_t _ima_adpcm_step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + static const int8_t _ima_adpcm_index_table[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + + + int datalen = p_data.size(); + int datamax=datalen; + if (datalen&1) + datalen++; + + dst_data.resize(datalen/2+4); + PoolVector<uint8_t>::Write w = dst_data.write(); + + + int i,step_idx=0,prev=0; + uint8_t *out = w.ptr(); + //int16_t xm_prev=0; + const float *in=p_data.ptr(); + + + /* initial value is zero */ + *(out++) =0; + *(out++) =0; + /* Table index initial value */ + *(out++) =0; + /* unused */ + *(out++) =0; + + for (i=0;i<datalen;i++) { + int step,diff,vpdiff,mask; + uint8_t nibble; + int16_t xm_sample; + + if (i>=datamax) + xm_sample=0; + else { + + + xm_sample=CLAMP(in[i]*32767.0,-32768,32767); + /* + if (xm_sample==32767 || xm_sample==-32768) + printf("clippy!\n",xm_sample); + */ + } + + //xm_sample=xm_sample+xm_prev; + //xm_prev=xm_sample; + + diff = (int)xm_sample - prev ; + + nibble=0 ; + step = _ima_adpcm_step_table[ step_idx ]; + vpdiff = step >> 3 ; + if (diff < 0) { + nibble=8; + diff=-diff ; + } + mask = 4 ; + while (mask) { + + if (diff >= step) { + + nibble |= mask; + diff -= step; + vpdiff += step; + } + + step >>= 1 ; + mask >>= 1 ; + }; + + if (nibble&8) + prev-=vpdiff ; + else + prev+=vpdiff ; + + if (prev > 32767) { + //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev); + prev=32767; + } else if (prev < -32768) { + //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev); + prev = -32768 ; + } + + step_idx += _ima_adpcm_index_table[nibble]; + if (step_idx< 0) + step_idx= 0 ; + else if (step_idx> 88) + step_idx= 88 ; + + + if (i&1) { + *out|=nibble<<4; + out++; + } else { + *out=nibble; + } + /*dataptr[i]=prev>>8;*/ + } + +} + + +EditorSampleImportPlugin* EditorSampleImportPlugin::singleton=NULL; + + +void EditorSampleImportPlugin::import_from_drop(const Vector<String>& p_drop, const String &p_dest_path) { + + + Vector<String> files; + for(int i=0;i<p_drop.size();i++) { + String ext = p_drop[i].get_extension().to_lower(); + + if (ext=="wav") { + + files.push_back(p_drop[i]); + } + } + + if (files.size()) { + import_dialog(); + dialog->_choose_files(files); + dialog->_choose_save_dir(p_dest_path); + } +} + +void EditorSampleImportPlugin::reimport_multiple_files(const Vector<String>& p_list) { + + if (p_list.size()==0) + return; + + Vector<String> sources; + for(int i=0;i<p_list.size();i++) { + int idx; + EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->find_file(p_list[i],&idx); + if (efsd) { + for(int j=0;j<efsd->get_source_count(idx);j++) { + String file = expand_source_path(efsd->get_source_file(idx,j)); + if (sources.find(file)==-1) { + sources.push_back(file); + } + + } + } + } + + if (sources.size()) { + + dialog->popup_import(p_list[0]); + dialog->_choose_files(sources); + dialog->_choose_save_dir(p_list[0].get_base_dir()); + } +} + +bool EditorSampleImportPlugin::can_reimport_multiple_files() const { + + return true; +} + +EditorSampleImportPlugin::EditorSampleImportPlugin(EditorNode* p_editor) { + + singleton=this; + dialog = memnew( EditorSampleImportDialog(this)); + p_editor->get_gui_base()->add_child(dialog); +} + +Vector<uint8_t> EditorSampleExportPlugin::custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform) { + + + + if (EditorImportExport::get_singleton()->sample_get_action()==EditorImportExport::SAMPLE_ACTION_NONE || p_path.get_extension().to_lower()!="wav") { + + return Vector<uint8_t>(); + } + + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + + imd->add_source(EditorImportPlugin::validate_source_path(p_path)); + + imd->set_option("force/8_bit",false); + imd->set_option("force/mono",false); + imd->set_option("force/max_rate",true); + imd->set_option("force/max_rate_hz",EditorImportExport::get_singleton()->sample_get_max_hz()); + imd->set_option("edit/trim",EditorImportExport::get_singleton()->sample_get_trim()); + imd->set_option("edit/normalize",false); + imd->set_option("edit/loop",false); + imd->set_option("compress/mode",1); + + String savepath = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/smpconv.smp"); + Error err = EditorSampleImportPlugin::singleton->import(savepath,imd); + + + ERR_FAIL_COND_V(err!=OK,Vector<uint8_t>()); + + p_path=p_path.get_basename()+".converted.smp"; + return FileAccess::get_file_as_array(savepath); + +} + + + +EditorSampleExportPlugin::EditorSampleExportPlugin() { + +} + +#endif diff --git a/editor/io_plugins/editor_sample_import_plugin.h b/editor/io_plugins/editor_sample_import_plugin.h new file mode 100644 index 0000000000..6085043a83 --- /dev/null +++ b/editor/io_plugins/editor_sample_import_plugin.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* editor_sample_import_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_SAMPLE_IMPORT_PLUGIN_H +#define EDITOR_SAMPLE_IMPORT_PLUGIN_H + +#if 0 +#include "editor/editor_import_export.h" +#include "scene/resources/font.h" + +class EditorNode; +class EditorSampleImportDialog; + +class EditorSampleImportPlugin : public EditorImportPlugin { + + GDCLASS(EditorSampleImportPlugin,EditorImportPlugin); + + EditorSampleImportDialog *dialog; + void _compress_ima_adpcm(const Vector<float>& p_data,PoolVector<uint8_t>& dst_data); +public: + + static EditorSampleImportPlugin *singleton; + + virtual String get_name() const; + virtual String get_visible_name() const; + virtual void import_dialog(const String& p_from=""); + virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from); + void import_from_drop(const Vector<String>& p_drop, const String &p_dest_path); + virtual void reimport_multiple_files(const Vector<String>& p_list); + virtual bool can_reimport_multiple_files() const; + + + EditorSampleImportPlugin(EditorNode* p_editor); +}; + +class EditorSampleExportPlugin : public EditorExportPlugin { + + GDCLASS( EditorSampleExportPlugin, EditorExportPlugin); + + +public: + + virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform); + + EditorSampleExportPlugin(); +}; + +#endif // EDITOR_SAMPLE_IMPORT_PLUGIN_H +#endif diff --git a/editor/io_plugins/editor_scene_import_plugin.cpp b/editor/io_plugins/editor_scene_import_plugin.cpp new file mode 100644 index 0000000000..963968ce47 --- /dev/null +++ b/editor/io_plugins/editor_scene_import_plugin.cpp @@ -0,0 +1,2992 @@ +/*************************************************************************/ +/* editor_scene_import_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_scene_import_plugin.h" +#if 0 +#include "global_config.h" +#include "editor/editor_node.h" +#include "scene/resources/packed_scene.h" +#include "scene/resources/box_shape.h" +#include "os/file_access.h" +#include "scene/3d/path.h" +#include "scene/animation/animation_player.h" +#include "io/resource_saver.h" +#include "scene/3d/mesh_instance.h" +#include "scene/3d/navigation.h" +#include "scene/3d/room_instance.h" +#include "scene/3d/body_shape.h" +#include "scene/3d/physics_body.h" +#include "scene/3d/portal.h" +#include "scene/3d/vehicle_body.h" +#include "scene/resources/sphere_shape.h" +#include <scene/resources/box_shape.h> +#include <scene/resources/ray_shape.h> +#include <scene/resources/plane_shape.h> +#include "editor/create_dialog.h" +#include "os/os.h" + + + + +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 { + + GDCLASS( EditorImportAnimationOptions, VBoxContainer ); + + + + TreeItem *fps; + TreeItem* optimize_linear_error; + TreeItem* optimize_angular_error; + TreeItem* optimize_max_angle; + + TreeItem *clips_base; + + TextEdit *filters; + Vector<TreeItem*> clips; + + Tree *flags; + Tree *clips_tree; + Tree *optimization_tree; + Vector<TreeItem*> 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 { + + GDCLASS(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<TreeItem*> scene_flags; + + Map<Ref<Mesh>,Ref<Shape> > 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<ResourceImportMetadata> 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,"<new clip>"); + 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() { + + ClassDB::bind_method("_changed",&EditorImportAnimationOptions::_changed); + ClassDB::bind_method("_item_edited",&EditorImportAnimationOptions::_item_edited); + ClassDB::bind_method("_button_action",&EditorImportAnimationOptions::_button_action); + //ClassDB::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,"<new clip>"); + 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;i<scene_flags.size();i++) { + + scene_flags[i]->set_checked(0,f&(1<<i)); + } + } + } else { +#endif + save_path->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().get_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 = GlobalConfig::get_singleton()->localize_path(p_path); + if (!p.is_resource_file()) + p=GlobalConfig::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; + } + + String dst_path; + + if (texture_action->get_selected()==0) + dst_path=save_path->get_text();//.get_base_dir(); + else + dst_path=GlobalConfig::get_singleton()->get("import/shared_textures"); + + 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<Script> scr = ResourceLoader::load(script_path->get_text()); + if (!scr.is_valid()) { + error_dialog->set_text(TTR("Couldn't load post-import script.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + Ref<EditorScenePostImport> pi = Ref<EditorScenePostImport>( memnew( EditorScenePostImport ) ); + pi->set_script(scr.get_ref_ptr()); + if (!pi->get_script_instance()) { + + error_dialog->set_text(TTR("Invalid/broken script for post-import.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + } + + + // Scenes should always be imported as binary format since vertex data is large and would take + // up a lot of space and time to load if imported as text format (GH-5778) + String save_file = save_path->get_text().plus_file(import_path->get_text().get_file().get_basename()+".scn"); + print_line("Saving to: "+save_file); + + + + + + Node *scene=NULL; + + + Ref<ResourceImportMetadata> rim = memnew( ResourceImportMetadata ); + + rim->add_source(EditorImportPlugin::validate_source_path(import_path->get_text())); + rim->set_option("flags",flags); + print_line("GET FLAGS: "+itos(texture_options->get_flags())); + rim->set_option("texture_flags",texture_options->get_flags()); + rim->set_option("texture_format",texture_options->get_format()); + rim->set_option("texture_quality",texture_options->get_quality()); + rim->set_option("animation_flags",animation_options->get_flags()); + rim->set_option("animation_bake_fps",animation_options->get_fps()); + rim->set_option("animation_optimizer_linear_error",animation_options->get_optimize_linear_error()); + rim->set_option("animation_optimizer_angular_error",animation_options->get_optimize_angular_error()); + rim->set_option("animation_optimizer_max_angle",animation_options->get_optimize_max_angle()); + rim->set_option("animation_filters",animation_options->get_filter()); + rim->set_option("animation_clips",animation_options->get_clips()); + rim->set_option("post_import_script",script_path->get_text()); + rim->set_option("reimport",true); + if (!root_default->is_pressed()) { + rim->set_option("root_type",root_type->get_text()); + } + if (root_node_name->get_text().size()==0) { + root_node_name->set_text(import_path->get_text().get_file().get_basename()); + } + rim->set_option("root_name",root_node_name->get_text()); + + List<String> missing; + Error err = plugin->import1(rim,&scene,&missing); + + if (err || !scene) { + + error_dialog->set_text(TTR("Error importing scene.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + if (missing.size()) { + + missing_files->clear(); + for(List<String>::Element *E=missing.front();E;E=E->next()) { + + missing_files->add_text(E->get()); + missing_files->add_newline(); + } + wip_import=scene; + wip_rimd=rim; + wip_save_file=save_file; + confirm_import->popup_centered_ratio(); + return; + + } else { + + err = plugin->import2(scene,save_file,rim); + + if (err) { + + error_dialog->set_text(TTR("Error importing scene.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + if (wip_open) + EditorNode::get_singleton()->load_scene(save_file,false,false,false); + + } + + hide(); + + /* + editor->clear_scene(); + + Error err = EditorImport::import_scene(import_path->get_text(),save_file,dst_path,flags,texture_options->get_format(),compression,texture_options->get_flags(),texture_options->get_quality(),animation_options->get_flags(), &scene,pi); + + if (err) { + + error_dialog->set_text("Error importing scene."); + error_dialog->popup_centered(Size2(200,100)); + return; + } + + editor->save_import_export(); + if (scene) + editor->set_edited_scene(scene); + + hide(); + */ +}; + + +void EditorSceneImportDialog::_import_confirm() { + + wip_blocked=true; + print_line("import confirm!"); + Error err = plugin->import2(wip_import,wip_save_file,wip_rimd); + wip_blocked=false; + wip_import=NULL; + wip_rimd=Ref<ResourceImportMetadata>(); + confirm_import->hide(); + if (err) { + + wip_save_file=""; + error_dialog->set_text(TTR("Error importing scene.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + if (wip_open) + EditorNode::get_singleton()->load_scene(wip_save_file,false,false,false); + wip_open=false; + wip_save_file=""; + + hide(); + +} + + +void EditorSceneImportDialog::_browse() { + + file_select->popup_centered_ratio(); +} + +void EditorSceneImportDialog::_browse_target() { + + save_select->popup_centered_ratio(); + if (save_path->get_text()!="") + save_select->set_current_path(save_path->get_text()); + +} + +void EditorSceneImportDialog::_browse_script() { + + script_select->popup_centered_ratio(); + +} + +void EditorSceneImportDialog::popup_import(const String &p_from) { + + popup_centered(Size2(750,550)*EDSCALE); + if (p_from!="") { + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_from); + if (rimd.is_null()) + return; + + int flags = rimd->get_option("flags"); + + for(int i=0;i<scene_flags.size();i++) { + + int md = scene_flags[i]->get_metadata(0); + scene_flags[i]->set_checked(0,flags&md); + } + + texture_options->set_flags(rimd->get_option("texture_flags")); + texture_options->set_format(EditorTextureImportPlugin::ImageFormat(int(rimd->get_option("texture_format")))); + texture_options->set_quality(rimd->get_option("texture_quality")); + animation_options->set_flags(rimd->get_option("animation_flags")); + if (rimd->has_option("animation_clips")) + animation_options->setup_clips(rimd->get_option("animation_clips")); + if (rimd->has_option("animation_filters")) + animation_options->set_filter(rimd->get_option("animation_filters")); + if (rimd->has_option("animation_bake_fps")) + animation_options->set_fps(rimd->get_option("animation_bake_fps")); + if (rimd->has_option("animation_optimizer_linear_error")) + animation_options->set_optimize_linear_error(rimd->get_option("animation_optimizer_linear_error")); + if (rimd->has_option("animation_optimizer_angular_error")) + animation_options->set_optimize_angular_error(rimd->get_option("animation_optimizer_angular_error")); + if (rimd->has_option("animation_optimizer_max_angle")) + animation_options->set_optimize_max_angle(rimd->get_option("animation_optimizer_max_angle")); + + if (rimd->has_option("root_type")) { + root_default->set_pressed(false); + String type = rimd->get_option("root_type"); + root_type->set_text(type); + root_type->set_disabled(false); + + if (has_icon(type,"EditorIcons")) { + root_type->set_icon(get_icon(type,"EditorIcons")); + } else { + root_type->set_icon(get_icon("Object","EditorIcons")); + } + + } else { + root_default->set_pressed(true); + root_type->set_disabled(true); + } + if (rimd->has_option("root_name")) { + root_node_name->set_text(rimd->get_option("root_name")); + } else { + root_node_name->set_text(root_type->get_text()); // backward compatibility for 2.1 or before + } + script_path->set_text(rimd->get_option("post_import_script")); + + save_path->set_text(p_from.get_base_dir()); + import_path->set_text(EditorImportPlugin::expand_source_path(rimd->get_source_path(0))); + + } +} + + +void EditorSceneImportDialog::_notification(int p_what) { + + + if (p_what==NOTIFICATION_ENTER_TREE) { + + + List<String> extensions; + file_select->clear_filters(); + root_type->set_icon(get_icon("Spatial","EditorIcons")); + root_type->set_text("Spatial"); + root_type->set_disabled(true); + + for(int i=0;i<plugin->get_importers().size();i++) { + plugin->get_importers()[i]->get_extensions(&extensions); + } + + + for(int i=0;i<extensions.size();i++) { + + file_select->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); + } + + extensions.clear(); + + //EditorImport::get_import_extensions(&extensions) + /* ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions); + save_select->clear_filters(); + for(int i=0;i<extensions.size();i++) { + + save_select->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); + }*/ + + + } +} + +Error EditorSceneImportDialog::import(const String& p_from, const String& p_to, const String& p_preset) { + + import_path->set_text(p_from); + save_path->set_text(p_to); + script_path->set_text(p_preset); + + _import(); + + + + return OK; +} + +void EditorSceneImportDialog::_dialog_hid() { + + if (wip_blocked) + return; + print_line("DIALOGHID!"); + if (wip_import) { + memdelete(wip_import); + wip_import=NULL; + wip_save_file=""; + wip_rimd=Ref<ResourceImportMetadata>(); + } +} +void EditorSceneImportDialog::_root_default_pressed() { + + root_type->set_disabled(root_default->is_pressed()); +} + +void EditorSceneImportDialog::_root_type_pressed() { + + + root_type_choose->popup(false); +} + + +void EditorSceneImportDialog::_set_root_type() { + + String type = root_type_choose->get_selected_type(); + if (type==String()) + return; + root_type->set_text(type); + if (has_icon(type,"EditorIcons")) { + root_type->set_icon(get_icon(type,"EditorIcons")); + } else { + root_type->set_icon(get_icon("Object","EditorIcons")); + } +} + +void EditorSceneImportDialog::_bind_methods() { + + + ClassDB::bind_method("_choose_file",&EditorSceneImportDialog::_choose_file); + ClassDB::bind_method("_choose_save_file",&EditorSceneImportDialog::_choose_save_file); + ClassDB::bind_method("_choose_script",&EditorSceneImportDialog::_choose_script); + ClassDB::bind_method("_import",&EditorSceneImportDialog::_import,DEFVAL(false)); + ClassDB::bind_method("_browse",&EditorSceneImportDialog::_browse); + ClassDB::bind_method("_browse_target",&EditorSceneImportDialog::_browse_target); + ClassDB::bind_method("_browse_script",&EditorSceneImportDialog::_browse_script); + ClassDB::bind_method("_dialog_hid",&EditorSceneImportDialog::_dialog_hid); + ClassDB::bind_method("_import_confirm",&EditorSceneImportDialog::_import_confirm); + ClassDB::bind_method("_open_and_import",&EditorSceneImportDialog::_open_and_import); + ClassDB::bind_method("_root_default_pressed",&EditorSceneImportDialog::_root_default_pressed); + ClassDB::bind_method("_root_type_pressed",&EditorSceneImportDialog::_root_type_pressed); + ClassDB::bind_method("_set_root_type",&EditorSceneImportDialog::_set_root_type); + + + ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) ); +} + + + +const EditorSceneImportDialog::FlagInfo EditorSceneImportDialog::scene_flag_names[]={ + + {EditorSceneImportPlugin::SCENE_FLAG_REMOVE_NOIMP,("Actions"),"Remove Nodes (-noimp)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_IMPORT_ANIMATIONS,("Actions"),"Import Animations",true}, + {EditorSceneImportPlugin::SCENE_FLAG_COMPRESS_GEOMETRY,("Actions"),"Compress Geometry",false}, + {EditorSceneImportPlugin::SCENE_FLAG_GENERATE_TANGENT_ARRAYS,("Actions"),"Force Generation of Tangent Arrays",false}, + {EditorSceneImportPlugin::SCENE_FLAG_LINEARIZE_DIFFUSE_TEXTURES,("Actions"),"SRGB->Linear Of Diffuse Textures",false}, + {EditorSceneImportPlugin::SCENE_FLAG_CONVERT_NORMALMAPS_TO_XY,("Actions"),"Convert Normal Maps to XY",true}, + {EditorSceneImportPlugin::SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS,("Actions"),"Set Material Lightmap to UV2 if Tex2Array Exists",true}, + {EditorSceneImportPlugin::SCENE_FLAG_MERGE_KEEP_MATERIALS,("Merge"),"Keep Materials after first import (delete them for re-import).",true}, + {EditorSceneImportPlugin::SCENE_FLAG_MERGE_KEEP_EXTRA_ANIM_TRACKS,("Merge"),"Keep user-added Animation tracks.",true}, + {EditorSceneImportPlugin::SCENE_FLAG_DETECT_ALPHA,("Materials"),"Set Alpha in Materials (-alpha)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_DETECT_VCOLOR,("Materials"),"Set Vert. Color in Materials (-vcol)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_COLLISIONS,("Create"),"Create Collisions and/or Rigid Bodies (-col,-colonly,-rigid)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_PORTALS,("Create"),"Create Portals (-portal)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_ROOMS,("Create"),"Create Rooms (-room)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_SIMPLIFY_ROOMS,("Create"),"Simplify Rooms",false}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_BILLBOARDS,("Create"),"Create Billboards (-bb)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_IMPOSTORS,("Create"),"Create Impostors (-imp:dist)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_LODS,("Create"),"Create LODs (-lod:dist)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_CARS,("Create"),"Create Vehicles (-vehicle)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_WHEELS,("Create"),"Create Vehicle Wheels (-wheel)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_CREATE_NAVMESH,("Create"),"Create Navigation Meshes (-navmesh)",true}, + {EditorSceneImportPlugin::SCENE_FLAG_DETECT_LIGHTMAP_LAYER,("Create"),"Detect LightMap Layer (-lm:<int>).",true}, + {-1,NULL,NULL,false} +}; + + +EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSceneImportPlugin *p_plugin) { + + + editor=p_editor; + plugin=p_plugin; + + set_title(TTR("Import 3D Scene")); + HBoxContainer *import_hb = memnew( HBoxContainer ); + add_child(import_hb); + //set_child_rect(import_hb); + + VBoxContainer *vbc = memnew( VBoxContainer ); + import_hb->add_child(vbc); + vbc->set_h_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *hbc = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Source Scene:"),hbc); + + import_path = memnew( LineEdit ); + import_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(import_path); + + Button * import_choose = memnew( Button ); + import_choose->set_text(" .. "); + hbc->add_child(import_choose); + + import_choose->connect("pressed", this,"_browse"); + + hbc = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Target Path:"),hbc); + + save_path = memnew( LineEdit ); + save_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(save_path); + + Button * save_choose = memnew( Button ); + save_choose->set_text(" .. "); + hbc->add_child(save_choose); + + save_choose->connect("pressed", this,"_browse_target"); + + texture_action = memnew( OptionButton ); + texture_action->add_item(TTR("Same as Target Scene")); + texture_action->add_item(TTR("Shared")); + texture_action->select(0); + vbc->add_margin_child(TTR("Target Texture Folder:"),texture_action); + + import_options = memnew( Tree ); + vbc->set_v_size_flags(SIZE_EXPAND_FILL); + vbc->add_margin_child(TTR("Options:"),import_options,true); + + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + add_child(file_select); + + + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE); + + file_select->connect("file_selected", this,"_choose_file"); + + save_select = memnew(EditorDirDialog); + add_child(save_select); + + //save_select->set_mode(EditorFileDialog::MODE_SAVE_FILE); + save_select->connect("dir_selected", this,"_choose_save_file"); + + get_ok()->connect("pressed", this,"_import"); + get_ok()->set_text(TTR("Import")); + + TreeItem *root = import_options->create_item(NULL); + import_options->set_hide_root(true); + + const FlagInfo* fn=scene_flag_names; + + Map<String,TreeItem*> categories; + + while(fn->text) { + + String cat = fn->category; + TreeItem *parent; + if (!categories.has(cat)) { + parent = import_options->create_item(root); + parent->set_text(0,cat); + categories[cat]=parent; + } else { + parent=categories[cat]; + } + + TreeItem *opt = import_options->create_item(parent); + opt->set_cell_mode(0,TreeItem::CELL_MODE_CHECK); + opt->set_checked(0,fn->defval); + opt->set_editable(0,true); + opt->set_text(0,fn->text); + opt->set_metadata(0,fn->value); + + scene_flags.push_back(opt); + fn++; + } + + hbc = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Post-Process Script:"),hbc); + + script_path = memnew( LineEdit ); + script_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(script_path); + + Button * script_choose = memnew( Button ); + script_choose->set_text(" .. "); + hbc->add_child(script_choose); + + script_choose->connect("pressed", this,"_browse_script"); + + script_select = memnew(EditorFileDialog); + add_child(script_select); + for(int i=0;i<ScriptServer::get_language_count();i++) { + + ScriptLanguage *sl=ScriptServer::get_language(i); + String ext = sl->get_extension(); + if (ext=="") + continue; + script_select->add_filter("*."+ext+" ; "+sl->get_name()); + } + + + script_select->set_mode(EditorFileDialog::MODE_OPEN_FILE); + + script_select->connect("file_selected", this,"_choose_script"); + + error_dialog = memnew ( ConfirmationDialog ); + add_child(error_dialog); + error_dialog->get_ok()->set_text(TTR("Accept")); + //error_dialog->get_cancel()->hide(); + + + HBoxContainer *custom_root_hb = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Custom Root Node Type:"),custom_root_hb); + root_type = memnew(Button); + root_type->set_h_size_flags(SIZE_EXPAND_FILL); + root_type->set_text_align(Button::ALIGN_LEFT); + root_type->connect("pressed",this,"_root_type_pressed"); + custom_root_hb->add_child(root_type); + + root_default = memnew(CheckBox); + root_default->set_text(TTR("Auto")); + root_default->set_pressed(true); + root_default->connect("pressed",this,"_root_default_pressed"); + custom_root_hb->add_child(root_default); + + root_node_name = memnew( LineEdit ); + root_node_name->set_h_size_flags(SIZE_EXPAND_FILL); + vbc->add_margin_child(TTR("Root Node Name:"),root_node_name); + /* + this_import = memnew( OptionButton ); + this_import->add_item("Overwrite Existing Scene"); + this_import->add_item("Overwrite Existing, Keep Materials"); + this_import->add_item("Keep Existing, Merge with New"); + this_import->add_item("Keep Existing, Ignore New"); + vbc->add_margin_child("This Time:",this_import); + + next_import = memnew( OptionButton ); + next_import->add_item("Overwrite Existing Scene"); + next_import->add_item("Overwrite Existing, Keep Materials"); + next_import->add_item("Keep Existing, Merge with New"); + next_import->add_item("Keep Existing, Ignore New"); + vbc->add_margin_child("Next Time:",next_import); +*/ + set_hide_on_ok(false); + + GLOBAL_DEF("import/shared_textures","res://"); + GlobalConfig::get_singleton()->set_custom_property_info("import/shared_textures",PropertyInfo(Variant::STRING,"import/shared_textures",PROPERTY_HINT_DIR)); + + import_hb->add_constant_override("separation",30); + + + VBoxContainer *ovb = memnew( VBoxContainer); + ovb->set_h_size_flags(SIZE_EXPAND_FILL); + import_hb->add_child(ovb); + + texture_options = memnew( EditorImportTextureOptions ); + ovb->add_child(texture_options); + texture_options->set_v_size_flags(SIZE_EXPAND_FILL); + //animation_options->set_flags(EditorImport:: + texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM); + texture_options->set_flags( EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA | EditorTextureImportPlugin::IMAGE_FLAG_REPEAT | EditorTextureImportPlugin::IMAGE_FLAG_FILTER ); + + + animation_options = memnew( EditorImportAnimationOptions ); + ovb->add_child(animation_options); + animation_options->set_v_size_flags(SIZE_EXPAND_FILL); + animation_options->set_flags(EditorSceneAnimationImportPlugin::ANIMATION_DETECT_LOOP|EditorSceneAnimationImportPlugin::ANIMATION_KEEP_VALUE_TRACKS|EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE|EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS); + + + confirm_import = memnew( ConfirmationDialog ); + add_child(confirm_import); + VBoxContainer *cvb = memnew( VBoxContainer ); + confirm_import->add_child(cvb); + //confirm_import->set_child_rect(cvb); + + PanelContainer *pc = memnew( PanelContainer ); + pc->add_style_override("panel",get_stylebox("normal","TextEdit")); + //ec->add_child(pc); + missing_files = memnew( RichTextLabel ); + cvb->add_margin_child(TTR("The Following Files are Missing:"),pc,true); + pc->add_child(missing_files); + confirm_import->get_ok()->set_text(TTR("Import Anyway")); + confirm_import->get_cancel()->set_text(TTR("Cancel")); + confirm_import->connect("popup_hide",this,"_dialog_hid"); + confirm_import->connect("confirmed",this,"_import_confirm"); + confirm_import->set_hide_on_ok(false); + + add_button(TTR("Import & Open"),!OS::get_singleton()->get_swap_ok_cancel())->connect("pressed",this,"_open_and_import"); + + confirm_open = memnew( ConfirmationDialog ); + add_child(confirm_open); + confirm_open->set_text(TTR("Edited scene has not been saved, open imported scene anyway?")); + confirm_open->connect("confirmed",this,"_import",varray(true)); + + + wip_import=NULL; + wip_blocked=false; + wip_open=false; + //texture_options->set_format(EditorImport::IMAGE_FORMAT_C); + + root_type_choose = memnew( CreateDialog ); + add_child(root_type_choose); + root_type_choose->set_base_type("Node"); + root_type_choose->connect("create",this,"_set_root_type"); +} + + + +//////////////////////////////// + + + +String EditorSceneImportPlugin::get_name() const { + + return "scene_3d"; +} + +String EditorSceneImportPlugin::get_visible_name() const{ + + return TTR("Scene"); +} + +void EditorSceneImportPlugin::import_dialog(const String& p_from){ + + dialog->popup_import(p_from); +} + + +////////////////////////// + + +static bool _teststr(const String& p_what,const String& p_str) { + + if (p_what.findn("$"+p_str)!=-1) //blender and other stuff + return true; + if (p_what.to_lower().ends_with("-"+p_str)) //collada only supports "_" and "-" besides letters + return true; + if (p_what.to_lower().ends_with("_"+p_str)) //collada only supports "_" and "-" besides letters + return true; + return false; +} + +static String _fixstr(const String& p_what,const String& p_str) { + + if (p_what.findn("$"+p_str)!=-1) //blender and other stuff + return p_what.replace("$"+p_str,""); + if (p_what.to_lower().ends_with("-"+p_str)) //collada only supports "_" and "-" besides letters + return p_what.substr(0,p_what.length()-(p_str.length()+1)); + if (p_what.to_lower().ends_with("_"+p_str)) //collada only supports "_" and "-" besides letters + return p_what.substr(0,p_what.length()-(p_str.length()+1)); + return p_what; +} + + + +void EditorSceneImportPlugin::_find_resources(const Variant& p_var, Map<Ref<ImageTexture>, TextureRole> &image_map,int p_flags) { + + + switch(p_var.get_type()) { + + case Variant::OBJECT: { + + Ref<Resource> res = p_var; + if (res.is_valid()) { + + if (res->is_class("Texture") && !image_map.has(res)) { + + image_map.insert(res,TEXTURE_ROLE_DEFAULT); + + + } else { + + + List<PropertyInfo> pl; + res->get_property_list(&pl); + for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { + + if (E->get().type==Variant::OBJECT || E->get().type==Variant::ARRAY || E->get().type==Variant::DICTIONARY) { + if (E->get().type==Variant::OBJECT && res->cast_to<FixedSpatialMaterial>() && (E->get().name=="textures/diffuse" || E->get().name=="textures/detail" || E->get().name=="textures/emission")) { + + Ref<ImageTexture> tex =res->get(E->get().name); + if (tex.is_valid()) { + + image_map.insert(tex,TEXTURE_ROLE_DIFFUSE); + } + + } else if (E->get().type==Variant::OBJECT && res->cast_to<FixedSpatialMaterial>() && (E->get().name=="textures/normal")) { + + Ref<ImageTexture> tex =res->get(E->get().name); + if (tex.is_valid()) { + + image_map.insert(tex,TEXTURE_ROLE_NORMALMAP); + /* + if (p_flags&SCENE_FLAG_CONVERT_NORMALMAPS_TO_XY) + res->cast_to<FixedSpatialMaterial>()->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_XY_NORMALMAP,true); + */ + } + + + } else { + _find_resources(res->get(E->get().name),image_map,p_flags); + } + } + } + + } + } + + } break; + case Variant::DICTIONARY: { + + Dictionary d= p_var; + + List<Variant> keys; + d.get_key_list(&keys); + + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + + _find_resources(E->get(),image_map,p_flags); + _find_resources(d[E->get()],image_map,p_flags); + + } + + + } break; + case Variant::ARRAY: { + + Array a = p_var; + for(int i=0;i<a.size();i++) { + + _find_resources(a[i],image_map,p_flags); + } + + } break; + default: {} + + } + +} + + +Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map,uint32_t p_flags,Map<Ref<ImageTexture>,TextureRole >& image_map) { + + // children first.. + for(int i=0;i<p_node->get_child_count();i++) { + + + Node *r = _fix_node(p_node->get_child(i),p_root,collision_map,p_flags,image_map); + if (!r) { + print_line("was erased.."); + i--; //was erased + } + } + + String name = p_node->get_name(); + + bool isroot = p_node==p_root; + + + if (!isroot && p_flags&SCENE_FLAG_REMOVE_NOIMP && _teststr(name,"noimp")) { + + memdelete(p_node); + return NULL; + } + + { + + List<PropertyInfo> pl; + p_node->get_property_list(&pl); + for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { + + if (E->get().type==Variant::OBJECT || E->get().type==Variant::ARRAY || E->get().type==Variant::DICTIONARY) { + _find_resources(p_node->get(E->get().name),image_map,p_flags); + } + } + + } + + + + + if (p_flags&SCENE_FLAG_CREATE_BILLBOARDS && p_node->cast_to<MeshInstance>()) { + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + bool bb=false; + + if ((_teststr(name,"bb"))) { + bb=true; + } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"bb"))) { + bb=true; + + } + + if (bb) { + mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true); + if (mi->get_mesh().is_valid()) { + + Ref<Mesh> m = mi->get_mesh(); + for(int i=0;i<m->get_surface_count();i++) { + + Ref<FixedSpatialMaterial> fm = m->surface_get_material(i); + if (fm.is_valid()) { + //fm->set_flag(Material::FLAG_UNSHADED,true); + //fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); + //fm->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + //fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + } + } + } + } + } + + + if (p_flags&(SCENE_FLAG_DETECT_ALPHA|SCENE_FLAG_DETECT_VCOLOR|SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS) && p_node->cast_to<MeshInstance>()) { + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + Ref<Mesh> m = mi->get_mesh(); + + if (m.is_valid()) { + + for(int i=0;i<m->get_surface_count();i++) { + + Ref<FixedSpatialMaterial> mat = m->surface_get_material(i); + if (!mat.is_valid()) + continue; + + if (p_flags&SCENE_FLAG_DETECT_ALPHA && _teststr(mat->get_name(),"alpha")) { + + //mat->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + //mat->set_name(_fixstr(mat->get_name(),"alpha")); + } + if (p_flags&SCENE_FLAG_DETECT_VCOLOR && _teststr(mat->get_name(),"vcol")) { + + //mat->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_COLOR_ARRAY,true); + //mat->set_name(_fixstr(mat->get_name(),"vcol")); + } + + if (p_flags&SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS && m->surface_get_format(i)&Mesh::ARRAY_FORMAT_TEX_UV2) { + //mat->set_flag(Material::FLAG_LIGHTMAP_ON_UV2,true); + } + + } + } + } + + if (p_flags&SCENE_FLAG_REMOVE_NOIMP && p_node->cast_to<AnimationPlayer>()) { + //remove animations referencing non-importable nodes + AnimationPlayer *ap = p_node->cast_to<AnimationPlayer>(); + + List<StringName> anims; + ap->get_animation_list(&anims); + for(List<StringName>::Element *E=anims.front();E;E=E->next()) { + + Ref<Animation> anim=ap->get_animation(E->get()); + ERR_CONTINUE(anim.is_null()); + for(int i=0;i<anim->get_track_count();i++) { + NodePath path = anim->track_get_path(i); + + for(int j=0;j<path.get_name_count();j++) { + String node = path.get_name(j); + if (_teststr(node,"noimp")) { + anim->remove_track(i); + i--; + break; + } + } + } + + } + } + + + if (p_flags&SCENE_FLAG_CREATE_IMPOSTORS && p_node->cast_to<MeshInstance>()) { + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + String str; + + if ((_teststr(name,"imp"))) { + str=name; + } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"imp"))) { + str=mi->get_mesh()->get_name(); + + } + + + if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) { + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>(); + String d=str.substr(str.find("imp")+3,str.length()); + if (d!="") { + if ((d[0]<'0' || d[0]>'9')) + d=d.substr(1,d.length()); + if (d.length() && d[0]>='0' && d[0]<='9') { + float dist = d.to_double(); + mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true); + mi->set_flag(GeometryInstance::FLAG_BILLBOARD_FIX_Y,true); + //mi->set_draw_range_begin(dist); + //mi->set_draw_range_end(100000); + + //mip->set_draw_range_begin(0); + //mip->set_draw_range_end(dist); + + if (mi->get_mesh().is_valid()) { + + Ref<Mesh> m = mi->get_mesh(); + for(int i=0;i<m->get_surface_count();i++) { + + Ref<FixedSpatialMaterial> fm = m->surface_get_material(i); + if (fm.is_valid()) { + //fm->set_flag(Material::FLAG_UNSHADED,true); + //fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); + //fm->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + //fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + } + } + } + } + } + } + } + + if (p_flags&SCENE_FLAG_CREATE_LODS && p_node->cast_to<MeshInstance>()) { + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + String str; + + if ((_teststr(name,"lod"))) { + str=name; + } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"lod"))) { + str=mi->get_mesh()->get_name(); + + } + + + if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) { + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>(); + String d=str.substr(str.find("lod")+3,str.length()); + if (d!="") { + if ((d[0]<'0' || d[0]>'9')) + d=d.substr(1,d.length()); + if (d.length() && d[0]>='0' && d[0]<='9') { + float dist = d.to_double(); + /// mi->set_draw_range_begin(dist); + // mi->set_draw_range_end(100000); + + // mip->set_draw_range_begin(0); + // mip->set_draw_range_end(dist); + + /*if (mi->get_mesh().is_valid()) { + + Ref<Mesh> m = mi->get_mesh(); + for(int i=0;i<m->get_surface_count();i++) { + + Ref<FixedSpatialMaterial> fm = m->surface_get_material(i); + if (fm.is_valid()) { + fm->set_flag(Material::FLAG_UNSHADED,true); + fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); + fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + } + } + }*/ + } + } + } + } + + + if (p_flags&SCENE_FLAG_DETECT_LIGHTMAP_LAYER && _teststr(name,"lm") && p_node->cast_to<MeshInstance>()) { + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + String str=name; + int layer = str.substr(str.find("lm")+3,str.length()).to_int(); + //mi->set_baked_light_texture_id(layer); + } + + if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(name,"colonly")) { + + if (isroot) + return p_node; + + if (p_node->cast_to<MeshInstance>()) { + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + Node * col = mi->create_trimesh_collision_node(); + ERR_FAIL_COND_V(!col,NULL); + + col->set_name(_fixstr(name,"colonly")); + col->cast_to<Spatial>()->set_transform(mi->get_transform()); + p_node->replace_by(col); + memdelete(p_node); + p_node=col; + + StaticBody *sb = col->cast_to<StaticBody>(); + CollisionShape *colshape = memnew( CollisionShape); + colshape->set_shape(sb->get_shape(0)); + colshape->set_name("shape"); + sb->add_child(colshape); + colshape->set_owner(p_node->get_owner()); + } else if (p_node->has_meta("empty_draw_type")) { + String empty_draw_type = String(p_node->get_meta("empty_draw_type")); + print_line(empty_draw_type); + StaticBody *sb = memnew( StaticBody); + sb->set_name(_fixstr(name,"colonly")); + sb->cast_to<Spatial>()->set_transform(p_node->cast_to<Spatial>()->get_transform()); + p_node->replace_by(sb); + memdelete(p_node); + CollisionShape *colshape = memnew( CollisionShape); + if (empty_draw_type == "CUBE") { + BoxShape *boxShape = memnew( BoxShape); + boxShape->set_extents(Vector3(1, 1, 1)); + colshape->set_shape(boxShape); + colshape->set_name("BoxShape"); + } else if (empty_draw_type == "SINGLE_ARROW") { + RayShape *rayShape = memnew( RayShape); + rayShape->set_length(1); + colshape->set_shape(rayShape); + colshape->set_name("RayShape"); + sb->cast_to<Spatial>()->rotate_x(Math_PI / 2); + } else if (empty_draw_type == "IMAGE") { + PlaneShape *planeShape = memnew( PlaneShape); + colshape->set_shape(planeShape); + colshape->set_name("PlaneShape"); + } else { + SphereShape *sphereShape = memnew( SphereShape); + sphereShape->set_radius(1); + colshape->set_shape(sphereShape); + colshape->set_name("SphereShape"); + } + sb->add_child(colshape); + colshape->set_owner(sb->get_owner()); + } + + } else if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(name,"rigid") && p_node->cast_to<MeshInstance>()) { + + if (isroot) + return p_node; + + // get mesh instance and bounding box + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + Rect3 aabb = mi->get_aabb(); + + // create a new rigid body collision node + RigidBody * rigid_body = memnew( RigidBody ); + Node * col = rigid_body; + ERR_FAIL_COND_V(!col,NULL); + + // remove node name postfix + col->set_name(_fixstr(name,"rigid")); + // get mesh instance xform matrix to the rigid body collision node + col->cast_to<Spatial>()->set_transform(mi->get_transform()); + // save original node by duplicating it into a new instance and correcting the name + Node * mesh = p_node->duplicate(); + mesh->set_name(_fixstr(name,"rigid")); + // reset the xform matrix of the duplicated node so it can inherit parent node xform + mesh->cast_to<Spatial>()->set_transform(Transform(Basis())); + // reparent the new mesh node to the rigid body collision node + p_node->add_child(mesh); + mesh->set_owner(p_node->get_owner()); + // replace the original node with the rigid body collision node + p_node->replace_by(col); + memdelete(p_node); + p_node=col; + + // create an alias for the rigid body collision node + RigidBody *rb = col->cast_to<RigidBody>(); + // create a new Box collision shape and set the right extents + Ref<BoxShape> shape = memnew( BoxShape ); + shape->set_extents(aabb.get_size() * 0.5); + CollisionShape *colshape = memnew( CollisionShape); + colshape->set_name("shape"); + colshape->set_shape(shape); + // reparent the new collision shape to the rigid body collision node + rb->add_child(colshape); + colshape->set_owner(p_node->get_owner()); + + } else if (p_flags&SCENE_FLAG_CREATE_COLLISIONS &&_teststr(name,"col") && p_node->cast_to<MeshInstance>()) { + + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + mi->set_name(_fixstr(name,"col")); + Node *col= mi->create_trimesh_collision_node(); + ERR_FAIL_COND_V(!col,NULL); + + col->set_name("col"); + p_node->add_child(col); + + StaticBody *sb=col->cast_to<StaticBody>(); + CollisionShape *colshape = memnew( CollisionShape); + colshape->set_shape(sb->get_shape(0)); + colshape->set_name("shape"); + col->add_child(colshape); + colshape->set_owner(p_node->get_owner()); + sb->set_owner(p_node->get_owner()); + + } else if (p_flags&SCENE_FLAG_CREATE_NAVMESH &&_teststr(name,"navmesh") && p_node->cast_to<MeshInstance>()) { + + if (isroot) + return p_node; + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + Ref<Mesh> mesh=mi->get_mesh(); + ERR_FAIL_COND_V(mesh.is_null(),NULL); + NavigationMeshInstance *nmi = memnew( NavigationMeshInstance ); + + + nmi->set_name(_fixstr(name,"navmesh")); + Ref<NavigationMesh> nmesh = memnew( NavigationMesh); + nmesh->create_from_mesh(mesh); + nmi->set_navigation_mesh(nmesh); + nmi->cast_to<Spatial>()->set_transform(mi->get_transform()); + p_node->replace_by(nmi); + memdelete(p_node); + p_node=nmi; + } else if (p_flags&SCENE_FLAG_CREATE_CARS &&_teststr(name,"vehicle")) { + + if (isroot) + return p_node; + + Node *owner = p_node->get_owner(); + Spatial *s = p_node->cast_to<Spatial>(); + VehicleBody *bv = memnew( VehicleBody ); + String n = _fixstr(p_node->get_name(),"vehicle"); + bv->set_name(n); + p_node->replace_by(bv); + p_node->set_name(n); + bv->add_child(p_node); + bv->set_owner(owner); + p_node->set_owner(owner); + bv->set_transform(s->get_transform()); + s->set_transform(Transform()); + + p_node=bv; + + + } else if (p_flags&SCENE_FLAG_CREATE_CARS &&_teststr(name,"wheel")) { + + if (isroot) + return p_node; + + Node *owner = p_node->get_owner(); + Spatial *s = p_node->cast_to<Spatial>(); + VehicleWheel *bv = memnew( VehicleWheel ); + String n = _fixstr(p_node->get_name(),"wheel"); + bv->set_name(n); + p_node->replace_by(bv); + p_node->set_name(n); + bv->add_child(p_node); + bv->set_owner(owner); + p_node->set_owner(owner); + bv->set_transform(s->get_transform()); + s->set_transform(Transform()); + + p_node=bv; + + } else if (p_flags&SCENE_FLAG_CREATE_ROOMS && _teststr(name,"room") && p_node->cast_to<MeshInstance>()) { + + + if (isroot) + return p_node; + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + PoolVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID); + + + BSP_Tree bsptree(faces); + + Ref<RoomBounds> area = memnew( RoomBounds ); + //area->set_bounds(faces); + //area->set_geometry_hint(faces); + + + Room * room = memnew( Room ); + room->set_name(_fixstr(name,"room")); + room->set_transform(mi->get_transform()); + room->set_room(area); + + p_node->replace_by(room); + memdelete(p_node); + p_node=room; + + } else if (p_flags&SCENE_FLAG_CREATE_ROOMS &&_teststr(name,"room")) { + + if (isroot) + return p_node; + + Spatial *dummy = p_node->cast_to<Spatial>(); + ERR_FAIL_COND_V(!dummy,NULL); + + Room * room = memnew( Room ); + room->set_name(_fixstr(name,"room")); + room->set_transform(dummy->get_transform()); + + p_node->replace_by(room); + memdelete(p_node); + p_node=room; + + //room->compute_room_from_subtree(); + + } else if (p_flags&SCENE_FLAG_CREATE_PORTALS &&_teststr(name,"portal") && p_node->cast_to<MeshInstance>()) { + + if (isroot) + return p_node; + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + PoolVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID); + + ERR_FAIL_COND_V(faces.size()==0,NULL); + //step 1 compute the plane + Set<Vector3> points; + Plane plane; + + Vector3 center; + + for(int i=0;i<faces.size();i++) { + + Face3 f = faces.get(i); + Plane p = f.get_plane(); + plane.normal+=p.normal; + plane.d+=p.d; + + for(int i=0;i<3;i++) { + + Vector3 v = f.vertex[i].snapped(0.01); + if (!points.has(v)) { + points.insert(v); + center+=v; + } + } + } + + plane.normal.normalize(); + plane.d/=faces.size(); + center/=points.size(); + + //step 2, create points + + Transform t; + t.basis.from_z(plane.normal); + t.basis.transpose(); + t.origin=center; + + Vector<Point2> portal_points; + + for(Set<Vector3>::Element *E=points.front();E;E=E->next()) { + + Vector3 local = t.xform_inv(E->get()); + portal_points.push_back(Point2(local.x,local.y)); + } + // step 3 bubbly sort points + + int swaps=0; + + do { + swaps=0; + + for(int i=0;i<portal_points.size()-1;i++) { + + float a = portal_points[i].angle(); + float b = portal_points[i+1].angle(); + + if (a>b) { + SWAP( portal_points[i], portal_points[i+1] ); + swaps++; + } + + } + + } while(swaps); + + + Portal *portal = memnew( Portal ); + + portal->set_shape(portal_points); + portal->set_transform( mi->get_transform() * t); + + p_node->replace_by(portal); + memdelete(p_node); + p_node=portal; + + } else if (p_node->cast_to<MeshInstance>()) { + + //last attempt, maybe collision insde the mesh data + + MeshInstance *mi = p_node->cast_to<MeshInstance>(); + + Ref<Mesh> mesh = mi->get_mesh(); + if (!mesh.is_null()) { + + if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(mesh->get_name(),"col")) { + + mesh->set_name( _fixstr(mesh->get_name(),"col") ); + Ref<Shape> shape; + + if (collision_map.has(mesh)) { + shape = collision_map[mesh]; + + } else { + + shape = mesh->create_trimesh_shape(); + if (!shape.is_null()) + collision_map[mesh]=shape; + + + } + + if (!shape.is_null()) { +#if 0 + StaticBody* static_body = memnew( StaticBody ); + ERR_FAIL_COND_V(!static_body,NULL); + static_body->set_name( String(mesh->get_name()) + "_col" ); + shape->set_name(static_body->get_name()); + static_body->add_shape(shape); + + mi->add_child(static_body); + if (mi->get_owner()) + static_body->set_owner( mi->get_owner() ); +#endif + } + + } + + for(int i=0;i<mesh->get_surface_count();i++) { + + Ref<FixedSpatialMaterial> fm = mesh->surface_get_material(i); + if (fm.is_valid()) { + String name = fm->get_name(); + /* if (_teststr(name,"alpha")) { + fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + name=_fixstr(name,"alpha"); + } + + if (_teststr(name,"vcol")) { + fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_COLOR_ARRAY,true); + name=_fixstr(name,"vcol"); + }*/ + fm->set_name(name); + } + } + + } + + } + + + return p_node; +} + + + + +#if 0 + +Error EditorImport::import_scene(const String& p_path,const String& p_dest_path,const String& p_resource_path,uint32_t p_flags,ImageFormat p_image_format,ImageCompression p_image_compression,uint32_t p_image_flags,float p_quality,uint32_t animation_flags,Node **r_scene,Ref<EditorPostImport> p_post_import) { + + +} +#endif + +void EditorSceneImportPlugin::_tag_import_paths(Node *p_scene,Node *p_node) { + + if (p_scene!=p_node && p_node->get_owner()!=p_scene) + return; + + NodePath path = p_scene->get_path_to(p_node); + p_node->set_import_path( path ); + + Spatial *snode=p_node->cast_to<Spatial>(); + + if (snode) { + + snode->set_import_transform(snode->get_transform()); + } + + for(int i=0;i<p_node->get_child_count();i++) { + _tag_import_paths(p_scene,p_node->get_child(i)); + } + +} + +Error EditorSceneImportPlugin::import1(const Ref<ResourceImportMetadata>& p_from,Node**r_node,List<String> *r_missing) { + + Ref<ResourceImportMetadata> from=p_from; + + ERR_FAIL_COND_V(from->get_source_count()!=1,ERR_INVALID_PARAMETER); + + String src_path=EditorImportPlugin::expand_source_path(from->get_source_path(0)); + + Ref<EditorSceneImporter> importer; + String ext=src_path.get_extension().to_lower(); + + + EditorProgress progress("import",TTR("Import Scene"),104); + progress.step(TTR("Importing Scene.."),0); + + for(int i=0;i<importers.size();i++) { + + List<String> extensions; + importers[i]->get_extensions(&extensions); + + for(List<String>::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().to_lower()==ext) { + + importer = importers[i]; + break; + } + } + + if (importer.is_valid()) + break; + } + + ERR_FAIL_COND_V(!importer.is_valid(),ERR_FILE_UNRECOGNIZED); + + int animation_flags=p_from->get_option("animation_flags"); + int scene_flags = from->get_option("flags"); + int fps = 24; + if (from->has_option("animation_bake_fps")) + fps=from->get_option("animation_bake_fps"); + + + Array clips; + if (from->has_option("animation_clips")) + clips=from->get_option("animation_clips"); + + uint32_t import_flags=0; + if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_DETECT_LOOP) + import_flags|=EditorSceneImporter::IMPORT_ANIMATION_DETECT_LOOP; + if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_KEEP_VALUE_TRACKS) + import_flags |= EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS; + if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE) + import_flags|=EditorSceneImporter::IMPORT_ANIMATION_OPTIMIZE; + if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS) + import_flags|=EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS; + if (scene_flags&SCENE_FLAG_IMPORT_ANIMATIONS) + import_flags|=EditorSceneImporter::IMPORT_ANIMATION; + /* + if (scene_flags&SCENE_FLAG_FAIL_ON_MISSING_IMAGES) + import_flags|=EditorSceneImporter::IMPORT_FAIL_ON_MISSING_DEPENDENCIES; + */ + if (scene_flags&SCENE_FLAG_GENERATE_TANGENT_ARRAYS) + import_flags|=EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS; + + + + + + Error err=OK; + Node *scene = importer->import_scene(src_path,import_flags,fps,r_missing,&err); + if (!scene || err!=OK) { + return err; + } + + if (from->has_option("root_type")) { + String type = from->get_option("root_type"); + Object *base = ClassDB::instance(type); + Node *base_node = NULL; + if (base) + base_node=base->cast_to<Node>(); + + if (base_node) { + + scene->replace_by(base_node); + memdelete(scene); + scene=base_node; + } + } + + scene->set_name(from->get_option("root_name")); + _tag_import_paths(scene,scene); + + *r_node=scene; + return OK; +} + + +void EditorSceneImportPlugin::_create_clips(Node *scene, const Array& p_clips,bool p_bake_all) { + + if (!scene->has_node(String("AnimationPlayer"))) + return; + + Node* n = scene->get_node(String("AnimationPlayer")); + ERR_FAIL_COND(!n); + AnimationPlayer *anim = n->cast_to<AnimationPlayer>(); + ERR_FAIL_COND(!anim); + + if (!anim->has_animation("default")) + return; + + + Ref<Animation> default_anim = anim->get_animation("default"); + + for(int i=0;i<p_clips.size();i+=4) { + + String name = p_clips[i]; + float from=p_clips[i+1]; + float to=p_clips[i+2]; + bool loop=p_clips[i+3]; + if (from>=to) + continue; + + Ref<Animation> new_anim = memnew( Animation ); + + for(int j=0;j<default_anim->get_track_count();j++) { + + + List<float> keys; + int kc = default_anim->track_get_key_count(j); + int dtrack=-1; + for(int k=0;k<kc;k++) { + + float kt = default_anim->track_get_key_time(j,k); + if (kt>=from && kt<to) { + + //found a key within range, so create track + if (dtrack==-1) { + new_anim->add_track(default_anim->track_get_type(j)); + dtrack = new_anim->get_track_count()-1; + new_anim->track_set_path(dtrack,default_anim->track_get_path(j)); + + if (kt>(from+0.01) && k>0) { + + if (default_anim->track_get_type(j)==Animation::TYPE_TRANSFORM) { + Quat q; + Vector3 p; + Vector3 s; + default_anim->transform_track_interpolate(j,from,&p,&q,&s); + new_anim->transform_track_insert_key(dtrack,0,p,q,s); + } + } + + } + + if (default_anim->track_get_type(j)==Animation::TYPE_TRANSFORM) { + Quat q; + Vector3 p; + Vector3 s; + default_anim->transform_track_get_key(j,k,&p,&q,&s); + new_anim->transform_track_insert_key(dtrack,kt-from,p,q,s); + } + + } + + if (dtrack!=-1 && kt>=to) { + + if (default_anim->track_get_type(j)==Animation::TYPE_TRANSFORM) { + Quat q; + Vector3 p; + Vector3 s; + default_anim->transform_track_interpolate(j,to,&p,&q,&s); + new_anim->transform_track_insert_key(dtrack,to-from,p,q,s); + } + } + + } + + if (dtrack==-1 && p_bake_all) { + new_anim->add_track(default_anim->track_get_type(j)); + dtrack = new_anim->get_track_count()-1; + new_anim->track_set_path(dtrack,default_anim->track_get_path(j)); + if (default_anim->track_get_type(j)==Animation::TYPE_TRANSFORM) { + + + Quat q; + Vector3 p; + Vector3 s; + default_anim->transform_track_interpolate(j,from,&p,&q,&s); + new_anim->transform_track_insert_key(dtrack,0,p,q,s); + default_anim->transform_track_interpolate(j,to,&p,&q,&s); + new_anim->transform_track_insert_key(dtrack,to-from,p,q,s); + } + + } + } + + + new_anim->set_loop(loop); + new_anim->set_length(to-from); + anim->add_animation(name,new_anim); + } + + anim->remove_animation("default"); //remove default (no longer needed) +} + +void EditorSceneImportPlugin::_filter_anim_tracks(Ref<Animation> anim,Set<String> &keep) { + + Ref<Animation> a = anim; + ERR_FAIL_COND(!a.is_valid()); + + print_line("From Anim "+anim->get_name()+":"); + + for(int j=0;j<a->get_track_count();j++) { + + String path = a->track_get_path(j); + + if (!keep.has(path)) { + + print_line("Remove: "+path); + a->remove_track(j); + j--; + } + + } +} + + +void EditorSceneImportPlugin::_filter_tracks(Node *scene, const String& p_text) { + + if (!scene->has_node(String("AnimationPlayer"))) + return; + Node* n = scene->get_node(String("AnimationPlayer")); + ERR_FAIL_COND(!n); + AnimationPlayer *anim = n->cast_to<AnimationPlayer>(); + ERR_FAIL_COND(!anim); + + Vector<String> strings = p_text.split("\n"); + for(int i=0;i<strings.size();i++) { + + strings[i]=strings[i].strip_edges(); + } + + List<StringName> anim_names; + anim->get_animation_list(&anim_names); + for(List<StringName>::Element *E=anim_names.front();E;E=E->next()) { + + String name = E->get(); + bool valid_for_this=false; + bool valid=false; + + Set<String> keep; + Set<String> keep_local; + + + for(int i=0;i<strings.size();i++) { + + + if (strings[i].begins_with("@")) { + + valid_for_this=false; + for(Set<String>::Element *F=keep_local.front();F;F=F->next()) { + keep.insert(F->get()); + } + keep_local.clear(); + + Vector<String> filters=strings[i].substr(1,strings[i].length()).split(","); + for(int j=0;j<filters.size();j++) { + + String fname = filters[j].strip_edges(); + if (fname=="") + continue; + int fc = fname[0]; + bool plus; + if (fc=='+') + plus=true; + else if (fc=='-') + plus=false; + else + continue; + + String filter=fname.substr(1,fname.length()).strip_edges(); + + if (!name.matchn(filter)) + continue; + valid_for_this=plus; + } + + if (valid_for_this) + valid=true; + + } else if (valid_for_this) { + + Ref<Animation> a = anim->get_animation(name); + if (!a.is_valid()) + continue; + + for(int j=0;j<a->get_track_count();j++) { + + String path = a->track_get_path(j); + + String tname = strings[i]; + if (tname=="") + continue; + int fc = tname[0]; + bool plus; + if (fc=='+') + plus=true; + else if (fc=='-') + plus=false; + else + continue; + + String filter=tname.substr(1,tname.length()).strip_edges(); + + if (!path.matchn(filter)) + continue; + + if (plus) + keep_local.insert(path); + else if (!keep.has(path)) { + keep_local.erase(path); + } + } + + } + + } + + if (valid) { + for(Set<String>::Element *F=keep_local.front();F;F=F->next()) { + keep.insert(F->get()); + } + print_line("FILTERING ANIM: "+String(E->get())); + _filter_anim_tracks(anim->get_animation(name),keep); + } else { + print_line("NOT FILTERING ANIM: "+String(E->get())); + + } + + } + + + +} + +void EditorSceneImportPlugin::_optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle) { + + if (!scene->has_node(String("AnimationPlayer"))) + return; + Node* n = scene->get_node(String("AnimationPlayer")); + ERR_FAIL_COND(!n); + AnimationPlayer *anim = n->cast_to<AnimationPlayer>(); + ERR_FAIL_COND(!anim); + + + List<StringName> anim_names; + anim->get_animation_list(&anim_names); + for(List<StringName>::Element *E=anim_names.front();E;E=E->next()) { + + Ref<Animation> a = anim->get_animation(E->get()); + a->optimize(p_max_lin_error,p_max_ang_error,Math::deg2rad(p_max_angle)); + } +} + + +void EditorSceneImportPlugin::_find_resources_to_merge(Node *scene, Node *node, bool p_merge_material, Map<String, Ref<Material> > &materials, bool p_merge_anims, Map<String,Ref<Animation> >& merged_anims,Set<Ref<Mesh> > &tested_meshes) { + + if (node!=scene && node->get_owner()!=scene) + return; + + String path = scene->get_path_to(node); + + if (p_merge_anims && node->cast_to<AnimationPlayer>()) { + + AnimationPlayer *ap = node->cast_to<AnimationPlayer>(); + List<StringName> anims; + ap->get_animation_list(&anims); + for (List<StringName>::Element *E=anims.front();E;E=E->next()) { + Ref<Animation> anim = ap->get_animation(E->get()); + Ref<Animation> clone; + + bool has_user_tracks=false; + + for(int i=0;i<anim->get_track_count();i++) { + + if (!anim->track_is_imported(i)) { + has_user_tracks=true; + break; + } + } + + if (has_user_tracks) { + + clone = anim->duplicate(); + for(int i=0;i<clone->get_track_count();i++) { + if (clone->track_is_imported(i)) { + clone->remove_track(i); + i--; + } + } + + merged_anims[path+"::"+String(E->get())]=clone; + } + } + } + + + + if (p_merge_material && node->cast_to<MeshInstance>()) { + MeshInstance *mi=node->cast_to<MeshInstance>(); + Ref<Mesh> mesh = mi->get_mesh(); + if (mesh.is_valid() && mesh->get_name()!=String() && !tested_meshes.has(mesh)) { + + for(int i=0;i<mesh->get_surface_count();i++) { + Ref<Material> material = mesh->surface_get_material(i); + + if (material.is_valid()) { + + String sname = mesh->surface_get_name(i); + if (sname=="") + sname="surf_"+itos(i); + + sname=mesh->get_name()+":surf:"+sname; + materials[sname]=material; + } + } + + tested_meshes.insert(mesh); + } + + if (mesh.is_valid()) { + + for(int i=0;i<mesh->get_surface_count();i++) { + Ref<Material> material = mi->get_surface_material(i); + if (material.is_valid()) { + String sname = mesh->surface_get_name(i); + if (sname=="") + sname="surf_"+itos(i); + + sname=path+":inst_surf:"+sname; + materials[sname]=material; + } + } + + } + + Ref<Material> override = mi->get_material_override(); + + if (override.is_valid()) { + + materials[path+":override"]=override; + } + } + + + + for(int i=0;i<node->get_child_count();i++) { + _find_resources_to_merge(scene,node->get_child(i),p_merge_material,materials,p_merge_anims,merged_anims,tested_meshes); + } + +} + + +void EditorSceneImportPlugin::_merge_found_resources(Node *scene, Node *node, bool p_merge_material, const Map<String, Ref<Material> > &materials, bool p_merge_anims, const Map<String,Ref<Animation> >& merged_anims, Set<Ref<Mesh> > &tested_meshes) { + + if (node!=scene && node->get_owner()!=scene) + return; + + String path = scene->get_path_to(node); + + print_line("at path: "+path); + + if (node->cast_to<AnimationPlayer>()) { + + AnimationPlayer *ap = node->cast_to<AnimationPlayer>(); + List<StringName> anims; + ap->get_animation_list(&anims); + for (List<StringName>::Element *E=anims.front();E;E=E->next()) { + Ref<Animation> anim = ap->get_animation(E->get()); + + String anim_path = path+"::"+String(E->get()); + + if (merged_anims.has(anim_path)) { + + Ref<Animation> user_tracks = merged_anims[anim_path]; + for(int i=0;i<user_tracks->get_track_count();i++) { + + int idx = anim->get_track_count(); + anim->add_track(user_tracks->track_get_type(i)); + anim->track_set_path(idx,user_tracks->track_get_path(i)); + anim->track_set_interpolation_type(idx,user_tracks->track_get_interpolation_type(i)); + for(int j=0;j<user_tracks->track_get_key_count(i);j++) { + + float ofs = user_tracks->track_get_key_time(i,j); + float trans = user_tracks->track_get_key_transition(i,j); + Variant value = user_tracks->track_get_key_value(i,j); + + anim->track_insert_key(idx,ofs,value,trans); + } + } + } + } + } + + + + if (node->cast_to<MeshInstance>()) { + MeshInstance *mi=node->cast_to<MeshInstance>(); + Ref<Mesh> mesh = mi->get_mesh(); + if (mesh.is_valid() && mesh->get_name()!=String() && !tested_meshes.has(mesh)) { + + for(int i=0;i<mesh->get_surface_count();i++) { + + String sname = mesh->surface_get_name(i); + if (sname=="") + sname="surf_"+itos(i); + + sname=mesh->get_name()+":surf:"+sname; + + + if (materials.has(sname)) { + + mesh->surface_set_material(i,materials[sname]); + } + } + + tested_meshes.insert(mesh); + } + + if (mesh.is_valid()) { + + for(int i=0;i<mesh->get_surface_count();i++) { + + String sname = mesh->surface_get_name(i); + if (sname=="") + sname="surf_"+itos(i); + + sname=path+":inst_surf:"+sname; + + + if (materials.has(sname)) { + + mi->set_surface_material(i,materials[sname]); + } + } + + } + + + String opath = path+":override"; + if (materials.has(opath)) { + mi->set_material_override(materials[opath]); + } + + } + + + + for(int i=0;i<node->get_child_count();i++) { + _merge_found_resources(scene,node->get_child(i),p_merge_material,materials,p_merge_anims,merged_anims,tested_meshes); + } + +} + +Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, const Ref<ResourceImportMetadata>& p_from) { + + Error err=OK; + Ref<ResourceImportMetadata> from=p_from; + String src_path=EditorImportPlugin::expand_source_path(from->get_source_path(0)); + int animation_flags=p_from->get_option("animation_flags"); + Array animation_clips = p_from->get_option("animation_clips"); + String animation_filter = p_from->get_option("animation_filters"); + int scene_flags = from->get_option("flags"); + float anim_optimizer_linerr=0.05; + float anim_optimizer_angerr=0.01; + float anim_optimizer_maxang=22; + + if (from->has_option("animation_optimizer_linear_error")) + anim_optimizer_linerr=from->get_option("animation_optimizer_linear_error"); + if (from->has_option("animation_optimizer_angular_error")) + anim_optimizer_angerr=from->get_option("animation_optimizer_angular_error"); + if (from->has_option("animation_optimizer_max_angle")) + anim_optimizer_maxang=from->get_option("animation_optimizer_max_angle"); + + EditorProgress progress("import",TTR("Import Scene"),104); + progress.step(TTR("Importing Scene.."),2); + + + from->set_source_md5(0,FileAccess::get_md5(src_path)); + from->set_editor(get_name()); + + from->set_option("reimport",false); + String target_res_path=p_dest_path.get_base_dir(); + + Map<Ref<Mesh>,Ref<Shape> > collision_map; + + Map< Ref<ImageTexture>,TextureRole > imagemap; + + scene=_fix_node(scene,scene,collision_map,scene_flags,imagemap); + if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE) + _optimize_animations(scene,anim_optimizer_linerr,anim_optimizer_angerr,anim_optimizer_maxang); + if (animation_clips.size()) + _create_clips(scene,animation_clips,animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS); + + _filter_tracks(scene,animation_filter); + + + if (scene_flags&(SCENE_FLAG_MERGE_KEEP_MATERIALS|SCENE_FLAG_MERGE_KEEP_EXTRA_ANIM_TRACKS) && FileAccess::exists(p_dest_path)) { + //must merge! + + print_line("MUST MERGE"); + Ref<PackedScene> pscene = ResourceLoader::load(p_dest_path,"PackedScene",true); + if (pscene.is_valid()) { + + Node *instance = pscene->instance(); + if (instance) { + Map<String,Ref<Animation> > merged_anims; + Map<String,Ref<Material> > merged_materials; + Set<Ref<Mesh> > tested_meshes; + + _find_resources_to_merge(instance,instance,scene_flags&SCENE_FLAG_MERGE_KEEP_MATERIALS,merged_materials,scene_flags&SCENE_FLAG_MERGE_KEEP_EXTRA_ANIM_TRACKS,merged_anims,tested_meshes); + + tested_meshes.clear(); + _merge_found_resources(scene,scene,scene_flags&SCENE_FLAG_MERGE_KEEP_MATERIALS,merged_materials,scene_flags&SCENE_FLAG_MERGE_KEEP_EXTRA_ANIM_TRACKS,merged_anims,tested_meshes); + + memdelete(instance); + } + + } + + } + + /// BEFORE ANYTHING, RUN SCRIPT + + progress.step(TTR("Running Custom Script.."),2); + + String post_import_script_path = from->get_option("post_import_script"); + Ref<EditorScenePostImport> post_import_script; + + if (post_import_script_path!="") { + post_import_script_path = post_import_script_path; + Ref<Script> scr = ResourceLoader::load(post_import_script_path); + if (!scr.is_valid()) { + EditorNode::add_io_error(TTR("Couldn't load post-import script:")+" "+post_import_script_path); + } else { + + post_import_script = Ref<EditorScenePostImport>( memnew( EditorScenePostImport ) ); + post_import_script->set_script(scr.get_ref_ptr()); + if (!post_import_script->get_script_instance()) { + EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):")+" "+post_import_script_path); + post_import_script.unref(); + return ERR_CANT_CREATE; + } + } + } + + + if (post_import_script.is_valid()) { + scene = post_import_script->post_import(scene); + if (!scene) { + EditorNode::add_io_error(TTR("Error running post-import script:")+" "+post_import_script_path); + return err; + } + + + } + + + /// IMPORT IMAGES + + + int idx=0; + + int image_format = from->get_option("texture_format"); + int image_flags = from->get_option("texture_flags"); + float image_quality = from->get_option("texture_quality"); + + for (Map< Ref<ImageTexture>,TextureRole >::Element *E=imagemap.front();E;E=E->next()) { + + //texture could be converted to something more useful for 3D, that could load individual mipmaps and stuff + //but not yet.. + + Ref<ImageTexture> texture = E->key(); + + ERR_CONTINUE(!texture.is_valid()); + + String path = texture->get_path(); + String fname= path.get_file(); + String target_path = GlobalConfig::get_singleton()->localize_path(target_res_path.plus_file(fname)); + progress.step(TTR("Import Image:")+" "+fname,3+(idx)*100/imagemap.size()); + + idx++; + + if (path==target_path) { + + EditorNode::add_io_error(TTR("Can't import a file over itself:")+" "+target_path); + continue; + } + + if (!target_path.begins_with("res://")) { + EditorNode::add_io_error(vformat(TTR("Couldn't localize path: %s (already local)"),target_path)); + continue; + } + + + { + + + target_path=target_path.get_basename()+".tex"; + + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + + uint32_t flags = image_flags; + if (E->get()==TEXTURE_ROLE_DIFFUSE && scene_flags&SCENE_FLAG_LINEARIZE_DIFFUSE_TEXTURES) + flags|=EditorTextureImportPlugin::IMAGE_FLAG_CONVERT_TO_LINEAR; + + if (E->get()==TEXTURE_ROLE_NORMALMAP && scene_flags&SCENE_FLAG_CONVERT_NORMALMAPS_TO_XY) + flags|=EditorTextureImportPlugin::IMAGE_FLAG_CONVERT_NORMAL_TO_XY; + + imd->set_option("flags",flags); + imd->set_option("format",image_format); + imd->set_option("quality",image_quality); + imd->set_option("atlas",false); + imd->add_source(EditorImportPlugin::validate_source_path(path)); + + + if (FileAccess::exists(target_path)) { + + Ref<ResourceImportMetadata> rimdex = ResourceLoader::load_import_metadata(target_path); + if (rimdex.is_valid()) { + //make sure the options are the same, otherwise re-import + List<String> opts; + imd->get_options(&opts); + bool differ=false; + for (List<String>::Element *E=opts.front();E;E=E->next()) { + if (!(rimdex->get_option(E->get())==imd->get_option(E->get()))) { + differ=true; + break; + } + } + + if (!differ) { + texture->set_path(target_path); + continue; //already imported + } + } + } + + EditorTextureImportPlugin::get_singleton()->import(target_path,imd); + + } + } + + + + progress.step(TTR("Saving.."),104); + + Ref<PackedScene> packer = memnew( PackedScene ); + packer->pack(scene); + //packer->set_path(p_dest_path); do not take over, let the changed files reload themselves + packer->set_import_metadata(from); + + print_line("SAVING TO: "+p_dest_path); + err = ResourceSaver::save(p_dest_path,packer); //do not take over, let the changed files reload themselves + + //EditorFileSystem::get_singleton()->update_resource(packer); + + memdelete(scene); + + /* + scene->set_filename(p_dest_path); + if (r_scene) { + *r_scene=scene; + } else { + memdelete(scene); + } + + String sp; + if (p_post_import.is_valid() && !p_post_import->get_script().is_null()) { + Ref<Script> scr = p_post_import->get_script(); + if (scr.is_valid()) + sp=scr->get_path(); + } + + String op=_getrelpath(p_path,p_dest_path); + + */ + + EditorNode::get_singleton()->reload_scene(p_dest_path); + + return err; + +} + + +Error EditorSceneImportPlugin::import(const String& p_dest_path, const Ref<ResourceImportMetadata>& p_from){ + + + Node *n=NULL; + Error err = import1(p_from,&n); + if (err!=OK) { + if (n) { + memdelete(n); + } + return err; + } + return import2(n,p_dest_path,p_from); +} + +void EditorSceneImportPlugin::add_importer(const Ref<EditorSceneImporter>& p_importer) { + + importers.push_back(p_importer); +} + +void EditorSceneImportPlugin::import_from_drop(const Vector<String>& p_drop,const String& p_dest_path) { + + List<String> extensions; + for(int i=0;i<importers.size();i++) { + importers[i]->get_extensions(&extensions); + } + //bool warn_compatible=false; + for(int i=0;i<p_drop.size();i++) { + + String extension = p_drop[i].get_extension().to_lower(); + + for(List<String>::Element *E=extensions.front();E;E=E->next()) { + + if (E->get()==extension) { + + dialog->popup_import(String()); + dialog->setup_popup(p_drop[i],p_dest_path); + return; + } + } + } + +} + + +EditorSceneImportPlugin::EditorSceneImportPlugin(EditorNode* p_editor) { + + dialog = memnew( EditorSceneImportDialog(p_editor,this) ); + p_editor->get_gui_base()->add_child(dialog); +} + + +/////////////////////////////// + + +String EditorSceneAnimationImportPlugin::get_name() const { + + return "anim_3d"; +} +String EditorSceneAnimationImportPlugin::get_visible_name() const{ + + + return TTR("3D Scene Animation"); +} +void EditorSceneAnimationImportPlugin::import_dialog(const String& p_from){ + + +} +Error EditorSceneAnimationImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from){ + + return OK; +} + +EditorSceneAnimationImportPlugin::EditorSceneAnimationImportPlugin(EditorNode* p_editor) { + + +} +#endif diff --git a/editor/io_plugins/editor_scene_import_plugin.h b/editor/io_plugins/editor_scene_import_plugin.h new file mode 100644 index 0000000000..2ae0693600 --- /dev/null +++ b/editor/io_plugins/editor_scene_import_plugin.h @@ -0,0 +1,200 @@ +/*************************************************************************/ +/* editor_scene_import_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_SCENE_IMPORT_PLUGIN_H +#define EDITOR_SCENE_IMPORT_PLUGIN_H +#if 0 +#include "scene/gui/dialogs.h" +#include "scene/gui/tree.h" +#include "scene/gui/label.h" +#include "scene/gui/option_button.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/file_dialog.h" +#include "scene/gui/progress_bar.h" +#include "scene/gui/slider.h" +#include "scene/gui/spin_box.h" +#include "scene/resources/mesh.h" +#include "editor/editor_file_system.h" +#include "editor/editor_dir_dialog.h" +#include "editor/editor_import_export.h" +#include "editor/io_plugins/editor_texture_import_plugin.h" +#include "scene/resources/animation.h" + + +class EditorNode; +class EditorSceneImportDialog; + +class EditorSceneImporter : public Reference { + + GDCLASS(EditorSceneImporter,Reference ); +public: + + enum ImportFlags { + IMPORT_SCENE=1, + IMPORT_ANIMATION=2, + IMPORT_ANIMATION_DETECT_LOOP=4, + IMPORT_ANIMATION_OPTIMIZE=8, + IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS=16, + IMPORT_ANIMATION_KEEP_VALUE_TRACKS=32, + IMPORT_GENERATE_TANGENT_ARRAYS=256, + IMPORT_FAIL_ON_MISSING_DEPENDENCIES=512 + + }; + + virtual uint32_t get_import_flags() const=0; + virtual void get_extensions(List<String> *r_extensions) const=0; + virtual Node* import_scene(const String& p_path,uint32_t p_flags,int p_bake_fps,List<String> *r_missing_deps,Error* r_err=NULL)=0; + virtual Ref<Animation> import_animation(const String& p_path,uint32_t p_flags)=0; + + + + EditorSceneImporter(); +}; + +///////////////////////////////////////// + + +//Plugin for post processing scenes or images + +class EditorScenePostImport : public Reference { + + GDCLASS(EditorScenePostImport,Reference ); +protected: + + static void _bind_methods(); +public: + + virtual Node* post_import(Node* p_scene); + EditorScenePostImport(); +}; + + +class EditorSceneImportPlugin : public EditorImportPlugin { + + GDCLASS(EditorSceneImportPlugin,EditorImportPlugin); + + EditorSceneImportDialog *dialog; + + Vector<Ref<EditorSceneImporter> > importers; + + enum TextureRole { + TEXTURE_ROLE_DEFAULT, + TEXTURE_ROLE_DIFFUSE, + TEXTURE_ROLE_NORMALMAP + }; + + void _find_resources(const Variant& p_var,Map<Ref<ImageTexture>,TextureRole >& image_map,int p_flags); + Node* _fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map,uint32_t p_flags,Map<Ref<ImageTexture>,TextureRole >& image_map); + void _create_clips(Node *scene, const Array& p_clips, bool p_bake_all); + void _filter_anim_tracks(Ref<Animation> anim,Set<String> &keep); + void _filter_tracks(Node *scene, const String& p_text); + void _optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle); + + void _tag_import_paths(Node *p_scene,Node *p_node); + + void _find_resources_to_merge(Node *scene, Node *node, bool p_merge_material, Map<String,Ref<Material> >&materials, bool p_merge_anims, Map<String,Ref<Animation> >& merged_anims, Set<Ref<Mesh> > &tested_meshes); + void _merge_found_resources(Node *scene, Node *node, bool p_merge_material, const Map<String, Ref<Material> > &materials, bool p_merge_anims, const Map<String,Ref<Animation> >& merged_anims, Set<Ref<Mesh> > &tested_meshes); + + +public: + + enum SceneFlags { + + SCENE_FLAG_CREATE_COLLISIONS=1<<0, + SCENE_FLAG_CREATE_PORTALS=1<<1, + SCENE_FLAG_CREATE_ROOMS=1<<2, + SCENE_FLAG_SIMPLIFY_ROOMS=1<<3, + SCENE_FLAG_CREATE_BILLBOARDS=1<<4, + SCENE_FLAG_CREATE_IMPOSTORS=1<<5, + SCENE_FLAG_CREATE_LODS=1<<6, + SCENE_FLAG_CREATE_CARS=1<<8, + SCENE_FLAG_CREATE_WHEELS=1<<9, + SCENE_FLAG_DETECT_ALPHA=1<<15, + SCENE_FLAG_DETECT_VCOLOR=1<<16, + SCENE_FLAG_CREATE_NAVMESH=1<<17, + SCENE_FLAG_DETECT_LIGHTMAP_LAYER=1<<18, + + SCENE_FLAG_MERGE_KEEP_MATERIALS=1<<20, + SCENE_FLAG_MERGE_KEEP_EXTRA_ANIM_TRACKS=1<<21, + + SCENE_FLAG_REMOVE_NOIMP=1<<24, + SCENE_FLAG_IMPORT_ANIMATIONS=1<<25, + SCENE_FLAG_COMPRESS_GEOMETRY=1<<26, + SCENE_FLAG_GENERATE_TANGENT_ARRAYS=1<<27, + SCENE_FLAG_LINEARIZE_DIFFUSE_TEXTURES=1<<28, + SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS=1<<29, + SCENE_FLAG_CONVERT_NORMALMAPS_TO_XY=1<<30, + }; + + + + virtual String get_name() const; + virtual String get_visible_name() const; + virtual void import_dialog(const String& p_from=""); + virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from); + + Error import1(const Ref<ResourceImportMetadata>& p_from,Node**r_node,List<String> *r_missing=NULL); + Error import2(Node* p_scene,const String& p_path, const Ref<ResourceImportMetadata>& p_from); + + void add_importer(const Ref<EditorSceneImporter>& p_importer); + const Vector<Ref<EditorSceneImporter> >& get_importers() { return importers; } + + virtual void import_from_drop(const Vector<String>& p_drop,const String& p_dest_path); + + EditorSceneImportPlugin(EditorNode* p_editor=NULL); + + +}; + + +class EditorSceneAnimationImportPlugin : public EditorImportPlugin { + + GDCLASS(EditorSceneAnimationImportPlugin,EditorImportPlugin); +public: + + + enum AnimationFlags { + + ANIMATION_DETECT_LOOP=1, + ANIMATION_KEEP_VALUE_TRACKS=2, + ANIMATION_OPTIMIZE=4, + ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS=8 + }; + + virtual String get_name() const; + virtual String get_visible_name() const; + virtual void import_dialog(const String& p_from=""); + virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from); + + EditorSceneAnimationImportPlugin(EditorNode* p_editor=NULL); + + +}; + +#endif +#endif // EDITOR_SCENE_IMPORT_PLUGIN_H diff --git a/editor/io_plugins/editor_scene_importer_fbxconv.cpp b/editor/io_plugins/editor_scene_importer_fbxconv.cpp new file mode 100644 index 0000000000..d75a26a948 --- /dev/null +++ b/editor/io_plugins/editor_scene_importer_fbxconv.cpp @@ -0,0 +1,1136 @@ +/*************************************************************************/ +/* editor_scene_importer_fbxconv.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_scene_importer_fbxconv.h" + +#include "os/file_access.h" +#include "os/os.h" +#include "editor/editor_settings.h" +#include "scene/3d/mesh_instance.h" +#include "scene/animation/animation_player.h" + +#if 0 +String EditorSceneImporterFBXConv::_id(const String& p_id) const { + + return p_id.replace(":","_").replace("/","_"); +} + +uint32_t EditorSceneImporterFBXConv::get_import_flags() const { + + return IMPORT_SCENE|IMPORT_ANIMATION; +} +void EditorSceneImporterFBXConv::get_extensions(List<String> *r_extensions) const{ + + r_extensions->push_back("fbx"); + r_extensions->push_back("g3dj"); +} + + +Color EditorSceneImporterFBXConv::_get_color(const Array& a) { + + if (a.size()<3) + return Color(); + Color c; + c.r=a[0]; + c.g=a[1]; + c.b=a[2]; + if (a.size()>=4) + c.a=a[3]; + return c; + +} + +Transform EditorSceneImporterFBXConv::_get_transform_mixed(const Dictionary& d,const Dictionary& dbase) { + + + + + Array translation; + + if (d.has("translation")) + translation=d["translation"]; + else if (dbase.has("translation")) + translation=dbase["translation"]; + + Array rotation; + + if (d.has("rotation")) + rotation=d["rotation"]; + else if (dbase.has("rotation")) + rotation=dbase["rotation"]; + + Array scale; + + if (d.has("scale")) + scale=d["scale"]; + else if (dbase.has("scale")) + scale=dbase["scale"]; + + Transform t; + + + if (translation.size()) { + Array tr = translation; + if (tr.size()>=3) { + t.origin.x=tr[0]; + t.origin.y=tr[1]; + t.origin.z=tr[2]; + } + } + + if (rotation.size()) { + + Array r = rotation; + if (r.size()>=4) { + + Quat q; + q.x = r[0]; + q.y = r[1]; + q.z = r[2]; + q.w = r[3]; + t.basis=Matrix3(q); + } + } + + + if (scale.size()) { + + Array sc = scale; + if (sc.size()>=3) { + Vector3 s; + s.x=sc[0]; + s.y=sc[1]; + s.z=sc[2]; + t.basis.scale(s); + } + } + + return t; + + +} + +Transform EditorSceneImporterFBXConv::_get_transform(const Dictionary& d) { + + + Transform t; + + if (d.has("translation")) { + Array tr = d["translation"]; + if (tr.size()>=3) { + t.origin.x=tr[0]; + t.origin.y=tr[1]; + t.origin.z=tr[2]; + } + } + + if (d.has("rotation")) { + + Array r = d["rotation"]; + if (r.size()>=4) { + + Quat q; + q.x = r[0]; + q.y = r[1]; + q.z = r[2]; + q.w = r[3]; + t.basis=Matrix3(q); + } + } + + + if (d.has("scale")) { + + Array sc = d["scale"]; + if (sc.size()>=3) { + Vector3 s; + s.x=sc[0]; + s.y=sc[1]; + s.z=sc[2]; + t.basis.scale(s); + } + } + + return t; +} + + +void EditorSceneImporterFBXConv::_detect_bones_in_nodes(State& state,const Array& p_nodes) { + + + for(int i=0;i<p_nodes.size();i++) { + + Dictionary d = p_nodes[i]; + if (d.has("isBone") && bool(d["isBone"])) { + + String bone_name=_id(d["id"]); + print_line("IS BONE: "+bone_name); + if (!state.bones.has(bone_name)) { + state.bones.insert(bone_name,BoneInfo()); + } + + if (!state.bones[bone_name].has_rest) { + state.bones[bone_name].rest=_get_transform(d).affine_inverse(); + } + + state.bones[bone_name].node=d; + + //state.bones[name].rest=_get_transform(b); + } + + if (d.has("parts")) { + + Array parts=d["parts"]; + for(int j=0;j<parts.size();j++) { + + Dictionary p=parts[j]; + if (p.has("bones")) { + Array bones=p["bones"]; + //omfg + for(int k=0;k<bones.size();k++) { + + Dictionary b = bones[k]; + if (b.has("node")) { + + String name = _id(b["node"]); + if (!state.bones.has(name)) { + state.bones.insert(name,BoneInfo()); + } + + state.bones[name].rest=_get_transform(b); + state.bones[name].has_rest=true; + } + } + } + + } + } + + if (d.has("children")) { + + _detect_bones_in_nodes(state,d["children"]); + } + } + +} + +void EditorSceneImporterFBXConv::_parse_skeletons(const String& p_name,State& state, const Array &p_nodes, Skeleton *p_skeleton,int p_parent) { + + + + for(int i=0;i<p_nodes.size();i++) { + + + Dictionary d = p_nodes[i]; + int bone_idx=-1; + String id; + Skeleton* skeleton=p_skeleton; + if (d.has("id")) { + + id=_id(d["id"]); + if (state.bones.has(id)) { + //BONER + if (!skeleton) { + skeleton=memnew( Skeleton ); + state.skeletons[id]=skeleton; + } + bone_idx = skeleton->get_bone_count(); + skeleton->add_bone(id); + skeleton->set_bone_parent(bone_idx,p_parent); + skeleton->set_bone_rest(bone_idx,state.bones[id].rest); + state.bones[id].skeleton=skeleton; + } + } + + if (d.has("children")) { + + _parse_skeletons(id,state,d["children"],skeleton,bone_idx); + } + } + +} + +void EditorSceneImporterFBXConv::_detect_bones(State& state) { + //This format should mark when a node is a bone, + //which is the only thing that Collada does right. + //think about others implementing a parser. + //Just _one_ string and you avoid loads of lines of code to other people. + + for(int i=0;i<state.animations.size();i++) { + + Dictionary an = state.animations[i]; + if (an.has("bones")) { + + Array bo=an["bones"]; + for(int j=0;j<bo.size();j++) { + + Dictionary b=bo[j]; + if (b.has("boneId")) { + + String id = b["boneId"]; + if (!state.bones.has(id)) { + state.bones.insert(id,BoneInfo()); + } + state.bones[id].has_anim_chan=true; //used in anim + + + } + } + } + } + + _detect_bones_in_nodes(state,state.nodes); + _parse_skeletons("",state,state.nodes,NULL,-1); + + print_line("found bones: "+itos(state.bones.size())); + print_line("found skeletons: "+itos(state.skeletons.size())); +} + +Error EditorSceneImporterFBXConv::_parse_bones(State& state,const Array &p_bones,Skeleton* p_skeleton) { + + + + return OK; +} + + +void EditorSceneImporterFBXConv::_add_surface(State& state,Ref<Mesh>& m,const Dictionary &part) { + + if (part.has("meshpartid")) { + + String id = part["meshpartid"]; + ERR_FAIL_COND(!state.surface_cache.has(id)); + + + Ref<Material> mat; + if (part.has("materialid")) { + String matid=part["materialid"]; + if (state.material_cache.has(matid)) { + mat=state.material_cache[matid]; + } + } + int idx = m->get_surface_count(); + + Array array = state.surface_cache[id].array; + PoolVector<float> indices = array[Mesh::ARRAY_BONES]; + if (indices.size() && part.has("bones")) { + + + Map<int,int> index_map; + + Array bones=part["bones"]; + + for(int i=0;i<bones.size();i++) { + + Dictionary bone=bones[i]; + String name=_id(bone["node"]); + + if (state.bones.has(name)) { + int idx=state.bones[name].skeleton->find_bone(name); + if (idx==-1) + idx=0; + index_map[i]=idx; + } + } + + + + int ilen=indices.size(); + { + PoolVector<float>::Write iw=indices.write(); + for(int j=0;j<ilen;j++) { + int b = iw[j]; + ERR_CONTINUE(!index_map.has(b)); + b=index_map[b]; + iw[j]=b; + } + } + + array[Mesh::ARRAY_BONES]=indices; + + + } + + m->add_surface(state.surface_cache[id].primitive,array); + m->surface_set_material(idx,mat); + m->surface_set_name(idx,id); + } + +} + +Error EditorSceneImporterFBXConv::_parse_nodes(State& state,const Array &p_nodes,Node* p_base) { + + for(int i=0;i<p_nodes.size();i++) { + + Dictionary n = p_nodes[i]; + Spatial *node=NULL; + bool skip=false; + + String id; + if (n.has("id")) { + id=_id(n["id"]); + } + + print_line("ID: "+id); + + if (state.skeletons.has(id)) { + + Skeleton *skeleton = state.skeletons[id]; + node=skeleton; + skeleton->localize_rests(); + print_line("IS SKELETON! "); + } else if (state.bones.has(id)) { + if (p_base) + node=p_base->cast_to<Spatial>(); + if (!state.bones[id].has_anim_chan) { + print_line("no has anim "+id); + } + skip=true; + } else if (n.has("parts")) { + //is a mesh + MeshInstance *mesh = memnew( MeshInstance ); + node=mesh; + + Array parts=n["parts"]; + String long_identifier; + for(int j=0;j<parts.size();j++) { + + Dictionary part=parts[j]; + if (part.has("meshpartid")) { + String partid=part["meshpartid"]; + long_identifier+=partid; + } + } + + Ref<Mesh> m; + + if (state.mesh_cache.has(long_identifier)) { + m=state.mesh_cache[long_identifier]; + } else { + m = Ref<Mesh>( memnew( Mesh ) ); + + //and parts are surfaces + for(int j=0;j<parts.size();j++) { + + Dictionary part=parts[j]; + if (part.has("meshpartid")) { + _add_surface(state,m,part); + } + } + + + state.mesh_cache[long_identifier]=m; + } + + mesh->set_mesh(m); + } + + if (!skip) { + + if (!node) { + node = memnew( Spatial ); + } + + node->set_name(id); + node->set_transform(_get_transform(n)); + p_base->add_child(node); + node->set_owner(state.scene); + } + + + if (n.has("children")) { + Error err = _parse_nodes(state,n["children"],node); + if (err) + return err; + } + } + + return OK; +} + + +void EditorSceneImporterFBXConv::_parse_materials(State& state) { + + for(int i=0;i<state.materials.size();i++) { + + Dictionary material = state.materials[i]; + + ERR_CONTINUE(!material.has("id")); + String id = _id(material["id"]); + + Ref<FixedSpatialMaterial> mat = memnew( FixedSpatialMaterial ); + + if (material.has("diffuse")) { + mat->set_parameter(FixedSpatialMaterial::PARAM_DIFFUSE,_get_color(material["diffuse"])); + } + + if (material.has("specular")) { + mat->set_parameter(FixedSpatialMaterial::PARAM_SPECULAR,_get_color(material["specular"])); + } + + if (material.has("emissive")) { + mat->set_parameter(FixedSpatialMaterial::PARAM_EMISSION,_get_color(material["emissive"])); + } + + if (material.has("shininess")) { + float exp = material["shininess"]; + mat->set_parameter(FixedSpatialMaterial::PARAM_SPECULAR_EXP,exp); + } + + if (material.has("opacity")) { + Color c = mat->get_parameter(FixedSpatialMaterial::PARAM_DIFFUSE); + c.a=material["opacity"]; + mat->set_parameter(FixedSpatialMaterial::PARAM_DIFFUSE,c); + } + + + if (material.has("textures")) { + + Array textures = material["textures"]; + for(int j=0;j<textures.size();j++) { + + Dictionary texture=textures[j]; + Ref<Texture> tex; + if (texture.has("filename")) { + + + String filename=texture["filename"]; + String path=state.base_path+"/"+filename.replace("\\","/"); + if (state.texture_cache.has(path)) { + tex=state.texture_cache[path]; + } else { + tex = ResourceLoader::load(path,"ImageTexture"); + if (tex.is_null()) { + if (state.missing_deps) + state.missing_deps->push_back(path); + } + state.texture_cache[path]=tex; //add anyway + } + } + + if (tex.is_valid() && texture.has("type")) { + + String type=texture["type"]; + if (type=="DIFFUSE") + mat->set_texture(FixedSpatialMaterial::PARAM_DIFFUSE,tex); + else if (type=="SPECULAR") + mat->set_texture(FixedSpatialMaterial::PARAM_SPECULAR,tex); + else if (type=="SHININESS") + mat->set_texture(FixedSpatialMaterial::PARAM_SPECULAR_EXP,tex); + else if (type=="NORMAL") + mat->set_texture(FixedSpatialMaterial::PARAM_NORMAL,tex); + else if (type=="EMISSIVE") + mat->set_texture(FixedSpatialMaterial::PARAM_EMISSION,tex); + } + + } + } + + state.material_cache[id]=mat; + + } + +} + +void EditorSceneImporterFBXConv::_parse_surfaces(State& state) { + + for(int i=0;i<state.meshes.size();i++) { + + Dictionary mesh = state.meshes[i]; + + ERR_CONTINUE(!mesh.has("attributes")); + ERR_CONTINUE(!mesh.has("vertices")); + ERR_CONTINUE(!mesh.has("parts")); + + print_line("MESH #"+itos(i)); + + Array attrlist=mesh["attributes"]; + Array vertices=mesh["vertices"]; + bool exists[Mesh::ARRAY_MAX]; + int ofs[Mesh::ARRAY_MAX]; + int weight_max=0; + int binormal_ofs=-1; + int weight_ofs[4]; + + for(int j=0;j<Mesh::ARRAY_MAX;j++) { + exists[j]=false; + ofs[j]=0; + } + exists[Mesh::ARRAY_INDEX]=true; + float stride=0; + + for(int j=0;j<attrlist.size();j++) { + + String attr=attrlist[j]; + if (attr=="POSITION") { + exists[Mesh::ARRAY_VERTEX]=true; + ofs[Mesh::ARRAY_VERTEX]=stride; + stride+=3; + } else if (attr=="NORMAL") { + exists[Mesh::ARRAY_NORMAL]=true; + ofs[Mesh::ARRAY_NORMAL]=stride; + stride+=3; + } else if (attr=="COLOR") { + exists[Mesh::ARRAY_COLOR]=true; + ofs[Mesh::ARRAY_COLOR]=stride; + stride+=4; + } else if (attr=="COLORPACKED") { + stride+=1; //ignore + } else if (attr=="TANGENT") { + exists[Mesh::ARRAY_TANGENT]=true; + ofs[Mesh::ARRAY_TANGENT]=stride; + stride+=3; + } else if (attr=="BINORMAL") { + binormal_ofs=stride; + stride+=3; + } else if (attr=="TEXCOORD0") { + exists[Mesh::ARRAY_TEX_UV]=true; + ofs[Mesh::ARRAY_TEX_UV]=stride; + stride+=2; + } else if (attr=="TEXCOORD1") { + exists[Mesh::ARRAY_TEX_UV2]=true; + ofs[Mesh::ARRAY_TEX_UV2]=stride; + stride+=2; + } else if (attr.begins_with("TEXCOORD")) { + stride+=2; + } else if (attr.begins_with("BLENDWEIGHT")) { + int idx=attr.replace("BLENDWEIGHT","").to_int(); + if (idx==0) { + exists[Mesh::ARRAY_BONES]=true; + ofs[Mesh::ARRAY_BONES]=stride; + exists[Mesh::ARRAY_WEIGHTS]=true; + ofs[Mesh::ARRAY_WEIGHTS]=stride+1; + } if (idx<4) { + weight_ofs[idx]=stride; + weight_max=MAX(weight_max,idx+1); + } + + stride+=2; + } + + print_line("ATTR "+attr+" OFS: "+itos(stride)); + + } + + Array parts=mesh["parts"]; + + for(int j=0;j<parts.size();j++) { + + + + Dictionary part=parts[j]; + ERR_CONTINUE(!part.has("indices")); + ERR_CONTINUE(!part.has("id")); + + print_line("PART: "+String(part["id"])); + Array indices=part["indices"]; + Map<int,int> iarray; + Map<int,int> array; + + for(int k=0;k<indices.size();k++) { + + int idx = indices[k]; + if (!iarray.has(idx)) { + int map_to=array.size(); + iarray[idx]=map_to; + array[map_to]=idx; + } + } + + print_line("indices total "+itos(indices.size())+" vertices used: "+itos(array.size())); + + Array arrays; + arrays.resize(Mesh::ARRAY_MAX); + + + + for(int k=0;k<Mesh::ARRAY_MAX;k++) { + + + if (!exists[k]) + continue; + print_line("exists: "+itos(k)); + int lofs = ofs[k]; + switch(k) { + + case Mesh::ARRAY_VERTEX: + case Mesh::ARRAY_NORMAL: { + + PoolVector<Vector3> vtx; + vtx.resize(array.size()); + { + int len=array.size(); + PoolVector<Vector3>::Write w = vtx.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + w[l].x=vertices[pos*stride+lofs+0]; + w[l].y=vertices[pos*stride+lofs+1]; + w[l].z=vertices[pos*stride+lofs+2]; + } + } + arrays[k]=vtx; + + } break; + case Mesh::ARRAY_TANGENT: { + + if (binormal_ofs<0) + break; + + PoolVector<float> tangents; + tangents.resize(array.size()*4); + { + int len=array.size(); + + PoolVector<float>::Write w = tangents.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + Vector3 n; + n.x=vertices[pos*stride+ofs[Mesh::ARRAY_NORMAL]+0]; + n.y=vertices[pos*stride+ofs[Mesh::ARRAY_NORMAL]+1]; + n.z=vertices[pos*stride+ofs[Mesh::ARRAY_NORMAL]+2]; + Vector3 t; + t.x=vertices[pos*stride+lofs+0]; + t.y=vertices[pos*stride+lofs+1]; + t.z=vertices[pos*stride+lofs+2]; + Vector3 bi; + bi.x=vertices[pos*stride+binormal_ofs+0]; + bi.y=vertices[pos*stride+binormal_ofs+1]; + bi.z=vertices[pos*stride+binormal_ofs+2]; + float d = bi.dot(n.cross(t)); + + w[l*4+0]=t.x; + w[l*4+1]=t.y; + w[l*4+2]=t.z; + w[l*4+3]=d; + + } + } + arrays[k]=tangents; + + } break; + case Mesh::ARRAY_COLOR: { + + PoolVector<Color> cols; + cols.resize(array.size()); + { + int len=array.size(); + PoolVector<Color>::Write w = cols.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + w[l].r=vertices[pos*stride+lofs+0]; + w[l].g=vertices[pos*stride+lofs+1]; + w[l].b=vertices[pos*stride+lofs+2]; + w[l].a=vertices[pos*stride+lofs+3]; + } + } + arrays[k]=cols; + + } break; + case Mesh::ARRAY_TEX_UV: + case Mesh::ARRAY_TEX_UV2: { + + PoolVector<Vector2> uvs; + uvs.resize(array.size()); + { + int len=array.size(); + PoolVector<Vector2>::Write w = uvs.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + w[l].x=vertices[pos*stride+lofs+0]; + w[l].y=vertices[pos*stride+lofs+1]; + w[l].y=1.0-w[l].y; + } + } + arrays[k]=uvs; + + } break; + case Mesh::ARRAY_BONES: + case Mesh::ARRAY_WEIGHTS: { + + PoolVector<float> arr; + arr.resize(array.size()*4); + int po=k==Mesh::ARRAY_WEIGHTS?1:0; + lofs=ofs[Mesh::ARRAY_BONES]; + { + int len=array.size(); + + PoolVector<float>::Write w = arr.write(); + for(int l=0;l<len;l++) { + + int pos = array[l]; + + for(int m=0;m<4;m++) { + + float val=0; + if (m<=weight_max) + val=vertices[pos*stride+lofs+m*2+po]; + w[l*4+m]=val; + } + } + } + + arrays[k]=arr; + } break; + case Mesh::ARRAY_INDEX: { + + PoolVector<int> arr; + arr.resize(indices.size()); + { + int len=indices.size(); + + PoolVector<int>::Write w = arr.write(); + for(int l=0;l<len;l++) { + + w[l]=iarray[ indices[l] ]; + } + } + + arrays[k]=arr; + + } break; + + + } + + + } + + Mesh::PrimitiveType pt=Mesh::PRIMITIVE_TRIANGLES; + + if (part.has("type")) { + String type=part["type"]; + if (type=="LINES") + pt=Mesh::PRIMITIVE_LINES; + else if (type=="POINTS") + pt=Mesh::PRIMITIVE_POINTS; + else if (type=="TRIANGLE_STRIP") + pt=Mesh::PRIMITIVE_TRIANGLE_STRIP; + else if (type=="LINE_STRIP") + pt=Mesh::PRIMITIVE_LINE_STRIP; + } + + if (pt==Mesh::PRIMITIVE_TRIANGLES) { + PoolVector<int> ia=arrays[Mesh::ARRAY_INDEX]; + int len=ia.size(); + { + PoolVector<int>::Write w=ia.write(); + for(int l=0;l<len;l+=3) { + SWAP(w[l+1],w[l+2]); + } + } + arrays[Mesh::ARRAY_INDEX]=ia; + + + } + SurfaceInfo si; + si.array=arrays; + si.primitive=pt; + state.surface_cache[_id(part["id"])]=si; + + } + } +} + + +Error EditorSceneImporterFBXConv::_parse_animations(State& state) { + + AnimationPlayer *ap = memnew( AnimationPlayer ); + + state.scene->add_child(ap); + ap->set_owner(state.scene); + + for(int i=0;i<state.animations.size();i++) { + + Dictionary anim = state.animations[i]; + ERR_CONTINUE(!anim.has("id")); + Ref<Animation> an = memnew( Animation ); + an->set_name(_id(anim["id"])); + + + if (anim.has("bones")) { + + Array bone_tracks = anim["bones"]; + for(int j=0;j<bone_tracks.size();j++) { + Dictionary bone_track=bone_tracks[j]; + String bone = bone_track["boneId"]; + if (!bone_track.has("keyframes")) + continue; + if (!state.bones.has(bone)) + continue; + + Skeleton *sk = state.bones[bone].skeleton; + + if (!sk) + continue; + int bone_idx=sk->find_bone(bone); + if (bone_idx==-1) + continue; + + + + String path = state.scene->get_path_to(sk); + path+=":"+bone; + an->add_track(Animation::TYPE_TRANSFORM); + int tidx = an->get_track_count()-1; + an->track_set_path(tidx,path); + + + Dictionary parent_xform_dict; + Dictionary xform_dict; + + if (state.bones.has(bone)) { + xform_dict=state.bones[bone].node; + } + + + Array parent_keyframes; + if (sk->get_bone_parent(bone_idx)!=-1) { + String parent_name = sk->get_bone_name(sk->get_bone_parent(bone_idx)); + if (state.bones.has(parent_name)) { + parent_xform_dict=state.bones[parent_name].node; + } + + print_line("parent for "+bone+"? "+parent_name+" XFD: "+String(Variant(parent_xform_dict))); + for(int k=0;k<bone_tracks.size();k++) { + Dictionary d = bone_tracks[k]; + if (d["boneId"]==parent_name) { + parent_keyframes=d["keyframes"]; + print_line("found keyframes"); + break; + } + } + + + } + + print_line("BONE XFD "+String(Variant(xform_dict))); + + Array keyframes=bone_track["keyframes"]; + + for(int k=0;k<keyframes.size();k++) { + + Dictionary key=keyframes[k]; + Transform xform=_get_transform_mixed(key,xform_dict); + float time = key["keytime"]; + time=time/1000.0; +#if 0 + if (parent_keyframes.size()) { + //localize + print_line(itos(k)+" localizate for: "+bone); + + float prev_kt=-1; + float kt; + int idx=0; + + for(int l=0;l<parent_keyframes.size();l++) { + + Dictionary d=parent_keyframes[l]; + kt=d["keytime"]; + kt=kt/1000.0; + if (kt>time) + break; + prev_kt=kt; + idx++; + + } + + Transform t; + if (idx==0) { + t=_get_transform_mixed(parent_keyframes[0],parent_xform_dict); + } else if (idx==parent_keyframes.size()){ + t=_get_transform_mixed(parent_keyframes[idx-1],parent_xform_dict); + } else { + t=_get_transform_mixed(parent_keyframes[idx-1],parent_xform_dict); + float d = (time-prev_kt)/(kt-prev_kt); + if (d>0) { + Transform t2=_get_transform_mixed(parent_keyframes[idx],parent_xform_dict); + t=t.interpolate_with(t2,d); + } else { + print_line("exact: "+rtos(kt)); + } + } + + xform = t.affine_inverse() * xform; //localize + } else if (!parent_xform_dict.empty()) { + Transform t = _get_transform(parent_xform_dict); + xform = t.affine_inverse() * xform; //localize + } +#endif + + xform = sk->get_bone_rest(bone_idx).affine_inverse() * xform; + + + Quat q = xform.basis; + q.normalize(); + Vector3 s = xform.basis.get_scale(); + Vector3 l = xform.origin; + + + + an->transform_track_insert_key(tidx,time,l,q,s); + + } + + } + + + } + + + ap->add_animation(_id(anim["id"]),an); + + } + + return OK; +} + +Error EditorSceneImporterFBXConv::_parse_json(State& state, const String &p_path) { + + //not the happiest.... + Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); + ERR_FAIL_COND_V(!data.size(),ERR_FILE_CANT_OPEN); + String str; + bool utferr = str.parse_utf8((const char*)data.ptr(),data.size()); + ERR_FAIL_COND_V(utferr,ERR_PARSE_ERROR); + + Dictionary dict; + Error err = dict.parse_json(str); + str=String(); //free mem immediately + ERR_FAIL_COND_V(err,err); + + if (dict.has("meshes")) + state.meshes=dict["meshes"]; + if (dict.has("materials")) + state.materials=dict["materials"]; + if (dict.has("nodes")) + state.nodes=dict["nodes"]; + if (dict.has("animations")) + state.animations=dict["animations"]; + + + state.scene = memnew( Spatial ); + _detect_bones(state); + _parse_surfaces(state); + _parse_materials(state); + err = _parse_nodes(state,state.nodes,state.scene); + if (err) + return err; + + if (state.import_animations) { + err = _parse_animations(state); + if (err) + return err; + } + + print_line("JSON PARSED O-K!"); + + return OK; +} + +Error EditorSceneImporterFBXConv::_parse_fbx(State& state,const String& p_path) { + + state.base_path=p_path.get_base_dir(); + + if (p_path.to_lower().ends_with("g3dj")) { + return _parse_json(state,p_path.basename()+".g3dj"); + } + + String tool = EDITOR_DEF("fbxconv/path",""); + ERR_FAIL_COND_V( !FileAccess::exists(tool),ERR_UNCONFIGURED); + String wine = EDITOR_DEF("fbxconv/use_wine",""); + + List<String> args; + String path=p_path; + if (wine!="") { + List<String> wpargs; + wpargs.push_back("-w"); + wpargs.push_back(p_path); + String pipe; //winepath to convert to windows path + int wpres; + Error wperr = OS::get_singleton()->execute(wine+"path",wpargs,true,NULL,&pipe,&wpres); + ERR_FAIL_COND_V(wperr!=OK,ERR_CANT_CREATE); + ERR_FAIL_COND_V(wpres!=0,ERR_CANT_CREATE); + path=pipe.strip_edges(); + args.push_back(tool); + tool=wine; + } + + args.push_back("-o"); + args.push_back("G3DJ"); + args.push_back(path); + + int res; + Error err = OS::get_singleton()->execute(tool,args,true,NULL,NULL,&res); + ERR_FAIL_COND_V(err!=OK,ERR_CANT_CREATE); + ERR_FAIL_COND_V(res!=0,ERR_CANT_CREATE); + + return _parse_json(state,p_path.basename()+".g3dj"); + + +} + +Node* EditorSceneImporterFBXConv::import_scene(const String& p_path,uint32_t p_flags,List<String> *r_missing_deps,Error* r_err){ + + State state; + state.scene=NULL; + state.missing_deps=r_missing_deps; + state.import_animations=p_flags&IMPORT_ANIMATION; + Error err = _parse_fbx(state,p_path); + if (err!=OK) { + if (r_err) + *r_err=err; + return NULL; + } + + + return state.scene; +} +Ref<Animation> EditorSceneImporterFBXConv::import_animation(const String& p_path,uint32_t p_flags){ + + + return Ref<Animation>(); +} + + +EditorSceneImporterFBXConv::EditorSceneImporterFBXConv() { + + EDITOR_DEF("fbxconv/path",""); +#ifndef WINDOWS_ENABLED + EDITOR_DEF("fbxconv/use_wine",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"fbxconv/use_wine",PROPERTY_HINT_GLOBAL_FILE)); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"fbxconv/path",PROPERTY_HINT_GLOBAL_FILE)); +#else + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"fbxconv/path",PROPERTY_HINT_GLOBAL_FILE,"exe")); +#endif + +} +#endif diff --git a/editor/io_plugins/editor_scene_importer_fbxconv.h b/editor/io_plugins/editor_scene_importer_fbxconv.h new file mode 100644 index 0000000000..da7058ad88 --- /dev/null +++ b/editor/io_plugins/editor_scene_importer_fbxconv.h @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* editor_scene_importer_fbxconv.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_SCENE_IMPORTER_FBXCONV_H +#define EDITOR_SCENE_IMPORTER_FBXCONV_H + +#include "editor/io_plugins/editor_scene_import_plugin.h" +#include "scene/3d/skeleton.h" + +#if 0 + +class EditorSceneImporterFBXConv : public EditorSceneImporter { + + GDCLASS(EditorSceneImporterFBXConv,EditorSceneImporter ); + + + struct BoneInfo { + + Skeleton *skeleton; + Transform rest; + int index; + bool has_anim_chan; + bool has_rest; + Dictionary node; + BoneInfo() { + has_rest=false; + skeleton=NULL; + index=-1; + has_anim_chan=false; + } + }; + + struct SurfaceInfo { + Array array; + Mesh::PrimitiveType primitive; + }; + + struct State { + + Node *scene; + Array meshes; + Array materials; + Array nodes; + Array animations; + Map<String,BoneInfo > bones; + Map<String,Skeleton*> skeletons; + Map<String,Ref<Mesh> > mesh_cache; + Map<String,SurfaceInfo> surface_cache; + Map<String,Ref<Material> > material_cache; + Map<String,Ref<Texture> > texture_cache; + List<String> *missing_deps; + String base_path; + bool import_animations; + }; + + String _id(const String& p_id) const; + + Transform _get_transform_mixed(const Dictionary& d, const Dictionary& dbase); + Transform _get_transform(const Dictionary& d); + Color _get_color(const Array& a); + void _detect_bones_in_nodes(State& state,const Array& p_nodes); + void _detect_bones(State& state); + + Error _parse_bones(State& state,const Array &p_bones,Skeleton* p_skeleton); + void _parse_skeletons(const String& p_name,State& state, const Array &p_nodes, Skeleton*p_skeleton=NULL, int p_parent=-1); + + void _add_surface(State& state,Ref<Mesh>& m,const Dictionary &part); + Error _parse_nodes(State& state,const Array &p_nodes,Node* p_base); + Error _parse_animations(State& state); + void _parse_materials(State& state); + void _parse_surfaces(State& state); + Error _parse_json(State& state,const String& p_path); + Error _parse_fbx(State &state, const String &p_path); + +public: + + virtual uint32_t get_import_flags() const; + virtual void get_extensions(List<String> *r_extensions) const; + virtual Node* import_scene(const String& p_path,uint32_t p_flags,List<String> *r_missing_deps=NULL,Error* r_err=NULL); + virtual Ref<Animation> import_animation(const String& p_path,uint32_t p_flags); + + EditorSceneImporterFBXConv(); +}; + +#endif // EDITOR_SCENE_IMPORTER_FBXCONV_H +#endif diff --git a/editor/io_plugins/editor_texture_import_plugin.cpp b/editor/io_plugins/editor_texture_import_plugin.cpp new file mode 100644 index 0000000000..8bafe80673 --- /dev/null +++ b/editor/io_plugins/editor_texture_import_plugin.cpp @@ -0,0 +1,1893 @@ +/*************************************************************************/ +/* editor_texture_import_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_texture_import_plugin.h" +#if 0 +#include "io/image_loader.h" +#include "editor/editor_node.h" +#include "io/resource_saver.h" +#include "editor_atlas.h" +#include "editor/editor_settings.h" +#include "io/md5.h" +#include "io/marshalls.h" +#include "global_config.h" +#include "scene/gui/check_button.h" +#include "scene/gui/button_group.h" +#include "scene/gui/margin_container.h" +#include "scene/io/resource_format_image.h" + +static const char *flag_names[]={ + ("Streaming Format"), + ("Fix Border Alpha"), + ("Alpha Bit Hint"), + ("Compress Extra (PVRTC2)"), + ("No MipMaps"), + ("Repeat"), + ("Filter (Magnifying)"), + ("Premultiply Alpha"), + ("Convert SRGB->Linear"), + ("Convert NormalMap to XY"), + ("Use Anisotropy"), + NULL +}; + +#if 0 // not used +static const char *flag_short_names[]={ + "Stream", + "FixBorder", + "AlphBit", + "ExtComp", + "NoMipMap", + "Repeat", + "Filter", + "PMAlpha", + "ToLinear", + "ToRG", + "Anisoropic", + NULL +}; +#endif + + +void EditorImportTextureOptions::set_format(EditorTextureImportPlugin::ImageFormat p_format) { + + updating=true; + format->select(p_format); + if (p_format==EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY) { + quality_vb->show(); + } else { + quality_vb->hide(); + } + + updating=false; + +} + +EditorTextureImportPlugin::ImageFormat EditorImportTextureOptions::get_format() const{ + + return (EditorTextureImportPlugin::ImageFormat)format->get_selected(); + +} + +void EditorImportTextureOptions::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; + +} + +void EditorImportTextureOptions::set_quality(float p_quality) { + + quality->set_value(p_quality); +} + +float EditorImportTextureOptions::get_quality() const { + + return quality->get_value(); +} + + +uint32_t EditorImportTextureOptions::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 EditorImportTextureOptions::_changedp(int p_value) { + + _changed(); +} + +void EditorImportTextureOptions::_changed() { + + if (updating) + return; + if (format->get_selected()==EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY) { + quality_vb->show(); + } else { + quality_vb->hide(); + } + + emit_signal("changed"); +} + + +void EditorImportTextureOptions::_bind_methods() { + + ClassDB::bind_method("_changed",&EditorImportTextureOptions::_changed); + ClassDB::bind_method("_changedp",&EditorImportTextureOptions::_changedp); + + ADD_SIGNAL(MethodInfo("changed")); +} + + +void EditorImportTextureOptions::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_TREE) { + + flags->connect("item_edited",this,"_changed"); + format->connect("item_selected",this,"_changedp"); + } +} + +void EditorImportTextureOptions::show_2d_notice() { + + //notice_for_2d->show(); +} + +EditorImportTextureOptions::EditorImportTextureOptions() { + + + add_constant_override("separation",3); + updating=false; + format = memnew( OptionButton ); + + format->add_item(TTR("Uncompressed"),EditorTextureImportPlugin::IMAGE_FORMAT_UNCOMPRESSED); + format->add_item(TTR("Compress Lossless (PNG)"),EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS); + format->add_item(TTR("Compress Lossy (WebP)"),EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY); + format->add_item(TTR("Compress (VRAM)"),EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM); + + + add_margin_child(TTR("Texture Format"),format); + + quality_vb = memnew( VBoxContainer ); + + HBoxContainer *quality_hb = memnew(HBoxContainer); + HSlider *hs = memnew( HSlider ); + hs->set_h_size_flags(SIZE_EXPAND_FILL); + hs->set_stretch_ratio(0.8); + quality_hb->add_child(hs); + quality_hb->set_h_size_flags(SIZE_EXPAND_FILL); + SpinBox *sb = memnew( SpinBox ); + sb->set_h_size_flags(SIZE_EXPAND_FILL); + sb->set_stretch_ratio(0.2); + quality_hb->add_child(sb); + sb->share(hs); + hs->set_min(0); + hs->set_max(1.0); + hs->set_step(0.01); + hs->set_value(0.7); + quality=hs; + quality_vb->add_margin_child(TTR("Texture Compression Quality (WebP):"),quality_hb); + + add_child(quality_vb); + + flags = memnew( Tree ); + flags->set_hide_root(true); + TreeItem *root = flags->create_item(); + + + + const char ** fname=flag_names; + + 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); + items.push_back(ti); + fname++; + } + + add_margin_child(TTR("Texture Options"),flags,true); + + +} + +/////////////////////////////////////////////////////////// + + + + +class EditorTextureImportDialog : public ConfirmationDialog { + + GDCLASS(EditorTextureImportDialog,ConfirmationDialog); + + + + HBoxContainer *mode_hb; + CheckBox *mode_check[EditorTextureImportPlugin::MODE_MAX]; + + EditorImportTextureOptions *texture_options; + + EditorTextureImportPlugin::Mode mode; + //EditorNode *editor; + + LineEdit *import_path; + LineEdit *save_path; + EditorFileDialog *file_select; + EditorFileDialog *save_file_select; + EditorDirDialog *save_select; + OptionButton *texture_action; + ConfirmationDialog *error_dialog; + CheckButton *crop_source; + SpinBox *size; + + MarginContainer *size_mc; + Label* size_label; + + Label* source_label; + Label *notice_for_2d; + + EditorTextureImportPlugin *plugin; + + void _mode_changed(int p_mode); + void _choose_files(const Vector<String>& p_path); + void _choose_file(const String& p_path); + void _choose_save_dir(const String& p_path); + void _browse(); + void _browse_target(); + void _import(); + + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + + void setup_multiple_import_3d(const Vector<String>& p_path,const String& p_dest) { + + _mode_changed(EditorTextureImportPlugin::MODE_TEXTURE_3D); + _choose_files(p_path); + _choose_save_dir(p_dest); + } + + void add_sources_and_dest(const Vector<String>& p_path,const String& p_dest) { + + _choose_files(p_path); + _choose_save_dir(p_dest); + } + + Error import(const String& p_from, const String& p_to, const String& p_preset); + void popup_import(const String &p_from=String()); + EditorTextureImportDialog(EditorTextureImportPlugin *p_plugin=NULL); +}; + + +///////////////////////////////////////////////////////// + + + + +void EditorTextureImportDialog::_choose_files(const Vector<String>& p_path) { + + String files; + for(int i=0;i<p_path.size();i++) { + + if (i>0) + files+=","; + files+=p_path[i]; + } + /* + if (p_path.size()) { + String srctex=p_path[0]; + String ipath = EditorImportDB::get_singleton()->find_source_path(srctex); + + if (ipath!="") + save_path->set_text(ipath.get_base_dir()); + }*/ + import_path->set_text(files); + +} + + + +void EditorTextureImportDialog::_choose_file(const String& p_path) { + + + import_path->set_text(p_path); + +} +void EditorTextureImportDialog::_choose_save_dir(const String& p_path) { + + save_path->set_text(p_path); +} + + +void EditorTextureImportDialog::_import() { + + + //ImportMonitorBlock imb; + + Vector<String> files=import_path->get_text().split(","); + + if (!files.size()) { + + error_dialog->set_text(TTR("Please specify some files!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + } + + String dst_path=save_path->get_text(); + + 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 (mode!=EditorTextureImportPlugin::MODE_ATLAS && mode!=EditorTextureImportPlugin::MODE_LARGE && !DirAccess::exists(save_path->get_text())) { + error_dialog->set_text(TTR("Target path must exist.")); + error_dialog->popup_centered_minsize(); + return; + } + + if (mode==EditorTextureImportPlugin::MODE_ATLAS) { //atlas + + if (files.size()==0) { + + error_dialog->set_text(TTR("At least one file needed for Atlas.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + + } + String dst_file = dst_path; + //dst_file=dst_file.basename()+".tex"; + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + //imd->set_editor(); + for(int i=0;i<files.size();i++) { + imd->add_source(EditorImportPlugin::validate_source_path(files[i])); + } + imd->set_option("format",texture_options->get_format()); + imd->set_option("flags",texture_options->get_flags()); + imd->set_option("quality",texture_options->get_quality()); + imd->set_option("atlas",true); + imd->set_option("atlas_size",int(size->get_value())); + imd->set_option("large",false); + imd->set_option("crop",crop_source->is_pressed()); + imd->set_option("mode",mode); + + Error err = plugin->import(dst_file,imd); + if (err) { + + error_dialog->set_text(TTR("Error importing:")+" "+dst_file.get_file()); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + + } + } else if (mode==EditorTextureImportPlugin::MODE_LARGE) { //large + + if (files.size()!=1) { + + error_dialog->set_text(TTR("Only one file is required for large texture.")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + + } + String dst_file = dst_path; + //dst_file=dst_file.basename()+".tex"; + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + //imd->set_editor(); + for(int i=0;i<files.size();i++) { + imd->add_source(EditorImportPlugin::validate_source_path(files[i])); + } + imd->set_option("format",texture_options->get_format()); + imd->set_option("flags",texture_options->get_flags()); + imd->set_option("quality",texture_options->get_quality()); + imd->set_option("atlas",false); + imd->set_option("large",true); + imd->set_option("large_cell_size",int(size->get_value())); + imd->set_option("crop",crop_source->is_pressed()); + imd->set_option("mode",mode); + + Error err = plugin->import(dst_file,imd); + if (err) { + + error_dialog->set_text(TTR("Error importing:")+" "+dst_file.get_file()); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + + } + } else { + + + for(int i=0;i<files.size();i++) { + + String dst_file = dst_path.plus_file(files[i].get_file()); + dst_file=dst_file.get_basename()+".tex"; + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + //imd->set_editor(); + imd->add_source(EditorImportPlugin::validate_source_path(files[i])); + imd->set_option("format",texture_options->get_format()); + imd->set_option("flags",texture_options->get_flags()); + imd->set_option("quality",texture_options->get_quality()); + imd->set_option("atlas",false); + imd->set_option("large",false); + imd->set_option("mode",mode); + + Error err = plugin->import(dst_file,imd); + if (err) { + + error_dialog->set_text(TTR("Error importing:")+" "+dst_file.get_file()); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + + } + } + } + + hide(); +} + +void EditorTextureImportDialog::_browse() { + + file_select->popup_centered_ratio(); +} + +void EditorTextureImportDialog::_browse_target() { + + if (mode==EditorTextureImportPlugin::MODE_ATLAS || mode==EditorTextureImportPlugin::MODE_LARGE) { + save_file_select->popup_centered_ratio(); + } else { + save_select->popup_centered_ratio(); + } + +} + + +void EditorTextureImportDialog::popup_import(const String& p_from) { + + popup_centered(Size2(600,500)*EDSCALE); + if (p_from!="") { + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_from); + ERR_FAIL_COND(!rimd.is_valid()); + + if (rimd->has_option("mode")) { + //new imported stuff uses this option + _mode_changed(rimd->get_option("mode")); + } else { + //this one is for compatibility, will have to guess it + if (rimd->has_option("atlas") && rimd->get_option("atlas")) { + _mode_changed(EditorTextureImportPlugin::MODE_ATLAS); + } else if (rimd->has_option("large") && rimd->get_option("large")) { + _mode_changed(EditorTextureImportPlugin::MODE_LARGE); + } else { + //guess by usage of mipmaps..? + _mode_changed(EditorTextureImportPlugin::MODE_TEXTURE_2D); + } + + } + + if (mode==EditorTextureImportPlugin::MODE_ATLAS || mode==EditorTextureImportPlugin::MODE_LARGE) + save_path->set_text(p_from); + else + save_path->set_text(p_from.get_base_dir()); + + texture_options->set_format(EditorTextureImportPlugin::ImageFormat(int(rimd->get_option("format")))); + texture_options->set_flags(rimd->get_option("flags")); + texture_options->set_quality(rimd->get_option("quality")); + String src = ""; + for(int i=0;i<rimd->get_source_count();i++) { + if (i>0) + src+=","; + src+=EditorImportPlugin::expand_source_path(rimd->get_source_path(i)); + } + import_path->set_text(src); + } +} + + +void EditorTextureImportDialog::_notification(int p_what) { + + + if (p_what==NOTIFICATION_ENTER_TREE) { + + + List<String> extensions; + ImageLoader::get_recognized_extensions(&extensions); + //ResourceLoader::get_recognized_extensions_for_type("PackedTexture",&extensions); + file_select->clear_filters(); + for(int i=0;i<extensions.size();i++) { + + file_select->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); + } + } +} + +Error EditorTextureImportDialog::import(const String& p_from, const String& p_to, const String& p_preset) { + + + import_path->set_text(p_from); + save_path->set_text(p_to); + _import(); + + return OK; +} + +void EditorTextureImportDialog::_mode_changed(int p_mode) { + + mode = EditorTextureImportPlugin::Mode(p_mode); + + for(int i=0;i<EditorTextureImportPlugin::MODE_MAX;i++) { + mode_check[i]->set_pressed(i==mode); + } + + if (p_mode==EditorTextureImportPlugin::MODE_ATLAS) { + + size_label->set_text(TTR("Max Texture Size:")); + size->set_value(2048); + crop_source->show(); + size_label->show(); + size->show(); + + texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA|EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS|EditorTextureImportPlugin::IMAGE_FLAG_FILTER); + texture_options->set_quality(0.7); + texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY); + set_title(TTR("Import Textures for Atlas (2D)")); + + } else { + crop_source->hide(); + } + + + if (p_mode==EditorTextureImportPlugin::MODE_LARGE) { + + size_label->set_text(TTR("Cell Size:")); + size->set_value(256); + size_label->show(); + size->show(); + + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE); + save_file_select->add_filter("*.ltex;"+TTR("Large Texture")); + + texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA|EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS|EditorTextureImportPlugin::IMAGE_FLAG_FILTER); + texture_options->set_quality(0.7); + texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS); + set_title(TTR("Import Large Textures (2D)")); + source_label->set_text(TTR("Source Texture")); + + } else { + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILES); + save_file_select->add_filter("*.tex;"+TTR("Base Atlas Texture")); + source_label->set_text(TTR("Source Texture(s)")); + } + + if (p_mode==EditorTextureImportPlugin::MODE_TEXTURE_2D) { + + size_label->hide(); + size->hide(); + + texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS|EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA|EditorTextureImportPlugin::IMAGE_FLAG_FILTER); + texture_options->set_quality(0.7); + texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY); + notice_for_2d->show(); + set_title(TTR("Import Textures for 2D")); + + } else { + notice_for_2d->hide(); + } + + if (p_mode==EditorTextureImportPlugin::MODE_TEXTURE_3D) { + + size_label->hide(); + size->hide(); + //texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_); + //texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS); + texture_options->set_flags(EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA|EditorTextureImportPlugin::IMAGE_FLAG_FILTER|EditorTextureImportPlugin::IMAGE_FLAG_REPEAT); + texture_options->set_format(EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM); + set_title(TTR("Import Textures for 3D")); + } +} + +void EditorTextureImportDialog::_bind_methods() { + + + ClassDB::bind_method("_choose_files",&EditorTextureImportDialog::_choose_files); + ClassDB::bind_method("_choose_file",&EditorTextureImportDialog::_choose_file); + ClassDB::bind_method("_choose_save_dir",&EditorTextureImportDialog::_choose_save_dir); + ClassDB::bind_method("_import",&EditorTextureImportDialog::_import); + ClassDB::bind_method("_browse",&EditorTextureImportDialog::_browse); + ClassDB::bind_method("_browse_target",&EditorTextureImportDialog::_browse_target); + ClassDB::bind_method("_mode_changed",&EditorTextureImportDialog::_mode_changed); + //ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) ); +} + +EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* p_plugin) { + + + + + + plugin=p_plugin; + set_title(TTR("Import Textures")); + + mode_hb = memnew( HBoxContainer ); + add_child(mode_hb); + //set_child_rect(mode_hb); + + VBoxContainer *vbcg = memnew( VBoxContainer); + + + mode_hb->add_child(vbcg); + mode_hb->add_constant_override("separation",15); + VBoxContainer *bg = memnew( VBoxContainer ); + vbcg->add_margin_child("Import Mode",bg); + + for(int i=0;i<EditorTextureImportPlugin::MODE_MAX;i++) { + String mode_name[EditorTextureImportPlugin::MODE_MAX]={ + TTR("2D Texture"), + TTR("3D Texture"), + TTR("Atlas Texture"), + TTR("Large Texture") + }; + + + mode_check[i]=memnew(CheckBox); + bg->add_child(mode_check[i]); + mode_check[i]->set_text(mode_name[i]); + mode_check[i]->connect("pressed",this,"_mode_changed",varray(i)); + } + + VBoxContainer *vbc = memnew(VBoxContainer); + mode_hb->add_child(vbc); + vbc->set_h_size_flags(SIZE_EXPAND_FILL); + vbc->add_constant_override("separation",4); + + notice_for_2d = memnew( Label ); + notice_for_2d->set_text(TTR("NOTICE: Importing 2D textures is not mandatory. Just copy png/jpg files to the project.")); + //notice_for_2d->set_custom_minimum_size(Size2(0,50)); + notice_for_2d->set_autowrap(true); + notice_for_2d->hide(); + vbcg->add_child(notice_for_2d); + notice_for_2d->set_v_size_flags(SIZE_EXPAND_FILL); + notice_for_2d->set_valign(Label::VALIGN_BOTTOM); + + VBoxContainer *source_vb=memnew(VBoxContainer); + MarginContainer *source_mc = vbc->add_margin_child(TTR("Source Texture(s):"),source_vb); + + source_label = vbc->get_child(source_mc->get_index()-1)->cast_to<Label>(); + + HBoxContainer *hbc = memnew( HBoxContainer ); + source_vb->add_child(hbc); + + import_path = memnew( LineEdit ); + import_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(import_path); + crop_source = memnew( CheckButton ); + crop_source->set_pressed(true); + source_vb->add_child(crop_source); + crop_source->set_text(TTR("Crop empty space.")); + + + Button * import_choose = memnew( Button ); + import_choose->set_text(" .. "); + hbc->add_child(import_choose); + + import_choose->connect("pressed", this,"_browse"); + + hbc = memnew( HBoxContainer ); + vbc->add_margin_child(TTR("Target Path:"),hbc); + + size = memnew( SpinBox ); + size->set_min(128); + size->set_max(16384); + + + size->set_value(256); + size_mc=vbc->add_margin_child(TTR("Cell Size:"),size); + size_label=vbc->get_child(size_mc->get_index()-1)->cast_to<Label>(); + + + save_path = memnew( LineEdit ); + save_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(save_path); + + Button * save_choose = memnew( Button ); + save_choose->set_text(" .. "); + hbc->add_child(save_choose); + + save_choose->connect("pressed", this,"_browse_target"); + + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + add_child(file_select); + + file_select->connect("files_selected", this,"_choose_files"); + file_select->connect("file_selected", this,"_choose_file"); + + save_file_select = memnew(EditorFileDialog); + save_file_select->set_access(EditorFileDialog::ACCESS_RESOURCES); + add_child(save_file_select); + save_file_select->set_mode(EditorFileDialog::MODE_SAVE_FILE); + save_file_select->clear_filters(); + + save_file_select->connect("file_selected", this,"_choose_save_dir"); + + save_select = memnew( EditorDirDialog ); + add_child(save_select); + + //save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR); + save_select->connect("dir_selected", this,"_choose_save_dir"); + + get_ok()->connect("pressed", this,"_import"); + get_ok()->set_text(TTR("Import")); + + //move stuff up + /* + for(int i=0;i<4;i++) + vbc->move_child( vbc->get_child( vbc->get_child_count() -1), 0); + */ + + error_dialog = memnew ( ConfirmationDialog ); + add_child(error_dialog); + error_dialog->get_ok()->set_text(TTR("Accept")); + //error_dialog->get_cancel()->hide(); + + set_hide_on_ok(false); + + texture_options = memnew( EditorImportTextureOptions ); + vbc->add_child(texture_options); + texture_options->set_v_size_flags(SIZE_EXPAND_FILL); + + _mode_changed(EditorTextureImportPlugin::MODE_TEXTURE_3D); + + + //GLOBAL_DEF("import/shared_textures","res://"); + //Globals::get_singleton()->set_custom_property_info("import/shared_textures",PropertyInfo(Variant::STRING,"import/shared_textures",PROPERTY_HINT_DIR)); + + +} + + + +/////////////////////////////////////////////////////////// + + +String EditorTextureImportPlugin::get_name() const { + + return "texture"; +#if 0 //old names, kept for compatibility reference + switch(mode) { + case MODE_TEXTURE_2D: { + + return "texture_2d"; + } break; + case MODE_TEXTURE_3D: { + + return "texture_3d"; + + } break; + case MODE_ATLAS: { + + return "texture_atlas"; + } break; + case MODE_LARGE: { + + return "texture_large"; + } break; + + } + + + return ""; +#endif +} + +String EditorTextureImportPlugin::get_visible_name() const { + + return TTR("Texture"); + +} +void EditorTextureImportPlugin::import_dialog(const String& p_from) { + + dialog->popup_import(p_from); +} + +void EditorTextureImportPlugin::compress_image(EditorExportPlatform::ImageCompression p_mode,Image& image,bool p_smaller) { + + + switch(p_mode) { + case EditorExportPlatform::IMAGE_COMPRESSION_NONE: { + + //do absolutely nothing + + } break; + case EditorExportPlatform::IMAGE_COMPRESSION_BC: { + + + // for maximum compatibility, BC shall always use mipmaps and be PO2 + image.resize_to_po2(); + if (!image.has_mipmaps()) + image.generate_mipmaps(); + + image.compress(Image::COMPRESS_S3TC); + /* + if (has_alpha) { + + if (flags&IMAGE_FLAG_ALPHA_BIT) { + image.convert(Image::FORMAT_DXT5); + } else { + image.convert(Image::FORMAT_DXT3); + } + } else { + + image.convert(Image::FORMAT_DXT1); + }*/ + + + } break; + case EditorExportPlatform::IMAGE_COMPRESSION_PVRTC: + case EditorExportPlatform::IMAGE_COMPRESSION_PVRTC_SQUARE: { + + // for maximum compatibility (hi apple!), PVRT shall always + // use mipmaps, be PO2 and square + + if (!image.has_mipmaps()) + image.generate_mipmaps(); + image.resize_to_po2(true); + + if (p_smaller) { + + image.compress(Image::COMPRESS_PVRTC2); + //image.convert(has_alpha ? Image::FORMAT_PVRTC2A : Image::FORMAT_PVRTC2); + } else { + image.compress(Image::COMPRESS_PVRTC4); + //image.convert(has_alpha ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4); + } + + } break; + case EditorExportPlatform::IMAGE_COMPRESSION_ETC1: { + + image.resize_to_po2(); //square or not? + if (!image.has_mipmaps()) + image.generate_mipmaps(); + if (!image.detect_alpha()) { + //ETC1 is only opaque + image.compress(Image::COMPRESS_ETC); + } + + } break; + case EditorExportPlatform::IMAGE_COMPRESSION_ETC2: { + + + } break; + } + + +} + +Error EditorTextureImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from) { + + + return import2(p_path,p_from,EditorExportPlatform::IMAGE_COMPRESSION_BC,false); +} + + +Error EditorTextureImportPlugin::_process_texture_data(Ref<ImageTexture> &texture,int format, float quality,int flags,EditorExportPlatform::ImageCompression p_compr,int tex_flags,float shrink) { + + + if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS || format==IMAGE_FORMAT_COMPRESS_DISK_LOSSY) { + + Image image=texture->get_data(); + ERR_FAIL_COND_V(image.empty(),ERR_INVALID_DATA); + + bool has_alpha=image.detect_alpha(); + if (!has_alpha && image.get_format()==Image::FORMAT_RGBA8) { + + image.convert(Image::FORMAT_RGB8); + + } + + if (image.get_format()==Image::FORMAT_RGBA8 && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) { + + image.fix_alpha_edges(); + } + + if (image.get_format()==Image::FORMAT_RGBA8 && flags&IMAGE_FLAG_PREMULT_ALPHA) { + + image.premultiply_alpha(); + } + + if (flags&IMAGE_FLAG_CONVERT_NORMAL_TO_XY) { + image.normalmap_to_xy(); + } + + /* + if ((image.get_format()==Image::FORMAT_RGB8 || image.get_format()==Image::FORMAT_RGBA8) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) { + + image.srgb_to_linear(); + } + */ + + if (shrink>1) { + + int orig_w=image.get_width(); + int orig_h=image.get_height(); + image.resize(orig_w/shrink,orig_h/shrink,Image::INTERPOLATE_CUBIC); + texture->create_from_image(image,tex_flags); + texture->set_size_override(Size2(orig_w,orig_h)); + + + } else { + + texture->create_from_image(image,tex_flags); + } + + + if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS) { + texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSLESS); + } else { + texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + } + + + + texture->set_lossy_storage_quality(quality); + + + } else { + + + Image image=texture->get_data(); + ERR_FAIL_COND_V(image.empty(),ERR_INVALID_DATA); + + + bool has_alpha=image.detect_alpha(); + if (!has_alpha && image.get_format()==Image::FORMAT_RGBA8) { + + image.convert(Image::FORMAT_RGB8); + + } + + if (image.get_format()==Image::FORMAT_RGBA8 && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) { + + image.fix_alpha_edges(); + } + + if (image.get_format()==Image::FORMAT_RGBA8 && flags&IMAGE_FLAG_PREMULT_ALPHA) { + + image.premultiply_alpha(); + } + + if (flags&IMAGE_FLAG_CONVERT_NORMAL_TO_XY) { + image.normalmap_to_xy(); + } + + /* + if ((image.get_format()==Image::FORMAT_RGB8 || image.get_format()==Image::FORMAT_RGBA8) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) { + + print_line("CONVERT BECAUSE: "+itos(flags)); + image.srgb_to_linear(); + } + */ + + int orig_w=image.get_width(); + int orig_h=image.get_height(); + + if (shrink>1) { + image.resize(orig_w/shrink,orig_h/shrink,Image::INTERPOLATE_CUBIC); + texture->create_from_image(image,tex_flags); + texture->set_size_override(Size2(orig_w,orig_h)); + } + + if (!(flags&IMAGE_FLAG_NO_MIPMAPS)) { + image.generate_mipmaps(); + + } + + if (format!=IMAGE_FORMAT_UNCOMPRESSED) { + + compress_image(p_compr,image,flags&IMAGE_FLAG_COMPRESS_EXTRA); + } + + + texture->create_from_image(image,tex_flags); + + + if (shrink>1 || (format!=IMAGE_FORMAT_UNCOMPRESSED && (image.get_width()!=orig_w || image.get_height()!=orig_h))) { + texture->set_size_override(Size2(orig_w,orig_h)); + } + + //uint32_t save_flags=ResourceSaver::FLAG_COMPRESS; + } + + return OK; +} + + +Error EditorTextureImportPlugin::import2(const String& p_path, const Ref<ResourceImportMetadata>& p_from,EditorExportPlatform::ImageCompression p_compr, bool p_external){ + + + + ERR_FAIL_COND_V(p_from->get_source_count()==0,ERR_INVALID_PARAMETER); + + Ref<ResourceImportMetadata> from=p_from; + + Ref<ImageTexture> texture; + Vector<Ref<AtlasTexture> > atlases; + bool atlas = from->get_option("atlas"); + bool large = from->get_option("large"); + + int flags=from->get_option("flags"); + int format=from->get_option("format"); + float quality=from->get_option("quality"); + + uint32_t tex_flags=0; + + if (flags&EditorTextureImportPlugin::IMAGE_FLAG_REPEAT) + tex_flags|=Texture::FLAG_REPEAT; + if (flags&EditorTextureImportPlugin::IMAGE_FLAG_FILTER) + tex_flags|=Texture::FLAG_FILTER; + if (!(flags&EditorTextureImportPlugin::IMAGE_FLAG_NO_MIPMAPS)) + tex_flags|=Texture::FLAG_MIPMAPS; + if (flags&EditorTextureImportPlugin::IMAGE_FLAG_CONVERT_TO_LINEAR) + tex_flags|=Texture::FLAG_CONVERT_TO_LINEAR; + if (flags&EditorTextureImportPlugin::IMAGE_FLAG_USE_ANISOTROPY) + tex_flags|=Texture::FLAG_ANISOTROPIC_FILTER; + + print_line("path: "+p_path+" flags: "+itos(tex_flags)); + float shrink=1; + if (from->has_option("shrink")) + shrink=from->get_option("shrink"); + + if (large) { + ERR_FAIL_COND_V(from->get_source_count()!=1,ERR_INVALID_PARAMETER); + + String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0)); + + + int cell_size=from->get_option("large_cell_size"); + ERR_FAIL_COND_V(cell_size<128 || cell_size>16384,ERR_CANT_OPEN); + + EditorProgress pg("ltex",TTR("Import Large Texture"),3); + + pg.step(TTR("Load Source Image"),0); + Image img; + Error err = ImageLoader::load_image(src_path,&img); + if (err) { + return err; + } + + pg.step(TTR("Slicing"),1); + + Map<Vector2,Image> pieces; + for(int i=0;i<img.get_width();i+=cell_size) { + int w = MIN(img.get_width()-i,cell_size); + for(int j=0;j<img.get_height();j+=cell_size) { + int h = MIN(img.get_height()-j,cell_size); + + Image piece(w,h,0,img.get_format()); + piece.blit_rect(img,Rect2(i,j,w,h),Point2(0,0)); + if (!piece.is_invisible()) { + pieces[Vector2(i,j)]=piece; + //print_line("ADDING PIECE AT "+Vector2(i,j)); + } + } + } + + Ref<LargeTexture> existing; + if (ResourceCache::has(p_path)) { + existing = ResourceCache::get(p_path); + } + + if (existing.is_valid()) { + existing->clear(); + } else { + existing = Ref<LargeTexture>(memnew( LargeTexture )); + } + + existing->set_size(Size2(img.get_width(),img.get_height())); + pg.step(TTR("Inserting"),2); + + for (Map<Vector2,Image>::Element *E=pieces.front();E;E=E->next()) { + + Ref<ImageTexture> imgtex = Ref<ImageTexture>( memnew( ImageTexture ) ); + imgtex->create_from_image(E->get(),tex_flags); + _process_texture_data(imgtex,format,quality,flags,p_compr,tex_flags,shrink); + existing->add_piece(E->key(),imgtex); + } + + if (!p_external) { + from->set_editor(get_name()); + from->set_source_md5(0,FileAccess::get_md5(src_path)); + existing->set_path(p_path); + existing->set_import_metadata(from); + } + pg.step(TTR("Saving"),3); + + err = ResourceSaver::save(p_path,existing); + if (err!=OK) { + EditorNode::add_io_error(TTR("Couldn't save large texture:")+" "+p_path); + return err; + } + + return OK; + + + } else if (atlas) { + + //prepare atlas! + Vector< Image > sources; + Vector< Image > tsources; + bool alpha=false; + bool crop = from->get_option("crop"); + + EditorProgress ep("make_atlas",TTR("Build Atlas For:")+" "+p_path.get_file(),from->get_source_count()+3); + + print_line("sources: "+itos(from->get_source_count())); + + for(int i=0;i<from->get_source_count();i++) { + + String path = EditorImportPlugin::expand_source_path(from->get_source_path(i)); + String md5 = FileAccess::get_md5(path); + from->set_source_md5(i,FileAccess::get_md5(path)); + ep.step(TTR("Loading Image:")+" "+path,i); + print_line("source path: "+path+" md5 "+md5); + Image src; + Error err = ImageLoader::load_image(path,&src); + if (err) { + EditorNode::add_io_error(TTR("Couldn't load image:")+" "+path); + return err; + } + + if (src.detect_alpha()) + alpha=true; + + tsources.push_back(src); + } + ep.step(TTR("Converting Images"),sources.size()); + + + Map<uint64_t,int> source_md5; + Map<int,List<int> > source_map; + + for(int i=0;i<tsources.size();i++) { + + Image src = tsources[i]; + + if (alpha) { + src.convert(Image::FORMAT_RGBA8); + } else { + src.convert(Image::FORMAT_RGB8); + } + + PoolVector<uint8_t> data = src.get_data(); + MD5_CTX md5; + PoolVector<uint8_t>::Read r=data.read(); + MD5Init(&md5); + int len=data.size(); + for(int j=0;j<len;j++) { + uint8_t b = r[j]; + b>>=2; //to aid in comparing + MD5Update(&md5,(unsigned char*)&b,1); + } + MD5Final(&md5); + uint64_t *cmp = (uint64_t*)md5.digest; //less bits, but still useful for this + + tsources[i]=Image(); //clear + + if (source_md5.has(*cmp)) { + int sidx=source_md5[*cmp]; + source_map[sidx].push_back(i); + print_line("REUSING "+from->get_source_path(i)); + + } else { + int sidx=sources.size(); + source_md5[*cmp]=sidx; + sources.push_back(src); + List<int> sm; + sm.push_back(i); + source_map[sidx]=sm; + + } + + + } + + //texturepacker is not really good for optimizing, so.. + //will at some point likely replace with my own + //first, will find the nearest to a square packing + int border=1; + + Vector<Size2i> src_sizes; + Vector<Rect2> crops; + + ep.step(TTR("Cropping Images"),sources.size()+1); + + for(int j=0;j<sources.size();j++) { + + Size2i s; + if (crop) { + Rect2 crop = sources[j].get_used_rect(); + print_line("CROP: "+crop); + s=crop.size; + crops.push_back(crop); + } else { + + s=Size2i(sources[j].get_width(),sources[j].get_height()); + } + s+=Size2i(border*2,border*2); + src_sizes.push_back(s); //add a line to constraint width + } + + Vector<Point2i> dst_positions; + Size2i dst_size; + EditorAtlas::fit(src_sizes,dst_positions,dst_size); + + print_line("size that worked: "+itos(dst_size.width)+","+itos(dst_size.height)); + + ep.step(TTR("Blitting Images"),sources.size()+2); + + bool blit_to_po2=tex_flags&Texture::FLAG_MIPMAPS; + int atlas_w=dst_size.width; + int atlas_h=dst_size.height; + if (blit_to_po2) { + atlas_w=nearest_power_of_2(dst_size.width); + atlas_h=nearest_power_of_2(dst_size.height); + } + Image atlas; + atlas.create(atlas_w,atlas_h,0,alpha?Image::FORMAT_RGBA8:Image::FORMAT_RGB8); + + + atlases.resize(from->get_source_count()); + + for(int i=0;i<sources.size();i++) { + + int x=dst_positions[i].x; + int y=dst_positions[i].y; + + Size2 sz = Size2(sources[i].get_width(),sources[i].get_height()); + + Rect2 region; + Rect2 margin; + + if (crop && sz!=crops[i].size) { + Rect2 rect = crops[i]; + rect.size=sz-rect.size; + region=Rect2(x+border,y+border,crops[i].size.width,crops[i].size.height); + margin=rect; + atlas.blit_rect(sources[i],crops[i],Point2(x+border,y+border)); + } else { + region=Rect2(x+border,y+border,sz.x,sz.y); + atlas.blit_rect(sources[i],Rect2(0,0,sources[i].get_width(),sources[i].get_height()),Point2(x+border,y+border)); + } + + ERR_CONTINUE( !source_map.has(i) ); + for (List<int>::Element *E=source_map[i].front();E;E=E->next()) { + + String apath; + String spath = from->get_source_path(E->get()).get_file(); + + if (p_external) { + apath = p_path.get_base_dir().plus_file(spath.get_basename()+"."+from->get_source_path(E->get()).md5_text()+".atex"); + } else { + apath = p_path.get_base_dir().plus_file(spath.get_basename()+".atex"); + } + + Ref<AtlasTexture> at; + + if (ResourceCache::has(apath)) { + + at = Ref<AtlasTexture>( ResourceCache::get(apath)->cast_to<AtlasTexture>() ); + } else { + + at = Ref<AtlasTexture>( memnew( AtlasTexture ) ); + + } + at->set_region(region); + at->set_margin(margin); + at->set_path(apath); + atlases[E->get()]=at; + + } + } + if (ResourceCache::has(p_path)) { + texture = Ref<ImageTexture> ( ResourceCache::get(p_path)->cast_to<ImageTexture>() ); + } else { + texture = Ref<ImageTexture>( memnew( ImageTexture ) ); + } + texture->create_from_image(atlas,tex_flags); + + } else { + ERR_FAIL_COND_V(from->get_source_count()!=1,ERR_INVALID_PARAMETER); + + String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0)); + + if (ResourceCache::has(p_path)) { + Resource *r = ResourceCache::get(p_path); + + texture = Ref<ImageTexture> ( r->cast_to<ImageTexture>() ); + + Image img; + Error err = img.load(src_path); + ERR_FAIL_COND_V(err!=OK,ERR_CANT_OPEN); + texture->create_from_image(img); + } else { + texture=ResourceLoader::load(src_path,"ImageTexture"); + } + + ERR_FAIL_COND_V(texture.is_null(),ERR_CANT_OPEN); + if (!p_external) + from->set_source_md5(0,FileAccess::get_md5(src_path)); + + } + + + + if (!p_external) { + from->set_editor(get_name()); + texture->set_path(p_path); + texture->set_import_metadata(from); + } + + if (atlas) { + + if (p_external) { + //used by exporter + Array rects; + for(int i=0;i<atlases.size();i++) { + rects.push_back(atlases[i]->get_region()); + rects.push_back(atlases[i]->get_margin()); + } + from->set_option("rects",rects); + + } else { + //used by importer + for(int i=0;i<atlases.size();i++) { + String apath = atlases[i]->get_path(); + atlases[i]->set_atlas(texture); + Error err = ResourceSaver::save(apath,atlases[i]); + if (err) { + EditorNode::add_io_error(TTR("Couldn't save atlas image:")+" "+apath); + return err; + } + //from->set_source_md5(i,FileAccess::get_md5(apath)); + } + } + } + + bool compress=false; +#if 1 + + _process_texture_data(texture,format,quality,flags,p_compr,tex_flags,shrink); +#else + if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS || format==IMAGE_FORMAT_COMPRESS_DISK_LOSSY) { + + Image image=texture->get_data(); + ERR_FAIL_COND_V(image.empty(),ERR_INVALID_DATA); + + bool has_alpha=image.detect_alpha(); + if (!has_alpha && image.get_format()==Image::FORMAT_RGBA8) { + + image.convert(Image::FORMAT_RGB8); + + } + + if (image.get_format()==Image::FORMAT_RGBA8 && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) { + + image.fix_alpha_edges(); + } + + if (image.get_format()==Image::FORMAT_RGBA8 && flags&IMAGE_FLAG_PREMULT_ALPHA) { + + image.premultiply_alpha(); + } + + if (flags&IMAGE_FLAG_CONVERT_NORMAL_TO_XY) { + image.normalmap_to_xy(); + } + + /* + if ((image.get_format()==Image::FORMAT_RGB8 || image.get_format()==Image::FORMAT_RGBA8) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) { + + image.srgb_to_linear(); + } + */ + + if (shrink>1) { + + int orig_w=image.get_width(); + int orig_h=image.get_height(); + image.resize(orig_w/shrink,orig_h/shrink); + texture->create_from_image(image,tex_flags); + texture->set_size_override(Size2(orig_w,orig_h)); + + + } else { + + texture->create_from_image(image,tex_flags); + } + + + if (format==IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS) { + texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSLESS); + } else { + texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY); + } + + + + texture->set_lossy_storage_quality(quality); + + + } else { + + + Image image=texture->get_data(); + ERR_FAIL_COND_V(image.empty(),ERR_INVALID_DATA); + + + bool has_alpha=image.detect_alpha(); + if (!has_alpha && image.get_format()==Image::FORMAT_RGBA8) { + + image.convert(Image::FORMAT_RGB8); + + } + + if (image.get_format()==Image::FORMAT_RGBA8 && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) { + + image.fix_alpha_edges(); + } + + if (image.get_format()==Image::FORMAT_RGBA8 && flags&IMAGE_FLAG_PREMULT_ALPHA) { + + image.premultiply_alpha(); + } + + if (flags&IMAGE_FLAG_CONVERT_NORMAL_TO_XY) { + image.normalmap_to_xy(); + } + + /* + if ((image.get_format()==Image::FORMAT_RGB8 || image.get_format()==Image::FORMAT_RGBA8) && flags&IMAGE_FLAG_CONVERT_TO_LINEAR) { + + print_line("CONVERT BECAUSE: "+itos(flags)); + image.srgb_to_linear(); + } + */ + + int orig_w=image.get_width(); + int orig_h=image.get_height(); + + if (shrink>1) { + image.resize(orig_w/shrink,orig_h/shrink); + texture->create_from_image(image,tex_flags); + texture->set_size_override(Size2(orig_w,orig_h)); + } + + if (!(flags&IMAGE_FLAG_NO_MIPMAPS)) { + image.generate_mipmaps(); + + } + + if (format!=IMAGE_FORMAT_UNCOMPRESSED) { + + compress_image(p_compr,image,flags&IMAGE_FLAG_COMPRESS_EXTRA); + } + + + texture->create_from_image(image,tex_flags); + + + if (shrink>1 || (format!=IMAGE_FORMAT_UNCOMPRESSED && (image.get_width()!=orig_w || image.get_height()!=orig_h))) { + texture->set_size_override(Size2(orig_w,orig_h)); + } + + compress=true; + + + } +#endif + uint32_t save_flags=0; + if (compress) + save_flags=ResourceSaver::FLAG_COMPRESS; + + Error err = ResourceSaver::save(p_path,texture,save_flags); + if (err!=OK) { + EditorNode::add_io_error(TTR("Couldn't save converted texture:")+" "+p_path); + return err; + } + + return OK; +} + +Vector<uint8_t> EditorTextureImportPlugin::custom_export(const String& p_path, const Ref<EditorExportPlatform> &p_platform) { + + + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_path); + + if (rimd.is_null()) { + + StringName group = EditorImportExport::get_singleton()->image_get_export_group(p_path); + + if (group!=StringName()) { + //handled by export group + rimd = Ref<ResourceImportMetadata>( memnew( ResourceImportMetadata ) ); + + int group_format=0; + float group_lossy_quality=EditorImportExport::get_singleton()->image_export_group_get_lossy_quality(group); + int group_shrink=EditorImportExport::get_singleton()->image_export_group_get_shrink(group); + group_shrink*=EditorImportExport::get_singleton()->get_export_image_shrink(); + + switch(EditorImportExport::get_singleton()->image_export_group_get_image_action(group)) { + case EditorImportExport::IMAGE_ACTION_NONE: { + + switch(EditorImportExport::get_singleton()->get_export_image_action()) { + case EditorImportExport::IMAGE_ACTION_NONE: { + + group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS; //? + + } break; //use default + case EditorImportExport::IMAGE_ACTION_COMPRESS_DISK: { + group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY; + } break; //use default + case EditorImportExport::IMAGE_ACTION_COMPRESS_RAM: { + group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM; + } break; //use default + } + + group_lossy_quality=EditorImportExport::get_singleton()->get_export_image_quality(); + + } break; //use default + case EditorImportExport::IMAGE_ACTION_COMPRESS_DISK: { + group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY; + } break; //use default + case EditorImportExport::IMAGE_ACTION_COMPRESS_RAM: { + group_format=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM; + } break; //use default + case EditorImportExport::IMAGE_ACTION_KEEP: { + return Vector<uint8_t>(); + } break; //use default + } + + String validated_path=EditorImportPlugin::validate_source_path(p_path); + + int flags=texture_flags_to_export_flags(ResourceFormatLoaderImage::load_image_flags(validated_path)); + flags|=IMAGE_FLAG_FIX_BORDER_ALPHA; + + print_line("group format"+itos(group_format)); + rimd->set_option("format",group_format); + rimd->set_option("flags",flags); + rimd->set_option("quality",group_lossy_quality); + rimd->set_option("atlas",false); + rimd->set_option("shrink",group_shrink); + rimd->add_source(validated_path,FileAccess::get_md5(p_path)); + + } else if (EditorImportExport::get_singleton()->get_image_formats().has(p_path.get_extension().to_lower()) && EditorImportExport::get_singleton()->get_export_image_action()!=EditorImportExport::IMAGE_ACTION_NONE) { + //handled by general image export settings + + rimd = Ref<ResourceImportMetadata>( memnew( ResourceImportMetadata ) ); + + switch(EditorImportExport::get_singleton()->get_export_image_action()) { + case EditorImportExport::IMAGE_ACTION_COMPRESS_DISK: rimd->set_option("format",IMAGE_FORMAT_COMPRESS_DISK_LOSSY); break; + case EditorImportExport::IMAGE_ACTION_COMPRESS_RAM: rimd->set_option("format",IMAGE_FORMAT_COMPRESS_RAM); break; + } + + String validated_path=EditorImportPlugin::validate_source_path(p_path); + + int flags=texture_flags_to_export_flags(ResourceFormatLoaderImage::load_image_flags(validated_path)); + flags|=IMAGE_FLAG_FIX_BORDER_ALPHA; + + rimd->set_option("shrink",EditorImportExport::get_singleton()->get_export_image_shrink()); + rimd->set_option("flags",flags); + rimd->set_option("quality",EditorImportExport::get_singleton()->get_export_image_quality()); + rimd->set_option("atlas",false); + rimd->add_source(validated_path,FileAccess::get_md5(p_path)); + + } else { + return Vector<uint8_t>(); + } + } + + int fmt = rimd->get_option("format"); + + if (fmt!=IMAGE_FORMAT_COMPRESS_RAM && fmt!=IMAGE_FORMAT_COMPRESS_DISK_LOSSY) { + print_line("no compress ram or lossy"); + return Vector<uint8_t>(); //pointless to do anything, since no need to reconvert + } + + uint32_t flags = rimd->get_option("flags"); + uint8_t shrink = rimd->has_option("shrink") ? rimd->get_option("shrink"): Variant(1); + uint8_t format = rimd->get_option("format"); + uint8_t comp = (format==EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM)?uint8_t(p_platform->get_image_compression()):uint8_t(255); + + MD5_CTX ctx; + uint8_t f4[4]; + encode_uint32(flags,&f4[0]); + MD5Init(&ctx); + String gp = GlobalConfig::get_singleton()->globalize_path(p_path); + CharString cs = gp.utf8(); + MD5Update(&ctx,(unsigned char*)cs.get_data(),cs.length()); + MD5Update(&ctx,f4,4); + MD5Update(&ctx,&format,1); + MD5Update(&ctx,&comp,1); + MD5Update(&ctx,&shrink,1); + MD5Final(&ctx); + + + + uint64_t sd=0; + String smd5; + + String md5 = String::md5(ctx.digest); + print_line(p_path+" MD5: "+md5+" FLAGS: "+itos(flags)); + + String tmp_path = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/"); + + bool valid=false; + { + //if existing, make sure it's valid + FileAccessRef f = FileAccess::open(tmp_path+"imgexp-"+md5+".txt",FileAccess::READ); + if (f) { + + uint64_t d = f->get_line().strip_edges().to_int64(); + sd = FileAccess::get_modified_time(p_path); + + if (d==sd) { + valid=true; + } else { + String cmd5 = f->get_line().strip_edges(); + smd5 = FileAccess::get_md5(p_path); + if (cmd5==smd5) { + valid=true; + } + } + + + } + } + + if (!valid) { + //cache failed, convert + Error err = import2(tmp_path+"imgexp-"+md5+".tex",rimd,p_platform->get_image_compression(),true); + ERR_FAIL_COND_V(err!=OK,Vector<uint8_t>()); + FileAccessRef f = FileAccess::open(tmp_path+"imgexp-"+md5+".txt",FileAccess::WRITE); + + if (sd==0) + sd = FileAccess::get_modified_time(p_path); + if (smd5==String()) + smd5 = FileAccess::get_md5(p_path); + + f->store_line(String::num(sd)); + f->store_line(smd5); + f->store_line(gp); //source path for reference + } + + + Vector<uint8_t> ret; + FileAccessRef f = FileAccess::open(tmp_path+"imgexp-"+md5+".tex",FileAccess::READ); + ERR_FAIL_COND_V(!f,ret); + + ret.resize(f->get_len()); + f->get_buffer(ret.ptr(),ret.size()); + + return ret; +} + +uint32_t EditorTextureImportPlugin::texture_flags_to_export_flags(uint32_t p_tex_flags) const { + + uint32_t flags=0; + + if (!(p_tex_flags&Texture::FLAG_MIPMAPS)) { + flags|=IMAGE_FLAG_NO_MIPMAPS; + } + if (p_tex_flags&Texture::FLAG_REPEAT) { + flags|=IMAGE_FLAG_REPEAT; + } + if (p_tex_flags&Texture::FLAG_FILTER) { + flags|=IMAGE_FLAG_FILTER; + } + if (p_tex_flags&Texture::FLAG_ANISOTROPIC_FILTER) { + flags|=IMAGE_FLAG_USE_ANISOTROPY; + } + if (p_tex_flags&Texture::FLAG_CONVERT_TO_LINEAR) { + flags|=IMAGE_FLAG_CONVERT_TO_LINEAR; + } + /* // no correspondence yet + if (p_tex_flags&Texture::TEXTURE_FLAG_MIRRORED_REPEAT) { + flags|=; + }*/ + + return flags; +} + +void EditorTextureImportPlugin::import_from_drop(const Vector<String>& p_drop,const String& p_dest_path) { + + Vector<String> valid; + + List<String> valid_extensions; + ImageLoader::get_recognized_extensions(&valid_extensions); + for(int i=0;i<p_drop.size();i++) { + + String extension=p_drop[i].get_extension().to_lower(); + + for (List<String>::Element *E=valid_extensions.front();E;E=E->next()) { + + if (E->get()==extension) { + valid.push_back(p_drop[i]); + break; + } + } + } + + if (valid.size()) { + dialog->popup_import(); + dialog->setup_multiple_import_3d(valid,p_dest_path); + } +} + +void EditorTextureImportPlugin::reimport_multiple_files(const Vector<String>& p_list) { + + Vector<String> valid; + + + for(int i=0;i<p_list.size();i++) { + + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_list[i]); + String type = rimd->get_editor(); + if (type=="texture" || type.begins_with("texture_")) { + + if ((rimd->has_option("atlas") && rimd->get_option("atlas")) || (rimd->has_option("large") && rimd->get_option("large"))) { + continue; + } + + valid.push_back(p_list[i]); + } + } + + if (valid.size()) { + + dialog->popup_import(valid[0]); + + Vector<String> sources; + for(int i=0;i<valid.size();i++) { + int idx; + EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->find_file(valid[i],&idx); + if (efsd) { + for(int j=0;j<efsd->get_source_count(idx);j++) { + String file = expand_source_path(efsd->get_source_file(idx,j)); + if (sources.find(file)==-1) { + sources.push_back(file); + } + + } + } + } + + if (sources.size()) { + + dialog->add_sources_and_dest(sources,valid[0].get_base_dir()); + } + } +} + +bool EditorTextureImportPlugin::can_reimport_multiple_files() const { + + return true; + +} + + + +EditorTextureImportPlugin *EditorTextureImportPlugin::singleton=NULL; + +EditorTextureImportPlugin::EditorTextureImportPlugin(EditorNode *p_editor) { + + singleton=this; + editor=p_editor; + dialog = memnew( EditorTextureImportDialog(this) ); + editor->get_gui_base()->add_child(dialog); + +} + +//////////////////////////// + + + Vector<uint8_t> EditorTextureExportPlugin::custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform) { + + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_path); + + if (rimd.is_valid()) { + + if (rimd->get_editor()!="") { + int compression = rimd->get_option("format"); + if (compression!=EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM) + return Vector<uint8_t>(); //only useful for RAM compression to reconvert + Ref<EditorImportPlugin> pl = EditorImportExport::get_singleton()->get_import_plugin_by_name(rimd->get_editor()); + if (pl.is_valid()) { + Vector<uint8_t> ce = pl->custom_export(p_path,p_platform); + if (ce.size()) + return ce; + } + } + } else if (EditorImportExport::get_singleton()->image_get_export_group(p_path)) { + + + Ref<EditorImportPlugin> pl = EditorImportExport::get_singleton()->get_import_plugin_by_name("texture"); + if (pl.is_valid()) { + Vector<uint8_t> ce = pl->custom_export(p_path,p_platform); + if (ce.size()) { + p_path=p_path.get_basename()+".converted.tex"; + return ce; + } + } + + } else if (EditorImportExport::get_singleton()->get_export_image_action()!=EditorImportExport::IMAGE_ACTION_NONE){ + + String xt = p_path.get_extension().to_lower(); + if (EditorImportExport::get_singleton()->get_image_formats().has(xt)) { //should check for more I guess? + + Ref<EditorImportPlugin> pl = EditorImportExport::get_singleton()->get_import_plugin_by_name("texture"); + if (pl.is_valid()) { + Vector<uint8_t> ce = pl->custom_export(p_path,p_platform); + if (ce.size()) { + p_path=p_path.get_basename()+".converted.tex"; + return ce; + } + } + } + } + + return Vector<uint8_t>(); +} + +EditorTextureExportPlugin::EditorTextureExportPlugin() { + + +} +#endif diff --git a/editor/io_plugins/editor_texture_import_plugin.h b/editor/io_plugins/editor_texture_import_plugin.h new file mode 100644 index 0000000000..f63bc57ecd --- /dev/null +++ b/editor/io_plugins/editor_texture_import_plugin.h @@ -0,0 +1,179 @@ +/*************************************************************************/ +/* editor_texture_import_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_TEXTURE_IMPORT_PLUGIN_H +#define EDITOR_TEXTURE_IMPORT_PLUGIN_H + + + + + + +#if 0 +#include "editor/editor_import_export.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/tree.h" +#include "scene/gui/label.h" +#include "scene/gui/option_button.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/file_dialog.h" +#include "scene/gui/progress_bar.h" +#include "scene/gui/slider.h" +#include "scene/gui/spin_box.h" +#include "editor/editor_file_system.h" +#include "editor/editor_dir_dialog.h" + + + +class EditorNode; +class EditorTextureImportDialog; + +class EditorTextureImportPlugin : public EditorImportPlugin { + + GDCLASS(EditorTextureImportPlugin,EditorImportPlugin); +public: + + + enum Mode { + MODE_TEXTURE_2D, + MODE_TEXTURE_3D, + MODE_ATLAS, + MODE_LARGE, + MODE_MAX + }; + + + +private: + + EditorNode *editor; + EditorTextureImportDialog *dialog; + static EditorTextureImportPlugin *singleton; + //used by other importers such as mesh + + Error _process_texture_data(Ref<ImageTexture> &texture, int format, float quality, int flags,EditorExportPlatform::ImageCompression p_compr,int tex_flags,float shrink); + void compress_image(EditorExportPlatform::ImageCompression p_mode,Image& image,bool p_smaller); + + uint32_t texture_flags_to_export_flags(uint32_t p_tex_flags) const; +public: + + + static EditorTextureImportPlugin *get_singleton() { return singleton; } + + enum ImageFormat { + + IMAGE_FORMAT_UNCOMPRESSED, + IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS, + IMAGE_FORMAT_COMPRESS_DISK_LOSSY, + IMAGE_FORMAT_COMPRESS_RAM, + }; + + enum ImageFlags { + + IMAGE_FLAG_STREAM_FORMAT=1, + IMAGE_FLAG_FIX_BORDER_ALPHA=2, + IMAGE_FLAG_ALPHA_BIT=4, //hint for compressions that use a bit for alpha + IMAGE_FLAG_COMPRESS_EXTRA=8, // used for pvrtc2 + IMAGE_FLAG_NO_MIPMAPS=16, //normal for 2D games + IMAGE_FLAG_REPEAT=32, //usually disabled in 2D + IMAGE_FLAG_FILTER=64, //almost always enabled + IMAGE_FLAG_PREMULT_ALPHA=128,//almost always enabled + IMAGE_FLAG_CONVERT_TO_LINEAR=256, //convert image to linear + IMAGE_FLAG_CONVERT_NORMAL_TO_XY=512, //convert image to linear + IMAGE_FLAG_USE_ANISOTROPY=1024, //convert image to linear + }; + + virtual String get_name() const; + virtual String get_visible_name() const; + virtual void import_dialog(const String& p_from=""); + virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from); + virtual Error import2(const String& p_path, const Ref<ResourceImportMetadata>& p_from,EditorExportPlatform::ImageCompression p_compr, bool p_external=false); + virtual Vector<uint8_t> custom_export(const String& p_path,const Ref<EditorExportPlatform> &p_platform); + + virtual void import_from_drop(const Vector<String>& p_drop,const String& p_dest_path); + virtual void reimport_multiple_files(const Vector<String>& p_list); + virtual bool can_reimport_multiple_files() const; + + EditorTextureImportPlugin(EditorNode* p_editor=NULL); +}; + + +class EditorTextureExportPlugin : public EditorExportPlugin { + + GDCLASS( EditorTextureExportPlugin, EditorExportPlugin); + + +public: + + virtual Vector<uint8_t> custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform); + EditorTextureExportPlugin(); +}; + +class EditorImportTextureOptions : public VBoxContainer { + + GDCLASS( EditorImportTextureOptions, VBoxContainer ); + + + OptionButton *format; + VBoxContainer *quality_vb; + HSlider *quality; + Tree *flags; + Vector<TreeItem*> items; + + + bool updating; + + void _changedp(int p_value); + void _changed(); + + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + + + + void set_format(EditorTextureImportPlugin::ImageFormat p_format); + EditorTextureImportPlugin::ImageFormat get_format() const; + + void set_flags(uint32_t p_flags); + uint32_t get_flags() const; + + void set_quality(float p_quality); + float get_quality() const; + + void show_2d_notice(); + + EditorImportTextureOptions(); + + +}; +#endif // EDITOR_TEXTURE_IMPORT_PLUGIN_H +#endif diff --git a/editor/io_plugins/editor_translation_import_plugin.cpp b/editor/io_plugins/editor_translation_import_plugin.cpp new file mode 100644 index 0000000000..0fd298d6d3 --- /dev/null +++ b/editor/io_plugins/editor_translation_import_plugin.cpp @@ -0,0 +1,479 @@ +/*************************************************************************/ +/* editor_translation_import_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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 "editor_translation_import_plugin.h" + +#if 0 +#include "scene/gui/file_dialog.h" +#include "editor/editor_dir_dialog.h" +#include "editor/editor_node.h" +#include "editor/property_editor.h" +//#include "scene/resources/sample.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "translation.h" +#include "compressed_translation.h" +#include "editor/project_settings.h" + + +class EditorTranslationImportDialog : public ConfirmationDialog { + + GDCLASS(EditorTranslationImportDialog,ConfirmationDialog); + + EditorTranslationImportPlugin *plugin; + + LineEdit *import_path; + LineEdit *save_path; + EditorFileDialog *file_select; + CheckButton *ignore_first; + CheckButton *compress; + CheckButton *add_to_project; + EditorDirDialog *save_select; + ConfirmationDialog *error_dialog; + Vector<TreeItem*> items; + Tree *columns; + +public: + + void _choose_file(const String& p_path) { + + import_path->set_text(p_path); + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + + error_dialog->set_text(TTR("Invalid source!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + + } + + Vector<String> csvh = f->get_csv_line(); + memdelete(f); + + if (csvh.size()<2) { + + error_dialog->set_text(TTR("Invalid translation source!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + return; + + } + + columns->clear(); + columns->set_columns(2); + TreeItem *root = columns->create_item(); + columns->set_hide_root(true); + columns->set_column_titles_visible(true); + columns->set_column_title(0,TTR("Column")); + columns->set_column_title(1,TTR("Language")); + Vector<String> langs = TranslationServer::get_all_locales(); + Vector<String> names = TranslationServer::get_all_locale_names(); + if (csvh[0]=="") + ignore_first->set_pressed(true); + + + items.clear(); + + for(int i=1;i<csvh.size();i++) { + + TreeItem *ti = columns->create_item(root); + + ti->set_editable(0,true); + ti->set_selectable(0,false); + ti->set_cell_mode(0,TreeItem::CELL_MODE_CHECK); + ti->set_checked(0,true); + ti->set_text(0,itos(i)); + items.push_back(ti); + + String lname = csvh[i].to_lower().strip_edges(); + int idx=-1; + String hint; + for(int j=0;j<langs.size();j++) { + + if (langs[j]==lname.substr(0,langs[j].length()).to_lower()) { + idx=j; + } + if (j>0) { + hint+=","; + } + hint+=names[j].replace(","," "); + } + + ti->set_cell_mode(1,TreeItem::CELL_MODE_RANGE); + ti->set_text(1,hint); + ti->set_editable(1,true); + + + if (idx!=-1) { + ignore_first->set_pressed(true); + ti->set_range(1,idx); + } else { + + //not found, maybe used stupid name + if (lname.begins_with("br")) //brazilian + ti->set_range(1,langs.find("pt")); + else if (lname.begins_with("ch")) //chinese + ti->set_range(1,langs.find("zh")); + else if (lname.begins_with("sp")) //spanish + ti->set_range(1,langs.find("es")); + else if (lname.begins_with("kr"))// kprean + ti->set_range(1,langs.find("ko")); + else if (i==0) + ti->set_range(1,langs.find("en")); + else + ti->set_range(1,langs.find("es")); + } + + ti->set_metadata(1,names[ti->get_range(1)]); + } + + + + } + void _choose_save_dir(const String& p_path) { + + save_path->set_text(p_path); + } + + void _browse() { + + file_select->popup_centered_ratio(); + } + + void _browse_target() { + + save_select->popup_centered_ratio(); + + } + + + void popup_import(const String& p_from) { + + popup_centered(Size2(400,400)*EDSCALE); + + if (p_from!="") { + + Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_from); + ERR_FAIL_COND(!rimd.is_valid()); + ERR_FAIL_COND(rimd->get_source_count()!=1); + _choose_file(EditorImportPlugin::expand_source_path(rimd->get_source_path(0))); + _choose_save_dir(p_from.get_base_dir()); + String locale = rimd->get_option("locale"); + bool skip_first=rimd->get_option("skip_first"); + bool compressed = rimd->get_option("compress"); + + int idx=-1; + + for(int i=0;i<items.size();i++) { + + String il = TranslationServer::get_all_locales()[items[i]->get_range(1)]; + if (il==locale) { + idx=i; + break; + } + } + + if (idx!=-1) { + idx=rimd->get_option("index"); + } + + for(int i=0;i<items.size();i++) { + + if (i==idx) { + + Vector<String> locs = TranslationServer::get_all_locales(); + for(int j=0;j<locs.size();j++) { + if (locs[j]==locale) { + items[i]->set_range(1,j); + } + + } + items[i]->set_checked(0,true); + } else { + items[i]->set_checked(0,false); + + } + } + + ignore_first->set_pressed(skip_first); + compress->set_pressed(compressed); + + + + } + + } + + + void _import() { + + + if (items.size()==0) { + error_dialog->set_text(TTR("No items to import!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + } + + if (!save_path->get_text().begins_with("res://")) { + error_dialog->set_text(TTR("No target path!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + } + + EditorProgress progress("import_xl",TTR("Import Translations"),items.size()); + for(int i=0;i<items.size();i++) { + + progress.step(items[i]->get_metadata(1),i); + if (!items[i]->is_checked(0)) + continue; + + String locale = TranslationServer::get_all_locales()[items[i]->get_range(1)]; + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + imd->add_source(EditorImportPlugin::validate_source_path(import_path->get_text())); + imd->set_option("locale",locale); + imd->set_option("index",i); + imd->set_option("skip_first",ignore_first->is_pressed()); + imd->set_option("compress",compress->is_pressed()); + + String savefile = save_path->get_text().plus_file(import_path->get_text().get_file().get_basename()+"."+locale+".xl"); + Error err = plugin->import(savefile,imd); + if (err!=OK) { + error_dialog->set_text(TTR("Couldn't import!")); + error_dialog->popup_centered(Size2(200,100)*EDSCALE); + } else if (add_to_project->is_pressed()) { + + ProjectSettings::get_singleton()->add_translation(savefile); + } + } + hide(); + + } + + + void _notification(int p_what) { + + + if (p_what==NOTIFICATION_ENTER_TREE) { + + + } + } + + static void _bind_methods() { + + + ClassDB::bind_method("_choose_file",&EditorTranslationImportDialog::_choose_file); + ClassDB::bind_method("_choose_save_dir",&EditorTranslationImportDialog::_choose_save_dir); + ClassDB::bind_method("_import",&EditorTranslationImportDialog::_import); + ClassDB::bind_method("_browse",&EditorTranslationImportDialog::_browse); + ClassDB::bind_method("_browse_target",&EditorTranslationImportDialog::_browse_target); + //ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) ); + } + + EditorTranslationImportDialog(EditorTranslationImportPlugin *p_plugin) { + + plugin=p_plugin; + + + set_title(TTR("Import Translation")); + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + //set_child_rect(vbc); + + + + VBoxContainer *csvb = memnew( VBoxContainer ); + + HBoxContainer *hbc = memnew( HBoxContainer ); + csvb->add_child(hbc); + vbc->add_margin_child(TTR("Source CSV:"),csvb); + + import_path = memnew( LineEdit ); + import_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(import_path); + ignore_first = memnew( CheckButton ); + ignore_first->set_text(TTR("Ignore First Row")); + csvb->add_child(ignore_first); + + Button * import_choose = memnew( Button ); + import_choose->set_text(" .. "); + hbc->add_child(import_choose); + + import_choose->connect("pressed", this,"_browse"); + + VBoxContainer *tcomp = memnew( VBoxContainer); + hbc = memnew( HBoxContainer ); + tcomp->add_child(hbc); + vbc->add_margin_child(TTR("Target Path:"),tcomp); + + save_path = memnew( LineEdit ); + save_path->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(save_path); + + Button * save_choose = memnew( Button ); + save_choose->set_text(" .. "); + hbc->add_child(save_choose); + + save_choose->connect("pressed", this,"_browse_target"); + + compress = memnew( CheckButton); + compress->set_pressed(true); + compress->set_text(TTR("Compress")); + tcomp->add_child(compress); + + add_to_project = memnew( CheckButton); + add_to_project->set_pressed(true); + add_to_project->set_text(TTR("Add to Project (godot.cfg)")); + tcomp->add_child(add_to_project); + + file_select = memnew(EditorFileDialog); + file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + add_child(file_select); + file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_select->connect("file_selected", this,"_choose_file"); + file_select->add_filter("*.csv ; Translation CSV"); + save_select = memnew( EditorDirDialog ); + add_child(save_select); + + //save_select->set_mode(EditorFileDialog::MODE_OPEN_DIR); + save_select->connect("dir_selected", this,"_choose_save_dir"); + + get_ok()->connect("pressed", this,"_import"); + get_ok()->set_text(TTR("Import")); + + + error_dialog = memnew ( ConfirmationDialog ); + add_child(error_dialog); + error_dialog->get_ok()->set_text(TTR("Accept")); + //error_dialog->get_cancel()->hide(); + + set_hide_on_ok(false); + + columns = memnew( Tree ); + vbc->add_margin_child(TTR("Import Languages:"),columns,true); + } + + ~EditorTranslationImportDialog() { + + } + +}; + + +String EditorTranslationImportPlugin::get_name() const { + + return "translation"; +} +String EditorTranslationImportPlugin::get_visible_name() const { + + return TTR("Translation"); +} +void EditorTranslationImportPlugin::import_dialog(const String& p_from) { + + dialog->popup_import(p_from); +} + + + +void EditorTranslationImportPlugin::import_from_drop(const Vector<String>& p_drop, const String &p_dest_path) { + + + for(int i=0;i<p_drop.size();i++) { + String ext = p_drop[i].get_extension().to_lower(); + + if (ext=="csv") { + + import_dialog(); + dialog->_choose_file(p_drop[i]); + dialog->_choose_save_dir(p_dest_path); + break; + } + } + + +} + +Error EditorTranslationImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from) { + + Ref<ResourceImportMetadata> from = p_from; + ERR_FAIL_COND_V( from->get_source_count()!=1, ERR_INVALID_PARAMETER); + + String source = EditorImportPlugin::expand_source_path( from->get_source_path(0) ); + + FileAccessRef f = FileAccess::open(source,FileAccess::READ); + + ERR_FAIL_COND_V( !f, ERR_INVALID_PARAMETER ); + + bool skip_first = from->get_option("skip_first"); + int index = from->get_option("index"); + index+=1; + String locale = from->get_option("locale"); + + Ref<Translation> translation = memnew( Translation ); + + translation->set_locale( locale ); + + Vector<String> line = f->get_csv_line(); + + while(line.size()>1) { + + if (!skip_first) { + ERR_FAIL_INDEX_V(index,line.size(),ERR_INVALID_DATA ); + translation->add_message(line[0].strip_edges(),line[index]); + + } else { + + skip_first=false; + } + + line = f->get_csv_line(); + } + + from->set_source_md5(0,FileAccess::get_md5(source)); + from->set_editor(get_name()); + + String dst_path = p_path; + + if (from->get_option("compress")) { + + Ref<PHashTranslation> cxl = memnew( PHashTranslation ); + cxl->generate( translation ); + translation=cxl; + } + + translation->set_import_metadata(from); + return ResourceSaver::save(dst_path,translation); + +} + + +EditorTranslationImportPlugin::EditorTranslationImportPlugin(EditorNode* p_editor) { + + dialog = memnew(EditorTranslationImportDialog(this)); + p_editor->get_gui_base()->add_child(dialog); +} + +#endif diff --git a/editor/io_plugins/editor_translation_import_plugin.h b/editor/io_plugins/editor_translation_import_plugin.h new file mode 100644 index 0000000000..030c5bbf6f --- /dev/null +++ b/editor/io_plugins/editor_translation_import_plugin.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* editor_translation_import_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 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. */ +/*************************************************************************/ +#ifndef EDITOR_TRANSLATION_IMPORT_PLUGIN_H +#define EDITOR_TRANSLATION_IMPORT_PLUGIN_H + +#include "editor/editor_export.h" +#include "scene/resources/font.h" +#if 0 +class EditorNode; +class EditorTranslationImportDialog; + +class EditorTranslationImportPlugin : public EditorImportPlugin { + + GDCLASS(EditorTranslationImportPlugin,EditorImportPlugin); + + EditorTranslationImportDialog *dialog; +public: + + virtual String get_name() const; + virtual String get_visible_name() const; + virtual void import_dialog(const String& p_from=""); + virtual Error import(const String& p_path, const Ref<ResourceImportMetadata>& p_from); + void import_from_drop(const Vector<String>& p_drop, const String &p_dest_path); + + + EditorTranslationImportPlugin(EditorNode* p_editor); +}; + +#endif +#endif // EDITOR_TRANSLATION_IMPORT_PLUGIN_H |