zone.cpp

Go to the documentation of this file.
00001 /*
00002  * zone.cpp
00003  * 
00004  * Copyright (C) 2008  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  * Simple zone implementation.  See mapzone.h and aesop-map/zone.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "mapzone.h"
00036 
00037 #include "common/wave_ex.h"
00038 #include "typeinst/typeinst.h"
00039 #include "geometry/geometry_3d.h"
00040 #include "perf/perf.h"
00041 #include "util/token_stream.h"
00042 
00043 
00044 namespace mapzone {
00045 
00046 
00047 
00049 //
00050 //      private helper methods
00051 //
00053 
00054 class ZoneImpl : public aesop::LeafZone {
00055 public:
00056         // constructor, destructor ---------------------------------------------
00057         ZoneImpl(void) throw();
00058         ~ZoneImpl(void) throw() { }
00059 
00060         // public class methods ------------------------------------------------
00061         void initialize(IN std::istream& stream,
00062                                 IN eType type);
00063         void resolveIds(IN zone_map_t& map);
00064         void calcBoundaries(void);
00065         static ZoneImpl * getZoneImpl(IN aesop::Zone * z);
00066 
00067         // aesop::Zone class interface methods ---------------------------------
00068         const char * getId(void) const throw() { return m_id.c_str(); }
00069         aesop::Zone::eType getType(void) const throw() { return m_type; }
00070         void getBoundingRect(OUT rect3d_t& r) const { r = m_bounds; }
00071         bool containsPoint(IN const point3d_t& p) const throw();
00072         aesop::LeafZone * getLeafZone(IN const point3d_t& p) throw();
00073         const aesop::LeafZone * getLeafZone(IN const point3d_t& p) const throw();
00074         void * getUserPointer(void) const throw() { return m_user; }
00075         void setUserPointer(void * p) throw() { m_user = p; }
00076 
00077         // aesop::LeafZone class interface methods -----------------------------
00078         void iterateVisibleZones(IN aesop::leafzone_iteration_fn callback,
00079                                 IN void * context);
00080         bool isLeafVisible(IN LeafZone * lz) throw();
00081         void iterateStaticInstances(IN aesop::instance_iteration_fn callback,
00082                                 IN void * context);
00083 
00084 private:
00085         // private typedefs ----------------------------------------------------
00086 
00087         // these are weak references (do NOT delete!).  Containing map owns the
00088         // life cycle of zones.
00089         typedef std::set<ZoneImpl * > zone_set_t;
00090 
00091         // private helper methods ----------------------------------------------
00092         void loadParentZone(IN std::istream& stream);
00093         void loadLeafZone(IN std::istream& stream);
00094         void loadVirtualLeafZone(IN std::istream& stream);
00095 
00096         // private data members ------------------------------------------------
00097         std::string             m_id;           // ID of this zone
00098         eType                   m_type;         // what type of zone?
00099         rect3d_t                m_bounds;       // bounding rect
00100         VecString               m_zoneIds;      // used for loading only!
00101         aesop::vec_instance_t   m_static;       // static instances
00102         zone_set_t              m_zones;        // related zones
00103         void *                  m_user;         // user pointer
00104         bool                    m_haveBounds;   // calculated bounds?
00105 };
00106 
00107 
00108 
00109 ZoneImpl::ZoneImpl
00110 (
00111 void
00112 )
00113 throw()
00114 {
00115         m_user = NULL;
00116 }
00117 
00118 
00119 
00121 //
00122 //      ZoneImpl -- public class methods
00123 //
00125 
00126 void
00127 ZoneImpl::initialize
00128 (
00129 IN std::istream& stream,
00130 IN eType type
00131 )
00132 {
00133         perf::Timer timer("mapzone::Zone::initialize");
00134         ASSERT(stream.good(), "bad?");
00135         ASSERT(!m_zones.size(), "Already have zones?");
00136         ASSERT(!m_zoneIds.size(), "Already have zone IDs?");
00137 
00138         m_haveBounds = false;
00139 
00140         // save zone type
00141         ASSERT(eType_Leaf == type || eType_Parent == type
00142                || eType_VirtualLeaf == type,
00143             "Bad zone type: %d", type);
00144         m_type = type;
00145 
00146         // start
00147         expectToken(stream, "{");
00148 
00149         // zone metadata
00150         expectToken(stream, "id");
00151         getNextToken(stream, m_id);
00152 //      DPRINTF("Loading zone with id='%s'", m_id.c_str());
00153 
00154         // switch based on type
00155         if (eType_Leaf == this->getType()) {
00156                 this->loadLeafZone(stream);
00157         } else if (eType_Parent == this->getType()) {
00158                 this->loadParentZone(stream);
00159         } else if (eType_VirtualLeaf == this->getType()) {
00160                 this->loadVirtualLeafZone(stream);
00161         } else {
00162                 ASSERT(false, "should never get here (invalid zone type)");
00163         }
00164 
00165         // all done
00166 //      m_bounds.dump("  Final bounds");
00167 }
00168 
00169 
00170 
00171 void
00172 ZoneImpl::resolveIds
00173 (
00174 IN zone_map_t& map
00175 )
00176 {
00177         // loop through all our IDs, and resolve to zone objects
00178         ASSERT(!m_zones.size(), "Already have zones?");
00179         for (VecString::const_iterator i = m_zoneIds.begin();
00180              i != m_zoneIds.end(); ++i) {
00181                 const char * id = i->c_str();
00182 
00183                 zone_map_t::iterator j = map.find(id);
00184                 if (map.end() == j) {
00185                         const char * selfId = this->getId();
00186                         WAVE_EX(wex);
00187                         wex << "Zone id not found: '" << id << "'.  Used by ";
00188                         wex << "zone '" << selfId << "'";
00189                 }
00190 
00191                 Zone * z = j->second;
00192                 ASSERT(z, "null zone in map?");
00193 
00194                 ZoneImpl * zi = ZoneImpl::getZoneImpl(z);
00195                 ASSERT(zi, "null");
00196 
00197                 // okay, this ID is resolved!
00198                 m_zones.insert(zi);
00199         }
00200 
00201         // everything is resolved, release IDs
00202         m_zoneIds.clear();
00203 }
00204 
00205 
00206 
00207 void
00208 ZoneImpl::calcBoundaries
00209 (
00210 void
00211 )
00212 {
00213         if (m_haveBounds)
00214                 return;         // already calculated (or known)
00215 
00216         ASSERT(eType_Parent == this->getType(),
00217             "Only parent zones should need to calculate boundaries");
00218 
00219         // loop through all children, and have them recalculate
00220         bool first = true;
00221         for (zone_set_t::iterator i = m_zones.begin(); i != m_zones.end();
00222              ++i) {
00223                 ZoneImpl * zi = *i;
00224                 ASSERT(zi, "null zone in m_zones");
00225 
00226                 // don't accumulate this child if it is virtual
00227                 if (eType_VirtualLeaf == zi->getType()) {
00228                         continue;       // skip virtuals
00229                 }
00230 
00231                 zi->calcBoundaries();
00232 
00233                 if (first) {
00234                         // first child!  Copy bounds
00235                         zi->getBoundingRect(m_bounds);
00236                         first = false;
00237                 } else {
00238                         // later child.  Expand bounds to include
00239                         rect3d_t r;
00240                         zi->getBoundingRect(r);
00241                         m_bounds.expand(r);
00242                 }
00243         }
00244 
00245         // should have at least one child!
00246         ASSERT_THROW(!first, "Parent zone '" << this->getId() <<
00247             "' has no non-virtual children?");
00248 
00249         // okay, have bounds!
00250         m_haveBounds = true;
00251 }
00252 
00253 
00254 
00255 ZoneImpl *
00256 ZoneImpl::getZoneImpl
00257 (
00258 IN aesop::Zone * z
00259 )
00260 {
00261         ASSERT(z, "null");
00262 
00263         ZoneImpl * zi = dynamic_cast<ZoneImpl *>(z);
00264         if (!zi) {
00265                 WAVE_EX(wex);
00266                 wex << "Zone object is not implemented by mapzone library";
00267         }
00268         return zi;
00269 }
00270 
00271 
00272 
00274 //
00275 //      ZoneImpl -- aesop::Zone class interface methods
00276 //
00278 
00279 bool
00280 ZoneImpl::containsPoint
00281 (
00282 IN const point3d_t& p
00283 )
00284 const
00285 throw()
00286 {
00287         return m_bounds.containsPoint(p);
00288 }
00289 
00290 
00291 
00292 aesop::LeafZone *
00293 ZoneImpl::getLeafZone
00294 (
00295 IN const point3d_t& p
00296 )
00297 throw()
00298 {
00299         const Zone * cz = (const Zone *) this;
00300         const LeafZone * clz = cz->getLeafZone(p);
00301         return (LeafZone *)(clz);
00302 }
00303 
00304 
00305 
00306 const aesop::LeafZone *
00307 ZoneImpl::getLeafZone
00308 (
00309 IN const point3d_t& p
00310 )
00311 const
00312 throw()
00313 {
00314         if (eType_VirtualLeaf == this->getType()) {
00315                 return NULL;
00316         } else if (eType_Leaf == this->getType()) {
00317                 if (this->containsPoint(p)) {
00318                         return this;
00319                 }
00320         } else {
00321                 // this isn't a leaf zone!
00322                 // In that case, m_zones is the list of our children
00323                 for (zone_set_t::const_iterator i = m_zones.begin();
00324                      i != m_zones.end(); ++i) {
00325                         const ZoneImpl * zi = *i;
00326                         ASSERT(zi, "empty in child array");
00327 
00328                         const LeafZone * lz = zi->getLeafZone(p);
00329                         if (lz) {
00330                                 return lz;
00331                         }
00332                 }
00333         }
00334 
00335         // no leaf zone found that contained the specified point!
00336         return NULL;
00337 }
00338 
00339 
00340 
00342 //
00343 //      ZoneImpl -- aesop::LeafZone class interface methods
00344 //
00346 
00347 void
00348 ZoneImpl::iterateVisibleZones
00349 (
00350 IN aesop::leafzone_iteration_fn callback,
00351 IN void * context
00352 )
00353 {
00354         ASSERT(callback, "null");
00355         // ASSERT(context) -- we don't care!
00356 
00357         // first, call with ourselves!
00358         callback(this, context);
00359 
00360         // we are a leaf zone, so m_zones is the list of visible zones
00361         for (zone_set_t::iterator i = m_zones.begin(); i != m_zones.end();
00362              ++i) {
00363                 ZoneImpl * zi = *i;
00364                 ASSERT(zi, "null zone in vector");
00365 
00366                 // call back with this leaf zone
00367                 callback(zi, context);
00368         }
00369 }
00370 
00371 
00372 
00373 bool
00374 ZoneImpl::isLeafVisible
00375 (
00376 IN LeafZone * lz
00377 )
00378 throw()
00379 {
00380         ASSERT(lz, "null");
00381 
00382         ZoneImpl * zi = dynamic_cast<ZoneImpl *>(lz);
00383         ASSERT(zi, "can't downcast from leaf zone?");
00384 
00385         // special case: ourselves
00386         if (zi == this)
00387                 return true;
00388 
00389         // check against set
00390         return (m_zones.end() != m_zones.find(zi));
00391 }
00392 
00393 
00394 
00395 void
00396 ZoneImpl::iterateStaticInstances
00397 (
00398 IN aesop::instance_iteration_fn callback,
00399 IN void * context
00400 )
00401 {
00402         ASSERT(callback, "null");
00403         // ASSERT(context) -- we don't care!
00404 
00405         // iterate over all static instances
00406         for (aesop::vec_instance_t::iterator i = m_static.begin();
00407              i != m_static.end(); ++i) {
00408 
00409                 // call callback
00410                 callback(*i, context);
00411         }
00412 }
00413 
00414 
00415 
00417 //
00418 //      ZoneImpl -- private helper methods
00419 //
00421 
00422 void
00423 ZoneImpl::loadParentZone
00424 (
00425 IN std::istream& stream
00426 )
00427 {
00428         ASSERT(stream.good(), "bad?");
00429 
00430         // keep reading child nodes
00431         std::string token;
00432         while (true) {
00433                 getNextToken(stream, token);
00434 
00435                 if ("}" == token) {
00436                         break;  // no more child zones
00437                 }
00438 
00439                 if ("child" != token) {
00440                         WAVE_EX(wex);
00441                         wex << "parent zone should contain only 'child' ";
00442                         wex << "entries.  Found this instead: " << token;
00443                 }
00444 
00445                 getNextToken(stream, token);
00446                  DPRINTF("  Parent zone '%s' has child zone '%s'",
00447                     m_id.c_str(), token.c_str());
00448 
00449                 // don't resolve the ID yet (second pass).  Just store for now
00450                 m_zoneIds.push_back(token);
00451         }
00452 }
00453 
00454 
00455 
00456 void
00457 ZoneImpl::loadLeafZone
00458 (
00459 IN std::istream& stream
00460 )
00461 {
00462         ASSERT(stream.good(), "bad?");
00463 
00464         // bounding rect
00465         expectToken(stream, "boundary");
00466         parseRect3d(stream, m_bounds);
00467         m_bounds.dump("bounding rect");
00468         if (!m_bounds.isValid()) {
00469                 WAVE_EX(wex);
00470                 wex << "Leaf zone '" << m_id << "' has invalid bounding rect.";
00471         }
00472         m_haveBounds = true;            // okay, leaf has bounds
00473 
00474         // other leaf nodes visible from here
00475         expectToken(stream, "visible");
00476         expectToken(stream, "{");
00477         while (true) {
00478                 std::string token;
00479                 getNextToken(stream, token);
00480 
00481                 if ("}" == token) {
00482                         break;          // no more possibly visible nodes
00483                 }
00484                 if ("zone" != token) {
00485                         WAVE_EX(wex);
00486                         wex << "visible list should contain only 'zone' ";
00487                         wex << "entries.  Found this instead:" << token;
00488                 }
00489 
00490                 getNextToken(stream, token);
00491                 DPRINTF("  Leaf zone '%s' is visible from '%s'",
00492                     token.c_str(), m_id.c_str());
00493                 m_zoneIds.push_back(token);
00494         }
00495 
00496         // all static type instances in this leaf
00497         expectToken(stream, "staticInstances");
00498         expectToken(stream, "{");
00499         loadInstances(stream, m_static);
00500 
00501         // all done!
00502         expectToken(stream, "}");       // end of leaf zone
00503 }
00504 
00505 
00506 
00507 void
00508 ZoneImpl::loadVirtualLeafZone
00509 (
00510 IN std::istream& stream
00511 )
00512 {
00513         ASSERT(stream.good(), "bad?");
00514 
00515         // bounding rect
00516         expectToken(stream, "boundary");
00517         parseRect3d(stream, m_bounds);
00518         m_bounds.dump("bounding rect");
00519         if (!m_bounds.isValid()) {
00520                 WAVE_EX(wex);
00521                 wex << "Virtual leaf zone '" << m_id << "' has invalid bounding rect.";
00522         }
00523         m_haveBounds = true;            // okay, leaf has bounds
00524 
00525         // virtual leaf zone: static instances only!
00526         expectToken(stream, "staticInstances");
00527         expectToken(stream, "{");
00528         loadInstances(stream, m_static);
00529 
00530         // all done!
00531         expectToken(stream, "}");       // end of leaf zone
00532 }
00533 
00534 
00535 
00537 //
00538 //      public API
00539 //
00541 
00542 smart_ptr<aesop::Zone>
00543 loadZone
00544 (
00545 IN std::istream& stream,
00546 IN aesop::Zone::eType type
00547 )
00548 {
00549         ASSERT(stream.good(), "not good?");
00550         ASSERT(aesop::Zone::eType_Invalid != type, "Bad type: %d", type);
00551         smart_ptr<ZoneImpl> local = new ZoneImpl;
00552         ASSERT(local, "out of memory");
00553 
00554         local->initialize(stream, type);
00555 
00556         return local;
00557 }
00558 
00559 
00560 
00561 void
00562 resolveZoneIds
00563 (
00564 IN aesop::Zone * zone,
00565 IN zone_map_t& map
00566 )
00567 {
00568         ASSERT(zone, "null");
00569 
00570         ZoneImpl * zi = ZoneImpl::getZoneImpl(zone);
00571         ASSERT(zi, "null");
00572         zi->resolveIds(map);
00573 }
00574 
00575 
00576 
00577 void
00578 calcBoundaries
00579 (
00580 IN aesop::Zone * zone
00581 )
00582 {
00583         ASSERT(zone, "null");
00584 
00585         ZoneImpl * zi = ZoneImpl::getZoneImpl(zone);
00586         ASSERT(zi, "null");
00587         zi->calcBoundaries();
00588 }
00589 
00590 
00591 
00592 };      // mapzone namespace
00593