-/*] 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"
-// TODO: this file needs to be cleaned up
-
-
-#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;
}
-inline vector3 read_triplet(std::istream& stream)
+static vector3 read_triplet(std::istream& stream)
{
vector3 triplet;
stream >> triplet[0] >> triplet[1] >> triplet[2];
return triplet;
}
-inline vector4 read_color(std::istream& stream)
+static vector4 read_color(std::istream& stream)
{
vector4 color;
stream >> color[0] >> color[1] >> color[2];
}
-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_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];
+ obj = load_object(stream, obj);
}
- else if (atom == "OBJECT")
+ else if (atom == "MATERIAL")
{
- 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;
+ load_material(stream);
}
else if (atom == "name")
{
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
+ {
+ throw_invalid_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;
}
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");
-
- 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();
- }
+ if (atom != "SURF") throw_invalid_atom(atom);
+ load_surface(stream, obj);
}
+
}
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);
+ if (0 < numkids)
+ {
+ kids.push(numkids);
+ }
else
{
if (0 < kids.size() && 0 < --kids.top()) kids.pop();
obj = obj->parent.lock();
}
}
- else
+ }
+ while (stream);
+}
+
+
+void mesh::load_material(std::istream& stream)
+{
+ 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];
+}
+
+mesh::object_ptr mesh::load_object(std::istream& stream, object_ptr parent)
+{
+ std::string atom;
+ stream >> atom;
+ if (atom != "world" && atom != "group" && atom != "poly")
+ {
+ throw_invalid_atom(atom);
+ }
+
+ object_ptr obj = object::alloc(*this);
+
+ if (parent)
+ {
+ parent->kids.push_back(obj);
+ obj->parent = parent;
+ }
+ else
+ {
+ objects_.push_back(obj);
+ }
+
+ return obj;
+}
+
+void mesh::load_surface(std::istream& stream, object_ptr obj)
+{
+ std::string atom;
+ read_hex(stream);
+
+ int material = 0;
+ stream >> atom;
+ if (atom == "mat") stream >> material >> atom;
+ if (atom != "refs") throw_invalid_atom(atom);
+
+ 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);
+ }
+
+ // translate texture coordinates
+ if (obj->texture) obj->texture->fix_uv(uv);
+ // TODO why isn't texture always defined at this point?
+
+ for (int i = 0; i < numrefs; ++i)
+ {
+ scalar vert = verts[i];
+ vector2 texcoord = uv[i];
+ if (vert < face.triangles_uv.size())
{
- log_warning("UNKNOWN ATOM:", atom);
+ if (texcoord != 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] = texcoord;
+ verts[i] = vert;
}
- while (stream);
- std::vector<object_ptr>::iterator meh;
- for (meh = objects_.begin(); meh != objects_.end(); ++meh)
+ 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)
{
- object_ptr cow = *meh;
- log_info("OBJ: -", cow->name, cow->kids.size());
+ face.triangles.push_back(verts[0]);
+ face.triangles.push_back(verts[i-1]);
+ face.triangles.push_back(verts[i]);
+ }
- std::vector<object_ptr>::iterator foo;
- for (foo = cow->kids.begin(); foo != cow->kids.end(); ++foo)
+#if 0
+ unsigned vert;
+ stream >> vert;
+ vector2 uv = read_pair(stream);
+ if (vert < face.triangles_uv.size())
+ {
+ if (uv != face.triangles_uv[vert])
{
- log_info("OBJ: -", (*foo)->name, (*foo)->kids.size());
+ 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 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);
-//}
+ 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();
+ }
+#endif
+}
mesh::mesh(const std::string& path)
{
- std::ifstream file(path.c_str(), std::ifstream::in |
- std::ifstream::binary);
+ 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
{
log_info("text mesh detected");
file.seekg(std::ios::beg);
-
- import(file);
+ load(file);
}
else
{
- log_info("compressed mesh detected");
+ log_info("decompressing mesh...");
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);
+ inflate(file, stream);
+ load(stream);
}
}
-
void mesh::draw(scalar alpha) const
{
glEnableClientState(GL_VERTEX_ARRAY);
// TODO: disable vertex array?
}
-
void mesh::set_material(int index) const
{
set_material(materials_[index]);
{
std::vector<object_ptr>::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<mesh>("ac", "models");
- //}
-
- //~mesh_resource_loader()
- //{
- //resource::unregister_type("ac");
- //}
-//};
-
-//static mesh_resource_loader loader;
-
-
} // namespace moof