summaryrefslogtreecommitdiff
path: root/editor/io_plugins
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2017-03-05 14:21:25 +0100
committerRémi Verschelde <rverschelde@gmail.com>2017-03-05 14:21:25 +0100
commit49c065d29ca07040c3fd810026121164ad86b247 (patch)
tree285176e0c80a41c22c3e8f171024472cfdc7d765 /editor/io_plugins
parent532f6d4b431f940432e82b7fc7826652b7a4520d (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')
-rw-r--r--editor/io_plugins/SCsub5
-rw-r--r--editor/io_plugins/editor_atlas.cpp160
-rw-r--r--editor/io_plugins/editor_atlas.h43
-rw-r--r--editor/io_plugins/editor_bitmask_import_plugin.cpp387
-rw-r--r--editor/io_plugins/editor_bitmask_import_plugin.h70
-rw-r--r--editor/io_plugins/editor_export_scene.cpp142
-rw-r--r--editor/io_plugins/editor_export_scene.h44
-rw-r--r--editor/io_plugins/editor_font_import_plugin.cpp1704
-rw-r--r--editor/io_plugins/editor_font_import_plugin.h58
-rw-r--r--editor/io_plugins/editor_mesh_import_plugin.cpp593
-rw-r--r--editor/io_plugins/editor_mesh_import_plugin.h59
-rw-r--r--editor/io_plugins/editor_sample_import_plugin.cpp929
-rw-r--r--editor/io_plugins/editor_sample_import_plugin.h74
-rw-r--r--editor/io_plugins/editor_scene_import_plugin.cpp2992
-rw-r--r--editor/io_plugins/editor_scene_import_plugin.h200
-rw-r--r--editor/io_plugins/editor_scene_importer_fbxconv.cpp1136
-rw-r--r--editor/io_plugins/editor_scene_importer_fbxconv.h111
-rw-r--r--editor/io_plugins/editor_texture_import_plugin.cpp1893
-rw-r--r--editor/io_plugins/editor_texture_import_plugin.h179
-rw-r--r--editor/io_plugins/editor_translation_import_plugin.cpp479
-rw-r--r--editor/io_plugins/editor_translation_import_plugin.h56
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