]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/tilemgr.cxx
model paging patch from Till Busch
[flightgear.git] / src / Scenery / tilemgr.cxx
1 // tilemgr.cxx -- routines to handle dynamic management of scenery tiles
2 //
3 // Written by Curtis Olson, started January 1998.
4 //
5 // Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <algorithm>
29 #include <functional>
30
31 #include <simgear/constants.h>
32 #include <simgear/debug/logstream.hxx>
33 #include <simgear/math/point3d.hxx>
34 #include <simgear/math/polar3d.hxx>
35 #include <simgear/math/sg_geodesy.hxx>
36 #include <simgear/math/vector.hxx>
37 #include <simgear/structure/exception.hxx>
38 #include <simgear/scene/model/modellib.hxx>
39
40 #include <Main/globals.hxx>
41 #include <Main/fg_props.hxx>
42 #include <Main/renderer.hxx>
43 #include <Main/viewer.hxx>
44 #include <Scripting/NasalSys.hxx>
45
46 #include "newcache.hxx"
47 #include "scenery.hxx"
48 #include "SceneryPager.hxx"
49 #include "tilemgr.hxx"
50
51 using std::for_each;
52 using flightgear::SceneryPager;
53 using simgear::SGModelLib;
54
55 #define TEST_LAST_HIT_CACHE
56
57 // Constructor
58 FGTileMgr::FGTileMgr():
59     state( Start ),
60     current_tile( NULL ),
61     vis( 16000 )
62 {
63 }
64
65
66 // Destructor
67 FGTileMgr::~FGTileMgr() {
68 }
69
70
71 // Initialize the Tile Manager subsystem
72 int FGTileMgr::init() {
73     SG_LOG( SG_TERRAIN, SG_INFO, "Initializing Tile Manager subsystem." );
74
75     tile_cache.init();
76
77     state = Inited;
78
79     previous_bucket.make_bad();
80     current_bucket.make_bad();
81
82     longitude = latitude = -1000.0;
83
84     return 1;
85 }
86
87
88 // schedule a tile for loading
89 void FGTileMgr::sched_tile( const SGBucket& b, const bool is_inner_ring ) {
90     // see if tile already exists in the cache
91     FGTileEntry *t = tile_cache.get_tile( b );
92
93     if ( t == NULL ) {
94         // make space in the cache
95         SceneryPager* pager = FGScenery::getPagerSingleton();
96         while ( (int)tile_cache.get_size() > tile_cache.get_max_cache_size() ) {
97             long index = tile_cache.get_oldest_tile();
98             if ( index >= 0 ) {
99                 FGTileEntry *old = tile_cache.get_tile( index );
100                 tile_cache.clear_entry( index );
101                 osg::ref_ptr<osg::Object> subgraph = old->getNode();
102                 old->disconnect_ssg_nodes();
103                 delete old;
104                 // zeros out subgraph ref_ptr, so subgraph is owned by
105                 // the pager and will be deleted in the pager thread.
106                 pager->queueDeleteRequest(subgraph);
107             } else {
108                 // nothing to free ?!? forge ahead
109                 break;
110             }
111         }
112
113         // create a new entry
114         FGTileEntry *e = new FGTileEntry( b );
115
116         // insert the tile into the cache
117         if ( tile_cache.insert_tile( e ) ) {
118             // update_queues will generate load request
119         } else {
120             // insert failed (cache full with no available entries to
121             // delete.)  Try again later
122             delete e;
123         }
124         // Attach to scene graph
125         e->add_ssg_nodes(globals->get_scenery()->get_terrain_branch());
126     } else {
127         t->set_inner_ring( is_inner_ring );
128     }
129 }
130
131
132 // schedule a needed buckets for loading
133 void FGTileMgr::schedule_needed( double vis, const SGBucket& curr_bucket) {
134     // sanity check (unfortunately needed!)
135     if ( longitude < -180.0 || longitude > 180.0 
136          || latitude < -90.0 || latitude > 90.0 )
137     {
138         SG_LOG( SG_TERRAIN, SG_ALERT,
139                 "Attempting to schedule tiles for bogus lon and lat  = ("
140                 << longitude << "," << latitude << ")" );
141         return;         // FIXME
142         SG_LOG( SG_TERRAIN, SG_ALERT,
143                 "This is a FATAL error.  Exiting!" );
144         exit(-1);
145     }
146
147     SG_LOG( SG_TERRAIN, SG_INFO,
148             "scheduling needed tiles for " << longitude << " " << latitude );
149
150     // vis = fgGetDouble("/environment/visibility-m");
151
152     double tile_width = curr_bucket.get_width_m();
153     double tile_height = curr_bucket.get_height_m();
154     // cout << "tile width = " << tile_width << "  tile_height = "
155     //      << tile_height << endl;
156
157     xrange = (int)(vis / tile_width) + 1;
158     yrange = (int)(vis / tile_height) + 1;
159     if ( xrange < 1 ) { xrange = 1; }
160     if ( yrange < 1 ) { yrange = 1; }
161
162     // note * 2 at end doubles cache size (for fdm and viewer)
163     tile_cache.set_max_cache_size( (2*xrange + 2) * (2*yrange + 2) * 2 );
164     // cout << "xrange = " << xrange << "  yrange = " << yrange << endl;
165     // cout << "max cache size = " << tile_cache.get_max_cache_size()
166     //      << " current cache size = " << tile_cache.get_size() << endl;
167
168     // clear the inner ring flags so we can set them below.  This
169     // prevents us from having "true" entries we aren't able to find
170     // to get rid of if we teleport a long ways away from the current
171     // location.
172     tile_cache.clear_inner_ring_flags();
173
174     SGBucket b;
175
176     // schedule center tile first so it can be loaded first
177     b = sgBucketOffset( longitude, latitude, 0, 0 );
178     sched_tile( b, true );
179
180     int x, y;
181
182     // schedule next ring of 8 tiles
183     for ( x = -1; x <= 1; ++x ) {
184         for ( y = -1; y <= 1; ++y ) {
185             if ( x != 0 || y != 0 ) {
186                 b = sgBucketOffset( longitude, latitude, x, y );
187                 sched_tile( b, true );
188             }
189         }
190     }
191
192     // schedule remaining tiles
193     for ( x = -xrange; x <= xrange; ++x ) {
194         for ( y = -yrange; y <= yrange; ++y ) {
195             if ( x < -1 || x > 1 || y < -1 || y > 1 ) {
196                 SGBucket b = sgBucketOffset( longitude, latitude, x, y );
197                 sched_tile( b, false );
198             }
199         }
200     }
201 }
202
203
204 void FGTileMgr::initialize_queue()
205 {
206     // First time through or we have teleported, initialize the
207     // system and load all relavant tiles
208
209     SG_LOG( SG_TERRAIN, SG_INFO, "Initialize_queue(): Updating Tile list for "
210             << current_bucket );
211     // cout << "tile cache size = " << tile_cache.get_size() << endl;
212
213     // wipe/initialize tile cache
214     // tile_cache.init();
215     previous_bucket.make_bad();
216
217     // build the local area list and schedule tiles for loading
218
219     // start with the center tile and work out in concentric
220     // "rings"
221
222     double visibility_meters = fgGetDouble("/environment/visibility-m");
223     schedule_needed(visibility_meters, current_bucket);
224
225     // do we really want to lose this? CLO
226 #if 0
227     // Now force a load of the center tile and inner ring so we
228     // have something to see in our first frame.
229     int i;
230     for ( i = 0; i < 9; ++i ) {
231         if ( load_queue.size() ) {
232             SG_LOG( SG_TERRAIN, SG_DEBUG, 
233                     "Load queue not empty, loading a tile" );
234
235             SGBucket pending = load_queue.front();
236             load_queue.pop_front();
237             load_tile( pending );
238         }
239     }
240 #endif
241 }
242
243 osg::Node*
244 FGTileMgr::loadTileModel(const string& modelPath, bool cacheModel)
245 {
246     osg::Node* result = 0;
247     try {
248         if(cacheModel)
249         {
250             result =
251                 SGModelLib::loadModel(modelPath, globals->get_props(),
252                                       new FGNasalModelData);
253         }
254         else
255         {
256             result=
257                 SGModelLib::loadPagedModel(modelPath, globals->get_props(),
258                                            new FGNasalModelData);
259         }
260     } catch (const sg_io_exception& exc) {
261         string m(exc.getMessage());
262         m += " ";
263         m += exc.getLocation().asString();
264         SG_LOG( SG_ALL, SG_ALERT, m );
265     } catch (const sg_exception& exc) { // XXX may be redundant
266         SG_LOG( SG_ALL, SG_ALERT, exc.getMessage());
267     }
268     return result;
269 }
270
271 // Helper class for STL fun
272 class TileLoad : public std::unary_function<FGNewCache::tile_map::value_type,
273                                             void>
274 {
275 public:
276     TileLoad(SceneryPager *pager, osg::FrameStamp* framestamp,
277              osg::Group* terrainBranch) :
278         _pager(pager), _framestamp(framestamp) {}
279     TileLoad(const TileLoad& rhs) :
280         _pager(rhs._pager), _framestamp(rhs._framestamp) {}
281     void operator()(FGNewCache::tile_map::value_type& tilePair)
282     {
283         FGTileEntry* entry = tilePair.second;
284         if (entry->getNode()->getNumChildren() == 0) {
285             _pager->queueRequest(entry->tileFileName,
286                                  entry->getNode(),
287                                  entry->get_inner_ring() ? 10.0f : 1.0f,
288                                  _framestamp);
289         }
290     }
291 private:
292     SceneryPager* _pager;
293     osg::FrameStamp* _framestamp;
294 };
295
296 /**
297  * Update the various queues maintained by the tilemagr (private
298  * internal function, do not call directly.)
299  */
300 void FGTileMgr::update_queues()
301 {
302     SceneryPager* pager = FGScenery::getPagerSingleton();
303     for_each(tile_cache.begin(), tile_cache.end(),
304              TileLoad(pager,
305                       globals->get_renderer()->getViewer()->getFrameStamp(),
306                       globals->get_scenery()->get_terrain_branch()));
307 }
308
309
310 // given the current lon/lat (in degrees), fill in the array of local
311 // chunks.  If the chunk isn't already in the cache, then read it from
312 // disk.
313 int FGTileMgr::update( double visibility_meters )
314 {
315     SGLocation *location = globals->get_current_view()->getSGLocation();
316     return update( location, visibility_meters );
317 }
318
319
320 int FGTileMgr::update( SGLocation *location, double visibility_meters )
321 {
322     SG_LOG( SG_TERRAIN, SG_DEBUG, "FGTileMgr::update()" );
323
324     longitude = location->getLongitude_deg();
325     latitude = location->getLatitude_deg();
326     // add 1.0m to the max altitude to give a little leeway to the
327     // ground reaction code.
328     altitude_m = location->getAltitudeASL_ft() * SG_FEET_TO_METER + 1.0;
329
330     // if current altitude is apparently not initialized, set max
331     // altitude to something big.
332     if ( altitude_m < -1000 ) {
333         altitude_m = 10000;
334     }
335     // SG_LOG( SG_TERRAIN, SG_DEBUG, "FGTileMgr::update() for "
336     //         << longitude << " " << latatitude );
337
338     current_bucket.set_bucket( longitude, latitude );
339     // SG_LOG( SG_TERRAIN, SG_DEBUG, "Updating tile list for "
340     //         << current_bucket );
341     fgSetInt( "/environment/current-tile-id", current_bucket.gen_index() );
342
343     // do tile load scheduling. 
344     // Note that we need keep track of both viewer buckets and fdm buckets.
345     if ( state == Running ) {
346         SG_LOG( SG_TERRAIN, SG_DEBUG, "State == Running" );
347         if (current_bucket != previous_bucket) {
348             // We've moved to a new bucket, we need to schedule any
349             // needed tiles for loading.
350             SG_LOG( SG_TERRAIN, SG_INFO, "FGTileMgr::update()" );
351             schedule_needed(visibility_meters, current_bucket);
352         }
353     } else if ( state == Start || state == Inited ) {
354         SG_LOG( SG_TERRAIN, SG_INFO, "State == Start || Inited" );
355 //        initialize_queue();
356         state = Running;
357         if (current_bucket != previous_bucket
358             && current_bucket.get_chunk_lon() != -1000) {
359                SG_LOG( SG_TERRAIN, SG_INFO, "FGTileMgr::update()" );
360                schedule_needed(visibility_meters, current_bucket);
361         }
362     }
363
364     update_queues();
365
366     // save bucket...
367     previous_bucket = current_bucket;
368
369     return 1;
370 }
371
372 void FGTileMgr::prep_ssg_nodes(float vis) {
373
374     // traverse the potentially viewable tile list and update range
375     // selector and transform
376
377     FGTileEntry *e;
378     tile_cache.reset_traversal();
379
380     while ( ! tile_cache.at_end() ) {
381         // cout << "processing a tile" << endl;
382         if ( (e = tile_cache.get_current()) ) {
383             e->prep_ssg_node(vis);
384         } else {
385             SG_LOG(SG_INPUT, SG_ALERT, "warning ... empty tile in cache");
386         }
387         tile_cache.next();
388     }
389 }
390
391 bool FGTileMgr::scenery_available(double lat, double lon, double range_m)
392 {
393   // sanity check (unfortunately needed!)
394   if ( lon <= -180.0 || lon >= 180.0 || lat <= -90.0 || lat >= 90.0 )
395     return false;
396   
397   SGBucket bucket(lon, lat);
398   FGTileEntry *te = tile_cache.get_tile(bucket);
399   if (!te || !te->is_loaded())
400     return false;
401
402   // Traverse all tiles required to be there for the given visibility.
403   // This uses exactly the same algorithm like the tile scheduler.
404   double tile_width = bucket.get_width_m();
405   double tile_height = bucket.get_height_m();
406   
407   int xrange = (int)fabs(range_m / tile_width) + 1;
408   int yrange = (int)fabs(range_m / tile_height) + 1;
409   
410   for ( int x = -xrange; x <= xrange; ++x ) {
411     for ( int y = -yrange; y <= yrange; ++y ) {
412       // We have already checked for the center tile.
413       if ( x != 0 || y != 0 ) {
414         SGBucket b = sgBucketOffset( lon, lat, x, y );
415         FGTileEntry *te = tile_cache.get_tile(b);
416         if (!te || !te->is_loaded())
417           return false;
418       }
419     }
420   }
421
422   // Survived all tests.
423   return true;
424 }
425
426 namespace
427 {
428 struct IsTileLoaded :
429         public std::unary_function<FGNewCache::tile_map::value_type, bool>
430 {
431     bool operator()(const FGNewCache::tile_map::value_type& tilePair) const
432     {
433         return tilePair.second->is_loaded();
434     }
435 };
436 }
437
438 bool FGTileMgr::isSceneryLoaded()
439 {
440     return (std::find_if(tile_cache.begin(), tile_cache.end(),
441                          std::not1(IsTileLoaded()))
442             == tile_cache.end());
443 }