/* model/objfile.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include "sys/sys.h" #include "model/objfile.h" #include #include /* NOTE: Does NOT support reverse indexes. Cannot find an example of this in use, or a program that will export using them. */ namespace model { // max geometry bounds const float MAX_BOUNDS = 16384.0f; OBJFile::OBJFile(std::string const &name) { objfile_name.append(name); objfile_name.append(".obj"); objfile_ifs.open(objfile_name); obj_box.assign(MAX_BOUNDS, - MAX_BOUNDS); // a single fragmentgroup will contain all the model polygons obj_fragmentgroup = new FragmentGroup(); obj_fragmentgroup->set_type(FragmentGroup::None); // reset counters obj_normalcount = 0; } OBJFile::~OBJFile() { for (VertexList::iterator it = obj_vertexlist.begin(); it != obj_vertexlist.end(); ++it) { delete(*it).second; } obj_vertexlist.clear(); for (NormalList::iterator it = obj_normallist.begin(); it != obj_normallist.end(); ++it) { delete(*it).second; } obj_normallist.clear(); for (UVList::iterator it = obj_uvlist.begin(); it != obj_uvlist.end(); ++it) { delete(*it).second; } obj_uvlist.clear(); for (TriList::iterator it = obj_trilist.begin(); it != obj_trilist.end(); ++it) { delete(*it).second; } obj_trilist.clear(); for (QuadList::iterator it = obj_quadlist.begin(); it != obj_quadlist.end(); ++it) { delete(*it).second; } obj_quadlist.clear(); if (objfile_ifs.is_open()) objfile_ifs.close(); } math::Vector3f * OBJFile::find_vertex(size_t index) { VertexList::iterator it = obj_vertexlist.find(index); if (it == obj_vertexlist.end()) { return 0; } else { return (*it).second; } } math::Vector3f *OBJFile::find_normal(size_t index) { NormalList::iterator it = obj_normallist.find(index); if (it == obj_normallist.end()) { return 0; } else { return (*it).second; } } math::Vector2f *OBJFile::find_uv(size_t index) { UVList::iterator it = obj_uvlist.find(index); if (it == obj_uvlist.end()) { return 0; } else { return (*it).second; } } bool OBJFile::read() { char data[1024]; memset(data, 0, sizeof(data)); math::Vector3f zero; size_t current_m = 0; size_t index_line = 0; Fragment *fragment = 0; Material *material = 0; while (objfile_ifs.getline(data, sizeof(data) - 1)) { std::istringstream line(data); index_line++; std::string word; line >> word; if( word.compare("usemtl") == 0) { /* material definition */ std::string materialname; material = 0; if (line >> materialname) { for(MaterialList::iterator it = obj_materiallist.begin(); it != obj_materiallist.end(); ++it) { if( (*it).second->name() == materialname) { material = (*it).second; current_m = (*it).first; break; } } if (!material) { material = Material::load(materialname); obj_materiallist[obj_materiallist.size()] = material; current_m = obj_materiallist.size()-1; } } else con_warn << objfile_name << " invalid material definition at line " << index_line << std::endl; } else if (word.compare("v") == 0) { /* new wertex */ float x, y, z; line >> x >> y >> z; math::Vector3f *v = new math::Vector3f(x, y, z); obj_vertexlist[obj_vertexlist.size()] = v; obj_box.expand(*v * SCALE); } else if (word.compare("vt") == 0) { /* wertex texture coordinate */ float u, v; line >> u >> v; math::Vector2f *uv = new math::Vector2f(u, v); obj_uvlist[obj_uvlist.size()] = uv; } else if (word.compare("vn") == 0) { /* wertex wormal */ float x, y, z; line >> x >> y >> z; math::Vector3f *nm = new math::Vector3f(x, y, z); nm->normalize(); obj_normallist[obj_normalcount] = nm; obj_normalcount++; } else if (word.compare("f") == 0) { /* face/polygon */ size_t v[4]; size_t uv[4]; size_t vn[4]; bool have_vn[4], have_uv[4]; size_t nslash; for (size_t j = 0; j < 4; j++) { v[j] = 0; uv[j] = 0; vn[j] = 0; have_vn[j] = false; have_uv[j] = false; } std::string strbuf(line.str()); strbuf.erase(0,2); //remove "f " std::stringstream tmp(strbuf); std::vector points; int i=0; while(!tmp.eof()) { nslash = 0; have_vn[i] = 0; have_uv[i] = 0; std::string point; if (!(tmp >> point)) // bordercase for space at end of face definition continue; for(size_t c=0; c> v[i]; if(have_uv[i]) tmp2 >> uv[i]; if(have_vn[i]) tmp2 >> vn[i]; i++; } if(points.size() != 4 && points.size() != 3) { con_warn << objfile_name << " face with " << points.size() << " vertices at line " << index_line << std::endl; points.clear(); continue; } if(points.size() == 3) { math::Vector3f *v0 = find_vertex(v[0]-1); math::Vector3f *v1 = find_vertex(v[1]-1); math::Vector3f *v2 = find_vertex(v[2]-1); math::Vector3f *n0 = find_normal(vn[0]-1); math::Vector3f *n1 = find_normal(vn[1]-1); math::Vector3f *n2 = find_normal(vn[2]-1); math::Vector2f *t0 = find_uv(uv[0]-1); math::Vector2f *t1 = find_uv(uv[1]-1); math::Vector2f *t2 = find_uv(uv[2]-1); if(!v0 || !v1 || !v2) { con_warn << objfile_name << " invalid vertex called from line " << index_line << std::endl; continue; } Triangle *triangle = new Triangle(*v0, *v1, *v2); if(!n0) { n0 = new math::Vector3f(normal(triangle->v0(), triangle->v1(), triangle->v2())); obj_normallist[vn[0]-1] = n0; } if(!n1) { n1 = new math::Vector3f(normal(triangle->v1(), triangle->v2(), triangle->v0())); obj_normallist[vn[1]-1] = n1; } if(!n2) { n2 = new math::Vector3f(normal(triangle->v2(), triangle->v0(), triangle->v1())); obj_normallist[vn[2]-1] = n2; } triangle->n0().assign(n0->x(), n0->y(), n0->z()); triangle->n1().assign(n1->x(), n1->y(), n1->z()); triangle->n2().assign(n2->x(), n2->y(), n2->z()); if(t0) triangle->t0().assign(t0->x(), 1.0 - t0->y()); if(t1) triangle->t1().assign(t1->x(), 1.0 - t1->y()); if(t2) triangle->t2().assign(t2->x(), 1.0 - t2->y()); std::pair poly(current_m, triangle); obj_trilist.push_back(poly); } else if(points.size() == 4) { math::Vector3f *v0 = find_vertex(v[0]-1); math::Vector3f *v1 = find_vertex(v[1]-1); math::Vector3f *v2 = find_vertex(v[2]-1); math::Vector3f *v3 = find_vertex(v[3]-1); math::Vector3f *n0 = find_normal(vn[0]-1); math::Vector3f *n1 = find_normal(vn[1]-1); math::Vector3f *n2 = find_normal(vn[2]-1); math::Vector3f *n3 = find_normal(vn[3]-1); math::Vector2f *t0 = find_uv(uv[0]-1); math::Vector2f *t1 = find_uv(uv[1]-1); math::Vector2f *t2 = find_uv(uv[2]-1); math::Vector2f *t3 = find_uv(uv[3]-1); if(!v0 || !v1 || !v2 || !v3) { con_warn << objfile_name << " invalid vertex called from line " << index_line << std::endl; continue; } Quad *quad = new Quad(*v0, *v1, *v2, *v3, zero); if(!n0) { n0 = new math::Vector3f(normal(quad->v0(), quad->v1(), quad->v3())); obj_normallist[vn[0]-1] = n0; } if(!n1) { n1 = new math::Vector3f(normal(quad->v1(), quad->v2(), quad->v0())); obj_normallist[vn[1]-1] = n1; } if(!n2) { n2 = new math::Vector3f(normal(quad->v2(), quad->v3(), quad->v1())); obj_normallist[vn[2]-1] = n2; } if(!n3) { n2 = new math::Vector3f(normal(quad->v3(), quad->v0(), quad->v2())); obj_normallist[vn[3]-1] = n3; } quad->n0().assign(n0->x(), n0->y(), n0->z()); quad->n1().assign(n1->x(), n1->y(), n1->z()); quad->n2().assign(n2->x(), n2->y(), n2->z()); quad->n3().assign(n3->x(), n3->y(), n3->z()); if(t0) quad->t0().assign(t0->x(), 1.0 - t0->y()); if(t1) quad->t1().assign(t1->x(), 1.0 - t1->y()); if(t2) quad->t2().assign(t2->x(), 1.0 - t2->y()); if(t3) quad->t3().assign(t3->x(), 1.0 - t3->y()); std::pair poly(current_m, quad); obj_quadlist.push_back(poly); } } } //Go through tri and quad lists and create fragments for each material size_t total_m = obj_materiallist.size(); for(size_t i = 0; i <= total_m; i++) { material = obj_materiallist[i]; fragment = new Fragment(Fragment::Triangles, material); for(TriList::iterator it = obj_trilist.begin(); it != obj_trilist.end(); ++it) { if((*it).first == i) { fragment->add_vertex(((*it).second->v0() * SCALE) , (*it).second->n0(), (*it).second->t0(), false); fragment->add_vertex(((*it).second->v1() * SCALE) , (*it).second->n1(), (*it).second->t1(), false); fragment->add_vertex(((*it).second->v2() * SCALE) , (*it).second->n2(), (*it).second->t2(), false); } } if( fragment->structural_size() + fragment->detail_size() > 0 ) obj_fragmentgroup->add_fragment(fragment); fragment = new Fragment(Fragment::Quads, material); for(QuadList::iterator it = obj_quadlist.begin(); it != obj_quadlist.end(); ++it) { if((*it).first == i) { fragment->add_vertex(((*it).second->v0() * SCALE) , (*it).second->n0(), (*it).second->t0(), false); fragment->add_vertex(((*it).second->v1() * SCALE) , (*it).second->n1(), (*it).second->t1(), false); fragment->add_vertex(((*it).second->v2() * SCALE) , (*it).second->n2(), (*it).second->t2(), false); fragment->add_vertex(((*it).second->v3() * SCALE) , (*it).second->n3(), (*it).second->t3(), false); } } if( fragment->structural_size() + fragment->detail_size() > 0 ) obj_fragmentgroup->add_fragment(fragment); } return true; } Model *OBJFile::load(const std::string &name) { OBJFile objfile(name); if (!objfile.is_open()) { return 0; } if (!objfile.read()) { return 0; } if (!objfile.fragmentgroup()->size()) return 0; // create a new model Model *model = new Model(name); // center model around (0,0,0) and set the bounding box math::Vector3f obj_center((objfile.box().min() + objfile.box().max()) * 0.5f); model->model_box.assign( objfile.box().min() - obj_center, objfile.box().max() - obj_center ); model->set_radius(model->box().max().length()); model->set_origin(obj_center * -1.0f); objfile.fragmentgroup()->set_location(obj_center * -1.0f); for (FragmentGroup::Fragments::const_iterator fit = objfile.fragmentgroup()->fragments().begin(); fit != objfile.fragmentgroup()->fragments().end(); fit++) { const Fragment *fragment = (*fit); if(fragment->type() == Fragment::Triangles) model->model_tris_count += (fragment->structural_size() + fragment->detail_size()) / 3; else model->model_quad_count += (fragment->structural_size() + fragment->detail_size()) / 4; } model->add_group(objfile.fragmentgroup()); con_debug << " " << objfile.name() << " " << objfile.vertexcount() << " vertices " << model->model_tris_count << " triangles " << model->model_quad_count << " quads" << std::endl; return model; } } // namespace model