]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/tilemgr.cxx
4e5ebaf9469034fb2ae2d1ce723c9d88e40bb022
[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  - curt@infoplane.com
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #ifdef HAVE_WINDOWS_H
29 #  include <windows.h>
30 #endif
31
32 #include <GL/glut.h>
33 #include <simgear/xgl/xgl.h>
34
35 #include <simgear/constants.h>
36 #include <simgear/debug/logstream.hxx>
37 #include <simgear/math/fg_geodesy.hxx>
38 #include <simgear/math/point3d.hxx>
39 #include <simgear/math/polar3d.hxx>
40 #include <simgear/math/vector.hxx>
41
42 #include <Aircraft/aircraft.hxx>
43 #include <Main/options.hxx>
44 #include <Main/views.hxx>
45 #include <Objects/materialmgr.hxx>
46 #include <Objects/obj.hxx>
47
48 #ifndef FG_OLD_WEATHER
49 #  include <WeatherCM/FGLocalWeatherDatabase.h>
50 #else
51 #  include <Weather/weather.hxx>
52 #endif
53
54 #include "scenery.hxx"
55 #include "tilecache.hxx"
56 #include "tilemgr.hxx"
57
58 #define TEST_LAST_HIT_CACHE
59
60 extern ssgRoot *scene;
61 extern ssgBranch *terrain;
62
63 // the tile manager
64 FGTileMgr global_tile_mgr;
65
66
67 // a temporary hack until we get everything rewritten with sgdVec3
68 static inline Point3D operator + (const Point3D& a, const sgdVec3 b)
69 {
70     return Point3D(a.x()+b[0], a.y()+b[1], a.z()+b[2]);
71 }
72
73
74 // Constructor
75 FGTileMgr::FGTileMgr ( void ):
76     state( Start )
77 {
78 }
79
80
81 // Destructor
82 FGTileMgr::~FGTileMgr ( void ) {
83 }
84
85
86 // Initialize the Tile Manager subsystem
87 int FGTileMgr::init( void ) {
88     FG_LOG( FG_TERRAIN, FG_INFO, "Initializing Tile Manager subsystem." );
89
90     // load default material library
91     if ( ! material_mgr.loaded() ) {
92         material_mgr.load_lib();
93     }
94
95     global_tile_cache.init();
96     hit_list.clear();
97
98     state = Inited;
99
100     // last_hit = 0;
101
102     tile_diameter = current_options.get_tile_diameter();
103     FG_LOG( FG_TERRAIN, FG_INFO, "Tile Diameter = " << tile_diameter);
104     
105     previous_bucket.make_bad();
106     current_bucket.make_bad();
107
108     scroll_direction = SCROLL_INIT;
109     tile_index = -9999;
110
111     longitude = latitude = -1000.0;
112     last_longitude = last_latitude = -1000.0;
113         
114     return 1;
115 }
116
117 #if 0
118 // schedule a tile for loading
119 static void disable_tile( int cache_index ) {
120     // see if tile already exists in the cache
121     // cout << "DISABLING CACHE ENTRY = " << cache_index << endl;
122     FGTileEntry *t = global_tile_cache.get_tile( cache_index );
123     t->ssg_disable();
124 }
125 #endif
126
127 // schedule a tile for loading
128 int FGTileMgr::sched_tile( const FGBucket& b ) {
129     // see if tile already exists in the cache
130     int cache_index = global_tile_cache.exists( b );
131
132     if ( cache_index >= 0 ) {
133         // tile exists in cache, reenable it.
134         // cout << "REENABLING DISABLED TILE" << endl;
135         FGTileEntry *t = global_tile_cache.get_tile( cache_index );
136         t->select_ptr->select( 1 );
137         t->mark_loaded();
138     } else {
139         // find the next available cache entry and mark it as
140         // scheduled
141         cache_index = global_tile_cache.next_avail();
142         FGTileEntry *t = global_tile_cache.get_tile( cache_index );
143         t->mark_scheduled_for_use();
144
145         // register a load request
146         FGLoadRec request;
147         request.b = b;
148         request.cache_index = cache_index;
149         load_queue.push_back( request );
150     }
151
152     return cache_index;
153 }
154
155
156 // load a tile
157 void FGTileMgr::load_tile( const FGBucket& b, int cache_index) {
158
159     FG_LOG( FG_TERRAIN, FG_DEBUG, "Loading tile " << b );
160
161     global_tile_cache.fill_in(cache_index, b);
162
163     FG_LOG( FG_TERRAIN, FG_DEBUG, "Loaded for cache index: " << cache_index );
164 }
165
166
167 // Determine scenery altitude via ssg.  Normally this just happens
168 // when we render the scene, but we'd also like to be able to do this
169 // explicitely.  lat & lon are in radians.  view_pos in current world
170 // coordinate translated near (0,0,0) (in meters.)  Returns result in
171 // meters.
172
173 bool
174 FGTileMgr::current_elev_ssg( const Point3D& abs_view_pos, 
175                              const Point3D& view_pos )
176 {
177     sgdVec3 orig, dir;
178
179     sgdSetVec3(orig, view_pos.x(), view_pos.y(), view_pos.z() );
180     sgdSetVec3(dir, abs_view_pos.x(), abs_view_pos.y(), abs_view_pos.z() );
181
182     hit_list.Intersect( terrain, orig, dir );
183
184     int this_hit=0;
185     Point3D geoc;
186     double result = -9999;
187
188     int hitcount = hit_list.num_hits();
189     for ( int i = 0; i < hitcount; ++i ) {
190         geoc = fgCartToPolar3d( scenery.center + hit_list.get_point(i) );      
191         double lat_geod, alt, sea_level_r;
192         fgGeocToGeod(geoc.lat(), geoc.radius(), &lat_geod, 
193                      &alt, &sea_level_r);
194         if ( alt > result && alt < 10000 ) {
195             result = alt;
196             this_hit = i;
197         }
198     }
199
200     if ( result > -9000 ) {
201         scenery.cur_elev = result;
202         scenery.cur_radius = geoc.radius();
203         sgdCopyVec3(scenery.cur_normal, hit_list.get_normal(this_hit));
204         return true;
205     } else {
206         FG_LOG( FG_TERRAIN, FG_INFO, "no terrain intersection" );
207         scenery.cur_elev = 0.0;
208         float *up = current_view.local_up;
209         sgdSetVec3(scenery.cur_normal, up[0], up[1], up[2]);
210         return false;
211     }
212 }
213
214
215 FGBucket FGTileMgr::BucketOffset( int dx, int dy )
216 {
217     double clat, clon, span;
218     if( scroll_direction == SCROLL_INIT ) {
219         pending.set_bucket( longitude, latitude );
220         clat = pending.get_center_lat() + dy * FG_BUCKET_SPAN;
221
222         // walk dy units in the lat direction
223         pending.set_bucket( longitude, clat );
224
225         // find the lon span for the new latitude
226         span = bucket_span( clat );
227         
228         // walk dx units in the lon direction
229         clon = longitude + dx * span;
230     } else      {
231         pending.set_bucket( last_longitude, last_latitude );
232         clat = pending.get_center_lat() + dy * FG_BUCKET_SPAN;
233
234         // walk dy units in the lat direction
235         pending.set_bucket( last_longitude, clat );
236
237         // find the lon span for the new latitude
238         span = bucket_span( clat );
239
240         // walk dx units in the lon direction
241         clon = last_longitude + dx * span;
242     }    
243         
244     while ( clon < -180.0 ) clon += 360.0;
245     while ( clon >= 180.0 ) clon -= 360.0;
246     pending.set_bucket( clon, clat );
247
248     FG_LOG( FG_TERRAIN, FG_DEBUG, "    fgBucketOffset " << pending );
249     return pending;
250 }
251
252
253 // schedule a tile row(column) for loading
254 void FGTileMgr::scroll( void )
255 {
256     FG_LOG( FG_TERRAIN, FG_DEBUG, "schedule_row: Scrolling" );
257
258     int i, dw, dh;
259         
260     switch( scroll_direction ) {
261     case SCROLL_NORTH:
262         FG_LOG( FG_TERRAIN, FG_DEBUG, 
263                 "  (North) Loading " << tile_diameter << " tiles" );
264         dw = tile_diameter / 2;
265         dh = dw + 1;
266         for ( i = 0; i < tile_diameter; i++ ) {
267             sched_tile( BucketOffset( i - dw, dh) );
268         }
269         break;
270     case SCROLL_EAST:
271         FG_LOG( FG_TERRAIN, FG_DEBUG, 
272                 "  (East) Loading " << tile_diameter << " tiles" );
273         dh = tile_diameter / 2;
274         dw = dh + 1;
275         for ( i = 0; i < tile_diameter; i++ ) {
276             sched_tile( BucketOffset( dw, i - dh ) );
277         }
278         break;
279     case SCROLL_SOUTH:
280         dw = tile_diameter / 2;
281         dh = -dw - 1;
282         FG_LOG( FG_TERRAIN, FG_DEBUG, 
283                 "  (South) Loading " << tile_diameter << " tiles" );
284         for ( i = 0; i < tile_diameter; i++ ) {
285             sched_tile( BucketOffset( i - dw, dh) );
286         }
287         break;
288     case SCROLL_WEST:
289         dh = tile_diameter / 2;
290         dw = -dh - 1;
291         FG_LOG( FG_TERRAIN, FG_DEBUG, 
292                 "  (West) Loading " << tile_diameter << " tiles" );
293         for ( i = 0; i < tile_diameter; i++ ) {
294             sched_tile( BucketOffset( dw, i - dh ) );
295         }
296         break;
297     default:
298         FG_LOG( FG_TERRAIN, FG_WARN, "UNKNOWN SCROLL DIRECTION in schedule_row" );
299         ;
300     }
301     FG_LOG( FG_TERRAIN, FG_DEBUG, "\tschedule_row returns" );
302 }
303
304
305 void FGTileMgr::initialize_queue( void )
306 {
307     // First time through or we have teleported, initialize the
308     // system and load all relavant tiles
309
310     FG_LOG( FG_TERRAIN, FG_INFO, "Updating Tile list for " << current_bucket );
311     FG_LOG( FG_TERRAIN, FG_INFO, "  First time through ... " );
312     FG_LOG( FG_TERRAIN, FG_INFO, "  Updating Tile list for " << current_bucket );
313     FG_LOG( FG_TERRAIN, FG_INFO, "  Loading " 
314             << tile_diameter * tile_diameter << " tiles" );
315
316     int i;
317     scroll_direction = SCROLL_INIT;
318
319     // wipe/initialize tile cache
320     global_tile_cache.init();
321     previous_bucket.make_bad();
322
323     // build the local area list and schedule tiles for loading
324
325     // start with the center tile and work out in concentric
326     // "rings"
327
328     sched_tile( current_bucket );
329     Point3D geod_view_center( current_bucket.get_center_lon(), 
330                               current_bucket.get_center_lat(), 
331                               cur_fdm_state->get_Altitude()*FEET_TO_METER + 3 );
332
333     current_view.abs_view_pos = fgGeodToCart( geod_view_center );
334     current_view.view_pos = current_view.abs_view_pos - scenery.next_center;
335
336     for ( i = 3; i <= tile_diameter; i = i + 2 ) {
337         int j;
338         int span = i / 2;
339
340         // bottom row
341         for ( j = -span; j <= span; ++j ) {
342             sched_tile( BucketOffset( j, -span ) );
343         }
344
345         // top row
346         for ( j = -span; j <= span; ++j ) {
347             sched_tile( BucketOffset( j, span ) );
348         }
349
350         // middle rows
351         for ( j = -span + 1; j <= span - 1; ++j ) {
352             sched_tile( BucketOffset( -span, j ) );
353             sched_tile( BucketOffset( span, j ) );
354         }
355
356     }
357
358     // Now force a load of the center tile and inner ring so we
359     // have something to see in our first frame.
360     for ( i = 0; i < 9; ++i ) {
361         if ( load_queue.size() ) {
362             FG_LOG( FG_TERRAIN, FG_DEBUG, 
363                     "Load queue not empty, loading a tile" );
364
365             FGLoadRec pending = load_queue.front();
366             load_queue.pop_front();
367             load_tile( pending.b, pending.cache_index );
368         }
369     }
370 }
371
372
373 // given the current lon/lat, fill in the array of local chunks.  If
374 // the chunk isn't already in the cache, then read it from disk.
375 int FGTileMgr::update( void ) {
376     // FG_LOG( FG_TERRAIN, FG_DEBUG, "FGTileMgr::update()" );
377
378     FGInterface *f = current_aircraft.fdm_state;
379
380     // lonlat for this update 
381     longitude = f->get_Longitude() * RAD_TO_DEG;
382     latitude = f->get_Latitude() * RAD_TO_DEG;
383     // FG_LOG( FG_TERRAIN, FG_DEBUG, "lon "<< lonlat[LON] <<
384     //      " lat " << lonlat[LAT] );
385
386     current_bucket.set_bucket( longitude, latitude );
387     // FG_LOG( FG_TERRAIN, FG_DEBUG, "Updating Tile list for " << current_bucket );
388
389     tile_index = global_tile_cache.exists(current_bucket);
390     // FG_LOG( FG_TERRAIN, FG_DEBUG, "tile index " << tile_index );
391
392     if ( tile_index >= 0 ) {
393         current_tile = global_tile_cache.get_tile(tile_index);
394         scenery.next_center = current_tile->center;
395     } else {
396         FG_LOG( FG_TERRAIN, FG_WARN, "Tile not found" );
397     }
398
399     if ( state == Running ) {
400         if( current_bucket == previous_bucket) {
401             FG_LOG( FG_TERRAIN, FG_DEBUG, "Same bucket as last time" );
402             scroll_direction = SCROLL_NONE;
403         } else {
404             // We've moved to a new bucket, we need to scroll our
405             // structures, and load in the new tiles
406             // CURRENTLY THIS ASSUMES WE CAN ONLY MOVE TO ADJACENT TILES.
407             // AT ULTRA HIGH SPEEDS THIS ASSUMPTION MAY NOT BE VALID IF
408             // THE AIRCRAFT CAN SKIP A TILE IN A SINGLE ITERATION.
409
410             if ( (current_bucket.get_lon() > previous_bucket.get_lon()) ||
411                  ( (current_bucket.get_lon() == previous_bucket.get_lon()) && 
412                    (current_bucket.get_x() > previous_bucket.get_x()) ) )
413                 {
414                     scroll_direction = SCROLL_EAST;
415                 }
416             else if ( (current_bucket.get_lon() < previous_bucket.get_lon()) ||
417                       ( (current_bucket.get_lon() == previous_bucket.get_lon()) && 
418                         (current_bucket.get_x() < previous_bucket.get_x()) ) )
419                 {   
420                     scroll_direction = SCROLL_WEST;
421                 }   
422
423             if ( (current_bucket.get_lat() > previous_bucket.get_lat()) ||
424                  ( (current_bucket.get_lat() == previous_bucket.get_lat()) && 
425                    (current_bucket.get_y() > previous_bucket.get_y()) ) )
426                 {   
427                     scroll_direction = SCROLL_NORTH;
428                 }
429             else if ( (current_bucket.get_lat() < previous_bucket.get_lat()) ||
430                       ( (current_bucket.get_lat() == previous_bucket.get_lat()) && 
431                         (current_bucket.get_y() < previous_bucket.get_y()) ) )
432                 {
433                     scroll_direction = SCROLL_SOUTH;
434                 }
435
436             scroll();
437         }
438
439     } else if ( (state == Start) || (state == Inited) ) {
440         initialize_queue();
441         state = Running;
442     }
443
444     if ( load_queue.size() ) {
445         FG_LOG( FG_TERRAIN, FG_DEBUG, "Load queue not empty, loading a tile" );
446
447         FGLoadRec pending = load_queue.front();
448         load_queue.pop_front();
449         load_tile( pending.b, pending.cache_index );
450     }
451
452     // find our current elevation (feed in the current bucket to save work)
453     // Point3D geod_pos = Point3D( f->get_Longitude(), f->get_Latitude(), 0.0);
454     // Point3D tmp_abs_view_pos = fgGeodToCart(geod_pos);
455
456     // cout << "current elevation (old) == " 
457     //      << current_elev( f->get_Longitude(), f->get_Latitude(), 
458     //                       tmp_abs_view_pos ) 
459     //      << endl;
460
461     // set scenery.cur_elev and scenery.cur_radius
462
463     current_elev_ssg( current_view.abs_view_pos,
464                       current_view.view_pos );
465     // cout << "current elevation (ssg) == " << scenery.cur_elev << endl;
466
467     previous_bucket = current_bucket;
468     last_longitude = longitude;
469     last_latitude  = latitude;
470
471     return 1;
472 }
473
474 // Prepare the ssg nodes ... for each tile, set it's proper
475 // transform and update it's range selector based on current
476 // visibilty
477 void FGTileMgr::prep_ssg_node( int idx ) {
478 }
479
480 void FGTileMgr::prep_ssg_nodes( void ) {
481     FGTileEntry *t;
482     float ranges[2];
483     ranges[0] = 0.0f;
484
485     // traverse the potentially viewable tile list and update range
486     // selector and transform
487     for ( int i = 0; i < (int)global_tile_cache.get_size(); i++ ) {
488         t = global_tile_cache.get_tile( i );
489
490         if ( t->is_loaded() ) {
491             // set range selector (LOD trick) to be distance to center
492             // of tile + bounding radius
493
494 #ifndef FG_OLD_WEATHER
495             ranges[1] = WeatherDatabase->getWeatherVisibility()
496                 + t->bounding_radius;
497 #else
498             ranges[1] = current_weather.get_visibility()+t->bounding_radius;
499 #endif
500             t->range_ptr->setRanges( ranges, 2 );
501
502             // calculate tile offset
503             t->SetOffset( scenery.center );
504
505             // calculate ssg transform
506             sgCoord sgcoord;
507             sgSetCoord( &sgcoord,
508                         t->offset.x(), t->offset.y(), t->offset.z(),
509                         0.0, 0.0, 0.0 );
510             t->transform_ptr->setTransform( &sgcoord );
511         }
512     }
513 }