X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fmesh.cc;h=eac6ef9151a74fb25767613d6f6ce847f1fa3c4b;hp=ee4d4b62540c22c3ff5a19c891cf145fe238ed49;hb=HEAD;hpb=1fb5f7e36af1a4de040bc2989133703b0e0d4a9f diff --git a/src/moof/mesh.cc b/src/moof/mesh.cc index ee4d4b6..eac6ef9 100644 --- a/src/moof/mesh.cc +++ b/src/moof/mesh.cc @@ -1,13 +1,11 @@ -/*] Copyright (c) 2009-2010, Charles McGarvey [************************** +/*] Copyright (c) 2009-2011, 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 @@ -18,36 +16,33 @@ #include #include -#include - +#include "compression.hh" #include "debug.hh" #include "log.hh" #include "mesh.hh" #include "opengl.hh" - -#define AC3D_FORMAT_VERSION (0x0b) -#define ZLIB_BUF_SIZE (262114) +#define AC3D_FORMAT_VERSION 0x0b namespace moof { +MOOF_REGISTER_RESOURCE(mesh, ac, models); +MOOF_REGISTER_RESOURCE(mesh, acz, 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); - } + do stream.get(atom); while (stream && std::isspace(atom)); if (atom == '"') { - do - { + do { stream.get(atom); if (atom == '"') break; str += atom; @@ -56,8 +51,7 @@ static std::string read_string(std::istream& stream) } else { - do - { + do { stream.get(atom); if (std::isspace(atom)) break; str += atom; @@ -68,7 +62,7 @@ static std::string read_string(std::istream& stream) return str; } -inline int read_hex(std::istream& stream) +static int read_hex(std::istream& stream) { int hex; std::ios::fmtflags flags = stream.flags(); @@ -78,99 +72,82 @@ inline int read_hex(std::istream& stream) return hex; } -inline vector2 read_pair(std::istream& stream) +static vector2 read_pair(std::istream& stream) { - vector2 triplet; - stream >> triplet[0] >> triplet[1]; - return triplet; + vector2 pair; + stream >> pair[0] >> pair[1]; + return pair; } - -template -inline vector< scalar, fixed > read_triplet(std::istream& stream) +static vector3 read_triplet(std::istream& stream) { - vector< scalar, fixed > triplet; + vector3 triplet; stream >> triplet[0] >> triplet[1] >> triplet[2]; return triplet; } +static 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) + +static inline void throw_invalid_atom(const std::string& atom) { - std::string atom; + throw std::runtime_error("unexpected atom: " + atom); +} - object_ptr obj; - std::stack kids; +void mesh::load(std::istream& stream) +{ // read and verify the AC3D header - { - char magic[5]; - unsigned version = 0; + 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"); - } + 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"); - } + version = read_hex(stream); + if (version > AC3D_FORMAT_VERSION) + { + throw std::runtime_error("wrong mesh file format version"); } + std::string atom; + object_ptr obj; + std::stack kids; + while (stream) { stream >> atom; if (!stream) break; - if (atom == "MATERIAL") + if (atom == "OBJECT") { - 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; - - log_info("read material", materials_.back().name, - materials_.back().diffuse); + obj = load_object(stream, obj); } - else if (atom == "OBJECT") + else if (atom == "MATERIAL") + { + load_material(stream); + } + else if (atom == "name") { - 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; + obj->name = read_string(stream); + object_ptr parent = obj->parent.lock(); + if (parent) parent->kids_byname.insert(std::make_pair(obj->name, obj)); } else { - objects_.push_back(newObj); + throw_invalid_atom(atom); } - - 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") { @@ -184,7 +161,7 @@ void mesh::import(std::istream& stream) else if (atom == "texrep") { if (obj) obj->texrep = read_pair(stream); - else throw std::runtime_error("unexpected atom: " + atom); + else throw_invalid_atom(atom); } else if (atom == "rot") { @@ -199,26 +176,23 @@ void mesh::import(std::istream& stream) else if (atom == "url") { if (obj) std::getline(stream, obj->url); - else throw std::runtime_error("unexpected atom: " + atom); + else throw_invalid_atom(atom); } else if (atom == "numvert") { - if (!obj) throw std::runtime_error("unexpected atom: " + atom); + if (!obj) throw_invalid_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()); + obj->verts.push_back(read_triplet(stream)); } } else if (atom == "numsurf") { - if (!obj) throw std::runtime_error("unexpected atom: " + atom); + if (!obj) throw_invalid_atom(atom); int numsurf = 0; stream >> numsurf; @@ -226,174 +200,244 @@ void mesh::import(std::istream& stream) 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 || numrefs == 4); - - 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]; - for (int j = 0; j < numrefs; ++j) - { - int vert; - stream >> vert; - - vector2 uv = read_pair(stream); - - if (numrefs == 3) - { - face.triangles.push_back(vert); - face.triangles_uv.push_back(uv); - } - else - { - face.quads.push_back(vert); - face.quads_uv.push_back(uv); - } - } + if (atom != "SURF") throw_invalid_atom(atom); + load_surface(stream, obj); } + } 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) + if (0 < numkids) { - log_info("KIDS", kids.top(), "|", kids.size()); + 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); } -mesh::mesh(const std::string& path) +void mesh::load_material(std::istream& stream) { - std::ifstream file(path.c_str(), std::ifstream::in | - std::ifstream::binary); - if (!file) throw std::runtime_error("cannot find mesh file"); + materials_.push_back(material(read_string(stream))); + + std::string atom; + 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]; +} - // 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) +mesh::object_ptr mesh::load_object(std::istream& stream, object_ptr parent) +{ + std::string atom; + stream >> atom; + if (atom != "world" && atom != "group" && atom != "poly") { - log_info("text mesh detected"); - file.seekg(std::ios::beg); + throw_invalid_atom(atom); + } + + object_ptr obj = object::alloc(*this); - import(file); + if (parent) + { + parent->kids.push_back(obj); + obj->parent = parent; } else { - log_info("compressed mesh detected"); - file.seekg(std::ios::beg); + objects_.push_back(obj); + } - std::stringstream stream; - char in[ZLIB_BUF_SIZE]; - char out[ZLIB_BUF_SIZE]; + return obj; +} - z_stream zstream; +void mesh::load_surface(std::istream& stream, object_ptr obj) +{ + std::string atom; + read_hex(stream); - 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"); + int material = 0; + stream >> atom; + if (atom == "mat") stream >> material >> atom; + if (atom != "refs") throw_invalid_atom(atom); - do - { - file.read(in, sizeof(in)); - zstream.next_in = (Bytef*)in; - zstream.avail_in = file.gcount(); + 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]; + + std::vector verts(numrefs); + std::vector uv(numrefs); + + for (int i = 0; i < numrefs; ++i) + { + stream >> verts[i]; + uv[i] = read_pair(stream); + } - if (zstream.avail_in == 0) break; + // translate texture coordinates + if (obj->texture) obj->texture->fix_uv(uv); + // TODO why isn't texture always defined at this point? - do + for (int i = 0; i < numrefs; ++i) + { + scalar vert = verts[i]; + vector2 texcoord = uv[i]; + if (vert < face.triangles_uv.size()) + { + if (texcoord != face.triangles_uv[vert]) { - 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); + obj->verts.push_back(obj->verts[vert]); + face.triangles_uv.resize(obj->verts.size()); + vert = obj->verts.size() - 1; } - while (zstream.avail_out == 0); } - while(result != Z_STREAM_END); + else face.triangles_uv.resize(vert + 1); + face.triangles_uv[vert] = texcoord; + verts[i] = vert; + } + + face.triangles.push_back(verts[0]); + face.triangles.push_back(verts[1]); + face.triangles.push_back(verts[2]); + for (int i = 3; i < numrefs; ++i) + { + face.triangles.push_back(verts[0]); + face.triangles.push_back(verts[i-1]); + face.triangles.push_back(verts[i]); + } + +#if 0 + 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); - inflateEnd(&zstream); + 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); - import(stream); + last = face.triangles.back(); } +#endif } +mesh::mesh(const std::string& path) +{ + std::ifstream file(path.c_str(), std::ios::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); + load(file); + } + else + { + log_info("decompressing mesh..."); + file.seekg(std::ios::beg); + std::stringstream stream; + inflate(file, stream); + load(stream); + } +} + void mesh::draw(scalar alpha) const { - //glEnableClientState(GL_VERTEX_ARRAY); - //glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_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); + (*it)->draw(alpha); } -} + // TODO: disable vertex array? +} void mesh::set_material(int index) const { @@ -402,116 +446,48 @@ void mesh::set_material(int index) const 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); + 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(const mesh& mesh, scalar alpha) const +void mesh::object::draw(scalar alpha, bool recurse) 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 (int 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(); - } - - count = face.quads.size(); - for (int j = 0; j < count; j += 4) - { - glBegin(GL_QUADS); - glTexCoord(face.quads_uv[j]); - glVertex(verts[face.quads[j]]); - glTexCoord(face.quads_uv[j+1]); - glVertex(verts[face.quads[j+1]]); - glTexCoord(face.quads_uv[j+2]); - glVertex(verts[face.quads[j+2]]); - glTexCoord(face.quads_uv[j+3]); - glVertex(verts[face.quads[j+3]]); - glEnd(); - } - } + glVertexPointer(verts); - std::vector::const_iterator jt; - for (jt = kids.begin(); jt != kids.end(); ++jt) + if (texture) { - (*jt)->draw(mesh, alpha); + texture->bind(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); } -} - -void mesh::material_group::draw(scalar alpha) const -{ - // TODO: setup material - - /* - if (triangles.size() > 0) + else { - //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]); + image::reset_binding(); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); } - if (quads.size() > 0) + for (size_t i = 0; i < faces.size(); ++i) { - //log_info("drawing quads:", quads.size()/4); - glTexCoordPointer(2, GL_SCALAR, 0, quads_uv[0].data()); - glDrawElements(GL_QUADS, quads.size(), GL_UNSIGNED_INT, &quads[0]); - } - */ -} + const material_group& face = faces[i]; + if (face.triangles.size() == 0) continue; + mesh.set_material(i); -class mesh_resource_loader -{ -public: - - mesh_resource_loader() - { - resource::register_type("ac", "models"); + if (texture) glTexCoordPointer(face.triangles_uv); + glDrawElements(GL_TRIANGLES, face.triangles); } - ~mesh_resource_loader() + if (recurse) { - resource::unregister_type("ac"); + std::vector::const_iterator jt; + for (jt = kids.begin(); jt != kids.end(); ++jt) + (*jt)->draw(alpha); } -}; - -static mesh_resource_loader loader; +} } // namespace moof