summaryrefslogtreecommitdiff
path: root/editor/import
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2017-03-05 14:21:25 +0100
committerRémi Verschelde <rverschelde@gmail.com>2017-03-05 14:21:25 +0100
commit49c065d29ca07040c3fd810026121164ad86b247 (patch)
tree285176e0c80a41c22c3e8f171024472cfdc7d765 /editor/import
parent532f6d4b431f940432e82b7fc7826652b7a4520d (diff)
Refactoring: rename tools/editor/ to editor/
The other subfolders of tools/ had already been moved to either editor/, misc/ or thirdparty/, so the hiding the editor code that deep was no longer meaningful.
Diffstat (limited to 'editor/import')
-rw-r--r--editor/import/SCsub5
-rw-r--r--editor/import/editor_import_collada.cpp2508
-rw-r--r--editor/import/editor_import_collada.h51
-rw-r--r--editor/import/resource_importer_csv_translation.cpp126
-rw-r--r--editor/import/resource_importer_csv_translation.h27
-rw-r--r--editor/import/resource_importer_obj.cpp231
-rw-r--r--editor/import/resource_importer_obj.h28
-rw-r--r--editor/import/resource_importer_scene.cpp1328
-rw-r--r--editor/import/resource_importer_scene.h93
-rw-r--r--editor/import/resource_importer_texture.cpp393
-rw-r--r--editor/import/resource_importer_texture.h70
-rw-r--r--editor/import/resource_importer_wav.cpp619
-rw-r--r--editor/import/resource_importer_wav.h30
13 files changed, 5509 insertions, 0 deletions
diff --git a/editor/import/SCsub b/editor/import/SCsub
new file mode 100644
index 0000000000..f1fa50148f
--- /dev/null
+++ b/editor/import/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import('env')
+Export('env')
+env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
new file mode 100644
index 0000000000..7098e8812b
--- /dev/null
+++ b/editor/import/editor_import_collada.cpp
@@ -0,0 +1,2508 @@
+/*************************************************************************/
+/* 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 "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 "editor/collada/collada.h"
+#include "editor/editor_node.h"
+#include <iostream>
+
+
+struct ColladaImport {
+
+ Collada collada;
+ Spatial *scene;
+
+ Vector<Ref<Animation> > animations;
+
+ struct NodeMap {
+ //String path;
+ Spatial *node;
+ int bone;
+ List<int> anim_tracks;
+
+ NodeMap() { node=NULL; bone=-1; }
+ };
+
+ bool found_ambient;
+ Color ambient;
+ bool found_directional;
+ bool force_make_tangents;
+ bool apply_mesh_xform_to_vertices;
+ bool use_mesh_builtin_materials;
+ float bake_fps;
+
+
+
+ Map<String,NodeMap> node_map; //map from collada node to engine node
+ Map<String,String> node_name_map; //map from collada node to engine node
+ Map<String, Ref<Mesh> > mesh_cache;
+ Map<String, Ref<Curve3D> > curve_cache;
+ Map<String, Ref<Material> > material_cache;
+ Map<Collada::Node*,Skeleton*> skeleton_map;
+
+ Map< Skeleton*, Map< String, int> > skeleton_bone_map;
+
+ Set<String> valid_animated_nodes;
+ Vector<int> valid_animated_properties;
+ Map<String,bool> bones_with_animation;
+
+ Error _populate_skeleton(Skeleton *p_skeleton,Collada::Node *p_node, int &r_bone, int p_parent);
+ Error _create_scene_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<Mesh>& p_mesh, const Map<String,Collada::NodeGeometry::Material>& p_material_map, const Collada::MeshData &meshdata, const Transform& p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data, Vector<Ref<Mesh> > p_morph_meshes=Vector<Ref<Mesh> >(), bool p_for_morph=false, bool p_use_mesh_material=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<String> tracks_in_clips;
+ Vector<String> missing_textures;
+
+ void _pre_process_lights(Collada::Node *p_node);
+
+ ColladaImport() {
+
+ found_ambient=false;
+ found_directional=false;
+ force_make_tangents=false;
+ 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<Collada::NodeJoint*>(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;i<p_node->children.size();i++) {
+
+ Error err = _populate_skeleton(p_skeleton,p_node->children[i],r_bone,id);
+ if (err)
+ return err;
+ }
+
+ return OK;
+}
+
+
+void ColladaImport::_pre_process_lights(Collada::Node *p_node) {
+
+
+ if (p_node->type==Collada::Node::TYPE_LIGHT) {
+
+
+ Collada::NodeLight *light=static_cast<Collada::NodeLight*>(p_node);
+ if (collada.state.light_data_map.has(light->light)) {
+
+ Collada::LightData &ld = collada.state.light_data_map[light->light];
+ if (ld.mode==Collada::LightData::MODE_AMBIENT) {
+ found_ambient=true;
+ ambient=ld.color;
+ }
+ if (ld.mode==Collada::LightData::MODE_DIRECTIONAL) {
+ found_directional=true;
+ }
+ }
+
+ }
+
+
+ for(int i=0;i<p_node->children.size();i++)
+ _pre_process_lights(p_node->children[i]);
+}
+
+Error ColladaImport::_create_scene_skeletons(Collada::Node *p_node) {
+
+
+ if (p_node->type==Collada::Node::TYPE_SKELETON) {
+
+ Skeleton *sk = memnew( Skeleton );
+ int bone = 0;
+
+ for(int i=0;i<p_node->children.size();i++) {
+
+ _populate_skeleton(sk,p_node->children[i],bone,-1);
+ }
+ sk->localize_rests(); //after creating skeleton, rests must be localized...!
+ skeleton_map[p_node]=sk;
+ }
+
+
+ for(int i=0;i<p_node->children.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<Collada::NodeLight*>(p_node);
+ if (collada.state.light_data_map.has(light->light)) {
+
+ Collada::LightData &ld = collada.state.light_data_map[light->light];
+
+ if (ld.mode==Collada::LightData::MODE_AMBIENT) {
+
+ if (found_directional)
+ return OK; //do nothing not needed
+
+ if (!bool(GLOBAL_DEF("collada/use_ambient",false)))
+ return OK;
+ //well, it's an ambient light..
+ Light *l = memnew( DirectionalLight );
+ //l->set_color(Light::COLOR_AMBIENT,ld.color);
+ //l->set_color(Light::COLOR_DIFFUSE,Color(0,0,0));
+ //l->set_color(Light::COLOR_SPECULAR,Color(0,0,0));
+ node = l;
+
+ } else if (ld.mode==Collada::LightData::MODE_DIRECTIONAL) {
+
+ //well, it's an ambient light..
+ Light *l = memnew( DirectionalLight );
+ /*
+ if (found_ambient) //use it here
+ l->set_color(Light::COLOR_AMBIENT,ambient);
+
+ l->set_color(Light::COLOR_DIFFUSE,ld.color);
+ l->set_color(Light::COLOR_SPECULAR,Color(1,1,1));
+ */
+ node = l;
+ } else {
+
+ Light *l;
+
+ if (ld.mode==Collada::LightData::MODE_OMNI)
+ l=memnew( OmniLight );
+ else {
+ l=memnew( SpotLight );
+ //l->set_parameter(Light::PARAM_SPOT_ANGLE,ld.spot_angle);
+ //l->set_parameter(Light::PARAM_SPOT_ATTENUATION,ld.spot_exp);
+ }
+
+ //
+ //l->set_color(Light::COLOR_DIFFUSE,ld.color);
+ //l->set_color(Light::COLOR_SPECULAR,Color(1,1,1));
+ //l->approximate_opengl_attenuation(ld.constant_att,ld.linear_att,ld.quad_att);
+ node=l;
+ }
+
+ } else {
+
+ node = memnew( Spatial );
+ }
+ } break;
+ case Collada::Node::TYPE_CAMERA: {
+
+ Collada::NodeCamera *cam = static_cast<Collada::NodeCamera*>(p_node);
+ Camera *camera = memnew( Camera );
+
+ if (collada.state.camera_data_map.has(cam->camera)) {
+
+ const Collada::CameraData &cd = collada.state.camera_data_map[cam->camera];
+
+ switch(cd.mode) {
+
+ case Collada::CameraData::MODE_ORTHOGONAL: {
+
+ if (cd.orthogonal.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<Collada::NodeGeometry*>(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<MeshInstance>()->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;i<p_node->children.size();i++) {
+
+ Error err = _create_scene(p_node->children[i],node);
+ if (err)
+ return err;
+ }
+ return OK;
+}
+
+
+Error ColladaImport::_create_material(const String& p_target) {
+
+ ERR_FAIL_COND_V(material_cache.has(p_target),ERR_ALREADY_EXISTS);
+ ERR_FAIL_COND_V(!collada.state.material_map.has(p_target),ERR_INVALID_PARAMETER);
+ Collada::Material &src_mat=collada.state.material_map[p_target];
+ ERR_FAIL_COND_V(!collada.state.effect_map.has(src_mat.instance_effect),ERR_INVALID_PARAMETER);
+ Collada::Effect &effect=collada.state.effect_map[src_mat.instance_effect];
+
+ Ref<FixedSpatialMaterial> 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> 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> 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_metalness(effect.specular.color.get_v());
+ }
+
+
+ // EMISSION
+
+ if (effect.emission.texture!="") {
+
+ String texfile = effect.get_texture_path(effect.emission.texture,collada);
+ if (texfile!="") {
+
+ Ref<Texture> texture = ResourceLoader::load(texfile,"Texture");
+ if (texture.is_valid()) {
+
+ material->set_feature(FixedSpatialMaterial::FEATURE_EMISSION,true);
+ 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 {
+ if (effect.emission.color!=Color()) {
+ material->set_feature(FixedSpatialMaterial::FEATURE_EMISSION,true);
+ material->set_emission(effect.emission.color);
+ }
+ }
+
+ // NORMAL
+
+ if (effect.bump.texture!="") {
+
+ String texfile = effect.get_texture_path(effect.bump.texture,collada);
+ if (texfile!="") {
+
+ Ref<Texture> texture = ResourceLoader::load(texfile,"Texture");
+ if (texture.is_valid()) {
+ material->set_feature(FixedSpatialMaterial::FEATURE_NORMAL_MAPPING,true);
+ 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());
+ }
+
+ }
+ }
+
+
+ float roughness = Math::sqrt(1.0-((Math::log(effect.shininess)/Math::log(2.0))/8.0)); //not very right..
+ material->set_roughness(roughness);
+
+ 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<int>& p_indices,const PoolVector<Vector3>& p_vertices,PoolVector<Vector3>&r_normals) {
+
+
+ r_normals.resize(p_vertices.size());
+ PoolVector<Vector3>::Write narrayw = r_normals.write();
+
+ int iacount=p_indices.size()/3;
+ PoolVector<int>::Read index_arrayr = p_indices.read();
+ PoolVector<Vector3>::Read vertex_arrayr = p_vertices.read();
+
+ for(int idx=0;idx<iacount;idx++) {
+
+ Vector3 v[3]={
+ vertex_arrayr[index_arrayr[idx*3+0]],
+ vertex_arrayr[index_arrayr[idx*3+1]],
+ vertex_arrayr[index_arrayr[idx*3+2]]
+ };
+
+ Vector3 normal = Plane(v[0],v[1],v[2]).normal;
+
+ narrayw[index_arrayr[idx*3+0]]+=normal;
+ narrayw[index_arrayr[idx*3+1]]+=normal;
+ narrayw[index_arrayr[idx*3+2]]+=normal;
+ }
+
+ int vlen=p_vertices.size();
+
+ for(int idx=0;idx<vlen;idx++) {
+ narrayw[idx].normalize();
+ }
+
+}
+
+
+static void _generate_tangents_and_binormals(const PoolVector<int>& p_indices,const PoolVector<Vector3>& p_vertices,const PoolVector<Vector3>& p_uvs,const PoolVector<Vector3>& p_normals,PoolVector<real_t>&r_tangents) {
+
+ int vlen=p_vertices.size();
+
+ Vector<Vector3> tangents;
+ tangents.resize(vlen);
+ Vector<Vector3> binormals;
+ binormals.resize(vlen);
+
+
+ int iacount=p_indices.size()/3;
+
+ PoolVector<int>::Read index_arrayr = p_indices.read();
+ PoolVector<Vector3>::Read vertex_arrayr = p_vertices.read();
+ PoolVector<Vector3>::Read narrayr = p_normals.read();
+ PoolVector<Vector3>::Read uvarrayr = p_uvs.read();
+
+
+ for(int idx=0;idx<iacount;idx++) {
+
+
+ Vector3 v1 = vertex_arrayr[ index_arrayr[idx*3+0] ];
+ Vector3 v2 = vertex_arrayr[ index_arrayr[idx*3+1] ];
+ Vector3 v3 = vertex_arrayr[ index_arrayr[idx*3+2] ];
+
+ Vector3 w1 = uvarrayr[ index_arrayr[idx*3+0] ];
+ Vector3 w2 = uvarrayr[ index_arrayr[idx*3+1] ];
+ Vector3 w3 = uvarrayr[ index_arrayr[idx*3+2] ];
+
+ real_t x1 = v2.x - v1.x;
+ real_t x2 = v3.x - v1.x;
+ real_t y1 = v2.y - v1.y;
+ real_t y2 = v3.y - v1.y;
+ real_t z1 = v2.z - v1.z;
+ real_t z2 = v3.z - v1.z;
+
+ real_t s1 = w2.x - w1.x;
+ real_t s2 = w3.x - w1.x;
+ real_t t1 = w2.y - w1.y;
+ real_t t2 = w3.y - w1.y;
+
+ real_t r = (s1 * t2 - s2 * t1);
+
+ Vector3 tangent;
+ Vector3 binormal;
+
+ if (r==0) {
+
+ binormal=Vector3();
+ tangent=Vector3();
+ } else {
+ tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
+ (t2 * z1 - t1 * z2) * r).normalized();
+ binormal = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
+ (s1 * z2 - s2 * z1) * r).normalized();
+ }
+
+ tangents[ index_arrayr[idx*3+0] ]+=tangent;
+ binormals[ index_arrayr[idx*3+0] ]+=binormal;
+ tangents[ index_arrayr[idx*3+1] ]+=tangent;
+ binormals[ index_arrayr[idx*3+1] ]+=binormal;
+ tangents[ index_arrayr[idx*3+2] ]+=tangent;
+ binormals[ index_arrayr[idx*3+2] ]+=binormal;
+
+ //print_line(itos(idx)+" tangent: "+tangent);
+ //print_line(itos(idx)+" binormal: "+binormal);
+ }
+
+ r_tangents.resize(vlen*4);
+ PoolVector<real_t>::Write tarrayw = r_tangents.write();
+
+ for(int idx=0;idx<vlen;idx++) {
+ Vector3 tangent = tangents[idx];
+ Vector3 bingen = narrayr[idx].cross(tangent);
+ float dir;
+ if (bingen.dot(binormals[idx]) < 0 )
+ dir=-1.0;
+ else
+ dir=+1.0;
+
+ tarrayw[idx*4+0]=tangent.x;
+ tarrayw[idx*4+1]=tangent.y;
+ tarrayw[idx*4+2]=tangent.z;
+ tarrayw[idx*4+3]=dir;
+ }
+}
+
+Error ColladaImport::_create_mesh_surfaces(bool p_optimize,Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *skin_controller, const Collada::MorphControllerData *p_morph_data,Vector<Ref<Mesh> > p_morph_meshes,bool p_for_morph,bool p_use_mesh_material) {
+
+
+ bool local_xform_mirror=p_local_xform.basis.determinant() < 0;
+
+ if (p_morph_data) {
+
+ //add morphie target
+ ERR_FAIL_COND_V( !p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA );
+ String mt = p_morph_data->targets["MORPH_TARGET"];
+ ERR_FAIL_COND_V( !p_morph_data->sources.has(mt), ERR_INVALID_DATA);
+ int morph_targets = p_morph_data->sources[mt].sarray.size();
+ for(int i=0;i<morph_targets;i++) {
+
+ String target = p_morph_data->sources[mt].sarray[i];
+ ERR_FAIL_COND_V( !collada.state.mesh_data_map.has(target), ERR_INVALID_DATA );
+ String name = collada.state.mesh_data_map[target].name;
+
+ p_mesh->add_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<int,Vector<Collada::Vertex::Weight> > 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_i<skin_controller->weights.sets.size();w_i++) {
+
+ int amount = skin_controller->weights.sets[w_i];
+
+ Vector<Collada::Vertex::Weight> weights;
+
+ for (int a_i=0;a_i<amount;a_i++) {
+
+ Collada::Vertex::Weight w;
+
+ int read_from = index_ofs+a_i*wstride;
+ ERR_FAIL_INDEX_V(read_from+wstride-1,skin_controller->weights.indices.size(),ERR_INVALID_DATA);
+ int weight_index = skin_controller->weights.indices[read_from+weight_ofs];
+ ERR_FAIL_INDEX_V(weight_index,weight_src->array.size(),ERR_INVALID_DATA);
+
+ w.weight = weight_src->array[weight_index];
+
+ int bone_index = skin_controller->weights.indices[read_from+joint_ofs];
+ if (bone_index==-1)
+ continue; //ignore this weight (refers to bind shape)
+ ERR_FAIL_INDEX_V(bone_index,bone_remap.size(),ERR_INVALID_DATA);
+
+ w.bone_idx=bone_remap[bone_index];
+
+
+ weights.push_back(w);
+ }
+
+ /* FIX WEIGHTS */
+
+
+
+ weights.sort();
+
+ if (weights.size()>4) {
+ //cap to 4 and make weights add up 1
+ weights.resize(4);
+
+ }
+
+ //make sure weights allways add up to 1
+ float total=0;
+ for(int i=0;i<weights.size();i++)
+ total+=weights[i].weight;
+ if (total)
+ for(int i=0;i<weights.size();i++)
+ weights[i].weight/=total;
+
+ if (weights.size()==0 || total==0) { //if nothing, add a weight to bone 0
+ //no weights assigned
+ Collada::Vertex::Weight w;
+ w.bone_idx=0;
+ w.weight=1.0;
+ weights.clear();
+ weights.push_back(w);
+
+ }
+
+ pre_weights[w_i]=weights;
+
+ /*
+ for(Set<int>::Element *E=vertex_map[w_i].front();E;E=E->next()) {
+
+ int dst = E->get();
+ ERR_EXPLAIN("invalid vertex index in array");
+ ERR_FAIL_INDEX_V(dst,vertex_array.size(),ERR_INVALID_DATA);
+ vertex_array[dst].weights=weights;
+
+ }*/
+
+
+
+
+ index_ofs+=wstride*amount;
+
+ }
+
+ //vertices need to be localized
+ has_weights=true;
+
+ }
+
+ Set<Collada::Vertex> vertex_set; //vertex set will be the vertices
+ List<int> indices_list; //indices will be the indices
+ //Map<int,Set<int> > vertex_map; //map vertices (for setting skinning/morph)
+
+ /**************************/
+ /* CREATE PRIMITIVE ARRAY */
+ /**************************/
+
+ // The way collada uses indices is more optimal, and friendlier with 3D modelling sofware,
+ // because it can index everything, not only vertices (similar to how the WII works).
+ // This is, however, more incompatible with standard video cards, so arrays must be converted.
+ // Must convert to GL/DX format.
+
+ int _prim_ofs=0;
+ int vertidx=0;
+ for(int p_i=0;p_i<p.count;p_i++) {
+
+
+ int amount;
+ if (p.polygons.size()) {
+
+ ERR_FAIL_INDEX_V(p_i,p.polygons.size(),ERR_INVALID_DATA);
+ amount=p.polygons[p_i];
+ } else {
+ amount=3; //triangles;
+ }
+
+ //COLLADA_PRINT("amount: "+itos(amount));
+
+ int prev2[2]={0,0};
+
+ for(int j=0;j<amount;j++) {
+
+ int src=_prim_ofs;
+ //_prim_ofs+=p.sources.size()
+
+ ERR_FAIL_INDEX_V(src,p.indices.size(),ERR_INVALID_DATA);
+
+ Collada::Vertex vertex;
+ if (!p_optimize)
+ vertex.uid=vertidx++;
+
+
+
+ int vertex_index=p.indices[src+vertex_ofs]; //used for index field (later used by controllers)
+ int vertex_pos = (vertex_src->stride?vertex_src->stride:3) * vertex_index;
+ ERR_FAIL_INDEX_V(vertex_pos,vertex_src->array.size(),ERR_INVALID_DATA);
+ vertex.vertex=Vector3(vertex_src->array[vertex_pos+0],vertex_src->array[vertex_pos+1],vertex_src->array[vertex_pos+2]);
+
+ if (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<int>();
+ vertex_map[vertex_index].insert(index); //should be outside..*/
+ //build triangles if needed
+ if (j==0)
+ prev2[0]=index;
+
+ if (j>=2) {
+ //insert indices in reverse order (collada uses CCW as frontface)
+ if (local_xform_mirror) {
+
+ indices_list.push_back(prev2[0]);
+ indices_list.push_back(prev2[1]);
+ indices_list.push_back(index);
+
+ } else {
+ indices_list.push_back(prev2[0]);
+ indices_list.push_back(index);
+ indices_list.push_back(prev2[1]);
+ }
+ }
+
+ prev2[1]=index;
+ _prim_ofs+=p.vertex_size;
+ }
+
+ }
+
+
+
+ Vector<Collada::Vertex> vertex_array; //there we go, vertex array
+
+ vertex_array.resize(vertex_set.size());
+ for(Set<Collada::Vertex>::Element *F=vertex_set.front();F;F=F->next()) {
+
+ vertex_array[F->get().idx]=F->get();
+ }
+
+
+ if (has_weights) {
+
+ //if skeleton, localize
+ Transform local_xform = p_local_xform;
+ for(int i=0;i<vertex_array.size();i++) {
+
+ vertex_array[i].vertex=local_xform.xform(vertex_array[i].vertex);
+ vertex_array[i].normal=local_xform.basis.xform(vertex_array[i].normal).normalized();
+ vertex_array[i].tangent.normal=local_xform.basis.xform(vertex_array[i].tangent.normal).normalized();
+ if (local_xform_mirror) {
+ //i shouldn't do this? wtf?
+ //vertex_array[i].normal*=-1.0;
+ //vertex_array[i].tangent.normal*=-1.0;
+ }
+ }
+ }
+
+
+ PoolVector<int> index_array;
+ index_array.resize(indices_list.size());
+ PoolVector<int>::Write index_arrayw = index_array.write();
+
+ int iidx=0;
+ for(List<int>::Element *F=indices_list.front();F;F=F->next()) {
+
+ index_arrayw[iidx++]=F->get();
+ }
+
+ index_arrayw=PoolVector<int>::Write();
+
+
+ /*****************/
+ /* MAKE SURFACES */
+ /*****************/
+
+
+ {
+
+ Ref<FixedSpatialMaterial> 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<Vector3> final_vertex_array;
+ PoolVector<Vector3> final_normal_array;
+ PoolVector<float> final_tangent_array;
+ PoolVector<Color> final_color_array;
+ PoolVector<Vector3> final_uv_array;
+ PoolVector<Vector3> final_uv2_array;
+ PoolVector<int> final_bone_array;
+ PoolVector<float> final_weight_array;
+
+ uint32_t final_format=0;
+
+ //create format
+ final_format=Mesh::ARRAY_FORMAT_VERTEX|Mesh::ARRAY_FORMAT_INDEX;
+
+ if (normal_src) {
+ final_format|=Mesh::ARRAY_FORMAT_NORMAL;
+ if (uv_src && binormal_src && tangent_src) {
+ final_format|=Mesh::ARRAY_FORMAT_TANGENT;
+ }
+
+ }
+
+
+
+ if (color_src)
+ final_format|=Mesh::ARRAY_FORMAT_COLOR;
+ if (uv_src)
+ final_format|=Mesh::ARRAY_FORMAT_TEX_UV;
+ if (uv2_src)
+ final_format|=Mesh::ARRAY_FORMAT_TEX_UV2;
+
+ if (has_weights) {
+ final_format|=Mesh::ARRAY_FORMAT_WEIGHTS;
+ final_format|=Mesh::ARRAY_FORMAT_BONES;
+ }
+
+
+ //set arrays
+
+ int vlen = vertex_array.size();
+ { //vertices
+
+ PoolVector<Vector3> varray;
+ varray.resize(vertex_array.size());
+
+ PoolVector<Vector3>::Write varrayw = varray.write();
+
+ for(int k=0;k<vlen;k++)
+ varrayw[k]=vertex_array[k].vertex;
+
+ varrayw = PoolVector<Vector3>::Write();
+ final_vertex_array=varray;
+
+ }
+
+
+ if (uv_src) { //compute uv first, may be needed for computing tangent/bionrmal
+ PoolVector<Vector3> uvarray;
+ uvarray.resize(vertex_array.size());
+ PoolVector<Vector3>::Write uvarrayw = uvarray.write();
+
+ for(int k=0;k<vlen;k++) {
+ uvarrayw[k]=vertex_array[k].uv;
+ }
+
+ uvarrayw = PoolVector<Vector3>::Write();
+ final_uv_array=uvarray;
+
+ }
+
+ if (uv2_src) { //compute uv first, may be needed for computing tangent/bionrmal
+ PoolVector<Vector3> uv2array;
+ uv2array.resize(vertex_array.size());
+ PoolVector<Vector3>::Write uv2arrayw = uv2array.write();
+
+ for(int k=0;k<vlen;k++) {
+ uv2arrayw[k]=vertex_array[k].uv2;
+ }
+
+ uv2arrayw = PoolVector<Vector3>::Write();
+ final_uv2_array=uv2array;
+
+ }
+
+ if (normal_src) {
+ PoolVector<Vector3> narray;
+ narray.resize(vertex_array.size());
+ PoolVector<Vector3>::Write narrayw = narray.write();
+
+ for(int k=0;k<vlen;k++) {
+ narrayw[k]=vertex_array[k].normal;
+ }
+
+ narrayw = PoolVector<Vector3>::Write();
+ final_normal_array=narray;
+
+ /*
+ PoolVector<Vector3> altnaray;
+ _generate_normals(index_array,final_vertex_array,altnaray);
+
+ for(int i=0;i<altnaray.size();i++)
+ print_line(rtos(altnaray[i].dot(final_normal_array[i])));
+ */
+
+ } else if (primitive==Mesh::PRIMITIVE_TRIANGLES) {
+ //generate normals (even if unused later)
+
+ _generate_normals(index_array,final_vertex_array,final_normal_array);
+ if (OS::get_singleton()->is_stdout_verbose())
+ print_line("Collada: Triangle mesh lacks normals, so normals were generated.");
+ final_format|=Mesh::ARRAY_FORMAT_NORMAL;
+
+ }
+
+ if (final_normal_array.size() && uv_src && binormal_src && tangent_src && !force_make_tangents) {
+
+ PoolVector<real_t> tarray;
+ tarray.resize(vertex_array.size()*4);
+ PoolVector<real_t>::Write tarrayw = tarray.write();
+
+
+ for(int k=0;k<vlen;k++) {
+ tarrayw[k*4+0]=vertex_array[k].tangent.normal.x;
+ tarrayw[k*4+1]=vertex_array[k].tangent.normal.y;
+ tarrayw[k*4+2]=vertex_array[k].tangent.normal.z;
+ tarrayw[k*4+3]=vertex_array[k].tangent.d;
+
+ }
+
+ tarrayw = PoolVector<real_t>::Write();
+
+ final_tangent_array=tarray;
+ } else if (final_normal_array.size() && primitive==Mesh::PRIMITIVE_TRIANGLES && final_uv_array.size() && (force_make_tangents || (material.is_valid()))){
+ //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<Color> colorarray;
+ colorarray.resize(vertex_array.size());
+ PoolVector<Color>::Write colorarrayw = colorarray.write();
+
+ for(int k=0;k<vlen;k++) {
+ colorarrayw[k]=vertex_array[k].color;
+ }
+
+ colorarrayw = PoolVector<Color>::Write();
+
+ final_color_array=colorarray;
+ }
+
+ if (has_weights) {
+ PoolVector<float> weightarray;
+ PoolVector<int> bonearray;
+
+ weightarray.resize(vertex_array.size()*4);
+ PoolVector<float>::Write weightarrayw = weightarray.write();
+ bonearray.resize(vertex_array.size()*4);
+ PoolVector<int>::Write bonearrayw = bonearray.write();
+
+ for(int k=0;k<vlen;k++) {
+ float sum=0;
+
+ for(int l=0;l<VS::ARRAY_WEIGHTS_SIZE;l++) {
+ if (l<vertex_array[k].weights.size()) {
+ weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=vertex_array[k].weights[l].weight;
+ sum+=weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l];
+ bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=int(vertex_array[k].weights[l].bone_idx);
+ //COLLADA_PRINT(itos(k)+": "+rtos(bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l])+":"+rtos(weightarray[k*VS::ARRAY_WEIGHTS_SIZE+l]));
+ } else {
+
+ weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=0;
+ bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=0;
+
+ }
+
+
+ }
+ /*
+ if (sum<0.8)
+ COLLADA_PRINT("ERROR SUMMING INDEX "+itos(k)+" had weights: "+itos(vertex_array[k].weights.size()));
+ */
+
+ }
+
+ weightarrayw = PoolVector<float>::Write();
+ bonearrayw = PoolVector<int>::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;j<morph_targets;j++) {
+
+ Array mrt;
+ mrt.resize(VS::ARRAY_MAX);
+
+ String target = p_morph_data->sources[mt].sarray[j];
+ ERR_FAIL_COND_V( !collada.state.mesh_data_map.has(target), ERR_INVALID_DATA );
+ String name = collada.state.mesh_data_map[target].name;
+ Collada::MeshData &md = collada.state.mesh_data_map[target];
+
+ // collada in itself supports morphing everything. However, the spec is unclear and no examples or exporters that
+ // morph anything but "POSITIONS" seem to exit. Because of this, normals and binormals/tangents have to be regenerated here,
+ // which may result in inaccurate (but most of the time good enough) results.
+
+ PoolVector<Vector3> vertices;
+ vertices.resize(vlen);
+
+ ERR_FAIL_COND_V( md.vertices.size() != 1, ERR_INVALID_DATA);
+ String vertex_src_id=md.vertices.front()->key();
+ ERR_FAIL_COND_V(!md.vertices[vertex_src_id].sources.has("POSITION"),ERR_INVALID_DATA);
+ String position_src_id = md.vertices[vertex_src_id].sources["POSITION"];
+
+ ERR_FAIL_COND_V(!md.sources.has(position_src_id),ERR_INVALID_DATA);
+
+ const Collada::MeshData::Source *m=&md.sources[position_src_id];
+
+ ERR_FAIL_COND_V( m->array.size() != vertex_src->array.size(), ERR_INVALID_DATA);
+ int stride=m->stride;
+ if (stride==0)
+ stride=3;
+
+
+ //read vertices from morph target
+ PoolVector<Vector3>::Write vertw = vertices.write();
+
+ for(int m_i=0;m_i<m->array.size()/stride;m_i++) {
+
+ int pos = m_i*stride;
+ Vector3 vtx( m->array[pos+0], m->array[pos+1], m->array[pos+2] );
+
+#ifndef NO_UP_AXIS_SWAP
+ if (collada.state.up_axis==Vector3::AXIS_Z) {
+
+ SWAP( vtx.z, vtx.y );
+ vtx.z = -vtx.z;
+
+ }
+#endif
+
+ Collada::Vertex vertex;
+ vertex.vertex=vtx;
+ vertex.fix_unit_scale(collada);
+ vtx=vertex.vertex;
+
+ vtx = p_local_xform.xform(vtx);
+
+
+ if (vertex_map.has(m_i)) { //vertex may no longer be here, don't bother converting
+
+
+ for (Set<int> ::Element *E=vertex_map[m_i].front() ; E; E=E->next() ) {
+
+ vertw[E->get()]=vtx;
+ }
+ }
+ }
+
+
+ //vertices are in place, now generate everything else
+ vertw = PoolVector<Vector3>::Write();
+ PoolVector<Vector3> normals;
+ PoolVector<float> 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() <<std::endl;
+ ERR_FAIL_COND_V(!md.sources.has(normal_src_id),ERR_INVALID_DATA);
+
+ const Collada::MeshData::Source *m=&md.sources[normal_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 normals from morph target
+ PoolVector<Vector3>::Write vertw = normals.write();
+
+ for(int m_i=0;m_i<m->array.size()/stride;m_i++) {
+
+ int pos = m_i*stride;
+ Vector3 vtx( m->array[pos+0], m->array[pos+1], m->array[pos+2] );
+
+ #ifndef NO_UP_AXIS_SWAP
+ if (collada.state.up_axis==Vector3::AXIS_Z) {
+
+ SWAP( vtx.z, vtx.y );
+ vtx.z = -vtx.z;
+
+ }
+ #endif
+
+ Collada::Vertex vertex;
+ vertex.vertex=vtx;
+ vertex.fix_unit_scale(collada);
+ vtx=vertex.vertex;
+
+ vtx = p_local_xform.xform(vtx);
+
+
+ if (vertex_map.has(m_i)) { //vertex may no longer be here, don't bother converting
+
+
+ for (Set<int> ::Element *E=vertex_map[m_i].front() ; E; E=E->next() ) {
+
+ vertw[E->get()]=vtx;
+ }
+ }
+ }
+
+ 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;mi<p_morph_meshes.size();mi++) {
+
+ //print_line("want surface "+itos(mi)+" has "+itos(p_morph_meshes[mi]->get_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()) {
+ if (p_use_mesh_material) {
+ p_mesh->surface_set_material(surface, material);
+ }
+ p_mesh->surface_set_name(surface, material->get_name());
+ }
+ }
+
+ /*****************/
+ /* FIND MATERIAL */
+ /*****************/
+
+ surface++;
+ }
+
+
+ return OK;
+
+}
+
+
+Error ColladaImport::_create_resources(Collada::Node *p_node) {
+
+
+ if (p_node->type==Collada::Node::TYPE_GEOMETRY && node_map.has(p_node->id)) {
+
+
+ Spatial * node=node_map[p_node->id].node;
+ Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(p_node);
+
+
+ if (node->cast_to<Path>()) {
+
+ Path *path = node->cast_to<Path>();
+
+ String curve = ng->source;
+
+ if (curve_cache.has(ng->source)) {
+
+ path->set_curve(curve_cache[ng->source]);
+ } else {
+
+ Ref<Curve3D> c = memnew( Curve3D );
+
+ const Collada::CurveData &cd = collada.state.curve_data_map[ng->source];
+
+ ERR_FAIL_COND_V( !cd.control_vertices.has("POSITION") , ERR_INVALID_DATA);
+ ERR_FAIL_COND_V( !cd.control_vertices.has("IN_TANGENT") , ERR_INVALID_DATA);
+ ERR_FAIL_COND_V( !cd.control_vertices.has("OUT_TANGENT") , ERR_INVALID_DATA);
+ ERR_FAIL_COND_V( !cd.control_vertices.has("INTERPOLATION") , ERR_INVALID_DATA);
+
+
+ ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["POSITION"] ) , ERR_INVALID_DATA);
+ const Collada::CurveData::Source &vertices = cd.sources[ cd.control_vertices["POSITION"] ];
+ ERR_FAIL_COND_V( vertices.stride!=3, ERR_INVALID_DATA );
+
+ ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["IN_TANGENT"] ) , ERR_INVALID_DATA);
+ const Collada::CurveData::Source &in_tangents = cd.sources[ cd.control_vertices["IN_TANGENT"] ];
+ ERR_FAIL_COND_V( in_tangents.stride!=3 , ERR_INVALID_DATA);
+
+ ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["OUT_TANGENT"] ), ERR_INVALID_DATA );
+ const Collada::CurveData::Source &out_tangents = cd.sources[ cd.control_vertices["OUT_TANGENT"] ];
+ ERR_FAIL_COND_V( out_tangents.stride!=3, ERR_INVALID_DATA );
+
+ ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["INTERPOLATION"] ), ERR_INVALID_DATA );
+ const Collada::CurveData::Source &interps = cd.sources[ cd.control_vertices["INTERPOLATION"] ];
+ ERR_FAIL_COND_V( interps.stride!=1, ERR_INVALID_DATA );
+
+ const Collada::CurveData::Source *tilts=NULL;
+ if (cd.control_vertices.has("TILT") && cd.sources.has(cd.control_vertices["TILT"]))
+ tilts=&cd.sources[ cd.control_vertices["TILT"] ];
+
+
+ if (tilts) {
+ print_line("FOUND TILTS!!!");
+ }
+ int pc = vertices.array.size()/3;
+ for(int i=0;i<pc;i++) {
+
+ Vector3 pos( vertices.array[i*3+0], vertices.array[i*3+1], vertices.array[i*3+2] );
+ Vector3 in( in_tangents.array[i*3+0], in_tangents.array[i*3+1], in_tangents.array[i*3+2] );
+ Vector3 out( out_tangents.array[i*3+0], out_tangents.array[i*3+1], out_tangents.array[i*3+2] );
+
+#ifndef NO_UP_AXIS_SWAP
+ if (collada.state.up_axis==Vector3::AXIS_Z) {
+
+ SWAP(pos.y,pos.z);
+ pos.z=-pos.z;
+ SWAP(in.y,in.z);
+ in.z=-in.z;
+ SWAP(out.y,out.z);
+ out.z=-out.z;
+ }
+#endif
+ pos*=collada.state.unit_scale;
+ in*=collada.state.unit_scale;
+ out*=collada.state.unit_scale;
+
+ c->add_point(pos,in-pos,out-pos);
+ if (tilts)
+ c->set_point_tilt(i,tilts->array[i]);
+
+ }
+
+ curve_cache[ng->source]=c;
+ path->set_curve(c);
+
+ }
+
+
+ }
+
+
+ if (node->cast_to<MeshInstance>()) {
+
+
+ Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(p_node);
+
+ MeshInstance *mi = node->cast_to<MeshInstance>();
+
+
+ ERR_FAIL_COND_V(!mi,ERR_BUG);
+
+
+ Collada::SkinControllerData *skin=NULL;
+ Collada::MorphControllerData *morph=NULL;
+ String meshid;
+ Transform apply_xform;
+ Vector<int> bone_remap;
+ Vector<Ref<Mesh> > 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<String> 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<Skeleton>();
+ ERR_FAIL_COND_V( !sk, ERR_INVALID_DATA );
+ ERR_FAIL_COND_V( !skeleton_bone_map.has(sk), ERR_INVALID_DATA );
+ Map<String, int> &bone_remap_map=skeleton_bone_map[sk];
+
+
+ meshid=skin->base;
+
+ if (collada.state.morph_controller_data_map.has(meshid)) {
+ //it's a morph!!
+ morph = &collada.state.morph_controller_data_map[meshid];
+ 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;i<bone_remap.size();i++) {
+
+ String str = joint_src->sarray[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<String> targets;
+
+ morph->targets.has("MORPH_TARGET");
+ String target = morph->targets["MORPH_TARGET"];
+ bool valid=false;
+ if (morph->sources.has(target)) {
+ valid=true;
+ Vector<String> names = morph->sources[target].sarray;
+ for(int i=0;i<names.size();i++) {
+
+ String meshid=names[i];
+ if (collada.state.mesh_data_map.has(meshid)) {
+ Ref<Mesh> mesh=Ref<Mesh>(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<Ref<Mesh> >(),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> mesh;
+ if (mesh_cache.has(meshid)) {
+ mesh=mesh_cache[meshid];
+ } else {
+ if (collada.state.mesh_data_map.has(meshid)) {
+ //bleh, must ignore invalid
+
+ ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid),ERR_INVALID_DATA);
+ mesh=Ref<Mesh>(memnew( Mesh ));
+ const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
+ mesh->set_name( meshdata.name );
+ Error err = _create_mesh_surfaces(morphs.size()==0,mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,morph,morphs,false,use_mesh_builtin_materials);
+ 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);
+ if (!use_mesh_builtin_materials) {
+ const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
+
+ for(int i=0;i<meshdata.primitives.size();i++) {
+
+ String matname=meshdata.primitives[i].material;
+
+ if (ng->material_map.has(matname)) {
+ String target=ng->material_map[matname].target;
+
+ Ref<Material> material;
+ if (!material_cache.has(target)) {
+ Error err = _create_material(target);
+ if (!err)
+ material=material_cache[target];
+ } else
+ material=material_cache[target];
+
+ mi->set_surface_material(i,material);
+ } else if (matname!=""){
+ print_line("Warning, unreferenced material in geometry instance: "+matname);
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ for(int i=0;i<p_node->children.size();i++) {
+
+ Error err = _create_resources(p_node->children[i]);
+ if (err)
+ return err;
+ }
+ return OK;
+}
+
+
+Error ColladaImport::load(const String& p_path,int p_flags,bool p_force_make_tangents) {
+
+ Error err = collada.load(p_path,p_flags);
+ ERR_FAIL_COND_V(err,err);
+
+ force_make_tangents=p_force_make_tangents;
+ ERR_FAIL_COND_V( !collada.state.visual_scene_map.has( collada.state.root_visual_scene ), ERR_INVALID_DATA );
+ Collada::VisualScene &vs = collada.state.visual_scene_map[ collada.state.root_visual_scene ];
+
+ scene = memnew( Spatial ); // root
+
+ //determine what's going on with the lights
+ for(int i=0;i<vs.root_nodes.size();i++) {
+
+ _pre_process_lights(vs.root_nodes[i]);
+
+ }
+ //import scene
+
+ for(int i=0;i<vs.root_nodes.size();i++) {
+
+ Error err = _create_scene_skeletons(vs.root_nodes[i]);
+ if (err!=OK) {
+ memdelete(scene);
+ ERR_FAIL_COND_V(err,err);
+ }
+ }
+
+ for(int i=0;i<vs.root_nodes.size();i++) {
+
+ Error err = _create_scene(vs.root_nodes[i],scene);
+ if (err!=OK) {
+ memdelete(scene);
+ ERR_FAIL_COND_V(err,err);
+ }
+
+ Error err2 = _create_resources(vs.root_nodes[i]);
+ if (err2!=OK) {
+ memdelete(scene);
+ ERR_FAIL_COND_V(err2,err2);
+ }
+ }
+
+ //optatively, set unit scale in the root
+ scene->set_transform(collada.get_root_transform());
+
+
+ return OK;
+
+}
+
+void ColladaImport::_fix_param_animation_tracks() {
+
+ for (Map<String,Collada::Node*>::Element *E=collada.state.scene_map.front();E;E=E->next()) {
+
+ Collada::Node *n = E->get();
+ switch(n->type) {
+
+ case Collada::Node::TYPE_NODE: {
+ // ? do nothing
+ } break;
+ case Collada::Node::TYPE_JOINT: {
+
+ } break;
+ case Collada::Node::TYPE_SKELETON: {
+
+ } break;
+ case Collada::Node::TYPE_LIGHT: {
+
+ } break;
+ case Collada::Node::TYPE_CAMERA: {
+
+ } break;
+ case Collada::Node::TYPE_GEOMETRY: {
+
+ Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(n);
+ // test source(s)
+ String source = ng->source;
+
+ while (source!="") {
+
+ if (collada.state.skin_controller_data_map.has(source)) {
+
+ const Collada::SkinControllerData& skin = collada.state.skin_controller_data_map[source];
+
+ //nothing to animate here i think
+
+ source=skin.base;
+ } else if (collada.state.morph_controller_data_map.has(source)) {
+
+
+ const Collada::MorphControllerData& morph = collada.state.morph_controller_data_map[source];
+
+ if (morph.targets.has("MORPH_WEIGHT") && morph.targets.has("MORPH_TARGET")) {
+
+
+ String weights = morph.targets["MORPH_WEIGHT"];
+ String targets = morph.targets["MORPH_TARGET"];
+ //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<weight_src.array.size();i++) {
+
+ String track_name = weights+"("+itos(i)+")";
+ String mesh_name = target_src.sarray[i];
+ if (collada.state.mesh_name_map.has(mesh_name) && collada.state.referenced_tracks.has(track_name)) {
+
+
+ const Vector<int>&rt = collada.state.referenced_tracks[track_name];
+
+ for(int rti=0;rti<rt.size();rti++) {
+ Collada::AnimationTrack *at = &collada.state.animation_tracks[rt[rti]];
+
+ at->target=E->key();
+ at->param="morph/"+collada.state.mesh_name_map[mesh_name];
+ at->property=true;
+ //at->param
+ }
+ }
+ }
+ }
+ }
+ source=morph.mesh;
+ } else {
+
+ source=""; // for now nothing else supported
+ }
+ }
+
+ } break;
+
+ }
+ }
+
+}
+
+void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
+
+
+ _fix_param_animation_tracks();
+ for(int i=0;i<collada.state.animation_clips.size();i++) {
+
+ for(int j=0;j<collada.state.animation_clips[i].tracks.size();j++)
+ tracks_in_clips.insert(collada.state.animation_clips[i].tracks[j]);
+ }
+
+
+
+ for(int i=0;i<collada.state.animation_tracks.size();i++) {
+
+ Collada::AnimationTrack &at = collada.state.animation_tracks[i];
+ //print_line("CHANNEL: "+at.target+" PARAM: "+at.param);
+
+ String node;
+
+ if (!node_map.has(at.target)) {
+
+ if (node_name_map.has(at.target)) {
+
+ node=node_name_map[at.target];
+ } else {
+ print_line("Coudlnt find node: "+at.target);
+ continue;
+ }
+ } else {
+ node=at.target;
+ }
+
+
+ if (at.property) {
+
+ valid_animated_properties.push_back(i);
+
+ } else {
+
+ node_map[node].anim_tracks.push_back(i);
+ valid_animated_nodes.insert(node);
+ }
+
+ }
+
+ create_animation(-1,p_make_tracks_in_all_bones, p_import_value_tracks);
+ //print_line("clipcount: "+itos(collada.state.animation_clips.size()));
+ for(int i=0;i<collada.state.animation_clips.size();i++)
+ create_animation(i, p_make_tracks_in_all_bones, p_import_value_tracks);
+
+}
+
+void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
+
+ Ref<Animation> animation = Ref<Animation>( memnew( Animation ));
+
+
+ if (p_clip==-1) {
+
+ //print_line("default");
+ animation->set_name("default");
+ } else {
+ //print_line("clip name: "+collada.state.animation_clips[p_clip].name);
+ animation->set_name(collada.state.animation_clips[p_clip].name);
+ }
+
+ for(Map<String,NodeMap>::Element *E=node_map.front();E;E=E->next()) {
+
+ if (E->get().bone<0)
+ continue;
+ bones_with_animation[E->key()]=false;
+ }
+ //store and validate tracks
+
+ if (p_clip==-1) {
+ //main anim
+ }
+
+ Set<int> track_filter;
+
+
+ if (p_clip==-1) {
+
+ for(int i=0;i<collada.state.animation_clips.size();i++) {
+
+ int tc = collada.state.animation_clips[i].tracks.size();
+ for(int j=0;j<tc;j++) {
+
+ String n = collada.state.animation_clips[i].tracks[j];
+ if (collada.state.by_id_tracks.has(n)) {
+
+ const Vector<int>&ti = collada.state.by_id_tracks[n];
+ for(int k=0;k<ti.size();k++) {
+ track_filter.insert(ti[k]);
+ }
+ }
+ }
+ }
+ } else {
+
+ int tc = collada.state.animation_clips[p_clip].tracks.size();
+ for(int j=0;j<tc;j++) {
+
+ String n = collada.state.animation_clips[p_clip].tracks[j];
+ if (collada.state.by_id_tracks.has(n)) {
+
+ const Vector<int>&ti = collada.state.by_id_tracks[n];
+ for(int k=0;k<ti.size();k++) {
+ track_filter.insert(ti[k]);
+ }
+ }
+ }
+
+ }
+
+ //animation->set_loop(true);
+ //create animation tracks
+
+ Vector<float> base_snapshots;
+
+ float f=0;
+ float snapshot_interval = 1.0/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(f);
+
+ f+=snapshot_interval;
+
+ if (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<String>::Element* E=valid_animated_nodes.front();E;E=E->next()) {
+
+ // take snapshots
+
+
+ if (!collada.state.scene_map.has(E->get())) {
+
+ continue;
+ }
+
+ NodeMap &nm = node_map[E->get()];
+ String path = scene->get_path_to(nm.node);
+
+ if (nm.bone>=0) {
+ Skeleton *sk = static_cast<Skeleton*>(nm.node);
+ String name = sk->get_bone_name(nm.bone);
+ path=path+":"+name;
+ }
+
+ bool found_anim=false;
+
+
+ Collada::Node *cn = collada.state.scene_map[E->get()];
+ if (cn->ignore_anim) {
+
+ 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<float> snapshots = base_snapshots;
+
+ if (nm.anim_tracks.size()==1) {
+ //use snapshot keys from anim track instead, because this was most likely exported baked
+ Collada::AnimationTrack &at = collada.state.animation_tracks[nm.anim_tracks.front()->get()];
+ snapshots.clear();
+ for(int i=0;i<at.keys.size();i++)
+ snapshots.push_back(at.keys[i].time);
+
+
+ }
+
+
+ for(int i=0;i<snapshots.size();i++) {
+
+
+ for(List<int>::Element *ET=nm.anim_tracks.front();ET;ET=ET->next()) {
+ //apply tracks
+
+
+ if (p_clip==-1) {
+
+ if (track_filter.has(ET->get())) {
+
+ continue;
+ }
+ } else {
+
+ if (!track_filter.has(ET->get()))
+ continue;
+
+ }
+
+ found_anim=true;
+
+ Collada::AnimationTrack &at = collada.state.animation_tracks[ET->get()];
+
+ int xform_idx=-1;
+ for(int j=0;j<cn->xform_list.size();j++) {
+
+
+ if (cn->xform_list[j].id==at.param) {
+
+ xform_idx=j;
+ break;
+ }
+ }
+
+ if (xform_idx==-1) {
+ print_line("couldnt find matching node "+at.target+" xform for track "+at.param);
+ continue;
+ }
+
+ ERR_CONTINUE(xform_idx==-1);
+
+ Vector<float> data = at.get_value_at_time(snapshots[i]);
+ ERR_CONTINUE(data.empty());
+
+
+ Collada::Node::XForm &xf = cn->xform_list[xform_idx];
+
+ if (at.component=="ANGLE") {
+ ERR_CONTINUE(data.size()!=1);
+ ERR_CONTINUE(xf.op!=Collada::Node::XForm::OP_ROTATE);
+ ERR_CONTINUE(xf.data.size()<4);
+ xf.data[3]=data[0];
+ } else if (at.component=="X" || at.component=="Y" || at.component=="Z") {
+ int cn=at.component[0]-'X';
+ ERR_CONTINUE(cn>=xf.data.size());
+ ERR_CONTINUE(data.size()>1);
+ xf.data[cn]=data[0];
+ } else if (data.size()==xf.data.size()) {
+
+ xf.data=data;
+ } else {
+
+
+ if ( data.size()!=xf.data.size() ) {
+ print_line("component "+at.component+" datasize "+itos(data.size())+" xfdatasize "+itos(xf.data.size()));
+ }
+
+ ERR_CONTINUE( data.size()!=xf.data.size() );
+ }
+ }
+
+ Transform xform = cn->compute_transform(collada);
+ xform = collada.fix_transform(xform) * cn->post_transform;
+
+
+ if (nm.bone>=0) {
+ //make bone transform relative to rest (in case of skeleton)
+ Skeleton *sk = nm.node->cast_to<Skeleton>();
+ if (sk) {
+
+ xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
+ } else {
+
+ ERR_PRINT("INVALID SKELETON!!!!");
+ }
+ }
+
+ Quat q = xform.basis;
+ q.normalize();
+ Vector3 s = xform.basis.get_scale();
+ Vector3 l = xform.origin;
+
+ animation->transform_track_insert_key(track,snapshots[i],l,q,s);
+
+ }
+
+
+
+ if (nm.bone>=0) {
+ if (found_anim)
+ bones_with_animation[E->get()]=true;
+ }
+
+ if (found_anim)
+ tracks_found=true;
+ else {
+ animation->remove_track( track );
+ }
+
+ }
+
+ 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<String,bool>::Element *E=bones_with_animation.front();E;E=E->next()) {
+
+ if (E->get())
+ continue;
+
+ //print_line("BONE LACKS ANIM: "+E->key());
+
+ NodeMap &nm = node_map[E->key()];
+ String path = scene->get_path_to(nm.node);
+ ERR_CONTINUE( nm.bone <0 );
+ Skeleton *sk = static_cast<Skeleton*>(nm.node);
+ String name = sk->get_bone_name(nm.bone);
+ path=path+":"+name;
+
+ Collada::Node *cn = collada.state.scene_map[E->key()];
+ if (cn->ignore_anim) {
+ print_line("warning, ignoring animation on node: "+path);
+ continue;
+ }
+
+ animation->add_track(Animation::TYPE_TRANSFORM);
+ int track = animation->get_track_count() -1;
+ animation->track_set_path( track , path );
+ 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<float> data = at.keys[i].data;
+ if (data.size() == 1) {
+ //push a float
+ value = data[0];
+
+ }
+ else if (data.size() == 16) {
+ //matrix
+ print_line("value keys for matrices not supported");
+ }
+ else {
+
+ print_line("don't know what to do with this amount of value keys: " + itos(data.size()));
+ }
+
+ animation->track_insert_key(track, time, value);
+ }
+
+
+ tracks_found = true;
+
+ }
+ }
+
+
+
+ if (tracks_found) {
+
+ animations.push_back(animation);
+ }
+
+
+
+}
+
+
+/*********************************************************************************/
+/*************************************** SCENE ***********************************/
+/*********************************************************************************/
+
+
+#define DEBUG_ANIMATION
+
+
+uint32_t EditorSceneImporterCollada::get_import_flags() const {
+
+ return IMPORT_SCENE|IMPORT_ANIMATION;
+
+}
+void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) const {
+
+
+ r_extensions->push_back("dae");
+}
+Node* EditorSceneImporterCollada::import_scene(const String& p_path, uint32_t p_flags,int p_bake_fps, List<String> *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.use_mesh_builtin_materials=!(p_flags&IMPORT_MATERIALS_IN_INSTANCES);
+ 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;i<state.missing_textures.size();i++) {
+ EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]);
+ }
+ */
+
+
+ if (r_missing_deps) {
+
+ for(int i=0;i<state.missing_textures.size();i++) {
+ //EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]);
+ r_missing_deps->push_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;i<state.animations.size();i++) {
+ String name;
+ if (state.animations[i]->get_name()=="")
+ name="default";
+ else
+ name=state.animations[i]->get_name();
+
+ if (p_flags&IMPORT_ANIMATION_DETECT_LOOP) {
+
+ if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
+ state.animations[i]->set_loop(true);
+ }
+ }
+
+ ap->add_animation(name,state.animations[i]);
+ }
+ state.scene->add_child(ap);
+ ap->set_owner(state.scene);
+
+ }
+
+ return state.scene;
+
+}
+
+Ref<Animation> EditorSceneImporterCollada::import_animation(const String& p_path,uint32_t p_flags) {
+
+
+ ColladaImport state;
+
+
+ state.use_mesh_builtin_materials=false;
+
+ 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<Animation>();
+ Ref<Animation> 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/editor/import/editor_import_collada.h b/editor/import/editor_import_collada.h
new file mode 100644
index 0000000000..5c7624bd33
--- /dev/null
+++ b/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 "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<String> *r_extensions) const;
+ virtual Node* import_scene(const String& p_path,uint32_t p_flags,int p_bake_fps,List<String> *r_missing_deps=NULL,Error* r_err=NULL);
+ virtual Ref<Animation> import_animation(const String& p_path,uint32_t p_flags);
+
+ EditorSceneImporterCollada();
+};
+
+
+#endif
+
diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp
new file mode 100644
index 0000000000..f14c10fb99
--- /dev/null
+++ b/editor/import/resource_importer_csv_translation.cpp
@@ -0,0 +1,126 @@
+
+#include "resource_importer_csv_translation.h"
+#include "os/file_access.h"
+#include "translation.h"
+#include "io/resource_saver.h"
+#include "compressed_translation.h"
+
+String ResourceImporterCSVTranslation::get_importer_name() const {
+
+ return "csv_translation";
+}
+
+String ResourceImporterCSVTranslation::get_visible_name() const{
+
+ return "CSV Translation";
+}
+void ResourceImporterCSVTranslation::get_recognized_extensions(List<String> *p_extensions) const{
+
+ p_extensions->push_back("csv");
+}
+
+String ResourceImporterCSVTranslation::get_save_extension() const {
+ return ""; //does not save a single resoure
+}
+
+String ResourceImporterCSVTranslation::get_resource_type() const{
+
+ return "StreamCSVTranslation";
+}
+
+bool ResourceImporterCSVTranslation::get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const {
+
+ return true;
+}
+
+int ResourceImporterCSVTranslation::get_preset_count() const {
+ return 0;
+}
+String ResourceImporterCSVTranslation::get_preset_name(int p_idx) const {
+
+ return "";
+}
+
+
+void ResourceImporterCSVTranslation::get_import_options(List<ImportOption> *r_options,int p_preset) const {
+
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"compress"),true));
+
+}
+
+
+
+Error ResourceImporterCSVTranslation::import(const String& p_source_file, const String& p_save_path, const Map<StringName,Variant>& p_options, List<String>* r_platform_variants, List<String> *r_gen_files) {
+
+
+ bool compress = p_options["compress"];
+ FileAccessRef f = FileAccess::open(p_source_file,FileAccess::READ);
+
+ ERR_FAIL_COND_V( !f, ERR_INVALID_PARAMETER );
+
+ Vector<String> line = f->get_csv_line();
+ if (line.size()<=1) {
+ return ERR_PARSE_ERROR;
+ }
+
+ Vector<String> locales;
+ Vector<Ref<Translation> > translations;
+
+ for(int i=1;i<line.size();i++) {
+
+ String locale = line[i];
+ if (!TranslationServer::is_locale_valid(locale)) {
+ return ERR_PARSE_ERROR;
+ }
+
+ locales.push_back(locale);
+ Ref<Translation> translation;
+ translation.instance();
+ translation->set_locale(locale);
+ translations.push_back(translation);
+ }
+
+ line = f->get_csv_line();
+
+ while(line.size()==locales.size()+1) {
+
+ String key = line[0];
+ if (key!="") {
+
+ for(int i=1;i<line.size();i++) {
+ translations[i-1]->add_message(key,line[i]);
+ }
+ }
+
+ line = f->get_csv_line();
+ }
+
+
+ for(int i=0;i<translations.size();i++) {
+ Ref<Translation> xlt = translations[i];
+
+ if (compress) {
+ Ref<PHashTranslation> cxl = memnew( PHashTranslation );
+ cxl->generate( xlt );
+ xlt=cxl;
+ }
+
+ String save_path = p_source_file.get_basename()+"."+translations[i]->get_locale()+".xl";
+
+ ResourceSaver::save(save_path,xlt);
+ if (r_gen_files) {
+ r_gen_files->push_back(save_path);
+ }
+ }
+
+
+
+ return OK;
+
+}
+
+ResourceImporterCSVTranslation::ResourceImporterCSVTranslation()
+{
+
+}
diff --git a/editor/import/resource_importer_csv_translation.h b/editor/import/resource_importer_csv_translation.h
new file mode 100644
index 0000000000..d08218e7d9
--- /dev/null
+++ b/editor/import/resource_importer_csv_translation.h
@@ -0,0 +1,27 @@
+#ifndef RESOURCEIMPORTERCSVTRANSLATION_H
+#define RESOURCEIMPORTERCSVTRANSLATION_H
+
+#include "io/resource_import.h"
+
+
+class ResourceImporterCSVTranslation : public ResourceImporter {
+ GDCLASS(ResourceImporterCSVTranslation,ResourceImporter)
+public:
+ virtual String get_importer_name() const;
+ virtual String get_visible_name() const;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual String get_save_extension() const;
+ virtual String get_resource_type() const;
+
+ virtual int get_preset_count() const;
+ virtual String get_preset_name(int p_idx) const;
+
+ virtual void get_import_options(List<ImportOption> *r_options,int p_preset=0) const;
+ virtual bool get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const;
+
+ virtual Error import(const String& p_source_file,const String& p_save_path,const Map<StringName,Variant>& p_options,List<String>* r_platform_variants,List<String>* r_gen_files=NULL);
+
+ ResourceImporterCSVTranslation();
+};
+
+#endif // RESOURCEIMPORTERCSVTRANSLATION_H
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
new file mode 100644
index 0000000000..aacb5fbb2d
--- /dev/null
+++ b/editor/import/resource_importer_obj.cpp
@@ -0,0 +1,231 @@
+#include "resource_importer_obj.h"
+
+#include "io/resource_saver.h"
+#include "scene/resources/mesh.h"
+#include "scene/resources/surface_tool.h"
+#include "scene/resources/surface_tool.h"
+#include "os/file_access.h"
+
+String ResourceImporterOBJ::get_importer_name() const {
+
+ return "obj_mesh";
+}
+
+String ResourceImporterOBJ::get_visible_name() const{
+
+ return "OBJ As Mesh";
+}
+void ResourceImporterOBJ::get_recognized_extensions(List<String> *p_extensions) const{
+
+ p_extensions->push_back("obj");
+}
+String ResourceImporterOBJ::get_save_extension() const {
+ return "msh";
+}
+
+String ResourceImporterOBJ::get_resource_type() const{
+
+ return "Mesh";
+}
+
+bool ResourceImporterOBJ::get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const {
+
+ return true;
+}
+
+int ResourceImporterOBJ::get_preset_count() const {
+ return 0;
+}
+String ResourceImporterOBJ::get_preset_name(int p_idx) const {
+
+ return String();
+}
+
+
+void ResourceImporterOBJ::get_import_options(List<ImportOption> *r_options,int p_preset) const {
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"generate/tangents"),true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"generate/normals"),true));
+ //not for nowp
+ //r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"import/materials")));
+ //r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"import/textures")));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"force/flip_faces"),false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"force/smooth_shading"),true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"force/weld_vertices"),true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::REAL,"force/weld_tolerance",PROPERTY_HINT_RANGE,"0.00001,16,0.00001"),0.0001));
+ //r_options->push_back(PropertyInfo(Variant::INT,"compress/bitrate",PROPERTY_HINT_ENUM,"64,96,128,192"));
+
+}
+
+
+
+Error ResourceImporterOBJ::import(const String& p_source_file, const String& p_save_path, const Map<StringName,Variant>& p_options, List<String>* r_platform_variants, List<String> *r_gen_files) {
+
+ FileAccessRef f = FileAccess::open(p_source_file,FileAccess::READ);
+ ERR_FAIL_COND_V(!f,ERR_CANT_OPEN);
+
+ Ref<Mesh> mesh = Ref<Mesh>( memnew( Mesh ) );
+ Map<String,Ref<Material> > name_map;
+
+
+
+ bool generate_normals=p_options["generate/normals"];
+ bool generate_tangents=p_options["generate/tangents"];
+ bool flip_faces=p_options["force/flip_faces"];
+ bool force_smooth=p_options["force/smooth_shading"];
+ bool weld_vertices=p_options["force/weld_vertices"];
+ float weld_tolerance=p_options["force/weld_tolerance"];
+ Vector<Vector3> vertices;
+ Vector<Vector3> normals;
+ Vector<Vector2> uvs;
+ String name;
+
+ Ref<SurfaceTool> surf_tool = memnew( SurfaceTool) ;
+ surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ if (force_smooth)
+ surf_tool->add_smooth_group(true);
+ int has_index_data=false;
+
+ while(true) {
+
+
+ String l = f->get_line().strip_edges();
+
+ if (l.begins_with("v ")) {
+ //vertex
+ Vector<String> v = l.split(" ",false);
+ ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA);
+ Vector3 vtx;
+ vtx.x=v[1].to_float();
+ vtx.y=v[2].to_float();
+ vtx.z=v[3].to_float();
+ vertices.push_back(vtx);
+ } else if (l.begins_with("vt ")) {
+ //uv
+ Vector<String> v = l.split(" ",false);
+ ERR_FAIL_COND_V(v.size()<3,ERR_INVALID_DATA);
+ Vector2 uv;
+ uv.x=v[1].to_float();
+ uv.y=1.0-v[2].to_float();
+ uvs.push_back(uv);
+
+ } else if (l.begins_with("vn ")) {
+ //normal
+ Vector<String> v = l.split(" ",false);
+ ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA);
+ Vector3 nrm;
+ nrm.x=v[1].to_float();
+ nrm.y=v[2].to_float();
+ nrm.z=v[3].to_float();
+ normals.push_back(nrm);
+ } else if (l.begins_with("f ")) {
+ //vertex
+
+ has_index_data=true;
+ Vector<String> v = l.split(" ",false);
+ ERR_FAIL_COND_V(v.size()<4,ERR_INVALID_DATA);
+
+ //not very fast, could be sped up
+
+
+ Vector<String> face[3];
+ face[0] = v[1].split("/");
+ face[1] = v[2].split("/");
+ ERR_FAIL_COND_V(face[0].size()==0,ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(face[0].size()!=face[1].size(),ERR_PARSE_ERROR);
+ for(int i=2;i<v.size()-1;i++) {
+
+ face[2] = v[i+1].split("/");
+ ERR_FAIL_COND_V(face[0].size()!=face[2].size(),ERR_PARSE_ERROR);
+ for(int j=0;j<3;j++) {
+
+ int idx=j;
+
+ if (!flip_faces && idx<2) {
+ idx=1^idx;
+ }
+
+
+ if (face[idx].size()==3) {
+ int norm = face[idx][2].to_int()-1;
+ ERR_FAIL_INDEX_V(norm,normals.size(),ERR_PARSE_ERROR);
+ surf_tool->add_normal(normals[norm]);
+ }
+
+ if (face[idx].size()>=2 && face[idx][1]!=String()) {
+
+ int uv = face[idx][1].to_int()-1;
+ ERR_FAIL_INDEX_V(uv,uvs.size(),ERR_PARSE_ERROR);
+ surf_tool->add_uv(uvs[uv]);
+ }
+
+ int vtx = face[idx][0].to_int()-1;
+ ERR_FAIL_INDEX_V(vtx,vertices.size(),ERR_PARSE_ERROR);
+
+ Vector3 vertex = vertices[vtx];
+ if (weld_vertices)
+ vertex=vertex.snapped(weld_tolerance);
+ surf_tool->add_vertex(vertex);
+ }
+
+ face[1]=face[2];
+ }
+ } else if (l.begins_with("s ") && !force_smooth) { //smoothing
+ String what = l.substr(2,l.length()).strip_edges();
+ if (what=="off")
+ surf_tool->add_smooth_group(false);
+ else
+ surf_tool->add_smooth_group(true);
+
+ } else if (l.begins_with("o ") || f->eof_reached()) { //new surface or done
+
+ if (has_index_data) {
+ //new object/surface
+ if (generate_normals || force_smooth)
+ surf_tool->generate_normals();
+ if (uvs.size() && (normals.size() || generate_normals) && generate_tangents)
+ surf_tool->generate_tangents();
+
+ surf_tool->index();
+ mesh = surf_tool->commit(mesh);
+ if (name=="")
+ name=vformat(TTR("Surface %d"),mesh->get_surface_count()-1);
+ mesh->surface_set_name(mesh->get_surface_count()-1,name);
+ name="";
+ surf_tool->clear();
+ surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ if (force_smooth)
+ surf_tool->add_smooth_group(true);
+
+ has_index_data=false;
+
+ if (f->eof_reached())
+ break;
+ }
+
+ if (l.begins_with("o ")) //name
+ name=l.substr(2,l.length()).strip_edges();
+ }
+ }
+
+/*
+ TODO, check existing materials and merge?
+ //re-apply materials if exist
+ for(int i=0;i<mesh->get_surface_count();i++) {
+
+ String n = mesh->surface_get_name(i);
+ if (name_map.has(n))
+ mesh->surface_set_material(i,name_map[n]);
+ }
+*/
+
+ Error err = ResourceSaver::save(p_save_path+".msh",mesh);
+
+ return err;
+
+}
+
+ResourceImporterOBJ::ResourceImporterOBJ()
+{
+
+}
diff --git a/editor/import/resource_importer_obj.h b/editor/import/resource_importer_obj.h
new file mode 100644
index 0000000000..d2a3c4fddd
--- /dev/null
+++ b/editor/import/resource_importer_obj.h
@@ -0,0 +1,28 @@
+#ifndef RESOURCEIMPORTEROBJ_H
+#define RESOURCEIMPORTEROBJ_H
+
+
+#include "io/resource_import.h"
+
+class ResourceImporterOBJ : public ResourceImporter {
+ GDCLASS(ResourceImporterOBJ,ResourceImporter)
+public:
+ virtual String get_importer_name() const;
+ virtual String get_visible_name() const;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual String get_save_extension() const;
+ virtual String get_resource_type() const;
+
+ virtual int get_preset_count() const;
+ virtual String get_preset_name(int p_idx) const;
+
+ virtual void get_import_options(List<ImportOption> *r_options,int p_preset=0) const;
+ virtual bool get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const;
+
+ virtual Error import(const String& p_source_file,const String& p_save_path,const Map<StringName,Variant>& p_options,List<String>* r_platform_variants,List<String>* r_gen_files=NULL);
+
+ ResourceImporterOBJ();
+};
+
+
+#endif // RESOURCEIMPORTEROBJ_H
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
new file mode 100644
index 0000000000..cdaf40798b
--- /dev/null
+++ b/editor/import/resource_importer_scene.cpp
@@ -0,0 +1,1328 @@
+#include "resource_importer_scene.h"
+
+#include "scene/resources/packed_scene.h"
+#include "io/resource_saver.h"
+#include "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<String> *p_extensions) const{
+
+ for (Set< Ref<EditorSceneImporter> >::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<StringName,Variant>& 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<Mesh>,Ref<Shape> > &collision_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);
+ 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>()) {
+
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+
+ bool bb=false;
+
+ if ((_teststr(name,"bb"))) {
+ bb=true;
+ } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"bb"))) {
+ bb=true;
+
+ }
+
+ if (bb) {
+ mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true);
+ if (mi->get_mesh().is_valid()) {
+
+ Ref<Mesh> m = mi->get_mesh();
+ for(int i=0;i<m->get_surface_count();i++) {
+
+ Ref<FixedSpatialMaterial> fm = m->surface_get_material(i);
+ if (fm.is_valid()) {
+ //fm->set_flag(Material::FLAG_UNSHADED,true);
+ //fm->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ //fm->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
+ //fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true);
+ }
+ }
+ }
+ }
+ }
+
+
+ if (p_node->cast_to<MeshInstance>()) {
+
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+
+ Ref<Mesh> m = mi->get_mesh();
+
+ if (m.is_valid()) {
+
+ for(int i=0;i<m->get_surface_count();i++) {
+
+ Ref<FixedSpatialMaterial> mat = m->surface_get_material(i);
+ if (!mat.is_valid())
+ continue;
+
+ if (_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<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_node->cast_to<MeshInstance>()) {
+
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+
+ String str;
+
+ if ((_teststr(name,"imp"))) {
+ str=name;
+ } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"imp"))) {
+ str=mi->get_mesh()->get_name();
+
+ }
+
+
+ if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) {
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+ MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>();
+ String d=str.substr(str.find("imp")+3,str.length());
+ if (d!="") {
+ if ((d[0]<'0' || d[0]>'9'))
+ d=d.substr(1,d.length());
+ if (d.length() && d[0]>='0' && d[0]<='9') {
+ float dist = d.to_double();
+ mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true);
+ mi->set_flag(GeometryInstance::FLAG_BILLBOARD_FIX_Y,true);
+ //mi->set_draw_range_begin(dist);
+ //mi->set_draw_range_end(100000);
+
+ //mip->set_draw_range_begin(0);
+ //mip->set_draw_range_end(dist);
+
+ if (mi->get_mesh().is_valid()) {
+
+ Ref<Mesh> m = mi->get_mesh();
+ for(int i=0;i<m->get_surface_count();i++) {
+
+ Ref<FixedSpatialMaterial> fm = m->surface_get_material(i);
+ if (fm.is_valid()) {
+ //fm->set_flag(Material::FLAG_UNSHADED,true);
+ //fm->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ //fm->set_depth_draw_mode(Material::DEPTH_DRAW_NEVER);
+ //fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+#if 0
+ if (p_flags&SCENE_FLAG_CREATE_LODS && p_node->cast_to<MeshInstance>()) {
+
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+
+ String str;
+
+ if ((_teststr(name,"lod"))) {
+ str=name;
+ } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"lod"))) {
+ str=mi->get_mesh()->get_name();
+
+ }
+
+
+ if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) {
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+ MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>();
+ String d=str.substr(str.find("lod")+3,str.length());
+ if (d!="") {
+ if ((d[0]<'0' || d[0]>'9'))
+ d=d.substr(1,d.length());
+ if (d.length() && d[0]>='0' && d[0]<='9') {
+ float dist = d.to_double();
+ /// mi->set_draw_range_begin(dist);
+ // mi->set_draw_range_end(100000);
+
+ // mip->set_draw_range_begin(0);
+ // mip->set_draw_range_end(dist);
+
+ /*if (mi->get_mesh().is_valid()) {
+
+ Ref<Mesh> m = mi->get_mesh();
+ for(int i=0;i<m->get_surface_count();i++) {
+
+ Ref<FixedSpatialMaterial> fm = m->surface_get_material(i);
+ if (fm.is_valid()) {
+ fm->set_flag(Material::FLAG_UNSHADED,true);
+ fm->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+ fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true);
+ }
+ }
+ }*/
+ }
+ }
+ }
+ }
+
+
+ if (p_flags&SCENE_FLAG_DETECT_LIGHTMAP_LAYER && _teststr(name,"lm") && p_node->cast_to<MeshInstance>()) {
+
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+
+ String str=name;
+ int layer = str.substr(str.find("lm")+3,str.length()).to_int();
+ //mi->set_baked_light_texture_id(layer);
+ }
+#endif
+ if (_teststr(name,"colonly")) {
+
+ if (isroot)
+ return p_node;
+
+ if (p_node->cast_to<MeshInstance>()) {
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+ Node * col = mi->create_trimesh_collision_node();
+ ERR_FAIL_COND_V(!col,NULL);
+
+ col->set_name(_fixstr(name,"colonly"));
+ col->cast_to<Spatial>()->set_transform(mi->get_transform());
+ p_node->replace_by(col);
+ memdelete(p_node);
+ p_node=col;
+
+ StaticBody *sb = col->cast_to<StaticBody>();
+ CollisionShape *colshape = memnew( CollisionShape);
+ colshape->set_shape(sb->get_shape(0));
+ colshape->set_name("shape");
+ sb->add_child(colshape);
+ colshape->set_owner(p_node->get_owner());
+ } else if (p_node->has_meta("empty_draw_type")) {
+ String empty_draw_type = String(p_node->get_meta("empty_draw_type"));
+ print_line(empty_draw_type);
+ StaticBody *sb = memnew( StaticBody);
+ sb->set_name(_fixstr(name,"colonly"));
+ sb->cast_to<Spatial>()->set_transform(p_node->cast_to<Spatial>()->get_transform());
+ p_node->replace_by(sb);
+ memdelete(p_node);
+ CollisionShape *colshape = memnew( CollisionShape);
+ if (empty_draw_type == "CUBE") {
+ BoxShape *boxShape = memnew( BoxShape);
+ boxShape->set_extents(Vector3(1, 1, 1));
+ colshape->set_shape(boxShape);
+ colshape->set_name("BoxShape");
+ } else if (empty_draw_type == "SINGLE_ARROW") {
+ RayShape *rayShape = memnew( RayShape);
+ rayShape->set_length(1);
+ colshape->set_shape(rayShape);
+ colshape->set_name("RayShape");
+ sb->cast_to<Spatial>()->rotate_x(Math_PI / 2);
+ } else if (empty_draw_type == "IMAGE") {
+ PlaneShape *planeShape = memnew( PlaneShape);
+ colshape->set_shape(planeShape);
+ colshape->set_name("PlaneShape");
+ } else {
+ SphereShape *sphereShape = memnew( SphereShape);
+ sphereShape->set_radius(1);
+ colshape->set_shape(sphereShape);
+ colshape->set_name("SphereShape");
+ }
+ sb->add_child(colshape);
+ colshape->set_owner(sb->get_owner());
+ }
+
+ } else if (_teststr(name,"rigid") && p_node->cast_to<MeshInstance>()) {
+
+ if (isroot)
+ return p_node;
+
+ // get mesh instance and bounding box
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+ Rect3 aabb = mi->get_aabb();
+
+ // create a new rigid body collision node
+ RigidBody * rigid_body = memnew( RigidBody );
+ Node * col = rigid_body;
+ ERR_FAIL_COND_V(!col,NULL);
+
+ // remove node name postfix
+ col->set_name(_fixstr(name,"rigid"));
+ // get mesh instance xform matrix to the rigid body collision node
+ col->cast_to<Spatial>()->set_transform(mi->get_transform());
+ // save original node by duplicating it into a new instance and correcting the name
+ Node * mesh = p_node->duplicate();
+ mesh->set_name(_fixstr(name,"rigid"));
+ // reset the xform matrix of the duplicated node so it can inherit parent node xform
+ mesh->cast_to<Spatial>()->set_transform(Transform(Basis()));
+ // reparent the new mesh node to the rigid body collision node
+ p_node->add_child(mesh);
+ mesh->set_owner(p_node->get_owner());
+ // replace the original node with the rigid body collision node
+ p_node->replace_by(col);
+ memdelete(p_node);
+ p_node=col;
+
+ // create an alias for the rigid body collision node
+ RigidBody *rb = col->cast_to<RigidBody>();
+ // create a new Box collision shape and set the right extents
+ Ref<BoxShape> shape = memnew( BoxShape );
+ shape->set_extents(aabb.get_size() * 0.5);
+ CollisionShape *colshape = memnew( CollisionShape);
+ colshape->set_name("shape");
+ colshape->set_shape(shape);
+ // reparent the new collision shape to the rigid body collision node
+ rb->add_child(colshape);
+ colshape->set_owner(p_node->get_owner());
+
+ } else if (_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 (_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 (_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 (_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 (_teststr(name,"room") && p_node->cast_to<MeshInstance>()) {
+
+
+ if (isroot)
+ return p_node;
+
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+ PoolVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID);
+
+
+ BSP_Tree bsptree(faces);
+
+ Ref<RoomBounds> area = memnew( RoomBounds );
+ //area->set_bounds(faces);
+ //area->set_geometry_hint(faces);
+
+
+ Room * room = memnew( Room );
+ room->set_name(_fixstr(name,"room"));
+ room->set_transform(mi->get_transform());
+ room->set_room(area);
+
+ p_node->replace_by(room);
+ memdelete(p_node);
+ p_node=room;
+
+ } else if (_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 (_teststr(name,"portal") && p_node->cast_to<MeshInstance>()) {
+
+ if (isroot)
+ return p_node;
+
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+ PoolVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID);
+
+ ERR_FAIL_COND_V(faces.size()==0,NULL);
+ //step 1 compute the plane
+ Set<Vector3> points;
+ Plane plane;
+
+ Vector3 center;
+
+ for(int i=0;i<faces.size();i++) {
+
+ Face3 f = faces.get(i);
+ Plane p = f.get_plane();
+ plane.normal+=p.normal;
+ plane.d+=p.d;
+
+ for(int i=0;i<3;i++) {
+
+ Vector3 v = f.vertex[i].snapped(0.01);
+ if (!points.has(v)) {
+ points.insert(v);
+ center+=v;
+ }
+ }
+ }
+
+ plane.normal.normalize();
+ plane.d/=faces.size();
+ center/=points.size();
+
+ //step 2, create points
+
+ Transform t;
+ t.basis.from_z(plane.normal);
+ t.basis.transpose();
+ t.origin=center;
+
+ Vector<Point2> portal_points;
+
+ for(Set<Vector3>::Element *E=points.front();E;E=E->next()) {
+
+ Vector3 local = t.xform_inv(E->get());
+ portal_points.push_back(Point2(local.x,local.y));
+ }
+ // step 3 bubbly sort points
+
+ int swaps=0;
+
+ do {
+ swaps=0;
+
+ for(int i=0;i<portal_points.size()-1;i++) {
+
+ float a = portal_points[i].angle();
+ float b = portal_points[i+1].angle();
+
+ if (a>b) {
+ SWAP( portal_points[i], portal_points[i+1] );
+ swaps++;
+ }
+
+ }
+
+ } while(swaps);
+
+
+ Portal *portal = memnew( Portal );
+
+ portal->set_shape(portal_points);
+ portal->set_transform( mi->get_transform() * t);
+
+ p_node->replace_by(portal);
+ memdelete(p_node);
+ p_node=portal;
+
+ } else if (p_node->cast_to<MeshInstance>()) {
+
+ //last attempt, maybe collision insde the mesh data
+
+ MeshInstance *mi = p_node->cast_to<MeshInstance>();
+
+ Ref<Mesh> mesh = mi->get_mesh();
+ if (!mesh.is_null()) {
+
+ if (_teststr(mesh->get_name(),"col")) {
+
+ mesh->set_name( _fixstr(mesh->get_name(),"col") );
+ Ref<Shape> shape;
+
+ if (collision_map.has(mesh)) {
+ shape = collision_map[mesh];
+
+ } else {
+
+ shape = mesh->create_trimesh_shape();
+ if (!shape.is_null())
+ collision_map[mesh]=shape;
+
+
+ }
+
+ if (!shape.is_null()) {
+#if 0
+ StaticBody* static_body = memnew( StaticBody );
+ ERR_FAIL_COND_V(!static_body,NULL);
+ static_body->set_name( String(mesh->get_name()) + "_col" );
+ shape->set_name(static_body->get_name());
+ static_body->add_shape(shape);
+
+ mi->add_child(static_body);
+ if (mi->get_owner())
+ static_body->set_owner( mi->get_owner() );
+#endif
+ }
+
+ }
+
+ for(int i=0;i<mesh->get_surface_count();i++) {
+
+ Ref<FixedSpatialMaterial> fm = mesh->surface_get_material(i);
+ if (fm.is_valid()) {
+ String name = fm->get_name();
+ /* if (_teststr(name,"alpha")) {
+ fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_ALPHA,true);
+ name=_fixstr(name,"alpha");
+ }
+
+ if (_teststr(name,"vcol")) {
+ fm->set_fixed_flag(FixedSpatialMaterial::FLAG_USE_COLOR_ARRAY,true);
+ name=_fixstr(name,"vcol");
+ }*/
+ fm->set_name(name);
+ }
+ }
+
+ }
+
+ }
+
+
+ return p_node;
+}
+
+
+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<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 ResourceImporterScene::_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 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<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());
+ }
+ _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<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));
+ }
+}
+
+
+static String _make_extname(const String& p_str) {
+
+ String ext_name=p_str.replace(".","_");
+ ext_name=ext_name.replace(":","_");
+ ext_name=ext_name.replace("\"","_");
+ ext_name=ext_name.replace("<","_");
+ ext_name=ext_name.replace(">","_");
+ ext_name=ext_name.replace("/","_");
+ ext_name=ext_name.replace("|","_");
+ ext_name=ext_name.replace("\\","_");
+ ext_name=ext_name.replace("?","_");
+ ext_name=ext_name.replace("*","_");
+
+ return ext_name;
+}
+
+void ResourceImporterScene::_make_external_resources(Node* p_node,const String& p_base_path, bool p_make_materials, bool p_make_meshes, Map<Ref<Material>,Ref<Material> >& p_materials, Map<Ref<Mesh>,Ref<Mesh> >& p_meshes) {
+
+ List<PropertyInfo> pi;
+
+ p_node->get_property_list(&pi);
+
+ for (List<PropertyInfo>::Element *E=pi.front();E;E=E->next()) {
+
+ if (E->get().type==Variant::OBJECT) {
+
+ Ref<Material> mat = p_node->get(E->get().name);
+ if (p_make_materials && mat.is_valid() && mat->get_name()!="") {
+
+
+ if (!p_materials.has(mat)) {
+
+ String ext_name = p_base_path+"."+_make_extname(mat->get_name())+".mtl";
+ if (FileAccess::exists(ext_name)) {
+ //if exists, use it
+ Ref<Material> existing = ResourceLoader::load(ext_name);
+ p_materials[mat]=existing;
+ } else {
+
+ ResourceSaver::save(ext_name,mat,ResourceSaver::FLAG_CHANGE_PATH);
+ p_materials[mat]=mat;
+ }
+ }
+
+ if (p_materials[mat]!=mat) {
+
+ p_node->set(E->get().name,p_materials[mat]);
+ }
+ } else {
+
+ Ref<Mesh> mesh = p_node->get(E->get().name);
+
+ if (mesh.is_valid()) {
+
+ bool mesh_just_added=false;
+
+ if (p_make_meshes) {
+
+ if (!p_meshes.has(mesh)) {
+
+ String ext_name = p_base_path+"."+_make_extname(mesh->get_name())+".msh";
+ if (FileAccess::exists(ext_name)) {
+ //if exists, use it
+ Ref<Mesh> existing = ResourceLoader::load(ext_name);
+ p_meshes[mesh]=existing;
+ } else {
+
+ ResourceSaver::save(ext_name,mesh,ResourceSaver::FLAG_CHANGE_PATH);
+ p_meshes[mesh]=mesh;
+ mesh_just_added=true;
+ }
+
+
+ }
+ }
+
+
+ if (p_make_materials){
+
+ if (mesh_just_added || !p_meshes.has(mesh)) {
+
+
+ for(int i=0;i<mesh->get_surface_count();i++) {
+ mat=mesh->surface_get_material(i);
+ if (!mat.is_valid() || mat->get_name()=="")
+ continue;
+
+ if (!p_materials.has(mat)) {
+
+ String ext_name = p_base_path+"."+_make_extname(mat->get_name())+".mtl";
+ if (FileAccess::exists(ext_name)) {
+ //if exists, use it
+ Ref<Material> existing = ResourceLoader::load(ext_name);
+ p_materials[mat]=existing;
+ } else {
+
+ ResourceSaver::save(ext_name,mat,ResourceSaver::FLAG_CHANGE_PATH);
+ p_materials[mat]=mat;
+ }
+ }
+
+ if (p_materials[mat]!=mat) {
+
+ mesh->surface_set_material(i,p_materials[mat]);
+ }
+
+ }
+
+ if(!p_make_meshes) {
+ p_meshes[mesh]=Ref<Mesh>(); //save it anyway, so it won't be checked again
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+
+ _make_external_resources(p_node->get_child(i),p_base_path,p_make_materials,p_make_meshes,p_materials,p_meshes);
+ }
+}
+
+
+void ResourceImporterScene::get_import_options(List<ImportOption> *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<String> script_extentions;
+ ResourceLoader::get_recognized_extensions_for_type("Script",&script_extentions);
+
+ String script_ext_hint;
+
+ for(List<String>::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"),0));
+ 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<StringName,Variant>& p_options, List<String>* r_platform_variants, List<String> *r_gen_files) {
+
+ String src_path=p_source_file;
+
+ Ref<EditorSceneImporter> importer;
+ String ext=src_path.get_extension().to_lower();
+
+
+ EditorProgress progress("import",TTR("Import Scene"),104);
+ progress.step(TTR("Importing Scene.."),0);
+
+ for(Set< Ref<EditorSceneImporter> >::Element *E=importers.front();E;E=E->next()) {
+
+ List<String> extensions;
+ E->get()->get_extensions(&extensions);
+
+ for(List<String>::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;
+
+ if (int(p_options["materials/location"])==0)
+ import_flags|=EditorSceneImporter::IMPORT_MATERIALS_IN_INSTANCES;
+
+
+ Error err=OK;
+ List<String> 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<Node>();
+
+ 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<Mesh>,Ref<Shape> > 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<clip_count;i++) {
+ String name = p_options["animation/clip_"+itos(i+1)+"/name"];
+ int from_frame = p_options["animation/clip_"+itos(i+1)+"/start_frame"];
+ int end_frame = p_options["animation/clip_"+itos(i+1)+"/end_frame"];
+ bool loop = p_options["animation/clip_"+itos(i+1)+"/loops"];
+
+ animation_clips.push_back(name);
+ animation_clips.push_back(from_frame/fps);
+ animation_clips.push_back(end_frame/fps);
+ animation_clips.push_back(loop);
+ }
+
+ }
+ if (animation_clips.size()) {
+ _create_clips(scene,animation_clips,!bool(p_options["animation/optimizer/remove_unused_tracks"]));
+ }
+
+ if (animation_filter!="") {
+ _filter_tracks(scene,animation_filter);
+ }
+
+
+ bool external_materials = p_options["materials/storage"];
+ bool external_meshes = p_options["geometry/storage"];
+
+ if (external_materials || external_meshes) {
+ Map<Ref<Material>, Ref<Material> > mat_map;
+ Map<Ref<Mesh>, Ref<Mesh> > mesh_map;
+ _make_external_resources(scene,p_source_file.get_basename(),external_materials,external_meshes,mat_map,mesh_map);
+ }
+
+ progress.step(TTR("Running Custom Script.."),2);
+
+ String post_import_script_path = p_options["nodes/custom_script"];
+ Ref<EditorScenePostImport> post_import_script;
+
+ if (post_import_script_path!="") {
+ post_import_script_path = post_import_script_path; // FIXME: is there a good reason for this?
+ Ref<Script> scr = ResourceLoader::load(post_import_script_path);
+ if (!scr.is_valid()) {
+ EditorNode::add_io_error(TTR("Couldn't load post-import script:")+" "+post_import_script_path);
+ } else {
+
+ post_import_script = Ref<EditorScenePostImport>( memnew( EditorScenePostImport ) );
+ post_import_script->set_script(scr.get_ref_ptr());
+ if (!post_import_script->get_script_instance()) {
+ EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):")+" "+post_import_script_path);
+ post_import_script.unref();
+ return ERR_CANT_CREATE;
+ }
+ }
+ }
+
+
+ if (post_import_script.is_valid()) {
+ scene = post_import_script->post_import(scene);
+ if (!scene) {
+ EditorNode::add_io_error(TTR("Error running post-import script:")+" "+post_import_script_path);
+ return err;
+ }
+
+
+ }
+
+ progress.step(TTR("Saving.."),104);
+
+ Ref<PackedScene> packer = memnew( PackedScene );
+ packer->pack(scene);
+ print_line("SAVING TO: "+p_save_path+".scn");
+ err = ResourceSaver::save(p_save_path+".scn",packer); //do not take over, let the changed files reload themselves
+
+ memdelete(scene);
+
+ EditorNode::get_singleton()->reload_scene(p_source_file);
+
+ return OK;
+}
+
+ResourceImporterScene *ResourceImporterScene::singleton=NULL;
+
+ResourceImporterScene::ResourceImporterScene()
+{
+ singleton=this;
+}
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
new file mode 100644
index 0000000000..cfa44b160a
--- /dev/null
+++ b/editor/import/resource_importer_scene.h
@@ -0,0 +1,93 @@
+#ifndef RESOURCEIMPORTERSCENE_H
+#define RESOURCEIMPORTERSCENE_H
+
+#include "io/resource_import.h"
+#include "scene/resources/animation.h"
+#include "scene/resources/shape.h"
+
+class Material;
+
+class EditorSceneImporter : public Reference {
+
+ GDCLASS(EditorSceneImporter,Reference );
+public:
+
+ enum ImportFlags {
+ IMPORT_SCENE=1,
+ IMPORT_ANIMATION=2,
+ IMPORT_ANIMATION_DETECT_LOOP=4,
+ IMPORT_ANIMATION_OPTIMIZE=8,
+ IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS=16,
+ IMPORT_ANIMATION_KEEP_VALUE_TRACKS=32,
+ IMPORT_GENERATE_TANGENT_ARRAYS=256,
+ IMPORT_FAIL_ON_MISSING_DEPENDENCIES=512,
+ IMPORT_MATERIALS_IN_INSTANCES=1024
+
+ };
+
+ virtual uint32_t get_import_flags() const=0;
+ virtual void get_extensions(List<String> *r_extensions) const=0;
+ virtual Node* import_scene(const String& p_path,uint32_t p_flags,int p_bake_fps,List<String> *r_missing_deps,Error* r_err=NULL)=0;
+ virtual Ref<Animation> import_animation(const String& p_path,uint32_t p_flags)=0;
+
+
+
+ EditorSceneImporter() {}
+};
+
+class EditorScenePostImport : public Reference {
+
+ GDCLASS(EditorScenePostImport,Reference );
+protected:
+
+ static void _bind_methods();
+public:
+
+ virtual Node* post_import(Node* p_scene);
+ EditorScenePostImport();
+};
+
+
+class ResourceImporterScene : public ResourceImporter {
+ GDCLASS(ResourceImporterScene,ResourceImporter)
+
+ Set< Ref<EditorSceneImporter> > importers;
+
+ static ResourceImporterScene *singleton;
+public:
+
+ static ResourceImporterScene *get_singleton() { return singleton; }
+
+ const Set< Ref<EditorSceneImporter> >& get_importers() const { return importers; }
+
+ void add_importer(Ref<EditorSceneImporter> p_importer) { importers.insert(p_importer); }
+
+ virtual String get_importer_name() const;
+ virtual String get_visible_name() const;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual String get_save_extension() const;
+ virtual String get_resource_type() const;
+
+
+ virtual int get_preset_count() const;
+ virtual String get_preset_name(int p_idx) const;
+
+ virtual void get_import_options(List<ImportOption> *r_options,int p_preset=0) const;
+ virtual bool get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const;
+
+ void _make_external_resources(Node* p_node,const String& p_base_path, bool p_make_materials, bool p_make_meshes, Map<Ref<Material>, Ref<Material> > &p_materials, Map<Ref<Mesh>, Ref<Mesh> > &p_meshes);
+
+ Node* _fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map);
+
+ void _create_clips(Node *scene, const Array& p_clips,bool p_bake_all);
+ void _filter_anim_tracks(Ref<Animation> anim,Set<String> &keep);
+ void _filter_tracks(Node *scene, const String& p_text);
+ void _optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle);
+
+ virtual Error import(const String& p_source_file,const String& p_save_path,const Map<StringName,Variant>& p_options,List<String>* r_platform_variants,List<String>* r_gen_files=NULL);
+
+ ResourceImporterScene();
+};
+
+
+#endif // RESOURCEIMPORTERSCENE_H
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
new file mode 100644
index 0000000000..43c7ff79c6
--- /dev/null
+++ b/editor/import/resource_importer_texture.cpp
@@ -0,0 +1,393 @@
+#include "resource_importer_texture.h"
+#include "io/image_loader.h"
+#include "scene/resources/texture.h"
+#include "editor/editor_file_system.h"
+#include "io/config_file.h"
+
+
+void ResourceImporterTexture::_texture_reimport_srgb(const Ref<StreamTexture>& p_tex) {
+
+ singleton->mutex->lock();
+ StringName path = p_tex->get_path();
+
+ if (!singleton->make_flags.has(path)) {
+ singleton->make_flags[path]=0;
+ }
+
+ singleton->make_flags[path]|=MAKE_SRGB_FLAG;
+
+ print_line("requesting srgb for "+String(path));
+
+ singleton->mutex->unlock();
+
+}
+
+
+
+void ResourceImporterTexture::_texture_reimport_3d(const Ref<StreamTexture>& p_tex) {
+
+
+ singleton->mutex->lock();
+ StringName path = p_tex->get_path();
+
+ if (!singleton->make_flags.has(path)) {
+ singleton->make_flags[path]=0;
+ }
+
+ singleton->make_flags[path]|=MAKE_3D_FLAG;
+
+ print_line("requesting 3d for "+String(path));
+
+ singleton->mutex->unlock();
+
+
+}
+
+void ResourceImporterTexture::update_imports() {
+
+ if (EditorFileSystem::get_singleton()->is_scanning() || EditorFileSystem::get_singleton()->is_importing()) {
+ return; // do nothing for noe
+ }
+ mutex->lock();
+
+ if (make_flags.empty()) {
+ mutex->unlock();
+ return;
+ }
+
+ Vector<String> to_reimport;
+ for (Map<StringName,int>::Element *E=make_flags.front();E;E=E->next()) {
+
+ print_line("checking for reimport "+String(E->key()));
+
+
+ Ref<ConfigFile> cf;
+ cf.instance();
+ String src_path = String(E->key())+".import";
+
+ Error err = cf->load(src_path);
+ ERR_CONTINUE(err!=OK);
+
+ bool changed=false;
+ if (E->get()&MAKE_SRGB_FLAG && int(cf->get_value("params","flags/srgb"))==2) {
+ cf->set_value("params","flags/srgb",1);
+ changed=true;
+ }
+
+ if (E->get()&MAKE_3D_FLAG && bool(cf->get_value("params","detect_3d"))) {
+ cf->set_value("params","detect_3d",false);
+ cf->set_value("params","compress/mode",2);
+ cf->set_value("params","flags/repeat",true);
+ cf->set_value("params","flags/filter",true);
+ cf->set_value("params","flags/mipmaps",true);
+ changed=true;
+ }
+
+ if (changed) {
+ cf->save(src_path);
+ to_reimport.push_back(E->key());
+ }
+
+ }
+
+ make_flags.clear();
+
+ mutex->unlock();
+
+ if (to_reimport.size()) {
+ EditorFileSystem::get_singleton()->reimport_files(to_reimport);
+ }
+
+}
+
+
+
+String ResourceImporterTexture::get_importer_name() const {
+
+ return "texture";
+}
+
+String ResourceImporterTexture::get_visible_name() const{
+
+ return "Texture";
+}
+void ResourceImporterTexture::get_recognized_extensions(List<String> *p_extensions) const{
+
+ ImageLoader::get_recognized_extensions(p_extensions);
+}
+String ResourceImporterTexture::get_save_extension() const {
+ return "stex";
+}
+
+String ResourceImporterTexture::get_resource_type() const{
+
+ return "StreamTexture";
+}
+
+bool ResourceImporterTexture::get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const {
+
+ if (p_option=="compress/lossy_quality" && int(p_options["compress/mode"])!=COMPRESS_LOSSY)
+ return false;
+
+ return true;
+}
+
+int ResourceImporterTexture::get_preset_count() const {
+ return 4;
+}
+String ResourceImporterTexture::get_preset_name(int p_idx) const {
+
+ static const char* preset_names[]={
+ "2D, Detect 3D",
+ "2D",
+ "2D Pixel",
+ "3D"
+ };
+
+ return preset_names[p_idx];
+}
+
+
+void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,int p_preset) const {
+
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"compress/mode",PROPERTY_HINT_ENUM,"Lossless,Lossy,Video RAM,Uncompressed",PROPERTY_USAGE_DEFAULT|PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED),p_preset==PRESET_3D?2:0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::REAL,"compress/lossy_quality",PROPERTY_HINT_RANGE,"0,1,0.01"),0.7));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"flags/repeat",PROPERTY_HINT_ENUM,"Disabled,Enabled,Mirrored"),p_preset==PRESET_3D?1:0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"flags/filter"),p_preset==PRESET_2D_PIXEL?false:true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"flags/mipmaps"),p_preset==PRESET_3D?true:false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"flags/anisotropic"),false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"flags/srgb",PROPERTY_HINT_ENUM,"Disable,Enable,Detect"),2));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"process/fix_alpha_border"),p_preset!=PRESET_3D?true:false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"process/premult_alpha"),true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"stream"),false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"size_limit",PROPERTY_HINT_RANGE,"0,4096,1"),0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"detect_3d"),p_preset==PRESET_DETECT));
+
+}
+
+
+void ResourceImporterTexture::_save_stex(const Image& p_image, const String& p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb) {
+
+
+ FileAccess *f = FileAccess::open(p_to_path,FileAccess::WRITE);
+ f->store_8('G');
+ f->store_8('D');
+ f->store_8('S');
+ f->store_8('T'); //godot streamable texture
+
+ f->store_32(p_image.get_width());
+ f->store_32(p_image.get_height());
+ f->store_32(p_texture_flags);
+
+ uint32_t format=0;
+
+ if (p_streamable)
+ format|=StreamTexture::FORMAT_BIT_STREAM;
+ if (p_mipmaps || p_compress_mode==COMPRESS_VIDEO_RAM) //VRAM always uses mipmaps
+ format|=StreamTexture::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit
+ if (p_detect_3d)
+ format|=StreamTexture::FORMAT_BIT_DETECT_3D;
+ if (p_detect_srgb)
+ format|=StreamTexture::FORMAT_BIT_DETECT_SRGB;
+
+
+ switch (p_compress_mode) {
+ case COMPRESS_LOSSLESS: {
+
+ Image image = p_image;
+ if (p_mipmaps) {
+ image.generate_mipmaps();
+ } else {
+ image.clear_mipmaps();
+ }
+
+ int mmc = image.get_mipmap_count() + 1;
+
+ format|=StreamTexture::FORMAT_BIT_LOSSLESS;
+ f->store_32(format);
+ f->store_32(mmc);
+
+ for(int i=0;i<mmc;i++) {
+
+ if (i>0) {
+ image.shrink_x2();
+ }
+
+ PoolVector<uint8_t> data = Image::lossless_packer(image);
+ int data_len = data.size();
+ f->store_32(data_len);
+
+ PoolVector<uint8_t>::Read r= data.read();
+ f->store_buffer(r.ptr(),data_len);
+
+ }
+
+
+ } break;
+ case COMPRESS_LOSSY: {
+ Image image = p_image;
+ if (p_mipmaps) {
+ image.generate_mipmaps();
+ } else {
+ image.clear_mipmaps();
+ }
+
+ int mmc = image.get_mipmap_count() + 1;
+
+ format|=StreamTexture::FORMAT_BIT_LOSSY;
+ f->store_32(format);
+ f->store_32(mmc);
+
+ for(int i=0;i<mmc;i++) {
+
+ if (i>0) {
+ image.shrink_x2();
+ }
+
+ PoolVector<uint8_t> data = Image::lossy_packer(image,p_lossy_quality);
+ int data_len = data.size();
+ f->store_32(data_len);
+
+ PoolVector<uint8_t>::Read r = data.read();
+ f->store_buffer(r.ptr(),data_len);
+
+ }
+ } break;
+ case COMPRESS_VIDEO_RAM: {
+
+ Image image = p_image;
+ image.generate_mipmaps();
+ image.compress(p_vram_compression);
+
+ format |= image.get_format();
+
+ f->store_32(format);
+
+ PoolVector<uint8_t> data=image.get_data();
+ int dl = data.size();
+ PoolVector<uint8_t>::Read r = data.read();
+ f->store_buffer(r.ptr(),dl);
+
+ } break;
+ case COMPRESS_UNCOMPRESSED: {
+
+ Image image = p_image;
+ if (p_mipmaps) {
+ image.generate_mipmaps();
+ } else {
+ image.clear_mipmaps();
+ }
+
+ format |= image.get_format();
+ f->store_32(format);
+
+ PoolVector<uint8_t> data=image.get_data();
+ int dl = data.size();
+ PoolVector<uint8_t>::Read r = data.read();
+
+ f->store_buffer(r.ptr(),dl);
+
+ } break;
+ }
+
+ memdelete(f);
+}
+
+Error ResourceImporterTexture::import(const String& p_source_file, const String& p_save_path, const Map<StringName,Variant>& p_options, List<String>* r_platform_variants, List<String> *r_gen_files) {
+
+ int compress_mode = p_options["compress/mode"];
+ float lossy= p_options["compress/lossy_quality"];
+ int repeat= p_options["flags/repeat"];
+ bool filter= p_options["flags/filter"];
+ bool mipmaps= p_options["flags/mipmaps"];
+ bool anisotropic= p_options["flags/anisotropic"];
+ int srgb= p_options["flags/srgb"];
+ bool fix_alpha_border= p_options["process/fix_alpha_border"];
+ bool premult_alpha= p_options["process/premult_alpha"];
+ bool stream = p_options["stream"];
+ int size_limit = p_options["size_limit"];
+
+
+ Image image;
+ Error err = ImageLoader::load_image(p_source_file,&image);
+ if (err!=OK)
+ return err;
+
+
+ int tex_flags=0;
+ if (repeat>0)
+ tex_flags|=Texture::FLAG_REPEAT;
+ if (repeat==2)
+ tex_flags|=Texture::FLAG_MIRRORED_REPEAT;
+ if (filter)
+ tex_flags|=Texture::FLAG_FILTER;
+ if (mipmaps || compress_mode==COMPRESS_VIDEO_RAM)
+ tex_flags|=Texture::FLAG_MIPMAPS;
+ if (anisotropic)
+ tex_flags|=Texture::FLAG_ANISOTROPIC_FILTER;
+ if (srgb==1)
+ tex_flags|=Texture::FLAG_CONVERT_TO_LINEAR;
+
+ if (size_limit >0 && (image.get_width()>size_limit || image.get_height()>size_limit )) {
+ //limit size
+ if (image.get_width() >= image.get_height()) {
+ int new_width = size_limit;
+ int new_height = image.get_height() * new_width / image.get_width();
+
+ image.resize(new_width,new_height,Image::INTERPOLATE_CUBIC);
+ } else {
+
+ int new_height = size_limit;
+ int new_width = image.get_width() * new_height / image.get_height();
+
+ image.resize(new_width,new_height,Image::INTERPOLATE_CUBIC);
+ }
+ }
+
+ if (fix_alpha_border) {
+ image.fix_alpha_edges();
+ }
+
+ if (premult_alpha) {
+ image.premultiply_alpha();
+ }
+
+ bool detect_3d = p_options["detect_3d"];
+ bool detect_srgb = srgb==2;
+
+ if (compress_mode==COMPRESS_VIDEO_RAM) {
+ //must import in all formats
+ //Android, GLES 2.x
+ _save_stex(image,p_save_path+".etc.stex",compress_mode,lossy,Image::COMPRESS_ETC,mipmaps,tex_flags,stream,detect_3d,detect_srgb);
+ r_platform_variants->push_back("etc");
+ //_save_stex(image,p_save_path+".etc2.stex",compress_mode,lossy,Image::COMPRESS_ETC2,mipmaps,tex_flags,stream);
+ //r_platform_variants->push_back("etc2");
+ _save_stex(image,p_save_path+".s3tc.stex",compress_mode,lossy,Image::COMPRESS_S3TC,mipmaps,tex_flags,stream,detect_3d,detect_srgb);
+ r_platform_variants->push_back("s3tc");
+
+ } else {
+ //import normally
+ _save_stex(image,p_save_path+".stex",compress_mode,lossy,Image::COMPRESS_16BIT /*this is ignored */,mipmaps,tex_flags,stream,detect_3d,detect_srgb);
+ }
+
+ return OK;
+}
+
+ResourceImporterTexture *ResourceImporterTexture::singleton=NULL;
+
+ResourceImporterTexture::ResourceImporterTexture()
+{
+
+ singleton=this;
+ StreamTexture::request_3d_callback=_texture_reimport_3d;
+ StreamTexture::request_srgb_callback=_texture_reimport_srgb;
+ mutex = Mutex::create();
+}
+
+ResourceImporterTexture::~ResourceImporterTexture()
+{
+
+ memdelete(mutex);
+}
+
diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h
new file mode 100644
index 0000000000..4c795e132c
--- /dev/null
+++ b/editor/import/resource_importer_texture.h
@@ -0,0 +1,70 @@
+#ifndef RESOURCEIMPORTTEXTURE_H
+#define RESOURCEIMPORTTEXTURE_H
+
+#include "io/resource_import.h"
+class StreamTexture;
+
+class ResourceImporterTexture : public ResourceImporter {
+ GDCLASS(ResourceImporterTexture,ResourceImporter)
+
+
+
+protected:
+
+ enum {
+ MAKE_3D_FLAG=1,
+ MAKE_SRGB_FLAG=2
+ };
+
+ Mutex *mutex;
+ Map<StringName,int> make_flags;
+
+ static void _texture_reimport_srgb(const Ref<StreamTexture>& p_tex);
+ static void _texture_reimport_3d(const Ref<StreamTexture>& p_tex);
+
+
+
+
+ static ResourceImporterTexture *singleton;
+public:
+
+ static ResourceImporterTexture *get_singleton() { return singleton; }
+ virtual String get_importer_name() const;
+ virtual String get_visible_name() const;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual String get_save_extension() const;
+ virtual String get_resource_type() const;
+
+
+ enum Preset {
+ PRESET_DETECT,
+ PRESET_2D,
+ PRESET_2D_PIXEL,
+ PRESET_3D,
+ };
+
+ enum CompressMode {
+ COMPRESS_LOSSLESS,
+ COMPRESS_LOSSY,
+ COMPRESS_VIDEO_RAM,
+ COMPRESS_UNCOMPRESSED
+ };
+
+ virtual int get_preset_count() const;
+ virtual String get_preset_name(int p_idx) const;
+
+ virtual void get_import_options(List<ImportOption> *r_options,int p_preset=0) const;
+ virtual bool get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const;
+
+ void _save_stex(const Image& p_image, const String& p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable,bool p_detect_3d,bool p_detect_srgb);
+
+ virtual Error import(const String& p_source_file,const String& p_save_path,const Map<StringName,Variant>& p_options,List<String>* r_platform_variants,List<String>* r_gen_files=NULL);
+
+
+ void update_imports();
+
+ ResourceImporterTexture();
+ ~ResourceImporterTexture();
+};
+
+#endif // RESOURCEIMPORTTEXTURE_H
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
new file mode 100644
index 0000000000..ee53b740ca
--- /dev/null
+++ b/editor/import/resource_importer_wav.cpp
@@ -0,0 +1,619 @@
+#include "resource_importer_wav.h"
+
+#include "scene/resources/audio_stream_sample.h"
+#include "os/file_access.h"
+#include "io/marshalls.h"
+#include "io/resource_saver.h"
+
+String ResourceImporterWAV::get_importer_name() const {
+
+ return "wav";
+}
+
+String ResourceImporterWAV::get_visible_name() const{
+
+ return "Microsoft WAV";
+}
+void ResourceImporterWAV::get_recognized_extensions(List<String> *p_extensions) const{
+
+ p_extensions->push_back("wav");
+}
+String ResourceImporterWAV::get_save_extension() const {
+ return "smp";
+}
+
+String ResourceImporterWAV::get_resource_type() const{
+
+ return "AudioStreamSample";
+}
+
+bool ResourceImporterWAV::get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const {
+
+ return true;
+}
+
+int ResourceImporterWAV::get_preset_count() const {
+ return 0;
+}
+String ResourceImporterWAV::get_preset_name(int p_idx) const {
+
+ return String();
+}
+
+
+void ResourceImporterWAV::get_import_options(List<ImportOption> *r_options,int p_preset) const {
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"force/8_bit"),false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"force/mono"),false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"force/max_rate"),false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::REAL,"force/max_rate_hz",PROPERTY_HINT_EXP_RANGE,"11025,192000,1"),44100));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"edit/trim"),true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"edit/normalize"),true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL,"edit/loop"),false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT,"compress/mode",PROPERTY_HINT_ENUM,"Disabled,RAM (Ima-ADPCM)"),0));
+
+}
+
+
+Error ResourceImporterWAV::import(const String& p_source_file, const String& p_save_path, const Map<StringName,Variant>& p_options, List<String>* r_platform_variants, List<String> *r_gen_files) {
+
+ /* STEP 1, READ WAVE FILE */
+
+ Error err;
+ FileAccess *file=FileAccess::open(p_source_file, FileAccess::READ,&err);
+
+ ERR_FAIL_COND_V( err!=OK, ERR_CANT_OPEN );
+
+ /* CHECK RIFF */
+ char riff[5];
+ riff[4]=0;
+ file->get_buffer((uint8_t*)&riff,4); //RIFF
+
+ if (riff[0]!='R' || riff[1]!='I' || riff[2]!='F' || riff[3]!='F') {
+
+ file->close();
+ memdelete(file);
+ ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
+ }
+
+
+ /* GET FILESIZE */
+ uint32_t filesize=file->get_32();
+
+ /* CHECK WAVE */
+
+ char wave[4];
+
+ file->get_buffer((uint8_t*)&wave,4); //RIFF
+
+ if (wave[0]!='W' || wave[1]!='A' || wave[2]!='V' || wave[3]!='E') {
+
+
+ file->close();
+ memdelete(file);
+ ERR_EXPLAIN("Not a WAV file (no WAVE RIFF Header)")
+ ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
+ }
+
+ int format_bits=0;
+ int format_channels=0;
+
+ AudioStreamSample::LoopMode loop=AudioStreamSample::LOOP_DISABLED;
+ bool format_found=false;
+ bool data_found=false;
+ int format_freq=0;
+ int loop_begin=0;
+ int loop_end=0;
+ int frames;
+
+ Vector<float> data;
+
+ while (!file->eof_reached()) {
+
+
+ /* chunk */
+ char chunkID[4];
+ file->get_buffer((uint8_t*)&chunkID,4); //RIFF
+
+ /* chunk size */
+ uint32_t chunksize=file->get_32();
+ uint32_t file_pos=file->get_pos(); //save file pos, so we can skip to next chunk safely
+
+ if (file->eof_reached()) {
+
+ //ERR_PRINT("EOF REACH");
+ break;
+ }
+
+ if (chunkID[0]=='f' && chunkID[1]=='m' && chunkID[2]=='t' && chunkID[3]==' ' && !format_found) {
+ /* IS FORMAT CHUNK */
+
+ uint16_t compression_code=file->get_16();
+
+
+ if (compression_code!=1) {
+ ERR_PRINT("Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead.");
+ break;
+ }
+
+ format_channels=file->get_16();
+ if (format_channels!=1 && format_channels !=2) {
+
+ ERR_PRINT("Format not supported for WAVE file (not stereo or mono)");
+ break;
+
+ }
+
+ format_freq=file->get_32(); //sampling rate
+
+ file->get_32(); // average bits/second (unused)
+ file->get_16(); // block align (unused)
+ format_bits=file->get_16(); // bits per sample
+
+ if (format_bits%8) {
+
+ ERR_PRINT("Strange number of bits in sample (not 8,16,24,32)");
+ break;
+ }
+
+ /* Dont need anything else, continue */
+ format_found=true;
+ }
+
+
+ if (chunkID[0]=='d' && chunkID[1]=='a' && chunkID[2]=='t' && chunkID[3]=='a' && !data_found) {
+ /* IS FORMAT CHUNK */
+ data_found=true;
+
+ if (!format_found) {
+ ERR_PRINT("'data' chunk before 'format' chunk found.");
+ break;
+
+ }
+
+ frames=chunksize;
+
+ frames/=format_channels;
+ frames/=(format_bits>>3);
+
+ /*print_line("chunksize: "+itos(chunksize));
+ print_line("channels: "+itos(format_channels));
+ print_line("bits: "+itos(format_bits));
+*/
+
+ int len=frames;
+ if (format_channels==2)
+ len*=2;
+ if (format_bits>8)
+ len*=2;
+
+
+ data.resize(frames*format_channels);
+
+ for (int i=0;i<frames;i++) {
+
+
+ for (int c=0;c<format_channels;c++) {
+
+
+ if (format_bits==8) {
+ // 8 bit samples are UNSIGNED
+
+ uint8_t s = file->get_8();
+ s-=128;
+ int8_t *sp=(int8_t*)&s;
+
+ data[i*format_channels+c]=float(*sp)/128.0;
+
+ } else {
+ //16+ bits samples are SIGNED
+ // if sample is > 16 bits, just read extra bytes
+
+ uint32_t s=0;
+ for (int b=0;b<(format_bits>>3);b++) {
+
+ s|=((uint32_t)file->get_8())<<(b*8);
+ }
+ s<<=(32-format_bits);
+ int32_t ss=s;
+
+
+ data[i*format_channels+c]=(ss>>16)/32768.0;
+ }
+ }
+
+ }
+
+
+
+ if (file->eof_reached()) {
+ file->close();
+ memdelete(file);
+ ERR_EXPLAIN("Premature end of file.");
+ ERR_FAIL_V(ERR_FILE_CORRUPT);
+ }
+ }
+
+ if (chunkID[0]=='s' && chunkID[1]=='m' && chunkID[2]=='p' && chunkID[3]=='l') {
+ //loop point info!
+
+ for(int i=0;i<10;i++)
+ file->get_32(); // i wish to know why should i do this... no doc!
+
+ loop=file->get_32()?AudioStreamSample::LOOP_PING_PONG:AudioStreamSample::LOOP_FORWARD;
+ loop_begin=file->get_32();
+ loop_end=file->get_32();
+
+ }
+ file->seek( file_pos+chunksize );
+ }
+
+ file->close();
+ memdelete(file);
+
+ // STEP 2, APPLY CONVERSIONS
+
+
+ bool is16=format_bits!=8;
+ int rate=format_freq;
+
+ print_line("Input Sample: ");
+ print_line("\tframes: "+itos(frames));
+ print_line("\tformat_channels: "+itos(format_channels));
+ print_line("\t16bits: "+itos(is16));
+ print_line("\trate: "+itos(rate));
+ print_line("\tloop: "+itos(loop));
+ print_line("\tloop begin: "+itos(loop_begin));
+ print_line("\tloop end: "+itos(loop_end));
+
+
+ //apply frequency limit
+
+ bool limit_rate = p_options["force/max_rate"];
+ int limit_rate_hz = p_options["force/max_rate_hz"];
+ if (limit_rate && rate > limit_rate_hz) {
+ //resampleeee!!!
+ int new_data_frames = frames * limit_rate_hz / rate;
+ Vector<float> new_data;
+ new_data.resize( new_data_frames * format_channels );
+ for(int c=0;c<format_channels;c++) {
+
+ for(int i=0;i<new_data_frames;i++) {
+
+ //simple cubic interpolation should be enough.
+ float pos = float(i) * frames / new_data_frames;
+ float mu = pos-Math::floor(pos);
+ int ipos = int(Math::floor(pos));
+
+ float y0=data[MAX(0,ipos-1)*format_channels+c];
+ float y1=data[ipos*format_channels+c];
+ float y2=data[MIN(frames-1,ipos+1)*format_channels+c];
+ float y3=data[MIN(frames-1,ipos+2)*format_channels+c];
+
+ float mu2 = mu*mu;
+ float a0 = y3 - y2 - y0 + y1;
+ float a1 = y0 - y1 - a0;
+ float a2 = y2 - y0;
+ float a3 = y1;
+
+ float res=(a0*mu*mu2+a1*mu2+a2*mu+a3);
+
+ new_data[i*format_channels+c]=res;
+ }
+ }
+
+ if (loop) {
+
+ loop_begin=loop_begin*new_data_frames/frames;
+ loop_end=loop_end*new_data_frames/frames;
+ }
+ data=new_data;
+ rate=limit_rate_hz;
+ frames=new_data_frames;
+ }
+
+
+ bool normalize = p_options["edit/normalize"];
+
+ if (normalize) {
+
+ float max=0;
+ for(int i=0;i<data.size();i++) {
+
+ float amp = Math::abs(data[i]);
+ if (amp>max)
+ max=amp;
+ }
+
+ if (max>0) {
+
+ float mult=1.0/max;
+ for(int i=0;i<data.size();i++) {
+
+ data[i]*=mult;
+ }
+
+ }
+ }
+
+ bool trim = p_options["edit/trim"];
+
+ if (trim && !loop) {
+
+ int first=0;
+ int last=(frames*format_channels)-1;
+ bool found=false;
+ float limit = Math::db2linear((float)-30);
+ for(int i=0;i<data.size();i++) {
+ float amp = Math::abs(data[i]);
+
+ if (!found && amp > limit) {
+ first=i;
+ found=true;
+ }
+
+ if (found && amp > limit) {
+ last=i;
+ }
+ }
+
+ first/=format_channels;
+ last/=format_channels;
+
+ if (first<last) {
+
+ Vector<float> new_data;
+ new_data.resize((last-first+1)*format_channels);
+ for(int i=first*format_channels;i<=last*format_channels;i++) {
+ new_data[i-first*format_channels]=data[i];
+ }
+
+ data=new_data;
+ frames=data.size()/format_channels;
+ }
+
+ }
+
+ bool make_loop = p_options["edit/loop"];
+
+ if (make_loop && !loop) {
+
+ loop=AudioStreamSample::LOOP_FORWARD;
+ loop_begin=0;
+ loop_end=frames;
+ }
+
+ int compression = p_options["compress/mode"];
+ bool force_mono = p_options["force/mono"];
+
+
+ if (force_mono && format_channels==2) {
+
+ Vector<float> new_data;
+ new_data.resize(data.size()/2);
+ for(int i=0;i<frames;i++) {
+ new_data[i]=(data[i*2+0]+data[i*2+1])/2.0;
+ }
+
+ data=new_data;
+ format_channels=1;
+ }
+
+ bool force_8_bit = p_options["force/8_bit"];
+ if (force_8_bit) {
+
+ is16=false;
+ }
+
+
+ PoolVector<uint8_t> dst_data;
+ AudioStreamSample::Format dst_format;
+
+ if ( compression == 1) {
+
+ dst_format=AudioStreamSample::FORMAT_IMA_ADPCM;
+ if (format_channels==1) {
+ _compress_ima_adpcm(data,dst_data);
+ } else {
+
+ //byte interleave
+ Vector<float> left;
+ Vector<float> right;
+
+ int tframes = data.size()/2;
+ left.resize(tframes);
+ right.resize(tframes);
+
+ for(int i=0;i<tframes;i++) {
+ left[i]=data[i*2+0];
+ right[i]=data[i*2+1];
+ }
+
+ PoolVector<uint8_t> bleft;
+ PoolVector<uint8_t> bright;
+
+ _compress_ima_adpcm(left,bleft);
+ _compress_ima_adpcm(right,bright);
+
+ int dl = bleft.size();
+ dst_data.resize( dl *2 );
+
+ PoolVector<uint8_t>::Write w=dst_data.write();
+ PoolVector<uint8_t>::Read rl=bleft.read();
+ PoolVector<uint8_t>::Read rr=bright.read();
+
+ for(int i=0;i<dl;i++) {
+ w[i*2+0]=rl[i];
+ w[i*2+1]=rr[i];
+ }
+ }
+
+ //print_line("compressing ima-adpcm, resulting buffersize is "+itos(dst_data.size())+" from "+itos(data.size()));
+
+ } else {
+
+ dst_format=is16?AudioStreamSample::FORMAT_16_BITS:AudioStreamSample::FORMAT_8_BITS;
+ dst_data.resize( data.size() * (is16?2:1));
+ {
+ PoolVector<uint8_t>::Write w = dst_data.write();
+
+ int ds=data.size();
+ for(int i=0;i<ds;i++) {
+
+ if (is16) {
+ int16_t v = CLAMP(data[i]*32768,-32768,32767);
+ encode_uint16(v,&w[i*2]);
+ } else {
+ int8_t v = CLAMP(data[i]*128,-128,127);
+ w[i]=v;
+ }
+ }
+ }
+ }
+
+
+ Ref<AudioStreamSample> sample;
+ sample.instance();
+ sample->set_data(dst_data);
+ sample->set_format(dst_format);
+ sample->set_mix_rate(rate);
+ sample->set_loop_mode(loop);
+ sample->set_loop_begin(loop_begin);
+ sample->set_loop_end(loop_end);
+ sample->set_stereo(format_channels==2);
+
+ ResourceSaver::save(p_save_path+".smp",sample);
+
+
+ return OK;
+
+}
+
+void ResourceImporterWAV::_compress_ima_adpcm(const Vector<float>& p_data,PoolVector<uint8_t>& dst_data) {
+
+
+ /*p_sample_data->data = (void*)malloc(len);
+ xm_s8 *dataptr=(xm_s8*)p_sample_data->data;*/
+
+ static const int16_t _ima_adpcm_step_table[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ static const int8_t _ima_adpcm_index_table[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+
+ int datalen = p_data.size();
+ int datamax=datalen;
+ if (datalen&1)
+ datalen++;
+
+ dst_data.resize(datalen/2+4);
+ PoolVector<uint8_t>::Write w = dst_data.write();
+
+
+ int i,step_idx=0,prev=0;
+ uint8_t *out = w.ptr();
+ //int16_t xm_prev=0;
+ const float *in=p_data.ptr();
+
+
+ /* initial value is zero */
+ *(out++) =0;
+ *(out++) =0;
+ /* Table index initial value */
+ *(out++) =0;
+ /* unused */
+ *(out++) =0;
+
+ for (i=0;i<datalen;i++) {
+ int step,diff,vpdiff,mask;
+ uint8_t nibble;
+ int16_t xm_sample;
+
+ if (i>=datamax)
+ xm_sample=0;
+ else {
+
+
+ xm_sample=CLAMP(in[i]*32767.0,-32768,32767);
+ /*
+ if (xm_sample==32767 || xm_sample==-32768)
+ printf("clippy!\n",xm_sample);
+ */
+ }
+
+ //xm_sample=xm_sample+xm_prev;
+ //xm_prev=xm_sample;
+
+ diff = (int)xm_sample - prev ;
+
+ nibble=0 ;
+ step = _ima_adpcm_step_table[ step_idx ];
+ vpdiff = step >> 3 ;
+ if (diff < 0) {
+ nibble=8;
+ diff=-diff ;
+ }
+ mask = 4 ;
+ while (mask) {
+
+ if (diff >= step) {
+
+ nibble |= mask;
+ diff -= step;
+ vpdiff += step;
+ }
+
+ step >>= 1 ;
+ mask >>= 1 ;
+ };
+
+ if (nibble&8)
+ prev-=vpdiff ;
+ else
+ prev+=vpdiff ;
+
+ if (prev > 32767) {
+ //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev);
+ prev=32767;
+ } else if (prev < -32768) {
+ //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev);
+ prev = -32768 ;
+ }
+
+ step_idx += _ima_adpcm_index_table[nibble];
+ if (step_idx< 0)
+ step_idx= 0 ;
+ else if (step_idx> 88)
+ step_idx= 88 ;
+
+
+ if (i&1) {
+ *out|=nibble<<4;
+ out++;
+ } else {
+ *out=nibble;
+ }
+ /*dataptr[i]=prev>>8;*/
+ }
+
+
+
+
+}
+
+ResourceImporterWAV::ResourceImporterWAV()
+{
+
+}
diff --git a/editor/import/resource_importer_wav.h b/editor/import/resource_importer_wav.h
new file mode 100644
index 0000000000..9f1bd57da7
--- /dev/null
+++ b/editor/import/resource_importer_wav.h
@@ -0,0 +1,30 @@
+#ifndef RESOURCEIMPORTWAV_H
+#define RESOURCEIMPORTWAV_H
+
+
+#include "io/resource_import.h"
+
+class ResourceImporterWAV : public ResourceImporter {
+ GDCLASS(ResourceImporterWAV,ResourceImporter)
+public:
+ virtual String get_importer_name() const;
+ virtual String get_visible_name() const;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual String get_save_extension() const;
+ virtual String get_resource_type() const;
+
+
+ virtual int get_preset_count() const;
+ virtual String get_preset_name(int p_idx) const;
+
+ virtual void get_import_options(List<ImportOption> *r_options,int p_preset=0) const;
+ virtual bool get_option_visibility(const String& p_option,const Map<StringName,Variant>& p_options) const;
+
+ void _compress_ima_adpcm(const Vector<float>& p_data,PoolVector<uint8_t>& dst_data);
+
+ virtual Error import(const String& p_source_file,const String& p_save_path,const Map<StringName,Variant>& p_options,List<String>* r_platform_variants,List<String>* r_gen_files=NULL);
+
+ ResourceImporterWAV();
+};
+
+#endif // RESOURCEIMPORTWAV_H