aboutsummaryrefslogtreecommitdiff
path: root/tools/editor/io_plugins
diff options
context:
space:
mode:
authorJuan Linietsky2014-02-09 22:10:30 -0300
committerJuan Linietsky2014-02-09 22:10:30 -0300
commit0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch)
tree276c4d099e178eb67fbd14f61d77b05e3808e9e3 /tools/editor/io_plugins
parent0e49da1687bc8192ed210947da52c9e5c5f301bb (diff)
downloadgodot-0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac.tar.gz
godot-0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac.tar.zst
godot-0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac.zip
GODOT IS OPEN SOURCE
Diffstat (limited to 'tools/editor/io_plugins')
-rw-r--r--tools/editor/io_plugins/SCsub7
-rw-r--r--tools/editor/io_plugins/editor_atlas.cpp159
-rw-r--r--tools/editor/io_plugins/editor_atlas.h43
-rw-r--r--tools/editor/io_plugins/editor_font_import_plugin.cpp1298
-rw-r--r--tools/editor/io_plugins/editor_font_import_plugin.h56
-rw-r--r--tools/editor/io_plugins/editor_import_collada.cpp2200
-rw-r--r--tools/editor/io_plugins/editor_import_collada.h51
-rw-r--r--tools/editor/io_plugins/editor_sample_import_plugin.cpp630
-rw-r--r--tools/editor/io_plugins/editor_sample_import_plugin.h54
-rw-r--r--tools/editor/io_plugins/editor_scene_import_plugin.cpp1599
-rw-r--r--tools/editor/io_plugins/editor_scene_import_plugin.h167
-rw-r--r--tools/editor/io_plugins/editor_texture_import_plugin.cpp1177
-rw-r--r--tools/editor/io_plugins/editor_texture_import_plugin.h147
-rw-r--r--tools/editor/io_plugins/editor_translation_import_plugin.cpp457
-rw-r--r--tools/editor/io_plugins/editor_translation_import_plugin.h54
15 files changed, 8099 insertions, 0 deletions
diff --git a/tools/editor/io_plugins/SCsub b/tools/editor/io_plugins/SCsub
new file mode 100644
index 000000000..b525fb3f7
--- /dev/null
+++ b/tools/editor/io_plugins/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+Export('env')
+env.add_source_files(env.tool_sources,"*.cpp")
+
+
+
+
diff --git a/tools/editor/io_plugins/editor_atlas.cpp b/tools/editor/io_plugins/editor_atlas.cpp
new file mode 100644
index 000000000..4c716874b
--- /dev/null
+++ b/tools/editor/io_plugins/editor_atlas.cpp
@@ -0,0 +1,159 @@
+/*************************************************************************/
+/* editor_atlas.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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"
+
+
+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;
+ int limit_h=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;
+ if (ofs==0)
+ limit_h=end_h;
+
+ 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;
+
+ if (ofs==0 || end_h>limit_h ) //while h limit not reched, keep stacking
+ 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 = nearest_power_of_2(results[i].max_h);
+ float w = nearest_power_of_2(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/tools/editor/io_plugins/editor_atlas.h b/tools/editor/io_plugins/editor_atlas.h
new file mode 100644
index 000000000..685cf60c9
--- /dev/null
+++ b/tools/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-2014 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/tools/editor/io_plugins/editor_font_import_plugin.cpp b/tools/editor/io_plugins/editor_font_import_plugin.cpp
new file mode 100644
index 000000000..d64a2fd42
--- /dev/null
+++ b/tools/editor/io_plugins/editor_font_import_plugin.cpp
@@ -0,0 +1,1298 @@
+/*************************************************************************/
+/* editor_font_import_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/file_dialog.h"
+#include "tools/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 {
+
+ OBJ_TYPE(_EditorFontImportOptions,Object);
+public:
+
+ 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
+ };
+
+ 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;
+ String gradient_image;
+
+
+ bool round_advance;
+
+
+
+ bool _set(const StringName& p_name, const Variant& p_value) {
+
+ String n = p_name;
+ 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=="advanced/round_advance")
+ round_advance=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=="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=="advanced/round_advance")
+ r_ret=round_advance;
+ else
+ return false;
+
+ return true;
+
+ }
+
+ void _get_property_list( List<PropertyInfo> *p_list) const{
+
+ 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_FILE));
+
+ p_list->push_back(PropertyInfo(Variant::BOOL,"shadow/enabled"));
+ if (shadow) {
+ p_list->push_back(PropertyInfo(Variant::INT,"shadow/radius",PROPERTY_HINT_RANGE,"-64,64,1"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow/offset"));
+ p_list->push_back(PropertyInfo(Variant::COLOR,"shadow/color"));
+ p_list->push_back(PropertyInfo(Variant::REAL,"shadow/transition",PROPERTY_HINT_EXP_EASING));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL,"shadow2/enabled"));
+ if (shadow2) {
+ p_list->push_back(PropertyInfo(Variant::INT,"shadow2/radius",PROPERTY_HINT_RANGE,"-64,64,1"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2,"shadow2/offset"));
+ p_list->push_back(PropertyInfo(Variant::COLOR,"shadow2/color"));
+ p_list->push_back(PropertyInfo(Variant::REAL,"shadow2/transition",PROPERTY_HINT_EXP_EASING));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::INT,"color/mode",PROPERTY_HINT_ENUM,"White,Color,Gradient,Gradient Image"));
+ if (color_type==COLOR_CUSTOM) {
+ p_list->push_back(PropertyInfo(Variant::COLOR,"color/color"));
+
+ }
+ if (color_type==COLOR_GRADIENT_RANGE) {
+ p_list->push_back(PropertyInfo(Variant::COLOR,"color/begin"));
+ p_list->push_back(PropertyInfo(Variant::COLOR,"color/end"));
+ }
+ if (color_type==COLOR_GRADIENT_IMAGE) {
+ p_list->push_back(PropertyInfo(Variant::STRING,"color/image",PROPERTY_HINT_FILE));
+ }
+ p_list->push_back(PropertyInfo(Variant::BOOL,"advanced/round_advance"));
+
+ }
+
+
+ static void _bind_methods() {
+
+
+ ADD_SIGNAL( MethodInfo("changed"));
+ }
+
+
+ _EditorFontImportOptions() {
+
+ 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);
+
+ round_advance=true;
+ }
+
+
+};
+
+
+class EditorFontImportDialog : public ConfirmationDialog {
+
+ OBJ_TYPE(EditorFontImportDialog, ConfirmationDialog);
+
+
+ LineEditFileChooser *source;
+ LineEditFileChooser *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);
+ }
+
+ imd->add_source(EditorImportPlugin::validate_source_path(source->get_line_edit()->get_text()));
+ imd->set_option("font/size",font_size->get_val());
+
+ 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_color());
+ }
+
+ void _update() {
+
+ Ref<ResourceImportMetadata> imd = get_rimd();
+ Ref<Font> 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<Font> 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.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];
+ Font::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<Font::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())+";");
+ f->store_line("static const unsigned char _builtin_font_img_data["+itos(img.get_width()*img.get_height()*2)+"]={");
+ for(int i=0;i<img.get_height();i++) {
+
+ for(int j=0;j<img.get_width();j++) {
+
+ Color c = img.get_pixel(j,i);
+ int v = CLAMP(((c.r+c.g+c.b)/3.0)*255,0,255);
+ int a = CLAMP(c.a*255,0,255);
+
+ f->store_line(itos(v)+","+itos(a)+",");
+ }
+ }
+ f->store_line("};");
+
+ }
+
+ void _import() {
+
+ if (source->get_line_edit()->get_text()=="") {
+ error_dialog->set_text("No source font file!");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ if (dest->get_line_edit()->get_text()=="") {
+ error_dialog->set_text("No tatget font resource!");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ Ref<ResourceImportMetadata> rimd = get_rimd();
+
+ if (rimd.is_null()) {
+ error_dialog->set_text("Can't load/process source font");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ Error err = plugin->import(dest->get_line_edit()->get_text(),rimd);
+
+ if (err!=OK) {
+ error_dialog->set_text("Could't save font.");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ //_import_inc(dest->get_line_edit()->get_text());
+
+ hide();
+ }
+
+ EditorFontImportPlugin *plugin;
+ _EditorFontImportOptions *options;
+
+ static void _bind_methods() {
+
+ ObjectTypeDB::bind_method("_update",&EditorFontImportDialog::_update);
+ ObjectTypeDB::bind_method("_update_text",&EditorFontImportDialog::_update_text);
+ ObjectTypeDB::bind_method("_update_text2",&EditorFontImportDialog::_update_text2);
+ ObjectTypeDB::bind_method("_update_text3",&EditorFontImportDialog::_update_text3);
+ ObjectTypeDB::bind_method("_prop_changed",&EditorFontImportDialog::_prop_changed);
+ ObjectTypeDB::bind_method("_src_changed",&EditorFontImportDialog::_src_changed);
+ ObjectTypeDB::bind_method("_font_size_changed",&EditorFontImportDialog::_font_size_changed);
+ ObjectTypeDB::bind_method("_import",&EditorFontImportDialog::_import);
+
+ }
+
+public:
+
+ void _notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+ prop_edit->edit(options);
+ _update_text();
+ }
+ }
+
+ void popup_import(const String& p_path) {
+
+ popup_centered(Size2(600,500));
+
+ 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);
+ 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_val(rimd->get_option("font/size"));
+ }
+ }
+
+ 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( LineEditFileChooser );
+ source->get_file_dialog()->set_access(FileDialog::ACCESS_FILESYSTEM);
+ source->get_file_dialog()->set_mode(FileDialog::MODE_OPEN_FILE);
+ source->get_file_dialog()->add_filter("*.ttf;TrueType");
+ source->get_file_dialog()->add_filter("*.otf;OpenType");
+ source->get_line_edit()->connect("text_entered",this,"_src_changed");
+
+ vbl->add_margin_child("Source Font:",source);
+ font_size = memnew( SpinBox );
+ vbl->add_margin_child("Source Font Size:",font_size);
+ font_size->set_min(3);
+ font_size->set_max(256);
+ font_size->set_val(16);
+ font_size->connect("value_changed",this,"_font_size_changed");
+ dest = memnew( LineEditFileChooser );
+ //
+ List<String> fl;
+ Ref<Font> font= memnew(Font);
+ 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("Dest Resource:",dest);
+ HBoxContainer *testhb = memnew( HBoxContainer );
+ test_string = memnew( LineEdit );
+ test_string->set_text("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_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("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("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("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("Import");
+
+ error_dialog = memnew ( ConfirmationDialog );
+ add_child(error_dialog);
+ error_dialog->get_ok()->set_text("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); }
+
+};
+
+Ref<Font> 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<Font>());
+
+ String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0));
+ 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("Error initializing FreeType.");
+ ERR_FAIL_COND_V( error !=0, Ref<Font>() );
+
+ 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("Unknown font format.");
+ FT_Done_FreeType( library );
+ } else if ( error ) {
+
+ ERR_EXPLAIN("Error loading font.");
+ FT_Done_FreeType( library );
+
+ }
+
+ ERR_FAIL_COND_V(error,Ref<Font>());
+
+
+ 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("Invalid font size. ");
+ ERR_FAIL_COND_V( error,Ref<Font>() );
+
+ }
+
+ error = FT_Set_Pixel_Sizes(face,0,size);
+
+ 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("Invalid font custom source. ");
+ ERR_FAIL_COND_V( !fa,Ref<Font>() );
+
+ }
+
+
+ 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, FT_LOAD_RENDER );
+ if (error) skip=true;
+ else error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+ 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 );
+ fdata->bitmap.resize( slot->bitmap.width*slot->bitmap.rows );
+ fdata->width=slot->bitmap.width;
+ fdata->height=slot->bitmap.rows;
+ fdata->character=charcode;
+ fdata->glyph=FT_Get_Char_Index(face,charcode);
+ if (charcode=='x')
+ xsize=slot->bitmap.width;
+
+
+ if (charcode<127) {
+ if (slot->bitmap_top>max_up) {
+
+ max_up=slot->bitmap_top;
+ }
+
+
+ if ( (slot->bitmap_top - fdata->height)<max_down ) {
+
+ max_down=slot->bitmap_top - fdata->height;
+ }
+ }
+
+
+ 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);
+
+ fdata->advance+=font_spacing;
+
+ 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, ft_render_mode_normal )) {
+
+ spd->advance = slot->advance.x>>6; //round to nearest or store as float
+ 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;
+ };
+ };
+ printf("total %i/%i\n", missing, import_chars.size());
+
+ /* KERNING */
+
+
+ for(int i=0;i<font_data_list.size();i++) {
+
+ for(int j=0;j<font_data_list.size();j++) {
+
+ 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;
+ }
+ }
+ }
+
+ 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;
+
+ DVector<uint8_t> pixels;
+ pixels.resize(s.x*s.y*4);
+
+ DVector<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.0,tr)*255.0;
+
+ }
+ }
+
+ }
+
+ //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=DVector<uint8_t>::Write();
+
+ Image img(s.width,s.height,0,Image::FORMAT_RGBA,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_RGBA);
+
+ 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 (0) {
+ //debug the texture
+ Ref<ImageTexture> atlast = memnew( ImageTexture );
+ atlast->create_from_image(atlas);
+// atlast->create_from_image(font_data_list[5]->blit);
+ TextureFrame *tf = memnew( TextureFrame );
+ 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");
+
+ Ref<Font> font;
+
+ if (p_existing!=String() && ResourceCache::has(p_existing)) {
+
+ font = Ref<Font>( ResourceCache::get(p_existing)->cast_to<Font>());
+ }
+
+ if (font.is_null()) {
+ font = Ref<Font>( memnew( Font ) );
+ }
+
+ font->clear();
+ font->set_height(height+bottom_space+top_space);
+ font->set_ascent(ascent+top_space);
+
+ //register texures
+ {
+ Ref<ImageTexture> t = memnew(ImageTexture);
+ t->create_from_image(atlas);
+ 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<Font>();
+#endif
+}
+
+
+String EditorFontImportPlugin::get_name() const {
+
+ return "font";
+}
+String EditorFontImportPlugin::get_visible_name() const{
+
+ return "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<Font> 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);
+
+}
+
+
+EditorFontImportPlugin::EditorFontImportPlugin(EditorNode* p_editor) {
+
+ dialog = memnew( EditorFontImportDialog(this) );
+ p_editor->get_gui_base()->add_child(dialog);
+}
diff --git a/tools/editor/io_plugins/editor_font_import_plugin.h b/tools/editor/io_plugins/editor_font_import_plugin.h
new file mode 100644
index 000000000..ac3b4eb0f
--- /dev/null
+++ b/tools/editor/io_plugins/editor_font_import_plugin.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* editor_font_import_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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 "tools/editor/editor_import_export.h"
+#include "scene/resources/font.h"
+
+class EditorNode;
+class EditorFontImportDialog;
+
+class EditorFontImportPlugin : public EditorImportPlugin {
+
+ OBJ_TYPE(EditorFontImportPlugin,EditorImportPlugin);
+
+ EditorFontImportDialog *dialog;
+public:
+
+ Ref<Font> 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);
+
+
+ EditorFontImportPlugin(EditorNode* p_editor);
+};
+
+#endif // EDITOR_FONT_IMPORT_PLUGIN_H
diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp
new file mode 100644
index 000000000..ffb68dff8
--- /dev/null
+++ b/tools/editor/io_plugins/editor_import_collada.cpp
@@ -0,0 +1,2200 @@
+/*************************************************************************/
+/* editor_import_collada.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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_import_collada.h"
+#include "collada/collada.h"
+#include "scene/3d/spatial.h"
+#include "scene/3d/skeleton.h"
+#include "scene/3d/path.h"
+#include "scene/3d/camera.h"
+#include "scene/3d/light.h"
+#include "scene/animation/animation_player.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/resources/animation.h"
+#include "scene/resources/packed_scene.h"
+#include "os/os.h"
+#include "tools/editor/editor_node.h"
+
+
+struct ColladaImport {
+
+ Collada collada;
+ Spatial *scene;
+
+ Vector<Ref<Animation> > animations;
+
+ struct NodeMap {
+ //String path;
+ Spatial *node;
+ int bone;
+ List<int> anim_tracks;
+
+ NodeMap() { node=NULL; bone=-1; }
+ };
+
+ bool found_ambient;
+ Color ambient;
+ bool found_directional;
+ bool force_make_tangents;
+
+
+
+ Map<String,NodeMap> node_map; //map from collada node to engine node
+ Map<String, Ref<Mesh> > mesh_cache;
+ Map<String, Ref<Curve3D> > curve_cache;
+ Map<String, Ref<Material> > material_cache;
+
+ Map< Skeleton*, Map< String, int> > skeleton_bone_map;
+
+ Set<String> valid_animated_nodes;
+ Vector<int> valid_animated_properties;
+ Map<String,bool> bones_with_animation;
+
+ Error _populate_skeleton(Skeleton *p_skeleton,Collada::Node *p_node, int &r_bone, int p_parent);
+ Error _create_scene(Collada::Node *p_node, Spatial *p_parent);
+ Error _create_resources(Collada::Node *p_node);
+ Error _create_material(const String& p_material);
+ Error _create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data);
+ Error load(const String& p_path, int p_flags, bool p_force_make_tangents=false);
+ void _fix_param_animation_tracks();
+ void create_animation(int p_clip=-1);
+ void create_animations();
+
+ Set<String> tracks_in_clips;
+ Vector<String> missing_textures;
+
+ void _pre_process_lights(Collada::Node *p_node);
+
+ ColladaImport() {
+
+ found_ambient=false;
+ found_directional=false;
+ force_make_tangents=false;
+
+ }
+};
+
+
+Error ColladaImport::_populate_skeleton(Skeleton *p_skeleton,Collada::Node *p_node, int &r_bone, int p_parent) {
+
+
+ if (p_node->type!=Collada::Node::TYPE_JOINT)
+ return OK;
+
+ Collada::NodeJoint *joint = static_cast<Collada::NodeJoint*>(p_node);
+
+ p_skeleton->add_bone(p_node->name);
+ if (p_parent>=0)
+ p_skeleton->set_bone_parent(r_bone,p_parent);
+
+ NodeMap nm;
+ nm.node=p_skeleton;
+ nm.bone = r_bone;
+ node_map[p_node->id]=nm;
+
+ skeleton_bone_map[p_skeleton][joint->sid]=r_bone;
+
+ if (collada.state.bone_rest_map.has(joint->sid)) {
+
+ p_skeleton->set_bone_rest(r_bone,collada.fix_transform(collada.state.bone_rest_map[joint->sid]));
+ //should map this bone to something for animation?
+ } else {
+ print_line("no rest: "+joint->sid);
+ WARN_PRINT("Joint has no rest..");
+ }
+
+
+ int id = r_bone++;
+ for(int i=0;i<p_node->children.size();i++) {
+
+ Error err = _populate_skeleton(p_skeleton,p_node->children[i],r_bone,id);
+ if (err)
+ return err;
+ }
+
+ return OK;
+}
+
+
+void ColladaImport::_pre_process_lights(Collada::Node *p_node) {
+
+
+ if (p_node->type==Collada::Node::TYPE_LIGHT) {
+
+
+ Collada::NodeLight *light=static_cast<Collada::NodeLight*>(p_node);
+ if (collada.state.light_data_map.has(light->light)) {
+
+ Collada::LightData &ld = collada.state.light_data_map[light->light];
+ if (ld.mode==Collada::LightData::MODE_AMBIENT) {
+ found_ambient=true;
+ ambient=ld.color;
+ }
+ if (ld.mode==Collada::LightData::MODE_DIRECTIONAL) {
+ found_directional=true;
+ }
+ }
+
+ }
+
+
+ for(int i=0;i<p_node->children.size();i++)
+ _pre_process_lights(p_node->children[i]);
+}
+
+Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) {
+
+ Spatial * node=NULL;
+
+ switch(p_node->type) {
+
+ case Collada::Node::TYPE_NODE: {
+
+ node = memnew( Spatial );
+ } break;
+ case Collada::Node::TYPE_JOINT: {
+
+ return OK; // do nothing
+ } break;
+ case Collada::Node::TYPE_LIGHT: {
+
+ //node = memnew( Light)
+ Collada::NodeLight *light = static_cast<Collada::NodeLight*>(p_node);
+ if (collada.state.light_data_map.has(light->light)) {
+
+ Collada::LightData &ld = collada.state.light_data_map[light->light];
+
+ if (ld.mode==Collada::LightData::MODE_AMBIENT) {
+
+ if (found_directional)
+ return OK; //do nothing not needed
+
+ if (!bool(GLOBAL_DEF("collada/use_ambient",false)))
+ return OK;
+ //well, it's an ambient light..
+ Light *l = memnew( DirectionalLight );
+ l->set_color(Light::COLOR_AMBIENT,ld.color);
+ l->set_color(Light::COLOR_DIFFUSE,Color(0,0,0));
+ l->set_color(Light::COLOR_SPECULAR,Color(0,0,0));
+ node = l;
+
+ } else if (ld.mode==Collada::LightData::MODE_DIRECTIONAL) {
+
+ //well, it's an ambient light..
+ Light *l = memnew( DirectionalLight );
+ if (found_ambient) //use it here
+ l->set_color(Light::COLOR_AMBIENT,ambient);
+
+ l->set_color(Light::COLOR_DIFFUSE,ld.color);
+ l->set_color(Light::COLOR_SPECULAR,Color(1,1,1));
+ node = l;
+ } else {
+
+ Light *l;
+
+ if (ld.mode==Collada::LightData::MODE_OMNI)
+ l=memnew( OmniLight );
+ else {
+ l=memnew( SpotLight );
+ l->set_parameter(Light::PARAM_SPOT_ANGLE,ld.spot_angle);
+ l->set_parameter(Light::PARAM_SPOT_ATTENUATION,ld.spot_exp);
+ }
+
+ //
+ l->set_color(Light::COLOR_DIFFUSE,ld.color);
+ l->set_color(Light::COLOR_SPECULAR,Color(1,1,1));
+ l->approximate_opengl_attenuation(ld.constant_att,ld.linear_att,ld.quad_att);
+ node=l;
+ }
+
+ } else {
+
+ node = memnew( Spatial );
+ }
+ } break;
+ case Collada::Node::TYPE_CAMERA: {
+
+ Collada::NodeCamera *cam = static_cast<Collada::NodeCamera*>(p_node);
+ Camera *camera = memnew( Camera );
+
+ if (collada.state.camera_data_map.has(cam->camera)) {
+
+ const Collada::CameraData &cd = collada.state.camera_data_map[cam->camera];
+
+ switch(cd.mode) {
+
+ case Collada::CameraData::MODE_ORTHOGONAL: {
+
+ if (cd.orthogonal.x_mag) {
+
+ camera->set_orthogonal(cd.orthogonal.x_mag,cd.z_near,cd.z_far);
+
+ } else if (!cd.orthogonal.x_mag && cd.orthogonal.y_mag) {
+
+ camera->set_orthogonal(cd.orthogonal.y_mag * cd.aspect,cd.z_near,cd.z_far);
+ }
+
+ } break;
+ case Collada::CameraData::MODE_PERSPECTIVE: {
+
+ if (cd.perspective.y_fov) {
+
+ camera->set_perspective(cd.perspective.y_fov,cd.z_near,cd.z_far);
+
+ } else if (!cd.perspective.y_fov && cd.perspective.x_fov) {
+
+ camera->set_perspective(cd.perspective.x_fov / cd.aspect,cd.z_near,cd.z_far);
+ }
+
+ } break;
+ }
+
+ }
+
+ node=camera;
+
+ } break;
+ case Collada::Node::TYPE_GEOMETRY: {
+
+ Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(p_node);
+
+ if (collada.state.curve_data_map.has(ng->source)) {
+
+ node = memnew( Path );
+ } else {
+ //mesh since nothing else
+ node = memnew( MeshInstance );
+ }
+ } break;
+ case Collada::Node::TYPE_SKELETON: {
+
+ Skeleton *sk = memnew( Skeleton );
+ int bone = 0;
+
+ for(int i=0;i<p_node->children.size();i++) {
+
+ _populate_skeleton(sk,p_node->children[i],bone,-1);
+ }
+ sk->localize_rests(); //after creating skeleton, rests must be localized...!
+
+ node=sk;
+ } break;
+
+ }
+
+ if (p_node->name!="")
+ node->set_name(p_node->name);
+ NodeMap nm;
+ nm.node=node;
+ node_map[p_node->id]=nm;
+ Transform xf = p_node->default_transform;
+
+ xf = collada.fix_transform( xf ) * p_node->post_transform;
+ node->set_transform(xf);
+ p_parent->add_child(node);
+ node->set_owner(scene);
+
+ for(int i=0;i<p_node->children.size();i++) {
+
+ Error err = _create_scene(p_node->children[i],node);
+ if (err)
+ return err;
+ }
+ return OK;
+}
+
+
+Error ColladaImport::_create_material(const String& p_target) {
+
+ ERR_FAIL_COND_V(material_cache.has(p_target),ERR_ALREADY_EXISTS);
+ ERR_FAIL_COND_V(!collada.state.material_map.has(p_target),ERR_INVALID_PARAMETER);
+ Collada::Material &src_mat=collada.state.material_map[p_target];
+ ERR_FAIL_COND_V(!collada.state.effect_map.has(src_mat.instance_effect),ERR_INVALID_PARAMETER);
+ Collada::Effect &effect=collada.state.effect_map[src_mat.instance_effect];
+
+ Ref<FixedMaterial> material= memnew( FixedMaterial );
+
+ // DIFFUSE
+
+ if (effect.diffuse.texture!="") {
+
+ String texfile = effect.get_texture_path(effect.diffuse.texture,collada);
+ if (texfile!="") {
+
+ Ref<Texture> texture = ResourceLoader::load(texfile,"Texture");
+ if (texture.is_valid()) {
+
+ material->set_texture(FixedMaterial::PARAM_DIFFUSE,texture);
+ material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,1));
+ } else {
+ missing_textures.push_back(texfile.get_file());
+ }
+ }
+ } else {
+ material->set_parameter(FixedMaterial::PARAM_DIFFUSE,effect.diffuse.color);
+ }
+
+ // SPECULAR
+
+ if (effect.specular.texture!="") {
+
+ String texfile = effect.get_texture_path(effect.specular.texture,collada);
+ if (texfile!="") {
+
+ Ref<Texture> texture = ResourceLoader::load(texfile,"Texture");
+ if (texture.is_valid()) {
+
+ material->set_texture(FixedMaterial::PARAM_SPECULAR,texture);
+ material->set_parameter(FixedMaterial::PARAM_SPECULAR,Color(1,1,1,1));
+ } else {
+ missing_textures.push_back(texfile.get_file());
+ }
+
+ }
+ } else {
+ material->set_parameter(FixedMaterial::PARAM_SPECULAR,effect.specular.color);
+ }
+
+ // EMISSION
+
+ if (effect.emission.texture!="") {
+
+ String texfile = effect.get_texture_path(effect.emission.texture,collada);
+ if (texfile!="") {
+
+ Ref<Texture> texture = ResourceLoader::load(texfile,"Texture");
+ if (texture.is_valid()) {
+
+ material->set_texture(FixedMaterial::PARAM_EMISSION,texture);
+ material->set_parameter(FixedMaterial::PARAM_EMISSION,Color(1,1,1,1));
+ }else {
+ missing_textures.push_back(texfile.get_file());
+ }
+
+ }
+ } else {
+ material->set_parameter(FixedMaterial::PARAM_EMISSION,effect.emission.color);
+ }
+
+ // NORMAL
+
+ if (effect.bump.texture!="") {
+
+ String texfile = effect.get_texture_path(effect.bump.texture,collada);
+ if (texfile!="") {
+
+ Ref<Texture> texture = ResourceLoader::load(texfile,"Texture");
+ if (texture.is_valid()) {
+
+ material->set_texture(FixedMaterial::PARAM_NORMAL,texture);
+ }else {
+ missing_textures.push_back(texfile.get_file());
+ }
+
+ }
+ }
+
+
+ material->set_parameter(FixedMaterial::PARAM_SPECULAR_EXP,effect.shininess);
+ material->set_flag(Material::FLAG_DOUBLE_SIDED,effect.double_sided);
+
+ material_cache[p_target]=material;
+ return OK;
+}
+
+
+static void _generate_normals(const DVector<int>& p_indices,const DVector<Vector3>& p_vertices,DVector<Vector3>&r_normals) {
+
+
+ r_normals.resize(p_vertices.size());
+ DVector<Vector3>::Write narrayw = r_normals.write();
+
+ int iacount=p_indices.size()/3;
+ DVector<int>::Read index_arrayr = p_indices.read();
+ DVector<Vector3>::Read vertex_arrayr = p_vertices.read();
+
+ for(int idx=0;idx<iacount;idx++) {
+
+ Vector3 v[3]={
+ vertex_arrayr[index_arrayr[idx*3+0]],
+ vertex_arrayr[index_arrayr[idx*3+1]],
+ vertex_arrayr[index_arrayr[idx*3+2]]
+ };
+
+ Vector3 normal = Plane(v[0],v[1],v[2]).normal;
+
+ narrayw[index_arrayr[idx*3+0]]+=normal;
+ narrayw[index_arrayr[idx*3+1]]+=normal;
+ narrayw[index_arrayr[idx*3+2]]+=normal;
+ }
+
+ int vlen=p_vertices.size();
+
+ for(int idx=0;idx<vlen;idx++) {
+ narrayw[idx].normalize();
+ }
+
+}
+
+
+static void _generate_tangents_and_binormals(const DVector<int>& p_indices,const DVector<Vector3>& p_vertices,const DVector<Vector3>& p_uvs,const DVector<Vector3>& p_normals,DVector<real_t>&r_tangents) {
+
+ int vlen=p_vertices.size();
+
+ Vector<Vector3> tangents;
+ tangents.resize(vlen);
+ Vector<Vector3> binormals;
+ binormals.resize(vlen);
+
+
+ int iacount=p_indices.size()/3;
+
+ DVector<int>::Read index_arrayr = p_indices.read();
+ DVector<Vector3>::Read vertex_arrayr = p_vertices.read();
+ DVector<Vector3>::Read narrayr = p_normals.read();
+ DVector<Vector3>::Read uvarrayr = p_uvs.read();
+
+
+ for(int idx=0;idx<iacount;idx++) {
+
+
+ Vector3 v1 = vertex_arrayr[ index_arrayr[idx*3+0] ];
+ Vector3 v2 = vertex_arrayr[ index_arrayr[idx*3+1] ];
+ Vector3 v3 = vertex_arrayr[ index_arrayr[idx*3+2] ];
+
+ Vector3 w1 = uvarrayr[ index_arrayr[idx*3+0] ];
+ Vector3 w2 = uvarrayr[ index_arrayr[idx*3+1] ];
+ Vector3 w3 = uvarrayr[ index_arrayr[idx*3+2] ];
+
+ real_t x1 = v2.x - v1.x;
+ real_t x2 = v3.x - v1.x;
+ real_t y1 = v2.y - v1.y;
+ real_t y2 = v3.y - v1.y;
+ real_t z1 = v2.z - v1.z;
+ real_t z2 = v3.z - v1.z;
+
+ real_t s1 = w2.x - w1.x;
+ real_t s2 = w3.x - w1.x;
+ real_t t1 = w2.y - w1.y;
+ real_t t2 = w3.y - w1.y;
+
+ real_t r = (s1 * t2 - s2 * t1);
+
+ Vector3 tangent;
+ Vector3 binormal;
+
+ if (r==0) {
+
+ binormal=Vector3();
+ tangent=Vector3();
+ } else {
+ tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
+ (t2 * z1 - t1 * z2) * r);
+ binormal = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
+ (s1 * z2 - s2 * z1) * r);
+ }
+
+ tangents[ index_arrayr[idx*3+0] ]+=tangent;
+ binormals[ index_arrayr[idx*3+0] ]+=binormal;
+ tangents[ index_arrayr[idx*3+1] ]+=tangent;
+ binormals[ index_arrayr[idx*3+1] ]+=binormal;
+ tangents[ index_arrayr[idx*3+2] ]+=tangent;
+ binormals[ index_arrayr[idx*3+2] ]+=binormal;
+
+ }
+
+ r_tangents.resize(vlen*4);
+ DVector<real_t>::Write tarrayw = r_tangents.write();
+
+ for(int idx=0;idx<vlen;idx++) {
+ Vector3 tangent = tangents[idx];
+ Vector3 bingen = narrayr[idx].cross(tangent);
+ float dir;
+ if (bingen.dot(binormals[idx]) < 0 )
+ dir=-1.0;
+ else
+ dir=+1.0;
+
+ tarrayw[idx*4+0]=tangent.x;
+ tarrayw[idx*4+1]=tangent.y;
+ tarrayw[idx*4+2]=tangent.z;
+ tarrayw[idx*4+3]=dir;
+ }
+}
+
+Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *skin_controller, const Collada::MorphControllerData *p_morph_data) {
+
+
+ bool local_xform_mirror=p_local_xform.basis.determinant() < 0;
+
+ if (p_morph_data) {
+ //add morphie target
+ ERR_FAIL_COND_V( !p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA );
+ String mt = p_morph_data->targets["MORPH_TARGET"];
+ ERR_FAIL_COND_V( !p_morph_data->sources.has(mt), ERR_INVALID_DATA);
+ int morph_targets = p_morph_data->sources[mt].sarray.size();
+ for(int i=0;i<morph_targets;i++) {
+
+ String target = p_morph_data->sources[mt].sarray[i];
+ ERR_FAIL_COND_V( !collada.state.mesh_data_map.has(target), ERR_INVALID_DATA );
+ String name = collada.state.mesh_data_map[target].name;
+
+ p_mesh->add_morph_target(name);
+ }
+ if (p_morph_data->mode=="RELATIVE")
+ p_mesh->set_morph_target_mode(Mesh::MORPH_MODE_RELATIVE);
+ else if (p_morph_data->mode=="NORMALIZED")
+ p_mesh->set_morph_target_mode(Mesh::MORPH_MODE_NORMALIZED);
+ }
+
+
+ int surface=0;
+ for(int p_i = 0; p_i < meshdata.primitives.size(); p_i ++ ) {
+
+
+
+ const Collada::MeshData::Primitives& p = meshdata.primitives[p_i];
+
+ /* VERTEX SOURCE */
+ ERR_FAIL_COND_V(!p.sources.has("VERTEX"),ERR_INVALID_DATA);
+
+ String vertex_src_id = p.sources["VERTEX"].source;
+ int vertex_ofs=p.sources["VERTEX"].offset;
+
+ ERR_FAIL_COND_V(!meshdata.vertices.has(vertex_src_id),ERR_INVALID_DATA);
+
+ ERR_FAIL_COND_V(!meshdata.vertices[vertex_src_id].sources.has("POSITION"),ERR_INVALID_DATA);
+ String position_src_id = meshdata.vertices[vertex_src_id].sources["POSITION"];
+
+ ERR_FAIL_COND_V(!meshdata.sources.has(position_src_id),ERR_INVALID_DATA);
+
+ const Collada::MeshData::Source *vertex_src=&meshdata.sources[position_src_id];
+
+ /* NORMAL SOURCE */
+
+ const Collada::MeshData::Source *normal_src=NULL;
+ int normal_ofs=0;
+
+ if (p.sources.has("NORMAL")) {
+
+ String normal_source_id = p.sources["NORMAL"].source;
+ normal_ofs = p.sources["NORMAL"].offset;
+ ERR_FAIL_COND_V( !meshdata.sources.has(normal_source_id),ERR_INVALID_DATA);
+ normal_src=&meshdata.sources[normal_source_id];
+ }
+
+ const Collada::MeshData::Source *binormal_src=NULL;
+ int binormal_ofs=0;
+
+ if (p.sources.has("TEXBINORMAL")) {
+
+ String binormal_source_id = p.sources["TEXBINORMAL"].source;
+ binormal_ofs = p.sources["TEXBINORMAL"].offset;
+ ERR_FAIL_COND_V( !meshdata.sources.has(binormal_source_id),ERR_INVALID_DATA);
+ binormal_src=&meshdata.sources[binormal_source_id];
+ }
+
+ const Collada::MeshData::Source *tangent_src=NULL;
+ int tangent_ofs=0;
+
+ if (p.sources.has("TEXTANGENT")) {
+
+ String tangent_source_id = p.sources["TEXTANGENT"].source;
+ tangent_ofs = p.sources["TEXTANGENT"].offset;
+ ERR_FAIL_COND_V( !meshdata.sources.has(tangent_source_id),ERR_INVALID_DATA);
+ tangent_src=&meshdata.sources[tangent_source_id];
+ }
+
+
+ const Collada::MeshData::Source *uv_src=NULL;
+ int uv_ofs=0;
+
+ if (p.sources.has("TEXCOORD0")) {
+
+ String uv_source_id = p.sources["TEXCOORD0"].source;
+ uv_ofs = p.sources["TEXCOORD0"].offset;
+ ERR_FAIL_COND_V( !meshdata.sources.has(uv_source_id),ERR_INVALID_DATA);
+ uv_src=&meshdata.sources[uv_source_id];
+ }
+
+ const Collada::MeshData::Source *uv2_src=NULL;
+ int uv2_ofs=0;
+
+ if (p.sources.has("TEXCOORD1")) {
+
+ String uv2_source_id = p.sources["TEXCOORD1"].source;
+ uv2_ofs = p.sources["TEXCOORD1"].offset;
+ ERR_FAIL_COND_V( !meshdata.sources.has(uv2_source_id),ERR_INVALID_DATA);
+ uv2_src=&meshdata.sources[uv2_source_id];
+ }
+
+
+ const Collada::MeshData::Source *color_src=NULL;
+ int color_ofs=0;
+
+ if (false && p.sources.has("COLOR")) {
+
+ String color_source_id = p.sources["COLOR"].source;
+ color_ofs = p.sources["COLOR"].offset;
+ ERR_FAIL_COND_V( !meshdata.sources.has(color_source_id), ERR_INVALID_DATA );
+ color_src=&meshdata.sources[color_source_id];
+ }
+
+ //find largest source..
+
+
+ Set<Collada::Vertex> vertex_set; //vertex set will be the vertices
+ List<int> indices_list; //indices will be the indices
+ Map<int,Set<int> > vertex_map; //map vertices (for setting skinning/morph)
+
+ /**************************/
+ /* CREATE PRIMITIVE ARRAY */
+ /**************************/
+
+ // The way collada uses indices is more optimal, and friendlier with 3D modelling sofware,
+ // because it can index everything, not only vertices (similar to how the WII works).
+ // This is, however, more incompatible with standard video cards, so arrays must be converted.
+ // Must convert to GL/DX format.
+
+ int _prim_ofs=0;
+ for(int p_i=0;p_i<p.count;p_i++) {
+
+
+ int amount;
+ if (p.polygons.size()) {
+
+ ERR_FAIL_INDEX_V(p_i,p.polygons.size(),ERR_INVALID_DATA);
+ amount=p.polygons[p_i];
+ } else {
+ amount=3; //triangles;
+ }
+
+ //COLLADA_PRINT("amount: "+itos(amount));
+
+ int prev2[2]={0,0};
+
+ for(int j=0;j<amount;j++) {
+
+ int src=_prim_ofs;
+ //_prim_ofs+=p.sources.size()
+
+ ERR_FAIL_INDEX_V(src,p.indices.size(),ERR_INVALID_DATA);
+
+ Collada::Vertex vertex;
+
+ int vertex_index=p.indices[src+vertex_ofs]; //used for index field (later used by controllers)
+ int vertex_pos = (vertex_src->stride?vertex_src->stride:3) * vertex_index;
+ ERR_FAIL_INDEX_V(vertex_pos,vertex_src->array.size(),ERR_INVALID_DATA);
+ vertex.vertex=Vector3(vertex_src->array[vertex_pos+0],vertex_src->array[vertex_pos+1],vertex_src->array[vertex_pos+2]);
+
+
+ if (normal_src) {
+
+
+
+ int normal_pos = (normal_src->stride?normal_src->stride:3) * p.indices[src+normal_ofs];
+ ERR_FAIL_INDEX_V(normal_pos,normal_src->array.size(),ERR_INVALID_DATA);
+ vertex.normal=Vector3(normal_src->array[normal_pos+0],normal_src->array[normal_pos+1],normal_src->array[normal_pos+2]);
+ vertex.normal=vertex.normal.snapped(0.001);
+
+
+ if (tangent_src && binormal_src) {
+
+ int binormal_pos = (binormal_src->stride?binormal_src->stride:3) * p.indices[src+binormal_ofs];
+ ERR_FAIL_INDEX_V(binormal_pos,binormal_src->array.size(),ERR_INVALID_DATA);
+ Vector3 binormal =Vector3(binormal_src->array[binormal_pos+0],binormal_src->array[binormal_pos+1],binormal_src->array[binormal_pos+2]);
+
+ int tangent_pos = (tangent_src->stride?tangent_src->stride:3) * p.indices[src+tangent_ofs];
+ ERR_FAIL_INDEX_V(tangent_pos,tangent_src->array.size(),ERR_INVALID_DATA);
+ Vector3 tangent =Vector3(tangent_src->array[tangent_pos+0],tangent_src->array[tangent_pos+1],tangent_src->array[tangent_pos+2]);
+
+ vertex.tangent.normal=tangent;
+ vertex.tangent.d= vertex.normal.cross(tangent).dot(binormal) > 0 ? -1 : 1;
+ }
+
+ }
+
+
+ if (uv_src) {
+
+ int uv_pos = (uv_src->stride?uv_src->stride:2) * p.indices[src+uv_ofs];
+ ERR_FAIL_INDEX_V(uv_pos,uv_src->array.size(),ERR_INVALID_DATA);
+ vertex.uv=Vector3(uv_src->array[uv_pos+0],1.0-uv_src->array[uv_pos+1],0);
+ }
+
+ if (uv2_src) {
+
+ int uv2_pos = (uv2_src->stride?uv2_src->stride:2) * p.indices[src+uv2_ofs];
+ ERR_FAIL_INDEX_V(uv2_pos,uv2_src->array.size(),ERR_INVALID_DATA);
+ vertex.uv2=Vector3(uv2_src->array[uv2_pos+0],1.0-uv2_src->array[uv2_pos+1],0);
+ }
+
+ if (color_src) {
+
+ int color_pos = (color_src->stride?color_src->stride:3) * p.indices[src+color_ofs]; // colors are RGB in collada..
+ ERR_FAIL_INDEX_V(color_pos,color_src->array.size(),ERR_INVALID_DATA);
+ vertex.color=Color(color_src->array[color_pos+0],color_src->array[color_pos+1],color_src->array[color_pos+2],(color_src->stride>3)?color_src->array[color_pos+3]:1.0);
+
+ }
+
+#ifndef NO_UP_AXIS_SWAP
+ if (collada.state.up_axis==Vector3::AXIS_Z) {
+
+ SWAP( vertex.vertex.z, vertex.vertex.y );
+ vertex.vertex.z = -vertex.vertex.z;
+ SWAP( vertex.normal.z, vertex.normal.y );
+ vertex.normal.z = -vertex.normal.z;
+
+ }
+
+#endif
+
+ vertex.fix_unit_scale(collada);
+ int index=0;
+ //COLLADA_PRINT("vertex: "+vertex.vertex);
+
+ if (vertex_set.has(vertex)) {
+
+ index=vertex_set.find(vertex)->get().idx;
+ } else {
+
+ index=vertex_set.size();
+ vertex.idx=index;
+ vertex_set.insert(vertex);
+ }
+
+ if (!vertex_map.has(vertex_index))
+ vertex_map[vertex_index]=Set<int>();
+ vertex_map[vertex_index].insert(index); //should be outside..
+ //build triangles if needed
+ if (j==0)
+ prev2[0]=index;
+
+ if (j>=2) {
+ //insert indices in reverse order (collada uses CCW as frontface)
+ if (local_xform_mirror) {
+
+ indices_list.push_back(prev2[0]);
+ indices_list.push_back(prev2[1]);
+ indices_list.push_back(index);
+
+ } else {
+ indices_list.push_back(prev2[0]);
+ indices_list.push_back(index);
+ indices_list.push_back(prev2[1]);
+ }
+ }
+
+ prev2[1]=index;
+ _prim_ofs+=p.vertex_size;
+ }
+
+ }
+
+
+
+ Vector<Collada::Vertex> vertex_array; //there we go, vertex array
+
+ vertex_array.resize(vertex_set.size());
+ for(Set<Collada::Vertex>::Element *F=vertex_set.front();F;F=F->next()) {
+
+ vertex_array[F->get().idx]=F->get();
+ }
+
+ /************************/
+ /* ADD WEIGHTS IF EXIST */
+ /************************/
+
+
+ bool has_weights=false;
+
+ if (skin_controller) {
+
+ const Collada::SkinControllerData::Source *weight_src=NULL;
+ int weight_ofs=0;
+
+ if (skin_controller->weights.sources.has("WEIGHT")) {
+
+ String weight_id = skin_controller->weights.sources["WEIGHT"].source;
+ weight_ofs = skin_controller->weights.sources["WEIGHT"].offset;
+ if (skin_controller->sources.has(weight_id)) {
+
+ weight_src = &skin_controller->sources[weight_id];
+
+ }
+ }
+
+ int joint_ofs=0;
+
+ if (skin_controller->weights.sources.has("JOINT")) {
+
+ joint_ofs = skin_controller->weights.sources["JOINT"].offset;
+ }
+
+ //should be OK, given this was pre-checked.
+
+ int index_ofs=0;
+ int wstride = skin_controller->weights.sources.size();
+ for(int w_i=0;w_i<skin_controller->weights.sets.size();w_i++) {
+
+ int amount = skin_controller->weights.sets[w_i];
+
+ if (vertex_map.has(w_i)) { //vertex may no longer be here, don't bother converting
+
+ Vector<Collada::Vertex::Weight> weights;
+
+ for (int a_i=0;a_i<amount;a_i++) {
+
+ Collada::Vertex::Weight w;
+
+ int read_from = index_ofs+a_i*wstride;
+ ERR_FAIL_INDEX_V(read_from+wstride-1,skin_controller->weights.indices.size(),ERR_INVALID_DATA);
+ int weight_index = skin_controller->weights.indices[read_from+weight_ofs];
+ ERR_FAIL_INDEX_V(weight_index,weight_src->array.size(),ERR_INVALID_DATA);
+
+ w.weight = weight_src->array[weight_index];
+
+ int bone_index = skin_controller->weights.indices[read_from+joint_ofs];
+ if (bone_index==-1)
+ continue; //ignore this weight (refers to bind shape)
+ ERR_FAIL_INDEX_V(bone_index,bone_remap.size(),ERR_INVALID_DATA);
+
+ w.bone_idx=bone_remap[bone_index];
+
+
+ weights.push_back(w);
+ }
+
+ /* FIX WEIGHTS */
+
+
+
+ weights.sort();
+
+ if (weights.size()>4) {
+ //cap to 4 and make weights add up 1
+ weights.resize(4);
+
+ }
+
+ //make sure weights allways add up to 1
+ float total=0;
+ for(int i=0;i<weights.size();i++)
+ total+=weights[i].weight;
+ if (total)
+ for(int i=0;i<weights.size();i++)
+ weights[i].weight/=total;
+
+ if (weights.size()==0 || total==0) { //if nothing, add a weight to bone 0
+ //no weights assigned
+ Collada::Vertex::Weight w;
+ w.bone_idx=0;
+ w.weight=1.0;
+ weights.clear();
+ weights.push_back(w);
+
+ }
+
+
+ for(Set<int>::Element *E=vertex_map[w_i].front();E;E=E->next()) {
+
+ int dst = E->get();
+ ERR_EXPLAIN("invalid vertex index in array");
+ ERR_FAIL_INDEX_V(dst,vertex_array.size(),ERR_INVALID_DATA);
+ vertex_array[dst].weights=weights;
+
+ }
+
+ } else {
+ //zzprint_line("no vertex found for index "+itos(w_i));
+ }
+
+ index_ofs+=wstride*amount;
+
+ }
+
+ //vertices need to be localized
+
+ Transform local_xform = p_local_xform;
+ for(int i=0;i<vertex_array.size();i++) {
+
+ vertex_array[i].vertex=local_xform.xform(vertex_array[i].vertex);
+ vertex_array[i].normal=local_xform.basis.xform(vertex_array[i].normal).normalized();
+ vertex_array[i].tangent.normal=local_xform.basis.xform(vertex_array[i].tangent.normal).normalized();
+ if (local_xform_mirror) {
+ //i shouldn't do this? wtf?
+ //vertex_array[i].normal*=-1.0;
+ //vertex_array[i].tangent.normal*=-1.0;
+ }
+ }
+
+ has_weights=true;
+
+ }
+
+ DVector<int> index_array;
+ index_array.resize(indices_list.size());
+ DVector<int>::Write index_arrayw = index_array.write();
+
+ int iidx=0;
+ for(List<int>::Element *F=indices_list.front();F;F=F->next()) {
+
+ index_arrayw[iidx++]=F->get();
+ }
+
+ index_arrayw=DVector<int>::Write();
+
+
+ /*****************/
+ /* MAKE SURFACES */
+ /*****************/
+
+
+ {
+
+ Ref<FixedMaterial> material;
+
+ //find material
+ Mesh::PrimitiveType primitive=Mesh::PRIMITIVE_TRIANGLES;
+
+ {
+
+ if (p_material_map.has(p.material)) {
+ String target=p_material_map[p.material].target;
+
+ if (!material_cache.has(target)) {
+ Error err = _create_material(target);
+ if (!err)
+ material=material_cache[target];
+ } else
+ material=material_cache[target];
+
+ } else if (p.material!=""){
+ print_line("Warning, unreferenced material in geometry instance: "+p.material);
+ }
+ }
+
+
+
+ DVector<Vector3> final_vertex_array;
+ DVector<Vector3> final_normal_array;
+ DVector<float> final_tangent_array;
+ DVector<Color> final_color_array;
+ DVector<Vector3> final_uv_array;
+ DVector<Vector3> final_uv2_array;
+ DVector<float> final_bone_array;
+ DVector<float> final_weight_array;
+
+ uint32_t final_format=0;
+
+ //create format
+ final_format=Mesh::ARRAY_FORMAT_VERTEX|Mesh::ARRAY_FORMAT_INDEX;
+
+ if (normal_src) {
+ final_format|=Mesh::ARRAY_FORMAT_NORMAL;
+ if (uv_src && binormal_src && tangent_src) {
+ final_format|=Mesh::ARRAY_FORMAT_TANGENT;
+ }
+
+ }
+
+
+
+ if (color_src)
+ final_format|=Mesh::ARRAY_FORMAT_COLOR;
+ if (uv_src)
+ final_format|=Mesh::ARRAY_FORMAT_TEX_UV;
+ if (uv2_src)
+ final_format|=Mesh::ARRAY_FORMAT_TEX_UV2;
+
+ if (has_weights) {
+ final_format|=Mesh::ARRAY_FORMAT_WEIGHTS;
+ final_format|=Mesh::ARRAY_FORMAT_BONES;
+ }
+
+
+ //set arrays
+
+ int vlen = vertex_array.size();
+ { //vertices
+
+ DVector<Vector3> varray;
+ varray.resize(vertex_array.size());
+
+ DVector<Vector3>::Write varrayw = varray.write();
+
+ for(int k=0;k<vlen;k++)
+ varrayw[k]=vertex_array[k].vertex;
+
+ varrayw = DVector<Vector3>::Write();
+ final_vertex_array=varray;
+
+ }
+
+
+ if (uv_src) { //compute uv first, may be needed for computing tangent/bionrmal
+ DVector<Vector3> uvarray;
+ uvarray.resize(vertex_array.size());
+ DVector<Vector3>::Write uvarrayw = uvarray.write();
+
+ for(int k=0;k<vlen;k++) {
+ uvarrayw[k]=vertex_array[k].uv;
+ }
+
+ uvarrayw = DVector<Vector3>::Write();
+ final_uv_array=uvarray;
+
+ }
+
+ if (uv2_src) { //compute uv first, may be needed for computing tangent/bionrmal
+ DVector<Vector3> uv2array;
+ uv2array.resize(vertex_array.size());
+ DVector<Vector3>::Write uv2arrayw = uv2array.write();
+
+ for(int k=0;k<vlen;k++) {
+ uv2arrayw[k]=vertex_array[k].uv;
+ }
+
+ uv2arrayw = DVector<Vector3>::Write();
+ final_uv2_array=uv2array;
+
+ }
+
+ if (normal_src) {
+ DVector<Vector3> narray;
+ narray.resize(vertex_array.size());
+ DVector<Vector3>::Write narrayw = narray.write();
+
+ for(int k=0;k<vlen;k++) {
+ narrayw[k]=vertex_array[k].normal;
+ }
+
+ narrayw = DVector<Vector3>::Write();
+ final_normal_array=narray;
+
+ //DVector<Vector3> altnaray;
+ //_generate_normals(index_array,final_vertex_array,altnaray);
+
+ //for(int i=0;i<altnaray.size();i++)
+ // print_line(rtos(altnaray[i].dot(final_normal_array[i])));
+
+ } else if (primitive==Mesh::PRIMITIVE_TRIANGLES) {
+ //generate normals (even if unused later)
+
+ _generate_normals(index_array,final_vertex_array,final_normal_array);
+ if (OS::get_singleton()->is_stdout_verbose())
+ print_line("Collada: Triangle mesh lacks normals, so normals were generated.");
+ final_format|=Mesh::ARRAY_FORMAT_NORMAL;
+
+ }
+
+ if (final_normal_array.size() && uv_src && binormal_src && tangent_src && !force_make_tangents) {
+
+ DVector<real_t> tarray;
+ tarray.resize(vertex_array.size()*4);
+ DVector<real_t>::Write tarrayw = tarray.write();
+
+
+ for(int k=0;k<vlen;k++) {
+ tarrayw[k*4+0]=vertex_array[k].tangent.normal.x;
+ tarrayw[k*4+1]=vertex_array[k].tangent.normal.y;
+ tarrayw[k*4+2]=vertex_array[k].tangent.normal.z;
+ tarrayw[k*4+3]=vertex_array[k].tangent.d;
+
+ }
+
+ tarrayw = DVector<real_t>::Write();
+
+ final_tangent_array=tarray;
+ } else if (final_normal_array.size() && primitive==Mesh::PRIMITIVE_TRIANGLES && final_uv_array.size() && (force_make_tangents || (material.is_valid() && material->get_texture(FixedMaterial::PARAM_NORMAL).is_valid()))){
+ //if this uses triangles, there are uvs and the material is using a normalmap, generate tangents and binormals, because they WILL be needed
+ //generate binormals/tangents
+ _generate_tangents_and_binormals(index_array,final_vertex_array,final_uv_array,final_normal_array,final_tangent_array);
+ final_format|=Mesh::ARRAY_FORMAT_TANGENT;
+ if (OS::get_singleton()->is_stdout_verbose())
+ print_line("Collada: Triangle mesh lacks tangents (And normalmap was used), so tangents were generated.");
+
+ }
+
+
+ if (color_src) {
+ DVector<Color> colorarray;
+ colorarray.resize(vertex_array.size());
+ DVector<Color>::Write colorarrayw = colorarray.write();
+
+ for(int k=0;k<vlen;k++) {
+ colorarrayw[k]=vertex_array[k].color;
+ }
+
+ colorarrayw = DVector<Color>::Write();
+
+ final_color_array=colorarray;
+ }
+
+ if (has_weights) {
+ DVector<float> weightarray;
+ DVector<float> bonearray;
+
+ weightarray.resize(vertex_array.size()*4);
+ DVector<float>::Write weightarrayw = weightarray.write();
+ bonearray.resize(vertex_array.size()*4);
+ DVector<float>::Write bonearrayw = bonearray.write();
+
+ for(int k=0;k<vlen;k++) {
+ float sum=0;
+
+ for(int l=0;l<VS::ARRAY_WEIGHTS_SIZE;l++) {
+ if (l<vertex_array[k].weights.size()) {
+ weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=vertex_array[k].weights[l].weight;
+ sum+=weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l];
+ bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=vertex_array[k].weights[l].bone_idx;
+ //COLLADA_PRINT(itos(k)+": "+rtos(bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l])+":"+rtos(weightarray[k*VS::ARRAY_WEIGHTS_SIZE+l]));
+ } else {
+
+ weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=0;
+ bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=0;
+
+ }
+
+
+ }
+// if (sum<0.8)
+// COLLADA_PRINT("ERROR SUMMING INDEX "+itos(k)+" had weights: "+itos(vertex_array[k].weights.size()));
+
+ }
+
+ weightarrayw = DVector<float>::Write();
+ bonearrayw = DVector<float>::Write();
+
+ final_weight_array = weightarray;
+ final_bone_array = bonearray;
+ }
+
+
+
+ ////////////////////////////
+ // FINALLY CREATE SUFRACE //
+ ////////////////////////////
+
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+
+ d[Mesh::ARRAY_INDEX]=index_array;
+ d[Mesh::ARRAY_VERTEX]=final_vertex_array;
+
+ if (final_normal_array.size())
+ d[Mesh::ARRAY_NORMAL]=final_normal_array;
+ if (final_tangent_array.size())
+ d[Mesh::ARRAY_TANGENT]=final_tangent_array;
+ if (final_uv_array.size())
+ d[Mesh::ARRAY_TEX_UV]=final_uv_array;
+ if (final_uv2_array.size())
+ d[Mesh::ARRAY_TEX_UV2]=final_uv2_array;
+ if (final_color_array.size())
+ d[Mesh::ARRAY_COLOR]=final_color_array;
+ if (final_weight_array.size())
+ d[Mesh::ARRAY_WEIGHTS]=final_weight_array;
+ if (final_bone_array.size())
+ d[Mesh::ARRAY_BONES]=final_bone_array;
+
+
+ Array mr;
+
+ ////////////////////////////
+ // THEN THE MORPH TARGETS //
+ ////////////////////////////
+
+ if (p_morph_data) {
+
+ //add morphie target
+ ERR_FAIL_COND_V( !p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA );
+ String mt = p_morph_data->targets["MORPH_TARGET"];
+ ERR_FAIL_COND_V( !p_morph_data->sources.has(mt), ERR_INVALID_DATA);
+ int morph_targets = p_morph_data->sources[mt].sarray.size();
+ mr.resize(morph_targets);
+
+ for(int j=0;j<morph_targets;j++) {
+
+ Array mrt;
+ mrt.resize(VS::ARRAY_MAX);
+
+ String target = p_morph_data->sources[mt].sarray[j];
+ ERR_FAIL_COND_V( !collada.state.mesh_data_map.has(target), ERR_INVALID_DATA );
+ String name = collada.state.mesh_data_map[target].name;
+ Collada::MeshData &md = collada.state.mesh_data_map[target];
+
+ // collada in itself supports morphing everything. However, the spec is unclear and no examples or exporters that
+ // morph anything but "POSITIONS" seem to exit. Because of this, normals and binormals/tangents have to be regenerated here,
+ // which may result in inaccurate (but most of the time good enough) results.
+
+ DVector<Vector3> vertices;
+ vertices.resize(vlen);
+
+ ERR_FAIL_COND_V( md.vertices.size() != 1, ERR_INVALID_DATA);
+ String vertex_src_id=md.vertices.front()->key();
+ ERR_FAIL_COND_V(!md.vertices[vertex_src_id].sources.has("POSITION"),ERR_INVALID_DATA);
+ String position_src_id = md.vertices[vertex_src_id].sources["POSITION"];
+
+ ERR_FAIL_COND_V(!md.sources.has(position_src_id),ERR_INVALID_DATA);
+
+ const Collada::MeshData::Source *m=&md.sources[position_src_id];
+
+ ERR_FAIL_COND_V( m->array.size() != vertex_src->array.size(), ERR_INVALID_DATA);
+ int stride=m->stride;
+ if (stride==0)
+ stride=3;
+
+
+ //read vertices from morph target
+ DVector<Vector3>::Write vertw = vertices.write();
+
+ for(int m_i=0;m_i<m->array.size()/stride;m_i++) {
+
+ int pos = m_i*stride;
+ Vector3 vtx( m->array[pos+0], m->array[pos+1], m->array[pos+2] );
+
+#ifndef NO_UP_AXIS_SWAP
+ if (collada.state.up_axis==Vector3::AXIS_Z) {
+
+ SWAP( vtx.z, vtx.y );
+ vtx.z = -vtx.z;
+
+ }
+#endif
+
+ Collada::Vertex vertex;
+ vertex.vertex=vtx;
+ vertex.fix_unit_scale(collada);
+ vtx=vertex.vertex;
+
+ vtx = p_local_xform.xform(vtx);
+
+
+ if (vertex_map.has(m_i)) { //vertex may no longer be here, don't bother converting
+
+
+ for (Set<int> ::Element *E=vertex_map[m_i].front() ; E; E=E->next() ) {
+
+ vertw[E->get()]=vtx;
+ }
+ }
+ }
+
+
+ //vertices are in place, now generate everything else
+ vertw = DVector<Vector3>::Write();
+ DVector<Vector3> normals;
+ DVector<float> tangents;
+
+ _generate_normals(index_array,vertices,normals);
+ if (final_tangent_array.size() && final_uv_array.size()) {
+
+ _generate_tangents_and_binormals(index_array,vertices,final_uv_array,normals,tangents);
+
+ }
+
+ mrt[Mesh::ARRAY_VERTEX]=vertices;
+
+ mrt[Mesh::ARRAY_NORMAL]=normals;
+ if (tangents.size())
+ mrt[Mesh::ARRAY_TANGENT]=tangents;
+ if (final_uv_array.size())
+ mrt[Mesh::ARRAY_TEX_UV]=final_uv_array;
+ if (final_uv2_array.size())
+ mrt[Mesh::ARRAY_TEX_UV2]=final_uv2_array;
+ if (final_color_array.size())
+ mrt[Mesh::ARRAY_COLOR]=final_color_array;
+
+ mr[j]=mrt;
+
+ }
+
+ }
+
+
+ p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d,mr);
+
+ if (material.is_valid()) {
+ p_mesh->surface_set_material(surface, material);
+ p_mesh->surface_set_name(surface, material->get_name());
+ }
+ }
+
+ /*****************/
+ /* FIND MATERIAL */
+ /*****************/
+
+ surface++;
+ }
+
+
+ return OK;
+
+}
+
+
+Error ColladaImport::_create_resources(Collada::Node *p_node) {
+
+
+ if (p_node->type==Collada::Node::TYPE_GEOMETRY && node_map.has(p_node->id)) {
+
+
+ Spatial * node=node_map[p_node->id].node;
+ Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(p_node);
+
+
+ if (node->cast_to<Path>()) {
+
+ Path *path = node->cast_to<Path>();
+
+ String curve = ng->source;
+
+ if (curve_cache.has(ng->source)) {
+
+ path->set_curve(curve_cache[ng->source]);
+ } else {
+
+ Ref<Curve3D> c = memnew( Curve3D );
+
+ const Collada::CurveData &cd = collada.state.curve_data_map[ng->source];
+
+ ERR_FAIL_COND_V( !cd.control_vertices.has("POSITION") , ERR_INVALID_DATA);
+ ERR_FAIL_COND_V( !cd.control_vertices.has("IN_TANGENT") , ERR_INVALID_DATA);
+ ERR_FAIL_COND_V( !cd.control_vertices.has("OUT_TANGENT") , ERR_INVALID_DATA);
+ ERR_FAIL_COND_V( !cd.control_vertices.has("INTERPOLATION") , ERR_INVALID_DATA);
+
+
+ ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["POSITION"] ) , ERR_INVALID_DATA);
+ const Collada::CurveData::Source &vertices = cd.sources[ cd.control_vertices["POSITION"] ];
+ ERR_FAIL_COND_V( vertices.stride!=3, ERR_INVALID_DATA );
+
+ ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["IN_TANGENT"] ) , ERR_INVALID_DATA);
+ const Collada::CurveData::Source &in_tangents = cd.sources[ cd.control_vertices["IN_TANGENT"] ];
+ ERR_FAIL_COND_V( in_tangents.stride!=3 , ERR_INVALID_DATA);
+
+ ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["OUT_TANGENT"] ), ERR_INVALID_DATA );
+ const Collada::CurveData::Source &out_tangents = cd.sources[ cd.control_vertices["OUT_TANGENT"] ];
+ ERR_FAIL_COND_V( out_tangents.stride!=3, ERR_INVALID_DATA );
+
+ ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["INTERPOLATION"] ), ERR_INVALID_DATA );
+ const Collada::CurveData::Source &interps = cd.sources[ cd.control_vertices["INTERPOLATION"] ];
+ ERR_FAIL_COND_V( interps.stride!=1, ERR_INVALID_DATA );
+
+ const Collada::CurveData::Source *tilts=NULL;
+ if (cd.control_vertices.has("TILT") && cd.sources.has(cd.control_vertices["TILT"]))
+ tilts=&cd.sources[ cd.control_vertices["TILT"] ];
+
+
+ if (tilts) {
+ print_line("FOUND TILTS!!!");
+ }
+ int pc = vertices.array.size()/3;
+ for(int i=0;i<pc;i++) {
+
+ Vector3 pos( vertices.array[i*3+0], vertices.array[i*3+1], vertices.array[i*3+2] );
+ Vector3 in( in_tangents.array[i*3+0], in_tangents.array[i*3+1], in_tangents.array[i*3+2] );
+ Vector3 out( out_tangents.array[i*3+0], out_tangents.array[i*3+1], out_tangents.array[i*3+2] );
+
+#ifndef NO_UP_AXIS_SWAP
+ if (collada.state.up_axis==Vector3::AXIS_Z) {
+
+ SWAP(pos.y,pos.z);
+ pos.z=-pos.z;
+ SWAP(in.y,in.z);
+ in.z=-in.z;
+ SWAP(out.y,out.z);
+ out.z=-out.z;
+ }
+#endif
+ pos*=collada.state.unit_scale;
+ in*=collada.state.unit_scale;
+ out*=collada.state.unit_scale;
+
+ c->add_point(pos,in-pos,out-pos);
+ if (tilts)
+ c->set_point_tilt(i,tilts->array[i]);
+
+ }
+
+ curve_cache[ng->source]=c;
+ path->set_curve(c);
+
+ }
+
+
+ }
+
+
+ if (node->cast_to<MeshInstance>()) {
+
+
+ Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(p_node);
+
+ MeshInstance *mi = node->cast_to<MeshInstance>();
+
+
+ ERR_FAIL_COND_V(!mi,ERR_BUG);
+
+
+ Collada::SkinControllerData *skin=NULL;
+ Collada::MorphControllerData *morph=NULL;
+ String meshid;
+ Transform apply_xform;
+ Vector<int> bone_remap;
+
+ if (ng->controller) {
+
+ if (collada.state.skin_controller_data_map.has(ng->source)) {
+
+
+ ERR_FAIL_COND_V(!collada.state.skin_controller_data_map.has(ng->source),ERR_INVALID_DATA);
+ skin=&collada.state.skin_controller_data_map[ng->source];
+
+ Vector<String> skeletons = ng->skeletons;
+
+ ERR_FAIL_COND_V( skeletons.empty(), ERR_INVALID_DATA );
+
+ String skname = skeletons[0];
+ ERR_FAIL_COND_V( !node_map.has(skname), ERR_INVALID_DATA );
+ NodeMap nmsk = node_map[skname];
+ Skeleton *sk = nmsk.node->cast_to<Skeleton>();
+ ERR_FAIL_COND_V( !sk, ERR_INVALID_DATA );
+ ERR_FAIL_COND_V( !skeleton_bone_map.has(sk), ERR_INVALID_DATA );
+ Map<String, int> &bone_remap_map=skeleton_bone_map[sk];
+
+
+ meshid=skin->base;
+
+ if (collada.state.morph_controller_data_map.has(meshid)) {
+ //it's a morph!!
+ morph = &collada.state.morph_controller_data_map[meshid];
+ meshid=morph->mesh;
+ }
+
+ apply_xform=collada.fix_transform(p_node->default_transform);
+ node->set_transform(Transform());
+
+ Collada::SkinControllerData::Source *joint_src=NULL;
+
+ ERR_FAIL_COND_V(!skin->weights.sources.has("JOINT"),ERR_INVALID_DATA);
+
+ String joint_id = skin->weights.sources["JOINT"].source;
+ ERR_FAIL_COND_V(!skin->sources.has(joint_id),ERR_INVALID_DATA);
+
+ joint_src = &skin->sources[joint_id];
+
+ bone_remap.resize(joint_src->sarray.size());
+
+ for(int i=0;i<bone_remap.size();i++) {
+
+ String str = joint_src->sarray[i];
+ ERR_FAIL_COND_V( !bone_remap_map.has(str), ERR_INVALID_DATA );
+ bone_remap[i]=bone_remap_map[str];
+ }
+ } else if (collada.state.morph_controller_data_map.has(ng->source)) {
+ //it's a morph!!
+ morph = &collada.state.morph_controller_data_map[meshid];
+ meshid=morph->mesh;
+ } else {
+ ERR_EXPLAIN("Controller Instance Source '"+ng->source+"' is neither skin or morph!");
+ ERR_FAIL_V( ERR_INVALID_DATA );
+ }
+
+
+
+ } else {
+ meshid=ng->source;
+ }
+
+ Ref<Mesh> mesh;
+ if (mesh_cache.has(meshid)) {
+ mesh=mesh_cache[meshid];
+ } else {
+ if (collada.state.mesh_data_map.has(meshid)) {
+ //bleh, must ignore invalid
+
+ ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid),ERR_INVALID_DATA);
+ mesh=Ref<Mesh>(memnew( Mesh ));
+ const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
+ mesh->set_name( meshdata.name );
+ Error err = _create_mesh_surfaces(mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,morph);
+ ERR_FAIL_COND_V(err,err);
+
+ mesh_cache[meshid]=mesh;
+ } else {
+
+ print_line("Warning, will not import geometry: "+meshid);
+ }
+ }
+
+ if (!mesh.is_null()) {
+ mi->set_mesh(mesh);
+ }
+ }
+ }
+
+ for(int i=0;i<p_node->children.size();i++) {
+
+ Error err = _create_resources(p_node->children[i]);
+ if (err)
+ return err;
+ }
+ return OK;
+}
+
+
+Error ColladaImport::load(const String& p_path,int p_flags,bool p_force_make_tangents) {
+
+ Error err = collada.load(p_path,p_flags);
+ ERR_FAIL_COND_V(err,err);
+
+ force_make_tangents=p_force_make_tangents;
+ ERR_FAIL_COND_V( !collada.state.visual_scene_map.has( collada.state.root_visual_scene ), ERR_INVALID_DATA );
+ Collada::VisualScene &vs = collada.state.visual_scene_map[ collada.state.root_visual_scene ];
+
+ scene = memnew( Spatial ); // root
+
+ //determine what's going on with the lights
+ for(int i=0;i<vs.root_nodes.size();i++) {
+
+ _pre_process_lights(vs.root_nodes[i]);
+
+ }
+ //import scene
+ for(int i=0;i<vs.root_nodes.size();i++) {
+
+ Error err = _create_scene(vs.root_nodes[i],scene);
+ if (err!=OK) {
+ memdelete(scene);
+ ERR_FAIL_COND_V(err,err);
+ }
+
+ Error err2 = _create_resources(vs.root_nodes[i]);
+ if (err2!=OK) {
+ memdelete(scene);
+ ERR_FAIL_COND_V(err2,err2);
+ }
+ }
+
+ //optatively, set unit scale in the root
+ scene->set_transform(collada.get_root_transform());
+
+
+ return OK;
+
+}
+
+void ColladaImport::_fix_param_animation_tracks() {
+
+ for (Map<String,Collada::Node*>::Element *E=collada.state.scene_map.front();E;E=E->next()) {
+
+ Collada::Node *n = E->get();
+ switch(n->type) {
+
+ case Collada::Node::TYPE_NODE: {
+ // ? do nothing
+ } break;
+ case Collada::Node::TYPE_JOINT: {
+
+ } break;
+ case Collada::Node::TYPE_SKELETON: {
+
+ } break;
+ case Collada::Node::TYPE_LIGHT: {
+
+ } break;
+ case Collada::Node::TYPE_CAMERA: {
+
+ } break;
+ case Collada::Node::TYPE_GEOMETRY: {
+
+ Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(n);
+ // test source(s)
+ String source = ng->source;
+
+ while (source!="") {
+
+ if (collada.state.skin_controller_data_map.has(source)) {
+
+ const Collada::SkinControllerData& skin = collada.state.skin_controller_data_map[source];
+
+ //nothing to animate here i think
+
+ source=skin.base;
+ } else if (collada.state.morph_controller_data_map.has(source)) {
+
+ const Collada::MorphControllerData& morph = collada.state.morph_controller_data_map[source];
+ if (morph.targets.has("MORPH_WEIGHT") && morph.targets.has("MORPH_TARGET")) {
+
+ String weights = morph.targets["MORPH_WEIGHT"];
+ String targets = morph.targets["MORPH_TARGET"];
+
+ if (morph.sources.has(targets) && morph.sources.has(weights)) {
+ const Collada::MorphControllerData::Source &weight_src=morph.sources[weights];
+ const Collada::MorphControllerData::Source &target_src=morph.sources[targets];
+
+ ERR_FAIL_COND(weight_src.array.size() != target_src.sarray.size());
+
+ for(int i=0;i<weight_src.array.size();i++) {
+
+ String track_name = weights+"("+itos(i)+")";
+ String mesh_name = target_src.sarray[i];
+ if (collada.state.mesh_name_map.has(mesh_name) && collada.state.referenced_tracks.has(track_name)) {
+
+ const Vector<int>&rt = collada.state.referenced_tracks[track_name];
+
+ for(int rti=0;rti<rt.size();rti++) {
+ Collada::AnimationTrack *at = &collada.state.animation_tracks[rt[rti]];
+
+ at->target=E->key();
+ at->param="morph/"+collada.state.mesh_name_map[mesh_name];
+ at->property=true;
+ //at->param
+ }
+ }
+ }
+ }
+ }
+ source=morph.mesh;
+ } else {
+
+ source=""; // for now nothing else supported
+ }
+ }
+
+ } break;
+
+ }
+ }
+
+}
+
+void ColladaImport::create_animations() {
+
+ print_line("-=-=-=-=-PRE CA");
+ _fix_param_animation_tracks();
+ for(int i=0;i<collada.state.animation_clips.size();i++) {
+
+ for(int j=0;j<collada.state.animation_clips[i].tracks.size();j++)
+ tracks_in_clips.insert(collada.state.animation_clips[i].tracks[j]);
+ }
+
+
+
+ for(int i=0;i<collada.state.animation_tracks.size();i++) {
+
+ Collada::AnimationTrack &at = collada.state.animation_tracks[i];
+ if (!node_map.has(at.target)) {
+ print_line("Coudlnt find node: "+at.target);
+ continue;
+ }
+
+
+ if (at.property) {
+
+ valid_animated_properties.push_back(i);
+
+ } else {
+ node_map[at.target].anim_tracks.push_back(i);
+ valid_animated_nodes.insert(at.target);
+ }
+
+ }
+
+ create_animation();
+ print_line("clipcount: "+itos(collada.state.animation_clips.size()));
+ for(int i=0;i<collada.state.animation_clips.size();i++)
+ create_animation(i);
+
+}
+
+void ColladaImport::create_animation(int p_clip) {
+
+ Ref<Animation> animation = Ref<Animation>( memnew( Animation ));
+
+ if (p_clip==-1) {
+
+ print_line("default");
+ animation->set_name("default");
+ } else {
+ print_line("clip name: "+collada.state.animation_clips[p_clip].name);
+ animation->set_name(collada.state.animation_clips[p_clip].name);
+ }
+
+ for(Map<String,NodeMap>::Element *E=node_map.front();E;E=E->next()) {
+
+ if (E->get().bone<0)
+ continue;
+ bones_with_animation[E->key()]=false;
+ }
+ //store and validate tracks
+
+ if (p_clip==-1) {
+ //main anim
+ }
+
+ Set<int> track_filter;
+
+
+ if (p_clip==-1) {
+
+ for(int i=0;i<collada.state.animation_clips.size();i++) {
+
+ int tc = collada.state.animation_clips[i].tracks.size();
+ for(int j=0;j<tc;j++) {
+
+ String n = collada.state.animation_clips[i].tracks[j];
+ if (collada.state.by_id_tracks.has(n)) {
+
+ const Vector<int>&ti = collada.state.by_id_tracks[n];
+ for(int k=0;k<ti.size();k++) {
+ track_filter.insert(ti[k]);
+ }
+ }
+ }
+ }
+ } else {
+
+ int tc = collada.state.animation_clips[p_clip].tracks.size();
+ for(int j=0;j<tc;j++) {
+
+ String n = collada.state.animation_clips[p_clip].tracks[j];
+ if (collada.state.by_id_tracks.has(n)) {
+
+ const Vector<int>&ti = collada.state.by_id_tracks[n];
+ for(int k=0;k<ti.size();k++) {
+ track_filter.insert(ti[k]);
+ }
+ }
+ }
+
+ }
+
+ //animation->set_loop(true);
+ //create animation tracks
+
+ Vector<float> base_snapshots;
+
+ float f=0;
+ float snapshot_interval = 1.0/20.0; //should be customizable somewhere...
+
+ float anim_length=collada.state.animation_length;
+ if (p_clip>=0 && collada.state.animation_clips[p_clip].end)
+ anim_length=collada.state.animation_clips[p_clip].end;
+
+ while(f<collada.state.animation_length) {
+ if (f>=collada.state.animation_length)
+ f=collada.state.animation_length;
+
+ base_snapshots.push_back(f);
+ f+=snapshot_interval;
+ }
+ print_line("anim len: "+rtos(anim_length));
+ animation->set_length(anim_length);
+
+ bool tracks_found=false;
+
+ for(Set<String>::Element* E=valid_animated_nodes.front();E;E=E->next()) {
+
+ // take snapshots
+ if (!collada.state.scene_map.has(E->get()))
+ continue;
+
+ NodeMap &nm = node_map[E->get()];
+ String path = scene->get_path_to(nm.node);
+
+ if (nm.bone>=0) {
+ Skeleton *sk = static_cast<Skeleton*>(nm.node);
+ String name = sk->get_bone_name(nm.bone);
+ path=path+":"+name;
+ }
+
+ bool found_anim=false;
+
+
+ Collada::Node *cn = collada.state.scene_map[E->get()];
+ if (cn->ignore_anim) {
+ print_line("warning, ignoring animation on node: "+path);
+ continue;
+ }
+
+
+
+ animation->add_track(Animation::TYPE_TRANSFORM);
+ int track = animation->get_track_count() -1;
+ animation->track_set_path( track , path );
+
+ Vector<float> snapshots = base_snapshots;
+
+ if (nm.anim_tracks.size()==1) {
+ //use snapshot keys from anim track instead, because this was most likely exported baked
+ Collada::AnimationTrack &at = collada.state.animation_tracks[nm.anim_tracks.front()->get()];
+ snapshots.clear();
+ for(int i=0;i<at.keys.size();i++)
+ snapshots.push_back(at.keys[i].time);
+
+ }
+
+
+ for(int i=0;i<snapshots.size();i++) {
+
+ for(List<int>::Element *ET=nm.anim_tracks.front();ET;ET=ET->next()) {
+ //apply tracks
+
+ if (p_clip==-1) {
+
+ if (track_filter.has(ET->get()))
+ continue;
+ } else {
+
+ if (!track_filter.has(ET->get()))
+ continue;
+
+ }
+
+ found_anim=true;
+
+ Collada::AnimationTrack &at = collada.state.animation_tracks[ET->get()];
+
+ int xform_idx=-1;
+ for(int j=0;j<cn->xform_list.size();j++) {
+
+
+ if (cn->xform_list[j].id==at.param) {
+
+ xform_idx=j;
+ break;
+ }
+ }
+
+ if (xform_idx==-1) {
+ print_line("couldnt find matching node "+at.target+" xform for track "+at.param);
+ continue;
+ }
+
+ ERR_CONTINUE(xform_idx==-1);
+
+ Vector<float> data = at.get_value_at_time(snapshots[i]);
+ ERR_CONTINUE(data.empty());
+
+
+ Collada::Node::XForm &xf = cn->xform_list[xform_idx];
+
+ if (at.component=="ANGLE") {
+ ERR_CONTINUE(data.size()!=1);
+ ERR_CONTINUE(xf.op!=Collada::Node::XForm::OP_ROTATE);
+ ERR_CONTINUE(xf.data.size()<4);
+ xf.data[3]=data[0];
+ } else if (at.component=="X" || at.component=="Y" || at.component=="Z") {
+ int cn=at.component[0]-'X';
+ ERR_CONTINUE(cn>=xf.data.size());
+ ERR_CONTINUE(data.size()>1);
+ xf.data[cn]=data[0];
+ } else if (data.size()==xf.data.size()) {
+
+ xf.data=data;
+ } else {
+
+
+ if ( data.size()!=xf.data.size() ) {
+ print_line("component "+at.component+" datasize "+itos(data.size())+" xfdatasize "+itos(xf.data.size()));
+ }
+
+ ERR_CONTINUE( data.size()!=xf.data.size() );
+ }
+ }
+
+ Transform xform = cn->compute_transform(collada);
+ xform = collada.fix_transform(xform) * cn->post_transform;
+
+
+ if (nm.bone>=0) {
+ //make bone transform relative to rest (in case of skeleton)
+ Skeleton *sk = nm.node->cast_to<Skeleton>();
+ if (sk) {
+
+ xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
+ } else {
+
+ ERR_PRINT("INVALID SKELETON!!!!");
+ }
+ }
+
+ Quat q = xform.basis;
+ q.normalize();
+ Vector3 s = xform.basis.get_scale();
+ Vector3 l = xform.origin;
+
+ animation->transform_track_insert_key(track,snapshots[i],l,q,s);
+
+ }
+
+
+
+ if (nm.bone>=0) {
+ if (found_anim)
+ bones_with_animation[E->get()]=true;
+ }
+
+ if (found_anim)
+ tracks_found=true;
+ else {
+ animation->remove_track( track );
+ }
+
+ }
+
+
+ //some bones may lack animation, but since we don't store pose as a property, we must add keyframes!
+ for(Map<String,bool>::Element *E=bones_with_animation.front();E;E=E->next()) {
+
+ if (E->get())
+ continue;
+
+ //print_line("BONE LACKS ANIM: "+E->key());
+
+ NodeMap &nm = node_map[E->key()];
+ String path = scene->get_path_to(nm.node);
+ ERR_CONTINUE( nm.bone <0 );
+ Skeleton *sk = static_cast<Skeleton*>(nm.node);
+ String name = sk->get_bone_name(nm.bone);
+ path=path+":"+name;
+
+ Collada::Node *cn = collada.state.scene_map[E->key()];
+ if (cn->ignore_anim) {
+ print_line("warning, ignoring animation on node: "+path);
+ continue;
+ }
+
+ animation->add_track(Animation::TYPE_TRANSFORM);
+ int track = animation->get_track_count() -1;
+ animation->track_set_path( track , path );
+
+
+ Transform xform = cn->compute_transform(collada);
+ xform = collada.fix_transform(xform) * cn->post_transform;
+
+ xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
+
+ Quat q = xform.basis;
+ q.normalize();
+ Vector3 s = xform.basis.get_scale();
+ Vector3 l = xform.origin;
+
+ animation->transform_track_insert_key(track,0,l,q,s);
+
+ tracks_found=true;
+ }
+
+
+
+
+ for(int i=0;i<valid_animated_properties.size();i++) {
+
+
+ int ti = valid_animated_properties[i];
+
+ if (p_clip==-1) {
+
+ if (track_filter.has(ti))
+ continue;
+ } else {
+
+ if (!track_filter.has(ti))
+ continue;
+
+ }
+
+
+ Collada::AnimationTrack &at = collada.state.animation_tracks[ ti ];
+
+ // take snapshots
+ if (!collada.state.scene_map.has(at.target))
+ continue;
+
+ NodeMap &nm = node_map[at.target];
+ String path = scene->get_path_to(nm.node);
+
+ Collada::Node *cn = collada.state.scene_map[at.target];
+ //if (cn->ignore_anim) {
+ // print_line("warning, ignoring property animation on node: "+nm.path);
+ //continue;
+ //}
+
+ animation->add_track(Animation::TYPE_VALUE);
+ int track = animation->get_track_count() -1;
+ path = path +":"+at.param;
+ animation->track_set_path( track , path );
+
+ for(int i=0;i<at.keys.size();i++) {
+
+ float time = at.keys[i].time;
+ Variant value;
+ Vector<float> data = at.keys[i].data;
+ if (data.size()==1) {
+ //push a float
+ value=data[0];
+
+ } else if (data.size()==16) {
+ //matrix
+ print_line("value keys for matrices not supported");
+ } else {
+
+ print_line("don't know what to do with this amount of value keys: "+itos(data.size()));
+ }
+
+ animation->track_insert_key(track,time,value);
+ }
+
+
+ tracks_found=true;
+
+ }
+
+
+
+ if (tracks_found) {
+
+ animations.push_back(animation);
+ }
+
+
+
+}
+
+
+/*********************************************************************************/
+/*************************************** SCENE ***********************************/
+/*********************************************************************************/
+
+
+#define DEBUG_ANIMATION
+
+
+uint32_t EditorSceneImporterCollada::get_import_flags() const {
+
+ return IMPORT_SCENE|IMPORT_ANIMATION;
+
+}
+void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) const {
+
+
+ r_extensions->push_back("dae");
+}
+Node* EditorSceneImporterCollada::import_scene(const String& p_path,uint32_t p_flags,Error* r_err) {
+
+
+ ColladaImport state;
+ uint32_t flags=Collada::IMPORT_FLAG_SCENE;
+ if (p_flags&IMPORT_ANIMATION)
+ flags|=Collada::IMPORT_FLAG_ANIMATION;
+
+
+ Error err = state.load(p_path,flags,p_flags&EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
+
+ ERR_FAIL_COND_V(err!=OK,NULL);
+
+ if (state.missing_textures.size()) {
+
+ for(int i=0;i<state.missing_textures.size();i++) {
+ EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]);
+ }
+
+ if (p_flags&IMPORT_FAIL_ON_MISSING_DEPENDENCIES)
+ return NULL;
+ }
+
+ if (p_flags&IMPORT_ANIMATION) {
+
+ state.create_animations();
+ AnimationPlayer *ap = memnew( AnimationPlayer );
+ for(int i=0;i<state.animations.size();i++) {
+ String name;
+ if (state.animations[i]->get_name()=="")
+ name="default";
+ else
+ name=state.animations[i]->get_name();
+
+ if (p_flags&IMPORT_ANIMATION_OPTIMIZE)
+ state.animations[i]->optimize();
+ if (p_flags&IMPORT_ANIMATION_DETECT_LOOP) {
+
+ if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
+ state.animations[i]->set_loop(true);
+ }
+ }
+
+ ap->add_animation(name,state.animations[i]);
+ }
+ state.scene->add_child(ap);
+ ap->set_owner(state.scene);
+
+ }
+
+ return state.scene;
+
+}
+
+Ref<Animation> EditorSceneImporterCollada::import_animation(const String& p_path,uint32_t p_flags) {
+
+
+ ColladaImport state;
+
+
+ Error err = state.load(p_path,Collada::IMPORT_FLAG_ANIMATION,p_flags&EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
+ ERR_FAIL_COND_V(err!=OK,RES());
+
+
+ state.create_animations();
+ if (state.scene)
+ memdelete(state.scene);
+
+ if (state.animations.size()==0)
+ return Ref<Animation>();
+ Ref<Animation> anim=state.animations[0];
+ anim=state.animations[0];
+ print_line("Anim Load OK");
+ String base = p_path.basename().to_lower();
+ if (p_flags&IMPORT_ANIMATION_DETECT_LOOP) {
+
+ if (base.begins_with("loop") || base.ends_with("loop") || base.begins_with("cycle") || base.ends_with("cycle")) {
+ anim->set_loop(true);
+ }
+ }
+
+ if (p_flags&IMPORT_ANIMATION_OPTIMIZE)
+ anim->optimize();
+
+ return anim;
+}
+
+
+EditorSceneImporterCollada::EditorSceneImporterCollada() {
+
+
+}
diff --git a/tools/editor/io_plugins/editor_import_collada.h b/tools/editor/io_plugins/editor_import_collada.h
new file mode 100644
index 000000000..239f05119
--- /dev/null
+++ b/tools/editor/io_plugins/editor_import_collada.h
@@ -0,0 +1,51 @@
+/*************************************************************************/
+/* editor_import_collada.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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_IMPORT_COLLADA_H
+#define EDITOR_IMPORT_COLLADA_H
+
+#include "tools/editor/io_plugins/editor_scene_import_plugin.h"
+
+
+
+class EditorSceneImporterCollada : public EditorSceneImporter {
+
+ OBJ_TYPE(EditorSceneImporterCollada,EditorSceneImporter );
+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,Error* r_err=NULL);
+ virtual Ref<Animation> import_animation(const String& p_path,uint32_t p_flags);
+
+ EditorSceneImporterCollada();
+};
+
+
+#endif
+
diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.cpp b/tools/editor/io_plugins/editor_sample_import_plugin.cpp
new file mode 100644
index 000000000..55cba432e
--- /dev/null
+++ b/tools/editor/io_plugins/editor_sample_import_plugin.cpp
@@ -0,0 +1,630 @@
+/*************************************************************************/
+/* editor_sample_import_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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 "scene/gui/file_dialog.h"
+#include "tools/editor/editor_dir_dialog.h"
+#include "tools/editor/editor_node.h"
+#include "tools/editor/property_editor.h"
+#include "scene/resources/sample.h"
+#include "io/resource_saver.h"
+#include "os/file_access.h"
+#include "io/marshalls.h"
+
+class _EditorSampleImportOptions : public Object {
+
+ OBJ_TYPE(_EditorSampleImportOptions,Object);
+public:
+
+ 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;
+
+ bool compress_enable;
+ 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/enable")
+ compress_enable=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/enable")
+ r_ret=compress_enable;
+ 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::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"));
+ }
+
+
+ _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_enable=false;
+ compress_bitrate=COMPRESS_128;
+ }
+
+
+};
+
+class EditorSampleImportDialog : public ConfirmationDialog {
+
+ OBJ_TYPE(EditorSampleImportDialog,ConfirmationDialog);
+
+ EditorSampleImportPlugin *plugin;
+
+ LineEdit *import_path;
+ LineEdit *save_path;
+ FileDialog *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));
+ 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("No samples to import!");
+ error_dialog->popup_centered(Size2(200,100));
+ }
+
+ 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("Save path is empty!");
+ error_dialog->popup_centered(Size2(200,100));
+ }
+
+ dst = dst.plus_file(samples[i].get_file().basename()+".smp");
+
+ Error err = plugin->import(dst,imd);
+ }
+
+ hide();
+
+ }
+
+
+ void _notification(int p_what) {
+
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ option_editor->edit(options);
+ }
+ }
+
+ static void _bind_methods() {
+
+
+ ObjectTypeDB::bind_method("_choose_files",&EditorSampleImportDialog::_choose_files);
+ ObjectTypeDB::bind_method("_choose_save_dir",&EditorSampleImportDialog::_choose_save_dir);
+ ObjectTypeDB::bind_method("_import",&EditorSampleImportDialog::_import);
+ ObjectTypeDB::bind_method("_browse",&EditorSampleImportDialog::_browse);
+ ObjectTypeDB::bind_method("_browse_target",&EditorSampleImportDialog::_browse_target);
+ // ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) );
+ }
+
+ EditorSampleImportDialog(EditorSampleImportPlugin *p_plugin) {
+
+ plugin=p_plugin;
+
+
+ set_title("Import Audio Samples");
+
+ VBoxContainer *vbc = memnew( VBoxContainer );
+ add_child(vbc);
+ set_child_rect(vbc);
+
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ vbc->add_margin_child("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("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(FileDialog);
+ file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ add_child(file_select);
+ file_select->set_mode(FileDialog::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(FileDialog::MODE_OPEN_DIR);
+ save_select->connect("dir_selected", this,"_choose_save_dir");
+
+ get_ok()->connect("pressed", this,"_import");
+ get_ok()->set_text("Import");
+
+
+ error_dialog = memnew ( ConfirmationDialog );
+ add_child(error_dialog);
+ error_dialog->get_ok()->set_text("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("Options:",option_editor,true);
+ }
+
+ ~EditorSampleImportDialog() {
+ memdelete(options);
+ }
+
+};
+
+
+String EditorSampleImportPlugin::get_name() const {
+
+ return "sample";
+}
+String EditorSampleImportPlugin::get_visible_name() const{
+
+ return "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);
+
+ {
+ DVector<uint8_t> src_data = smp->get_data();
+ DVector<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-i)];
+ float y1=data[ipos];
+ float y2=data[MIN(len-1,ipos+1)];
+ float y3=data[MIN(len-1,ipos+2)];
+
+ 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(-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;
+ }
+
+ 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;
+ }
+
+
+ DVector<uint8_t> dst_data;
+ dst_data.resize( data.size() * (is16?2:1));
+ {
+ DVector<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(is16?Sample::FORMAT_PCM16:Sample::FORMAT_PCM8,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;
+
+}
+
+
+EditorSampleImportPlugin::EditorSampleImportPlugin(EditorNode* p_editor) {
+
+ dialog = memnew( EditorSampleImportDialog(this));
+ p_editor->get_gui_base()->add_child(dialog);
+}
+
diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.h b/tools/editor/io_plugins/editor_sample_import_plugin.h
new file mode 100644
index 000000000..a5420671e
--- /dev/null
+++ b/tools/editor/io_plugins/editor_sample_import_plugin.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* editor_sample_import_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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
+
+#include "tools/editor/editor_import_export.h"
+#include "scene/resources/font.h"
+
+class EditorNode;
+class EditorSampleImportDialog;
+
+class EditorSampleImportPlugin : public EditorImportPlugin {
+
+ OBJ_TYPE(EditorSampleImportPlugin,EditorImportPlugin);
+
+ EditorSampleImportDialog *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);
+
+
+ EditorSampleImportPlugin(EditorNode* p_editor);
+};
+
+#endif // EDITOR_SAMPLE_IMPORT_PLUGIN_H
diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.cpp b/tools/editor/io_plugins/editor_scene_import_plugin.cpp
new file mode 100644
index 000000000..e7e3a4206
--- /dev/null
+++ b/tools/editor/io_plugins/editor_scene_import_plugin.cpp
@@ -0,0 +1,1599 @@
+/*************************************************************************/
+/* editor_scene_import_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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"
+#include "globals.h"
+#include "tools/editor/editor_node.h"
+#include "scene/resources/packed_scene.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/room_instance.h"
+#include "scene/3d/portal.h"
+
+
+
+
+
+EditorSceneImporter::EditorSceneImporter() {
+
+
+}
+
+void EditorScenePostImport::_bind_methods() {
+
+ BIND_VMETHOD( MethodInfo("post_import",PropertyInfo(Variant::OBJECT,"scene")) );
+
+}
+
+Error EditorScenePostImport::post_import(Node* p_scene) {
+
+ if (get_script_instance())
+ return Error(int(get_script_instance()->call("post_import",p_scene)));
+ return OK;
+}
+
+EditorScenePostImport::EditorScenePostImport() {
+
+
+}
+
+
+/////////////////////////////
+
+
+class EditorImportAnimationOptions : public VBoxContainer {
+
+ OBJ_TYPE( EditorImportAnimationOptions, VBoxContainer );
+
+
+ Tree *flags;
+ Vector<TreeItem*> items;
+
+ bool updating;
+
+ void _changed();
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+
+ void set_flags(uint32_t p_flags);
+ uint32_t get_flags() const;
+
+
+ EditorImportAnimationOptions();
+
+
+};
+
+////////////////////////////
+
+class EditorSceneImportDialog : public ConfirmationDialog {
+
+ OBJ_TYPE(EditorSceneImportDialog,ConfirmationDialog);
+
+
+ EditorImportTextureOptions *texture_options;
+ EditorImportAnimationOptions *animation_options;
+
+ EditorSceneImportPlugin *plugin;
+
+ EditorNode *editor;
+
+ LineEdit *import_path;
+ LineEdit *save_path;
+ LineEdit *script_path;
+ Tree *import_options;
+ FileDialog *file_select;
+ FileDialog *script_select;
+ EditorDirDialog *save_select;
+ OptionButton *texture_action;
+
+ Vector<TreeItem*> scene_flags;
+
+ Map<Ref<Mesh>,Ref<Shape> > collision_map;
+ ConfirmationDialog *error_dialog;
+
+ 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();
+
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ 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",
+ "Keep Value Tracks",
+ "Optimize",
+ 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.",
+ 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::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_changed",&EditorImportAnimationOptions::_changed);
+// ObjectTypeDB::bind_method("_changedp",&EditorImportAnimationOptions::_changedp);
+
+ ADD_SIGNAL(MethodInfo("changed"));
+}
+
+
+void EditorImportAnimationOptions::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ flags->connect("item_edited",this,"_changed");
+// format->connect("item_selected",this,"_changedp");
+ }
+}
+
+EditorImportAnimationOptions::EditorImportAnimationOptions() {
+
+
+ updating=false;
+
+ flags = memnew( Tree );
+ flags->set_hide_root(true);
+ 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++;
+ }
+
+
+ add_margin_child("Animation Options",flags,true);
+
+}
+
+
+
+
+
+
+
+////////////////////////////////////////////////////////
+
+
+
+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("");
+ //save_path->set_text(p_path.get_file().basename()+".scn");
+#if 0
+ }
+#endif
+
+ import_path->set_text(p_path);
+
+}
+void EditorSceneImportDialog::_choose_save_file(const String& p_path) {
+
+ save_path->set_text(p_path);
+}
+
+void EditorSceneImportDialog::_choose_script(const String& p_path) {
+
+ String p = Globals::get_singleton()->localize_path(p_path);
+ if (!p.is_resource_file())
+ p=Globals::get_singleton()->get_resource_path().path_to(p_path.get_base_dir())+p_path.get_file();
+ script_path->set_text(p);
+
+}
+
+void EditorSceneImportDialog::_import() {
+
+//' ImportMonitorBlock imb;
+
+ if (import_path->get_text()=="") {
+ error_dialog->set_text("Source path is empty.");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ if (save_path->get_text()=="") {
+ error_dialog->set_text("Target path is empty.");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ String dst_path;
+
+ if (texture_action->get_selected()==0)
+ dst_path=save_path->get_text();//.get_base_dir();
+ else
+ dst_path=Globals::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))
+ flags|=(1<<i);
+ }
+
+
+ Ref<EditorScenePostImport> pi;
+
+ if (script_path->get_text()!="") {
+ Ref<Script> scr = ResourceLoader::load(script_path->get_text());
+ if (!scr.is_valid()) {
+ error_dialog->set_text("Couldn't load Post-Import Script.");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ pi = Ref<EditorScenePostImport>( memnew( EditorScenePostImport ) );
+ pi->set_script(scr.get_ref_ptr());
+ if (!pi->get_script_instance()) {
+
+ error_dialog->set_text("Invalid/Broken Script for Post-Import.");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+ }
+
+
+ String save_file = save_path->get_text().plus_file(import_path->get_text().get_file().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);
+ 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("post_import_script",script_path->get_text()!=String()?EditorImportPlugin::validate_source_path(script_path->get_text()):String());
+ rim->set_option("reimport",true);
+
+ Error err = plugin->import(save_file,rim);
+
+ if (err) {
+
+ error_dialog->set_text("Error importing scene.");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ 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::_browse() {
+
+ file_select->popup_centered_ratio();
+}
+
+void EditorSceneImportDialog::_browse_target() {
+
+ if (save_path->get_text()!="")
+ save_select->set_current_path(save_path->get_text());
+ save_select->popup_centered_ratio();
+
+}
+
+void EditorSceneImportDialog::_browse_script() {
+
+ script_select->popup_centered_ratio();
+
+}
+
+void EditorSceneImportDialog::popup_import(const String &p_from) {
+
+ popup_centered(Size2(700,500));
+ 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++) {
+
+ scene_flags[i]->set_checked(0,flags&(1<<i));
+ }
+
+ 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"));
+ 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_SCENE) {
+
+
+ List<String> extensions;
+ file_select->clear_filters();
+
+ 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::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method("_choose_file",&EditorSceneImportDialog::_choose_file);
+ ObjectTypeDB::bind_method("_choose_save_file",&EditorSceneImportDialog::_choose_save_file);
+ ObjectTypeDB::bind_method("_choose_script",&EditorSceneImportDialog::_choose_script);
+ ObjectTypeDB::bind_method("_import",&EditorSceneImportDialog::_import);
+ ObjectTypeDB::bind_method("_browse",&EditorSceneImportDialog::_browse);
+ ObjectTypeDB::bind_method("_browse_target",&EditorSceneImportDialog::_browse_target);
+ ObjectTypeDB::bind_method("_browse_script",&EditorSceneImportDialog::_browse_script);
+ ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) );
+}
+
+
+
+static const char *scene_flag_names[]={
+ "Create Collisions (-col,-colonly)",
+ "Create Portals (-portal)",
+ "Create Rooms (-room)",
+ "Simplify Rooms",
+ "Create Billboards (-bb)",
+ "Create Impostors (-imp:dist)",
+ "Create LODs (-lod:dist)",
+ "Remove Nodes (-noimp)",
+ "Import Animations",
+ "Compress Geometry",
+ "Fail on Missing Images",
+ "Force Generation of Tangent Arrays",
+ NULL
+};
+
+
+EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSceneImportPlugin *p_plugin) {
+
+
+ editor=p_editor;
+ plugin=p_plugin;
+
+ set_title("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("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("Target Scene:",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("Same as Target Scene");
+ texture_action->add_item("Shared");
+ texture_action->select(0);
+ vbc->add_margin_child("Target Texture Folder:",texture_action);
+
+ import_options = memnew( Tree );
+ vbc->set_v_size_flags(SIZE_EXPAND_FILL);
+ vbc->add_margin_child("Options:",import_options,true);
+
+ file_select = memnew(FileDialog);
+ file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ add_child(file_select);
+
+
+ file_select->set_mode(FileDialog::MODE_OPEN_FILE);
+
+ file_select->connect("file_selected", this,"_choose_file");
+
+ save_select = memnew(EditorDirDialog);
+ add_child(save_select);
+
+ //save_select->set_mode(FileDialog::MODE_SAVE_FILE);
+ save_select->connect("dir_selected", this,"_choose_save_file");
+
+ get_ok()->connect("pressed", this,"_import");
+ get_ok()->set_text("Import");
+
+ TreeItem *root = import_options->create_item(NULL);
+ import_options->set_hide_root(true);
+
+
+
+
+ TreeItem *importopts = import_options->create_item(root);
+ importopts->set_text(0,"Import:");
+
+ const char ** fn=scene_flag_names;
+
+ while(*fn) {
+
+ TreeItem *opt = import_options->create_item(importopts);
+ opt->set_cell_mode(0,TreeItem::CELL_MODE_CHECK);
+ opt->set_checked(0,true);
+ opt->set_editable(0,true);
+ opt->set_text(0,*fn);
+ scene_flags.push_back(opt);
+ fn++;
+ }
+
+ hbc = memnew( HBoxContainer );
+ vbc->add_margin_child("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(FileDialog);
+ 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(FileDialog::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("Accept");
+// error_dialog->get_cancel()->hide();
+
+ set_hide_on_ok(false);
+
+ 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));
+
+ 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);
+
+ 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);
+
+ //texture_options->set_format(EditorImport::IMAGE_FORMAT_C);
+
+}
+
+
+
+////////////////////////////////
+
+
+
+String EditorSceneImportPlugin::get_name() const {
+
+ return "scene_3d";
+}
+
+String EditorSceneImportPlugin::get_visible_name() const{
+
+ return "3D 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;
+ 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));
+ return p_what;
+}
+
+
+
+void EditorSceneImportPlugin::_find_resources(const Variant& p_var,Set<Ref<ImageTexture> >& image_map) {
+
+
+ switch(p_var.get_type()) {
+
+ case Variant::OBJECT: {
+
+ Ref<Resource> res = p_var;
+ if (res.is_valid()) {
+
+ if (res->is_type("Texture")) {
+
+ image_map.insert(res);
+
+
+ } 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) {
+ _find_resources(res->get(E->get().name),image_map);
+ }
+ }
+
+ }
+ }
+
+ } 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);
+ _find_resources(d[E->get()],image_map);
+
+ }
+
+
+ } break;
+ case Variant::ARRAY: {
+
+ Array a = p_var;
+ for(int i=0;i<a.size();i++) {
+
+ _find_resources(a[i],image_map);
+ }
+
+ } break;
+
+ }
+
+}
+
+
+Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map,uint32_t p_flags,Set<Ref<ImageTexture> >& 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);
+ }
+ }
+
+ }
+
+
+
+
+ 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<FixedMaterial> 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(FixedMaterial::FLAG_USE_ALPHA,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<FixedMaterial> 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(FixedMaterial::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<FixedMaterial> 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(FixedMaterial::FLAG_USE_ALPHA,true);
+ }
+ }
+ }*/
+ }
+ }
+ }
+ }
+ if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(name,"colonly") && p_node->cast_to<MeshInstance>()) {
+
+ if (isroot)
+ return p_node;
+
+ 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;
+
+ } 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"));
+ mi->create_trimesh_collision();
+ } 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>();
+ DVector<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>();
+ DVector<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].atan2();
+ float b = portal_points[i+1].atan2();
+
+ 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
+ }
+
+ }
+
+ }
+
+ }
+
+
+
+
+ return p_node;
+}
+
+
+void EditorSceneImportPlugin::_merge_node(Node *p_node,Node*p_root,Node *p_existing,Set<Ref<Resource> >& checked_resources) {
+
+
+ NodePath path = p_root->get_path_to(p_node);
+
+ bool valid=false;
+
+ if (p_existing->has_node(path)) {
+
+ Node *existing = p_existing->get_node(path);
+
+ if (existing->get_type()==p_node->get_type()) {
+ //same thing, check what it is
+
+ if (existing->get_type()=="MeshInstance") {
+
+ //merge mesh instance, this is a special case!
+ MeshInstance *mi_existing=existing->cast_to<MeshInstance>();
+ MeshInstance *mi_node=p_node->cast_to<MeshInstance>();
+
+ Ref<Mesh> mesh_existing = mi_existing->get_mesh();
+ Ref<Mesh> mesh_node = mi_node->get_mesh();
+
+ if (mesh_existing.is_null() || checked_resources.has(mesh_node)) {
+
+ if (mesh_node.is_valid())
+ mi_existing->set_mesh(mesh_node);
+ } else if (mesh_node.is_valid()) {
+
+ //mesh will always be overwritten, so check materials from original
+
+ for(int i=0;i<mesh_node->get_surface_count();i++) {
+
+ String name = mesh_node->surface_get_name(i);
+
+ if (name!="") {
+
+ for(int j=0;j<mesh_existing->get_surface_count();j++) {
+
+ Ref<Material> keep;
+
+ if (name==mesh_existing->surface_get_name(j)) {
+
+ Ref<Material> mat = mesh_existing->surface_get_material(j);
+
+ if (mat.is_valid()) {
+ if (mat->get_path()!="" && mat->get_path().begins_with("res://") && mat->get_path().find("::")==-1) {
+ keep=mat; //mat was loaded from file
+ } else if (mat->is_edited()) {
+ keep=mat; //mat was edited
+ }
+ }
+ break;
+ }
+ if (keep.is_valid())
+ mesh_node->surface_set_material(i,keep); //kept
+ }
+ }
+ }
+
+ mi_existing->set_mesh(mesh_node); //always overwrite mesh
+ checked_resources.insert(mesh_node);
+
+ }
+ } else if (existing->get_type()=="Path") {
+
+ Path *path_existing =existing->cast_to<Path>();
+ Path *path_node =p_node->cast_to<Path>();
+
+ if (path_node->get_curve().is_valid()) {
+
+ if (!path_existing->get_curve().is_valid() || !path_existing->get_curve()->is_edited()) {
+ path_existing->set_curve(path_node->get_curve());
+ }
+ }
+ }
+ }
+
+ valid=true;
+ } else {
+
+ if (p_node!=p_root && p_existing->has_node(p_root->get_path_to(p_node->get_parent()))) {
+
+ Node *parent = p_existing->get_node(p_root->get_path_to(p_node->get_parent()));
+ NodePath path = p_root->get_path_to(p_node);
+
+ //add it.. because not existing in existing scene
+ Object *o = ObjectTypeDB::instance(p_existing->get_type());
+ Node *n=NULL;
+ if (o)
+ n=o->cast_to<Node>();
+
+ if (n) {
+
+ List<PropertyInfo> pl;
+ p_existing->get_property_list(&pl);
+ for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
+ if (!(E->get().usage&PROPERTY_USAGE_STORAGE))
+ continue;
+ n->set( E->get().name, p_existing->get(E->get().name) );
+ }
+
+ parent->add_child(n);
+
+ valid=true;
+ }
+ }
+
+ }
+
+
+ if (valid) {
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+ _merge_node(p_node->get_child(i),p_root,p_existing,checked_resources);
+ }
+ }
+
+}
+
+
+void EditorSceneImportPlugin::_merge_scenes(Node *p_existing,Node *p_new) {
+
+ Set<Ref<Resource> > checked_resources;
+ _merge_node(p_new,p_new,p_existing,checked_resources);
+
+}
+
+#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
+
+Error EditorSceneImportPlugin::import(const String& p_dest_path, const Ref<ResourceImportMetadata>& p_from){
+
+ 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.extension().to_lower();
+
+
+ EditorNode::progress_add_task("import","Import Scene",104);
+ 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;
+ }
+
+ if (!importer.is_valid()) {
+ EditorNode::progress_end_task("import");
+ }
+ 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");
+
+ uint32_t import_flags=0;
+ if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_DETECT_LOOP)
+ import_flags|=EditorSceneImporter::IMPORT_ANIMATION_DETECT_LOOP;
+ if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE)
+ import_flags|=EditorSceneImporter::IMPORT_ANIMATION_OPTIMIZE;
+ 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;
+
+
+
+ EditorNode::progress_task_step("import","Importing Scene..",0);
+
+
+ Error err=OK;
+ Node *scene = importer->import_scene(src_path,import_flags,&err);
+ if (!scene || err!=OK) {
+ EditorNode::progress_end_task("import");
+ return err;
+ }
+
+
+ bool merge = !bool(from->get_option("reimport"));
+ 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;
+
+ Ref<ResourceImportMetadata> imd = memnew(ResourceImportMetadata);
+
+ Set< Ref<ImageTexture> > imagemap;
+ EditorNode::progress_task_step("import","Post-Processing Scene..",1);
+
+
+
+ scene=_fix_node(scene,scene,collision_map,scene_flags,imagemap);
+
+
+ /// BEFORE ANYTHING, RUN SCRIPT
+
+ EditorNode::progress_task_step("import","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 = EditorImportPlugin::expand_source_path(post_import_script_path);
+ Ref<Script> scr = ResourceLoader::load(post_import_script_path);
+ if (!scr.is_valid()) {
+ EditorNode::add_io_error("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("Invalid/Broken Script for Post-Import: '"+post_import_script_path);
+ post_import_script.unref();
+ }
+ }
+ }
+
+
+ if (post_import_script.is_valid()) {
+ err = post_import_script->post_import(scene);
+ if (err) {
+ EditorNode::add_io_error("Error running Post-Import script: '"+post_import_script_path);
+ EditorNode::progress_end_task("import");
+ 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 (Set< Ref<ImageTexture> >::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->get();
+
+ ERR_CONTINUE(!texture.is_valid());
+
+ String path = texture->get_path();
+ String fname= path.get_file();
+ String target_path = Globals::get_singleton()->localize_path(target_res_path.plus_file(fname));
+ EditorNode::progress_task_step("import","Import Img: "+fname,3+(idx)*100/imagemap.size());
+
+ idx++;
+
+ if (path==target_path) {
+
+ EditorNode::add_io_error("Can't import a file over itself: '"+target_path);
+ continue;
+ }
+
+ if (!target_path.begins_with("res://")) {
+ EditorNode::add_io_error("Couldn't localize path: '"+target_path+"' (already local)");
+ continue;
+ }
+
+
+ {
+
+
+ target_path=target_path.basename()+".tex";
+
+ if (FileAccess::exists(target_path)) {
+ texture->set_path(target_path);
+ continue; //already imported
+ }
+ Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata );
+ imd->set_option("flags",image_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));
+
+ Error err = EditorTextureImportPlugin::get_singleton(EditorTextureImportPlugin::MODE_TEXTURE_3D)->import(target_path,imd);
+
+ }
+ }
+
+
+ /// BEFORE SAVING - MERGE
+
+
+ if (merge) {
+
+ EditorNode::progress_task_step("import","Merging..",103);
+
+ FileAccess *fa = FileAccess::create(FileAccess::ACCESS_FILESYSTEM);
+ if (fa->file_exists(p_dest_path)) {
+
+ //try to merge
+
+ Ref<PackedScene> s = ResourceLoader::load(p_dest_path);
+ if (s.is_valid()) {
+
+ Node *existing = s->instance(true);
+
+ if (existing) {
+
+ _merge_scenes(scene,existing);
+
+ memdelete(scene);
+ scene=existing;
+ }
+ }
+
+ }
+
+ memdelete(fa);
+ }
+
+
+ EditorNode::progress_task_step("import","Saving..",104);
+
+ Ref<PackedScene> packer = memnew( PackedScene );
+ packer->pack(scene);
+ packer->set_path(p_dest_path);
+ packer->set_import_metadata(from);
+
+ print_line("SAVING TO: "+p_dest_path);
+ err = ResourceSaver::save(p_dest_path,packer);
+
+ //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::progress_end_task("import");
+
+ return err;
+
+}
+
+void EditorSceneImportPlugin::add_importer(const Ref<EditorSceneImporter>& p_importer) {
+
+ importers.push_back(p_importer);
+}
+
+
+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 "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) {
+
+
+}
+
diff --git a/tools/editor/io_plugins/editor_scene_import_plugin.h b/tools/editor/io_plugins/editor_scene_import_plugin.h
new file mode 100644
index 000000000..d4c96b97d
--- /dev/null
+++ b/tools/editor/io_plugins/editor_scene_import_plugin.h
@@ -0,0 +1,167 @@
+/*************************************************************************/
+/* editor_scene_import_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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
+
+#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 "tools/editor/editor_file_system.h"
+#include "tools/editor/editor_dir_dialog.h"
+#include "tools/editor/editor_import_export.h"
+#include "tools/editor/io_plugins/editor_texture_import_plugin.h"
+#include "scene/resources/animation.h"
+
+class EditorNode;
+class EditorSceneImportDialog;
+
+class EditorSceneImporter : public Reference {
+
+ OBJ_TYPE(EditorSceneImporter,Reference );
+public:
+
+ enum ImportFlags {
+ IMPORT_SCENE=1,
+ IMPORT_ANIMATION=2,
+ IMPORT_ANIMATION_DETECT_LOOP=4,
+ IMPORT_ANIMATION_OPTIMIZE=8,
+ IMPORT_GENERATE_TANGENT_ARRAYS=16,
+ IMPORT_FAIL_ON_MISSING_DEPENDENCIES=128
+
+ };
+
+ 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,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 {
+
+ OBJ_TYPE(EditorScenePostImport,Reference );
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual Error post_import(Node* p_scene);
+ EditorScenePostImport();
+};
+
+
+class EditorSceneImportPlugin : public EditorImportPlugin {
+
+ OBJ_TYPE(EditorSceneImportPlugin,EditorImportPlugin);
+
+ EditorSceneImportDialog *dialog;
+
+ Vector<Ref<EditorSceneImporter> > importers;
+
+ void _find_resources(const Variant& p_var,Set<Ref<ImageTexture> >& image_map);
+ Node* _fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map,uint32_t p_flags,Set<Ref<ImageTexture> >& image_map);
+ void _merge_node(Node *p_node,Node*p_root,Node *p_existing,Set<Ref<Resource> >& checked_resources);
+ void _merge_scenes(Node *p_existing,Node *p_new);
+
+
+public:
+
+ enum SceneFlags {
+
+ SCENE_FLAG_CREATE_COLLISIONS=1,
+ SCENE_FLAG_CREATE_PORTALS=2,
+ SCENE_FLAG_CREATE_ROOMS=4,
+ SCENE_FLAG_SIMPLIFY_ROOMS=8,
+ SCENE_FLAG_CREATE_BILLBOARDS=16,
+ SCENE_FLAG_CREATE_IMPOSTORS=32,
+ SCENE_FLAG_CREATE_LODS=64,
+ SCENE_FLAG_REMOVE_NOIMP=128,
+ SCENE_FLAG_IMPORT_ANIMATIONS=256,
+ SCENE_FLAG_COMPRESS_GEOMETRY=512,
+ SCENE_FLAG_FAIL_ON_MISSING_IMAGES=1024,
+ SCENE_FLAG_GENERATE_TANGENT_ARRAYS=2048,
+ SCENE_FLAG_DONT_SAVE_TO_DB=8192
+ };
+
+
+ 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 add_importer(const Ref<EditorSceneImporter>& p_importer);
+ const Vector<Ref<EditorSceneImporter> >& get_importers() { return importers; }
+
+ EditorSceneImportPlugin(EditorNode* p_editor=NULL);
+
+
+};
+
+
+class EditorSceneAnimationImportPlugin : public EditorImportPlugin {
+
+ OBJ_TYPE(EditorSceneAnimationImportPlugin,EditorImportPlugin);
+public:
+
+
+ enum AnimationFlags {
+
+ ANIMATION_DETECT_LOOP=1,
+ ANIMATION_KEEP_VALUE_TRACKS=2,
+ ANIMATION_OPTIMIZE=4
+ };
+
+ 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 // EDITOR_SCENE_IMPORT_PLUGIN_H
diff --git a/tools/editor/io_plugins/editor_texture_import_plugin.cpp b/tools/editor/io_plugins/editor_texture_import_plugin.cpp
new file mode 100644
index 000000000..7c506a33b
--- /dev/null
+++ b/tools/editor/io_plugins/editor_texture_import_plugin.cpp
@@ -0,0 +1,1177 @@
+/*************************************************************************/
+/* editor_texture_import_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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"
+#include "io/image_loader.h"
+#include "tools/editor/editor_node.h"
+#include "io/resource_saver.h"
+#include "editor_atlas.h"
+#include "tools/editor/editor_settings.h"
+#include "io/md5.h"
+#include "io/marshalls.h"
+#include "globals.h"
+
+static const char *flag_names[]={
+ "Streaming Format",
+ "Fix Border Alpha",
+ "Alpha Bit Hint",
+ "Compress Extra (PVRTC2)",
+ "No MipMaps",
+ "Repeat",
+ "Filter (Magnifying)",
+ NULL
+};
+
+static const char *flag_short_names[]={
+ "Stream",
+ "FixBorder",
+ "AlphBit",
+ "ExtComp",
+ "NoMipMap",
+ "Repeat",
+ "Filter",
+ NULL
+};
+
+
+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_val(p_quality);
+}
+
+float EditorImportTextureOptions::get_quality() const {
+
+ return quality->get_val();
+}
+
+
+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() {
+
+ ObjectTypeDB::bind_method("_changed",&EditorImportTextureOptions::_changed);
+ ObjectTypeDB::bind_method("_changedp",&EditorImportTextureOptions::_changedp);
+
+ ADD_SIGNAL(MethodInfo("changed"));
+}
+
+
+void EditorImportTextureOptions::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ flags->connect("item_edited",this,"_changed");
+ format->connect("item_selected",this,"_changedp");
+ }
+}
+
+EditorImportTextureOptions::EditorImportTextureOptions() {
+
+
+ updating=false;
+ format = memnew( OptionButton );
+
+ format->add_item("Uncompressed",EditorTextureImportPlugin::IMAGE_FORMAT_UNCOMPRESSED);
+ format->add_item("Compress Lossless (PNG)",EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSLESS);
+ format->add_item("Compress Lossy (WebP)",EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_DISK_LOSSY);
+ format->add_item("Compress (VRAM)",EditorTextureImportPlugin::IMAGE_FORMAT_COMPRESS_RAM);
+
+
+ add_margin_child("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_val(0.7);
+ quality=hs;
+ quality_vb->add_margin_child("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("Texture Options",flags,true);
+}
+
+///////////////////////////////////////////////////////////
+
+
+
+
+class EditorTextureImportDialog : public ConfirmationDialog {
+
+ OBJ_TYPE(EditorTextureImportDialog,ConfirmationDialog);
+
+
+ EditorImportTextureOptions *texture_options;
+
+ //EditorNode *editor;
+
+ LineEdit *import_path;
+ LineEdit *save_path;
+ FileDialog *file_select;
+ FileDialog *save_file_select;
+ EditorDirDialog *save_select;
+ OptionButton *texture_action;
+ ConfirmationDialog *error_dialog;
+ CheckButton *crop_source;
+ bool atlas;
+
+ EditorTextureImportPlugin *plugin;
+
+ void _choose_files(const Vector<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:
+
+ 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,bool p_2d=false,bool p_atlas=false);
+};
+
+
+/////////////////////////////////////////////////////////
+
+
+
+
+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_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("Please specify some files!");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+ }
+
+ String dst_path=save_path->get_text();
+
+ if (dst_path.empty()) {
+
+ error_dialog->set_text("Please specify a valid target import path!");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+
+ }
+
+ if (atlas) { //atlas
+
+ if (files.size()==0) {
+
+ error_dialog->set_text("At least one file needed for Atlas.");
+ error_dialog->popup_centered(Size2(200,100));
+ 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("crop",crop_source->is_pressed());
+
+ Error err = plugin->import(dst_file,imd);
+ if (err) {
+
+ error_dialog->set_text("Error importing: "+dst_file.get_file());
+ error_dialog->popup_centered(Size2(200,100));
+ 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.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);
+ Error err = plugin->import(dst_file,imd);
+ if (err) {
+
+ error_dialog->set_text("Error importing: "+dst_file.get_file());
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+
+ }
+ }
+ }
+
+ hide();
+}
+
+void EditorTextureImportDialog::_browse() {
+
+ file_select->popup_centered_ratio();
+}
+
+void EditorTextureImportDialog::_browse_target() {
+
+ if (atlas) {
+ save_file_select->popup_centered_ratio();
+ } else {
+ save_select->popup_centered_ratio();
+ }
+
+}
+
+
+void EditorTextureImportDialog::popup_import(const String& p_from) {
+
+ popup_centered(Size2(400,400));
+ if (p_from!="") {
+ Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_from);
+ ERR_FAIL_COND(!rimd.is_valid());
+
+ 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_SCENE) {
+
+
+ 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::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method("_choose_files",&EditorTextureImportDialog::_choose_files);
+ ObjectTypeDB::bind_method("_choose_save_dir",&EditorTextureImportDialog::_choose_save_dir);
+ ObjectTypeDB::bind_method("_import",&EditorTextureImportDialog::_import);
+ ObjectTypeDB::bind_method("_browse",&EditorTextureImportDialog::_browse);
+ ObjectTypeDB::bind_method("_browse_target",&EditorTextureImportDialog::_browse_target);
+// ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) );
+}
+
+EditorTextureImportDialog::EditorTextureImportDialog(EditorTextureImportPlugin* p_plugin, bool p_2d, bool p_atlas) {
+
+
+ atlas=p_atlas;
+ plugin=p_plugin;
+ set_title("Import Textures");
+
+ texture_options = memnew( EditorImportTextureOptions );;
+ VBoxContainer *vbc = texture_options;
+ add_child(vbc);
+ set_child_rect(vbc);
+
+
+ VBoxContainer *source_vb=memnew(VBoxContainer);
+ vbc->add_margin_child("Source Texture(s):",source_vb);
+
+ 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("Crop empty space.");
+ if (!p_atlas)
+ crop_source->hide();
+
+ 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("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(FileDialog);
+ file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ add_child(file_select);
+ file_select->set_mode(FileDialog::MODE_OPEN_FILES);
+ file_select->connect("files_selected", this,"_choose_files");
+
+ save_file_select = memnew(FileDialog);
+ save_file_select->set_access(FileDialog::ACCESS_RESOURCES);
+ add_child(save_file_select);
+ save_file_select->set_mode(FileDialog::MODE_SAVE_FILE);
+ save_file_select->clear_filters();
+ save_file_select->add_filter("*.tex;Base Atlas Texture");
+ save_file_select->connect("file_selected", this,"_choose_save_dir");
+
+ save_select = memnew( EditorDirDialog );
+ add_child(save_select);
+
+// save_select->set_mode(FileDialog::MODE_OPEN_DIR);
+ save_select->connect("dir_selected", this,"_choose_save_dir");
+
+ get_ok()->connect("pressed", this,"_import");
+ get_ok()->set_text("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("Accept");
+// error_dialog->get_cancel()->hide();
+
+ set_hide_on_ok(false);
+
+ if (atlas) {
+
+ 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("Import Textures for Atlas (2D)");
+
+ } else if (p_2d) {
+
+ 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);
+ set_title("Import Textures for 2D");
+ } else {
+
+ //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("Import Textures for 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 {
+
+ 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;
+
+ }
+
+ return "";
+
+}
+String EditorTextureImportPlugin::get_visible_name() const {
+
+ switch(mode) {
+ case MODE_TEXTURE_2D: {
+
+ return "2D Texture";
+ } break;
+ case MODE_TEXTURE_3D: {
+
+ return "3D Texture";
+
+ } break;
+ case MODE_ATLAS: {
+
+ return "Atlas Teture";
+ } break;
+
+ }
+
+ return "";
+
+}
+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_INDEXED: {
+
+ //quantize
+ image.quantize();
+
+ } break;
+ case EditorExportPlatform::IMAGE_COMPRESSION_BC: {
+
+
+ // for maximum compatibility, BC shall always use mipmaps and be PO2
+ image.resize_to_po2();
+ if (image.get_mipmaps()==0)
+ image.generate_mipmaps();
+
+ image.compress(Image::COMPRESS_BC);
+ /*
+ if (has_alpha) {
+
+ if (flags&IMAGE_FLAG_ALPHA_BIT) {
+ image.convert(Image::FORMAT_BC3);
+ } else {
+ image.convert(Image::FORMAT_BC2);
+ }
+ } else {
+
+ image.convert(Image::FORMAT_BC1);
+ }*/
+
+
+ } 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.get_mipmaps()==0)
+ image.generate_mipmaps();
+ image.resize_to_po2(true);
+
+ if (p_smaller) {
+
+ image.compress(Image::COMPRESS_PVRTC2);
+ //image.convert(has_alpha ? Image::FORMAT_PVRTC2_ALPHA : Image::FORMAT_PVRTC2);
+ } else {
+ image.compress(Image::COMPRESS_PVRTC4);
+ //image.convert(has_alpha ? Image::FORMAT_PVRTC4_ALPHA : Image::FORMAT_PVRTC4);
+ }
+
+ } break;
+ case EditorExportPlatform::IMAGE_COMPRESSION_ETC1: {
+
+ image.resize_to_po2(); //square or not?
+ if (image.get_mipmaps()==0)
+ 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::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");
+
+ int flags=from->get_option("flags");
+ 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;
+
+ int shrink=1;
+ if (from->has_option("shrink"))
+ shrink=from->get_option("shrink");
+
+ if (atlas) {
+
+ //prepare atlas!
+ Vector< Image > sources;
+ bool alpha=false;
+ bool crop = from->get_option("crop");
+
+ EditorProgress ep("make_atlas","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));
+ ep.step("Loading Image: "+path,i);
+ print_line("source path: "+path);
+ Image src;
+ Error err = ImageLoader::load_image(path,&src);
+ if (err) {
+ EditorNode::add_io_error("Couldn't load image: "+path);
+ return err;
+ }
+
+ if (src.detect_alpha())
+ alpha=true;
+
+ sources.push_back(src);
+ }
+ ep.step("Converting Images",sources.size());
+
+ for(int i=0;i<sources.size();i++) {
+
+ if (alpha) {
+ sources[i].convert(Image::FORMAT_RGBA);
+ } else {
+ sources[i].convert(Image::FORMAT_RGB);
+ }
+ }
+
+ //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("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("Blitting Images",sources.size()+2);
+
+ Image atlas;
+ atlas.create(nearest_power_of_2(dst_size.width),nearest_power_of_2(dst_size.height),0,alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB);
+
+ for(int i=0;i<sources.size();i++) {
+
+ int x=dst_positions[i].x;
+ int y=dst_positions[i].y;
+
+ Ref<AtlasTexture> at = memnew( AtlasTexture );
+ Size2 sz = Size2(sources[i].get_width(),sources[i].get_height());
+ if (crop && sz!=crops[i].size) {
+ Rect2 rect = crops[i];
+ rect.size=sz-rect.size;
+ at->set_region(Rect2(x+border,y+border,crops[i].size.width,crops[i].size.height));
+ at->set_margin(rect);
+ atlas.blit_rect(sources[i],crops[i],Point2(x+border,y+border));
+ } else {
+ at->set_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));
+ }
+ String apath = p_path.get_base_dir().plus_file(from->get_source_path(i).get_file().basename()+".atex");
+ print_line("Atlas Tex: "+apath);
+ at->set_path(apath);
+ atlases.push_back(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));
+
+ }
+
+
+ int format=from->get_option("format");
+ float quality=from->get_option("quality");
+
+ 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(true);
+ 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("Couldn't save atlas image: "+apath);
+ return err;
+ }
+ from->set_source_md5(i,FileAccess::get_md5(apath));
+ }
+ }
+ }
+
+ 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_RGBA) {
+
+ image.convert(Image::FORMAT_RGB);
+
+ }
+
+ if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) {
+
+ image.fix_alpha_edges();
+ }
+
+
+ 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);
+
+ Error err = ResourceSaver::save(p_path,texture);
+
+ if (err!=OK) {
+ EditorNode::add_io_error("Couldn't save converted texture: "+p_path);
+ return err;
+ }
+
+
+ } 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_RGBA) {
+
+ image.convert(Image::FORMAT_RGB);
+
+ }
+
+ if (image.get_format()==Image::FORMAT_RGBA && flags&IMAGE_FLAG_FIX_BORDER_ALPHA) {
+
+ image.fix_alpha_edges();
+ }
+
+ 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) {
+ texture->set_size_override(Size2(orig_w,orig_h));
+ }
+
+ Error err = ResourceSaver::save(p_path,texture);
+ if (err!=OK) {
+ EditorNode::add_io_error("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);
+
+ 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
+ }
+
+
+ int flags=0;
+
+ if (Globals::get_singleton()->get("texture_import/filter"))
+ flags|=IMAGE_FLAG_FILTER;
+ if (!Globals::get_singleton()->get("texture_import/gen_mipmaps"))
+ flags|=IMAGE_FLAG_NO_MIPMAPS;
+ if (!Globals::get_singleton()->get("texture_import/repeat"))
+ flags|=IMAGE_FLAG_REPEAT;
+
+ flags|=IMAGE_FLAG_FIX_BORDER_ALPHA;
+
+ 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(EditorImportPlugin::validate_source_path(p_path));
+
+ } else if (EditorImportExport::get_singleton()->get_image_formats().has(p_path.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;
+ }
+
+ int flags=0;
+
+ if (Globals::get_singleton()->get("texture_import/filter"))
+ flags|=IMAGE_FLAG_FILTER;
+ if (!Globals::get_singleton()->get("texture_import/gen_mipmaps"))
+ flags|=IMAGE_FLAG_NO_MIPMAPS;
+ if (!Globals::get_singleton()->get("texture_import/repeat"))
+ flags|=IMAGE_FLAG_REPEAT;
+
+ flags|=IMAGE_FLAG_FIX_BORDER_ALPHA;
+
+ rimd->set_option("flags",flags);
+ rimd->set_option("quality",EditorImportExport::get_singleton()->get_export_image_quality());
+ rimd->set_option("atlas",false);
+ rimd->add_source(EditorImportPlugin::validate_source_path(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");
+
+ MD5_CTX ctx;
+ uint8_t f4[4];
+ encode_uint32(flags,&f4[0]);
+ uint8_t ic = p_platform->get_image_compression();
+ MD5Init(&ctx);
+ String gp = Globals::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,&ic,1);
+
+ MD5Final(&ctx);
+
+ uint64_t sd=0;
+ String smd5;
+
+ String md5 = String::md5(ctx.digest);
+
+ 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;
+}
+
+
+EditorTextureImportPlugin *EditorTextureImportPlugin::singleton[3]={NULL,NULL,NULL};
+
+EditorTextureImportPlugin::EditorTextureImportPlugin(EditorNode *p_editor, Mode p_mode) {
+
+ singleton[p_mode]=this;
+ editor=p_editor;
+ mode=p_mode;
+ dialog = memnew( EditorTextureImportDialog(this,p_mode==MODE_TEXTURE_2D || p_mode==MODE_ATLAS,p_mode==MODE_ATLAS) );
+ editor->get_gui_base()->add_child(dialog);
+
+}
diff --git a/tools/editor/io_plugins/editor_texture_import_plugin.h b/tools/editor/io_plugins/editor_texture_import_plugin.h
new file mode 100644
index 000000000..1c4df1a4d
--- /dev/null
+++ b/tools/editor/io_plugins/editor_texture_import_plugin.h
@@ -0,0 +1,147 @@
+/*************************************************************************/
+/* editor_texture_import_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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
+
+#include "tools/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 "tools/editor/editor_file_system.h"
+#include "tools/editor/editor_dir_dialog.h"
+
+
+
+class EditorNode;
+class EditorTextureImportDialog;
+
+class EditorTextureImportPlugin : public EditorImportPlugin {
+
+ OBJ_TYPE(EditorTextureImportPlugin,EditorImportPlugin);
+public:
+
+
+ enum Mode {
+ MODE_TEXTURE_2D,
+ MODE_TEXTURE_3D,
+ MODE_ATLAS
+ };
+
+
+
+private:
+ Mode mode;
+ EditorNode *editor;
+ EditorTextureImportDialog *dialog;
+ static EditorTextureImportPlugin *singleton[3];
+ //used by other importers such as mesh
+
+
+ void compress_image(EditorExportPlatform::ImageCompression p_mode,Image& image,bool p_smaller);
+public:
+
+
+ static EditorTextureImportPlugin *get_singleton(Mode p_mode) { return singleton[p_mode]; }
+
+ 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
+ };
+
+ 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);
+
+
+ EditorTextureImportPlugin(EditorNode* p_editor=NULL,Mode p_mode=MODE_TEXTURE_2D);
+};
+
+
+class EditorImportTextureOptions : public VBoxContainer {
+
+ OBJ_TYPE( 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;
+
+ EditorImportTextureOptions();
+
+
+};
+#endif // EDITOR_TEXTURE_IMPORT_PLUGIN_H
diff --git a/tools/editor/io_plugins/editor_translation_import_plugin.cpp b/tools/editor/io_plugins/editor_translation_import_plugin.cpp
new file mode 100644
index 000000000..89a7584f7
--- /dev/null
+++ b/tools/editor/io_plugins/editor_translation_import_plugin.cpp
@@ -0,0 +1,457 @@
+/*************************************************************************/
+/* editor_translation_import_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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"
+#include "scene/gui/file_dialog.h"
+#include "tools/editor/editor_dir_dialog.h"
+#include "tools/editor/editor_node.h"
+#include "tools/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 "tools/editor/project_settings.h"
+
+
+class EditorTranslationImportDialog : public ConfirmationDialog {
+
+ OBJ_TYPE(EditorTranslationImportDialog,ConfirmationDialog);
+
+ EditorTranslationImportPlugin *plugin;
+
+ LineEdit *import_path;
+ LineEdit *save_path;
+ FileDialog *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("Invalid source!");
+ error_dialog->popup_centered(Size2(200,100));
+ return;
+
+ }
+
+ Vector<String> csvh = f->get_csv_line();
+ memdelete(f);
+
+ if (csvh.size()<2) {
+
+ error_dialog->set_text("Invalid translation source!");
+ error_dialog->popup_centered(Size2(200,100));
+ 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,"Column");
+ columns->set_column_title(1,"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));
+
+ 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("No items to import!");
+ error_dialog->popup_centered(Size2(200,100));
+ }
+
+ if (!save_path->get_text().begins_with("res://")) {
+ error_dialog->set_text("No target path!!");
+ error_dialog->popup_centered(Size2(200,100));
+ }
+
+ EditorProgress progress("import_xl","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().basename()+"."+locale+".xl");
+ Error err = plugin->import(savefile,imd);
+ if (err!=OK) {
+ error_dialog->set_text("Couldnt import!");
+ error_dialog->popup_centered(Size2(200,100));
+ } else if (add_to_project->is_pressed()) {
+
+ ProjectSettings::get_singleton()->add_translation(savefile);
+ }
+ }
+ hide();
+
+ }
+
+
+ void _notification(int p_what) {
+
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+
+ }
+ }
+
+ static void _bind_methods() {
+
+
+ ObjectTypeDB::bind_method("_choose_file",&EditorTranslationImportDialog::_choose_file);
+ ObjectTypeDB::bind_method("_choose_save_dir",&EditorTranslationImportDialog::_choose_save_dir);
+ ObjectTypeDB::bind_method("_import",&EditorTranslationImportDialog::_import);
+ ObjectTypeDB::bind_method("_browse",&EditorTranslationImportDialog::_browse);
+ ObjectTypeDB::bind_method("_browse_target",&EditorTranslationImportDialog::_browse_target);
+ // ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) );
+ }
+
+ EditorTranslationImportDialog(EditorTranslationImportPlugin *p_plugin) {
+
+ plugin=p_plugin;
+
+
+ set_title("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("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("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("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("Compress");
+ tcomp->add_child(compress);
+
+ add_to_project = memnew( CheckButton);
+ add_to_project->set_pressed(true);
+ add_to_project->set_text("Add to Project (engine.cfg)");
+ tcomp->add_child(add_to_project);
+
+ file_select = memnew(FileDialog);
+ file_select->set_access(FileDialog::ACCESS_FILESYSTEM);
+ add_child(file_select);
+ file_select->set_mode(FileDialog::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(FileDialog::MODE_OPEN_DIR);
+ save_select->connect("dir_selected", this,"_choose_save_dir");
+
+ get_ok()->connect("pressed", this,"_import");
+ get_ok()->set_text("Import");
+
+
+ error_dialog = memnew ( ConfirmationDialog );
+ add_child(error_dialog);
+ error_dialog->get_ok()->set_text("Accept");
+ // error_dialog->get_cancel()->hide();
+
+ set_hide_on_ok(false);
+
+ columns = memnew( Tree );
+ vbc->add_margin_child("Import Languages:",columns,true);
+ }
+
+ ~EditorTranslationImportDialog() {
+
+ }
+
+};
+
+
+String EditorTranslationImportPlugin::get_name() const {
+
+ return "translation";
+}
+String EditorTranslationImportPlugin::get_visible_name() const {
+
+ return "Translation";
+}
+void EditorTranslationImportPlugin::import_dialog(const String& p_from) {
+
+ dialog->popup_import(p_from);
+}
+
+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 first=false;
+ 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);
+}
diff --git a/tools/editor/io_plugins/editor_translation_import_plugin.h b/tools/editor/io_plugins/editor_translation_import_plugin.h
new file mode 100644
index 000000000..8ea422c24
--- /dev/null
+++ b/tools/editor/io_plugins/editor_translation_import_plugin.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* editor_translation_import_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 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 "tools/editor/editor_import_export.h"
+#include "scene/resources/font.h"
+
+class EditorNode;
+class EditorTranslationImportDialog;
+
+class EditorTranslationImportPlugin : public EditorImportPlugin {
+
+ OBJ_TYPE(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);
+
+
+ EditorTranslationImportPlugin(EditorNode* p_editor);
+};
+
+#endif // EDITOR_TRANSLATION_IMPORT_PLUGIN_H