/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * **************************************************************************/ #include #include #include #include #include #include #include #include #include "debug.hh" #include "log.hh" #include "mesh.hh" #include "opengl.hh" // TODO: this file needs to be cleaned up #define AC3D_FORMAT_VERSION (0x0b) #define ZLIB_BUF_SIZE (262114) namespace moof { MOOF_REGISTER_RESOURCE(mesh, ac, models); static std::string read_string(std::istream& stream) { std::string str; char atom; do // skip to the next non-space character { stream.get(atom); } while (stream && std::isspace(atom)); if (atom == '"') { do { stream.get(atom); if (atom == '"') break; str += atom; } while (stream); } else { do { stream.get(atom); if (std::isspace(atom)) break; str += atom; } while (stream); } return str; } inline int read_hex(std::istream& stream) { int hex; std::ios::fmtflags flags = stream.flags(); stream.setf(std::ios::hex, std::ios::basefield); stream >> hex; stream.flags(flags); return hex; } inline vector2 read_pair(std::istream& stream) { vector2 triplet; stream >> triplet[0] >> triplet[1]; return triplet; } inline vector3 read_triplet(std::istream& stream) { vector3 triplet; stream >> triplet[0] >> triplet[1] >> triplet[2]; return triplet; } inline vector4 read_color(std::istream& stream) { vector4 color; stream >> color[0] >> color[1] >> color[2]; color[3] = SCALAR(1.0); return color; } void mesh::import(std::istream& stream) { std::string atom; object_ptr obj; std::stack kids; // read and verify the AC3D header { char magic[5]; unsigned version = 0; stream.get(magic, sizeof(magic)); if (!stream || strncmp(magic, "AC3D", 4) != 0) { throw std::runtime_error("invalid mesh header"); } version = read_hex(stream); if (version > AC3D_FORMAT_VERSION) { throw std::runtime_error("wrong mesh file format version"); } } while (stream) { stream >> atom; if (!stream) break; if (atom == "MATERIAL") { materials_.push_back(material(read_string(stream))); stream >> atom; materials_.back().diffuse = read_color(stream); stream >> atom; materials_.back().ambient = read_color(stream); stream >> atom; materials_.back().emissive = read_color(stream); stream >> atom; materials_.back().specular = read_color(stream); stream >> atom >> materials_.back().shininess; stream >> atom >> materials_.back().diffuse[3]; materials_.back().diffuse[3] = SCALAR(1.0) - materials_.back().diffuse[3]; } else if (atom == "OBJECT") { stream >> atom; if (atom != "world" && atom != "group" && atom != "poly") { throw std::runtime_error("unexpected object type " + atom); } object_ptr newObj = object::alloc(*this); if (obj) { obj->kids.push_back(newObj); newObj->parent = obj; } else { objects_.push_back(newObj); } obj = newObj; } else if (atom == "name") { if (obj) { obj->name = read_string(stream); object_ptr parent = obj->parent.lock(); if (parent) parent->kids_byname.insert(std::make_pair(obj->name, obj)); } else throw std::runtime_error("unexpected atom: " + atom); } else if (atom == "data") { std::getline(stream, atom); std::getline(stream, obj ? obj->data : atom); } else if (atom == "texture") { if (obj) obj->texture = resource::load(read_string(stream)); } else if (atom == "texrep") { if (obj) obj->texrep = read_pair(stream); else throw std::runtime_error("unexpected atom: " + atom); } else if (atom == "rot") { // TODO std::getline(stream, atom); } else if (atom == "loc") { // TODO std::getline(stream, atom); } else if (atom == "url") { if (obj) std::getline(stream, obj->url); else throw std::runtime_error("unexpected atom: " + atom); } else if (atom == "numvert") { if (!obj) throw std::runtime_error("unexpected atom: " + atom); int numvert = 0; stream >> numvert; for (int i = 0; i < numvert; ++i) { obj->verts.push_back(read_triplet(stream)); } } else if (atom == "numsurf") { if (!obj) throw std::runtime_error("unexpected atom: " + atom); int numsurf = 0; stream >> numsurf; for (int i = 0; i < numsurf; ++i) { stream >> atom; if (atom != "SURF") throw std::runtime_error("uh oh"); read_hex(stream); int material = 0; stream >> atom; if (atom == "mat") stream >> material >> atom; if (atom != "refs") { throw std::runtime_error("blaaaaaaaahhh!!"); } int numrefs = 0; stream >> numrefs; ASSERT(numrefs >= 3); if ((int)obj->faces.size() <= material) { obj->faces.resize(material + 1); } material_group& face = obj->faces[material]; unsigned vert; stream >> vert; vector2 uv = read_pair(stream); if (vert < face.triangles_uv.size()) { if (uv != face.triangles_uv[vert]) { obj->verts.push_back(obj->verts[vert]); face.triangles_uv.resize(obj->verts.size()); vert = obj->verts.size() - 1; } } else face.triangles_uv.resize(vert + 1); face.triangles_uv[vert] = uv; face.triangles.push_back(vert); unsigned first = vert; stream >> vert; uv = read_pair(stream); if (vert < face.triangles_uv.size()) { if (uv != face.triangles_uv[vert]) { obj->verts.push_back(obj->verts[vert]); face.triangles_uv.resize(obj->verts.size()); vert = obj->verts.size() - 1; } } else face.triangles_uv.resize(vert + 1); face.triangles_uv[vert] = uv; face.triangles.push_back(vert); stream >> vert; uv = read_pair(stream); if (vert < face.triangles_uv.size()) { if (uv != face.triangles_uv[vert]) { obj->verts.push_back(obj->verts[vert]); face.triangles_uv.resize(obj->verts.size()); vert = obj->verts.size() - 1; } } else face.triangles_uv.resize(vert + 1); face.triangles_uv[vert] = uv; face.triangles.push_back(vert); unsigned last = vert; for (int j = 3; j < numrefs; ++j) { face.triangles.push_back(first); face.triangles.push_back(last); stream >> vert; uv = read_pair(stream); if (vert < face.triangles_uv.size()) { if (uv != face.triangles_uv[vert]) { obj->verts.push_back(obj->verts[vert]); face.triangles_uv.resize(obj->verts.size()); vert = obj->verts.size() - 1; } } else face.triangles_uv.resize(vert + 1); face.triangles_uv[vert] = uv; face.triangles.push_back(vert); last = face.triangles.back(); } } } else if (atom == "kids") { //while (0 < kids.size()) //{ //if (--kids.top() <= 0) //{ //ASSERT(obj && "should be an object"); //obj = obj->parent; //kids.pop(); //} //else break; //} int numkids = 0; stream >> numkids; if (0 < numkids) kids.push(numkids); else { if (0 < kids.size() && 0 < --kids.top()) kids.pop(); obj = obj->parent.lock(); } } else { log_warning("UNKNOWN ATOM:", atom); } } while (stream); std::vector::iterator meh; for (meh = objects_.begin(); meh != objects_.end(); ++meh) { object_ptr cow = *meh; log_info("OBJ: -", cow->name, cow->kids.size()); std::vector::iterator foo; for (foo = cow->kids.begin(); foo != cow->kids.end(); ++foo) { log_info("OBJ: -", (*foo)->name, (*foo)->kids.size()); } } } //unsigned mesh::read_vertex_line(std::istream& stream) //{ //unsigned vert; //stream >> vert; //vector2 uv = read_pair(stream); //if (vert < face.triangles_uv.size()) //{ //if (uv != face.triangles_uv[vert]) //{ //obj->verts.push_back(obj->verts[vert]); //face.triangles_uv.resize(obj->verts.size()); //vert = obj->verts.size() - 1; //} //} //else face.triangles_uv.resize(vert + 1); //face.triangles_uv[vert] = uv; //face.triangles.push_back(vert); //} mesh::mesh(const std::string& path) { std::ifstream file(path.c_str(), std::ifstream::in | std::ifstream::binary); if (!file) throw std::runtime_error("cannot find mesh file"); // if we can read the header, the file isn't compressed char magic[5]; file.get(magic, sizeof(magic)); if (strncmp(magic, "AC3D", 4) == 0) { log_info("text mesh detected"); file.seekg(std::ios::beg); import(file); } else { log_info("compressed mesh detected"); file.seekg(std::ios::beg); std::stringstream stream; char in[ZLIB_BUF_SIZE]; char out[ZLIB_BUF_SIZE]; z_stream zstream; zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; zstream.avail_in = 0; zstream.next_in = Z_NULL; int result = inflateInit2(&zstream, 32+MAX_WBITS); if (result != Z_OK) throw std::runtime_error("zlib init error"); do { file.read(in, sizeof(in)); zstream.next_in = (Bytef*)in; zstream.avail_in = file.gcount(); if (zstream.avail_in == 0) break; do { zstream.next_out = (Bytef*)out; zstream.avail_out = sizeof(out); result = inflate(&zstream, Z_NO_FLUSH); switch (result) { case Z_NEED_DICT: case Z_DATA_ERROR: case Z_MEM_ERROR: inflateEnd(&zstream); throw std::runtime_error("zlib inflate error"); case Z_STREAM_ERROR: throw std::runtime_error("zlib stream error"); } int inflated = sizeof(out) - zstream.avail_out; stream.write(out, inflated); } while (zstream.avail_out == 0); } while(result != Z_STREAM_END); inflateEnd(&zstream); import(stream); } } void mesh::draw(scalar alpha) const { glEnableClientState(GL_VERTEX_ARRAY); std::vector::const_iterator it; for (it = objects_.begin(); it != objects_.end(); ++it) { (*it)->draw(alpha); } // TODO: disable vertex array? } void mesh::set_material(int index) const { set_material(materials_[index]); } void mesh::set_material(const material& material) const { glColor(material.diffuse); glMaterial(GL_FRONT, GL_DIFFUSE, material.diffuse); glMaterial(GL_FRONT, GL_AMBIENT, material.ambient); glMaterial(GL_FRONT, GL_SPECULAR, material.specular); glMaterial(GL_FRONT, GL_EMISSION, material.emissive); glMaterial(GL_FRONT, GL_SHININESS, material.shininess); } void mesh::object::draw(scalar alpha, bool recurse) const { glVertexPointer(verts); if (texture) { texture->bind(); glEnableClientState(GL_TEXTURE_COORD_ARRAY); } else { image::reset_binding(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } for (size_t i = 0; i < faces.size(); ++i) { const material_group& face = faces[i]; if (face.triangles.size() == 0) continue; mesh.set_material(i); if (texture) glTexCoordPointer(face.triangles_uv); glDrawElements(GL_TRIANGLES, face.triangles); } if (recurse) { std::vector::const_iterator jt; for (jt = kids.begin(); jt != kids.end(); ++jt) { (*jt)->draw(alpha); } } } //class mesh_resource_loader //{ //public: //mesh_resource_loader() //{ //resource::register_type("ac", "models"); //} //~mesh_resource_loader() //{ //resource::unregister_type("ac"); //} //}; //static mesh_resource_loader loader; } // namespace moof