]> Dogcows Code - chaz/yoink/blobdiff - src/moof/mesh.cc
fixed documentation about where to find licenses
[chaz/yoink] / src / moof / mesh.cc
index b41c377f29429514a1bcfffe48b91e16e877f3cc..eac6ef9151a74fb25767613d6f6ce847f1fa3c4b 100644 (file)
@@ -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 <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;
@@ -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 <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;     // FIXME: temporary
-
-                       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,197 +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);
-
-                               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();
-                               }
+                               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);
+       }
 
-               import(file);
+       object_ptr obj = object::alloc(*this);
+
+       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;
+       }
 
-               inflateEnd(&zstream);
+       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]);
+       }
 
-               import(stream);
+#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);
+
+               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::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
 {
@@ -425,94 +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);
+       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();
+       glVertexPointer(verts);
 
-       for (size_t i = 0; i < faces.size(); ++i)
+       if (texture)
        {
-               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();
-               }
+               texture->bind();
+               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        }
-
-       std::vector<object_ptr>::const_iterator jt;
-       for (jt = kids.begin(); jt != kids.end(); ++jt)
+       else
        {
-               (*jt)->draw(mesh, alpha);
+               image::reset_binding();
+               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }
-}
 
-void mesh::material_group::draw(scalar alpha) const
-{
-       // TODO: setup material
-       
-       /*
-       if (triangles.size() > 0)
+       for (size_t i = 0; i < faces.size(); ++i)
        {
-               //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]);
-       }
-       */
-}
+               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
This page took 0.04095 seconds and 4 git commands to generate.