/*] 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" #define AC3D_FORMAT_VERSION (0x0b) #define ZLIB_BUF_SIZE (262114) namespace moof { 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; } template inline vector< scalar, fixed > read_triplet(std::istream& stream) { vector< scalar, fixed > triplet; stream >> triplet[0] >> triplet[1] >> triplet[2]; return triplet; } 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_triplet<4>(stream); stream >> atom; materials_.back().ambient = read_triplet<3>(stream); stream >> atom; materials_.back().emissive = read_triplet<3>(stream); stream >> atom; materials_.back().specular = read_triplet<3>(stream); stream >> atom >> materials_.back().shininess; stream >> atom >> materials_.back().diffuse[3]; // transparency materials_.back().diffuse[3] = 1.0; // FIXME: temporary log_info("read material", materials_.back().name, materials_.back().diffuse); } 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(); 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); 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; log_warning("adding verts", numvert); for (int i = 0; i < numvert; ++i) { obj->verts.push_back(read_triplet<3>(stream)); log_error("vert", obj->verts.back()); } } 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"); int flags = read_hex(stream); log_info(flags); 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) { log_info("inserting face..."); //obj->faces.insert(obj->faces.begin() + material, //material_group()); obj->faces.resize(material + 1); log_info("inserted face", material, obj->faces.size()); } material_group& face = obj->faces[material]; int vert; stream >> vert; vector2 uv = read_pair(stream); face.triangles.push_back(vert); face.triangles_uv.push_back(uv); unsigned first = face.triangles.back(); vector2 first_uv = face.triangles_uv.back(); stream >> vert; uv = read_pair(stream); face.triangles.push_back(vert); face.triangles_uv.push_back(uv); stream >> vert; uv = read_pair(stream); face.triangles.push_back(vert); face.triangles_uv.push_back(uv); unsigned last = face.triangles.back(); vector2 last_uv = face.triangles_uv.back(); for (int j = 3; j < numrefs; ++j) { // first face.triangles.push_back(first); face.triangles_uv.push_back(first_uv); // last face.triangles.push_back(last); face.triangles_uv.push_back(last_uv); stream >> vert; uv = read_pair(stream); face.triangles.push_back(vert); face.triangles_uv.push_back(uv); last = face.triangles.back(); last_uv = face.triangles_uv.back(); } } } else if (atom == "kids") { for (int i = kids.size(); i > 0; --i) { if (--kids.top() <= 0) { ASSERT(obj && "should be an object"); obj = obj->parent; kids.pop(); } else break; } int numkids = 0; stream >> numkids; if (numkids > 0) kids.push(numkids); if (kids.size() > 0) { log_info("KIDS", kids.top(), "|", kids.size()); } } else { log_warning("UNKNOWN ATOM:", atom); } } while (stream); } 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); //glEnableClientState(GL_TEXTURE_COORD_ARRAY); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); std::vector::const_iterator it; for (it = objects_.begin(); it != objects_.end(); ++it) { (*it)->draw(*this, alpha); } } void mesh::set_material(int index) const { set_material(materials_[index]); } void mesh::set_material(const material& material) const { glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material.diffuse.data()); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material.ambient.data()); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material.specular.data()); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material.emissive.data()); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.shininess); //glColor(material.diffuse); } void mesh::object::draw(const mesh& mesh, scalar alpha) const { //log_info("cool", verts.size()); //{ //image::reset_binding(); //std::vector::const_iterator it; //glBegin(GL_LINE_STRIP); //for (it = verts.begin(); it != verts.end(); ++it) //{ //glVertex(*it); //} //glEnd(); //} //glPolygonMode(GL_BACK, GL_LINE); //glVertexPointer(3, GL_SCALAR, 0, verts[0].data()); if (texture) texture->bind(); else image::reset_binding(); for (size_t i = 0; i < faces.size(); ++i) { const material_group& face = faces[i]; mesh.set_material(i); //it->draw(alpha); //std::vector::const_iterator jt; int count = face.triangles.size(); for (int j = 0; j < count; j += 3) { glBegin(GL_TRIANGLES); glTexCoord(face.triangles_uv[j]); glVertex(verts[face.triangles[j]]); glTexCoord(face.triangles_uv[j+1]); glVertex(verts[face.triangles[j+1]]); glTexCoord(face.triangles_uv[j+2]); glVertex(verts[face.triangles[j+2]]); glEnd(); } } std::vector::const_iterator jt; for (jt = kids.begin(); jt != kids.end(); ++jt) { (*jt)->draw(mesh, alpha); } } void mesh::material_group::draw(scalar alpha) const { // TODO: setup material /* if (triangles.size() > 0) { //log_info("drawing triangles:", triangles.size()/3); glTexCoordPointer(2, GL_SCALAR, 0, triangles_uv[0].data()); glDrawElements(GL_TRIANGLES, triangles.size(), GL_UNSIGNED_INT, &triangles[0]); } */ } 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