# include <config.h>
#endif
+#include <algorithm>
+#include <functional>
+
+#include <osgViewer/Viewer>
+
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
-#include <simgear/math/point3d.hxx>
-#include <simgear/math/polar3d.hxx>
-#include <simgear/math/sg_geodesy.hxx>
-#include <simgear/math/vector.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/scene/model/modellib.hxx>
+#include <simgear/scene/tgdb/SGReaderWriterBTGOptions.hxx>
+#include <simgear/scene/tsync/terrasync.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
+#include <Main/renderer.hxx>
#include <Main/viewer.hxx>
#include <Scripting/NasalSys.hxx>
-#include "newcache.hxx"
#include "scenery.hxx"
+#include "SceneryPager.hxx"
#include "tilemgr.hxx"
-#define TEST_LAST_HIT_CACHE
+using std::for_each;
+using flightgear::SceneryPager;
+using simgear::SGModelLib;
+using simgear::TileEntry;
+using simgear::TileCache;
+
+
+// helper: listen to property changes affecting tile loading
+class LoaderPropertyWatcher : public SGPropertyChangeListener
+{
+public:
+ LoaderPropertyWatcher(FGTileMgr* pTileMgr) :
+ _pTileMgr(pTileMgr)
+ {
+ }
+
+ virtual void valueChanged(SGPropertyNode*)
+ {
+ _pTileMgr->configChanged();
+ }
+
+private:
+ FGTileMgr* _pTileMgr;
+};
-#if defined(ENABLE_THREADS)
-SGLockedQueue<FGTileEntry *> FGTileMgr::attach_queue;
-SGLockedQueue<FGDeferredModel *> FGTileMgr::model_queue;
-#else
-queue<FGTileEntry *> FGTileMgr::attach_queue;
-queue<FGDeferredModel *> FGTileMgr::model_queue;
-#endif // ENABLE_THREADS
-queue<FGTileEntry *> FGTileMgr::delete_queue;
-// Constructor
FGTileMgr::FGTileMgr():
state( Start ),
- current_tile( NULL ),
- vis( 16000 )
+ vis( 16000 ),
+ _terra_sync(NULL),
+ _propListener(new LoaderPropertyWatcher(this))
{
+ _randomObjects = fgGetNode("/sim/rendering/random-objects", true);
+ _randomVegetation = fgGetNode("/sim/rendering/random-vegetation", true);
+ _maxTileRangeM = fgGetNode("/sim/rendering/static-lod/bare", true);
}
-// Destructor
-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());
+ delete _propListener;
+ _propListener = NULL;
+ // 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." );
- tile_cache.init();
+ _options = new SGReaderWriterBTGOptions;
+ _options->setMatlib(globals->get_matlib());
-#if 0
+ _randomObjects.get()->addChangeListener(_propListener, false);
+ _randomVegetation.get()->addChangeListener(_propListener, false);
+ configChanged();
- // instead it's just a lot easier to let any pending work flush
- // through, rather than trying to arrest the queue and nuke all
- // the various work at all the various stages and get everything
- // cleaned up properly.
+ osgDB::FilePathList &fp = _options->getDatabasePathList();
+ const string_list &sc = globals->get_fg_scenery();
+ fp.clear();
+ std::copy(sc.begin(), sc.end(), back_inserter(fp));
- while ( ! attach_queue.empty() ) {
- attach_queue.pop();
- }
+ TileEntry::setModelLoadHelper(this);
+
+ _visibilityMeters = fgGetNode("/environment/visibility-m", true);
- while ( ! model_queue.empty() ) {
-#if defined(ENABLE_THREADS)
- FGDeferredModel* dm = model_queue.pop();
-#else
- FGDeferredModel* dm = model_queue.front();
- model_queue.pop();
-#endif
- delete dm;
- }
- loader.reinit();
-#endif
+ reinit();
+}
- state = Inited;
+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->setTileCache(&tile_cache);
+
+ // force an update now
+ update(0.0);
}
+void FGTileMgr::configChanged()
+{
+ _options->setUseRandomObjects(_randomObjects.get()->getBoolValue());
+ _options->setUseRandomVegetation(_randomVegetation.get()->getBoolValue());
+}
-// 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
- FGTileEntry *t = tile_cache.get_tile( b );
-
- if ( t == NULL ) {
- // make space in the cache
- while ( (int)tile_cache.get_size() > tile_cache.get_max_cache_size() ) {
- long index = tile_cache.get_oldest_tile();
- if ( index >= 0 ) {
- FGTileEntry *old = tile_cache.get_tile( index );
- // OSGFIXME
-// shadows->deleteOccluderFromTile( (ssgBranch *) old->get_terra_transform() );
- old->disconnect_ssg_nodes();
- delete_queue.push( old );
- tile_cache.clear_entry( index );
- } else {
- // nothing to free ?!? forge ahead
- break;
- }
- }
-
+ TileEntry *t = tile_cache.get_tile( b );
+ if (!t)
+ {
// create a new entry
- FGTileEntry *e = new FGTileEntry( b );
-
- // insert the tile into the cache
- if ( tile_cache.insert_tile( e ) ) {
- // Schedule tile for loading
- loader.add( e );
- } 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;
}
- } 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 )
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 = 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; }
- // note * 2 at end doubles cache size (for fdm and viewer)
+ // make the cache twice as large to avoid losing terrain when switching
+ // between aircraft and tower views
tile_cache.set_max_cache_size( (2*xrange + 2) * (2*yrange + 2) * 2 );
// cout << "xrange = " << xrange << " yrange = " << yrange << endl;
// 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()
+osg::Node*
+FGTileMgr::loadTileModel(const string& modelPath, bool cacheModel)
{
- // 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);
-
- // do we really want to lose this? CLO
-#if 0
- // Now force a load of the center tile and inner ring so we
- // have something to see in our first frame.
- int i;
- for ( i = 0; i < 9; ++i ) {
- if ( load_queue.size() ) {
- SG_LOG( SG_TERRAIN, SG_DEBUG,
- "Load queue not empty, loading a tile" );
-
- SGBucket pending = load_queue.front();
- load_queue.pop_front();
- load_tile( pending );
+ 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;
+ }
}
+ } else {
+ fullPath.append(modelPath);
}
-#endif
-}
-
-/**
- * return current status of queues
- *
- */
-
-bool FGTileMgr::all_queues_empty() {
- return attach_queue.empty() && model_queue.empty();
+ 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());
+ }
+ return result;
}
-
/**
* Update the various queues maintained by the tilemagr (private
* internal function, do not call directly.)
*/
void FGTileMgr::update_queues()
{
- // load the next model in the load queue. Currently this must
- // happen in the render thread because model loading can trigger
- // texture loading which involves use of the opengl api. Skip any
- // models belonging to not loaded tiles (i.e. the tile was removed
- // before we were able to load some of the associated models.)
- if ( !model_queue.empty() ) {
- bool processed_one = false;
-
- while ( model_queue.size() > 200 || processed_one == false ) {
- processed_one = true;
-
- if ( model_queue.size() > 200 ) {
- SG_LOG( SG_TERRAIN, SG_INFO,
- "Alert: catching up on model load queue" );
- }
+ SceneryPager* pager = FGScenery::getPagerSingleton();
+ 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;
+
+ tile_cache.set_current_time( current_time );
+ tile_cache.reset_traversal();
- // cout << "loading next model ..." << endl;
- // load the next tile in the queue
-#if defined(ENABLE_THREADS)
- FGDeferredModel* dm = model_queue.pop();
-#else
- FGDeferredModel* dm = model_queue.front();
- model_queue.pop();
-#endif
+ 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);
- // only load the model if the tile still exists in the
- // tile cache
- FGTileEntry *t = tile_cache.get_tile( dm->get_bucket() );
- if ( t != NULL ) {
- //OSGFIXME
-// ssgTexturePath( (char *)(dm->get_texture_path().c_str()) );
- try {
- osg::Node *obj_model =
- globals->get_model_lib()->load_model( ".",
- dm->get_model_path(),
- globals->get_props(),
- globals->get_sim_time_sec(),
- dm->get_cache_state(),
- new FGNasalModelData );
- if ( obj_model != NULL ) {
- dm->get_obj_trans()->addChild( obj_model );
- //OSGFIXME
-// shadows->addOccluder( (ssgBranch *) obj_model->getParent(0),
-// SGShadowVolume::occluderTypeTileObject,
-// (ssgBranch *) dm->get_tile()->get_terra_transform());
- }
- } 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());
- }
-
- dm->get_tile()->dec_pending_models();
+ 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++;
}
- delete dm;
+ } else
+ {
+ SG_LOG(SG_INPUT, SG_ALERT, "Warning: empty tile in cache!");
}
+ tile_cache.next();
+ sz++;
}
-
- // Notify the tile loader that it can load another tile
- loader.update();
-
- if ( !attach_queue.empty() ) {
-#if defined(ENABLE_THREADS)
- FGTileEntry* e = attach_queue.pop();
-#else
- FGTileEntry* e = attach_queue.front();
- attach_queue.pop();
-#endif
- e->add_ssg_nodes( globals->get_scenery()->get_terrain_branch() );
- // cout << "Adding ssg nodes for "
- }
-
- if ( !delete_queue.empty() ) {
- // cout << "delete queue = " << delete_queue.size() << endl;
- bool processed_one = false;
-
- while ( delete_queue.size() > 30 || processed_one == false ) {
- processed_one = true;
-
- if ( delete_queue.size() > 30 ) {
- // uh oh, delete queue is blowing up, we aren't clearing
- // it fast enough. Let's just panic, well not panic, but
- // get real serious and agressively free up some tiles so
- // we don't explode our memory usage.
-
- SG_LOG( SG_TERRAIN, SG_WARN,
- "Warning: catching up on tile delete queue" );
- }
- FGTileEntry* e = delete_queue.front();
- if ( e->free_tile() ) {
- delete_queue.pop();
- delete e;
- }
+ 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<osg::Object> 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 );
-}
+ SG_LOG( SG_TERRAIN, SG_DEBUG, "FGTileMgr::update()" );
+ 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.getLongitudeDeg();
+ latitude = location.getLatitudeDeg();
- 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;
- }
// 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 (!(current_bucket == previous_bucket )) {
+ 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();
+ // do not update bucket yet (position not valid in initial loop)
state = Running;
-
- // load the next tile in the load queue (or authorize the next
- // load in the case of the threaded tile pager)
- loader.update();
+ previous_bucket.make_bad();
}
- update_queues();
-
- // save bucket...
- previous_bucket = current_bucket;
-
return 1;
}
+/** 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)
+{
+ 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;
+
+ SGBucket bucket(position);
+ available = sched_tile( bucket, priority, false, duration );
+
+ if ((!available)&&(duration==0.0))
+ return false;
-// timer event driven call to scheduler for the purpose of refreshing the tile timestamps
-void FGTileMgr::refresh_view_timestamps() {
- SG_LOG( SG_TERRAIN, SG_INFO,
- "Refreshing timestamps for " << current_bucket.get_center_lon()
- << " " << current_bucket.get_center_lat() );
- if ( longitude >= -180.0 && longitude <= 180.0
- && latitude >= -90.0 && latitude <= 90.0 )
- {
- schedule_needed(fgGetDouble("/environment/visibility-m"), current_bucket);
- }
-}
-
-void FGTileMgr::prep_ssg_nodes(float vis) {
-
- // traverse the potentially viewable tile list and update range
- // selector and transform
+ SGVec3d cartPos = SGVec3d::fromGeod(position);
- FGTileEntry *e;
- tile_cache.reset_traversal();
+ // 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;
- 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");
+ 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( 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;
+ }
+ }
}
- tile_cache.next();
}
+
+ return available;
}
-bool FGTileMgr::scenery_available(double lat, double lon, double range_m)
+// Returns true if tiles around current view position have been loaded
+bool FGTileMgr::isSceneryLoaded()
{
- // sanity check (unfortunately needed!)
- if ( lon <= -180.0 || lon >= 180.0 || lat <= -90.0 || lat >= 90.0 )
- return false;
-
- SGBucket bucket(lon, lat);
- FGTileEntry *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();
-
- 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 ) {
- 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 );
- FGTileEntry *te = tile_cache.get_tile(b);
- if (!te || !te->is_loaded())
- return false;
- }
- }
- }
+ double range_m = 100.0;
+ if (scheduled_visibility < range_m)
+ range_m = scheduled_visibility;
- // Survived all tests.
- return true;
+ return schedule_scenery(SGGeod::fromDeg(longitude, latitude), range_m, 0.0);
}