Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { List<Ref<Mesh> > meshes; Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, Vector3(1, 1, 1), r_missing_deps); if (err != OK) { if (r_err) { *r_err = err; } return NULL; } Spatial *scene = memnew(Spatial); for (List<Ref<Mesh> >::Element *E = meshes.front(); E; E = E->next()) { MeshInstance *mi = memnew(MeshInstance); mi->set_mesh(E->get()); mi->set_name(E->get()->get_name()); scene->add_child(mi); mi->set_owner(scene); } if (r_err) { *r_err = OK; } return scene; }
void MeshInstance::create_debug_tangents() { Vector<Vector3> lines; Vector<Color> colors; Ref<Mesh> mesh = get_mesh(); if (!mesh.is_valid()) return; for (int i = 0; i < mesh->get_surface_count(); i++) { Array arrays = mesh->surface_get_arrays(i); Vector<Vector3> verts = arrays[Mesh::ARRAY_VERTEX]; Vector<Vector3> norms = arrays[Mesh::ARRAY_NORMAL]; if (norms.size() == 0) continue; Vector<float> tangents = arrays[Mesh::ARRAY_TANGENT]; if (tangents.size() == 0) continue; for (int j = 0; j < verts.size(); j++) { Vector3 v = verts[j]; Vector3 n = norms[j]; Vector3 t = Vector3(tangents[j * 4 + 0], tangents[j * 4 + 1], tangents[j * 4 + 2]); Vector3 b = (n.cross(t)).normalized() * tangents[j * 4 + 3]; lines.push_back(v); //normal colors.push_back(Color(0, 0, 1)); //color lines.push_back(v + n * 0.04); //normal colors.push_back(Color(0, 0, 1)); //color lines.push_back(v); //tangent colors.push_back(Color(1, 0, 0)); //color lines.push_back(v + t * 0.04); //tangent colors.push_back(Color(1, 0, 0)); //color lines.push_back(v); //binormal colors.push_back(Color(0, 1, 0)); //color lines.push_back(v + b * 0.04); //binormal colors.push_back(Color(0, 1, 0)); //color } } if (lines.size()) { Ref<SpatialMaterial> sm; sm.instance(); sm->set_flag(SpatialMaterial::FLAG_UNSHADED, true); sm->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); sm->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); Ref<ArrayMesh> am; am.instance(); Array a; a.resize(Mesh::ARRAY_MAX); a[Mesh::ARRAY_VERTEX] = lines; a[Mesh::ARRAY_COLOR] = colors; am->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a); am->surface_set_material(0, sm); MeshInstance *mi = memnew(MeshInstance); mi->set_mesh(am); mi->set_name("DebugTangents"); add_child(mi); #ifdef TOOLS_ENABLED if (this == get_tree()->get_edited_scene_root()) mi->set_owner(this); else mi->set_owner(get_owner()); #endif } }
Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map,uint32_t p_flags,Set<Ref<ImageTexture> >& image_map) { // children first.. for(int i=0;i<p_node->get_child_count();i++) { Node *r = _fix_node(p_node->get_child(i),p_root,collision_map,p_flags,image_map); if (!r) { print_line("was erased.."); i--; //was erased } } String name = p_node->get_name(); bool isroot = p_node==p_root; if (!isroot && p_flags&SCENE_FLAG_REMOVE_NOIMP && _teststr(name,"noimp")) { memdelete(p_node); return NULL; } { List<PropertyInfo> pl; p_node->get_property_list(&pl); for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { if (E->get().type==Variant::OBJECT || E->get().type==Variant::ARRAY || E->get().type==Variant::DICTIONARY) { _find_resources(p_node->get(E->get().name),image_map); } } } if (p_flags&SCENE_FLAG_CREATE_BILLBOARDS && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); bool bb=false; if ((_teststr(name,"bb"))) { bb=true; } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"bb"))) { bb=true; } if (bb) { mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true); if (mi->get_mesh().is_valid()) { Ref<Mesh> m = mi->get_mesh(); for(int i=0;i<m->get_surface_count();i++) { Ref<FixedMaterial> fm = m->surface_get_material(i); if (fm.is_valid()) { fm->set_flag(Material::FLAG_UNSHADED,true); fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true); fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); } } } } } if (p_flags&SCENE_FLAG_REMOVE_NOIMP && p_node->cast_to<AnimationPlayer>()) { //remove animations referencing non-importable nodes AnimationPlayer *ap = p_node->cast_to<AnimationPlayer>(); List<StringName> anims; ap->get_animation_list(&anims); for(List<StringName>::Element *E=anims.front();E;E=E->next()) { Ref<Animation> anim=ap->get_animation(E->get()); ERR_CONTINUE(anim.is_null()); for(int i=0;i<anim->get_track_count();i++) { NodePath path = anim->track_get_path(i); for(int j=0;j<path.get_name_count();j++) { String node = path.get_name(j); if (_teststr(node,"noimp")) { anim->remove_track(i); i--; break; } } } } } if (p_flags&SCENE_FLAG_CREATE_IMPOSTORS && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); String str; if ((_teststr(name,"imp"))) { str=name; } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"imp"))) { str=mi->get_mesh()->get_name(); } if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>(); String d=str.substr(str.find("imp")+3,str.length()); if (d!="") { if ((d[0]<'0' || d[0]>'9')) d=d.substr(1,d.length()); if (d.length() && d[0]>='0' && d[0]<='9') { float dist = d.to_double(); mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true); mi->set_flag(GeometryInstance::FLAG_BILLBOARD_FIX_Y,true); mi->set_draw_range_begin(dist); mi->set_draw_range_end(100000); mip->set_draw_range_begin(0); mip->set_draw_range_end(dist); if (mi->get_mesh().is_valid()) { Ref<Mesh> m = mi->get_mesh(); for(int i=0;i<m->get_surface_count();i++) { Ref<FixedMaterial> fm = m->surface_get_material(i); if (fm.is_valid()) { fm->set_flag(Material::FLAG_UNSHADED,true); fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true); fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); } } } } } } } if (p_flags&SCENE_FLAG_CREATE_LODS && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); String str; if ((_teststr(name,"lod"))) { str=name; } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"lod"))) { str=mi->get_mesh()->get_name(); } if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>(); String d=str.substr(str.find("lod")+3,str.length()); if (d!="") { if ((d[0]<'0' || d[0]>'9')) d=d.substr(1,d.length()); if (d.length() && d[0]>='0' && d[0]<='9') { float dist = d.to_double(); mi->set_draw_range_begin(dist); mi->set_draw_range_end(100000); mip->set_draw_range_begin(0); mip->set_draw_range_end(dist); /*if (mi->get_mesh().is_valid()) { Ref<Mesh> m = mi->get_mesh(); for(int i=0;i<m->get_surface_count();i++) { Ref<FixedMaterial> fm = m->surface_get_material(i); if (fm.is_valid()) { fm->set_flag(Material::FLAG_UNSHADED,true); fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true); fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); } } }*/ } } } } if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(name,"colonly") && p_node->cast_to<MeshInstance>()) { if (isroot) return p_node; MeshInstance *mi = p_node->cast_to<MeshInstance>(); Node * col = mi->create_trimesh_collision_node(); ERR_FAIL_COND_V(!col,NULL); col->set_name(_fixstr(name,"colonly")); col->cast_to<Spatial>()->set_transform(mi->get_transform()); p_node->replace_by(col); memdelete(p_node); p_node=col; } else if (p_flags&SCENE_FLAG_CREATE_COLLISIONS &&_teststr(name,"col") && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); mi->set_name(_fixstr(name,"col")); mi->create_trimesh_collision(); } else if (p_flags&SCENE_FLAG_CREATE_ROOMS && _teststr(name,"room") && p_node->cast_to<MeshInstance>()) { if (isroot) return p_node; MeshInstance *mi = p_node->cast_to<MeshInstance>(); DVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID); BSP_Tree bsptree(faces); Ref<RoomBounds> area = memnew( RoomBounds ); area->set_bounds(faces); area->set_geometry_hint(faces); Room * room = memnew( Room ); room->set_name(_fixstr(name,"room")); room->set_transform(mi->get_transform()); room->set_room(area); p_node->replace_by(room); memdelete(p_node); p_node=room; } else if (p_flags&SCENE_FLAG_CREATE_ROOMS &&_teststr(name,"room")) { if (isroot) return p_node; Spatial *dummy = p_node->cast_to<Spatial>(); ERR_FAIL_COND_V(!dummy,NULL); Room * room = memnew( Room ); room->set_name(_fixstr(name,"room")); room->set_transform(dummy->get_transform()); p_node->replace_by(room); memdelete(p_node); p_node=room; room->compute_room_from_subtree(); } else if (p_flags&SCENE_FLAG_CREATE_PORTALS &&_teststr(name,"portal") && p_node->cast_to<MeshInstance>()) { if (isroot) return p_node; MeshInstance *mi = p_node->cast_to<MeshInstance>(); DVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID); ERR_FAIL_COND_V(faces.size()==0,NULL); //step 1 compute the plane Set<Vector3> points; Plane plane; Vector3 center; for(int i=0;i<faces.size();i++) { Face3 f = faces.get(i); Plane p = f.get_plane(); plane.normal+=p.normal; plane.d+=p.d; for(int i=0;i<3;i++) { Vector3 v = f.vertex[i].snapped(0.01); if (!points.has(v)) { points.insert(v); center+=v; } } } plane.normal.normalize(); plane.d/=faces.size(); center/=points.size(); //step 2, create points Transform t; t.basis.from_z(plane.normal); t.basis.transpose(); t.origin=center; Vector<Point2> portal_points; for(Set<Vector3>::Element *E=points.front();E;E=E->next()) { Vector3 local = t.xform_inv(E->get()); portal_points.push_back(Point2(local.x,local.y)); } // step 3 bubbly sort points int swaps=0; do { swaps=0; for(int i=0;i<portal_points.size()-1;i++) { float a = portal_points[i].atan2(); float b = portal_points[i+1].atan2(); if (a>b) { SWAP( portal_points[i], portal_points[i+1] ); swaps++; } } } while(swaps); Portal *portal = memnew( Portal ); portal->set_shape(portal_points); portal->set_transform( mi->get_transform() * t); p_node->replace_by(portal); memdelete(p_node); p_node=portal; } else if (p_node->cast_to<MeshInstance>()) { //last attempt, maybe collision insde the mesh data MeshInstance *mi = p_node->cast_to<MeshInstance>(); Ref<Mesh> mesh = mi->get_mesh(); if (!mesh.is_null()) { if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(mesh->get_name(),"col")) { mesh->set_name( _fixstr(mesh->get_name(),"col") ); Ref<Shape> shape; if (collision_map.has(mesh)) { shape = collision_map[mesh]; } else { shape = mesh->create_trimesh_shape(); if (!shape.is_null()) collision_map[mesh]=shape; } if (!shape.is_null()) { #if 0 StaticBody* static_body = memnew( StaticBody ); ERR_FAIL_COND_V(!static_body,NULL); static_body->set_name( String(mesh->get_name()) + "_col" ); shape->set_name(static_body->get_name()); static_body->add_shape(shape); mi->add_child(static_body); if (mi->get_owner()) static_body->set_owner( mi->get_owner() ); #endif } } for(int i=0;i<mesh->get_surface_count();i++) { Ref<FixedMaterial> fm = mesh->surface_get_material(i); if (fm.is_valid()) { String name = fm->get_name(); if (_teststr(name,"alpha")) { fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); name=_fixstr(name,"alpha"); } if (_teststr(name,"vcol")) { fm->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true); name=_fixstr(name,"vcol"); } fm->set_name(name); } } } } return p_node; }
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); if (r_err) { *r_err = ERR_CANT_OPEN; } ERR_FAIL_COND_V(!f, NULL); if (r_err) { *r_err = OK; } Spatial *scene = memnew(Spatial); Ref<ArrayMesh> mesh; mesh.instance(); Map<String, Ref<Material> > name_map; bool generate_tangents = p_flags & IMPORT_GENERATE_TANGENT_ARRAYS; bool flip_faces = false; //bool flip_faces = p_options["force/flip_faces"]; //bool force_smooth = p_options["force/smooth_shading"]; //bool weld_vertices = p_options["force/weld_vertices"]; //float weld_tolerance = p_options["force/weld_tolerance"]; Vector<Vector3> vertices; Vector<Vector3> normals; Vector<Vector2> uvs; String name; Map<String, Map<String, Ref<SpatialMaterial> > > material_map; Ref<SurfaceTool> surf_tool = memnew(SurfaceTool); surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES); String current_material_library; String current_material; String current_group; while (true) { String l = f->get_line().strip_edges(); if (l.begins_with("v ")) { //vertex Vector<String> v = l.split(" ", false); ERR_FAIL_COND_V(v.size() < 4, NULL); Vector3 vtx; vtx.x = v[1].to_float(); vtx.y = v[2].to_float(); vtx.z = v[3].to_float(); vertices.push_back(vtx); } else if (l.begins_with("vt ")) { //uv Vector<String> v = l.split(" ", false); ERR_FAIL_COND_V(v.size() < 3, NULL); Vector2 uv; uv.x = v[1].to_float(); uv.y = 1.0 - v[2].to_float(); uvs.push_back(uv); } else if (l.begins_with("vn ")) { //normal Vector<String> v = l.split(" ", false); ERR_FAIL_COND_V(v.size() < 4, NULL); Vector3 nrm; nrm.x = v[1].to_float(); nrm.y = v[2].to_float(); nrm.z = v[3].to_float(); normals.push_back(nrm); } else if (l.begins_with("f ")) { //vertex Vector<String> v = l.split(" ", false); ERR_FAIL_COND_V(v.size() < 4, NULL); //not very fast, could be sped up Vector<String> face[3]; face[0] = v[1].split("/"); face[1] = v[2].split("/"); ERR_FAIL_COND_V(face[0].size() == 0, NULL); ERR_FAIL_COND_V(face[0].size() != face[1].size(), NULL); for (int i = 2; i < v.size() - 1; i++) { face[2] = v[i + 1].split("/"); ERR_FAIL_COND_V(face[0].size() != face[2].size(), NULL); for (int j = 0; j < 3; j++) { int idx = j; if (!flip_faces && idx < 2) { idx = 1 ^ idx; } if (face[idx].size() == 3) { int norm = face[idx][2].to_int() - 1; if (norm < 0) norm += normals.size() + 1; ERR_FAIL_INDEX_V(norm, normals.size(), NULL); surf_tool->add_normal(normals[norm]); } if (face[idx].size() >= 2 && face[idx][1] != String()) { int uv = face[idx][1].to_int() - 1; if (uv < 0) uv += uvs.size() + 1; ERR_FAIL_INDEX_V(uv, uvs.size(), NULL); surf_tool->add_uv(uvs[uv]); } int vtx = face[idx][0].to_int() - 1; if (vtx < 0) vtx += vertices.size() + 1; ERR_FAIL_INDEX_V(vtx, vertices.size(), NULL); Vector3 vertex = vertices[vtx]; //if (weld_vertices) // vertex.snap(Vector3(weld_tolerance, weld_tolerance, weld_tolerance)); surf_tool->add_vertex(vertex); } face[1] = face[2]; } } else if (l.begins_with("s ")) { //smoothing String what = l.substr(2, l.length()).strip_edges(); if (what == "off") surf_tool->add_smooth_group(false); else surf_tool->add_smooth_group(true); } else if (/*l.begins_with("g ") ||*/ l.begins_with("usemtl ") || (l.begins_with("o ") || f->eof_reached())) { //commit group to mesh //groups are too annoying if (surf_tool->get_vertex_array().size()) { //another group going on, commit it if (normals.size() == 0) { surf_tool->generate_normals(); } if (generate_tangents && uvs.size()) { surf_tool->generate_tangents(); } surf_tool->index(); print_line("current material library " + current_material_library + " has " + itos(material_map.has(current_material_library))); print_line("current material " + current_material + " has " + itos(material_map.has(current_material_library) && material_map[current_material_library].has(current_material))); if (material_map.has(current_material_library) && material_map[current_material_library].has(current_material)) { surf_tool->set_material(material_map[current_material_library][current_material]); } mesh = surf_tool->commit(mesh); if (current_material != String()) { mesh->surface_set_name(mesh->get_surface_count() - 1, current_material.get_basename()); } else if (current_group != String()) { mesh->surface_set_name(mesh->get_surface_count() - 1, current_group); } print_line("Added surface :" + mesh->surface_get_name(mesh->get_surface_count() - 1)); surf_tool->clear(); surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES); } if (l.begins_with("o ") || f->eof_reached()) { MeshInstance *mi = memnew(MeshInstance); mi->set_name(name); mi->set_mesh(mesh); scene->add_child(mi); mi->set_owner(scene); mesh.instance(); current_group = ""; current_material = ""; } if (f->eof_reached()) { break; } if (l.begins_with("o ")) { name = l.substr(2, l.length()).strip_edges(); } if (l.begins_with("usemtl ")) { current_material = l.replace("usemtl", "").strip_edges(); } if (l.begins_with("g ")) { current_group = l.substr(2, l.length()).strip_edges(); } } else if (l.begins_with("mtllib ")) { //parse material current_material_library = l.replace("mtllib", "").strip_edges(); if (!material_map.has(current_material_library)) { Map<String, Ref<SpatialMaterial> > lib; Error err = _parse_material_library(current_material_library, lib, r_missing_deps); if (err == ERR_CANT_OPEN) { String dir = p_path.get_base_dir(); err = _parse_material_library(dir.plus_file(current_material_library), lib, r_missing_deps); } if (err == OK) { material_map[current_material_library] = lib; } } } } /* TODO, check existing materials and merge? //re-apply materials if exist for(int i=0;i<mesh->get_surface_count();i++) { String n = mesh->surface_get_name(i); if (name_map.has(n)) mesh->surface_set_material(i,name_map[n]); } */ return scene; }