model-parsing.cpp

Go to the documentation of this file.
00001 /*
00002  * model-parsing.cpp
00003  *
00004  * Copyright (C) 2008,2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  * Routines to parse/load GLUT 3D models.
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "glut-model.h"         // always include our own header first
00035 
00036 #include <fstream>
00037 
00038 #include "common/wave_ex.h"
00039 #include "glut/glut.h"
00040 #include "glut/texture.h"
00041 #include "nstream/nstream.h"
00042 #include "perf/perf.h"
00043 #include "util/file.h"
00044 #include "util/parsing.h"
00045 #include "util/token_stream.h"
00046 
00047 
00048 namespace glut {
00049 
00050 
00051 // interface destructors
00052 MaterialRegistry::~MaterialRegistry(void) throw() { }
00053 
00054 
00055 ////////////////////////////////////////////////////////////////////////////////
00056 //
00057 //      static helper methods
00058 //
00059 ////////////////////////////////////////////////////////////////////////////////
00060 
00061 static void
00062 getDictionaryFromNextLine
00063 (
00064 IO std::istream& stream,
00065 IN const char * debug_info,
00066 OUT dictionary_t& d
00067 )
00068 {
00069         ASSERT(stream.good(), "bad?");
00070         ASSERT(debug_info, "null");
00071         d.clear();
00072 
00073         eParseBehavior behavior = (eParseBehavior)
00074             (eParse_StripComments | eParse_StripBogus);
00075 
00076         std::string line = getNextLineFromStream(stream, behavior);
00077         getDictionaryFromString(line.c_str(), debug_info, d);
00078 }
00079 
00080 
00081 
00082 static byte_t
00083 getOptionalByte
00084 (
00085 IN const dictionary_t& d,
00086 IN const char * key
00087 )
00088 {
00089         int i = atoi(getOptionalValue(d, key, "0"));
00090         if (i < 0 || i > 255) {
00091                 WAVE_EX(wex);
00092                 wex << "Bad byte value (" << key << " = " << i << ")";
00093         }
00094         return (byte_t) i;
00095 }
00096 
00097 
00098 
00099 static void
00100 writePoint3dToStream
00101 (
00102 IO std::ostream& stream,
00103 IN const point3d_t& p
00104 )
00105 {
00106         ASSERT(stream.good(), "bad?");
00107         stream << "x " << p.x << " y " << p.y << " z " << p.z;
00108 }
00109 
00110 
00111 
00112 /*
00113 static void
00114 writeNonzeroByte
00115 (
00116 IO std::ostream& stream,
00117 IN const char * key,
00118 IN byte_t val
00119 )
00120 {
00121         ASSERT(stream.good(), "bad?");
00122         ASSERT(key, "null");
00123 
00124         if (!val)
00125                 return;
00126 
00127         stream << key << " " << (int) val << "\t";
00128 }
00129 
00130 
00131 
00132 static void
00133 writeColorToStream
00134 (
00135 IO std::ostream& stream,
00136 IN const glut::color_t& color
00137 )
00138 {
00139         writeNonzeroByte(stream, "r", color.red);
00140         writeNonzeroByte(stream, "g", color.green);
00141         writeNonzeroByte(stream, "b", color.blue);
00142         writeNonzeroByte(stream, "a", color.alpha);
00143 }
00144 */
00145 
00146 
00147 ////////////////////////////////////////////////////////////////////////////////
00148 //
00149 //      public API
00150 //
00151 ////////////////////////////////////////////////////////////////////////////////
00152 
00153 void
00154 parseColor
00155 (
00156 IO std::istream& stream,
00157 OUT img_color_t& color
00158 )
00159 {
00160         ASSERT(stream.good(), "bad?");
00161         color.clear();
00162 
00163         dictionary_t d;
00164         getDictionaryFromNextLine(stream, "polygon color", d);
00165 
00166         color.red = getOptionalByte(d, "r");
00167         color.green = getOptionalByte(d, "g");
00168         color.blue = getOptionalByte(d, "b");
00169         color.alpha = getOptionalByte(d, "a");
00170 }
00171 
00172 
00173 
00174 void
00175 parseMaterial
00176 (
00177 IO std::istream& stream,
00178 IN const char * parentDir,
00179 OUT material_t& material
00180 )
00181 {
00182         perf::Timer timer("parseMaterial");
00183         ASSERT(stream.good(), "bad?");
00184         ASSERT(parentDir, "null");
00185         material.clear();
00186 
00187         expectToken(stream, "{");
00188 
00189         std::string token;
00190         for (;;) {
00191                 getNextToken(stream, token);
00192                 if ("}" == token) {
00193                         break;          // end of material
00194                 } else if ("isTransparent" == token) {
00195                         getNextToken(stream, token);
00196                         if ("1" == token ||
00197                             !strcasecmp("true", token.c_str()) ||
00198                             !strcasecmp("t", token.c_str())) {
00199                                 // true!
00200                                 material.isTransparent = true;
00201                         }
00202                 } else if ("texture" == token) {
00203                         getNextToken(stream, token);
00204 
00205                         // create filesystem manager
00206                         smart_ptr<nstream::Manager> mgr =
00207                             nstream::getFilesystemManager(parentDir);
00208                         ASSERT_THROW(mgr, "failed to create filesystem mgr: "
00209                             << parentDir);
00210                         media::image_t image;
00211                         ASSERT_THROW(
00212                             media::loadImage(mgr, token.c_str(), image),
00213                             "Failed to load image: " << token);
00214 
00215                         material.texture_id = createTextureFromImage(image);
00216                 } else {
00217                         WAVE_EX(wex);
00218                         wex << "Unknown keyword in texture file: " << token;
00219                 }
00220         }
00221 }
00222 
00223 
00224 
00225 void
00226 parsePolygon
00227 (
00228 IN MaterialRegistry * mreg,
00229 IO std::istream& stream,
00230 OUT polygon_t& polygon
00231 )
00232 {
00233         ASSERT(mreg, "null");
00234         ASSERT(stream.good(), "bad?");
00235         polygon.clear();
00236 
00237         // open polygon
00238         expectToken(stream, "{");
00239         std::string token;
00240 
00241         // read vertex count
00242         expectToken(stream, "nVertices");
00243         polygon.nVertices = readInteger(stream, token);
00244         if (polygon.nVertices < 3) {
00245                 WAVE_EX(wex);
00246                 wex << "Too few vertices in polygon: " << polygon.nVertices;
00247         }
00248 
00249         // allocate arrays
00250         polygon.indices = new int[polygon.nVertices];
00251         polygon.u = new float[polygon.nVertices];
00252         polygon.v = new float[polygon.nVertices];
00253         ASSERT(polygon.indices && polygon.u && polygon.v, "out of memory");
00254 
00255         // read vertex information
00256         expectToken(stream, "vertices");
00257         expectToken(stream, "{");
00258         for (int i = 0; i < polygon.nVertices; ++i) {
00259                 // polygon vertex line should begin with "pv" token
00260                 expectToken(stream, "pv");
00261 
00262                 // grab the rest of the line, parse into dictionary
00263                 std::string line = getNextLineFromStream(stream, eParse_Strip);
00264                 dictionary_t d;
00265                 getDictionaryFromString(line.c_str(), "polygon vertex", d);
00266 
00267                 // pull important values out of dictionary
00268                 polygon.indices[i] = atoi(getRequiredValue(d, "i"));
00269                 polygon.u[i] = atof(getOptionalValue(d, "u", "0"));
00270                 polygon.v[i] = atof(getOptionalValue(d, "v", "0"));
00271         }
00272         expectToken(stream, "}");
00273 
00274         // read normal
00275         expectToken(stream, "normal");
00276         parsePoint3d(stream, polygon.normal);
00277 
00278         // read material ID
00279         expectToken(stream, "material");
00280         getNextToken(stream, token);
00281         DPRINTF("material = '%s'", token.c_str());
00282         polygon.material = mreg->getMaterial(token.c_str());
00283         if (!polygon.material) {
00284                 WAVE_EX(wex);
00285                 wex << "Failed to load material for polygon: " << token;
00286         }
00287 
00288         // all done!
00289         expectToken(stream, "}");
00290 }
00291 
00292 
00293 
00294 void
00295 parseModel
00296 (
00297 IN MaterialRegistry * mreg,
00298 IO std::istream& stream,
00299 OUT model_t& model
00300 )
00301 {
00302         perf::Timer timer("parseModel");
00303         ASSERT(mreg, "null");
00304         ASSERT(stream.good(), "bad?");
00305         model.clear();
00306 
00307         // open model
00308         expectToken(stream, "{");
00309         std::string token;
00310 
00311         // read vertex count
00312         expectToken(stream, "nVertices");
00313         model.nVertices = readInteger(stream, token);
00314         if (model.nVertices < 3) {
00315                 WAVE_EX(wex);
00316                 wex << "Too few vertices in model: " << model.nVertices;
00317         }
00318         model.vertices = new point3d_t[model.nVertices];
00319         ASSERT(model.vertices, "out of memory");
00320 
00321         // read vertices
00322         expectToken(stream, "vertices");
00323         expectToken(stream, "{");
00324         for (int i = 0; i < model.nVertices; ++i) {
00325                 expectToken(stream, "vertex");
00326                 parsePoint3d(stream, model.vertices[i]);
00327                 if (!i) {
00328                         model.boundingBox.setToPoint(model.vertices[0]);
00329                 } else {
00330                         model.boundingBox.includePoint(model.vertices[i]);
00331                 }
00332         }
00333         expectToken(stream, "}");
00334         model.boundingBox.dump("bounding box");
00335 
00336         // read polygon count
00337         expectToken(stream, "nPolygons");
00338         model.nPolygons = readInteger(stream, token);
00339         if (model.nPolygons < 1) {
00340                 WAVE_EX(wex);
00341                 wex << "Too few polygons in model: " << model.nPolygons;
00342         }
00343         model.polygons = new polygon_t[model.nPolygons];
00344         ASSERT(model.polygons, "out of memory");
00345 
00346         // read polygons
00347         expectToken(stream, "polygons");
00348         expectToken(stream, "{");
00349         for (int i = 0; i < model.nPolygons; ++i) {
00350                 expectToken(stream, "polygon");
00351                 parsePolygon(mreg, stream, model.polygons[i]);
00352         }
00353         expectToken(stream, "}");
00354 }
00355 
00356 
00357 
00358 void
00359 parseLodEntry
00360 (
00361 IN MaterialRegistry * mreg,
00362 IO std::istream& stream,
00363 OUT lod_entry_t& entry
00364 )
00365 {
00366         ASSERT(mreg, "null");
00367         ASSERT(stream.good(), "bad?");
00368         entry.clear();
00369 
00370         // open LOD entry
00371         expectToken(stream, "{");
00372         std::string token;
00373 
00374         // read distance
00375         expectToken(stream, "distance");
00376         entry.distance = readFloat(stream, token);
00377         if (entry.distance <= 0.0) {
00378                 WAVE_EX(wex);
00379                 wex << "Bad level-of-detail distance: " << entry.distance;
00380         }
00381 
00382         // read model
00383         expectToken(stream, "model");
00384         parseModel(mreg, stream, entry.model);
00385 }
00386 
00387 
00388 
00389 void
00390 parseLodModel
00391 (
00392 IN MaterialRegistry * mreg,
00393 IO std::istream& stream,
00394 OUT lod_model_t& model
00395 )
00396 {
00397         perf::Timer timer("parseLodModel");
00398         ASSERT(mreg, "null");
00399         ASSERT(stream.good(), "bad?");
00400         model.clear();
00401 
00402         // read header and opening tags
00403         std::string token;
00404         expectToken(stream, "lodModel");
00405         expectToken(stream, "{");
00406 
00407         // how many entries?
00408         expectToken(stream, "nEntries");
00409         int nEntries = readInteger(stream, token);
00410         if (nEntries < 1) {
00411                 WAVE_EX(wex);
00412                 wex << "Bad count of lod entries: " << nEntries;
00413         }
00414         model.entries = new lod_entry_t[nEntries];
00415         ASSERT(model.entries, "out of memory");
00416 
00417         // read lod entries
00418         for (int i = 0; i < nEntries; ++i) {
00419                 expectToken(stream, "lodEntry");
00420                 parseLodEntry(mreg, stream, model.entries[i]);
00421 
00422                 // ensure our bounding box encloses all child models
00423                 model_t& child = model.entries[i].model;
00424                 if (!i) {
00425                         model.boundingBox = child.getBoundingBox();
00426                 } else {
00427                         model.boundingBox.expand(child.getBoundingBox());
00428                 }
00429         }
00430 
00431         // finished parsing!
00432         model.nEntries = nEntries;
00433 }
00434 
00435 
00436 
00437 void
00438 writeModelToStream
00439 (
00440 IO std::ostream& stream,
00441 IN const model_t& model
00442 )
00443 {
00444         perf::Timer timer("writeModelToStream");
00445         ASSERT(stream.good(), "bad?");
00446 
00447         // write opening bracket
00448         stream << " {\n";
00449 
00450         // write vertex count
00451         stream << "nVertices " << model.nVertices << "\n";
00452 
00453         // write out vertices
00454         stream << "vertices {\n";
00455         for (int i = 0; i < model.nVertices; ++i) {
00456                 stream << "\tvertex\t";
00457                 writePoint3dToStream(stream, model.vertices[i]);
00458                 stream << "\n";
00459         }
00460         stream << "}\n";
00461 
00462         // write polygon count
00463         stream << "nPolygons " << model.nPolygons << "\n";
00464 
00465         // write polygons
00466         stream << "polygons {\n";
00467         for (int i = 0; i < model.nPolygons; ++i) {
00468                 const polygon_t& poly = model.polygons[i];
00469 
00470                 // open polygon entry
00471                 stream << "\tpolygon {\n";
00472 
00473                 // write polygon vertex count
00474                 stream << "\t\tnVertices " << poly.nVertices << "\n";
00475 
00476                 // write polygon vertices
00477                 stream << "\t\tvertices {\n";
00478                 for (int j = 0; j < poly.nVertices; ++j) {
00479                         stream << "\t\t\tpv\t";
00480                         stream << "i " << poly.indices[j] << "\t";
00481                         stream << "u " << ((poly.u) ? poly.u[j] : 0.0) << "\t";
00482                         stream << "v " << ((poly.v) ? poly.v[j] : 0.0) << "\n";
00483                 }
00484                 stream << "\t\t}\n";
00485 
00486                 // write normal
00487                 stream << "\t\tnormal\t";
00488                 writePoint3dToStream(stream, poly.normal);
00489                 stream << "\n";
00490 
00491                 // write material ID
00492                 stream << "\t\tmaterial\t";
00493                 stream << "BOGUS_ID";
00494                 stream << "\n";
00495 
00496                 // close polygon
00497                 stream << "\t}\n";
00498         }
00499         stream << "}\n";        // end of polygon list
00500 
00501         // write closing bracket for model
00502         stream << "}\n";
00503 }
00504 
00505 
00506 
00507 smart_ptr<Renderable>
00508 loadModel
00509 (
00510 IN const char * filename,
00511 IN MaterialRegistry * reg
00512 )
00513 {
00514         ASSERT(filename, "null");
00515         ASSERT(reg, "null");
00516 
00517         std::ifstream infile(filename);
00518         if (!infile.good()) {
00519                 WAVE_EX(wex);
00520                 wex << "Failed to open wavepacket glut model file: ";
00521                 wex << filename;
00522         }
00523 
00524         smart_ptr<lod_model_t> r = new lod_model_t;
00525         ASSERT(r, "out of memory");
00526 
00527         parseLodModel(reg, infile, *r);
00528 
00529         return r;
00530 }
00531 
00532 
00533 
00534 };      // glut namespace
00535