#ifndef __GEOWORLD_H__ #define __GEOWORLD_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sphpack.h" #include "worldfile.h" #include "gwutil.h" #include "PNGDEM.h" #include "geomorph.h" #include "oregen.h" namespace geoworld { class UnlockEx : public std::exception { public: const mars::BBox< long > bbox; UnlockEx (const mars::BBox< long > & bbox) : bbox(bbox) {} }; class PGWFileEx : public std::exception { public: const std::string message; PGWFileEx(const char * szMsg) : message(szMsg) {} }; class PGWIllegal : public std::exception { public: const std::string message; PGWIllegal(const char * szMsg) : message(szMsg) {} }; class LockedGWSection { public: static const unsigned short MAX_LOCK_DIM = 5000; enum StratumSyncOp { SSync_None = 0, SSync_Perfect = 1 }; class View { private: struct { const GeoHeightMap::View * heightmap; const Stratum::View * stratum; } const_view; struct { GeoHeightMap::View * heightmap; Stratum::View * stratum; } mutable_view; // What? do I look like a Staples store to you? View (const View & copy); public: const unsigned short width, height; View (GeoHeightMap::View * heightmap, Stratum::View * stratum) : width(heightmap->width), height(heightmap->height) { mutable_view.heightmap = heightmap; mutable_view.stratum = stratum; const_view.heightmap = heightmap; const_view.stratum = stratum; } View (const GeoHeightMap::View * heightmap, const Stratum::View * stratum) : width(heightmap->width), height(heightmap->height) { mutable_view.heightmap = NULL; mutable_view.stratum = NULL; const_view.heightmap = heightmap; const_view.stratum = stratum; } inline GeoHeightMap::View * getHeightMap () { assert(mutable_view.heightmap != NULL); return mutable_view.heightmap; } inline const GeoHeightMap::View * getHeightMap () const { return const_view.heightmap; } inline Stratum::View * getStratum () { assert(mutable_view.stratum != NULL); return mutable_view.stratum; } inline const Stratum::View * getStratum () const { return const_view.stratum; } }; const mars::BBox< long > bbox; LockedGWSection( const mars::BBox< long > & bbox, const long nDepth, const unsigned short nDEMLOD, const size_t nLayers, const unsigned short nStratumLOD ); LockedGWSection( const GeoHeightMap & hm, const unsigned short nDEMLOD, const Stratum & stratum ); static bool isValid (const mars::BBox< long > & bbox, const unsigned short nTileDim, const bool bPad = true ); static mars::BBox< long > computeLockedSection (const mars::BBox< long > & bbox, const unsigned short nTileDim, const bool bPad = true ); inline static unsigned short getMaxViewportDim (const unsigned short nTileDim, const bool bPad = true ) { return getActualViewportDim(MAX_LOCK_DIM, nTileDim, bPad); } inline static unsigned short getActualViewportDim (const unsigned short nDimTest, const unsigned short nTileDim, const bool bPad = true) { return nDimTest - nTileDim * (bPad ? 2 : 1); } // DEPS: LGWSBBA inline unsigned short getActualWidth () const { return _hm.width; } inline unsigned short getActualHeight () const { return _hm.height; } inline mars::Range< GeoHeightMap::Precision > getElevationRange () const { return _hm.range(); } inline void setStratumSyncOp (const StratumSyncOp & ensso) { _ssop = ensso; } inline const StratumSyncOp & getStratumSyncOp () const { return _ssop; } inline mars::ScalarFieldView< GeoHeightMap::Precision >::Offset at (const long x, const long y) { return GeoHeightMap::Offset(&_hm, static_cast< signed int > (x - bbox.left), static_cast< signed int > (y - bbox.top)); } void operator << (const View & view); void operator >> (View && view) const; inline void operator << (const Stratum::Offset offs) { _stratum << offs; } void blitFrom (const FileSource::MainDEM::Block & block, const unsigned short nTileX, const unsigned short nTileY); void blitTo (FileSource::MainDEM::Block & block, const unsigned short nTileX, const unsigned short nTileY) const; inline void processSyncOp () { switch (_ssop) { case SSync_Perfect: _stratum.applyDEM(_hm, _nDEMLOD); break; case SSync_None: break; } } void operator >> (PNGDEM & pngdem) const; void operator << (const PNGDEM & pngdem); View * createView (const long nLeft, const long nTop, const long nRight, const long nBottom); const View * createView (const long nLeft, const long nTop, const long nRight, const long nBottom) const; inline void releaseView (const View * pView) const { LOG(mars::Log::Debug) << pView->getHeightMap(); _hm.releaseView(pView->getHeightMap()); _stratum.releaseView(pView->getStratum()); delete pView; } template< typename J, typename JST > inline LockedGWSection & operator += (const mars::ScalarFieldView< J, JST > & other) { _hm += other; return *this; } inline LockedGWSection & operator += (const GeoHeightMap::View & other) { _hm += other; return *this; } template< typename J, typename JST > inline LockedGWSection & operator -= (const mars::ScalarFieldView< J, JST > & other) { _hm -= other; return *this; } inline LockedGWSection & operator -= (const GeoHeightMap::View & other) { _hm -= other; return *this; } inline const GeoHeightMap::Precision & heightmap (const long x, const long y) const { assert (bbox.inside(x, y)); return _hm.getw(x - bbox.left, y - bbox.top); } inline GeoHeightMap::Precision & heightmap (const long x, const long y) { assert (bbox.inside(x, y)); return _hm.getw(x - bbox.left, y - bbox.top); } inline const GeoHeightMap::Precision getSurfaceMean (const long x, const long y) const { assert(bbox.inside(x - 1, y - 1) && bbox.inside(x + 1, y + 1)); return _hm.mean (x - bbox.left, y - bbox.top); } inline const GeoHeightMap::Precision getSurfaceMeanVN( const long x, const long y ) const { assert(bbox.inside(x - 1, y - 1) && bbox.inside(x + 1, y + 1)); return _hm.meanVN (x - bbox.left, y - bbox.top); } inline GWCoords createCoordsRVN(const long x, const long y, const unsigned short nScale) const { typedef mars::Numerics< GeoHeightMap::Precision >::UP R; return GWCoords (x, y, static_cast< GWCoords::Precision > ( ( static_cast< R > (_hm.meanVN(x - nScale - bbox.left, y - nScale - bbox.top)) + static_cast< R > (_hm.meanVN(x + nScale - bbox.left, y - nScale - bbox.top)) + static_cast< R > (_hm.meanVN(x - nScale - bbox.left, y + nScale - bbox.top)) + static_cast< R > (_hm.meanVN(x + nScale - bbox.left, y + nScale - bbox.top)) + static_cast< R > (_hm.meanVN(x - bbox.left, y - bbox.top)) ) / 5 ) ); } inline GWCoords createCoords(const long x, const long y) const { return GWCoords (x, y, _hm.meanVN(x - bbox.left, y - bbox.top)); } inline GWCoords createCoords(const GWSurfacePos & pos) const { return createCoords(pos.x, pos.y); } inline GWCoords createCoordsRVN(const GWSurfacePos & pos, const unsigned short nScale) const { return createCoordsRVN(pos.x, pos.y, nScale); } private: GeoHeightMap _hm; unsigned short _nDEMLOD; Stratum _stratum; StratumSyncOp _ssop; /* LGWSBBA: Section bbox computation algorithm: * - Aligned along tile-boundary * - Iff padding: * - Minimum extents minus one-half tile dimension * - Maximum extents plus one-half tile dimension * - Maximum extents plus one tile dimension */ }; class RegionContainer : private std::list< mars::BBox< long > > { private: const unsigned long _nWWidth, _nWHeight; public: RegionContainer(const unsigned long nWWidth, const unsigned long nWHeight) : _nWWidth(nWWidth), _nWHeight(nWHeight) {} inline void addRegion (const mars::BBox< long > & bbox) { assert(!intersects(bbox)); push_back(bbox); } bool removeRegion (const mars::BBox< long > & bbox); bool intersects (const mars::BBox< long > & bbox) const; inline bool empty () const { return std::list< mars::BBox< long > >::empty(); } }; class QTMorphs : private mars::QuadTree_BBoxIndexed< GeoMorph, long > { private: unsigned long _nWWidth, _nWHeight; public: using MorphIterator = mars::QuadTree_BBoxIndexed< GeoMorph, long >::EntityRectIterator; using Region = mars::QuadTree_BBoxIndexed< GeoMorph, long >::Region; using VECTOR = mars::QuadTree_BBoxIndexed< GeoMorph, long >::VECTOR; QTMorphs (const unsigned long nWWidth, const unsigned long nWHeight) : QuadTree_BBoxIndexed(std::max(nWWidth, nWHeight)), _nWWidth(nWWidth), _nWHeight(nWHeight) {} bool addMorph (mars::ptr gm); inline MorphIterator morphs (const mars::BBox< long > & bbox) { return morphs(Region(bbox.getMinimum(), bbox.getMaximum())); } inline MorphIterator morphs (const long nLeft, const long nTop, const long nRight, const long nBottom) { return morphs(Region(VECTOR(nLeft, nTop), VECTOR(nRight, nBottom))); } inline MorphIterator morphs (const Region & rg) { return contents(rg); } }; class AgentMap { private: class FieldTypeWrapper { public: FieldType type; FieldTypeWrapper(const FieldType enftType) : type(enftType) {} inline operator FieldType () const { return type; } }; public: FieldType type; const unsigned long width, height; const float base; AgentMap (const FieldType enftType, const unsigned long nWidth, const unsigned long nHeight, const float fBase = 0.0f) : type(enftType), width(nWidth), height(nHeight), _qtField(std::max(nWidth, nHeight)), base(fBase) {} inline void addSpherePack (const mars::ptr< GeoSpherePack > & gsp) { gsp->store(_qtField, mars::ptr< FieldTypeWrapper > (new FieldTypeWrapper(type))); } inline float sample (const long x, const long y, const long z) const { return sample (GWCoords(x, y, z)); } inline mars::vector3Df gradient (const long x, const long y, const long z) const { return gradient (GWCoords(x, y, z)); } float sample (const GWCoords & coords) const; mars::vector3Df gradient (const GWCoords & coords) const; private: typedef mars::QuadTree_SphereIndexed< FieldTypeWrapper, GeoSpherePack::SCALAR > QuadTreeSpherePacks; // TODO: Name collision in worldfile.h typedef std::vector< std::pair< std::string, mars::ptr< GeoSpherePack > > > SphPackList; SphPackList _sphpacks; QuadTreeSpherePacks _qtField; }; class AllAgentMaps { public: AgentMap density; AllAgentMaps (const unsigned long nWidth, const unsigned long nHeight); AgentMap & operator [] ( const FieldType enftType ); const AgentMap & operator [] ( const FieldType enftType ) const; }; // TODO: Move into DepositSynthesizer class MineralsContainer { public: inline void addSpherePack (const std::string & sName, const mars::ptr< GeoSpherePack > & rsp) { _sphpacks.push_back(std::pair< std::string, mars::ptr< GeoSpherePack > > (sName, rsp)); } void flushTo (FileSource::MineralMap & minmap); private: typedef std::vector< std::pair< std::string, mars::ptr< GeoSpherePack > > > SphPackList; SphPackList _sphpacks; }; class IGeoWorldListener { public: virtual void runningMorph (const mars::ptr< GeoMorph > & pGM, const size_t i, const size_t nTotal) {}; virtual void runningMorphs (MorphList::const_iterator i0, MorphList::const_iterator iN, const size_t i, const size_t nCount, const size_t nTotal) {}; virtual void rejectedMorph (const mars::ptr< GeoMorph > & pGM) {}; }; class PagedGeoWorld : public mars::Observable< IGeoWorldListener > { private: unsigned short _nNumProcs; GW_MUTEX(mutex); mutable FileSource * _pFile; mutable RegionContainer * _plsbboxLockedMutableRegions; DepositSynthesizer * _pDepSynth; struct Pass { QTMorphs quadtree; MorphList list; Pass(const unsigned long nWWidth, const unsigned long nWHeight) : quadtree(nWWidth, nWHeight) {} }; typedef std::vector< Pass * > PassList; PassList _passes; AllAgentMaps * _pAgentMaps; protected: typedef std::set< mars::ptr< GeoMorph > > MorphSet; enum Mode { Read, Write }; class Picks : public MorphSet { private: mars::BBox< long > _bbox; public: inline const mars::BBox< long > & getBBox() const { return _bbox; } inline void setBBox (const mars::BBox< long > & bbox) { _bbox = bbox; } void computeBBox (); void consume( const IMineralQuery * pQMin, LockedGWSection * pSection, MineralsContainer & ctrMins ); }; struct WorkItem { mars::ptr< Picks > picks; LockedGWSection * section; MineralsContainer minerals; WorkItem () : picks(NULL), section(NULL) {} WorkItem(const mars::ptr< Picks > & pPicks) : picks(pPicks), section(NULL) {} WorkItem(const mars::ptr< GeoMorph > & pGM, const mars::BBox< long > & bbox = mars::BBox< long > ()) : picks(new Picks()) { picks->insert(pGM); if (bbox.empty()) picks->computeBBox(); else picks->setBBox(bbox); } }; struct WorkQueue { typedef std::deque< WorkItem > ItemQueue; ItemQueue inbox, outbox, ready; }; class IWorkQueueListener { public: virtual void readyQueue (const WorkQueue::ItemQueue & readyq) {} virtual void collisions (const WorkQueue::ItemQueue & colq) {} virtual void unlockQueue (const WorkQueue::ItemQueue & unlq) {} }; class MasterSet : private MorphSet { private: QTMorphs * _pQTMorphs; GW_MUTEX(mutex); public: MasterSet (QTMorphs * pQTMorphs, MorphList::const_iterator iMorphs0, MorphList::const_iterator iMorphsN); bool empty (); mars::ptr< Picks > collect( const QTMorphs::Region & rgBBox ); void push (const mars::ptr< Picks > & pPicks); MorphSet::size_type size() const { return MorphSet::size(); } const_iterator begin () const { return MorphSet::begin(); } const_iterator end () const { return MorphSet::end(); } }; LockedGWSection * doLock (const mars::BBox< long > & bbox, const bool bPad = true, const bool bReadOnly = false) const; template< Mode MODE, typename TYPE_LockedGWSection > void performIOOperation (TYPE_LockedGWSection * pSection) const; template< Mode MODE > inline void blockOp ( LockedGWSection * pSection, FileSource::MainDEM::Block & block, const unsigned short i, const unsigned short j ) const { switch (MODE) { case Read: readBlock(pSection, block, i, j); break; case Write: writeBlock(pSection, block, i, j); break; } } template< Mode MODE > inline void seekTileOp (const int nTX, const int nTY, const std::ios_base::seekdir dir = std::ios_base::cur) const { switch (MODE) { case Read: _pFile->tilemap.seekg(nTX, nTY, dir); break; case Write: _pFile->tilemap.seekp(nTX, nTY, dir); break; } } void readBlock( LockedGWSection * pSection, FileSource::MainDEM::Block & block, const unsigned short i, const unsigned short j ) const; void writeBlock( const LockedGWSection * pSection, FileSource::MainDEM::Block & block, const unsigned short i, const unsigned short j ) const; void processWorkQueue (PagedGeoWorld::WorkQueue & q, IWorkQueueListener * pListener = NULL); void flushMinerals (MineralsContainer & ctrMins); void flushOutbox( WorkQueue::ItemQueue &outbox ); bool isPickValid (const Picks & p) const; bool isValidSectionBBox (const mars::BBox< long > & bbox) const; bool checkGeoMorph( const mars::ptr &gm ); public: static const unsigned short DEFAULT_NUM_PROCS = 16; PagedGeoWorld (DepositSynthesizer * pDepSynth, FileSource * pFile, const unsigned short nPasses); ~PagedGeoWorld(); inline void setNumProcs(const unsigned short nNumProcs) { _nNumProcs = nNumProcs; } inline const unsigned short getLOD () const { return 0; } // TODO: Get rid of LOD for PagedGeoWorld unsigned short getMaxGeoMorphWidth () const; unsigned short getMaxGeoMorphHeight () const; void addMorphs (MorphList & morphs, MorphList & rejects, const unsigned short nPass); bool addMorph (mars::ptr & gm, const unsigned short nPass); inline MorphList::const_iterator beginPass (const unsigned short nPass) const { return _passes[nPass]->list.begin(); } inline MorphList::iterator beginPass (const unsigned short nPass) { return _passes[nPass]->list.begin(); } inline MorphList::const_iterator endPass (const unsigned short nPass) const { return _passes[nPass]->list.end(); } inline MorphList::iterator endPass (const unsigned short nPass) { return _passes[nPass]->list.end(); } inline unsigned short getPassCount () const { return _passes.size(); } void runMorphs (const unsigned short nPass, MorphList & rejects); inline const AllAgentMaps & getAgentMaps () const { assert(_pAgentMaps != NULL); return *_pAgentMaps; } inline AllAgentMaps & getAgentMaps () { assert(_pAgentMaps != NULL); return *_pAgentMaps; } inline LockedGWSection * lock (const mars::BBox< long > & bbox, const bool bPad = true) { return doLock(bbox, bPad, false); } inline const LockedGWSection * lockro (const mars::BBox< long > & bbox, const bool bPad = true) const { return doLock(bbox, bPad, true); } inline LockedGWSection * lock (const long x, const long y, const unsigned short nWidth, const unsigned short nHeight, const bool bPad = true) { return doLock (mars::BBox< long > (x, y, x + nWidth - 1, y + nHeight - 1), bPad, false); } inline const LockedGWSection * lockro (const long x, const long y, const unsigned short nWidth, const unsigned short nHeight, const bool bPad = true) const { return doLock (mars::BBox< long > (x, y, x + nWidth - 1, y + nHeight - 1), bPad, true); } void unlock (LockedGWSection * pSection); void unlock (const LockedGWSection * pSection) const; inline void operator >> (mars::ScalarFieldView< GeoHeightMap::Precision > & view) const { assert(_pFile != NULL); _pFile->tilemap >> view; } inline const unsigned long getWorldWidth () const { return static_cast< unsigned long > (_pFile->tilemap.getTilesWide()) * static_cast< unsigned long > (_pFile->tilemap.getVirtualTileDim()); } inline const unsigned long getWorldHeight () const { return static_cast< unsigned long > (_pFile->tilemap.getTilesHigh()) * static_cast< unsigned long > (_pFile->tilemap.getVirtualTileDim()); } inline const unsigned short getTileDimension () const { return _pFile->tilemap.getVirtualTileDim(); } }; class GeoWorld : public mars::Observable< IGeoWorldListener > { private: unsigned short _nLODLowest, _nLODMid, _nTileDim; GeoHeightMap * _phmLowest; DepositSynthesizer * _pDepSynth; MineralsContainer _ctrMins; AllAgentMaps * _pAllAgentMaps; unsigned long _nLowWidth, _nLowHeight; FileSource * _pFile; MorphList _morphs; mutable RegionContainer * _plsbboxLockedMutableRegions; GW_MUTEX(mutex); protected: LockedGWSection * lock (const mars::BBox< long > & bbox, const bool bReadOnly) const; LockedGWSection * lock (const bool bReadOnly) const; void runMorph( const MineralsDB & mindb, mars::ptr< GeoMorph > pGM, LockedGWSection * pLockedSection ); bool checkGeoMorph( const mars::ptr &gm ); public: static const unsigned short MIN_LOWEST_LOD = 2; GeoWorld ( DepositSynthesizer * pDepSynth, const unsigned short nLODLowest, const unsigned short nLODMid, FileSource * pFile ); ~GeoWorld(); inline AllAgentMaps & getAgentMaps () { return *_pAllAgentMaps; } inline const AllAgentMaps & getAgentMaps () const { return *_pAllAgentMaps; } inline unsigned long getWidth () const { return _nLowWidth; } inline unsigned long getHeight () const { return _nLowHeight; } inline unsigned short getLOD () const { return _nLODLowest; } inline unsigned short getDeltaLOD () const { return _nLODLowest - _nLODMid; } unsigned short getMaxGeoMorphWidth () const; unsigned short getMaxGeoMorphHeight () const; void addMorphs( MorphList & morphs, MorphList & rejects ); bool addMorph (mars::ptr & gm); inline MorphList::const_iterator beginMorphs () const { return _morphs.begin(); } inline MorphList::const_iterator endMorphs () const { return _morphs.end(); } inline MorphList::iterator beginMorphs () { return _morphs.begin(); } inline MorphList::iterator endMorphs () { return _morphs.end(); } inline LockedGWSection * lock () { return lock(false); } inline const LockedGWSection * lock () const { return lock(true); } inline LockedGWSection * lock (const mars::BBox< long > & bbox) { return lock(bbox, false); } inline const LockedGWSection * lock (const mars::BBox< long > & bbox) const { return lock(bbox, true); } inline LockedGWSection * lock (const long x, const long y, const unsigned short nWidth, const unsigned short nHeight) { return lock (mars::BBox< long > (x, y, x + nWidth - 1, y + nHeight - 1)); } inline const LockedGWSection * lock (const long x, const long y, const unsigned short nWidth, const unsigned short nHeight) const { return lock (mars::BBox< long > (x, y, x + nWidth - 1, y + nHeight - 1)); } void unlock (LockedGWSection * pSection); void unlock (const LockedGWSection * pSection) const; void runMorphs (const MineralsDB & mindb, MorphList & rejects); void write(const float fCoarseness); inline const GeoHeightMap & getLowHM () const { return *_phmLowest; } inline void operator << (const mars::ScalarFieldView< GeoHeightMap::Precision > & view) { *_phmLowest << view; } inline void operator >> (mars::ScalarFieldView< GeoHeightMap::Precision > & view) const { *_phmLowest >> view; } }; } #endif