X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fmesh.cc;fp=src%2Fmoof%2Fmesh.cc;h=ee4d4b62540c22c3ff5a19c891cf145fe238ed49;hp=0000000000000000000000000000000000000000;hb=1fb5f7e36af1a4de040bc2989133703b0e0d4a9f;hpb=151f60a78d5e45c8d46c3e793fdefee306c6dc4c diff --git a/src/moof/mesh.cc b/src/moof/mesh.cc new file mode 100644 index 0000000..ee4d4b6 --- /dev/null +++ b/src/moof/mesh.cc @@ -0,0 +1,518 @@ + +/*] 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; + + 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 || 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); + } + } + } + } + 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 (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(); + } + } + + 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]); + } + + if (quads.size() > 0) + { + //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]); + } + */ +} + + +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 +