X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FScenery%2Ftilemgr.cxx;h=4ebce444cabacd8aac80694a2264e70b42bb0ac2;hb=578df0f4845f565592663f86ea690fc5e96a14a4;hp=26ce7135a5b1e7640c7e03e3e4521aad77334120;hpb=76958a038251a697ad798bce630e7d793797cf78;p=flightgear.git diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index 26ce7135a..4ebce444c 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -29,15 +29,14 @@ #include #include +#include #include #include -#include -#include -#include #include #include -#include +#include +#include #include
#include
@@ -55,28 +54,36 @@ using simgear::SGModelLib; using simgear::TileEntry; using simgear::TileCache; + FGTileMgr::FGTileMgr(): state( Start ), - vis( 16000 ) + last_state( Running ), + vis( 16000 ), + _terra_sync(NULL), + _visibilityMeters(fgGetNode("/environment/visibility-m", true)), + _maxTileRangeM(fgGetNode("/sim/rendering/static-lod/bare", true)) { } -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(); @@ -84,64 +91,78 @@ int FGTileMgr::init() { TileEntry::setModelLoadHelper(this); - tile_cache.init(); + reinit(); +} - state = Inited; +void FGTileMgr::refresh_tile(void* tileMgr, long tileIndex) +{ + ((FGTileMgr*) tileMgr)->tile_cache.refresh_tile(tileIndex); +} +void FGTileMgr::reinit() +{ + // 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; - return 1; + _terra_sync = (simgear::SGTerraSync*) globals->get_subsystem("terrasync"); + if (_terra_sync) + _terra_sync->setTileRefreshCb(&refresh_tile, this); + + // 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); -// schedule a needed buckets for loading -void FGTileMgr::schedule_needed( double vis, const SGBucket& curr_bucket) { + return t->is_loaded(); +} + +/* 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 ) @@ -149,24 +170,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()); + xrange = (int)(tileRangeM / tile_width) + 1; + yrange = (int)(tileRangeM / tile_height) + 1; if ( xrange < 1 ) { xrange = 1; } if ( yrange < 1 ) { yrange = 1; } @@ -177,122 +194,74 @@ 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() -{ - // 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 - - // start with the center tile and work out in concentric - // "rings" - - double visibility_meters = fgGetDouble("/environment/visibility-m"); - schedule_needed(visibility_meters, current_bucket); -} - osg::Node* FGTileMgr::loadTileModel(const string& modelPath, bool cacheModel) { + SGPath fullPath = modelPath; + if ((fullPath.isRelative())&& + (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) { + // fg_senery contains empty strings as "markers" (see FGGlobals::set_fg_scenery) + if (!it->empty()) { + SGPath tmpPath(*it); + tmpPath.append(modelPath); + if (tmpPath.exists()) { + fullPath = tmpPath; + break; + } + } + } + } osg::Node* result = 0; try { if(cacheModel) result = - SGModelLib::loadModel(modelPath, globals->get_props(), - new FGNasalModelData); - + SGModelLib::loadModel(fullPath.str(), globals->get_props(), + _disableNasalHooks->getBoolValue() ? NULL : new FGNasalModelDataProxy); else + { result= - SGModelLib::loadPagedModel(modelPath, globals->get_props(), - new FGNasalModelData); + SGModelLib::loadDeferredModel(fullPath.str(), globals->get_props(), + _disableNasalHooks->getBoolValue() ? NULL : new FGNasalModelDataProxy); + } } catch (const sg_io_exception& exc) { string m(exc.getMessage()); m += " "; m += exc.getLocation().asString(); - SG_LOG( SG_ALL, SG_ALERT, m ); + SG_LOG( SG_TERRAIN, SG_ALERT, m ); } catch (const sg_exception& exc) { // XXX may be redundant - SG_LOG( SG_ALL, SG_ALERT, exc.getMessage()); + SG_LOG( SG_TERRAIN, SG_ALERT, exc.getMessage()); } 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) - { - TileEntry* entry = tilePair.second; - if (entry->getNode()->getNumChildren() == 0) { - _pager->queueRequest(entry->tileFileName, - entry->getNode(), - entry->get_inner_ring() ? 10.0f : 1.0f, - _framestamp, -#ifdef FGOSGPAGER25 - entry->getDatabaseRequest(), -#endif - _options); - } - } -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.) @@ -302,145 +271,200 @@ 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())); -} + double current_time = framestamp->getReferenceTime(); + double vis = _visibilityMeters->getDoubleValue(); + TileEntry *e; + int loading=0; + int sz=0; + + tile_cache.set_current_time( current_time ); + tile_cache.reset_traversal(); + + 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); + + 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!"); + } + tile_cache.next(); + sz++; + } + int drop_count = sz - tile_cache.get_max_cache_size(); + if (( drop_count > 0 )&& + ((loading==0)||(drop_count > 10))) + { + 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; + } + } +} // 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) { - SGLocation *location = globals->get_current_view()->getSGLocation(); - return update( location, visibility_meters ); -} + SGVec3d viewPos = globals->get_current_view()->get_view_pos(); + double vis = _visibilityMeters->getDoubleValue(); + schedule_tiles_at(SGGeod::fromCart(viewPos), vis); + update_queues(); +} -int FGTileMgr::update( SGLocation *location, double visibility_meters ) +// schedule tiles for the viewer bucket (FDM/AI/groundcache/... use +// "schedule_scenery" instead +int FGTileMgr::schedule_tiles_at(const SGGeod& location, double range_m) { - SG_LOG( SG_TERRAIN, SG_DEBUG, "FGTileMgr::update()" ); - - longitude = location->getLongitude_deg(); - latitude = location->getLatitude_deg(); - // add 1.0m to the max altitude to give a little leeway to the - // ground reaction code. - altitude_m = location->getAltitudeASL_ft() * SG_FEET_TO_METER + 1.0; - - // if current altitude is apparently not initialized, set max - // altitude to something big. - if ( altitude_m < -1000 ) { - altitude_m = 10000; - } + longitude = location.getLongitudeDeg(); + latitude = location.getLatitudeDeg(); + // SG_LOG( SG_TERRAIN, SG_DEBUG, "FGTileMgr::update() for " // << longitude << " " << latatitude ); - current_bucket.set_bucket( 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; + last_state = state; return 1; } -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(double lat, double lon, 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 ( lon <= -180.0 || lon >= 180.0 || lat <= -90.0 || lat >= 90.0 ) - return false; - - SGBucket bucket(lon, lat); - TileEntry *te = tile_cache.get_tile(bucket); - if (!te || !te->is_loaded()) - return false; - - // 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(); + 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( lon, lat, x, y ); - 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); }