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