From 27eae4ada1eeecd63f8733bfc477e22508c893a6 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sat, 4 Feb 2017 09:48:04 -0300 Subject: Scene import more or less working, needs some missing features. --- tools/editor/editor_node.cpp | 13 +- tools/editor/import/editor_import_collada.cpp | 2468 +++++++++++++++++++++ tools/editor/import/editor_import_collada.h | 51 + tools/editor/import/resource_importer_scene.cpp | 1193 ++++++++++ tools/editor/import/resource_importer_scene.h | 87 + tools/editor/io_plugins/editor_import_collada.cpp | 2468 --------------------- tools/editor/io_plugins/editor_import_collada.h | 51 - 7 files changed, 3811 insertions(+), 2520 deletions(-) create mode 100644 tools/editor/import/editor_import_collada.cpp create mode 100644 tools/editor/import/editor_import_collada.h create mode 100644 tools/editor/import/resource_importer_scene.cpp create mode 100644 tools/editor/import/resource_importer_scene.h delete mode 100644 tools/editor/io_plugins/editor_import_collada.cpp delete mode 100644 tools/editor/io_plugins/editor_import_collada.h diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index 9607ee9876..03b65992fc 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -103,6 +103,7 @@ #include "import/resource_importer_csv_translation.h" #include "import/resource_importer_wav.h" #include "import/resource_importer_obj.h" +#include "import/resource_importer_scene.h" // end #include "editor_settings.h" #include "io_plugins/editor_texture_import_plugin.h" @@ -113,7 +114,7 @@ #include "io_plugins/editor_bitmask_import_plugin.h" #include "io_plugins/editor_mesh_import_plugin.h" #include "io_plugins/editor_export_scene.h" -#include "io_plugins/editor_import_collada.h" +#include "import/editor_import_collada.h" #include "io_plugins/editor_scene_importer_fbxconv.h" #include "plugins/editor_preview_plugins.h" @@ -5137,6 +5138,16 @@ EditorNode::EditorNode() { import_obj.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_obj); + Ref import_scene; + import_scene.instance(); + ResourceFormatImporter::get_singleton()->add_importer(import_scene); + + { + Ref import_collada; + import_collada.instance(); + import_scene->add_importer(import_collada); + } + } _pvrtc_register_compressors(); diff --git a/tools/editor/import/editor_import_collada.cpp b/tools/editor/import/editor_import_collada.cpp new file mode 100644 index 0000000000..10b9dda2cb --- /dev/null +++ b/tools/editor/import/editor_import_collada.cpp @@ -0,0 +1,2468 @@ +/*************************************************************************/ +/* editor_import_collada.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "editor_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" +#include + + +struct ColladaImport { + + Collada collada; + Spatial *scene; + + Vector > animations; + + struct NodeMap { + //String path; + Spatial *node; + int bone; + List anim_tracks; + + NodeMap() { node=NULL; bone=-1; } + }; + + bool found_ambient; + Color ambient; + bool found_directional; + bool force_make_tangents; + bool apply_mesh_xform_to_vertices; + float bake_fps; + + + + Map node_map; //map from collada node to engine node + Map node_name_map; //map from collada node to engine node + Map > mesh_cache; + Map > curve_cache; + Map > material_cache; + Map skeleton_map; + + Map< Skeleton*, Map< String, int> > skeleton_bone_map; + + Set valid_animated_nodes; + Vector valid_animated_properties; + Map bones_with_animation; + + Error _populate_skeleton(Skeleton *p_skeleton,Collada::Node *p_node, int &r_bone, int p_parent); + Error _create_scene_skeletons(Collada::Node *p_node); + 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(bool p_optimize, Ref& p_mesh, const Map& p_material_map, const Collada::MeshData &meshdata, const Transform& p_local_xform, const Vector &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data, Vector > p_morph_meshes=Vector >(), bool p_for_morph=false); + 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,bool p_make_tracks_in_all_bones, bool p_import_value_tracks); + void create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks); + + Set tracks_in_clips; + Vector missing_textures; + + void _pre_process_lights(Collada::Node *p_node); + + ColladaImport() { + + found_ambient=false; + found_directional=false; + force_make_tangents=false; + apply_mesh_xform_to_vertices=true; + bake_fps=15; + + } +}; + + +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(p_node); + + print_line("populating joint "+joint->name); + 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; + node_name_map[p_node->name]=p_node->id; + + 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;ichildren.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(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;ichildren.size();i++) + _pre_process_lights(p_node->children[i]); +} + +Error ColladaImport::_create_scene_skeletons(Collada::Node *p_node) { + + + if (p_node->type==Collada::Node::TYPE_SKELETON) { + + Skeleton *sk = memnew( Skeleton ); + int bone = 0; + + for(int i=0;ichildren.size();i++) { + + _populate_skeleton(sk,p_node->children[i],bone,-1); + } + sk->localize_rests(); //after creating skeleton, rests must be localized...! + skeleton_map[p_node]=sk; + } + + + for(int i=0;ichildren.size();i++) { + + Error err = _create_scene_skeletons(p_node->children[i]); + if (err) + return err; + } + return OK; + +} + + +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(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(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.y_mag) { + + camera->set_keep_aspect_mode(Camera::KEEP_HEIGHT); + camera->set_orthogonal(cd.orthogonal.y_mag*2.0 ,cd.z_near,cd.z_far); + + } else if (!cd.orthogonal.y_mag && cd.orthogonal.x_mag) { + + + camera->set_keep_aspect_mode(Camera::KEEP_WIDTH); + camera->set_orthogonal(cd.orthogonal.x_mag*2.0,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(p_node); + + if (collada.state.curve_data_map.has(ng->source)) { + + node = memnew( Path ); + } else { + //mesh since nothing else + node = memnew( MeshInstance ); + node->cast_to()->set_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT,true); + } + } break; + case Collada::Node::TYPE_SKELETON: { + + ERR_FAIL_COND_V(!skeleton_map.has(p_node),ERR_CANT_CREATE); + Skeleton *sk = skeleton_map[p_node]; + node=sk; + } break; + + } + + if (p_node->name!="") + node->set_name(p_node->name); + NodeMap nm; + nm.node=node; + node_map[p_node->id]=nm; + node_name_map[p_node->name]=p_node->id; + 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); + + if (p_node->empty_draw_type!="") { + node->set_meta("empty_draw_type", Variant(p_node->empty_draw_type)); + } + + for(int i=0;ichildren.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 material= memnew( FixedSpatialMaterial ); + + if (src_mat.name!="") + material->set_name(src_mat.name); + else if (effect.name!="") + material->set_name(effect.name); + + // DIFFUSE + + if (effect.diffuse.texture!="") { + + String texfile = effect.get_texture_path(effect.diffuse.texture,collada); + if (texfile!="") { + + Ref texture = ResourceLoader::load(texfile,"Texture"); + if (texture.is_valid()) { + + material->set_texture(FixedSpatialMaterial::TEXTURE_ALBEDO,texture); + material->set_albedo(Color(1,1,1,1)); + //material->set_parameter(FixedSpatialMaterial::PARAM_DIFFUSE,Color(1,1,1,1)); + } else { + missing_textures.push_back(texfile.get_file()); + } + } + } else { + //material->set_parameter(FixedSpatialMaterial::PARAM_DIFFUSE,effect.diffuse.color); + } + + // SPECULAR + + if (effect.specular.texture!="") { + + String texfile = effect.get_texture_path(effect.specular.texture,collada); + if (texfile!="") { + + Ref texture = ResourceLoader::load(texfile,"Texture"); + if (texture.is_valid()) { + material->set_texture(FixedSpatialMaterial::TEXTURE_SPECULAR,texture); + material->set_specular(Color(1,1,1,1)); + + //material->set_texture(FixedSpatialMaterial::PARAM_SPECULAR,texture); + //material->set_parameter(FixedSpatialMaterial::PARAM_SPECULAR,Color(1,1,1,1)); + } else { + missing_textures.push_back(texfile.get_file()); + } + + } + } else { + //material->set_parameter(FixedSpatialMaterial::PARAM_SPECULAR,effect.specular.color); + } + + // EMISSION + + if (effect.emission.texture!="") { + + String texfile = effect.get_texture_path(effect.emission.texture,collada); + if (texfile!="") { + + Ref texture = ResourceLoader::load(texfile,"Texture"); + if (texture.is_valid()) { + + material->set_texture(FixedSpatialMaterial::TEXTURE_EMISSION,texture); + material->set_emission(Color(1,1,1,1)); + + //material->set_parameter(FixedSpatialMaterial::PARAM_EMISSION,Color(1,1,1,1)); + }else { + //missing_textures.push_back(texfile.get_file()); + } + + } + } else { + //material->set_parameter(FixedSpatialMaterial::PARAM_EMISSION,effect.emission.color); + } + + // NORMAL + + if (effect.bump.texture!="") { + + String texfile = effect.get_texture_path(effect.bump.texture,collada); + if (texfile!="") { + + Ref texture = ResourceLoader::load(texfile,"Texture"); + if (texture.is_valid()) { + material->set_texture(FixedSpatialMaterial::TEXTURE_NORMAL,texture); + //material->set_emission(Color(1,1,1,1)); + + //material->set_texture(FixedSpatialMaterial::PARAM_NORMAL,texture); + }else { + //missing_textures.push_back(texfile.get_file()); + } + + } + } + + + //material->set_parameter(FixedSpatialMaterial::PARAM_SPECULAR_EXP,effect.shininess); + if (effect.double_sided) { + material->set_cull_mode(FixedSpatialMaterial::CULL_DISABLED); + } + material->set_flag(FixedSpatialMaterial::FLAG_UNSHADED,effect.unshaded); + + + + material_cache[p_target]=material; + return OK; +} + + +static void _generate_normals(const PoolVector& p_indices,const PoolVector& p_vertices,PoolVector&r_normals) { + + + r_normals.resize(p_vertices.size()); + PoolVector::Write narrayw = r_normals.write(); + + int iacount=p_indices.size()/3; + PoolVector::Read index_arrayr = p_indices.read(); + PoolVector::Read vertex_arrayr = p_vertices.read(); + + for(int idx=0;idx& p_indices,const PoolVector& p_vertices,const PoolVector& p_uvs,const PoolVector& p_normals,PoolVector&r_tangents) { + + int vlen=p_vertices.size(); + + Vector tangents; + tangents.resize(vlen); + Vector binormals; + binormals.resize(vlen); + + + int iacount=p_indices.size()/3; + + PoolVector::Read index_arrayr = p_indices.read(); + PoolVector::Read vertex_arrayr = p_vertices.read(); + PoolVector::Read narrayr = p_normals.read(); + PoolVector::Read uvarrayr = p_uvs.read(); + + + for(int idx=0;idx::Write tarrayw = r_tangents.write(); + + for(int idx=0;idx& p_mesh,const Map& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector &bone_remap, const Collada::SkinControllerData *skin_controller, const Collada::MorphControllerData *p_morph_data,Vector > p_morph_meshes,bool p_for_morph) { + + + 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;isources[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_blend_shape(name); + } + if (p_morph_data->mode=="RELATIVE") + p_mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_RELATIVE); + else if (p_morph_data->mode=="NORMALIZED") + p_mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_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 (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.. + + /************************/ + /* ADD WEIGHTS IF EXIST */ + /************************/ + + Map > pre_weights; + + 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_iweights.sets.size();w_i++) { + + int amount = skin_controller->weights.sets[w_i]; + + Vector weights; + + for (int a_i=0;a_iweights.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::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; + + }*/ + + + + + index_ofs+=wstride*amount; + + } + + //vertices need to be localized + has_weights=true; + + } + + Set vertex_set; //vertex set will be the vertices + List indices_list; //indices will be the indices + //Map > 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; + int vertidx=0; + for(int p_i=0;p_istride?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 (pre_weights.has(vertex_index)) { + vertex.weights=pre_weights[vertex_index]; + } + + 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; + SWAP( vertex.tangent.normal.z, vertex.tangent.normal.y ); + vertex.tangent.normal.z = -vertex.tangent.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(); + 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 vertex_array; //there we go, vertex array + + vertex_array.resize(vertex_set.size()); + for(Set::Element *F=vertex_set.front();F;F=F->next()) { + + vertex_array[F->get().idx]=F->get(); + } + + + if (has_weights) { + + //if skeleton, localize + Transform local_xform = p_local_xform; + for(int i=0;i index_array; + index_array.resize(indices_list.size()); + PoolVector::Write index_arrayw = index_array.write(); + + int iidx=0; + for(List::Element *F=indices_list.front();F;F=F->next()) { + + index_arrayw[iidx++]=F->get(); + } + + index_arrayw=PoolVector::Write(); + + + /*****************/ + /* MAKE SURFACES */ + /*****************/ + + + { + + Ref 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); + } + } + + + + PoolVector final_vertex_array; + PoolVector final_normal_array; + PoolVector final_tangent_array; + PoolVector final_color_array; + PoolVector final_uv_array; + PoolVector final_uv2_array; + PoolVector final_bone_array; + PoolVector 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 + + PoolVector varray; + varray.resize(vertex_array.size()); + + PoolVector::Write varrayw = varray.write(); + + for(int k=0;k::Write(); + final_vertex_array=varray; + + } + + + if (uv_src) { //compute uv first, may be needed for computing tangent/bionrmal + PoolVector uvarray; + uvarray.resize(vertex_array.size()); + PoolVector::Write uvarrayw = uvarray.write(); + + for(int k=0;k::Write(); + final_uv_array=uvarray; + + } + + if (uv2_src) { //compute uv first, may be needed for computing tangent/bionrmal + PoolVector uv2array; + uv2array.resize(vertex_array.size()); + PoolVector::Write uv2arrayw = uv2array.write(); + + for(int k=0;k::Write(); + final_uv2_array=uv2array; + + } + + if (normal_src) { + PoolVector narray; + narray.resize(vertex_array.size()); + PoolVector::Write narrayw = narray.write(); + + for(int k=0;k::Write(); + final_normal_array=narray; + + /* + PoolVector altnaray; + _generate_normals(index_array,final_vertex_array,altnaray); + + for(int i=0;iis_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) { + + PoolVector tarray; + tarray.resize(vertex_array.size()*4); + PoolVector::Write tarrayw = tarray.write(); + + + for(int k=0;k::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()))){ + //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) { + PoolVector colorarray; + colorarray.resize(vertex_array.size()); + PoolVector::Write colorarrayw = colorarray.write(); + + for(int k=0;k::Write(); + + final_color_array=colorarray; + } + + if (has_weights) { + PoolVector weightarray; + PoolVector bonearray; + + weightarray.resize(vertex_array.size()*4); + PoolVector::Write weightarrayw = weightarray.write(); + bonearray.resize(vertex_array.size()*4); + PoolVector::Write bonearrayw = bonearray.write(); + + for(int k=0;k::Write(); + bonearrayw = PoolVector::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 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(); + mr.resize(morph_targets); + + for(int j=0;jsources[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. + + PoolVector 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 + PoolVector::Write vertw = vertices.write(); + + for(int m_i=0;m_iarray.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 ::Element *E=vertex_map[m_i].front() ; E; E=E->next() ) { + + vertw[E->get()]=vtx; + } + } + } + + + //vertices are in place, now generate everything else + vertw = PoolVector::Write(); + PoolVector normals; + PoolVector tangents; + print_line("vertex source id: "+vertex_src_id); + if(md.vertices[vertex_src_id].sources.has("NORMAL")){ + //has normals + normals.resize(vlen); + //std::cout << "has normals" << std::endl; + String normal_src_id = md.vertices[vertex_src_id].sources["NORMAL"]; + //std::cout << "normals source: "<< normal_src_id.utf8().get_data() <array.size() != vertex_src->array.size(), ERR_INVALID_DATA); + int stride=m->stride; + if (stride==0) + stride=3; + + + //read normals from morph target + PoolVector::Write vertw = normals.write(); + + for(int m_i=0;m_iarray.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 ::Element *E=vertex_map[m_i].front() ; E; E=E->next() ) { + + vertw[E->get()]=vtx; + } + } + } + + print_line("using built-in normals"); + }else{ + print_line("generating normals"); + _generate_normals(index_array,vertices,normals);//no 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; + + } + + } + +#endif + for(int mi=0;miget_surface_count())); + Array a = p_morph_meshes[mi]->surface_get_arrays(surface); + //add valid weight and bone arrays if they exist, TODO check if they are unique to shape (generally not) + + if (final_weight_array.size()) + a[Mesh::ARRAY_WEIGHTS]=final_weight_array; + if (final_bone_array.size()) + a[Mesh::ARRAY_BONES]=final_bone_array; + + a[Mesh::ARRAY_INDEX]=Variant(); + //a.resize(Mesh::ARRAY_MAX); //no need for index + mr.push_back(a); + } + + + p_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES,d,mr,p_for_morph?0:Mesh::ARRAY_COMPRESS_DEFAULT); + + 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(p_node); + + + if (node->cast_to()) { + + Path *path = node->cast_to(); + + String curve = ng->source; + + if (curve_cache.has(ng->source)) { + + path->set_curve(curve_cache[ng->source]); + } else { + + Ref 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;iadd_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()) { + + + Collada::NodeGeometry *ng = static_cast(p_node); + + MeshInstance *mi = node->cast_to(); + + + ERR_FAIL_COND_V(!mi,ERR_BUG); + + + Collada::SkinControllerData *skin=NULL; + Collada::MorphControllerData *morph=NULL; + String meshid; + Transform apply_xform; + Vector bone_remap; + Vector > morphs; + + print_line("mesh: "+String(mi->get_name())); + + if (ng->controller) { + + print_line("has controller"); + + String ngsource = ng->source; + + if (collada.state.skin_controller_data_map.has(ngsource)) { + + + ERR_FAIL_COND_V(!collada.state.skin_controller_data_map.has(ngsource),ERR_INVALID_DATA); + skin=&collada.state.skin_controller_data_map[ngsource]; + + Vector skeletons = ng->skeletons; + + ERR_FAIL_COND_V( skeletons.empty(), ERR_INVALID_DATA ); + + String skname = skeletons[0]; + if (!node_map.has(skname)) { + print_line("no node for skeleton "+skname); + } + ERR_FAIL_COND_V( !node_map.has(skname), ERR_INVALID_DATA ); + NodeMap nmsk = node_map[skname]; + Skeleton *sk = nmsk.node->cast_to(); + ERR_FAIL_COND_V( !sk, ERR_INVALID_DATA ); + ERR_FAIL_COND_V( !skeleton_bone_map.has(sk), ERR_INVALID_DATA ); + Map &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]; + ngsource=meshid; + meshid=morph->mesh; + } else { + ngsource=""; + } + + if (apply_mesh_xform_to_vertices) { + apply_xform=collada.fix_transform(p_node->default_transform); + node->set_transform(Transform()); + } else { + apply_xform=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;isarray[i]; + if (!bone_remap_map.has(str)) { + print_line("bone not found for remap: "+str); + print_line("in skeleton: "+skname); + } + ERR_FAIL_COND_V( !bone_remap_map.has(str), ERR_INVALID_DATA ); + bone_remap[i]=bone_remap_map[str]; + } + } + + if (collada.state.morph_controller_data_map.has(ngsource)) { + print_line("is morph "+ngsource); + //it's a morph!! + morph = &collada.state.morph_controller_data_map[ngsource]; + meshid=morph->mesh; + printf("KKmorph: %p\n",morph); + print_line("morph mshid: "+meshid); + + Vector targets; + + morph->targets.has("MORPH_TARGET"); + String target = morph->targets["MORPH_TARGET"]; + bool valid=false; + if (morph->sources.has(target)) { + valid=true; + Vector names = morph->sources[target].sarray; + for(int i=0;i mesh=Ref(memnew( Mesh )); + const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid]; + Error err = _create_mesh_surfaces(false,mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,NULL,Vector >(),true); + ERR_FAIL_COND_V(err,err); + + morphs.push_back(mesh); + } else { + valid=false; + } + } + } + + if (!valid) + morphs.clear(); + + ngsource=""; + } + + if (ngsource!=""){ + ERR_EXPLAIN("Controller Instance Source '"+ngsource+"' is neither skin or morph!"); + ERR_FAIL_V( ERR_INVALID_DATA ); + } + + + + } else { + meshid=ng->source; + } + + Ref 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(memnew( Mesh )); + const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid]; + mesh->set_name( meshdata.name ); + Error err = _create_mesh_surfaces(morphs.size()==0,mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,morph,morphs); + 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;ichildren.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;iset_transform(collada.get_root_transform()); + + + return OK; + +} + +void ColladaImport::_fix_param_animation_tracks() { + + for (Map::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(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"]; + //fails here + + 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&rt = collada.state.referenced_tracks[track_name]; + + for(int rti=0;rtitarget=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(bool p_make_tracks_in_all_bones, bool p_import_value_tracks) { + + + _fix_param_animation_tracks(); + for(int i=0;i animation = Ref( 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::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 track_filter; + + + if (p_clip==-1) { + + for(int i=0;i&ti = collada.state.by_id_tracks[n]; + for(int k=0;k&ti = collada.state.by_id_tracks[n]; + for(int k=0;kset_loop(true); + //create animation tracks + + Vector base_snapshots; + + float f=0; + float snapshot_interval = 1.0/bake_fps; //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=anim_length) { + base_snapshots.push_back(anim_length); + + } + } + + //print_line("anim len: "+rtos(anim_length)); + animation->set_length(anim_length); + + bool tracks_found=false; + + + + for(Set::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(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) { + + continue; + } + + + + animation->add_track(Animation::TYPE_TRANSFORM); + int track = animation->get_track_count() -1; + animation->track_set_path( track , path ); + animation->track_set_imported( track , true ); //helps merging later + + Vector 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::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;jxform_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 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(); + 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 ); + } + + } + + if (p_make_tracks_in_all_bones) { + + //some bones may lack animation, but since we don't store pose as a property, we must add keyframes! + for(Map::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(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 ); + animation->track_set_imported( track , true ); //helps merging later + + + 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; + } + } + + + + if (p_import_value_tracks) { + 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); + + animation->add_track(Animation::TYPE_VALUE); + int track = animation->get_track_count() - 1; + + path = path + ":" + at.param; + animation->track_set_path(track, path); + animation->track_set_imported(track, true); //helps merging later + + + for (int i = 0; i < at.keys.size(); i++) { + + float time = at.keys[i].time; + Variant value; + Vector 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 *r_extensions) const { + + + r_extensions->push_back("dae"); +} +Node* EditorSceneImporterCollada::import_scene(const String& p_path, uint32_t p_flags,int p_bake_fps, List *r_missing_deps, Error* r_err) { + + ColladaImport state; + uint32_t flags=Collada::IMPORT_FLAG_SCENE; + if (p_flags&IMPORT_ANIMATION) + flags|=Collada::IMPORT_FLAG_ANIMATION; + + state.bake_fps=p_bake_fps; + + 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;ipush_back(state.missing_textures[i]); + } + + } + } + + if (p_flags&IMPORT_ANIMATION) { + + state.create_animations(p_flags&IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS,p_flags&EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS); + AnimationPlayer *ap = memnew( AnimationPlayer ); + for(int i=0;iget_name()=="") + name="default"; + else + name=state.animations[i]->get_name(); + + 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 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(p_flags&EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS,p_flags&EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS); + if (state.scene) + memdelete(state.scene); + + if (state.animations.size()==0) + return Ref(); + Ref anim=state.animations[0]; + anim=state.animations[0]; + print_line("Anim Load OK"); + String base = p_path.get_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); + } + } + + + return anim; +} + + +EditorSceneImporterCollada::EditorSceneImporterCollada() { + + +} + diff --git a/tools/editor/import/editor_import_collada.h b/tools/editor/import/editor_import_collada.h new file mode 100644 index 0000000000..cd3614bb40 --- /dev/null +++ b/tools/editor/import/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-2017 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef EDITOR_IMPORT_COLLADA_H +#define EDITOR_IMPORT_COLLADA_H + +#include "tools/editor/import/resource_importer_scene.h" + + + +class EditorSceneImporterCollada : public EditorSceneImporter { + + GDCLASS(EditorSceneImporterCollada,EditorSceneImporter ); +public: + + virtual uint32_t get_import_flags() const; + virtual void get_extensions(List *r_extensions) const; + virtual Node* import_scene(const String& p_path,uint32_t p_flags,int p_bake_fps,List *r_missing_deps=NULL,Error* r_err=NULL); + virtual Ref import_animation(const String& p_path,uint32_t p_flags); + + EditorSceneImporterCollada(); +}; + + +#endif + diff --git a/tools/editor/import/resource_importer_scene.cpp b/tools/editor/import/resource_importer_scene.cpp new file mode 100644 index 0000000000..5d886615f3 --- /dev/null +++ b/tools/editor/import/resource_importer_scene.cpp @@ -0,0 +1,1193 @@ +#include "resource_importer_scene.h" + +#include "scene/resources/packed_scene.h" +#include "io/resource_saver.h" +#include "tools/editor/editor_node.h" + +#include "scene/3d/mesh_instance.h" +#include "scene/3d/navigation.h" +#include "scene/3d/room_instance.h" +#include "scene/3d/body_shape.h" +#include "scene/3d/physics_body.h" +#include "scene/3d/portal.h" +#include "scene/3d/vehicle_body.h" +#include "scene/resources/sphere_shape.h" +#include "scene/resources/box_shape.h" +#include "scene/resources/ray_shape.h" +#include "scene/resources/plane_shape.h" + + +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() { + + +} + + +String ResourceImporterScene::get_importer_name() const { + + return "scene"; +} + +String ResourceImporterScene::get_visible_name() const{ + + return "Scene"; +} + +void ResourceImporterScene::get_recognized_extensions(List *p_extensions) const{ + + for (Set< Ref >::Element *E=importers.front();E;E=E->next()) { + E->get()->get_extensions(p_extensions); + } +} + +String ResourceImporterScene::get_save_extension() const { + return "scn"; +} + +String ResourceImporterScene::get_resource_type() const{ + + return "PackedScene"; +} + +bool ResourceImporterScene::get_option_visibility(const String& p_option,const Map& p_options) const { + + if (p_option.begins_with("animation/")) { + if (p_option!="animation/import" && !bool(p_options["animation/import"])) + return false; + + if (p_option.begins_with("animation/optimizer/") && p_option!="animation/optimizer/enabled" && !bool(p_options["animation/optimizer/enabled"])) + return false; + + if (p_option.begins_with("animation/clip_")) { + int max_clip = p_options["animation/clips/amount"]; + int clip = p_option.get_slice("/",1).get_slice("_",1).to_int()-1; + if (clip>=max_clip) + return false; + } + } + + return true; + +} + +int ResourceImporterScene::get_preset_count() const { + return 0; +} +String ResourceImporterScene::get_preset_name(int p_idx) const { + + return ""; +} + + +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; +} + + +Node* ResourceImporterScene::_fix_node(Node *p_node,Node *p_root,Map,Ref > &collision_map) { + + // children first.. + for(int i=0;iget_child_count();i++) { + + + Node *r = _fix_node(p_node->get_child(i),p_root,collision_map); + if (!r) { + print_line("was erased.."); + i--; //was erased + } + } + + String name = p_node->get_name(); + + bool isroot = p_node==p_root; + + + if (!isroot && _teststr(name,"noimp")) { + + memdelete(p_node); + return NULL; + } + + + if (p_node->cast_to()) { + + MeshInstance *mi = p_node->cast_to(); + + 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 m = mi->get_mesh(); + for(int i=0;iget_surface_count();i++) { + + Ref fm = m->surface_get_material(i); + if (fm.is_valid()) { + //fm->set_flag(Material::FLAG_UNSHADED,true); + //fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); + //fm->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + //fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + } + } + } + } + } + + + if (p_node->cast_to()) { + + MeshInstance *mi = p_node->cast_to(); + + Ref m = mi->get_mesh(); + + if (m.is_valid()) { + + for(int i=0;iget_surface_count();i++) { + + Ref mat = m->surface_get_material(i); + if (!mat.is_valid()) + continue; + + if (_teststr(mat->get_name(),"alpha")) { + + mat->set_feature(FixedSpatialMaterial::FEATURE_TRANSPARENT,true); + mat->set_name(_fixstr(mat->get_name(),"alpha")); + } + if (_teststr(mat->get_name(),"vcol")) { + + mat->set_flag(FixedSpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR,true); + mat->set_flag(FixedSpatialMaterial::FLAG_SRGB_VERTEX_COLOR,true); + mat->set_name(_fixstr(mat->get_name(),"vcol")); + } + + } + } + } + + if (p_node->cast_to()) { + //remove animations referencing non-importable nodes + AnimationPlayer *ap = p_node->cast_to(); + + List anims; + ap->get_animation_list(&anims); + for(List::Element *E=anims.front();E;E=E->next()) { + + Ref anim=ap->get_animation(E->get()); + ERR_CONTINUE(anim.is_null()); + for(int i=0;iget_track_count();i++) { + NodePath path = anim->track_get_path(i); + + for(int j=0;jremove_track(i); + i--; + break; + } + } + } + + } + } + + + if (p_node->cast_to()) { + + MeshInstance *mi = p_node->cast_to(); + + 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 *mi = p_node->cast_to(); + MeshInstance *mip = p_node->get_parent()->cast_to(); + 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 m = mi->get_mesh(); + for(int i=0;iget_surface_count();i++) { + + Ref fm = m->surface_get_material(i); + if (fm.is_valid()) { + //fm->set_flag(Material::FLAG_UNSHADED,true); + //fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); + //fm->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER); + //fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + } + } + } + } + } + } + } +#if 0 + if (p_flags&SCENE_FLAG_CREATE_LODS && p_node->cast_to()) { + + MeshInstance *mi = p_node->cast_to(); + + 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 *mi = p_node->cast_to(); + MeshInstance *mip = p_node->get_parent()->cast_to(); + 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 m = mi->get_mesh(); + for(int i=0;iget_surface_count();i++) { + + Ref fm = m->surface_get_material(i); + if (fm.is_valid()) { + fm->set_flag(Material::FLAG_UNSHADED,true); + fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); + fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true); + fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + } + } + }*/ + } + } + } + } + + + if (p_flags&SCENE_FLAG_DETECT_LIGHTMAP_LAYER && _teststr(name,"lm") && p_node->cast_to()) { + + MeshInstance *mi = p_node->cast_to(); + + String str=name; + int layer = str.substr(str.find("lm")+3,str.length()).to_int(); + //mi->set_baked_light_texture_id(layer); + } +#endif + if (_teststr(name,"colonly")) { + + if (isroot) + return p_node; + + if (p_node->cast_to()) { + MeshInstance *mi = p_node->cast_to(); + Node * col = mi->create_trimesh_collision_node(); + ERR_FAIL_COND_V(!col,NULL); + + col->set_name(_fixstr(name,"colonly")); + col->cast_to()->set_transform(mi->get_transform()); + p_node->replace_by(col); + memdelete(p_node); + p_node=col; + + StaticBody *sb = col->cast_to(); + CollisionShape *colshape = memnew( CollisionShape); + colshape->set_shape(sb->get_shape(0)); + colshape->set_name("shape"); + sb->add_child(colshape); + colshape->set_owner(p_node->get_owner()); + } else if (p_node->has_meta("empty_draw_type")) { + String empty_draw_type = String(p_node->get_meta("empty_draw_type")); + print_line(empty_draw_type); + StaticBody *sb = memnew( StaticBody); + sb->set_name(_fixstr(name,"colonly")); + sb->cast_to()->set_transform(p_node->cast_to()->get_transform()); + p_node->replace_by(sb); + memdelete(p_node); + CollisionShape *colshape = memnew( CollisionShape); + if (empty_draw_type == "CUBE") { + BoxShape *boxShape = memnew( BoxShape); + boxShape->set_extents(Vector3(1, 1, 1)); + colshape->set_shape(boxShape); + colshape->set_name("BoxShape"); + } else if (empty_draw_type == "SINGLE_ARROW") { + RayShape *rayShape = memnew( RayShape); + rayShape->set_length(1); + colshape->set_shape(rayShape); + colshape->set_name("RayShape"); + sb->cast_to()->rotate_x(Math_PI / 2); + } else if (empty_draw_type == "IMAGE") { + PlaneShape *planeShape = memnew( PlaneShape); + colshape->set_shape(planeShape); + colshape->set_name("PlaneShape"); + } else { + SphereShape *sphereShape = memnew( SphereShape); + sphereShape->set_radius(1); + colshape->set_shape(sphereShape); + colshape->set_name("SphereShape"); + } + sb->add_child(colshape); + colshape->set_owner(sb->get_owner()); + } + + } else if (_teststr(name,"rigid") && p_node->cast_to()) { + + if (isroot) + return p_node; + + // get mesh instance and bounding box + MeshInstance *mi = p_node->cast_to(); + Rect3 aabb = mi->get_aabb(); + + // create a new rigid body collision node + RigidBody * rigid_body = memnew( RigidBody ); + Node * col = rigid_body; + ERR_FAIL_COND_V(!col,NULL); + + // remove node name postfix + col->set_name(_fixstr(name,"rigid")); + // get mesh instance xform matrix to the rigid body collision node + col->cast_to()->set_transform(mi->get_transform()); + // save original node by duplicating it into a new instance and correcting the name + Node * mesh = p_node->duplicate(); + mesh->set_name(_fixstr(name,"rigid")); + // reset the xform matrix of the duplicated node so it can inherit parent node xform + mesh->cast_to()->set_transform(Transform(Basis())); + // reparent the new mesh node to the rigid body collision node + p_node->add_child(mesh); + mesh->set_owner(p_node->get_owner()); + // replace the original node with the rigid body collision node + p_node->replace_by(col); + memdelete(p_node); + p_node=col; + + // create an alias for the rigid body collision node + RigidBody *rb = col->cast_to(); + // create a new Box collision shape and set the right extents + Ref shape = memnew( BoxShape ); + shape->set_extents(aabb.get_size() * 0.5); + CollisionShape *colshape = memnew( CollisionShape); + colshape->set_name("shape"); + colshape->set_shape(shape); + // reparent the new collision shape to the rigid body collision node + rb->add_child(colshape); + colshape->set_owner(p_node->get_owner()); + + } else if (_teststr(name,"col") && p_node->cast_to()) { + + + MeshInstance *mi = p_node->cast_to(); + + 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(); + 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 (_teststr(name,"navmesh") && p_node->cast_to()) { + + if (isroot) + return p_node; + + MeshInstance *mi = p_node->cast_to(); + + Ref mesh=mi->get_mesh(); + ERR_FAIL_COND_V(mesh.is_null(),NULL); + NavigationMeshInstance *nmi = memnew( NavigationMeshInstance ); + + + nmi->set_name(_fixstr(name,"navmesh")); + Ref nmesh = memnew( NavigationMesh); + nmesh->create_from_mesh(mesh); + nmi->set_navigation_mesh(nmesh); + nmi->cast_to()->set_transform(mi->get_transform()); + p_node->replace_by(nmi); + memdelete(p_node); + p_node=nmi; + } else if (_teststr(name,"vehicle")) { + + if (isroot) + return p_node; + + Node *owner = p_node->get_owner(); + Spatial *s = p_node->cast_to(); + 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 (_teststr(name,"wheel")) { + + if (isroot) + return p_node; + + Node *owner = p_node->get_owner(); + Spatial *s = p_node->cast_to(); + 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 (_teststr(name,"room") && p_node->cast_to()) { + + + if (isroot) + return p_node; + + MeshInstance *mi = p_node->cast_to(); + PoolVector faces = mi->get_faces(VisualInstance::FACES_SOLID); + + + BSP_Tree bsptree(faces); + + Ref 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 (_teststr(name,"room")) { + + if (isroot) + return p_node; + + Spatial *dummy = p_node->cast_to(); + 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 (_teststr(name,"portal") && p_node->cast_to()) { + + if (isroot) + return p_node; + + MeshInstance *mi = p_node->cast_to(); + PoolVector faces = mi->get_faces(VisualInstance::FACES_SOLID); + + ERR_FAIL_COND_V(faces.size()==0,NULL); + //step 1 compute the plane + Set points; + Plane plane; + + Vector3 center; + + for(int i=0;i portal_points; + + for(Set::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;ib) { + 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()) { + + //last attempt, maybe collision insde the mesh data + + MeshInstance *mi = p_node->cast_to(); + + Ref mesh = mi->get_mesh(); + if (!mesh.is_null()) { + + if (_teststr(mesh->get_name(),"col")) { + + mesh->set_name( _fixstr(mesh->get_name(),"col") ); + Ref 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;iget_surface_count();i++) { + + Ref fm = mesh->surface_get_material(i); + if (fm.is_valid()) { + String name = fm->get_name(); + /* if (_teststr(name,"alpha")) { + fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true); + name=_fixstr(name,"alpha"); + } + + if (_teststr(name,"vcol")) { + fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_COLOR_ARRAY,true); + name=_fixstr(name,"vcol"); + }*/ + fm->set_name(name); + } + } + + } + + } + + + return p_node; +} + + +void ResourceImporterScene::_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(); + ERR_FAIL_COND(!anim); + + if (!anim->has_animation("default")) + return; + + + Ref default_anim = anim->get_animation("default"); + + for(int i=0;i=to) + continue; + + Ref new_anim = memnew( Animation ); + + for(int j=0;jget_track_count();j++) { + + + List keys; + int kc = default_anim->track_get_key_count(j); + int dtrack=-1; + for(int k=0;ktrack_get_key_time(j,k); + if (kt>=from && ktadd_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 ResourceImporterScene::_filter_anim_tracks(Ref anim,Set &keep) { + + Ref a = anim; + ERR_FAIL_COND(!a.is_valid()); + + print_line("From Anim "+anim->get_name()+":"); + + for(int j=0;jget_track_count();j++) { + + String path = a->track_get_path(j); + + if (!keep.has(path)) { + + print_line("Remove: "+path); + a->remove_track(j); + j--; + } + + } +} + + +void ResourceImporterScene::_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(); + ERR_FAIL_COND(!anim); + + Vector strings = p_text.split("\n"); + for(int i=0;i anim_names; + anim->get_animation_list(&anim_names); + for(List::Element *E=anim_names.front();E;E=E->next()) { + + String name = E->get(); + bool valid_for_this=false; + bool valid=false; + + Set keep; + Set keep_local; + + + for(int i=0;i::Element *F=keep_local.front();F;F=F->next()) { + keep.insert(F->get()); + } + keep_local.clear(); + + Vector filters=strings[i].substr(1,strings[i].length()).split(","); + for(int j=0;j a = anim->get_animation(name); + if (!a.is_valid()) + continue; + + for(int j=0;jget_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::Element *F=keep_local.front();F;F=F->next()) { + keep.insert(F->get()); + } + _filter_anim_tracks(anim->get_animation(name),keep); + } else { + + } + + } + + + +} + +void ResourceImporterScene::_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(); + ERR_FAIL_COND(!anim); + + + List anim_names; + anim->get_animation_list(&anim_names); + for(List::Element *E=anim_names.front();E;E=E->next()) { + + Ref a = anim->get_animation(E->get()); + a->optimize(p_max_lin_error,p_max_ang_error,Math::deg2rad(p_max_angle)); + } +} + + +void ResourceImporterScene::get_import_options(List *r_options,int p_preset) const { + + + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING,"nodes/root_type",PROPERTY_HINT_TYPE_STRING,"Node"),"Spatial")); + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING,"nodes/root_name"),"Scene Root")); + + List script_extentions; + ResourceLoader::get_recognized_extensions_for_type("Script",&script_extentions); + + String script_ext_hint; + + for(List::Element *E=script_extentions.front();E;E=E->next()) { + if (script_ext_hint!="") + script_ext_hint+=","; + script_ext_hint+="*."+E->get(); + } + + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING,"nodes/custom_script",PROPERTY_HINT_FILE,script_ext_hint),"")); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"materials/location",PROPERTY_HINT_ENUM,"Node,Mesh"),0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"materials/storage",PROPERTY_HINT_ENUM,"Bult-In,Files"),1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"geometry/compress"),true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"geometry/ensure_tangents"),true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"geometry/storage",PROPERTY_HINT_ENUM,"Built-In,Files"),0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"animation/import",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT|PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED),true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::REAL,"animation/fps",PROPERTY_HINT_RANGE,"1,120,1"),15)); + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING,"animation/filter_script",PROPERTY_HINT_MULTILINE_TEXT),"")); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"animation/optimizer/enabled",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT|PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED),true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::REAL,"animation/optimizer/max_linear_error"),0.05)); + r_options->push_back(ImportOption(PropertyInfo(Variant::REAL,"animation/optimizer/max_angular_error"),0.01)); + r_options->push_back(ImportOption(PropertyInfo(Variant::REAL,"animation/optimizer/max_angle"),22)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"animation/optimizer/remove_unused_tracks"),true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"animation/clips/amount",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_DEFAULT|PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED),0)); + for(int i=0;i<256;i++) { + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING,"animation/clip_"+itos(i+1)+"/name"),"")); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"animation/clip_"+itos(i+1)+"/start_frame"),0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"animation/clip_"+itos(i+1)+"/end_frame"),0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"animation/clip_"+itos(i+1)+"/loops"),false)); + } +} +Error ResourceImporterScene::import(const String& p_source_file, const String& p_save_path, const Map& p_options, List* r_platform_variants, List *r_gen_files) { + + String src_path=p_source_file; + + Ref importer; + String ext=src_path.get_extension().to_lower(); + + + EditorProgress progress("import",TTR("Import Scene"),104); + progress.step(TTR("Importing Scene.."),0); + + for(Set< Ref >::Element *E=importers.front();E;E=E->next()) { + + List extensions; + E->get()->get_extensions(&extensions); + + for(List::Element *F=extensions.front();F;F=F->next()) { + + if (F->get().to_lower()==ext) { + + importer = E->get(); + break; + } + } + + if (importer.is_valid()) + break; + } + + ERR_FAIL_COND_V(!importer.is_valid(),ERR_FILE_UNRECOGNIZED); + + float fps=p_options["animation/fps"]; + + + + int import_flags=EditorSceneImporter::IMPORT_ANIMATION_DETECT_LOOP; + if (!bool(p_options["animation/optimizer/remove_unused_tracks"])) + import_flags|=EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS; + + if (bool(p_options["animation/import"])) + import_flags|=EditorSceneImporter::IMPORT_ANIMATION; + + if (bool(p_options["geometry/ensure_tangents"])) + import_flags|=EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS; + + + + + + Error err=OK; + List missing_deps; // for now, not much will be done with this + Node *scene = importer->import_scene(src_path,import_flags,fps,&missing_deps,&err); + if (!scene || err!=OK) { + return err; + } + + String root_type = p_options["nodes/root_type"]; + + if (scene->get_class()!=root_type) { + Object *base = ClassDB::instance(root_type); + Node *base_node = NULL; + if (base) + base_node=base->cast_to(); + + if (base_node) { + + scene->replace_by(base_node); + memdelete(scene); + scene=base_node; + } + } + + scene->set_name(p_options["nodes/root_name"]); + + + err=OK; + + String animation_filter = String(p_options["animation/filter_script"]).strip_edges(); + + bool use_optimizer = p_options["animation/optimizer/enabled"]; + float anim_optimizer_linerr=p_options["animation/optimizer/max_linear_error"]; + float anim_optimizer_angerr=p_options["animation/optimizer/max_angular_error"]; + float anim_optimizer_maxang=p_options["animation/optimizer/max_angle"]; + + Map,Ref > collision_map; + + scene=_fix_node(scene,scene,collision_map); + + if (use_optimizer) { + _optimize_animations(scene,anim_optimizer_linerr,anim_optimizer_angerr,anim_optimizer_maxang); + } + + Array animation_clips; + { + + + int clip_count = p_options["animation/clips/amount"]; + + for(int i=0;i post_import_script; + + if (post_import_script_path!="") { + post_import_script_path = post_import_script_path; + Ref