diff options
Diffstat (limited to 'tools/editor/io_plugins/editor_import_collada.cpp')
| -rw-r--r-- | tools/editor/io_plugins/editor_import_collada.cpp | 2200 |
1 files changed, 2200 insertions, 0 deletions
diff --git a/tools/editor/io_plugins/editor_import_collada.cpp b/tools/editor/io_plugins/editor_import_collada.cpp new file mode 100644 index 000000000..ffb68dff8 --- /dev/null +++ b/tools/editor/io_plugins/editor_import_collada.cpp @@ -0,0 +1,2200 @@ +/*************************************************************************/ +/* editor_import_collada.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "editor_import_collada.h" +#include "collada/collada.h" +#include "scene/3d/spatial.h" +#include "scene/3d/skeleton.h" +#include "scene/3d/path.h" +#include "scene/3d/camera.h" +#include "scene/3d/light.h" +#include "scene/animation/animation_player.h" +#include "scene/3d/mesh_instance.h" +#include "scene/resources/animation.h" +#include "scene/resources/packed_scene.h" +#include "os/os.h" +#include "tools/editor/editor_node.h" + + +struct ColladaImport { + + Collada collada; + Spatial *scene; + + Vector<Ref<Animation> > animations; + + struct NodeMap { + //String path; + Spatial *node; + int bone; + List<int> anim_tracks; + + NodeMap() { node=NULL; bone=-1; } + }; + + bool found_ambient; + Color ambient; + bool found_directional; + bool force_make_tangents; + + + + Map<String,NodeMap> node_map; //map from collada node to engine node + Map<String, Ref<Mesh> > mesh_cache; + Map<String, Ref<Curve3D> > curve_cache; + Map<String, Ref<Material> > material_cache; + + Map< Skeleton*, Map< String, int> > skeleton_bone_map; + + Set<String> valid_animated_nodes; + Vector<int> valid_animated_properties; + Map<String,bool> bones_with_animation; + + Error _populate_skeleton(Skeleton *p_skeleton,Collada::Node *p_node, int &r_bone, int p_parent); + Error _create_scene(Collada::Node *p_node, Spatial *p_parent); + Error _create_resources(Collada::Node *p_node); + Error _create_material(const String& p_material); + Error _create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_data, const Collada::MorphControllerData *p_morph_data); + Error load(const String& p_path, int p_flags, bool p_force_make_tangents=false); + void _fix_param_animation_tracks(); + void create_animation(int p_clip=-1); + void create_animations(); + + Set<String> tracks_in_clips; + Vector<String> missing_textures; + + void _pre_process_lights(Collada::Node *p_node); + + ColladaImport() { + + found_ambient=false; + found_directional=false; + force_make_tangents=false; + + } +}; + + +Error ColladaImport::_populate_skeleton(Skeleton *p_skeleton,Collada::Node *p_node, int &r_bone, int p_parent) { + + + if (p_node->type!=Collada::Node::TYPE_JOINT) + return OK; + + Collada::NodeJoint *joint = static_cast<Collada::NodeJoint*>(p_node); + + p_skeleton->add_bone(p_node->name); + if (p_parent>=0) + p_skeleton->set_bone_parent(r_bone,p_parent); + + NodeMap nm; + nm.node=p_skeleton; + nm.bone = r_bone; + node_map[p_node->id]=nm; + + skeleton_bone_map[p_skeleton][joint->sid]=r_bone; + + if (collada.state.bone_rest_map.has(joint->sid)) { + + p_skeleton->set_bone_rest(r_bone,collada.fix_transform(collada.state.bone_rest_map[joint->sid])); + //should map this bone to something for animation? + } else { + print_line("no rest: "+joint->sid); + WARN_PRINT("Joint has no rest.."); + } + + + int id = r_bone++; + for(int i=0;i<p_node->children.size();i++) { + + Error err = _populate_skeleton(p_skeleton,p_node->children[i],r_bone,id); + if (err) + return err; + } + + return OK; +} + + +void ColladaImport::_pre_process_lights(Collada::Node *p_node) { + + + if (p_node->type==Collada::Node::TYPE_LIGHT) { + + + Collada::NodeLight *light=static_cast<Collada::NodeLight*>(p_node); + if (collada.state.light_data_map.has(light->light)) { + + Collada::LightData &ld = collada.state.light_data_map[light->light]; + if (ld.mode==Collada::LightData::MODE_AMBIENT) { + found_ambient=true; + ambient=ld.color; + } + if (ld.mode==Collada::LightData::MODE_DIRECTIONAL) { + found_directional=true; + } + } + + } + + + for(int i=0;i<p_node->children.size();i++) + _pre_process_lights(p_node->children[i]); +} + +Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { + + Spatial * node=NULL; + + switch(p_node->type) { + + case Collada::Node::TYPE_NODE: { + + node = memnew( Spatial ); + } break; + case Collada::Node::TYPE_JOINT: { + + return OK; // do nothing + } break; + case Collada::Node::TYPE_LIGHT: { + + //node = memnew( Light) + Collada::NodeLight *light = static_cast<Collada::NodeLight*>(p_node); + if (collada.state.light_data_map.has(light->light)) { + + Collada::LightData &ld = collada.state.light_data_map[light->light]; + + if (ld.mode==Collada::LightData::MODE_AMBIENT) { + + if (found_directional) + return OK; //do nothing not needed + + if (!bool(GLOBAL_DEF("collada/use_ambient",false))) + return OK; + //well, it's an ambient light.. + Light *l = memnew( DirectionalLight ); + l->set_color(Light::COLOR_AMBIENT,ld.color); + l->set_color(Light::COLOR_DIFFUSE,Color(0,0,0)); + l->set_color(Light::COLOR_SPECULAR,Color(0,0,0)); + node = l; + + } else if (ld.mode==Collada::LightData::MODE_DIRECTIONAL) { + + //well, it's an ambient light.. + Light *l = memnew( DirectionalLight ); + if (found_ambient) //use it here + l->set_color(Light::COLOR_AMBIENT,ambient); + + l->set_color(Light::COLOR_DIFFUSE,ld.color); + l->set_color(Light::COLOR_SPECULAR,Color(1,1,1)); + node = l; + } else { + + Light *l; + + if (ld.mode==Collada::LightData::MODE_OMNI) + l=memnew( OmniLight ); + else { + l=memnew( SpotLight ); + l->set_parameter(Light::PARAM_SPOT_ANGLE,ld.spot_angle); + l->set_parameter(Light::PARAM_SPOT_ATTENUATION,ld.spot_exp); + } + + // + l->set_color(Light::COLOR_DIFFUSE,ld.color); + l->set_color(Light::COLOR_SPECULAR,Color(1,1,1)); + l->approximate_opengl_attenuation(ld.constant_att,ld.linear_att,ld.quad_att); + node=l; + } + + } else { + + node = memnew( Spatial ); + } + } break; + case Collada::Node::TYPE_CAMERA: { + + Collada::NodeCamera *cam = static_cast<Collada::NodeCamera*>(p_node); + Camera *camera = memnew( Camera ); + + if (collada.state.camera_data_map.has(cam->camera)) { + + const Collada::CameraData &cd = collada.state.camera_data_map[cam->camera]; + + switch(cd.mode) { + + case Collada::CameraData::MODE_ORTHOGONAL: { + + if (cd.orthogonal.x_mag) { + + camera->set_orthogonal(cd.orthogonal.x_mag,cd.z_near,cd.z_far); + + } else if (!cd.orthogonal.x_mag && cd.orthogonal.y_mag) { + + camera->set_orthogonal(cd.orthogonal.y_mag * cd.aspect,cd.z_near,cd.z_far); + } + + } break; + case Collada::CameraData::MODE_PERSPECTIVE: { + + if (cd.perspective.y_fov) { + + camera->set_perspective(cd.perspective.y_fov,cd.z_near,cd.z_far); + + } else if (!cd.perspective.y_fov && cd.perspective.x_fov) { + + camera->set_perspective(cd.perspective.x_fov / cd.aspect,cd.z_near,cd.z_far); + } + + } break; + } + + } + + node=camera; + + } break; + case Collada::Node::TYPE_GEOMETRY: { + + Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(p_node); + + if (collada.state.curve_data_map.has(ng->source)) { + + node = memnew( Path ); + } else { + //mesh since nothing else + node = memnew( MeshInstance ); + } + } break; + case Collada::Node::TYPE_SKELETON: { + + Skeleton *sk = memnew( Skeleton ); + int bone = 0; + + for(int i=0;i<p_node->children.size();i++) { + + _populate_skeleton(sk,p_node->children[i],bone,-1); + } + sk->localize_rests(); //after creating skeleton, rests must be localized...! + + node=sk; + } break; + + } + + if (p_node->name!="") + node->set_name(p_node->name); + NodeMap nm; + nm.node=node; + node_map[p_node->id]=nm; + Transform xf = p_node->default_transform; + + xf = collada.fix_transform( xf ) * p_node->post_transform; + node->set_transform(xf); + p_parent->add_child(node); + node->set_owner(scene); + + for(int i=0;i<p_node->children.size();i++) { + + Error err = _create_scene(p_node->children[i],node); + if (err) + return err; + } + return OK; +} + + +Error ColladaImport::_create_material(const String& p_target) { + + ERR_FAIL_COND_V(material_cache.has(p_target),ERR_ALREADY_EXISTS); + ERR_FAIL_COND_V(!collada.state.material_map.has(p_target),ERR_INVALID_PARAMETER); + Collada::Material &src_mat=collada.state.material_map[p_target]; + ERR_FAIL_COND_V(!collada.state.effect_map.has(src_mat.instance_effect),ERR_INVALID_PARAMETER); + Collada::Effect &effect=collada.state.effect_map[src_mat.instance_effect]; + + Ref<FixedMaterial> material= memnew( FixedMaterial ); + + // DIFFUSE + + if (effect.diffuse.texture!="") { + + String texfile = effect.get_texture_path(effect.diffuse.texture,collada); + if (texfile!="") { + + Ref<Texture> texture = ResourceLoader::load(texfile,"Texture"); + if (texture.is_valid()) { + + material->set_texture(FixedMaterial::PARAM_DIFFUSE,texture); + material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,1)); + } else { + missing_textures.push_back(texfile.get_file()); + } + } + } else { + material->set_parameter(FixedMaterial::PARAM_DIFFUSE,effect.diffuse.color); + } + + // SPECULAR + + if (effect.specular.texture!="") { + + String texfile = effect.get_texture_path(effect.specular.texture,collada); + if (texfile!="") { + + Ref<Texture> texture = ResourceLoader::load(texfile,"Texture"); + if (texture.is_valid()) { + + material->set_texture(FixedMaterial::PARAM_SPECULAR,texture); + material->set_parameter(FixedMaterial::PARAM_SPECULAR,Color(1,1,1,1)); + } else { + missing_textures.push_back(texfile.get_file()); + } + + } + } else { + material->set_parameter(FixedMaterial::PARAM_SPECULAR,effect.specular.color); + } + + // EMISSION + + if (effect.emission.texture!="") { + + String texfile = effect.get_texture_path(effect.emission.texture,collada); + if (texfile!="") { + + Ref<Texture> texture = ResourceLoader::load(texfile,"Texture"); + if (texture.is_valid()) { + + material->set_texture(FixedMaterial::PARAM_EMISSION,texture); + material->set_parameter(FixedMaterial::PARAM_EMISSION,Color(1,1,1,1)); + }else { + missing_textures.push_back(texfile.get_file()); + } + + } + } else { + material->set_parameter(FixedMaterial::PARAM_EMISSION,effect.emission.color); + } + + // NORMAL + + if (effect.bump.texture!="") { + + String texfile = effect.get_texture_path(effect.bump.texture,collada); + if (texfile!="") { + + Ref<Texture> texture = ResourceLoader::load(texfile,"Texture"); + if (texture.is_valid()) { + + material->set_texture(FixedMaterial::PARAM_NORMAL,texture); + }else { + missing_textures.push_back(texfile.get_file()); + } + + } + } + + + material->set_parameter(FixedMaterial::PARAM_SPECULAR_EXP,effect.shininess); + material->set_flag(Material::FLAG_DOUBLE_SIDED,effect.double_sided); + + material_cache[p_target]=material; + return OK; +} + + +static void _generate_normals(const DVector<int>& p_indices,const DVector<Vector3>& p_vertices,DVector<Vector3>&r_normals) { + + + r_normals.resize(p_vertices.size()); + DVector<Vector3>::Write narrayw = r_normals.write(); + + int iacount=p_indices.size()/3; + DVector<int>::Read index_arrayr = p_indices.read(); + DVector<Vector3>::Read vertex_arrayr = p_vertices.read(); + + for(int idx=0;idx<iacount;idx++) { + + Vector3 v[3]={ + vertex_arrayr[index_arrayr[idx*3+0]], + vertex_arrayr[index_arrayr[idx*3+1]], + vertex_arrayr[index_arrayr[idx*3+2]] + }; + + Vector3 normal = Plane(v[0],v[1],v[2]).normal; + + narrayw[index_arrayr[idx*3+0]]+=normal; + narrayw[index_arrayr[idx*3+1]]+=normal; + narrayw[index_arrayr[idx*3+2]]+=normal; + } + + int vlen=p_vertices.size(); + + for(int idx=0;idx<vlen;idx++) { + narrayw[idx].normalize(); + } + +} + + +static void _generate_tangents_and_binormals(const DVector<int>& p_indices,const DVector<Vector3>& p_vertices,const DVector<Vector3>& p_uvs,const DVector<Vector3>& p_normals,DVector<real_t>&r_tangents) { + + int vlen=p_vertices.size(); + + Vector<Vector3> tangents; + tangents.resize(vlen); + Vector<Vector3> binormals; + binormals.resize(vlen); + + + int iacount=p_indices.size()/3; + + DVector<int>::Read index_arrayr = p_indices.read(); + DVector<Vector3>::Read vertex_arrayr = p_vertices.read(); + DVector<Vector3>::Read narrayr = p_normals.read(); + DVector<Vector3>::Read uvarrayr = p_uvs.read(); + + + for(int idx=0;idx<iacount;idx++) { + + + Vector3 v1 = vertex_arrayr[ index_arrayr[idx*3+0] ]; + Vector3 v2 = vertex_arrayr[ index_arrayr[idx*3+1] ]; + Vector3 v3 = vertex_arrayr[ index_arrayr[idx*3+2] ]; + + Vector3 w1 = uvarrayr[ index_arrayr[idx*3+0] ]; + Vector3 w2 = uvarrayr[ index_arrayr[idx*3+1] ]; + Vector3 w3 = uvarrayr[ index_arrayr[idx*3+2] ]; + + real_t x1 = v2.x - v1.x; + real_t x2 = v3.x - v1.x; + real_t y1 = v2.y - v1.y; + real_t y2 = v3.y - v1.y; + real_t z1 = v2.z - v1.z; + real_t z2 = v3.z - v1.z; + + real_t s1 = w2.x - w1.x; + real_t s2 = w3.x - w1.x; + real_t t1 = w2.y - w1.y; + real_t t2 = w3.y - w1.y; + + real_t r = (s1 * t2 - s2 * t1); + + Vector3 tangent; + Vector3 binormal; + + if (r==0) { + + binormal=Vector3(); + tangent=Vector3(); + } else { + tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, + (t2 * z1 - t1 * z2) * r); + binormal = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, + (s1 * z2 - s2 * z1) * r); + } + + tangents[ index_arrayr[idx*3+0] ]+=tangent; + binormals[ index_arrayr[idx*3+0] ]+=binormal; + tangents[ index_arrayr[idx*3+1] ]+=tangent; + binormals[ index_arrayr[idx*3+1] ]+=binormal; + tangents[ index_arrayr[idx*3+2] ]+=tangent; + binormals[ index_arrayr[idx*3+2] ]+=binormal; + + } + + r_tangents.resize(vlen*4); + DVector<real_t>::Write tarrayw = r_tangents.write(); + + for(int idx=0;idx<vlen;idx++) { + Vector3 tangent = tangents[idx]; + Vector3 bingen = narrayr[idx].cross(tangent); + float dir; + if (bingen.dot(binormals[idx]) < 0 ) + dir=-1.0; + else + dir=+1.0; + + tarrayw[idx*4+0]=tangent.x; + tarrayw[idx*4+1]=tangent.y; + tarrayw[idx*4+2]=tangent.z; + tarrayw[idx*4+3]=dir; + } +} + +Error ColladaImport::_create_mesh_surfaces(Ref<Mesh>& p_mesh,const Map<String,Collada::NodeGeometry::Material>& p_material_map,const Collada::MeshData &meshdata,const Transform& p_local_xform,const Vector<int> &bone_remap, const Collada::SkinControllerData *skin_controller, const Collada::MorphControllerData *p_morph_data) { + + + bool local_xform_mirror=p_local_xform.basis.determinant() < 0; + + if (p_morph_data) { + //add morphie target + ERR_FAIL_COND_V( !p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA ); + String mt = p_morph_data->targets["MORPH_TARGET"]; + ERR_FAIL_COND_V( !p_morph_data->sources.has(mt), ERR_INVALID_DATA); + int morph_targets = p_morph_data->sources[mt].sarray.size(); + for(int i=0;i<morph_targets;i++) { + + String target = p_morph_data->sources[mt].sarray[i]; + ERR_FAIL_COND_V( !collada.state.mesh_data_map.has(target), ERR_INVALID_DATA ); + String name = collada.state.mesh_data_map[target].name; + + p_mesh->add_morph_target(name); + } + if (p_morph_data->mode=="RELATIVE") + p_mesh->set_morph_target_mode(Mesh::MORPH_MODE_RELATIVE); + else if (p_morph_data->mode=="NORMALIZED") + p_mesh->set_morph_target_mode(Mesh::MORPH_MODE_NORMALIZED); + } + + + int surface=0; + for(int p_i = 0; p_i < meshdata.primitives.size(); p_i ++ ) { + + + + const Collada::MeshData::Primitives& p = meshdata.primitives[p_i]; + + /* VERTEX SOURCE */ + ERR_FAIL_COND_V(!p.sources.has("VERTEX"),ERR_INVALID_DATA); + + String vertex_src_id = p.sources["VERTEX"].source; + int vertex_ofs=p.sources["VERTEX"].offset; + + ERR_FAIL_COND_V(!meshdata.vertices.has(vertex_src_id),ERR_INVALID_DATA); + + ERR_FAIL_COND_V(!meshdata.vertices[vertex_src_id].sources.has("POSITION"),ERR_INVALID_DATA); + String position_src_id = meshdata.vertices[vertex_src_id].sources["POSITION"]; + + ERR_FAIL_COND_V(!meshdata.sources.has(position_src_id),ERR_INVALID_DATA); + + const Collada::MeshData::Source *vertex_src=&meshdata.sources[position_src_id]; + + /* NORMAL SOURCE */ + + const Collada::MeshData::Source *normal_src=NULL; + int normal_ofs=0; + + if (p.sources.has("NORMAL")) { + + String normal_source_id = p.sources["NORMAL"].source; + normal_ofs = p.sources["NORMAL"].offset; + ERR_FAIL_COND_V( !meshdata.sources.has(normal_source_id),ERR_INVALID_DATA); + normal_src=&meshdata.sources[normal_source_id]; + } + + const Collada::MeshData::Source *binormal_src=NULL; + int binormal_ofs=0; + + if (p.sources.has("TEXBINORMAL")) { + + String binormal_source_id = p.sources["TEXBINORMAL"].source; + binormal_ofs = p.sources["TEXBINORMAL"].offset; + ERR_FAIL_COND_V( !meshdata.sources.has(binormal_source_id),ERR_INVALID_DATA); + binormal_src=&meshdata.sources[binormal_source_id]; + } + + const Collada::MeshData::Source *tangent_src=NULL; + int tangent_ofs=0; + + if (p.sources.has("TEXTANGENT")) { + + String tangent_source_id = p.sources["TEXTANGENT"].source; + tangent_ofs = p.sources["TEXTANGENT"].offset; + ERR_FAIL_COND_V( !meshdata.sources.has(tangent_source_id),ERR_INVALID_DATA); + tangent_src=&meshdata.sources[tangent_source_id]; + } + + + const Collada::MeshData::Source *uv_src=NULL; + int uv_ofs=0; + + if (p.sources.has("TEXCOORD0")) { + + String uv_source_id = p.sources["TEXCOORD0"].source; + uv_ofs = p.sources["TEXCOORD0"].offset; + ERR_FAIL_COND_V( !meshdata.sources.has(uv_source_id),ERR_INVALID_DATA); + uv_src=&meshdata.sources[uv_source_id]; + } + + const Collada::MeshData::Source *uv2_src=NULL; + int uv2_ofs=0; + + if (p.sources.has("TEXCOORD1")) { + + String uv2_source_id = p.sources["TEXCOORD1"].source; + uv2_ofs = p.sources["TEXCOORD1"].offset; + ERR_FAIL_COND_V( !meshdata.sources.has(uv2_source_id),ERR_INVALID_DATA); + uv2_src=&meshdata.sources[uv2_source_id]; + } + + + const Collada::MeshData::Source *color_src=NULL; + int color_ofs=0; + + if (false && p.sources.has("COLOR")) { + + String color_source_id = p.sources["COLOR"].source; + color_ofs = p.sources["COLOR"].offset; + ERR_FAIL_COND_V( !meshdata.sources.has(color_source_id), ERR_INVALID_DATA ); + color_src=&meshdata.sources[color_source_id]; + } + + //find largest source.. + + + Set<Collada::Vertex> vertex_set; //vertex set will be the vertices + List<int> indices_list; //indices will be the indices + Map<int,Set<int> > vertex_map; //map vertices (for setting skinning/morph) + + /**************************/ + /* CREATE PRIMITIVE ARRAY */ + /**************************/ + + // The way collada uses indices is more optimal, and friendlier with 3D modelling sofware, + // because it can index everything, not only vertices (similar to how the WII works). + // This is, however, more incompatible with standard video cards, so arrays must be converted. + // Must convert to GL/DX format. + + int _prim_ofs=0; + for(int p_i=0;p_i<p.count;p_i++) { + + + int amount; + if (p.polygons.size()) { + + ERR_FAIL_INDEX_V(p_i,p.polygons.size(),ERR_INVALID_DATA); + amount=p.polygons[p_i]; + } else { + amount=3; //triangles; + } + + //COLLADA_PRINT("amount: "+itos(amount)); + + int prev2[2]={0,0}; + + for(int j=0;j<amount;j++) { + + int src=_prim_ofs; + //_prim_ofs+=p.sources.size() + + ERR_FAIL_INDEX_V(src,p.indices.size(),ERR_INVALID_DATA); + + Collada::Vertex vertex; + + int vertex_index=p.indices[src+vertex_ofs]; //used for index field (later used by controllers) + int vertex_pos = (vertex_src->stride?vertex_src->stride:3) * vertex_index; + ERR_FAIL_INDEX_V(vertex_pos,vertex_src->array.size(),ERR_INVALID_DATA); + vertex.vertex=Vector3(vertex_src->array[vertex_pos+0],vertex_src->array[vertex_pos+1],vertex_src->array[vertex_pos+2]); + + + if (normal_src) { + + + + int normal_pos = (normal_src->stride?normal_src->stride:3) * p.indices[src+normal_ofs]; + ERR_FAIL_INDEX_V(normal_pos,normal_src->array.size(),ERR_INVALID_DATA); + vertex.normal=Vector3(normal_src->array[normal_pos+0],normal_src->array[normal_pos+1],normal_src->array[normal_pos+2]); + vertex.normal=vertex.normal.snapped(0.001); + + + if (tangent_src && binormal_src) { + + int binormal_pos = (binormal_src->stride?binormal_src->stride:3) * p.indices[src+binormal_ofs]; + ERR_FAIL_INDEX_V(binormal_pos,binormal_src->array.size(),ERR_INVALID_DATA); + Vector3 binormal =Vector3(binormal_src->array[binormal_pos+0],binormal_src->array[binormal_pos+1],binormal_src->array[binormal_pos+2]); + + int tangent_pos = (tangent_src->stride?tangent_src->stride:3) * p.indices[src+tangent_ofs]; + ERR_FAIL_INDEX_V(tangent_pos,tangent_src->array.size(),ERR_INVALID_DATA); + Vector3 tangent =Vector3(tangent_src->array[tangent_pos+0],tangent_src->array[tangent_pos+1],tangent_src->array[tangent_pos+2]); + + vertex.tangent.normal=tangent; + vertex.tangent.d= vertex.normal.cross(tangent).dot(binormal) > 0 ? -1 : 1; + } + + } + + + if (uv_src) { + + int uv_pos = (uv_src->stride?uv_src->stride:2) * p.indices[src+uv_ofs]; + ERR_FAIL_INDEX_V(uv_pos,uv_src->array.size(),ERR_INVALID_DATA); + vertex.uv=Vector3(uv_src->array[uv_pos+0],1.0-uv_src->array[uv_pos+1],0); + } + + if (uv2_src) { + + int uv2_pos = (uv2_src->stride?uv2_src->stride:2) * p.indices[src+uv2_ofs]; + ERR_FAIL_INDEX_V(uv2_pos,uv2_src->array.size(),ERR_INVALID_DATA); + vertex.uv2=Vector3(uv2_src->array[uv2_pos+0],1.0-uv2_src->array[uv2_pos+1],0); + } + + if (color_src) { + + int color_pos = (color_src->stride?color_src->stride:3) * p.indices[src+color_ofs]; // colors are RGB in collada.. + ERR_FAIL_INDEX_V(color_pos,color_src->array.size(),ERR_INVALID_DATA); + vertex.color=Color(color_src->array[color_pos+0],color_src->array[color_pos+1],color_src->array[color_pos+2],(color_src->stride>3)?color_src->array[color_pos+3]:1.0); + + } + +#ifndef NO_UP_AXIS_SWAP + if (collada.state.up_axis==Vector3::AXIS_Z) { + + SWAP( vertex.vertex.z, vertex.vertex.y ); + vertex.vertex.z = -vertex.vertex.z; + SWAP( vertex.normal.z, vertex.normal.y ); + vertex.normal.z = -vertex.normal.z; + + } + +#endif + + vertex.fix_unit_scale(collada); + int index=0; + //COLLADA_PRINT("vertex: "+vertex.vertex); + + if (vertex_set.has(vertex)) { + + index=vertex_set.find(vertex)->get().idx; + } else { + + index=vertex_set.size(); + vertex.idx=index; + vertex_set.insert(vertex); + } + + if (!vertex_map.has(vertex_index)) + vertex_map[vertex_index]=Set<int>(); + vertex_map[vertex_index].insert(index); //should be outside.. + //build triangles if needed + if (j==0) + prev2[0]=index; + + if (j>=2) { + //insert indices in reverse order (collada uses CCW as frontface) + if (local_xform_mirror) { + + indices_list.push_back(prev2[0]); + indices_list.push_back(prev2[1]); + indices_list.push_back(index); + + } else { + indices_list.push_back(prev2[0]); + indices_list.push_back(index); + indices_list.push_back(prev2[1]); + } + } + + prev2[1]=index; + _prim_ofs+=p.vertex_size; + } + + } + + + + Vector<Collada::Vertex> vertex_array; //there we go, vertex array + + vertex_array.resize(vertex_set.size()); + for(Set<Collada::Vertex>::Element *F=vertex_set.front();F;F=F->next()) { + + vertex_array[F->get().idx]=F->get(); + } + + /************************/ + /* ADD WEIGHTS IF EXIST */ + /************************/ + + + bool has_weights=false; + + if (skin_controller) { + + const Collada::SkinControllerData::Source *weight_src=NULL; + int weight_ofs=0; + + if (skin_controller->weights.sources.has("WEIGHT")) { + + String weight_id = skin_controller->weights.sources["WEIGHT"].source; + weight_ofs = skin_controller->weights.sources["WEIGHT"].offset; + if (skin_controller->sources.has(weight_id)) { + + weight_src = &skin_controller->sources[weight_id]; + + } + } + + int joint_ofs=0; + + if (skin_controller->weights.sources.has("JOINT")) { + + joint_ofs = skin_controller->weights.sources["JOINT"].offset; + } + + //should be OK, given this was pre-checked. + + int index_ofs=0; + int wstride = skin_controller->weights.sources.size(); + for(int w_i=0;w_i<skin_controller->weights.sets.size();w_i++) { + + int amount = skin_controller->weights.sets[w_i]; + + if (vertex_map.has(w_i)) { //vertex may no longer be here, don't bother converting + + Vector<Collada::Vertex::Weight> weights; + + for (int a_i=0;a_i<amount;a_i++) { + + Collada::Vertex::Weight w; + + int read_from = index_ofs+a_i*wstride; + ERR_FAIL_INDEX_V(read_from+wstride-1,skin_controller->weights.indices.size(),ERR_INVALID_DATA); + int weight_index = skin_controller->weights.indices[read_from+weight_ofs]; + ERR_FAIL_INDEX_V(weight_index,weight_src->array.size(),ERR_INVALID_DATA); + + w.weight = weight_src->array[weight_index]; + + int bone_index = skin_controller->weights.indices[read_from+joint_ofs]; + if (bone_index==-1) + continue; //ignore this weight (refers to bind shape) + ERR_FAIL_INDEX_V(bone_index,bone_remap.size(),ERR_INVALID_DATA); + + w.bone_idx=bone_remap[bone_index]; + + + weights.push_back(w); + } + + /* FIX WEIGHTS */ + + + + weights.sort(); + + if (weights.size()>4) { + //cap to 4 and make weights add up 1 + weights.resize(4); + + } + + //make sure weights allways add up to 1 + float total=0; + for(int i=0;i<weights.size();i++) + total+=weights[i].weight; + if (total) + for(int i=0;i<weights.size();i++) + weights[i].weight/=total; + + if (weights.size()==0 || total==0) { //if nothing, add a weight to bone 0 + //no weights assigned + Collada::Vertex::Weight w; + w.bone_idx=0; + w.weight=1.0; + weights.clear(); + weights.push_back(w); + + } + + + for(Set<int>::Element *E=vertex_map[w_i].front();E;E=E->next()) { + + int dst = E->get(); + ERR_EXPLAIN("invalid vertex index in array"); + ERR_FAIL_INDEX_V(dst,vertex_array.size(),ERR_INVALID_DATA); + vertex_array[dst].weights=weights; + + } + + } else { + //zzprint_line("no vertex found for index "+itos(w_i)); + } + + index_ofs+=wstride*amount; + + } + + //vertices need to be localized + + Transform local_xform = p_local_xform; + for(int i=0;i<vertex_array.size();i++) { + + vertex_array[i].vertex=local_xform.xform(vertex_array[i].vertex); + vertex_array[i].normal=local_xform.basis.xform(vertex_array[i].normal).normalized(); + vertex_array[i].tangent.normal=local_xform.basis.xform(vertex_array[i].tangent.normal).normalized(); + if (local_xform_mirror) { + //i shouldn't do this? wtf? + //vertex_array[i].normal*=-1.0; + //vertex_array[i].tangent.normal*=-1.0; + } + } + + has_weights=true; + + } + + DVector<int> index_array; + index_array.resize(indices_list.size()); + DVector<int>::Write index_arrayw = index_array.write(); + + int iidx=0; + for(List<int>::Element *F=indices_list.front();F;F=F->next()) { + + index_arrayw[iidx++]=F->get(); + } + + index_arrayw=DVector<int>::Write(); + + + /*****************/ + /* MAKE SURFACES */ + /*****************/ + + + { + + Ref<FixedMaterial> material; + + //find material + Mesh::PrimitiveType primitive=Mesh::PRIMITIVE_TRIANGLES; + + { + + if (p_material_map.has(p.material)) { + String target=p_material_map[p.material].target; + + if (!material_cache.has(target)) { + Error err = _create_material(target); + if (!err) + material=material_cache[target]; + } else + material=material_cache[target]; + + } else if (p.material!=""){ + print_line("Warning, unreferenced material in geometry instance: "+p.material); + } + } + + + + DVector<Vector3> final_vertex_array; + DVector<Vector3> final_normal_array; + DVector<float> final_tangent_array; + DVector<Color> final_color_array; + DVector<Vector3> final_uv_array; + DVector<Vector3> final_uv2_array; + DVector<float> final_bone_array; + DVector<float> final_weight_array; + + uint32_t final_format=0; + + //create format + final_format=Mesh::ARRAY_FORMAT_VERTEX|Mesh::ARRAY_FORMAT_INDEX; + + if (normal_src) { + final_format|=Mesh::ARRAY_FORMAT_NORMAL; + if (uv_src && binormal_src && tangent_src) { + final_format|=Mesh::ARRAY_FORMAT_TANGENT; + } + + } + + + + if (color_src) + final_format|=Mesh::ARRAY_FORMAT_COLOR; + if (uv_src) + final_format|=Mesh::ARRAY_FORMAT_TEX_UV; + if (uv2_src) + final_format|=Mesh::ARRAY_FORMAT_TEX_UV2; + + if (has_weights) { + final_format|=Mesh::ARRAY_FORMAT_WEIGHTS; + final_format|=Mesh::ARRAY_FORMAT_BONES; + } + + + //set arrays + + int vlen = vertex_array.size(); + { //vertices + + DVector<Vector3> varray; + varray.resize(vertex_array.size()); + + DVector<Vector3>::Write varrayw = varray.write(); + + for(int k=0;k<vlen;k++) + varrayw[k]=vertex_array[k].vertex; + + varrayw = DVector<Vector3>::Write(); + final_vertex_array=varray; + + } + + + if (uv_src) { //compute uv first, may be needed for computing tangent/bionrmal + DVector<Vector3> uvarray; + uvarray.resize(vertex_array.size()); + DVector<Vector3>::Write uvarrayw = uvarray.write(); + + for(int k=0;k<vlen;k++) { + uvarrayw[k]=vertex_array[k].uv; + } + + uvarrayw = DVector<Vector3>::Write(); + final_uv_array=uvarray; + + } + + if (uv2_src) { //compute uv first, may be needed for computing tangent/bionrmal + DVector<Vector3> uv2array; + uv2array.resize(vertex_array.size()); + DVector<Vector3>::Write uv2arrayw = uv2array.write(); + + for(int k=0;k<vlen;k++) { + uv2arrayw[k]=vertex_array[k].uv; + } + + uv2arrayw = DVector<Vector3>::Write(); + final_uv2_array=uv2array; + + } + + if (normal_src) { + DVector<Vector3> narray; + narray.resize(vertex_array.size()); + DVector<Vector3>::Write narrayw = narray.write(); + + for(int k=0;k<vlen;k++) { + narrayw[k]=vertex_array[k].normal; + } + + narrayw = DVector<Vector3>::Write(); + final_normal_array=narray; + + //DVector<Vector3> altnaray; + //_generate_normals(index_array,final_vertex_array,altnaray); + + //for(int i=0;i<altnaray.size();i++) + // print_line(rtos(altnaray[i].dot(final_normal_array[i]))); + + } else if (primitive==Mesh::PRIMITIVE_TRIANGLES) { + //generate normals (even if unused later) + + _generate_normals(index_array,final_vertex_array,final_normal_array); + if (OS::get_singleton()->is_stdout_verbose()) + print_line("Collada: Triangle mesh lacks normals, so normals were generated."); + final_format|=Mesh::ARRAY_FORMAT_NORMAL; + + } + + if (final_normal_array.size() && uv_src && binormal_src && tangent_src && !force_make_tangents) { + + DVector<real_t> tarray; + tarray.resize(vertex_array.size()*4); + DVector<real_t>::Write tarrayw = tarray.write(); + + + for(int k=0;k<vlen;k++) { + tarrayw[k*4+0]=vertex_array[k].tangent.normal.x; + tarrayw[k*4+1]=vertex_array[k].tangent.normal.y; + tarrayw[k*4+2]=vertex_array[k].tangent.normal.z; + tarrayw[k*4+3]=vertex_array[k].tangent.d; + + } + + tarrayw = DVector<real_t>::Write(); + + final_tangent_array=tarray; + } else if (final_normal_array.size() && primitive==Mesh::PRIMITIVE_TRIANGLES && final_uv_array.size() && (force_make_tangents || (material.is_valid() && material->get_texture(FixedMaterial::PARAM_NORMAL).is_valid()))){ + //if this uses triangles, there are uvs and the material is using a normalmap, generate tangents and binormals, because they WILL be needed + //generate binormals/tangents + _generate_tangents_and_binormals(index_array,final_vertex_array,final_uv_array,final_normal_array,final_tangent_array); + final_format|=Mesh::ARRAY_FORMAT_TANGENT; + if (OS::get_singleton()->is_stdout_verbose()) + print_line("Collada: Triangle mesh lacks tangents (And normalmap was used), so tangents were generated."); + + } + + + if (color_src) { + DVector<Color> colorarray; + colorarray.resize(vertex_array.size()); + DVector<Color>::Write colorarrayw = colorarray.write(); + + for(int k=0;k<vlen;k++) { + colorarrayw[k]=vertex_array[k].color; + } + + colorarrayw = DVector<Color>::Write(); + + final_color_array=colorarray; + } + + if (has_weights) { + DVector<float> weightarray; + DVector<float> bonearray; + + weightarray.resize(vertex_array.size()*4); + DVector<float>::Write weightarrayw = weightarray.write(); + bonearray.resize(vertex_array.size()*4); + DVector<float>::Write bonearrayw = bonearray.write(); + + for(int k=0;k<vlen;k++) { + float sum=0; + + for(int l=0;l<VS::ARRAY_WEIGHTS_SIZE;l++) { + if (l<vertex_array[k].weights.size()) { + weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=vertex_array[k].weights[l].weight; + sum+=weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]; + bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=vertex_array[k].weights[l].bone_idx; + //COLLADA_PRINT(itos(k)+": "+rtos(bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l])+":"+rtos(weightarray[k*VS::ARRAY_WEIGHTS_SIZE+l])); + } else { + + weightarrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=0; + bonearrayw[k*VS::ARRAY_WEIGHTS_SIZE+l]=0; + + } + + + } +// if (sum<0.8) +// COLLADA_PRINT("ERROR SUMMING INDEX "+itos(k)+" had weights: "+itos(vertex_array[k].weights.size())); + + } + + weightarrayw = DVector<float>::Write(); + bonearrayw = DVector<float>::Write(); + + final_weight_array = weightarray; + final_bone_array = bonearray; + } + + + + //////////////////////////// + // FINALLY CREATE SUFRACE // + //////////////////////////// + + Array d; + d.resize(VS::ARRAY_MAX); + + d[Mesh::ARRAY_INDEX]=index_array; + d[Mesh::ARRAY_VERTEX]=final_vertex_array; + + if (final_normal_array.size()) + d[Mesh::ARRAY_NORMAL]=final_normal_array; + if (final_tangent_array.size()) + d[Mesh::ARRAY_TANGENT]=final_tangent_array; + if (final_uv_array.size()) + d[Mesh::ARRAY_TEX_UV]=final_uv_array; + if (final_uv2_array.size()) + d[Mesh::ARRAY_TEX_UV2]=final_uv2_array; + if (final_color_array.size()) + d[Mesh::ARRAY_COLOR]=final_color_array; + if (final_weight_array.size()) + d[Mesh::ARRAY_WEIGHTS]=final_weight_array; + if (final_bone_array.size()) + d[Mesh::ARRAY_BONES]=final_bone_array; + + + Array mr; + + //////////////////////////// + // THEN THE MORPH TARGETS // + //////////////////////////// + + if (p_morph_data) { + + //add morphie target + ERR_FAIL_COND_V( !p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA ); + String mt = p_morph_data->targets["MORPH_TARGET"]; + ERR_FAIL_COND_V( !p_morph_data->sources.has(mt), ERR_INVALID_DATA); + int morph_targets = p_morph_data->sources[mt].sarray.size(); + mr.resize(morph_targets); + + for(int j=0;j<morph_targets;j++) { + + Array mrt; + mrt.resize(VS::ARRAY_MAX); + + String target = p_morph_data->sources[mt].sarray[j]; + ERR_FAIL_COND_V( !collada.state.mesh_data_map.has(target), ERR_INVALID_DATA ); + String name = collada.state.mesh_data_map[target].name; + Collada::MeshData &md = collada.state.mesh_data_map[target]; + + // collada in itself supports morphing everything. However, the spec is unclear and no examples or exporters that + // morph anything but "POSITIONS" seem to exit. Because of this, normals and binormals/tangents have to be regenerated here, + // which may result in inaccurate (but most of the time good enough) results. + + DVector<Vector3> vertices; + vertices.resize(vlen); + + ERR_FAIL_COND_V( md.vertices.size() != 1, ERR_INVALID_DATA); + String vertex_src_id=md.vertices.front()->key(); + ERR_FAIL_COND_V(!md.vertices[vertex_src_id].sources.has("POSITION"),ERR_INVALID_DATA); + String position_src_id = md.vertices[vertex_src_id].sources["POSITION"]; + + ERR_FAIL_COND_V(!md.sources.has(position_src_id),ERR_INVALID_DATA); + + const Collada::MeshData::Source *m=&md.sources[position_src_id]; + + ERR_FAIL_COND_V( m->array.size() != vertex_src->array.size(), ERR_INVALID_DATA); + int stride=m->stride; + if (stride==0) + stride=3; + + + //read vertices from morph target + DVector<Vector3>::Write vertw = vertices.write(); + + for(int m_i=0;m_i<m->array.size()/stride;m_i++) { + + int pos = m_i*stride; + Vector3 vtx( m->array[pos+0], m->array[pos+1], m->array[pos+2] ); + +#ifndef NO_UP_AXIS_SWAP + if (collada.state.up_axis==Vector3::AXIS_Z) { + + SWAP( vtx.z, vtx.y ); + vtx.z = -vtx.z; + + } +#endif + + Collada::Vertex vertex; + vertex.vertex=vtx; + vertex.fix_unit_scale(collada); + vtx=vertex.vertex; + + vtx = p_local_xform.xform(vtx); + + + if (vertex_map.has(m_i)) { //vertex may no longer be here, don't bother converting + + + for (Set<int> ::Element *E=vertex_map[m_i].front() ; E; E=E->next() ) { + + vertw[E->get()]=vtx; + } + } + } + + + //vertices are in place, now generate everything else + vertw = DVector<Vector3>::Write(); + DVector<Vector3> normals; + DVector<float> tangents; + + _generate_normals(index_array,vertices,normals); + if (final_tangent_array.size() && final_uv_array.size()) { + + _generate_tangents_and_binormals(index_array,vertices,final_uv_array,normals,tangents); + + } + + mrt[Mesh::ARRAY_VERTEX]=vertices; + + mrt[Mesh::ARRAY_NORMAL]=normals; + if (tangents.size()) + mrt[Mesh::ARRAY_TANGENT]=tangents; + if (final_uv_array.size()) + mrt[Mesh::ARRAY_TEX_UV]=final_uv_array; + if (final_uv2_array.size()) + mrt[Mesh::ARRAY_TEX_UV2]=final_uv2_array; + if (final_color_array.size()) + mrt[Mesh::ARRAY_COLOR]=final_color_array; + + mr[j]=mrt; + + } + + } + + + p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d,mr); + + if (material.is_valid()) { + p_mesh->surface_set_material(surface, material); + p_mesh->surface_set_name(surface, material->get_name()); + } + } + + /*****************/ + /* FIND MATERIAL */ + /*****************/ + + surface++; + } + + + return OK; + +} + + +Error ColladaImport::_create_resources(Collada::Node *p_node) { + + + if (p_node->type==Collada::Node::TYPE_GEOMETRY && node_map.has(p_node->id)) { + + + Spatial * node=node_map[p_node->id].node; + Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(p_node); + + + if (node->cast_to<Path>()) { + + Path *path = node->cast_to<Path>(); + + String curve = ng->source; + + if (curve_cache.has(ng->source)) { + + path->set_curve(curve_cache[ng->source]); + } else { + + Ref<Curve3D> c = memnew( Curve3D ); + + const Collada::CurveData &cd = collada.state.curve_data_map[ng->source]; + + ERR_FAIL_COND_V( !cd.control_vertices.has("POSITION") , ERR_INVALID_DATA); + ERR_FAIL_COND_V( !cd.control_vertices.has("IN_TANGENT") , ERR_INVALID_DATA); + ERR_FAIL_COND_V( !cd.control_vertices.has("OUT_TANGENT") , ERR_INVALID_DATA); + ERR_FAIL_COND_V( !cd.control_vertices.has("INTERPOLATION") , ERR_INVALID_DATA); + + + ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["POSITION"] ) , ERR_INVALID_DATA); + const Collada::CurveData::Source &vertices = cd.sources[ cd.control_vertices["POSITION"] ]; + ERR_FAIL_COND_V( vertices.stride!=3, ERR_INVALID_DATA ); + + ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["IN_TANGENT"] ) , ERR_INVALID_DATA); + const Collada::CurveData::Source &in_tangents = cd.sources[ cd.control_vertices["IN_TANGENT"] ]; + ERR_FAIL_COND_V( in_tangents.stride!=3 , ERR_INVALID_DATA); + + ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["OUT_TANGENT"] ), ERR_INVALID_DATA ); + const Collada::CurveData::Source &out_tangents = cd.sources[ cd.control_vertices["OUT_TANGENT"] ]; + ERR_FAIL_COND_V( out_tangents.stride!=3, ERR_INVALID_DATA ); + + ERR_FAIL_COND_V( !cd.sources.has(cd.control_vertices["INTERPOLATION"] ), ERR_INVALID_DATA ); + const Collada::CurveData::Source &interps = cd.sources[ cd.control_vertices["INTERPOLATION"] ]; + ERR_FAIL_COND_V( interps.stride!=1, ERR_INVALID_DATA ); + + const Collada::CurveData::Source *tilts=NULL; + if (cd.control_vertices.has("TILT") && cd.sources.has(cd.control_vertices["TILT"])) + tilts=&cd.sources[ cd.control_vertices["TILT"] ]; + + + if (tilts) { + print_line("FOUND TILTS!!!"); + } + int pc = vertices.array.size()/3; + for(int i=0;i<pc;i++) { + + Vector3 pos( vertices.array[i*3+0], vertices.array[i*3+1], vertices.array[i*3+2] ); + Vector3 in( in_tangents.array[i*3+0], in_tangents.array[i*3+1], in_tangents.array[i*3+2] ); + Vector3 out( out_tangents.array[i*3+0], out_tangents.array[i*3+1], out_tangents.array[i*3+2] ); + +#ifndef NO_UP_AXIS_SWAP + if (collada.state.up_axis==Vector3::AXIS_Z) { + + SWAP(pos.y,pos.z); + pos.z=-pos.z; + SWAP(in.y,in.z); + in.z=-in.z; + SWAP(out.y,out.z); + out.z=-out.z; + } +#endif + pos*=collada.state.unit_scale; + in*=collada.state.unit_scale; + out*=collada.state.unit_scale; + + c->add_point(pos,in-pos,out-pos); + if (tilts) + c->set_point_tilt(i,tilts->array[i]); + + } + + curve_cache[ng->source]=c; + path->set_curve(c); + + } + + + } + + + if (node->cast_to<MeshInstance>()) { + + + Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(p_node); + + MeshInstance *mi = node->cast_to<MeshInstance>(); + + + ERR_FAIL_COND_V(!mi,ERR_BUG); + + + Collada::SkinControllerData *skin=NULL; + Collada::MorphControllerData *morph=NULL; + String meshid; + Transform apply_xform; + Vector<int> bone_remap; + + if (ng->controller) { + + if (collada.state.skin_controller_data_map.has(ng->source)) { + + + ERR_FAIL_COND_V(!collada.state.skin_controller_data_map.has(ng->source),ERR_INVALID_DATA); + skin=&collada.state.skin_controller_data_map[ng->source]; + + Vector<String> skeletons = ng->skeletons; + + ERR_FAIL_COND_V( skeletons.empty(), ERR_INVALID_DATA ); + + String skname = skeletons[0]; + ERR_FAIL_COND_V( !node_map.has(skname), ERR_INVALID_DATA ); + NodeMap nmsk = node_map[skname]; + Skeleton *sk = nmsk.node->cast_to<Skeleton>(); + ERR_FAIL_COND_V( !sk, ERR_INVALID_DATA ); + ERR_FAIL_COND_V( !skeleton_bone_map.has(sk), ERR_INVALID_DATA ); + Map<String, int> &bone_remap_map=skeleton_bone_map[sk]; + + + meshid=skin->base; + + if (collada.state.morph_controller_data_map.has(meshid)) { + //it's a morph!! + morph = &collada.state.morph_controller_data_map[meshid]; + meshid=morph->mesh; + } + + apply_xform=collada.fix_transform(p_node->default_transform); + node->set_transform(Transform()); + + Collada::SkinControllerData::Source *joint_src=NULL; + + ERR_FAIL_COND_V(!skin->weights.sources.has("JOINT"),ERR_INVALID_DATA); + + String joint_id = skin->weights.sources["JOINT"].source; + ERR_FAIL_COND_V(!skin->sources.has(joint_id),ERR_INVALID_DATA); + + joint_src = &skin->sources[joint_id]; + + bone_remap.resize(joint_src->sarray.size()); + + for(int i=0;i<bone_remap.size();i++) { + + String str = joint_src->sarray[i]; + ERR_FAIL_COND_V( !bone_remap_map.has(str), ERR_INVALID_DATA ); + bone_remap[i]=bone_remap_map[str]; + } + } else if (collada.state.morph_controller_data_map.has(ng->source)) { + //it's a morph!! + morph = &collada.state.morph_controller_data_map[meshid]; + meshid=morph->mesh; + } else { + ERR_EXPLAIN("Controller Instance Source '"+ng->source+"' is neither skin or morph!"); + ERR_FAIL_V( ERR_INVALID_DATA ); + } + + + + } else { + meshid=ng->source; + } + + Ref<Mesh> mesh; + if (mesh_cache.has(meshid)) { + mesh=mesh_cache[meshid]; + } else { + if (collada.state.mesh_data_map.has(meshid)) { + //bleh, must ignore invalid + + ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid),ERR_INVALID_DATA); + mesh=Ref<Mesh>(memnew( Mesh )); + const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid]; + mesh->set_name( meshdata.name ); + Error err = _create_mesh_surfaces(mesh,ng->material_map,meshdata,apply_xform,bone_remap,skin,morph); + ERR_FAIL_COND_V(err,err); + + mesh_cache[meshid]=mesh; + } else { + + print_line("Warning, will not import geometry: "+meshid); + } + } + + if (!mesh.is_null()) { + mi->set_mesh(mesh); + } + } + } + + for(int i=0;i<p_node->children.size();i++) { + + Error err = _create_resources(p_node->children[i]); + if (err) + return err; + } + return OK; +} + + +Error ColladaImport::load(const String& p_path,int p_flags,bool p_force_make_tangents) { + + Error err = collada.load(p_path,p_flags); + ERR_FAIL_COND_V(err,err); + + force_make_tangents=p_force_make_tangents; + ERR_FAIL_COND_V( !collada.state.visual_scene_map.has( collada.state.root_visual_scene ), ERR_INVALID_DATA ); + Collada::VisualScene &vs = collada.state.visual_scene_map[ collada.state.root_visual_scene ]; + + scene = memnew( Spatial ); // root + + //determine what's going on with the lights + for(int i=0;i<vs.root_nodes.size();i++) { + + _pre_process_lights(vs.root_nodes[i]); + + } + //import scene + for(int i=0;i<vs.root_nodes.size();i++) { + + Error err = _create_scene(vs.root_nodes[i],scene); + if (err!=OK) { + memdelete(scene); + ERR_FAIL_COND_V(err,err); + } + + Error err2 = _create_resources(vs.root_nodes[i]); + if (err2!=OK) { + memdelete(scene); + ERR_FAIL_COND_V(err2,err2); + } + } + + //optatively, set unit scale in the root + scene->set_transform(collada.get_root_transform()); + + + return OK; + +} + +void ColladaImport::_fix_param_animation_tracks() { + + for (Map<String,Collada::Node*>::Element *E=collada.state.scene_map.front();E;E=E->next()) { + + Collada::Node *n = E->get(); + switch(n->type) { + + case Collada::Node::TYPE_NODE: { + // ? do nothing + } break; + case Collada::Node::TYPE_JOINT: { + + } break; + case Collada::Node::TYPE_SKELETON: { + + } break; + case Collada::Node::TYPE_LIGHT: { + + } break; + case Collada::Node::TYPE_CAMERA: { + + } break; + case Collada::Node::TYPE_GEOMETRY: { + + Collada::NodeGeometry *ng = static_cast<Collada::NodeGeometry*>(n); + // test source(s) + String source = ng->source; + + while (source!="") { + + if (collada.state.skin_controller_data_map.has(source)) { + + const Collada::SkinControllerData& skin = collada.state.skin_controller_data_map[source]; + + //nothing to animate here i think + + source=skin.base; + } else if (collada.state.morph_controller_data_map.has(source)) { + + const Collada::MorphControllerData& morph = collada.state.morph_controller_data_map[source]; + if (morph.targets.has("MORPH_WEIGHT") && morph.targets.has("MORPH_TARGET")) { + + String weights = morph.targets["MORPH_WEIGHT"]; + String targets = morph.targets["MORPH_TARGET"]; + + if (morph.sources.has(targets) && morph.sources.has(weights)) { + const Collada::MorphControllerData::Source &weight_src=morph.sources[weights]; + const Collada::MorphControllerData::Source &target_src=morph.sources[targets]; + + ERR_FAIL_COND(weight_src.array.size() != target_src.sarray.size()); + + for(int i=0;i<weight_src.array.size();i++) { + + String track_name = weights+"("+itos(i)+")"; + String mesh_name = target_src.sarray[i]; + if (collada.state.mesh_name_map.has(mesh_name) && collada.state.referenced_tracks.has(track_name)) { + + const Vector<int>&rt = collada.state.referenced_tracks[track_name]; + + for(int rti=0;rti<rt.size();rti++) { + Collada::AnimationTrack *at = &collada.state.animation_tracks[rt[rti]]; + + at->target=E->key(); + at->param="morph/"+collada.state.mesh_name_map[mesh_name]; + at->property=true; + //at->param + } + } + } + } + } + source=morph.mesh; + } else { + + source=""; // for now nothing else supported + } + } + + } break; + + } + } + +} + +void ColladaImport::create_animations() { + + print_line("-=-=-=-=-PRE CA"); + _fix_param_animation_tracks(); + for(int i=0;i<collada.state.animation_clips.size();i++) { + + for(int j=0;j<collada.state.animation_clips[i].tracks.size();j++) + tracks_in_clips.insert(collada.state.animation_clips[i].tracks[j]); + } + + + + for(int i=0;i<collada.state.animation_tracks.size();i++) { + + Collada::AnimationTrack &at = collada.state.animation_tracks[i]; + if (!node_map.has(at.target)) { + print_line("Coudlnt find node: "+at.target); + continue; + } + + + if (at.property) { + + valid_animated_properties.push_back(i); + + } else { + node_map[at.target].anim_tracks.push_back(i); + valid_animated_nodes.insert(at.target); + } + + } + + create_animation(); + print_line("clipcount: "+itos(collada.state.animation_clips.size())); + for(int i=0;i<collada.state.animation_clips.size();i++) + create_animation(i); + +} + +void ColladaImport::create_animation(int p_clip) { + + Ref<Animation> animation = Ref<Animation>( memnew( Animation )); + + if (p_clip==-1) { + + print_line("default"); + animation->set_name("default"); + } else { + print_line("clip name: "+collada.state.animation_clips[p_clip].name); + animation->set_name(collada.state.animation_clips[p_clip].name); + } + + for(Map<String,NodeMap>::Element *E=node_map.front();E;E=E->next()) { + + if (E->get().bone<0) + continue; + bones_with_animation[E->key()]=false; + } + //store and validate tracks + + if (p_clip==-1) { + //main anim + } + + Set<int> track_filter; + + + if (p_clip==-1) { + + for(int i=0;i<collada.state.animation_clips.size();i++) { + + int tc = collada.state.animation_clips[i].tracks.size(); + for(int j=0;j<tc;j++) { + + String n = collada.state.animation_clips[i].tracks[j]; + if (collada.state.by_id_tracks.has(n)) { + + const Vector<int>&ti = collada.state.by_id_tracks[n]; + for(int k=0;k<ti.size();k++) { + track_filter.insert(ti[k]); + } + } + } + } + } else { + + int tc = collada.state.animation_clips[p_clip].tracks.size(); + for(int j=0;j<tc;j++) { + + String n = collada.state.animation_clips[p_clip].tracks[j]; + if (collada.state.by_id_tracks.has(n)) { + + const Vector<int>&ti = collada.state.by_id_tracks[n]; + for(int k=0;k<ti.size();k++) { + track_filter.insert(ti[k]); + } + } + } + + } + + //animation->set_loop(true); + //create animation tracks + + Vector<float> base_snapshots; + + float f=0; + float snapshot_interval = 1.0/20.0; //should be customizable somewhere... + + float anim_length=collada.state.animation_length; + if (p_clip>=0 && collada.state.animation_clips[p_clip].end) + anim_length=collada.state.animation_clips[p_clip].end; + + while(f<collada.state.animation_length) { + if (f>=collada.state.animation_length) + f=collada.state.animation_length; + + base_snapshots.push_back(f); + f+=snapshot_interval; + } + print_line("anim len: "+rtos(anim_length)); + animation->set_length(anim_length); + + bool tracks_found=false; + + for(Set<String>::Element* E=valid_animated_nodes.front();E;E=E->next()) { + + // take snapshots + if (!collada.state.scene_map.has(E->get())) + continue; + + NodeMap &nm = node_map[E->get()]; + String path = scene->get_path_to(nm.node); + + if (nm.bone>=0) { + Skeleton *sk = static_cast<Skeleton*>(nm.node); + String name = sk->get_bone_name(nm.bone); + path=path+":"+name; + } + + bool found_anim=false; + + + Collada::Node *cn = collada.state.scene_map[E->get()]; + if (cn->ignore_anim) { + print_line("warning, ignoring animation on node: "+path); + continue; + } + + + + animation->add_track(Animation::TYPE_TRANSFORM); + int track = animation->get_track_count() -1; + animation->track_set_path( track , path ); + + Vector<float> snapshots = base_snapshots; + + if (nm.anim_tracks.size()==1) { + //use snapshot keys from anim track instead, because this was most likely exported baked + Collada::AnimationTrack &at = collada.state.animation_tracks[nm.anim_tracks.front()->get()]; + snapshots.clear(); + for(int i=0;i<at.keys.size();i++) + snapshots.push_back(at.keys[i].time); + + } + + + for(int i=0;i<snapshots.size();i++) { + + for(List<int>::Element *ET=nm.anim_tracks.front();ET;ET=ET->next()) { + //apply tracks + + if (p_clip==-1) { + + if (track_filter.has(ET->get())) + continue; + } else { + + if (!track_filter.has(ET->get())) + continue; + + } + + found_anim=true; + + Collada::AnimationTrack &at = collada.state.animation_tracks[ET->get()]; + + int xform_idx=-1; + for(int j=0;j<cn->xform_list.size();j++) { + + + if (cn->xform_list[j].id==at.param) { + + xform_idx=j; + break; + } + } + + if (xform_idx==-1) { + print_line("couldnt find matching node "+at.target+" xform for track "+at.param); + continue; + } + + ERR_CONTINUE(xform_idx==-1); + + Vector<float> data = at.get_value_at_time(snapshots[i]); + ERR_CONTINUE(data.empty()); + + + Collada::Node::XForm &xf = cn->xform_list[xform_idx]; + + if (at.component=="ANGLE") { + ERR_CONTINUE(data.size()!=1); + ERR_CONTINUE(xf.op!=Collada::Node::XForm::OP_ROTATE); + ERR_CONTINUE(xf.data.size()<4); + xf.data[3]=data[0]; + } else if (at.component=="X" || at.component=="Y" || at.component=="Z") { + int cn=at.component[0]-'X'; + ERR_CONTINUE(cn>=xf.data.size()); + ERR_CONTINUE(data.size()>1); + xf.data[cn]=data[0]; + } else if (data.size()==xf.data.size()) { + + xf.data=data; + } else { + + + if ( data.size()!=xf.data.size() ) { + print_line("component "+at.component+" datasize "+itos(data.size())+" xfdatasize "+itos(xf.data.size())); + } + + ERR_CONTINUE( data.size()!=xf.data.size() ); + } + } + + Transform xform = cn->compute_transform(collada); + xform = collada.fix_transform(xform) * cn->post_transform; + + + if (nm.bone>=0) { + //make bone transform relative to rest (in case of skeleton) + Skeleton *sk = nm.node->cast_to<Skeleton>(); + if (sk) { + + xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform; + } else { + + ERR_PRINT("INVALID SKELETON!!!!"); + } + } + + Quat q = xform.basis; + q.normalize(); + Vector3 s = xform.basis.get_scale(); + Vector3 l = xform.origin; + + animation->transform_track_insert_key(track,snapshots[i],l,q,s); + + } + + + + if (nm.bone>=0) { + if (found_anim) + bones_with_animation[E->get()]=true; + } + + if (found_anim) + tracks_found=true; + else { + animation->remove_track( track ); + } + + } + + + //some bones may lack animation, but since we don't store pose as a property, we must add keyframes! + for(Map<String,bool>::Element *E=bones_with_animation.front();E;E=E->next()) { + + if (E->get()) + continue; + + //print_line("BONE LACKS ANIM: "+E->key()); + + NodeMap &nm = node_map[E->key()]; + String path = scene->get_path_to(nm.node); + ERR_CONTINUE( nm.bone <0 ); + Skeleton *sk = static_cast<Skeleton*>(nm.node); + String name = sk->get_bone_name(nm.bone); + path=path+":"+name; + + Collada::Node *cn = collada.state.scene_map[E->key()]; + if (cn->ignore_anim) { + print_line("warning, ignoring animation on node: "+path); + continue; + } + + animation->add_track(Animation::TYPE_TRANSFORM); + int track = animation->get_track_count() -1; + animation->track_set_path( track , path ); + + + Transform xform = cn->compute_transform(collada); + xform = collada.fix_transform(xform) * cn->post_transform; + + xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform; + + Quat q = xform.basis; + q.normalize(); + Vector3 s = xform.basis.get_scale(); + Vector3 l = xform.origin; + + animation->transform_track_insert_key(track,0,l,q,s); + + tracks_found=true; + } + + + + + for(int i=0;i<valid_animated_properties.size();i++) { + + + int ti = valid_animated_properties[i]; + + if (p_clip==-1) { + + if (track_filter.has(ti)) + continue; + } else { + + if (!track_filter.has(ti)) + continue; + + } + + + Collada::AnimationTrack &at = collada.state.animation_tracks[ ti ]; + + // take snapshots + if (!collada.state.scene_map.has(at.target)) + continue; + + NodeMap &nm = node_map[at.target]; + String path = scene->get_path_to(nm.node); + + Collada::Node *cn = collada.state.scene_map[at.target]; + //if (cn->ignore_anim) { + // print_line("warning, ignoring property animation on node: "+nm.path); + //continue; + //} + + animation->add_track(Animation::TYPE_VALUE); + int track = animation->get_track_count() -1; + path = path +":"+at.param; + animation->track_set_path( track , path ); + + for(int i=0;i<at.keys.size();i++) { + + float time = at.keys[i].time; + Variant value; + Vector<float> data = at.keys[i].data; + if (data.size()==1) { + //push a float + value=data[0]; + + } else if (data.size()==16) { + //matrix + print_line("value keys for matrices not supported"); + } else { + + print_line("don't know what to do with this amount of value keys: "+itos(data.size())); + } + + animation->track_insert_key(track,time,value); + } + + + tracks_found=true; + + } + + + + if (tracks_found) { + + animations.push_back(animation); + } + + + +} + + +/*********************************************************************************/ +/*************************************** SCENE ***********************************/ +/*********************************************************************************/ + + +#define DEBUG_ANIMATION + + +uint32_t EditorSceneImporterCollada::get_import_flags() const { + + return IMPORT_SCENE|IMPORT_ANIMATION; + +} +void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) const { + + + r_extensions->push_back("dae"); +} +Node* EditorSceneImporterCollada::import_scene(const String& p_path,uint32_t p_flags,Error* r_err) { + + + ColladaImport state; + uint32_t flags=Collada::IMPORT_FLAG_SCENE; + if (p_flags&IMPORT_ANIMATION) + flags|=Collada::IMPORT_FLAG_ANIMATION; + + + Error err = state.load(p_path,flags,p_flags&EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS); + + ERR_FAIL_COND_V(err!=OK,NULL); + + if (state.missing_textures.size()) { + + for(int i=0;i<state.missing_textures.size();i++) { + EditorNode::add_io_error("Texture Not Found: "+state.missing_textures[i]); + } + + if (p_flags&IMPORT_FAIL_ON_MISSING_DEPENDENCIES) + return NULL; + } + + if (p_flags&IMPORT_ANIMATION) { + + state.create_animations(); + AnimationPlayer *ap = memnew( AnimationPlayer ); + for(int i=0;i<state.animations.size();i++) { + String name; + if (state.animations[i]->get_name()=="") + name="default"; + else + name=state.animations[i]->get_name(); + + if (p_flags&IMPORT_ANIMATION_OPTIMIZE) + state.animations[i]->optimize(); + if (p_flags&IMPORT_ANIMATION_DETECT_LOOP) { + + if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) { + state.animations[i]->set_loop(true); + } + } + + ap->add_animation(name,state.animations[i]); + } + state.scene->add_child(ap); + ap->set_owner(state.scene); + + } + + return state.scene; + +} + +Ref<Animation> EditorSceneImporterCollada::import_animation(const String& p_path,uint32_t p_flags) { + + + ColladaImport state; + + + Error err = state.load(p_path,Collada::IMPORT_FLAG_ANIMATION,p_flags&EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS); + ERR_FAIL_COND_V(err!=OK,RES()); + + + state.create_animations(); + if (state.scene) + memdelete(state.scene); + + if (state.animations.size()==0) + return Ref<Animation>(); + Ref<Animation> anim=state.animations[0]; + anim=state.animations[0]; + print_line("Anim Load OK"); + String base = p_path.basename().to_lower(); + if (p_flags&IMPORT_ANIMATION_DETECT_LOOP) { + + if (base.begins_with("loop") || base.ends_with("loop") || base.begins_with("cycle") || base.ends_with("cycle")) { + anim->set_loop(true); + } + } + + if (p_flags&IMPORT_ANIMATION_OPTIMIZE) + anim->optimize(); + + return anim; +} + + +EditorSceneImporterCollada::EditorSceneImporterCollada() { + + +} |
