/* model/asefile.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/asefile.h" #include #include namespace model { // max geometry bounds const float MAX_BOUNDS = 16384.0f; ASEFile::ASEFile(std::string const &name) { asefile_name.append(name); asefile_name.append(".ase"); asefile_ifs.open(asefile_name); for (int i = 0; i < 3; i++) { ase_minbbox[i] = 0; ase_maxbbox[i] = 0; } // a single fragmentgroup wil contain all the model triangles ase_fragmentgroup = new FragmentGroup(); ase_fragmentgroup->set_type(FragmentGroup::None); // reset counters ase_vertexcount = 0; ase_facecount = 0 ; } ASEFile::~ASEFile() { for (VertexList::iterator it = ase_tvertexlist.begin(); it != ase_tvertexlist.end(); it++) { delete(*it).second; } ase_tvertexlist.clear(); for (VertexList::iterator it = ase_vertexlist.begin(); it != ase_vertexlist.end(); it++) { delete(*it).second; } ase_vertexlist.clear(); for (FaceList::iterator it = ase_facelist.begin(); it != ase_facelist.end(); it++) { delete(*it).second; } ase_facelist.clear(); for (MaterialList::iterator it = ase_materials.begin(); it != ase_materials.end(); it++) { SubMaterialList *submaterials = (*it).second; for (SubMaterialList::iterator smit = submaterials->begin(); smit != submaterials->end(); smit++) { (*smit).second = 0; } submaterials->clear(); delete submaterials; (*it).second = 0; } ase_materials.clear(); if (asefile_ifs.is_open()) asefile_ifs.close(); } bool ASEFile::read_header(std::istream &is) { if (!is.good()) { return false; } char data[1024]; memset(data, 0, sizeof(data)); if (!is.getline(data, sizeof(data) - 1)) { return false; } std::istringstream line(data); std::string word; line >> word; if (!word.compare("*3DSMAX_ASCIIEXPORT") == 0) { return false; } return true; } Material *ASEFile::read_submaterial(std::istream &is) { Material *material = 0; char data[1024]; memset(data, 0, sizeof(data)); int level = 1; while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string word; line >> word; if ((level == 1) && (word.compare("*MATERIAL_NAME") == 0)) { // read material name std::string n; char c; while ((line.get(c)) && (c != '"')); while ((line.get(c)) && (c != '"')) n += c; // find material material = Material::find(n); if (!material) { material = new Material(n); Material::add(material); material->set_texture(material->name()); } } else { do { if (word.compare("{") == 0) { level++; } else if (word.compare("}") == 0) { level--; } } while (line >> word); } if (level == 0) { return material; } } return material; } ASEFile::SubMaterialList *ASEFile::read_material(std::istream &is) { SubMaterialList *submaterials = new SubMaterialList; std::string materialname; char data[1024]; memset(data, 0, sizeof(data)); int level = 1; size_t index = 0; while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string word; line >> word; if ((level == 1) && (word.compare("*MATERIAL_NAME") == 0)) { // read material name std::string n; char c; while ((line.get(c)) && (c != '"')); while ((line.get(c)) && (c != '"')) n += c; materialname.assign(n); } else if ((level == 1) && (word.compare("*SUBMATERIAL") == 0)) { if ((line >> index) && (line >> word) && (word.compare("{") == 0)) { // this material defines submaterials, we ignore the parent definition materialname.clear(); // add material to the submaterial list (*submaterials)[index] = read_submaterial(is); } } else { do { if (word.compare("{") == 0) { level++; } else if (word.compare("}") == 0) { level--; } } while (line >> word); } if (level == 0) { if (materialname.size()) { // no submaterials, add a single material to the submaterial list Material *material = Material::find(materialname); if (!material) { material = new Material(materialname); Material::add(material); material->set_texture(materialname); } (*submaterials)[0] = material; } return submaterials; } } return submaterials; } bool ASEFile::read_material_list(std::istream &is) { char data[1024]; memset(data, 0, sizeof(data)); int level = 1; while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string word; size_t index; line >> word; if ((level == 1) && (word.compare("*MATERIAL") == 0)) { if ((line >> index) && (line >> word) && (word.compare("{") == 0)) { // add material to the ase material list ase_materials[index] = read_material(is); //con_debug << " " << name() << " " << "*MATERIAL " << index << " " << ase_materials[index]->name() << std::endl; } } else { do { if (word.compare("{") == 0) { level++; } else if (word.compare("}") == 0) { level--; } } while (line >> word); } if (level == 0) return true; } return false; } bool ASEFile::read_mesh_vertex_list(std::istream &is) { char data[1024]; memset(data, 0, sizeof(data)); while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string firstword; line >> firstword; if (firstword.compare("}") == 0) { return true; } else if (firstword.compare("*MESH_VERTEX") == 0) { size_t index; float x, y, z; if (line >> index >> x >> y >> z) { math::Vector3f *v = new math::Vector3f(x, y, z); ase_vertexlist[index] = v; for (size_t i = 0; i < 3; i++) { if ((*v)[i] > ase_maxbbox[i]) { ase_maxbbox[i] = (*v)[i]; } if ((*v)[i] < ase_minbbox[i]) { ase_minbbox[i] = (*v)[i]; } } ase_vertexcount++; } } } return false; } bool ASEFile::read_mesh_face_list(std::istream &is) { char data[1024]; memset(data, 0, sizeof(data)); while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string word; line >> word; if (word.compare("}") == 0) { return true; } else if (word.compare("*MESH_FACE") == 0) { std::string facestr; size_t a, b, c; if ((line >> facestr) && (line >> word) && (line >> a) && (line >> word) && (line >> b) && (line >> word) && (line >> c)) { size_t index; if (facestr.size() && facestr[facestr.size()-1] == ':') { facestr.erase(facestr.size() - 1); } std::istringstream faceindexstr(facestr); if (faceindexstr >> index) { Triangle *triangle = new Triangle(*ase_vertexlist[a], *ase_vertexlist[b], *ase_vertexlist[c]); ase_facelist[index] = triangle; while (line >> word) { if (word.compare("*MESH_MTLID") == 0) { size_t submaterial_index = 0; if (line >> submaterial_index) { triangle->set_material_index(submaterial_index); } } } } } ase_facecount++; } } return false; } bool ASEFile::read_mesh_normals(std::istream &is) { char data[1024]; memset(data, 0, sizeof(data)); size_t count = 0; size_t index = 0; size_t vertindex = 0; float x, y, z; FaceList::iterator it; while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string firstword; line >> firstword; if (firstword.compare("}") == 0) { //con_debug << " " << count << " face normals" << std::endl; return true; } else if (firstword.compare("*MESH_FACENORMAL") == 0) { if (line >> index >> x >> y >> z) { it = ase_facelist.find(index); if (it != ase_facelist.end()) { (*it).second->normal().assign(x, y, z); count++; } else { con_warn << " could not find face " << index << std::endl; } vertindex = 0; } else { it = ase_facelist.end(); } } else if (firstword.compare("*MESH_VERTEXNORMAL") == 0) { if ((it != ase_facelist.end()) && (line >> index >> x >> y >> z)) { if (vertindex == 0) { (*it).second->n0().assign(x, y, z); } else if (vertindex == 1) { (*it).second->n1().assign(x, y, z); } else if (vertindex == 2) { (*it).second->n2().assign(x, y, z); } vertindex++; } } } return false; } bool ASEFile::read_mesh_tvertex_list(std::istream &is) { size_t count = 0; char data[1024]; memset(data, 0, sizeof(data)); while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string firstword; line >> firstword; if (firstword.compare("}") == 0) { //con_debug << " " << count << " texture vertices" << std::endl; return true; } else if (firstword.compare("*MESH_TVERT") == 0) { size_t index; float x, y, z; if (line >> index >> x >> y >> z) { math::Vector3f *v = new math::Vector3f(x, y, z); ase_tvertexlist[index] = v; count++; } } } return false; } bool ASEFile::read_mesh_tface_list(std::istream &is) { size_t count = 0; char data[1024]; memset(data, 0, sizeof(data)); while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string firstword; line >> firstword; if (firstword.compare("}") == 0) { //con_debug << " " << count << " face texture coordinates" << std::endl; return true; } else if (firstword.compare("*MESH_TFACE") == 0) { // face texture coordinates size_t index, a, b, c; if (line >> index >> a >> b >> c) { Triangle *triangle = ase_facelist[index]; math::Vector3f *t0 = ase_tvertexlist[a]; math::Vector3f *t1 = ase_tvertexlist[b]; math::Vector3f *t2 = ase_tvertexlist[c]; if (triangle) { if (t0) triangle->t0().assign(t0->x(), 1.0f - t0->y()); if (t1) triangle->t1().assign(t1->x(), 1.0f - t1->y()); if (t2) triangle->t2().assign(t2->x(), 1.0f - t2->y()); } count++; } } } return false; } bool ASEFile::read_mesh(std::istream &is) { char data[1024]; memset(data, 0, sizeof(data)); int level = 1; ase_vertexlist.clear(); while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string word; line >> word; if ((level == 1) && (word.compare("*MESH_VERTEX_LIST") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { //con_debug << " " << name() << " *MESH_VERTEX_LIST" << std::endl; read_mesh_vertex_list(is); } } else if ((level == 1) && (word.compare("*MESH_FACE_LIST") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { //con_debug << " " << name() << " *MESH_FACE_LIST" << std::endl; read_mesh_face_list(is); } } else if ((level == 1) && (word.compare("*MESH_NORMALS") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { //con_debug << " " << name() << " *MESH_NORMALS" << std::endl; read_mesh_normals(is); } } else if ((level == 1) && (word.compare("*MESH_TVERTLIST") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { //con_debug << " " << name() << " *MESH_TVERTLIST" << std::endl; read_mesh_tvertex_list(is); } } else if ((level == 1) && (word.compare("*MESH_TFACELIST") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { //con_debug << " " << name() << " *MESH_TFACELIST" << std::endl; read_mesh_tface_list(is); } } else { do { if (word.compare("{") == 0) { level++; } else if (word.compare("}") == 0) { level--; } } while (line >> word); } if (level == 0) return true; } return false; } bool ASEFile::clear_geom() { // delete texture vertices for (VertexList::iterator it = ase_tvertexlist.begin(); it != ase_tvertexlist.end(); it++) { delete(*it).second; } ase_tvertexlist.clear(); // delete geometry vertices for (VertexList::iterator it = ase_vertexlist.begin(); it != ase_vertexlist.end(); it++) { delete(*it).second; } ase_vertexlist.clear(); // delete faces for (FaceList::iterator it = ase_facelist.begin(); it != ase_facelist.end(); it++) { delete(*it).second; } ase_facelist.clear(); return true; } bool ASEFile::read_geom(std::istream &is) { char data[1024]; memset(data, 0, sizeof(data)); int level = 1; SubMaterialList *submateriallist = 0; while (is.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string word; size_t index; line >> word; if ((level == 1) && (word.compare("*MESH") == 0)) { if ((line >> word) && (word.compare("{") == 0)) { //con_debug << " " << name() << " " << "*MESH" << std::endl; read_mesh(is); } } else if ((level == 1) && (word.compare("*MATERIAL_REF") == 0)) { // the material to use for this GEOMOBJECT if (line >> index) { //con_debug << " " << name() << " " << "*MATERIAL_REF " << index << std::endl; // find the Material for index MaterialList::iterator it = ase_materials.find(index); if (it != ase_materials.end()) { submateriallist = (*it).second; } else { submateriallist = 0; } } } else { do { if (word.compare("{") == 0) { level++; } else if (word.compare("}") == 0) { level--; } } while (line >> word); } if (level == 0) { // import only if the material is defined and there actually are triangles to import if (submateriallist && ase_facelist.size()) { // iterate submaterial list, create a fragment per material for (SubMaterialList::iterator smit = submateriallist->begin(); smit != submateriallist->end(); smit++) { size_t submaterial_index = (*smit).first; Material *material = (*smit).second; if ((material->flags() & Material::Ignore) != Material::Ignore ) { // load GEOMOBJECT triangles with matching material into the fragment Fragment *fragment = new Fragment(Fragment::Triangles, material); for (FaceList::iterator it = ase_facelist.begin(); it != ase_facelist.end(); it++) { Triangle *triangle = (*it).second; if (triangle->material_index() == submaterial_index) { fragment->add_vertex((triangle->v0() * SCALE) , triangle->n0(), triangle->t0(), false); fragment->add_vertex((triangle->v1() * SCALE) , triangle->n1(), triangle->t1(), false); fragment->add_vertex((triangle->v2() * SCALE) , triangle->n2(), triangle->t2(), false); } } if (fragment->structural_size() + fragment->detail_size() > 0) { ase_fragmentgroup->add_fragment(fragment); } else { delete fragment; } } } } // clear geometry data clear_geom(); return true; } } clear_geom(); return false; } bool ASEFile::read() { if (!read_header(asefile_ifs)) { con_warn << name() << ": not a valid ASE file!\n"; return 0; } char data[1024]; memset(data, 0, sizeof(data)); while (asefile_ifs.getline(data, sizeof(data) - 1)) { std::istringstream line(data); std::string word; line >> word; if (word.compare("*GEOMOBJECT") == 0) { if ((line >> word) && (word.compare("{") == 0)) { //con_debug << " " << name() << " " << "*GEOMOBJECT" << std::endl; read_geom(asefile_ifs); } } else if (word.compare("*MATERIAL_LIST") == 0) { if ((line >> word) && (word.compare("{") == 0)) { //con_debug << " " << name() << " " << "*MATERIAL_LIST" << std::endl; read_material_list(asefile_ifs); } } } return true; } Model *ASEFile::load(const std::string &name) { ASEFile asefile(name); if (!asefile.is_open()) { return 0; } if (!asefile.read()) { return 0; } if (!asefile.fragmentgroup()->size()) return 0; // create a new model Model *model = new Model(name); // set bounding box properties asefile.ase_minbbox *= SCALE; asefile.ase_maxbbox *= SCALE; math::Vector3f ase_center((asefile.ase_maxbbox + asefile.ase_minbbox) * 0.5f); asefile.fragmentgroup()->set_transform(true); asefile.fragmentgroup()->set_location(ase_center * -1.0f); model->model_minbbox.assign(asefile.ase_minbbox - ase_center); model->model_maxbbox.assign(asefile.ase_maxbbox - ase_center); model->set_radius(asefile.ase_maxbbox.length()); model->set_origin(ase_center); for (FragmentGroup::Fragments::const_iterator fit = asefile.fragmentgroup()->fragments().begin(); fit != asefile.fragmentgroup()->fragments().end(); fit++) { const Fragment *fragment = (*fit); model->model_tris_count += fragment->structural_size() + fragment->detail_size(); } model->add_group(asefile.fragmentgroup()); con_debug << " " << asefile.name() << " " << asefile.vertexcount() << " vertices " << asefile.facecount() << " faces " << std::endl; return model; } } // namespace model