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