godot/tools/editor/io_plugins/editor_scene_import_plugin.cpp
Juan Linietsky 6fc1c3a4d1 Completed the support for plugins! It is not possible to add plugins.
Not all APIs are provided yet, please request whathever you are missing.
Some example plugins are provided in demos/plugins. Just copy them to a folder in your project named addons/ and then enable them from the project settings.
Have fun!
2016-02-27 23:12:27 -03:00

2994 lines
81 KiB
C++

/*************************************************************************/
/* editor_scene_import_plugin.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "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/navigation.h"
#include "scene/3d/room_instance.h"
#include "scene/3d/body_shape.h"
#include "scene/3d/physics_body.h"
#include "scene/3d/portal.h"
#include "scene/3d/vehicle_body.h"
#include "os/os.h"
EditorSceneImporter::EditorSceneImporter() {
}
void EditorScenePostImport::_bind_methods() {
BIND_VMETHOD( MethodInfo("post_import",PropertyInfo(Variant::OBJECT,"scene")) );
}
Node *EditorScenePostImport::post_import(Node* p_scene) {
if (get_script_instance())
return get_script_instance()->call("post_import",p_scene);
return p_scene;
}
EditorScenePostImport::EditorScenePostImport() {
}
/////////////////////////////
class EditorImportAnimationOptions : public VBoxContainer {
OBJ_TYPE( EditorImportAnimationOptions, VBoxContainer );
TreeItem *fps;
TreeItem* optimize_linear_error;
TreeItem* optimize_angular_error;
TreeItem* optimize_max_angle;
TreeItem *clips_base;
TextEdit *filters;
Vector<TreeItem*> clips;
Tree *flags;
Tree *clips_tree;
Tree *optimization_tree;
Vector<TreeItem*> items;
bool updating;
bool validating;
void _changed();
void _item_edited();
void _button_action(Object *p_obj,int p_col,int p_id);
protected:
static void _bind_methods();
void _notification(int p_what);
public:
void set_flags(uint32_t p_flags);
uint32_t get_flags() const;
void set_fps(int p_fps);
int get_fps() const;
void set_optimize_linear_error(float p_error);
float get_optimize_linear_error() const;
void set_optimize_angular_error(float p_error);
float get_optimize_angular_error() const;
void set_optimize_max_angle(float p_error);
float get_optimize_max_angle() const;
void setup_clips(const Array& p_clips);
Array get_clips() const;
void set_filter(const String& p_filter);
String get_filter() const;
EditorImportAnimationOptions();
};
////////////////////////////
class EditorSceneImportDialog : public ConfirmationDialog {
OBJ_TYPE(EditorSceneImportDialog,ConfirmationDialog);
struct FlagInfo {
int value;
const char *category;
const char *text;
bool defval;
};
static const FlagInfo scene_flag_names[];
EditorImportTextureOptions *texture_options;
EditorImportAnimationOptions *animation_options;
EditorSceneImportPlugin *plugin;
EditorNode *editor;
LineEdit *import_path;
LineEdit *save_path;
LineEdit *script_path;
Tree *import_options;
EditorFileDialog *file_select;
EditorFileDialog *script_select;
EditorDirDialog *save_select;
OptionButton *texture_action;
ConfirmationDialog *confirm_open;
ConfirmationDialog *confirm_import;
RichTextLabel *missing_files;
Vector<TreeItem*> scene_flags;
Map<Ref<Mesh>,Ref<Shape> > collision_map;
ConfirmationDialog *error_dialog;
OptionButton *this_import;
OptionButton *next_import;
void _choose_file(const String& p_path);
void _choose_save_file(const String& p_path);
void _choose_script(const String& p_path);
void _browse();
void _browse_target();
void _browse_script();
void _import(bool p_and_open=false);
void _import_confirm();
Ref<ResourceImportMetadata> wip_rimd;
Node *wip_import;
String wip_save_file;
bool wip_blocked;
bool wip_open;
void _dialog_hid();
void _open_and_import();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
Error import(const String& p_from, const String& p_to, const String& p_preset);
void popup_import(const String& p_from);
EditorSceneImportDialog(EditorNode *p_editor,EditorSceneImportPlugin *p_plugin);
};
///////////////////////////////////
static const char *anim_flag_names[]={
"Detect Loop (-loop,-cycle)",
"Keep Value Tracks",
"Optimize",
"Force All Tracks in All Clips",
NULL
};
static const char *anim_flag_descript[]={
"Set loop flag for animation names that\ncontain 'cycle' or 'loop' in the name.",
"When merging an existing aimation,\nkeep the user-created value-tracks.",
"Remove redundant keyframes in\n transform tacks.",
"Some exporters will rely on default pose for some bones.\nThis forces those bones to have at least one animation key.",
NULL
};
void EditorImportAnimationOptions::set_flags(uint32_t p_flags){
updating=true;
for(int i=0;i<items.size();i++) {
items[i]->set_checked(0,p_flags&(1<<i));
}
updating=false;
}
uint32_t EditorImportAnimationOptions::get_flags() const{
uint32_t f=0;
for(int i=0;i<items.size();i++) {
if (items[i]->is_checked(0))
f|=(1<<i);
}
return f;
}
void EditorImportAnimationOptions::_changed() {
if (updating)
return;
emit_signal("changed");
}
void EditorImportAnimationOptions::_button_action(Object *p_obj,int p_col,int p_id) {
memdelete(p_obj);
}
void EditorImportAnimationOptions::_item_edited() {
if (validating)
return;
if (clips.size()==0)
return;
validating=true;
print_line("edited");
TreeItem *item = clips_tree->get_edited();
if (item==clips[clips.size()-1]) {
//add new
print_line("islast");
if (item->get_text(0).find("<")!=-1 || item->get_text(0).find(">")!=-1) {
validating=false;
return; //fuckit
}
item->set_editable(1,true);
item->set_editable(2,true);
item->add_button(0,EditorNode::get_singleton()->get_gui_base()->get_icon("Del","EditorIcons"));
item->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
item->set_range_config(1,0,3600,0.01);
item->set_range(1,0);
item->set_editable(1,true);
item->set_cell_mode(2,TreeItem::CELL_MODE_RANGE);
item->set_range_config(2,0,3600,0.01);
item->set_range(2,0);
item->set_cell_mode(3,TreeItem::CELL_MODE_CHECK);
item->set_editable(3,true);
TreeItem *newclip = clips_tree->create_item(clips_base);
newclip->set_text(0,"<new clip>");
newclip->set_editable(0,true);
newclip->set_editable(1,false);
newclip->set_editable(2,false);
clips.push_back(newclip);
}
//make name unique JUST IN CASE
String name = item->get_text(0);
name=name.replace("/","_").replace(":","_").strip_edges();
if (name=="")
name="New Clip";
if (clips.size()>2) {
int index=1;
while(true) {
bool valid = true;
String try_name=name;
if (index>1)
try_name+=" "+itos(index);
for(int i=0;i<clips.size()-1;i++) {
if (clips[i]==item)
continue;
if (clips[i]->get_text(0)==try_name) {
index++;
valid=false;
break;
}
}
if (valid) {
name=try_name;
break;
}
}
}
if (item->get_text(0)!=name)
item->set_text(0,name);
validating=false;
}
void EditorImportAnimationOptions::_bind_methods() {
ObjectTypeDB::bind_method("_changed",&EditorImportAnimationOptions::_changed);
ObjectTypeDB::bind_method("_item_edited",&EditorImportAnimationOptions::_item_edited);
ObjectTypeDB::bind_method("_button_action",&EditorImportAnimationOptions::_button_action);
// ObjectTypeDB::bind_method("_changedp",&EditorImportAnimationOptions::_changedp);
ADD_SIGNAL(MethodInfo("changed"));
}
void EditorImportAnimationOptions::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
flags->connect("item_edited",this,"_changed");
clips_tree->connect("item_edited",this,"_item_edited");
clips_tree->connect("button_pressed",this,"_button_action",varray(),CONNECT_DEFERRED);
// format->connect("item_selected",this,"_changedp");
}
}
Array EditorImportAnimationOptions::get_clips() const {
Array arr;
for(int i=0;i<clips.size()-1;i++) {
arr.push_back(clips[i]->get_text(0));
arr.push_back(clips[i]->get_range(1));
arr.push_back(clips[i]->get_range(2));
arr.push_back(clips[i]->is_checked(3));
}
return arr;
}
void EditorImportAnimationOptions::setup_clips(const Array& p_clips) {
ERR_FAIL_COND(p_clips.size()%4!=0);
for(int i=0;i<clips.size();i++) {
memdelete(clips[i]);
}
clips.clear();
for(int i=0;i<p_clips.size();i+=4) {
TreeItem *clip = clips_tree->create_item(clips_base);
clip->set_text(0,p_clips[i]);
clip->add_button(0,EditorNode::get_singleton()->get_gui_base()->get_icon("Del","EditorIcons"));
clip->set_editable(0,true);
clip->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
clip->set_range_config(1,0,3600,0.01);
clip->set_range(1,p_clips[i+1]);
clip->set_editable(1,true);
clip->set_cell_mode(2,TreeItem::CELL_MODE_RANGE);
clip->set_range_config(2,0,3600,0.01);
clip->set_range(2,p_clips[i+2]);
clip->set_editable(2,true);
clip->set_cell_mode(3,TreeItem::CELL_MODE_CHECK);
clip->set_editable(3,true);
clip->set_checked(3,p_clips[i+3]);
clips.push_back(clip);
}
TreeItem *newclip = clips_tree->create_item(clips_base);
newclip->set_text(0,"<new clip>");
newclip->set_editable(0,true);
newclip->set_editable(1,false);
newclip->set_editable(2,false);
newclip->set_editable(3,false);
clips.push_back(newclip);
}
EditorImportAnimationOptions::EditorImportAnimationOptions() {
updating=false;
validating=false;
TabContainer *tab= memnew(TabContainer);
add_margin_child("Animation Options",tab,true);
flags = memnew( Tree );
flags->set_hide_root(true);
tab->add_child(flags);
flags->set_name("Flags");
TreeItem *root = flags->create_item();
const char ** fname=anim_flag_names;
const char ** fdescr=anim_flag_descript;
while( *fname ) {
TreeItem*ti = flags->create_item(root);
ti->set_cell_mode(0,TreeItem::CELL_MODE_CHECK);
ti->set_text(0,*fname);
ti->set_editable(0,true);
ti->set_tooltip(0,*fdescr);
items.push_back(ti);
fname++;
fdescr++;
}
TreeItem *fps_base = flags->create_item(root);
fps_base->set_text(0,"Bake FPS:");
fps_base->set_editable(0,false);
fps = flags->create_item(fps_base);
fps->set_cell_mode(0,TreeItem::CELL_MODE_RANGE);
fps->set_editable(0,true);
fps->set_range_config(0,1,120,1);
fps->set_range(0,15);
optimization_tree = memnew( Tree );
optimization_tree->set_columns(2);
tab->add_child(optimization_tree);
optimization_tree->set_name("Optimizer");
optimization_tree->set_column_expand(0,true);
optimization_tree->set_column_expand(1,false);
optimization_tree->set_column_min_width(1,80);
optimization_tree->set_hide_root(true);
TreeItem *optimize_root = optimization_tree->create_item();
optimize_linear_error = optimization_tree->create_item(optimize_root);
optimize_linear_error->set_text(0,"Max Linear Error");
optimize_linear_error->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
optimize_linear_error->set_editable(1,true);
optimize_linear_error->set_range_config(1,0,1,0.001);
optimize_linear_error->set_range(1,0.05);
optimize_angular_error = optimization_tree->create_item(optimize_root);
optimize_angular_error->set_text(0,"Max Angular Error");
optimize_angular_error->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
optimize_angular_error->set_editable(1,true);
optimize_angular_error->set_range_config(1,0,1,0.001);
optimize_angular_error->set_range(1,0.01);
optimize_max_angle = optimization_tree->create_item(optimize_root);
optimize_max_angle->set_text(0,"Max Angle");
optimize_max_angle->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
optimize_max_angle->set_editable(1,true);
optimize_max_angle->set_range_config(1,0,360,0.001);
optimize_max_angle->set_range(1,int(180*0.125));
clips_tree = memnew( Tree );
clips_tree->set_hide_root(true);
tab->add_child(clips_tree);
clips_tree->set_name("Clips");
clips_tree->set_columns(4);
clips_tree->set_column_expand(0,1);
clips_tree->set_column_expand(1,0);
clips_tree->set_column_expand(2,0);
clips_tree->set_column_expand(3,0);
clips_tree->set_column_min_width(1,60);
clips_tree->set_column_min_width(2,60);
clips_tree->set_column_min_width(3,40);
clips_tree->set_column_titles_visible(true);
clips_tree->set_column_title(0,"Name");
clips_tree->set_column_title(1,"Start(s)");
clips_tree->set_column_title(2,"End(s)");
clips_tree->set_column_title(3,"Loop");
clips_base =clips_tree->create_item(0);
setup_clips(Array());
filters = memnew( TextEdit );
tab->add_child(filters);
filters->set_name("Filters");
}
void EditorImportAnimationOptions::set_fps(int p_fps) {
fps->set_range(0,p_fps);
}
int EditorImportAnimationOptions::get_fps() const {
return fps->get_range(0);
}
void EditorImportAnimationOptions::set_optimize_linear_error(float p_optimize_linear_error) {
optimize_linear_error->set_range(1,p_optimize_linear_error);
}
float EditorImportAnimationOptions::get_optimize_linear_error() const {
return optimize_linear_error->get_range(1);
}
void EditorImportAnimationOptions::set_optimize_angular_error(float p_optimize_angular_error) {
optimize_angular_error->set_range(1,p_optimize_angular_error);
}
float EditorImportAnimationOptions::get_optimize_angular_error() const {
return optimize_angular_error->get_range(1);
}
void EditorImportAnimationOptions::set_optimize_max_angle(float p_optimize_max_angle) {
optimize_max_angle->set_range(1,p_optimize_max_angle);
}
float EditorImportAnimationOptions::get_optimize_max_angle() const {
return optimize_max_angle->get_range(1);
}
void EditorImportAnimationOptions::set_filter(const String& p_filter) {
filters->set_text(p_filter);
}
String EditorImportAnimationOptions::get_filter() const {
return filters->get_text();
}
////////////////////////////////////////////////////////
void EditorSceneImportDialog::_choose_file(const String& p_path) {
#if 0
StringName sn = EditorImportDB::get_singleton()->find_source_path(p_path);
if (sn!=StringName()) {
EditorImportDB::ImportScene isc;
if (EditorImportDB::get_singleton()->get_scene(sn,isc)==OK) {
save_path->set_text(String(sn).get_base_dir());
texture_options->set_flags( isc.image_flags );
texture_options->set_quality( isc.image_quality );
texture_options->set_format( isc.image_format );
animation_options->set_flags( isc.anim_flags );
script_path->set_text( isc.import_script );
uint32_t f = isc.flags;
for(int i=0;i<scene_flags.size();i++) {
scene_flags[i]->set_checked(0,f&(1<<i));
}
}
} else {
#endif
save_path->set_text("");
//save_path->set_text(p_path.get_file().basename()+".scn");
#if 0
}
#endif
if (p_path!=String()) {
String from_path = EditorFileSystem::get_singleton()->find_resource_from_source(EditorImportPlugin::validate_source_path(p_path));
print_line("from path.."+from_path);
if (from_path!=String()) {
popup_import(from_path);
}
}
import_path->set_text(p_path);
}
void EditorSceneImportDialog::_choose_save_file(const String& p_path) {
save_path->set_text(p_path);
}
void EditorSceneImportDialog::_choose_script(const String& p_path) {
String p = Globals::get_singleton()->localize_path(p_path);
if (!p.is_resource_file())
p=Globals::get_singleton()->get_resource_path().path_to(p_path.get_base_dir())+p_path.get_file();
script_path->set_text(p);
}
void EditorSceneImportDialog::_open_and_import() {
bool unsaved=EditorNode::has_unsaved_changes();
if (unsaved) {
confirm_open->popup_centered_minsize(Size2(300,80));
} else {
_import(true);
}
}
void EditorSceneImportDialog::_import(bool p_and_open) {
wip_open=p_and_open;
//' ImportMonitorBlock imb;
if (import_path->get_text().strip_edges()=="") {
error_dialog->set_text("Source path is empty.");
error_dialog->popup_centered_minsize();
return;
}
if (save_path->get_text().strip_edges()=="") {
error_dialog->set_text("Target path is empty.");
error_dialog->popup_centered_minsize();
return;
}
if (!save_path->get_text().begins_with("res://")) {
error_dialog->set_text("Target path must be full resource path.");
error_dialog->popup_centered_minsize();
return;
}
if (!DirAccess::exists(save_path->get_text())) {
error_dialog->set_text("Target path must exist.");
error_dialog->popup_centered_minsize();
return;
}
String dst_path;
if (texture_action->get_selected()==0)
dst_path=save_path->get_text();//.get_base_dir();
else
dst_path=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)) {
int md = scene_flags[i]->get_metadata(0);
flags|=md;
}
}
if (script_path->get_text()!="") {
Ref<Script> scr = ResourceLoader::load(script_path->get_text());
if (!scr.is_valid()) {
error_dialog->set_text("Couldn't load Post-Import Script.");
error_dialog->popup_centered(Size2(200,100));
return;
}
Ref<EditorScenePostImport> 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);
print_line("GET FLAGS: "+itos(texture_options->get_flags()));
rim->set_option("texture_flags",texture_options->get_flags());
rim->set_option("texture_format",texture_options->get_format());
rim->set_option("texture_quality",texture_options->get_quality());
rim->set_option("animation_flags",animation_options->get_flags());
rim->set_option("animation_bake_fps",animation_options->get_fps());
rim->set_option("animation_optimizer_linear_error",animation_options->get_optimize_linear_error());
rim->set_option("animation_optimizer_angular_error",animation_options->get_optimize_angular_error());
rim->set_option("animation_optimizer_max_angle",animation_options->get_optimize_max_angle());
rim->set_option("animation_filters",animation_options->get_filter());
rim->set_option("animation_clips",animation_options->get_clips());
rim->set_option("post_import_script",script_path->get_text());
rim->set_option("import_this_time",this_import->get_selected());
rim->set_option("import_next_time",next_import->get_selected());
rim->set_option("reimport",true);
List<String> missing;
Error err = plugin->import1(rim,&scene,&missing);
if (err || !scene) {
error_dialog->set_text("Error importing scene.");
error_dialog->popup_centered(Size2(200,100));
return;
}
if (missing.size()) {
missing_files->clear();
for(List<String>::Element *E=missing.front();E;E=E->next()) {
missing_files->add_text(E->get());
missing_files->add_newline();
}
wip_import=scene;
wip_rimd=rim;
wip_save_file=save_file;
confirm_import->popup_centered_ratio();
return;
} else {
err = plugin->import2(scene,save_file,rim);
if (err) {
error_dialog->set_text("Error importing scene.");
error_dialog->popup_centered(Size2(200,100));
return;
}
if (wip_open)
EditorNode::get_singleton()->load_scene(save_file);
}
hide();
/*
editor->clear_scene();
Error err = EditorImport::import_scene(import_path->get_text(),save_file,dst_path,flags,texture_options->get_format(),compression,texture_options->get_flags(),texture_options->get_quality(),animation_options->get_flags(), &scene,pi);
if (err) {
error_dialog->set_text("Error importing scene.");
error_dialog->popup_centered(Size2(200,100));
return;
}
editor->save_import_export();
if (scene)
editor->set_edited_scene(scene);
hide();
*/
};
void EditorSceneImportDialog::_import_confirm() {
wip_blocked=true;
print_line("import confirm!");
Error err = plugin->import2(wip_import,wip_save_file,wip_rimd);
wip_blocked=false;
wip_import=NULL;
wip_rimd=Ref<ResourceImportMetadata>();
confirm_import->hide();
if (err) {
wip_save_file="";
error_dialog->set_text("Error importing scene.");
error_dialog->popup_centered(Size2(200,100));
return;
}
if (wip_open)
EditorNode::get_singleton()->load_scene(wip_save_file);
wip_open=false;
wip_save_file="";
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(750,550));
if (p_from!="") {
Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(p_from);
if (rimd.is_null())
return;
int flags = rimd->get_option("flags");
for(int i=0;i<scene_flags.size();i++) {
int md = scene_flags[i]->get_metadata(0);
scene_flags[i]->set_checked(0,flags&md);
}
texture_options->set_flags(rimd->get_option("texture_flags"));
texture_options->set_format(EditorTextureImportPlugin::ImageFormat(int(rimd->get_option("texture_format"))));
texture_options->set_quality(rimd->get_option("texture_quality"));
animation_options->set_flags(rimd->get_option("animation_flags"));
if (rimd->has_option("animation_clips"))
animation_options->setup_clips(rimd->get_option("animation_clips"));
if (rimd->has_option("animation_filters"))
animation_options->set_filter(rimd->get_option("animation_filters"));
if (rimd->has_option("animation_bake_fps"))
animation_options->set_fps(rimd->get_option("animation_bake_fps"));
if (rimd->has_option("animation_optimizer_linear_error"))
animation_options->set_optimize_linear_error(rimd->get_option("animation_optimizer_linear_error"));
if (rimd->has_option("animation_optimizer_angular_error"))
animation_options->set_optimize_angular_error(rimd->get_option("animation_optimizer_angular_error"));
if (rimd->has_option("animation_optimizer_max_angle"))
animation_options->set_optimize_max_angle(rimd->get_option("animation_optimizer_max_angle"));
script_path->set_text(rimd->get_option("post_import_script"));
if (rimd->has_option("import_this_time"))
this_import->select(rimd->get_option("import_this_time"));
if (rimd->has_option("import_next_time"))
next_import->select(rimd->get_option("import_next_time"));
save_path->set_text(p_from.get_base_dir());
import_path->set_text(EditorImportPlugin::expand_source_path(rimd->get_source_path(0)));
}
}
void EditorSceneImportDialog::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
List<String> extensions;
file_select->clear_filters();
for(int i=0;i<plugin->get_importers().size();i++) {
plugin->get_importers()[i]->get_extensions(&extensions);
}
for(int i=0;i<extensions.size();i++) {
file_select->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper());
}
extensions.clear();
//EditorImport::get_import_extensions(&extensions)
/* ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions);
save_select->clear_filters();
for(int i=0;i<extensions.size();i++) {
save_select->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper());
}*/
}
}
Error EditorSceneImportDialog::import(const String& p_from, const String& p_to, const String& p_preset) {
import_path->set_text(p_from);
save_path->set_text(p_to);
script_path->set_text(p_preset);
_import();
return OK;
}
void EditorSceneImportDialog::_dialog_hid() {
if (wip_blocked)
return;
print_line("DIALOGHID!");
if (wip_import) {
memdelete(wip_import);
wip_import=NULL;
wip_save_file="";
wip_rimd=Ref<ResourceImportMetadata>();
}
}
void EditorSceneImportDialog::_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,DEFVAL(false));
ObjectTypeDB::bind_method("_browse",&EditorSceneImportDialog::_browse);
ObjectTypeDB::bind_method("_browse_target",&EditorSceneImportDialog::_browse_target);
ObjectTypeDB::bind_method("_browse_script",&EditorSceneImportDialog::_browse_script);
ObjectTypeDB::bind_method("_dialog_hid",&EditorSceneImportDialog::_dialog_hid);
ObjectTypeDB::bind_method("_import_confirm",&EditorSceneImportDialog::_import_confirm);
ObjectTypeDB::bind_method("_open_and_import",&EditorSceneImportDialog::_open_and_import);
ADD_SIGNAL( MethodInfo("imported",PropertyInfo(Variant::OBJECT,"scene")) );
}
const EditorSceneImportDialog::FlagInfo EditorSceneImportDialog::scene_flag_names[]={
{EditorSceneImportPlugin::SCENE_FLAG_REMOVE_NOIMP,"Actions","Remove Nodes (-noimp)",true},
{EditorSceneImportPlugin::SCENE_FLAG_IMPORT_ANIMATIONS,"Actions","Import Animations",true},
{EditorSceneImportPlugin::SCENE_FLAG_COMPRESS_GEOMETRY,"Actions","Compress Geometry",false},
{EditorSceneImportPlugin::SCENE_FLAG_GENERATE_TANGENT_ARRAYS,"Actions","Force Generation of Tangent Arrays",false},
{EditorSceneImportPlugin::SCENE_FLAG_DETECT_ALPHA,"Materials","Set Alpha in Materials (-alpha)",true},
{EditorSceneImportPlugin::SCENE_FLAG_DETECT_VCOLOR,"Materials","Set Vert. Color in Materials (-vcol)",true},
{EditorSceneImportPlugin::SCENE_FLAG_LINEARIZE_DIFFUSE_TEXTURES,"Actions","SRGB->Linear Of Diffuse Textures",false},
{EditorSceneImportPlugin::SCENE_FLAG_CONVERT_NORMALMAPS_TO_XY,"Actions","Convert Normal Maps to XY",true},
{EditorSceneImportPlugin::SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS,"Actions","Set Material Lightmap to UV2 if Tex2Array Exists",true},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_COLLISIONS,"Create","Create Collisions (-col},-colonly)",true},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_PORTALS,"Create","Create Portals (-portal)",true},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_ROOMS,"Create","Create Rooms (-room)",true},
{EditorSceneImportPlugin::SCENE_FLAG_SIMPLIFY_ROOMS,"Create","Simplify Rooms",false},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_BILLBOARDS,"Create","Create Billboards (-bb)",true},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_IMPOSTORS,"Create","Create Impostors (-imp:dist)",true},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_LODS,"Create","Create LODs (-lod:dist)",true},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_CARS,"Create","Create Vehicles (-vehicle)",true},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_WHEELS,"Create","Create Vehicle Wheels (-wheel)",true},
{EditorSceneImportPlugin::SCENE_FLAG_CREATE_NAVMESH,"Create","Create Navigation Meshes (-navmesh)",true},
{EditorSceneImportPlugin::SCENE_FLAG_DETECT_LIGHTMAP_LAYER,"Create","Detect LightMap Layer (-lm:<int>).",true},
{-1,NULL,NULL,false}
};
EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSceneImportPlugin *p_plugin) {
editor=p_editor;
plugin=p_plugin;
set_title("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 Path:",hbc);
save_path = memnew( LineEdit );
save_path->set_h_size_flags(SIZE_EXPAND_FILL);
hbc->add_child(save_path);
Button * save_choose = memnew( Button );
save_choose->set_text(" .. ");
hbc->add_child(save_choose);
save_choose->connect("pressed", this,"_browse_target");
texture_action = memnew( OptionButton );
texture_action->add_item("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(EditorFileDialog);
file_select->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
add_child(file_select);
file_select->set_mode(EditorFileDialog::MODE_OPEN_FILE);
file_select->connect("file_selected", this,"_choose_file");
save_select = memnew(EditorDirDialog);
add_child(save_select);
//save_select->set_mode(EditorFileDialog::MODE_SAVE_FILE);
save_select->connect("dir_selected", this,"_choose_save_file");
get_ok()->connect("pressed", this,"_import");
get_ok()->set_text("Import");
TreeItem *root = import_options->create_item(NULL);
import_options->set_hide_root(true);
const FlagInfo* fn=scene_flag_names;
Map<String,TreeItem*> categories;
while(fn->text) {
String cat = fn->category;
TreeItem *parent;
if (!categories.has(cat)) {
parent = import_options->create_item(root);
parent->set_text(0,cat);
categories[cat]=parent;
} else {
parent=categories[cat];
}
TreeItem *opt = import_options->create_item(parent);
opt->set_cell_mode(0,TreeItem::CELL_MODE_CHECK);
opt->set_checked(0,fn->defval);
opt->set_editable(0,true);
opt->set_text(0,fn->text);
opt->set_metadata(0,fn->value);
scene_flags.push_back(opt);
fn++;
}
hbc = memnew( HBoxContainer );
vbc->add_margin_child("Post-Process Script:",hbc);
script_path = memnew( LineEdit );
script_path->set_h_size_flags(SIZE_EXPAND_FILL);
hbc->add_child(script_path);
Button * script_choose = memnew( Button );
script_choose->set_text(" .. ");
hbc->add_child(script_choose);
script_choose->connect("pressed", this,"_browse_script");
script_select = memnew(EditorFileDialog);
add_child(script_select);
for(int i=0;i<ScriptServer::get_language_count();i++) {
ScriptLanguage *sl=ScriptServer::get_language(i);
String ext = sl->get_extension();
if (ext=="")
continue;
script_select->add_filter("*."+ext+" ; "+sl->get_name());
}
script_select->set_mode(EditorFileDialog::MODE_OPEN_FILE);
script_select->connect("file_selected", this,"_choose_script");
error_dialog = memnew ( ConfirmationDialog );
add_child(error_dialog);
error_dialog->get_ok()->set_text("Accept");
// error_dialog->get_cancel()->hide();
this_import = memnew( OptionButton );
this_import->add_item("Overwrite Existing Scene");
this_import->add_item("Owerwrite Existing, Keep Materials");
this_import->add_item("Keep Existing, Merge with New");
this_import->add_item("Keep Existing, Ignore New");
vbc->add_margin_child("This Time:",this_import);
next_import = memnew( OptionButton );
next_import->add_item("Overwrite Existing Scene");
next_import->add_item("Owerwrite Existing, Keep Materials");
next_import->add_item("Keep Existing, Merge with New");
next_import->add_item("Keep Existing, Ignore New");
vbc->add_margin_child("Next Time:",next_import);
set_hide_on_ok(false);
GLOBAL_DEF("import/shared_textures","res://");
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);
texture_options->set_flags( EditorTextureImportPlugin::IMAGE_FLAG_FIX_BORDER_ALPHA | EditorTextureImportPlugin::IMAGE_FLAG_REPEAT | EditorTextureImportPlugin::IMAGE_FLAG_FILTER );
animation_options = memnew( EditorImportAnimationOptions );
ovb->add_child(animation_options);
animation_options->set_v_size_flags(SIZE_EXPAND_FILL);
animation_options->set_flags(EditorSceneAnimationImportPlugin::ANIMATION_DETECT_LOOP|EditorSceneAnimationImportPlugin::ANIMATION_KEEP_VALUE_TRACKS|EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE|EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS);
confirm_import = memnew( ConfirmationDialog );
add_child(confirm_import);
VBoxContainer *cvb = memnew( VBoxContainer );
confirm_import->add_child(cvb);
confirm_import->set_child_rect(cvb);
PanelContainer *pc = memnew( PanelContainer );
pc->add_style_override("panel",get_stylebox("normal","TextEdit"));
//ec->add_child(pc);
missing_files = memnew( RichTextLabel );
cvb->add_margin_child("The Following Files are Missing:",pc,true);
pc->add_child(missing_files);
confirm_import->get_ok()->set_text("Import Anyway");
confirm_import->get_cancel()->set_text("Cancel");
confirm_import->connect("popup_hide",this,"_dialog_hid");
confirm_import->connect("confirmed",this,"_import_confirm");
confirm_import->set_hide_on_ok(false);
add_button("Import & Open",!OS::get_singleton()->get_swap_ok_cancel())->connect("pressed",this,"_open_and_import");
confirm_open = memnew( ConfirmationDialog );
add_child(confirm_open);
confirm_open->set_text("Edited scene has not been saved, open imported scene anyway?");
confirm_open->connect("confirmed",this,"_import",varray(true));
wip_import=NULL;
wip_blocked=false;
wip_open=false;
//texture_options->set_format(EditorImport::IMAGE_FORMAT_C);
}
////////////////////////////////
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;
if (p_what.to_lower().ends_with("_"+p_str)) //collada only supports "_" and "-" besides letters
return true;
return false;
}
static String _fixstr(const String& p_what,const String& p_str) {
if (p_what.findn("$"+p_str)!=-1) //blender and other stuff
return p_what.replace("$"+p_str,"");
if (p_what.to_lower().ends_with("-"+p_str)) //collada only supports "_" and "-" besides letters
return p_what.substr(0,p_what.length()-(p_str.length()+1));
if (p_what.to_lower().ends_with("_"+p_str)) //collada only supports "_" and "-" besides letters
return p_what.substr(0,p_what.length()-(p_str.length()+1));
return p_what;
}
void EditorSceneImportPlugin::_find_resources(const Variant& p_var, Map<Ref<ImageTexture>, TextureRole> &image_map,int p_flags) {
switch(p_var.get_type()) {
case Variant::OBJECT: {
Ref<Resource> res = p_var;
if (res.is_valid()) {
if (res->is_type("Texture") && !image_map.has(res)) {
image_map.insert(res,TEXTURE_ROLE_DEFAULT);
} else {
List<PropertyInfo> pl;
res->get_property_list(&pl);
for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
if (E->get().type==Variant::OBJECT || E->get().type==Variant::ARRAY || E->get().type==Variant::DICTIONARY) {
if (E->get().type==Variant::OBJECT && res->cast_to<FixedMaterial>() && (E->get().name=="textures/diffuse" || E->get().name=="textures/detail" || E->get().name=="textures/emission")) {
Ref<ImageTexture> tex =res->get(E->get().name);
if (tex.is_valid()) {
image_map.insert(tex,TEXTURE_ROLE_DIFFUSE);
}
} else if (E->get().type==Variant::OBJECT && res->cast_to<FixedMaterial>() && (E->get().name=="textures/normal")) {
Ref<ImageTexture> tex =res->get(E->get().name);
if (tex.is_valid()) {
image_map.insert(tex,TEXTURE_ROLE_NORMALMAP);
if (p_flags&SCENE_FLAG_CONVERT_NORMALMAPS_TO_XY)
res->cast_to<FixedMaterial>()->set_fixed_flag(FixedMaterial::FLAG_USE_XY_NORMALMAP,true);
}
} else {
_find_resources(res->get(E->get().name),image_map,p_flags);
}
}
}
}
}
} break;
case Variant::DICTIONARY: {
Dictionary d= p_var;
List<Variant> keys;
d.get_key_list(&keys);
for(List<Variant>::Element *E=keys.front();E;E=E->next()) {
_find_resources(E->get(),image_map,p_flags);
_find_resources(d[E->get()],image_map,p_flags);
}
} break;
case Variant::ARRAY: {
Array a = p_var;
for(int i=0;i<a.size();i++) {
_find_resources(a[i],image_map,p_flags);
}
} break;
}
}
Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map,uint32_t p_flags,Map<Ref<ImageTexture>,TextureRole >& image_map) {
// children first..
for(int i=0;i<p_node->get_child_count();i++) {
Node *r = _fix_node(p_node->get_child(i),p_root,collision_map,p_flags,image_map);
if (!r) {
print_line("was erased..");
i--; //was erased
}
}
String name = p_node->get_name();
bool isroot = p_node==p_root;
if (!isroot && p_flags&SCENE_FLAG_REMOVE_NOIMP && _teststr(name,"noimp")) {
memdelete(p_node);
return NULL;
}
{
List<PropertyInfo> pl;
p_node->get_property_list(&pl);
for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
if (E->get().type==Variant::OBJECT || E->get().type==Variant::ARRAY || E->get().type==Variant::DICTIONARY) {
_find_resources(p_node->get(E->get().name),image_map,p_flags);
}
}
}
if (p_flags&SCENE_FLAG_CREATE_BILLBOARDS && p_node->cast_to<MeshInstance>()) {
MeshInstance *mi = p_node->cast_to<MeshInstance>();
bool bb=false;
if ((_teststr(name,"bb"))) {
bb=true;
} else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"bb"))) {
bb=true;
}
if (bb) {
mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true);
if (mi->get_mesh().is_valid()) {
Ref<Mesh> m = mi->get_mesh();
for(int i=0;i<m->get_surface_count();i++) {
Ref<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_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
}
}
}
}
}
if (p_flags&(SCENE_FLAG_DETECT_ALPHA|SCENE_FLAG_DETECT_VCOLOR|SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS) && p_node->cast_to<MeshInstance>()) {
MeshInstance *mi = p_node->cast_to<MeshInstance>();
Ref<Mesh> m = mi->get_mesh();
if (m.is_valid()) {
for(int i=0;i<m->get_surface_count();i++) {
Ref<FixedMaterial> mat = m->surface_get_material(i);
if (!mat.is_valid())
continue;
if (p_flags&SCENE_FLAG_DETECT_ALPHA && _teststr(mat->get_name(),"alpha")) {
mat->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
mat->set_name(_fixstr(mat->get_name(),"alpha"));
}
if (p_flags&SCENE_FLAG_DETECT_VCOLOR && _teststr(mat->get_name(),"vcol")) {
mat->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true);
mat->set_name(_fixstr(mat->get_name(),"vcol"));
}
if (p_flags&SCENE_FLAG_SET_LIGHTMAP_TO_UV2_IF_EXISTS && m->surface_get_format(i)&Mesh::ARRAY_FORMAT_TEX_UV2) {
mat->set_flag(Material::FLAG_LIGHTMAP_ON_UV2,true);
}
}
}
}
if (p_flags&SCENE_FLAG_REMOVE_NOIMP && p_node->cast_to<AnimationPlayer>()) {
//remove animations referencing non-importable nodes
AnimationPlayer *ap = p_node->cast_to<AnimationPlayer>();
List<StringName> anims;
ap->get_animation_list(&anims);
for(List<StringName>::Element *E=anims.front();E;E=E->next()) {
Ref<Animation> anim=ap->get_animation(E->get());
ERR_CONTINUE(anim.is_null());
for(int i=0;i<anim->get_track_count();i++) {
NodePath path = anim->track_get_path(i);
for(int j=0;j<path.get_name_count();j++) {
String node = path.get_name(j);
if (_teststr(node,"noimp")) {
anim->remove_track(i);
i--;
break;
}
}
}
}
}
if (p_flags&SCENE_FLAG_CREATE_IMPOSTORS && p_node->cast_to<MeshInstance>()) {
MeshInstance *mi = p_node->cast_to<MeshInstance>();
String str;
if ((_teststr(name,"imp"))) {
str=name;
} else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"imp"))) {
str=mi->get_mesh()->get_name();
}
if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) {
MeshInstance *mi = p_node->cast_to<MeshInstance>();
MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>();
String d=str.substr(str.find("imp")+3,str.length());
if (d!="") {
if ((d[0]<'0' || d[0]>'9'))
d=d.substr(1,d.length());
if (d.length() && d[0]>='0' && d[0]<='9') {
float dist = d.to_double();
mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true);
mi->set_flag(GeometryInstance::FLAG_BILLBOARD_FIX_Y,true);
mi->set_draw_range_begin(dist);
mi->set_draw_range_end(100000);
mip->set_draw_range_begin(0);
mip->set_draw_range_end(dist);
if (mi->get_mesh().is_valid()) {
Ref<Mesh> m = mi->get_mesh();
for(int i=0;i<m->get_surface_count();i++) {
Ref<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_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
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_DETECT_LIGHTMAP_LAYER && _teststr(name,"lm") && p_node->cast_to<MeshInstance>()) {
MeshInstance *mi = p_node->cast_to<MeshInstance>();
String str=name;
int layer = str.substr(str.find("lm")+3,str.length()).to_int();
mi->set_baked_light_texture_id(layer);
}
if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(name,"colonly") && 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;
StaticBody *sb = col->cast_to<StaticBody>();
CollisionShape *colshape = memnew( CollisionShape);
colshape->set_shape(sb->get_shape(0));
colshape->set_name("shape");
sb->add_child(colshape);
colshape->set_owner(p_node->get_owner());
} else if (p_flags&SCENE_FLAG_CREATE_COLLISIONS &&_teststr(name,"col") && p_node->cast_to<MeshInstance>()) {
MeshInstance *mi = p_node->cast_to<MeshInstance>();
mi->set_name(_fixstr(name,"col"));
Node *col= mi->create_trimesh_collision_node();
ERR_FAIL_COND_V(!col,NULL);
col->set_name("col");
p_node->add_child(col);
StaticBody *sb=col->cast_to<StaticBody>();
CollisionShape *colshape = memnew( CollisionShape);
colshape->set_shape(sb->get_shape(0));
colshape->set_name("shape");
col->add_child(colshape);
colshape->set_owner(p_node->get_owner());
sb->set_owner(p_node->get_owner());
} else if (p_flags&SCENE_FLAG_CREATE_NAVMESH &&_teststr(name,"navmesh") && p_node->cast_to<MeshInstance>()) {
if (isroot)
return p_node;
MeshInstance *mi = p_node->cast_to<MeshInstance>();
Ref<Mesh> mesh=mi->get_mesh();
ERR_FAIL_COND_V(mesh.is_null(),NULL);
NavigationMeshInstance *nmi = memnew( NavigationMeshInstance );
nmi->set_name(_fixstr(name,"navmesh"));
Ref<NavigationMesh> nmesh = memnew( NavigationMesh);
nmesh->create_from_mesh(mesh);
nmi->set_navigation_mesh(nmesh);
nmi->cast_to<Spatial>()->set_transform(mi->get_transform());
p_node->replace_by(nmi);
memdelete(p_node);
p_node=nmi;
} else if (p_flags&SCENE_FLAG_CREATE_CARS &&_teststr(name,"vehicle")) {
if (isroot)
return p_node;
Node *owner = p_node->get_owner();
Spatial *s = p_node->cast_to<Spatial>();
VehicleBody *bv = memnew( VehicleBody );
String n = _fixstr(p_node->get_name(),"vehicle");
bv->set_name(n);
p_node->replace_by(bv);
p_node->set_name(n);
bv->add_child(p_node);
bv->set_owner(owner);
p_node->set_owner(owner);
bv->set_transform(s->get_transform());
s->set_transform(Transform());
p_node=bv;
} else if (p_flags&SCENE_FLAG_CREATE_CARS &&_teststr(name,"wheel")) {
if (isroot)
return p_node;
Node *owner = p_node->get_owner();
Spatial *s = p_node->cast_to<Spatial>();
VehicleWheel *bv = memnew( VehicleWheel );
String n = _fixstr(p_node->get_name(),"wheel");
bv->set_name(n);
p_node->replace_by(bv);
p_node->set_name(n);
bv->add_child(p_node);
bv->set_owner(owner);
p_node->set_owner(owner);
bv->set_transform(s->get_transform());
s->set_transform(Transform());
p_node=bv;
} else if (p_flags&SCENE_FLAG_CREATE_ROOMS && _teststr(name,"room") && p_node->cast_to<MeshInstance>()) {
if (isroot)
return p_node;
MeshInstance *mi = p_node->cast_to<MeshInstance>();
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].angle();
float b = portal_points[i+1].angle();
if (a>b) {
SWAP( portal_points[i], portal_points[i+1] );
swaps++;
}
}
} while(swaps);
Portal *portal = memnew( Portal );
portal->set_shape(portal_points);
portal->set_transform( mi->get_transform() * t);
p_node->replace_by(portal);
memdelete(p_node);
p_node=portal;
} else if (p_node->cast_to<MeshInstance>()) {
//last attempt, maybe collision insde the mesh data
MeshInstance *mi = p_node->cast_to<MeshInstance>();
Ref<Mesh> mesh = mi->get_mesh();
if (!mesh.is_null()) {
if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(mesh->get_name(),"col")) {
mesh->set_name( _fixstr(mesh->get_name(),"col") );
Ref<Shape> shape;
if (collision_map.has(mesh)) {
shape = collision_map[mesh];
} else {
shape = mesh->create_trimesh_shape();
if (!shape.is_null())
collision_map[mesh]=shape;
}
if (!shape.is_null()) {
#if 0
StaticBody* static_body = memnew( StaticBody );
ERR_FAIL_COND_V(!static_body,NULL);
static_body->set_name( String(mesh->get_name()) + "_col" );
shape->set_name(static_body->get_name());
static_body->add_shape(shape);
mi->add_child(static_body);
if (mi->get_owner())
static_body->set_owner( mi->get_owner() );
#endif
}
}
for(int i=0;i<mesh->get_surface_count();i++) {
Ref<FixedMaterial> fm = mesh->surface_get_material(i);
if (fm.is_valid()) {
String name = fm->get_name();
if (_teststr(name,"alpha")) {
fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
name=_fixstr(name,"alpha");
}
if (_teststr(name,"vcol")) {
fm->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true);
name=_fixstr(name,"vcol");
}
fm->set_name(name);
}
}
}
}
return p_node;
}
void EditorSceneImportPlugin::_merge_existing_node(Node *p_node,Node *p_imported_scene,Set<Ref<Resource> >& checked_resources,Set<Node*> &checked_nodes) {
NodePath path = p_node->get_import_path();
if (!path.is_empty() && p_imported_scene->has_node(path)) {
Node *imported_node = p_imported_scene->get_node(path);
if (imported_node->get_type()==p_node->get_type()) {
//same thing, check what it is
if (p_node->get_type()=="MeshInstance") {
//merge mesh instance, this is a special case!
MeshInstance *mi_imported=imported_node->cast_to<MeshInstance>();
MeshInstance *mi_node=p_node->cast_to<MeshInstance>();
Ref<Mesh> mesh_imported = mi_imported->get_mesh();
Ref<Mesh> mesh_node = mi_node->get_mesh();
if (mesh_node.is_null() && mesh_imported.is_valid()) {
mi_node->set_mesh(mesh_imported);
} else if (mesh_node.is_valid() && mesh_imported.is_valid()) {
if (checked_resources.has(mesh_imported)) {
mi_node->set_mesh(mesh_imported);
} else {
//mix up meshes
//import new geometry but keep materials
for(int i=0;i<mesh_imported->get_surface_count();i++) {
String name = mesh_imported->surface_get_name(i);
for(int j=0;j<mesh_node->get_surface_count();j++) {
Ref<Material> mat = mesh_node->surface_get_material(j);
if (mat.is_valid() && mesh_node->surface_get_name(j)==name ) {
mesh_imported->surface_set_material(i,mat);
break;
}
}
}
// was imported, do nothing further
checked_resources.insert(mesh_imported);
mi_node->set_mesh(mesh_imported);
}
}
} else if (p_node->get_type()=="Path") {
//for paths, overwrite path
Path *path_imported =imported_node->cast_to<Path>();
Path *path_node =p_node->cast_to<Path>();
if (path_imported->get_curve().is_valid()) {
path_node->set_curve(path_imported->get_curve());
}
} else if (p_node->get_type()=="Portal") {
//for paths, overwrite path
Portal *portal_imported =imported_node->cast_to<Portal>();
Portal *portal_node =p_node->cast_to<Portal>();
portal_node->set_shape( portal_imported->get_shape() );
} else if (p_node->get_type()=="Room") {
//for paths, overwrite path
Room *room_imported =imported_node->cast_to<Room>();
Room *room_node =p_node->cast_to<Room>();
room_node->set_room( room_imported->get_room() );
} else if (p_node->get_type()=="Skeleton") {
//for paths, overwrite path
Skeleton *skeleton_imported =imported_node->cast_to<Skeleton>();
Skeleton *skeleton_node =p_node->cast_to<Skeleton>();
//use imported bones, obviously
skeleton_node->clear_bones();
for(int i=0;i<skeleton_imported->get_bone_count();i++) {
skeleton_node->add_bone(skeleton_imported->get_bone_name(i));
skeleton_node->set_bone_parent(i,skeleton_imported->get_bone_parent(i));
skeleton_node->set_bone_rest(i,skeleton_imported->get_bone_rest(i));
//skeleton_node->set_bone_pose(i,skeleton_imported->get_bone_pose(i)); // not in a scene, will throw errors
}
}
else if (p_node->get_type() == "AnimationPlayer") {
//for paths, overwrite path
AnimationPlayer *aplayer_imported = imported_node->cast_to<AnimationPlayer>();
AnimationPlayer *aplayer_node = p_node->cast_to<AnimationPlayer>();
//use imported bones, obviously
List<StringName> anims;
List<StringName> existing_anims;
aplayer_imported->get_animation_list(&anims);
aplayer_node->get_animation_list(&existing_anims);
//use imported animations
for (List<StringName>::Element *N = anims.front(); N; N = N->next()) {
Ref<Animation> candidate = aplayer_imported->get_animation(N->get());
if (aplayer_node->has_animation(N->get())) {
Ref<Animation> found = aplayer_node->get_animation(N->get());
candidate->set_loop(found->has_loop());
candidate->set_step(found->get_step());
//For each track candidate
for (int i = 0; i < candidate->get_track_count(); i++) {
NodePath track_path = candidate->track_get_path(i);
// For each track existing
for (int x = 0; x < found->get_track_count(); x++) {
NodePath path_to_compare = found->track_get_path(x);
if (track_path.hash() == path_to_compare.hash() && candidate->track_get_type(x) == found->track_get_type(i)) {
//Tracks matches
if (candidate->track_get_interpolation_type(i) != found->track_get_interpolation_type(x))
candidate->track_set_interpolation_type(i, found->track_get_interpolation_type(x));
if (candidate->track_get_type(i) == Animation::TYPE_VALUE && candidate->value_track_is_continuous(i) != found->value_track_is_continuous(x))
candidate->value_track_set_continuous(i, found->value_track_is_continuous(x));
//Key transitions might have changed, but the animation remained unchanged
if (candidate->track_get_key_count(i) == found->track_get_key_count(x)) {
for (int k = 0; k < candidate->track_get_key_count(i); k++) {
if (candidate->track_get_key_transition(i, k) != found->track_get_key_transition(x, k))
candidate->track_set_key_transition(i, k, found->track_get_key_transition(x, k));
}
}
}
}
}
// Append function callbacks and values
for (int x = 0; x < found->get_track_count(); x++) {
if (found->track_get_type(x) == Animation::TYPE_METHOD || found->track_get_type(x) == Animation::TYPE_VALUE)
candidate->add_track(found->track_get_type(x), candidate->get_track_count());
for (int k = 0; k < found->track_get_key_count(x); k++)
candidate->track_insert_key(x, found->track_get_key_time(x, k), found->track_get_key_value(x, k), found->track_get_key_transition(x, k));
}
}
aplayer_node->add_animation(N->get(), candidate);
}
} else if (p_node->get_type()=="CollisionShape") {
//for paths, overwrite path
CollisionShape *collision_imported =imported_node->cast_to<CollisionShape>();
CollisionShape *collision_node =p_node->cast_to<CollisionShape>();
collision_node->set_shape( collision_imported->get_shape() );
}
}
if (p_node->cast_to<Spatial>() && imported_node->cast_to<Spatial>()) {
//apply transform if changed
Spatial *snode = p_node->cast_to<Spatial>();
Spatial *simp = imported_node->cast_to<Spatial>();
if (snode->get_import_transform() == snode->get_transform()) {
//not moved, apply new
snode->set_import_transform(simp->get_transform());
snode->set_transform(simp->get_transform());
} else if (snode->get_import_transform() == simp->get_import_transform()) {
//do nothing, nothing changed keep local changes
} else {
//changed both, imported and edited, merge
Transform local_xform = snode->get_import_transform().affine_inverse() * snode->get_transform();
snode->set_import_transform(simp->get_import_transform());
snode->set_transform(simp->get_import_transform() * local_xform);
}
}
checked_nodes.insert(imported_node);
}
#if 0
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;
}
}
}
#endif
for(int i=0;i<p_node->get_child_count();i++) {
_merge_existing_node(p_node->get_child(i),p_imported_scene,checked_resources,checked_nodes);
}
}
void EditorSceneImportPlugin::_add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Node *p_existing_scene,Set<Node*> &checked_nodes) {
for(int i=0;i<p_imported->get_child_count();i++) {
Node *imported_node = p_imported->get_child(i);
if (imported_node->get_owner()!=p_imported_scene) {
// print_line("skipping because not imported at "+String(imported_node->get_name()));
continue; //end of the road
}
Vector<StringName> nn;
nn.push_back(imported_node->get_name());
NodePath imported_path(nn,false);
//print_line("check for: "+String(imported_path));
if (!p_node->has_node(imported_path) && !checked_nodes.has(imported_node)) {
//not there, re-add it
//add it.. because not existing in existing scene
Object *o = ObjectTypeDB::instance(imported_node->get_type());
Node *n=NULL;
if (o)
n=o->cast_to<Node>();
//print_line("creating node of same type..");
if (n) {
//print_line("copy props and add");
List<PropertyInfo> pl;
imported_node->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, imported_node->get(E->get().name) );
}
p_node->add_child(n);
n->set_owner(p_existing_scene);
}
} else {
//print_line("already exists");
}
if (p_node->has_node(imported_path)) {
Node *other_node = p_node->get_node(imported_path);
_add_new_nodes(other_node,imported_node,p_imported_scene,p_existing_scene,checked_nodes);
}
}
}
void EditorSceneImportPlugin::_merge_scenes(Node *p_node,Node *p_imported) {
Set<Ref<Resource> > checked_resources;
Set<Node*> checked_nodes;
_merge_existing_node(p_node,p_imported,checked_resources,checked_nodes);
_add_new_nodes(p_node,p_imported,p_imported,p_node,checked_nodes);
//add existing.. ?
}
void EditorSceneImportPlugin::_scan_materials(Node*p_base,Node *p_node,Map<String,Ref<Material> > &mesh_materials,Map<String,Ref<Material> >& override_materials) {
if (!p_base && p_node->get_owner()!=p_base)
return;
MeshInstance *mi=p_node->cast_to<MeshInstance>();
if (mi) {
if (mi->get_material_override().is_valid()) {
String path = p_base->get_path_to(p_node);
override_materials[path]=mi->get_material_override();
}
Ref<Mesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
for(int i=0;i<mesh->get_surface_count();i++) {
String name = mesh->get_name()+":"+mesh->surface_get_name(i);
if (!mesh_materials.has(name)) {
mesh_materials[name]=mesh->surface_get_material(i);
}
}
}
}
for(int i=0;i<p_node->get_child_count();i++) {
_scan_materials(p_base,p_node->get_child(i),mesh_materials,override_materials);
}
}
void EditorSceneImportPlugin::_apply_materials(Node*p_base,Node *p_node,Map<String,Ref<Material> > &mesh_materials,Map<String,Ref<Material> >& override_materials,Set<Ref<Mesh> >& meshes_processed) {
if (p_node!=p_base && p_node->get_owner()!=p_base)
return;
MeshInstance *mi=p_node->cast_to<MeshInstance>();
if (mi) {
print_line("is mesh "+String(p_node->get_name()));
String path = p_base->get_path_to(p_node);
if (override_materials.has(path)) {
print_line("is in material overrides");
mi->set_material_override(override_materials[path]);
}
Ref<Mesh> mesh = mi->get_mesh();
if (mesh.is_valid() && !meshes_processed.has(mesh)) {
print_line("mesh was not processed");
meshes_processed.insert(mesh);
for(int i=0;i<mesh->get_surface_count();i++) {
String name = mesh->get_name()+":"+mesh->surface_get_name(i);
print_line("name for surface "+itos(i)+": "+name);
if (mesh_materials.has(name)) {
Ref<Material> mat = mesh_materials[name];
mesh->surface_set_material(i,mat);
print_line("overriding!");
}
}
}
}
for(int i=0;i<p_node->get_child_count();i++) {
_apply_materials(p_base,p_node->get_child(i),mesh_materials,override_materials,meshes_processed);
}
}
void EditorSceneImportPlugin::_merge_materials(Node *p_node,Node *p_imported) {
Map<String,Ref<Material> > mesh_materials;
Map<String,Ref<Material> > override_materials;
_scan_materials(p_node,p_node,mesh_materials,override_materials);
for (Map<String,Ref<Material> >::Element *E=mesh_materials.front();E;E=E->next()) {
print_line("Mats: "+String(E->key()));
}
for (Map<String,Ref<Material> >::Element *E=override_materials.front();E;E=E->next()) {
print_line("Overrides: "+String(E->key()));
}
Set<Ref<Mesh> > mp;
_apply_materials(p_imported,p_imported,mesh_materials,override_materials,mp);
}
#if 0
Error EditorImport::import_scene(const String& p_path,const String& p_dest_path,const String& p_resource_path,uint32_t p_flags,ImageFormat p_image_format,ImageCompression p_image_compression,uint32_t p_image_flags,float p_quality,uint32_t animation_flags,Node **r_scene,Ref<EditorPostImport> p_post_import) {
}
#endif
void EditorSceneImportPlugin::_tag_import_paths(Node *p_scene,Node *p_node) {
if (p_scene!=p_node && p_node->get_owner()!=p_scene)
return;
NodePath path = p_scene->get_path_to(p_node);
p_node->set_import_path( path );
Spatial *snode=p_node->cast_to<Spatial>();
if (snode) {
snode->set_import_transform(snode->get_transform());
}
for(int i=0;i<p_node->get_child_count();i++) {
_tag_import_paths(p_scene,p_node->get_child(i));
}
}
Error EditorSceneImportPlugin::import1(const Ref<ResourceImportMetadata>& p_from,Node**r_node,List<String> *r_missing) {
Ref<ResourceImportMetadata> from=p_from;
ERR_FAIL_COND_V(from->get_source_count()!=1,ERR_INVALID_PARAMETER);
String src_path=EditorImportPlugin::expand_source_path(from->get_source_path(0));
Ref<EditorSceneImporter> importer;
String ext=src_path.extension().to_lower();
EditorProgress progress("import","Import Scene",104);
progress.step("Importing Scene..",0);
for(int i=0;i<importers.size();i++) {
List<String> extensions;
importers[i]->get_extensions(&extensions);
for(List<String>::Element *E=extensions.front();E;E=E->next()) {
if (E->get().to_lower()==ext) {
importer = importers[i];
break;
}
}
if (importer.is_valid())
break;
}
ERR_FAIL_COND_V(!importer.is_valid(),ERR_FILE_UNRECOGNIZED);
int animation_flags=p_from->get_option("animation_flags");
int scene_flags = from->get_option("flags");
int fps = 24;
if (from->has_option("animation_bake_fps"))
fps=from->get_option("animation_bake_fps");
Array clips;
if (from->has_option("animation_clips"))
clips=from->get_option("animation_clips");
uint32_t import_flags=0;
if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_DETECT_LOOP)
import_flags|=EditorSceneImporter::IMPORT_ANIMATION_DETECT_LOOP;
if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE)
import_flags|=EditorSceneImporter::IMPORT_ANIMATION_OPTIMIZE;
if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS)
import_flags|=EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS;
if (scene_flags&SCENE_FLAG_IMPORT_ANIMATIONS)
import_flags|=EditorSceneImporter::IMPORT_ANIMATION;
//if (scene_flags&SCENE_FLAG_FAIL_ON_MISSING_IMAGES)
// import_flags|=EditorSceneImporter::IMPORT_FAIL_ON_MISSING_DEPENDENCIES;
if (scene_flags&SCENE_FLAG_GENERATE_TANGENT_ARRAYS)
import_flags|=EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS;
Error err=OK;
Node *scene = importer->import_scene(src_path,import_flags,fps,r_missing,&err);
if (!scene || err!=OK) {
return err;
}
_tag_import_paths(scene,scene);
*r_node=scene;
return OK;
}
void EditorSceneImportPlugin::_create_clips(Node *scene, const Array& p_clips,bool p_bake_all) {
if (!scene->has_node(String("AnimationPlayer")))
return;
Node* n = scene->get_node(String("AnimationPlayer"));
ERR_FAIL_COND(!n);
AnimationPlayer *anim = n->cast_to<AnimationPlayer>();
ERR_FAIL_COND(!anim);
if (!anim->has_animation("default"))
return;
Ref<Animation> default_anim = anim->get_animation("default");
for(int i=0;i<p_clips.size();i+=4) {
String name = p_clips[i];
float from=p_clips[i+1];
float to=p_clips[i+2];
bool loop=p_clips[i+3];
if (from>=to)
continue;
Ref<Animation> new_anim = memnew( Animation );
for(int j=0;j<default_anim->get_track_count();j++) {
List<float> keys;
int kc = default_anim->track_get_key_count(j);
int dtrack=-1;
for(int k=0;k<kc;k++) {
float kt = default_anim->track_get_key_time(j,k);
if (kt>=from && kt<to) {
//found a key within range, so create track
if (dtrack==-1) {
new_anim->add_track(default_anim->track_get_type(j));
dtrack = new_anim->get_track_count()-1;
new_anim->track_set_path(dtrack,default_anim->track_get_path(j));
if (kt>(from+0.01) && k>0) {
if (default_anim->track_get_type(j)==Animation::TYPE_TRANSFORM) {
Quat q;
Vector3 p;
Vector3 s;
default_anim->transform_track_interpolate(j,from,&p,&q,&s);
new_anim->transform_track_insert_key(dtrack,0,p,q,s);
}
}
}
if (default_anim->track_get_type(j)==Animation::TYPE_TRANSFORM) {
Quat q;
Vector3 p;
Vector3 s;
default_anim->transform_track_get_key(j,k,&p,&q,&s);
new_anim->transform_track_insert_key(dtrack,kt-from,p,q,s);
}
}
if (dtrack!=-1 && kt>=to) {
if (default_anim->track_get_type(j)==Animation::TYPE_TRANSFORM) {
Quat q;
Vector3 p;
Vector3 s;
default_anim->transform_track_interpolate(j,to,&p,&q,&s);
new_anim->transform_track_insert_key(dtrack,to-from,p,q,s);
}
}
}
if (dtrack==-1 && p_bake_all) {
new_anim->add_track(default_anim->track_get_type(j));
dtrack = new_anim->get_track_count()-1;
new_anim->track_set_path(dtrack,default_anim->track_get_path(j));
if (default_anim->track_get_type(j)==Animation::TYPE_TRANSFORM) {
Quat q;
Vector3 p;
Vector3 s;
default_anim->transform_track_interpolate(j,from,&p,&q,&s);
new_anim->transform_track_insert_key(dtrack,0,p,q,s);
default_anim->transform_track_interpolate(j,to,&p,&q,&s);
new_anim->transform_track_insert_key(dtrack,to-from,p,q,s);
}
}
}
new_anim->set_loop(loop);
new_anim->set_length(to-from);
anim->add_animation(name,new_anim);
}
anim->remove_animation("default"); //remove default (no longer needed)
}
void EditorSceneImportPlugin::_filter_anim_tracks(Ref<Animation> anim,Set<String> &keep) {
Ref<Animation> a = anim;
ERR_FAIL_COND(!a.is_valid());
print_line("From Anim "+anim->get_name()+":");
for(int j=0;j<a->get_track_count();j++) {
String path = a->track_get_path(j);
if (!keep.has(path)) {
print_line("Remove: "+path);
a->remove_track(j);
j--;
}
}
}
void EditorSceneImportPlugin::_filter_tracks(Node *scene, const String& p_text) {
if (!scene->has_node(String("AnimationPlayer")))
return;
Node* n = scene->get_node(String("AnimationPlayer"));
ERR_FAIL_COND(!n);
AnimationPlayer *anim = n->cast_to<AnimationPlayer>();
ERR_FAIL_COND(!anim);
Vector<String> strings = p_text.split("\n");
for(int i=0;i<strings.size();i++) {
strings[i]=strings[i].strip_edges();
}
List<StringName> anim_names;
anim->get_animation_list(&anim_names);
for(List<StringName>::Element *E=anim_names.front();E;E=E->next()) {
String name = E->get();
bool valid_for_this=false;
bool valid=false;
Set<String> keep;
Set<String> keep_local;
for(int i=0;i<strings.size();i++) {
if (strings[i].begins_with("@")) {
valid_for_this=false;
for(Set<String>::Element *F=keep_local.front();F;F=F->next()) {
keep.insert(F->get());
}
keep_local.clear();
Vector<String> filters=strings[i].substr(1,strings[i].length()).split(",");
for(int j=0;j<filters.size();j++) {
String fname = filters[j].strip_edges();
if (fname=="")
continue;
int fc = fname[0];
bool plus;
if (fc=='+')
plus=true;
else if (fc=='-')
plus=false;
else
continue;
String filter=fname.substr(1,fname.length()).strip_edges();
if (!name.matchn(filter))
continue;
valid_for_this=plus;
}
if (valid_for_this)
valid=true;
} else if (valid_for_this) {
Ref<Animation> a = anim->get_animation(name);
if (!a.is_valid())
continue;
for(int j=0;j<a->get_track_count();j++) {
String path = a->track_get_path(j);
String tname = strings[i];
if (tname=="")
continue;
int fc = tname[0];
bool plus;
if (fc=='+')
plus=true;
else if (fc=='-')
plus=false;
else
continue;
String filter=tname.substr(1,tname.length()).strip_edges();
if (!path.matchn(filter))
continue;
if (plus)
keep_local.insert(path);
else if (!keep.has(path)) {
keep_local.erase(path);
}
}
}
}
if (valid) {
for(Set<String>::Element *F=keep_local.front();F;F=F->next()) {
keep.insert(F->get());
}
print_line("FILTERING ANIM: "+String(E->get()));
_filter_anim_tracks(anim->get_animation(name),keep);
} else {
print_line("NOT FILTERING ANIM: "+String(E->get()));
}
}
}
void EditorSceneImportPlugin::_optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle) {
if (!scene->has_node(String("AnimationPlayer")))
return;
Node* n = scene->get_node(String("AnimationPlayer"));
ERR_FAIL_COND(!n);
AnimationPlayer *anim = n->cast_to<AnimationPlayer>();
ERR_FAIL_COND(!anim);
List<StringName> anim_names;
anim->get_animation_list(&anim_names);
for(List<StringName>::Element *E=anim_names.front();E;E=E->next()) {
Ref<Animation> a = anim->get_animation(E->get());
a->optimize(p_max_lin_error,p_max_ang_error,Math::deg2rad(p_max_angle));
}
}
Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, const Ref<ResourceImportMetadata>& p_from) {
Error err=OK;
Ref<ResourceImportMetadata> from=p_from;
String src_path=EditorImportPlugin::expand_source_path(from->get_source_path(0));
int animation_flags=p_from->get_option("animation_flags");
Array animation_clips = p_from->get_option("animation_clips");
String animation_filter = p_from->get_option("animation_filters");
int scene_flags = from->get_option("flags");
float anim_optimizer_linerr=0.05;
float anim_optimizer_angerr=0.01;
float anim_optimizer_maxang=22;
if (from->has_option("animation_optimizer_linear_error"))
anim_optimizer_linerr=from->get_option("animation_optimizer_linear_error");
if (from->has_option("animation_optimizer_angular_error"))
anim_optimizer_angerr=from->get_option("animation_optimizer_angular_error");
if (from->has_option("animation_optimizer_max_angle"))
anim_optimizer_maxang=from->get_option("animation_optimizer_max_angle");
EditorProgress progress("import","Import Scene",104);
progress.step("Importing Scene..",2);
bool reimport = bool(from->get_option("reimport"));
int this_time_action = from->get_option("import_this_time");
int next_time_action = from->get_option("import_next_time");
int import_action = reimport?this_time_action:next_time_action;
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);
Map< Ref<ImageTexture>,TextureRole > imagemap;
scene=_fix_node(scene,scene,collision_map,scene_flags,imagemap);
if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE)
_optimize_animations(scene,anim_optimizer_linerr,anim_optimizer_angerr,anim_optimizer_maxang);
if (animation_clips.size())
_create_clips(scene,animation_clips,animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS);
_filter_tracks(scene,animation_filter);
/// BEFORE ANYTHING, RUN SCRIPT
progress.step("Running Custom Script..",2);
String post_import_script_path = from->get_option("post_import_script");
Ref<EditorScenePostImport> post_import_script;
if (post_import_script_path!="") {
post_import_script_path = post_import_script_path;
Ref<Script> scr = ResourceLoader::load(post_import_script_path);
if (!scr.is_valid()) {
EditorNode::add_io_error("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()) {
scene = post_import_script->post_import(scene);
if (!scene) {
EditorNode::add_io_error("Error running Post-Import script: '"+post_import_script_path);
return err;
}
}
/// IMPORT IMAGES
int idx=0;
int image_format = from->get_option("texture_format");
int image_flags = from->get_option("texture_flags");
float image_quality = from->get_option("texture_quality");
for (Map< Ref<ImageTexture>,TextureRole >::Element *E=imagemap.front();E;E=E->next()) {
//texture could be converted to something more useful for 3D, that could load individual mipmaps and stuff
//but not yet..
Ref<ImageTexture> texture = E->key();
ERR_CONTINUE(!texture.is_valid());
String path = texture->get_path();
String fname= path.get_file();
String target_path = Globals::get_singleton()->localize_path(target_res_path.plus_file(fname));
progress.step("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";
Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata );
print_line("flags: "+itos(image_flags));
uint32_t flags = image_flags;
if (E->get()==TEXTURE_ROLE_DIFFUSE && scene_flags&SCENE_FLAG_LINEARIZE_DIFFUSE_TEXTURES)
flags|=EditorTextureImportPlugin::IMAGE_FLAG_CONVERT_TO_LINEAR;
if (E->get()==TEXTURE_ROLE_NORMALMAP && scene_flags&SCENE_FLAG_CONVERT_NORMALMAPS_TO_XY)
flags|=EditorTextureImportPlugin::IMAGE_FLAG_CONVERT_NORMAL_TO_XY;
imd->set_option("flags",flags);
imd->set_option("format",image_format);
imd->set_option("quality",image_quality);
imd->set_option("atlas",false);
imd->add_source(EditorImportPlugin::validate_source_path(path));
if (FileAccess::exists(target_path)) {
Ref<ResourceImportMetadata> rimdex = ResourceLoader::load_import_metadata(target_path);
if (rimdex.is_valid()) {
//make sure the options are the same, otherwise re-import
List<String> opts;
imd->get_options(&opts);
bool differ=false;
for (List<String>::Element *E=opts.front();E;E=E->next()) {
if (!(rimdex->get_option(E->get())==imd->get_option(E->get()))) {
differ=true;
break;
}
}
if (!differ) {
texture->set_path(target_path);
continue; //already imported
}
}
}
Error err = EditorTextureImportPlugin::get_singleton(EditorTextureImportPlugin::MODE_TEXTURE_3D)->import(target_path,imd);
}
}
/// BEFORE SAVING - MERGE
if (import_action!=SCENE_UPDATE_REPLACE_WITH_NEW) {
progress.step("Merging..",103);
FileAccess *fa = FileAccess::create(FileAccess::ACCESS_RESOURCES);
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) {
switch(import_action) {
case SCENE_UPDATE_REPLACE_WITH_NEW: break;
case SCENE_UPDATE_REPLACE_WITH_NEW_KEEP_MATERIALS: {
_merge_materials(existing,scene);
memdelete(existing);
} break;
case SCENE_UPDATE_KEEP_OLD_MERGE_CHANGES: {
_merge_scenes(existing,scene);
memdelete(scene);
scene=existing;
} break;
case SCENE_UPDATE_KEEP_OLD: {
memdelete(scene);
scene=existing;
} break;
}
}
}
}
memdelete(fa);
}
progress.step("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,ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS);
//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);
*/
return err;
}
Error EditorSceneImportPlugin::import(const String& p_dest_path, const Ref<ResourceImportMetadata>& p_from){
Node *n=NULL;
Error err = import1(p_from,&n);
if (err!=OK) {
if (n) {
memdelete(n);
}
return err;
}
return import2(n,p_dest_path,p_from);
}
void EditorSceneImportPlugin::add_importer(const Ref<EditorSceneImporter>& p_importer) {
importers.push_back(p_importer);
}
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) {
}