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