diff options
author | Rémi Verschelde <rverschelde@gmail.com> | 2017-03-05 14:21:25 +0100 |
---|---|---|
committer | Rémi Verschelde <rverschelde@gmail.com> | 2017-03-05 14:21:25 +0100 |
commit | 49c065d29ca07040c3fd810026121164ad86b247 (patch) | |
tree | 285176e0c80a41c22c3e8f171024472cfdc7d765 /editor/import | |
parent | 532f6d4b431f940432e82b7fc7826652b7a4520d (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/SCsub | 5 | ||||
-rw-r--r-- | editor/import/editor_import_collada.cpp | 2508 | ||||
-rw-r--r-- | editor/import/editor_import_collada.h | 51 | ||||
-rw-r--r-- | editor/import/resource_importer_csv_translation.cpp | 126 | ||||
-rw-r--r-- | editor/import/resource_importer_csv_translation.h | 27 | ||||
-rw-r--r-- | editor/import/resource_importer_obj.cpp | 231 | ||||
-rw-r--r-- | editor/import/resource_importer_obj.h | 28 | ||||
-rw-r--r-- | editor/import/resource_importer_scene.cpp | 1328 | ||||
-rw-r--r-- | editor/import/resource_importer_scene.h | 93 | ||||
-rw-r--r-- | editor/import/resource_importer_texture.cpp | 393 | ||||
-rw-r--r-- | editor/import/resource_importer_texture.h | 70 | ||||
-rw-r--r-- | editor/import/resource_importer_wav.cpp | 619 | ||||
-rw-r--r-- | editor/import/resource_importer_wav.h | 30 |
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 |