-/*] 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 <cstring>
#include <fstream>
#include <boost/algorithm/string/trim.hpp>
#include <zlib.h>
-#include <stlplus/strings/string_utilities.hpp>
-
+#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;
}
else
{
- do
- {
+ do {
stream.get(atom);
if (std::isspace(atom)) break;
str += atom;
return str;
}
-inline int read_hex(std::istream& stream)
+static int read_hex(std::istream& stream)
{
int hex;
std::ios::fmtflags flags = stream.flags();
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 <int D>
-inline vector< scalar, fixed<D> > read_triplet(std::istream& stream)
+static vector3 read_triplet(std::istream& stream)
{
- vector< scalar, fixed<D> > 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<int> 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<int> 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")
{
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")
{
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;
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<unsigned> verts(numrefs);
+ std::vector<vector2> 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<object_ptr>::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
{
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<vector3>::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<unsigned>::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<object_ptr>::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<mesh>("ac", "models");
+ if (texture) glTexCoordPointer(face.triangles_uv);
+ glDrawElements(GL_TRIANGLES, face.triangles);
}
- ~mesh_resource_loader()
+ if (recurse)
{
- resource::unregister_type("ac");
+ std::vector<object_ptr>::const_iterator jt;
+ for (jt = kids.begin(); jt != kids.end(); ++jt)
+ (*jt)->draw(alpha);
}
-};
-
-static mesh_resource_loader loader;
+}
} // namespace moof