X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FScenery%2Ftilemgr.cxx;h=298b05c50d20bcf5a081ce1356cef564f1f14248;hb=948aa70af78626584042b70fdf3c3babfb5aa804;hp=e5cd08bbb281bd8b9358b71f0cd468d831bbde06;hpb=9604908a8d38767345410447b75a13582158128e;p=flightgear.git diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index e5cd08bbb..298b05c50 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -29,116 +29,155 @@ #include #include +#include #include #include #include #include -#include +#include +#include #include
#include
-#include
-#include
+#include +#include #include +#include #include "scenery.hxx" #include "SceneryPager.hxx" #include "tilemgr.hxx" -using std::for_each; using flightgear::SceneryPager; -using simgear::SGModelLib; -using simgear::TileEntry; -using simgear::TileCache; + FGTileMgr::FGTileMgr(): state( Start ), - vis( 16000 ) + last_state( Running ), + longitude(-1000.0), + latitude(-1000.0), + scheduled_visibility(100.0), + _terra_sync(NULL), + _visibilityMeters(fgGetNode("/environment/visibility-m", true)), + _maxTileRangeM(fgGetNode("/sim/rendering/static-lod/bare", true)), + _disableNasalHooks(fgGetNode("/sim/temp/disable-scenery-nasal", true)), + _scenery_loaded(fgGetNode("/sim/sceneryloaded", true)), + _scenery_override(fgGetNode("/sim/sceneryloaded-override", true)), + _pager(FGScenery::getPagerSingleton()) { } -FGTileMgr::~FGTileMgr() { +FGTileMgr::~FGTileMgr() +{ // remove all nodes we might have left behind osg::Group* group = globals->get_scenery()->get_terrain_branch(); group->removeChildren(0, group->getNumChildren()); + // clear OSG cache + osgDB::Registry::instance()->clearObjectCache(); } // Initialize the Tile Manager subsystem -int FGTileMgr::init() { +void FGTileMgr::init() { SG_LOG( SG_TERRAIN, SG_INFO, "Initializing Tile Manager subsystem." ); - _options = new SGReaderWriterBTGOptions; - _options->setMatlib(globals->get_matlib()); - _options->setUseRandomObjects(fgGetBool("/sim/rendering/random-objects", true)); - _options->setUseRandomVegetation(fgGetBool("/sim/rendering/random-vegetation", true)); + _options = new simgear::SGReaderWriterOptions; + _options->setMaterialLib(globals->get_matlib()); + _options->setPropertyNode(globals->get_props()); + osgDB::FilePathList &fp = _options->getDatabasePathList(); const string_list &sc = globals->get_fg_scenery(); fp.clear(); std::copy(sc.begin(), sc.end(), back_inserter(fp)); + _options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root()); + if (!_disableNasalHooks->getBoolValue()) + _options->setModelData(new FGNasalModelDataProxy); - TileEntry::setModelLoadHelper(this); + reinit(); +} - tile_cache.init(); +void FGTileMgr::refresh_tile(void* tileMgr, long tileIndex) +{ + ((FGTileMgr*) tileMgr)->tile_cache.refresh_tile(tileIndex); +} +void FGTileMgr::reinit() +{ + _terra_sync = static_cast (globals->get_subsystem("terrasync")); + if (_terra_sync) + _terra_sync->setTileRefreshCb(&refresh_tile, this); + + // protect against multiple scenery reloads and properly reset flags, + // otherwise aircraft fall through the ground while reloading scenery + if (!fgGetBool("/sim/sceneryloaded",true)) + return; + fgSetBool("/sim/sceneryloaded",false); + fgSetDouble("/sim/startup/splash-alpha", 1.0); + + // Reload the materials definitions + _options->setMaterialLib(globals->get_matlib()); + + // remove all old scenery nodes from scenegraph and clear cache + osg::Group* group = globals->get_scenery()->get_terrain_branch(); + group->removeChildren(0, group->getNumChildren()); + tile_cache.init(); + + // clear OSG cache, except on initial start-up + if (state != Start) + { + osgDB::Registry::instance()->clearObjectCache(); + } + state = Inited; - + previous_bucket.make_bad(); current_bucket.make_bad(); - longitude = latitude = -1000.0; + scheduled_visibility = 100.0; - return 1; + // force an update now + update(0.0); } -// schedule a tile for loading -void FGTileMgr::sched_tile( const SGBucket& b, const bool is_inner_ring ) { +/* schedule a tile for loading, keep request for given amount of time. + * Returns true if tile is already loaded. */ +bool FGTileMgr::sched_tile( const SGBucket& b, double priority, bool current_view, double duration) +{ // see if tile already exists in the cache TileEntry *t = tile_cache.get_tile( b ); - - if ( !t ) { - // make space in the cache - SceneryPager* pager = FGScenery::getPagerSingleton(); - while ( (int)tile_cache.get_size() > tile_cache.get_max_cache_size() ) { - long index = tile_cache.get_oldest_tile(); - if ( index >= 0 ) { - TileEntry *old = tile_cache.get_tile( index ); - tile_cache.clear_entry( index ); - osg::ref_ptr subgraph = old->getNode(); - old->removeFromSceneGraph(); - delete old; - // zeros out subgraph ref_ptr, so subgraph is owned by - // the pager and will be deleted in the pager thread. - pager->queueDeleteRequest(subgraph); - } else { - // nothing to free ?!? forge ahead - break; - } - } - + if (!t) + { // create a new entry - TileEntry *e = new TileEntry( b ); - - // insert the tile into the cache - if ( tile_cache.insert_tile( e ) ) { - // update_queues will generate load request - } else { + t = new TileEntry( b ); + // insert the tile into the cache, update will generate load request + if ( tile_cache.insert_tile( t ) ) + { + // Attach to scene graph + + t->addToSceneGraph(globals->get_scenery()->get_terrain_branch()); + } else + { // insert failed (cache full with no available entries to // delete.) Try again later - delete e; + delete t; + return false; } - // Attach to scene graph - e->addToSceneGraph(globals->get_scenery()->get_terrain_branch()); - } else { - t->set_inner_ring( is_inner_ring ); + + SG_LOG( SG_TERRAIN, SG_DEBUG, " New tile cache size " << (int)tile_cache.get_size() ); } -} + // update tile's properties + tile_cache.request_tile(t,priority,current_view,duration); + + return t->is_loaded(); +} -// schedule a needed buckets for loading -void FGTileMgr::schedule_needed( double vis, const SGBucket& curr_bucket) { +/* schedule needed buckets for the current view position for loading, + * keep request for given amount of time */ +void FGTileMgr::schedule_needed(const SGBucket& curr_bucket, double vis) +{ // sanity check (unfortunately needed!) if ( longitude < -180.0 || longitude > 180.0 || latitude < -90.0 || latitude > 90.0 ) @@ -146,24 +185,20 @@ void FGTileMgr::schedule_needed( double vis, const SGBucket& curr_bucket) { SG_LOG( SG_TERRAIN, SG_ALERT, "Attempting to schedule tiles for bogus lon and lat = (" << longitude << "," << latitude << ")" ); - return; // FIXME - SG_LOG( SG_TERRAIN, SG_ALERT, - "This is a FATAL error. Exiting!" ); - exit(-1); + return; } SG_LOG( SG_TERRAIN, SG_INFO, "scheduling needed tiles for " << longitude << " " << latitude ); - // vis = fgGetDouble("/environment/visibility-m"); - double tile_width = curr_bucket.get_width_m(); double tile_height = curr_bucket.get_height_m(); // cout << "tile width = " << tile_width << " tile_height = " // << tile_height << endl; - xrange = (int)(vis / tile_width) + 1; - yrange = (int)(vis / tile_height) + 1; + double tileRangeM = std::min(vis,_maxTileRangeM->getDoubleValue()); + int xrange = (int)(tileRangeM / tile_width) + 1; + int yrange = (int)(tileRangeM / tile_height) + 1; if ( xrange < 1 ) { xrange = 1; } if ( yrange < 1 ) { yrange = 1; } @@ -174,283 +209,252 @@ void FGTileMgr::schedule_needed( double vis, const SGBucket& curr_bucket) { // cout << "max cache size = " << tile_cache.get_max_cache_size() // << " current cache size = " << tile_cache.get_size() << endl; - // clear the inner ring flags so we can set them below. This - // prevents us from having "true" entries we aren't able to find - // to get rid of if we teleport a long ways away from the current - // location. - tile_cache.clear_inner_ring_flags(); + // clear flags of all tiles belonging to the previous view set + tile_cache.clear_current_view(); - SGBucket b; + // update timestamps, so all tiles scheduled now are *newer* than any tile previously loaded + osg::FrameStamp* framestamp + = globals->get_renderer()->getViewer()->getFrameStamp(); + tile_cache.set_current_time(framestamp->getReferenceTime()); - // schedule center tile first so it can be loaded first - b = sgBucketOffset( longitude, latitude, 0, 0 ); - sched_tile( b, true ); + SGBucket b; int x, y; - // schedule next ring of 8 tiles - for ( x = -1; x <= 1; ++x ) { - for ( y = -1; y <= 1; ++y ) { - if ( x != 0 || y != 0 ) { - b = sgBucketOffset( longitude, latitude, x, y ); - sched_tile( b, true ); - } - } - } - - // schedule remaining tiles - for ( x = -xrange; x <= xrange; ++x ) { - for ( y = -yrange; y <= yrange; ++y ) { - if ( x < -1 || x > 1 || y < -1 || y > 1 ) { - SGBucket b = sgBucketOffset( longitude, latitude, x, y ); - sched_tile( b, false ); - } + /* schedule all tiles, use distance-based loading priority, + * so tiles are loaded in innermost-to-outermost sequence. */ + for ( x = -xrange; x <= xrange; ++x ) + { + for ( y = -yrange; y <= yrange; ++y ) + { + SGBucket b = sgBucketOffset( longitude, latitude, x, y ); + float priority = (-1.0) * (x*x+y*y); + sched_tile( b, priority, true, 0.0 ); } } } - -void FGTileMgr::initialize_queue() +/** + * Update the various queues maintained by the tilemagr (private + * internal function, do not call directly.) + */ +void FGTileMgr::update_queues() { - // First time through or we have teleported, initialize the - // system and load all relavant tiles - - SG_LOG( SG_TERRAIN, SG_INFO, "Initialize_queue(): Updating Tile list for " - << current_bucket ); - // cout << "tile cache size = " << tile_cache.get_size() << endl; - - // wipe/initialize tile cache - // tile_cache.init(); - previous_bucket.make_bad(); - - // build the local area list and schedule tiles for loading + osg::FrameStamp* framestamp + = globals->get_renderer()->getViewer()->getFrameStamp(); + double current_time = framestamp->getReferenceTime(); + double vis = _visibilityMeters->getDoubleValue(); + TileEntry *e; + int loading=0; + int sz=0; - // start with the center tile and work out in concentric - // "rings" + tile_cache.set_current_time( current_time ); + tile_cache.reset_traversal(); - double visibility_meters = fgGetDouble("/environment/visibility-m"); - schedule_needed(visibility_meters, current_bucket); -} + while ( ! tile_cache.at_end() ) + { + e = tile_cache.get_current(); + // cout << "processing a tile" << endl; + if ( e ) + { + // Prepare the ssg nodes corresponding to each tile. + // Set the ssg transform and update it's range selector + // based on current visibilty + e->prep_ssg_node(vis); -osg::Node* -FGTileMgr::loadTileModel(const string& modelPath, bool cacheModel) -{ - SGPath fullPath; - if (fgGetBool("/sim/paths/use-custom-scenery-data") == true) { - string_list sc = globals->get_fg_scenery(); - - for (string_list_iterator it = sc.begin(); it != sc.end(); ++it) { - SGPath tmpPath(*it); - tmpPath.append(modelPath); - if (tmpPath.exists()) { - fullPath = tmpPath; - break; - } + if (( !e->is_loaded() )&& + ((!e->is_expired(current_time))|| + e->is_current_view() )) + { + // schedule tile for loading with osg pager + _pager->queueRequest(e->tileFileName, + e->getNode(), + e->get_priority(), + framestamp, + e->getDatabaseRequest(), + _options.get()); + loading++; + } + } else + { + SG_LOG(SG_TERRAIN, SG_ALERT, "Warning: empty tile in cache!"); } - } else { - fullPath.append(modelPath); - } - osg::Node* result = 0; - try { - if(cacheModel) - result = - SGModelLib::loadModel(fullPath.str(), globals->get_props(), - new FGNasalModelData); - else - result= - SGModelLib::loadPagedModel(modelPath, globals->get_props(), - new FGNasalModelData); - } catch (const sg_io_exception& exc) { - string m(exc.getMessage()); - m += " "; - m += exc.getLocation().asString(); - SG_LOG( SG_ALL, SG_ALERT, m ); - } catch (const sg_exception& exc) { // XXX may be redundant - SG_LOG( SG_ALL, SG_ALERT, exc.getMessage()); + tile_cache.next(); + sz++; } - return result; -} - -// Helper class for STL fun -class TileLoad : public std::unary_function -{ -public: - TileLoad(SceneryPager *pager, osg::FrameStamp* framestamp, - osg::Group* terrainBranch, osgDB::ReaderWriter::Options* options) : - _pager(pager), _framestamp(framestamp), _options(options) {} - - TileLoad(const TileLoad& rhs) : - _pager(rhs._pager), _framestamp(rhs._framestamp), - _options(rhs._options) {} - void operator()(TileCache::tile_map::value_type& tilePair) + int drop_count = sz - tile_cache.get_max_cache_size(); + if (( drop_count > 0 )&& + ((loading==0)||(drop_count > 10))) { - TileEntry* entry = tilePair.second; - if (entry->getNode()->getNumChildren() == 0) { - _pager->queueRequest(entry->tileFileName, - entry->getNode(), - entry->get_inner_ring() ? 10.0f : 1.0f, - _framestamp, - entry->getDatabaseRequest(), - _options); + long drop_index = tile_cache.get_drop_tile(); + while ( drop_index > -1 ) + { + // schedule tile for deletion with osg pager + TileEntry* old = tile_cache.get_tile(drop_index); + tile_cache.clear_entry(drop_index); + + osg::ref_ptr subgraph = old->getNode(); + old->removeFromSceneGraph(); + delete old; + // zeros out subgraph ref_ptr, so subgraph is owned by + // the pager and will be deleted in the pager thread. + _pager->queueDeleteRequest(subgraph); + + if (--drop_count > 0) + drop_index = tile_cache.get_drop_tile(); + else + drop_index = -1; } } -private: - SceneryPager* _pager; - osg::FrameStamp* _framestamp; - osgDB::ReaderWriter::Options* _options; -}; - -/** - * Update the various queues maintained by the tilemagr (private - * internal function, do not call directly.) - */ -void FGTileMgr::update_queues() -{ - SceneryPager* pager = FGScenery::getPagerSingleton(); - osg::FrameStamp* framestamp - = globals->get_renderer()->getViewer()->getFrameStamp(); - tile_cache.set_current_time(framestamp->getReferenceTime()); - for_each(tile_cache.begin(), tile_cache.end(), - TileLoad(pager, - framestamp, - globals->get_scenery()->get_terrain_branch(), _options.get())); } - // given the current lon/lat (in degrees), fill in the array of local // chunks. If the chunk isn't already in the cache, then read it from // disk. -int FGTileMgr::update( double visibility_meters ) +void FGTileMgr::update(double) { - SGVec3d viewPos = globals->get_current_view()->get_view_pos(); - return update(SGGeod::fromCart(viewPos), visibility_meters); + double vis = _visibilityMeters->getDoubleValue(); + schedule_tiles_at(globals->get_view_position(), vis); + + update_queues(); + + // scenery loading check, triggers after each sim (tile manager) reinit + if (!_scenery_loaded->getBoolValue()) + { + bool fdmInited = fgGetBool("sim/fdm-initialized"); + bool positionFinalized = fgGetBool("sim/position-finalized"); + bool sceneryOverride = _scenery_override->getBoolValue(); + + // we are done if final position is set and the scenery & FDM are done. + // scenery-override can ignore the last two, but not position finalization. + if (positionFinalized && (sceneryOverride || (isSceneryLoaded() && fdmInited))) + { + _scenery_loaded->setBoolValue(true); + fgSplashProgress(""); + } + else + { + fgSplashProgress(positionFinalized ? "loading-scenery" : "finalize-position"); + // be nice to loader threads while waiting for initial scenery, reduce to 20fps + SGTimeStamp::sleepForMSec(50); + } + } } -int FGTileMgr::update( const SGGeod& location, double visibility_meters) +// schedule tiles for the viewer bucket +// (FDM/AI/groundcache/... should use "schedule_scenery" instead) +void FGTileMgr::schedule_tiles_at(const SGGeod& location, double range_m) { - SG_LOG( SG_TERRAIN, SG_DEBUG, "FGTileMgr::update()" ); - longitude = location.getLongitudeDeg(); latitude = location.getLatitudeDeg(); // SG_LOG( SG_TERRAIN, SG_DEBUG, "FGTileMgr::update() for " - // << longitude << " " << latatitude ); + // << longitude << " " << latitude ); current_bucket.set_bucket( location ); + + // schedule more tiles when visibility increased considerably + // TODO Calculate tile size - instead of using fixed value (5000m) + if (range_m - scheduled_visibility > 5000.0) + previous_bucket.make_bad(); + // SG_LOG( SG_TERRAIN, SG_DEBUG, "Updating tile list for " // << current_bucket ); fgSetInt( "/environment/current-tile-id", current_bucket.gen_index() ); - // do tile load scheduling. + // do tile load scheduling. // Note that we need keep track of both viewer buckets and fdm buckets. if ( state == Running ) { - SG_LOG( SG_TERRAIN, SG_DEBUG, "State == Running" ); + if (last_state != state) + { + SG_LOG( SG_TERRAIN, SG_DEBUG, "State == Running" ); + } if (current_bucket != previous_bucket) { // We've moved to a new bucket, we need to schedule any // needed tiles for loading. SG_LOG( SG_TERRAIN, SG_INFO, "FGTileMgr::update()" ); - schedule_needed(visibility_meters, current_bucket); + scheduled_visibility = range_m; + schedule_needed(current_bucket, range_m); + if (_terra_sync) + _terra_sync->schedulePosition(latitude,longitude); } + // save bucket + previous_bucket = current_bucket; } else if ( state == Start || state == Inited ) { - SG_LOG( SG_TERRAIN, SG_INFO, "State == Start || Inited" ); -// initialize_queue(); + SG_LOG( SG_TERRAIN, SG_DEBUG, "State == Start || Inited" ); + // do not update bucket yet (position not valid in initial loop) state = Running; - if (current_bucket != previous_bucket - && current_bucket.get_chunk_lon() != -1000) { - SG_LOG( SG_TERRAIN, SG_INFO, "FGTileMgr::update()" ); - schedule_needed(visibility_meters, current_bucket); - } + previous_bucket.make_bad(); } - - update_queues(); - - // save bucket... - previous_bucket = current_bucket; - - return 1; + last_state = state; } -void FGTileMgr::prep_ssg_nodes(float vis) { - - // traverse the potentially viewable tile list and update range - // selector and transform - - TileEntry *e; - tile_cache.reset_traversal(); - - while ( ! tile_cache.at_end() ) { - // cout << "processing a tile" << endl; - if ( (e = tile_cache.get_current()) ) { - e->prep_ssg_node(vis); - } else { - SG_LOG(SG_INPUT, SG_ALERT, "warning ... empty tile in cache"); - } - tile_cache.next(); - } -} - -bool FGTileMgr::scenery_available(const SGGeod& position, double range_m) +/** Schedules scenery for given position. Load request remains valid for given duration + * (duration=0.0 => nothing is loaded). + * Used for FDM/AI/groundcache/... requests. Viewer uses "schedule_tiles_at" instead. + * Returns true when all tiles for the given position are already loaded, false otherwise. + */ +bool FGTileMgr::schedule_scenery(const SGGeod& position, double range_m, double duration) { - // sanity check (unfortunately needed!) - if (position.getLongitudeDeg() < -180 || position.getLongitudeDeg() > 180 || - position.getLatitudeDeg() < -90 || position.getLatitudeDeg() > 90) - return false; - - SGBucket bucket(position); - TileEntry *te = tile_cache.get_tile(bucket); - if (!te || !te->is_loaded()) - return false; - - SGVec3d cartPos = SGVec3d::fromGeod(position); - - // Traverse all tiles required to be there for the given visibility. - // This uses exactly the same algorithm like the tile scheduler. - double tile_width = bucket.get_width_m(); - double tile_height = bucket.get_height_m(); - double tile_r = 0.5*sqrt(tile_width*tile_width + tile_height*tile_height); - double max_dist = tile_r + range_m; - double max_dist2 = max_dist*max_dist; + const float priority = 0.0; + double current_longitude = position.getLongitudeDeg(); + double current_latitude = position.getLatitudeDeg(); + bool available = true; + + // sanity check (unfortunately needed!) + if (current_longitude < -180 || current_longitude > 180 || + current_latitude < -90 || current_latitude > 90) + return false; - int xrange = (int)fabs(range_m / tile_width) + 1; - int yrange = (int)fabs(range_m / tile_height) + 1; + SGBucket bucket(position); + available = sched_tile( bucket, priority, false, duration ); - for ( int x = -xrange; x <= xrange; ++x ) { - for ( int y = -yrange; y <= yrange; ++y ) { - // We have already checked for the center tile. - if ( x != 0 || y != 0 ) { - SGBucket b = sgBucketOffset( position.getLongitudeDeg(), - position.getLatitudeDeg(), x, y ); - // Do not ask if it is just the next tile but way out of range. - if (max_dist2 < distSqr(cartPos, SGVec3d::fromGeod(b.get_center()))) - continue; - TileEntry *te = tile_cache.get_tile(b); - if (!te || !te->is_loaded()) - return false; - } - } - } - - // Survived all tests. - return true; -} - -namespace -{ -struct IsTileLoaded : - public std::unary_function -{ - bool operator()(const TileCache::tile_map::value_type& tilePair) const + if ((!available)&&(duration==0.0)) + return false; + + SGVec3d cartPos = SGVec3d::fromGeod(position); + + // Traverse all tiles required to be there for the given visibility. + double tile_width = bucket.get_width_m(); + double tile_height = bucket.get_height_m(); + double tile_r = 0.5*sqrt(tile_width*tile_width + tile_height*tile_height); + double max_dist = tile_r + range_m; + double max_dist2 = max_dist*max_dist; + + int xrange = (int)fabs(range_m / tile_width) + 1; + int yrange = (int)fabs(range_m / tile_height) + 1; + + for ( int x = -xrange; x <= xrange; ++x ) { - return tilePair.second->is_loaded(); + for ( int y = -yrange; y <= yrange; ++y ) + { + // We have already checked for the center tile. + if ( x != 0 || y != 0 ) + { + SGBucket b = sgBucketOffset( current_longitude, + current_latitude, x, y ); + double distance2 = distSqr(cartPos, SGVec3d::fromGeod(b.get_center())); + // Do not ask if it is just the next tile but way out of range. + if (distance2 <= max_dist2) + { + available &= sched_tile( b, priority, false, duration ); + if ((!available)&&(duration==0.0)) + return false; + } + } + } } -}; + + return available; } +// Returns true if tiles around current view position have been loaded bool FGTileMgr::isSceneryLoaded() { - return (std::find_if(tile_cache.begin(), tile_cache.end(), - std::not1(IsTileLoaded())) - == tile_cache.end()); + double range_m = 100.0; + if (scheduled_visibility < range_m) + range_m = scheduled_visibility; + + return schedule_scenery(SGGeod::fromDeg(longitude, latitude), range_m, 0.0); }