bsp.cpp

Go to the documentation of this file.
00001 /*
00002  * bsp.cpp
00003  *
00004  * Copyright (C) 2010  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  *
00031  * BSP parsing.  See bsp.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "bsp.h"                        // always include our own header first!
00036 
00037 #include <iostream>
00038 
00039 #include "geometry/plane.h"
00040 #include "perf/perf.h"
00041 
00042 
00043 namespace quake {
00044 
00045 
00046 
00047 /// quake BSP file directory entry
00048 struct direntry_t {
00049         int32_t         offset; ///< offset to start of lump from file begin
00050         int32_t         length; ///< length of this lump in bytes--multiple of 4
00051 };
00052 
00053 
00054 typedef std::map<eLumpType, direntry_t> lump_entry_map_t;
00055 
00056 #define DECLARE_LUMP_READER(type)                                       \
00057         lump_object_t * readLump ## type (IO std::istream& in);
00058 
00059 
00060 typedef lump_object_t * (*lump_reader_t)(IO std::istream& in);
00061 
00062 
00063 struct lump_entry_t {
00064         eLumpType               type;
00065         lump_reader_t           reader;
00066         int                     obj_size;       // size on disk/file
00067 };
00068 
00069 #define LUMP_READER_ENTRY( type , size )                                \
00070          { eLump_ ## type , readLump ## type , size },
00071 
00072 
00073 // list of lump readers
00074 DECLARE_LUMP_READER(Entities);
00075 DECLARE_LUMP_READER(Faces);
00076 DECLARE_LUMP_READER(Leaffaces);
00077 DECLARE_LUMP_READER(Leafs);
00078 DECLARE_LUMP_READER(Meshverts);
00079 DECLARE_LUMP_READER(Nodes);
00080 DECLARE_LUMP_READER(Planes);
00081 DECLARE_LUMP_READER(Textures);
00082 DECLARE_LUMP_READER(Vertices);
00083 
00084 
00085 // map from type to lump reading data
00086 static const lump_entry_t s_lumpMap[] = {
00087         LUMP_READER_ENTRY(Entities,     -1)  // negative value --> variable size
00088         LUMP_READER_ENTRY(Faces,        14 * sizeof(int32_t) + 12 * sizeof(float))
00089         LUMP_READER_ENTRY(Leaffaces,    sizeof(int32_t))
00090         LUMP_READER_ENTRY(Leafs,        12 * sizeof(int32_t))
00091         LUMP_READER_ENTRY(Meshverts,    sizeof(int32_t))
00092         LUMP_READER_ENTRY(Nodes,        9 * sizeof(int32_t))
00093         LUMP_READER_ENTRY(Planes,       4 * sizeof(float))
00094         LUMP_READER_ENTRY(Textures,     64 + 2 * sizeof(int32_t))
00095         LUMP_READER_ENTRY(Vertices,     4 + 10 * sizeof(float))
00096 
00097         // must be last!
00098         { eLump_Invalid, NULL, 0 }
00099 };
00100 
00101 
00102 
00103 // interface destructors
00104 Bsp::~Bsp(void) throw() { }
00105 lump_object_t::~lump_object_t(void) throw() { }
00106 
00107 entity_t::~entity_t(void) throw() { }
00108 face_t::~face_t(void) throw() { }
00109 leaf_t::~leaf_t(void) throw() { }
00110 leafface_t::~leafface_t(void) throw() { }
00111 meshvert_t::~meshvert_t(void) throw() { }
00112 node_t::~node_t(void) throw() { }
00113 plane_t::~plane_t(void) throw() { }
00114 texture_path_t::~texture_path_t(void) throw() { }
00115 vertex_t::~vertex_t(void) throw() { }
00116 
00117 
00118 
00119 ////////////////////////////////////////////////////////////////////////////////
00120 //
00121 //      static helper methods
00122 //
00123 ////////////////////////////////////////////////////////////////////////////////
00124 
00125 static const lump_entry_t *
00126 getLumpEntry
00127 (
00128 IN eLumpType type
00129 )
00130 throw()
00131 {
00132         for (const lump_entry_t * p = s_lumpMap; p->reader; ++p) {
00133                 if (p->type == type)
00134                         return p;
00135         }
00136 
00137         DPRINTF("Could not find lump type in static map: %d", type);
00138         return NULL;
00139 }
00140 
00141 
00142 
00143 ////////////////////////////////////////////////////////////////////////////////
00144 //
00145 //      lump readers (TODO: break out into separate files?)
00146 //
00147 ////////////////////////////////////////////////////////////////////////////////
00148 
00149 lump_object_t *
00150 readLumpEntities
00151 (
00152 IO std::istream& in
00153 )
00154 {
00155         ASSERT_THROW(in.good(), "bad input BSP stream");
00156 
00157         static entity_t s_entity;
00158 
00159         s_entity.dictionary.clear();
00160         //DPRINTF("ENTITY -----------------------");
00161 
00162         char a;
00163         std::string key, value;
00164         in >> a;
00165         if (!a) {
00166                 // end of entities!
00167                 return NULL;
00168         }
00169         ASSERT_THROW('{' == a, "Expected an opening bracket, but read: " << a);
00170         while (true) {
00171                 in >> a;
00172                 if ('}' == a) {
00173                         break;          // end of entity
00174                 }
00175                 ASSERT_THROW('\"' == a, "Malformed key.  " <<
00176                     "Should start with quotes, but read this: " << a);
00177 
00178                 key.clear();
00179                 value.clear();
00180 
00181                 while (true) {
00182                         in >> a;
00183                         if ('\"' == a) {
00184                                 break;
00185                         }
00186                         key += a;
00187                 }
00188 
00189                 while (true) {
00190                         in >> a;
00191                         if ('\"' == a) {
00192                                 break;
00193                         }
00194                 }
00195 
00196                 while (true) {
00197                         in >> a;
00198                         if ('\"' == a) {
00199                                 break;
00200                         }
00201                         value += a;
00202                 }
00203 
00204                 // DPRINTF("  '%s' = '%s'", key.c_str(), value.c_str());
00205                 s_entity.dictionary[key] = value;
00206         }
00207 
00208         return &s_entity;
00209 }
00210 
00211 
00212 
00213 lump_object_t *
00214 readLumpFaces
00215 (
00216 IO std::istream& in
00217 )
00218 {
00219         ASSERT_THROW(in.good(), "bad input BSP stream");
00220 
00221         static face_t s_face;
00222 
00223         s_face.texture = readInt(in);
00224         s_face.effect = readInt(in);
00225         s_face.type = readInt(in);
00226         s_face.vertex = readInt(in);
00227         s_face.nVertices = readInt(in);
00228         s_face.meshvert = readInt(in);
00229         s_face.nMeshverts = readInt(in);
00230         s_face.lmIndex = readInt(in);
00231         s_face.lmStart[0] = readInt(in);
00232         s_face.lmStart[1] = readInt(in);
00233         s_face.lmSize[0] = readInt(in);
00234         s_face.lmSize[1] = readInt(in);
00235         s_face.origin = readPoint3d(in);
00236         s_face.lmVecs[0] = readPoint3d(in);
00237         s_face.lmVecs[1] = readPoint3d(in);
00238         s_face.normal = readPoint3d(in);
00239         s_face.size[0] = readInt(in);
00240         s_face.size[1] = readInt(in);
00241 
00242         return &s_face;
00243 }
00244 
00245 
00246 
00247 lump_object_t *
00248 readLumpLeaffaces
00249 (
00250 IO std::istream& in
00251 )
00252 {
00253         ASSERT_THROW(in.good(), "bad input BSP stream");
00254 
00255         static leafface_t s_lf;
00256         s_lf.face = readInt(in);
00257         return &s_lf;
00258 }
00259 
00260 
00261 
00262 lump_object_t *
00263 readLumpLeafs
00264 (
00265 IO std::istream& in
00266 )
00267 {
00268         ASSERT_THROW(in.good(), "bad BSP input stream");
00269 
00270         static leaf_t s_leaf;
00271 
00272         int32_t * n = (int32_t *) &s_leaf.cluster;
00273         // a leaf is really just 12 ints
00274         for (int j = 0; j < 12; ++j) {
00275                 n[j] = readInt(in);
00276         }
00277         return &s_leaf;
00278 }
00279 
00280 
00281 
00282 lump_object_t *
00283 readLumpMeshverts
00284 (
00285 IO std::istream& in
00286 )
00287 {
00288         ASSERT_THROW(in.good(), "bad BSP input stream");
00289 
00290         static meshvert_t s_meshvert;
00291 
00292         s_meshvert.vertex = readInt(in);
00293 
00294         return &s_meshvert;
00295 }
00296 
00297 
00298 
00299 lump_object_t *
00300 readLumpNodes
00301 (
00302 IO std::istream& in
00303 )
00304 {
00305         ASSERT_THROW(in.good(), "bad BSP input stream");
00306 
00307         static node_t s_node;
00308 
00309         // read node entry (just a set of 9 integers)
00310         int32_t * n = (int32_t *) &s_node.plane;
00311         for (int j = 0; j < 9; ++j) {
00312                 n[j] = readInt(in);
00313         }
00314         return &s_node;
00315 }
00316 
00317 
00318 
00319 lump_object_t *
00320 readLumpPlanes
00321 (
00322 IO std::istream& in
00323 )
00324 {
00325         ASSERT_THROW(in.good(), "bad bsp stream in readLumpPlanes");
00326 
00327         static plane_t s_plane;
00328 
00329         s_plane.n = readPoint3d(in);
00330         s_plane.d = readFloat(in);
00331 
00332         return &s_plane;
00333 }
00334 
00335 
00336 
00337 lump_object_t *
00338 readLumpTextures
00339 (
00340 IO std::istream& in
00341 )
00342 {
00343         ASSERT_THROW(in.good(), "bad bsp stream");
00344 
00345         static texture_path_t s_texture;
00346 
00347         in.read(s_texture.name, 64);
00348         s_texture.name[64] = 0; // force null termination
00349         s_texture.flags = readInt(in);
00350         s_texture.contents = readInt(in);
00351 
00352         return &s_texture;
00353 }
00354 
00355 
00356 
00357 lump_object_t *
00358 readLumpVertices
00359 (
00360 IO std::istream& in
00361 )
00362 {
00363         ASSERT_THROW(in.good(), "Bad BSP stream");
00364 
00365         static vertex_t s_vertex;
00366 
00367         s_vertex.position = readPoint3d(in);
00368         s_vertex.texcoord[0][0] = readFloat(in);
00369         s_vertex.texcoord[0][1] = readFloat(in);
00370         s_vertex.texcoord[1][0] = readFloat(in);
00371         s_vertex.texcoord[1][1] = readFloat(in);
00372         s_vertex.normal = readPoint3d(in);
00373         in.read((char *) &s_vertex.color, 4);
00374 
00375         return &s_vertex;
00376 }
00377 
00378 
00379 
00380 ////////////////////////////////////////////////////////////////////////////////
00381 //
00382 //      BspImpl - class that implements the quake::Bsp interface
00383 //
00384 ////////////////////////////////////////////////////////////////////////////////
00385 
00386 class BspImpl : public Bsp {
00387 public:
00388         // constructor, destructor ---------------------------------------------
00389         BspImpl(void) throw();
00390         ~BspImpl(void) throw() { }
00391 
00392         // public class methods ------------------------------------------------
00393         void initialize(IN smart_ptr<nstream::Stream>& stream);
00394 
00395         // quake::Bsp class interface methods ----------------------------------
00396         virtual BspVersion getVersion(void) const throw() { return m_version; }
00397         virtual const char * getName(void) const throw() { return m_name.c_str(); }
00398         virtual const vec_lump_type_t& getLumps(void) const throw() { return m_lumps; }
00399         virtual int getLumpObjectCount(IN eLumpType type);
00400         virtual void startLumpIteration(IN eLumpType type);
00401         virtual const lump_object_t * getNextLumpObject(void);
00402 
00403 private:
00404         // private typedefs ----------------------------------------------------
00405         // private helper methods ----------------------------------------------
00406         direntry_t * getEntry(IN eLumpType type) throw();
00407 
00408         // private member data -------------------------------------------------
00409         BspVersion              m_version;
00410         std::string             m_name;
00411         vec_lump_type_t         m_lumps;
00412         lump_entry_map_t        m_lumpMap;
00413         smart_ptr<nstream::Stream>      m_stream;
00414         std::istream *          m_pinput;
00415         const lump_entry_t *    m_entry;
00416         int                     m_index;
00417         int                     m_max;          // max offset
00418 };
00419 
00420 
00421 
00422 ////////////////////////////////////////////////////////////////////////////////
00423 //
00424 //      BspImpl -- public class methods
00425 //
00426 ////////////////////////////////////////////////////////////////////////////////
00427 
00428 BspImpl::BspImpl
00429 (
00430 void
00431 )
00432 throw()
00433 {
00434         m_index = -1;
00435         m_max = 0;
00436         m_pinput = NULL;
00437         m_entry = NULL;
00438 }
00439 
00440 
00441 
00442 void
00443 BspImpl::initialize
00444 (
00445 IN smart_ptr<nstream::Stream>& stream
00446 )
00447 {
00448         perf::Timer timer("loadBsp");
00449         ASSERT(stream, "null");
00450         ASSERT_THROW(stream->good(), "bad input BSP stream");
00451         ASSERT(!m_stream, "already have a stream");
00452 
00453         // save
00454         m_stream = stream;
00455 
00456         // debug diagnostics
00457         smart_ptr<nstream::File> file = stream->getFile();
00458         ASSERT_THROW(file, "stream not attached to file?");
00459         m_name = file->getName();
00460         DPRINTF("Reading bsp file from stream: %s", m_name.c_str());
00461 
00462         // okay, get underlying stream
00463         std::istream& in = stream->getStream();
00464         ASSERT_THROW(in.good(), "bad input BSP stream");
00465 
00466         // read version
00467         m_version.read(in);
00468         ASSERT_THROW(m_version.isValid(), "Could not parse BSP version");
00469 
00470         // get lumps
00471         ASSERT_THROW(m_version.getLumpTypes(m_lumps),
00472             "Could not determine BSP lumps from version");
00473 
00474         // now keep reading directory entries
00475         for (vec_lump_type_t::const_iterator i = m_lumps.begin();
00476              i != m_lumps.end(); ++i) {
00477                 eLumpType type = *i;
00478                 // const char * name = getLumpName(type);
00479                 // DPRINTF("Reading directory entry for lump '%s'", name);
00480 
00481                 direntry_t entry;
00482                 entry.offset = readInt(in);
00483                 entry.length = readInt(in);
00484 
00485                 // DPRINTF("  offset: %d", entry.offset);
00486                 // DPRINTF("  length: %d", entry.length);
00487 
00488                 ASSERT_THROW(entry.offset > 0,
00489                     "bad lump offset: " << entry.offset);
00490                 ASSERT_THROW(entry.length >= 0,
00491                     "bad lump length: " << entry.length);
00492 
00493                 // remember the entry information for this lump
00494                 m_lumpMap[type] = entry;
00495         }
00496 }
00497 
00498 
00499 
00500 ////////////////////////////////////////////////////////////////////////////////
00501 //
00502 //      BspImpl -- quake::Bsp class interface methods
00503 //
00504 ////////////////////////////////////////////////////////////////////////////////
00505 
00506 int
00507 BspImpl::getLumpObjectCount
00508 (
00509 IN eLumpType type
00510 )
00511 {
00512         // do we have a lump directory entry for this type?
00513         direntry_t * dirent = this->getEntry(type);
00514         if (!dirent) {
00515                 return 0;       // nope
00516         }
00517         ASSERT(dirent->length >= 0, "Bad length: %d", dirent->length);
00518 
00519         // get the lump entry
00520         const lump_entry_t * p = getLumpEntry(type);
00521         if (!p) {
00522                 return 0;       // unrecognized lump type
00523         }
00524         if (p->obj_size < 0) {
00525                 // objects are of unknown size!
00526                 return -1;
00527         }
00528         ASSERT(p->obj_size > 0, "Bad object size: %d", p->obj_size);
00529 
00530         return (dirent->length / p->obj_size);
00531 }
00532 
00533 
00534 
00535 void
00536 BspImpl::startLumpIteration
00537 (
00538 IN eLumpType type
00539 )
00540 {
00541         perf::Timer timer("Bsp::startLumpIteration");
00542         DPRINTF("Caller has requested to start iterating lump '%s'",
00543             getLumpName(type));
00544 
00545         // clear
00546         m_index = 0;
00547         m_entry = NULL;
00548         m_max = 0;
00549         m_pinput = NULL;
00550 
00551         // do we have a directory entry for this lump?
00552         direntry_t * dirent = this->getEntry(type);
00553         if (!dirent) {
00554                 return;         // nope
00555         }
00556 
00557         // look up entry
00558         m_entry = getLumpEntry(type);
00559         if (!m_entry) {
00560                 DPRINTF("Coult not find lump entry for type %d", type);
00561                 return;
00562         }
00563         ASSERT(m_entry->reader, "null reader in lump entry map");
00564 
00565         DPRINTF("  Moving to offset %d", dirent->offset);
00566         ASSERT(dirent->offset > 0, "Bad lump offset: %d", dirent->offset);
00567 
00568         ASSERT(m_stream, "null");
00569         m_pinput = &m_stream->getStream();
00570         ASSERT(m_pinput, "null");
00571         ASSERT_THROW(m_pinput->good(), "bad input BSP stream");
00572         m_pinput->seekg(dirent->offset);
00573 
00574         // reset max
00575         m_max = dirent->offset + dirent->length;
00576 }
00577 
00578 
00579 
00580 const lump_object_t *
00581 BspImpl::getNextLumpObject
00582 (
00583 void
00584 )
00585 {
00586         if (!m_entry || !m_pinput) {
00587                 return NULL;    // no input stream
00588         }
00589         int offset = m_pinput->tellg();
00590         if (offset >= m_max)
00591                 return NULL;    // end of iteration
00592 
00593         // let reader do its work
00594         lump_object_t * retval = m_entry->reader(*m_pinput);
00595         if (!retval) {
00596                 DPRINTF("lump reader returned null object");
00597                 return NULL;
00598         }
00599         retval->index = m_index;
00600 
00601         // update
00602         ++m_index;
00603 
00604         // all done
00605         return retval;
00606 }
00607 
00608 
00609 
00610 ////////////////////////////////////////////////////////////////////////////////
00611 //
00612 //      BspImpl -- private member functions
00613 //
00614 ////////////////////////////////////////////////////////////////////////////////
00615 
00616 direntry_t *
00617 BspImpl::getEntry
00618 (
00619 IN eLumpType type
00620 )
00621 throw()
00622 {
00623         lump_entry_map_t::iterator i = m_lumpMap.find(type);
00624         if (m_lumpMap.end() == i) {
00625                 return NULL;
00626         }
00627         return &i->second;
00628 }
00629 
00630 
00631 
00632 ////////////////////////////////////////////////////////////////////////////////
00633 //
00634 //      public API
00635 //
00636 ////////////////////////////////////////////////////////////////////////////////
00637 
00638 smart_ptr<Bsp>
00639 Bsp::load
00640 (
00641 IN smart_ptr<nstream::Stream>& stream
00642 )
00643 {
00644         ASSERT(stream, "null");
00645 
00646         smart_ptr<BspImpl> local = new BspImpl;
00647         ASSERT(local, "out of memory");
00648 
00649         local->initialize(stream);
00650 
00651         return local;
00652 }
00653 
00654 
00655 
00656 };      // quake namespace
00657