]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/tilemgr.cxx
0266ca5b65508c10d5ebb9f0f55774a0f276d150
[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 static void CurrentNormalInLocalPlane(sgVec3 dst, sgVec3 src) {
157     sgVec3 tmp;
158     sgSetVec3(tmp, src[0], src[1], src[2] );
159     sgMat4 TMP;
160     sgTransposeNegateMat4 ( TMP, globals->get_current_view()->get_UP() ) ;
161     sgXformVec3(tmp, tmp, TMP);
162     sgSetVec3(dst, tmp[2], tmp[1], tmp[0] );
163 }
164
165
166 // Determine scenery altitude via ssg.  Normally this just happens
167 // when we render the scene, but we'd also like to be able to do this
168 // explicitely.  lat & lon are in radians.  view_pos in current world
169 // coordinate translated near (0,0,0) (in meters.)  Returns result in
170 // meters.
171 bool FGTileMgr::current_elev_ssg( sgdVec3 abs_view_pos, sgVec3 view_pos,
172                                   double *terrain_elev ) {
173     sgdVec3 orig, dir;
174
175     sgdSetVec3(orig, view_pos );
176     sgdCopyVec3(dir, abs_view_pos );
177
178     hit_list.Intersect( terrain, orig, dir );
179
180     int this_hit=0;
181     Point3D geoc;
182     double result = -9999;
183
184     int hitcount = hit_list.num_hits();
185     for ( int i = 0; i < hitcount; ++i ) {
186         geoc = sgCartToPolar3d( scenery.center + hit_list.get_point(i) );      
187         double lat_geod, alt, sea_level_r;
188         sgGeocToGeod(geoc.lat(), geoc.radius(), &lat_geod, 
189                      &alt, &sea_level_r);
190         if ( alt > result && alt < 10000 ) {
191             result = alt;
192             this_hit = i;
193         }
194     }
195
196     if ( result > -9000 ) {
197         *terrain_elev = result;
198         scenery.cur_radius = geoc.radius();
199         sgVec3 tmp;
200         sgSetVec3(tmp, hit_list.get_normal(this_hit));
201         ssgState *IntersectedLeafState =
202             ((ssgLeaf*)hit_list.get_entity(this_hit))->getState();
203         CurrentNormalInLocalPlane(tmp, tmp);
204         sgdSetVec3( scenery.cur_normal, tmp );
205         // cout << "NED: " << tmp[0] << " " << tmp[1] << " " << tmp[2] << endl;
206         return true;
207     } else {
208         FG_LOG( FG_TERRAIN, FG_INFO, "no terrain intersection" );
209         *terrain_elev = 0.0;
210         float *up = globals->get_current_view()->get_world_up();
211         sgdSetVec3(scenery.cur_normal, up[0], up[1], up[2]);
212         return false;
213     }
214 }
215
216
217 FGBucket FGTileMgr::BucketOffset( int dx, int dy )
218 {
219     double clat, clon, span;
220     if( scroll_direction == SCROLL_INIT ) {
221         // use current latitude and longitude
222         // walk dy units in the lat direction
223         clat = current_bucket.get_center_lat() + dy * FG_BUCKET_SPAN;
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         // use previous latitude and longitude
232         // walk dy units in the lat direction
233         clat = previous_bucket.get_center_lat() + dy * FG_BUCKET_SPAN;
234
235         // find the lon span for the new latitude
236         span = bucket_span( clat );
237
238         // walk dx units in the lon direction
239         clon = last_longitude + dx * span;
240     }    
241         
242     while ( clon < -180.0 ) clon += 360.0;
243     while ( clon >= 180.0 ) clon -= 360.0;
244     pending.set_bucket( clon, clat );
245
246     FG_LOG( FG_TERRAIN, FG_DEBUG, "    fgBucketOffset " << pending );
247     return pending;
248 }
249
250
251 // schedule a tile row(column) for loading
252 void FGTileMgr::scroll( void )
253 {
254     FG_LOG( FG_TERRAIN, FG_DEBUG, "schedule_row: Scrolling" );
255
256     int i, dw, dh;
257         
258     switch( scroll_direction ) {
259     case SCROLL_NORTH:
260         FG_LOG( FG_TERRAIN, FG_DEBUG, 
261                 "  (North) Loading " << tile_diameter << " tiles" );
262         dw = tile_diameter / 2;
263         dh = dw + 1;
264         for ( i = 0; i < tile_diameter; i++ ) {
265             sched_tile( BucketOffset( i - dw, dh ) );
266         }
267         break;
268     case SCROLL_EAST:
269         FG_LOG( FG_TERRAIN, FG_DEBUG, 
270                 "  (East) Loading " << tile_diameter << " tiles" );
271         dh = tile_diameter / 2;
272         dw = dh + 1;
273         for ( i = 0; i < tile_diameter; i++ ) {
274             sched_tile( BucketOffset( dw, i - dh ) );
275         }
276         break;
277     case SCROLL_SOUTH:
278         FG_LOG( FG_TERRAIN, FG_DEBUG, 
279                 "  (South) Loading " << tile_diameter << " tiles" );
280         dw = tile_diameter / 2;
281         dh = -dw - 1;
282         for ( i = 0; i < tile_diameter; i++ ) {
283             sched_tile( BucketOffset( i - dw, dh ) );
284         }
285         break;
286     case SCROLL_WEST:
287         FG_LOG( FG_TERRAIN, FG_DEBUG, 
288                 "  (West) Loading " << tile_diameter << " tiles" );
289         dh = tile_diameter / 2;
290         dw = -dh - 1;
291         for ( i = 0; i < tile_diameter; i++ ) {
292             sched_tile( BucketOffset( dw, i - dh ) );
293         }
294         break;
295     default:
296         FG_LOG( FG_TERRAIN, FG_WARN, "UNKNOWN SCROLL DIRECTION in schedule_row" );
297         ;
298     }
299     FG_LOG( FG_TERRAIN, FG_DEBUG, "\tschedule_row returns" );
300 }
301
302
303 void FGTileMgr::initialize_queue()
304 {
305     // First time through or we have teleported, initialize the
306     // system and load all relavant tiles
307
308     FG_LOG( FG_TERRAIN, FG_INFO, "Updating Tile list for " << current_bucket );
309     FG_LOG( FG_TERRAIN, FG_INFO, "  Updating Tile list for " << current_bucket );
310     FG_LOG( FG_TERRAIN, FG_INFO, "  Loading " 
311             << tile_diameter * tile_diameter << " tiles" );
312
313     int i;
314     scroll_direction = SCROLL_INIT;
315
316     // wipe/initialize tile cache
317     global_tile_cache.init();
318     previous_bucket.make_bad();
319
320     // build the local area list and schedule tiles for loading
321
322     // start with the center tile and work out in concentric
323     // "rings"
324
325     sched_tile( current_bucket );
326
327     for ( i = 3; i <= tile_diameter; i = i + 2 ) {
328         int j;
329         int span = i / 2;
330
331         // bottom row
332         for ( j = -span; j <= span; ++j ) {
333             sched_tile( BucketOffset( j, -span ) );
334         }
335
336         // top row
337         for ( j = -span; j <= span; ++j ) {
338             sched_tile( BucketOffset( j, span ) );
339         }
340
341         // middle rows
342         for ( j = -span + 1; j <= span - 1; ++j ) {
343             sched_tile( BucketOffset( -span, j ) );
344             sched_tile( BucketOffset( span, j ) );
345         }
346
347     }
348
349     // Now force a load of the center tile and inner ring so we
350     // have something to see in our first frame.
351     for ( i = 0; i < 9; ++i ) {
352         if ( load_queue.size() ) {
353             FG_LOG( FG_TERRAIN, FG_DEBUG, 
354                     "Load queue not empty, loading a tile" );
355
356             FGLoadRec pending = load_queue.front();
357             load_queue.pop_front();
358             load_tile( pending.b, pending.cache_index );
359         }
360     }
361 }
362
363
364 // forced emptying of the queue
365 // This is necessay to keep bookeeping straight for the
366 // tile_cache   -- which actually handles all the
367 // (de)allocations  
368 void FGTileMgr::destroy_queue() {
369     while( load_queue.size() ) {
370         FG_LOG( FG_TERRAIN, FG_INFO, 
371                 "Load queue not empty, popping a tile" );
372         FGLoadRec pending = load_queue.front();
373         load_queue.pop_front();
374         FGTileEntry *t = global_tile_cache.get_tile( pending.cache_index );
375         // just t->mark_unused() should be enough
376         // but a little paranoia doesn't hurt us here
377         if(t->is_scheduled_for_use())
378             t->mark_unused();
379         else
380             load_tile( pending.b, pending.cache_index );
381     }
382 }
383
384
385 // given the current lon/lat (in degrees), fill in the array of local
386 // chunks.  If the chunk isn't already in the cache, then read it from
387 // disk.
388 int FGTileMgr::update( double lon, double lat ) {
389     // FG_LOG( FG_TERRAIN, FG_DEBUG, "FGTileMgr::update()" );
390
391     // FGInterface *f = current_aircraft.fdm_state;
392
393     // lonlat for this update 
394     // longitude = f->get_Longitude() * RAD_TO_DEG;
395     // latitude = f->get_Latitude() * RAD_TO_DEG;
396     longitude = lon;
397     latitude = lat;
398     // FG_LOG( FG_TERRAIN, FG_DEBUG, "lon "<< lonlat[LON] <<
399     //      " lat " << lonlat[LAT] );
400
401     current_bucket.set_bucket( longitude, latitude );
402     // FG_LOG( FG_TERRAIN, FG_DEBUG, "Updating Tile list for " << current_bucket );
403
404     tile_index = global_tile_cache.exists(current_bucket);
405     // FG_LOG( FG_TERRAIN, FG_DEBUG, "tile index " << tile_index );
406
407     if ( tile_index >= 0 ) {
408         current_tile = global_tile_cache.get_tile(tile_index);
409         scenery.next_center = current_tile->center;
410     } else {
411         FG_LOG( FG_TERRAIN, FG_WARN, "Tile not found (Ok if initializing)" );
412     }
413
414     if ( state == Running ) {
415         if( current_bucket == previous_bucket) {
416             FG_LOG( FG_TERRAIN, FG_DEBUG, "Same bucket as last time" );
417             scroll_direction = SCROLL_NONE;
418         } else {
419             // We've moved to a new bucket, we need to scroll our
420             // structures, and load in the new tiles
421             // CURRENTLY THIS ASSUMES WE CAN ONLY MOVE TO ADJACENT TILES.
422             // AT ULTRA HIGH SPEEDS THIS ASSUMPTION MAY NOT BE VALID IF
423             // THE AIRCRAFT CAN SKIP A TILE IN A SINGLE ITERATION.
424
425             if ( (current_bucket.get_lon() > previous_bucket.get_lon()) ||
426                  ( (current_bucket.get_lon() == previous_bucket.get_lon()) && 
427                    (current_bucket.get_x() > previous_bucket.get_x()) ) )
428                 {
429                     scroll_direction = SCROLL_EAST;
430                 }
431             else if ( (current_bucket.get_lon() < previous_bucket.get_lon()) ||
432                       ( (current_bucket.get_lon() == previous_bucket.get_lon()) && 
433                         (current_bucket.get_x() < previous_bucket.get_x()) ) )
434                 {   
435                     scroll_direction = SCROLL_WEST;
436                 }   
437
438             if ( (current_bucket.get_lat() > previous_bucket.get_lat()) ||
439                  ( (current_bucket.get_lat() == previous_bucket.get_lat()) && 
440                    (current_bucket.get_y() > previous_bucket.get_y()) ) )
441                 {   
442                     scroll_direction = SCROLL_NORTH;
443                 }
444             else if ( (current_bucket.get_lat() < previous_bucket.get_lat()) ||
445                       ( (current_bucket.get_lat() == previous_bucket.get_lat()) && 
446                         (current_bucket.get_y() < previous_bucket.get_y()) ) )
447                 {
448                     scroll_direction = SCROLL_SOUTH;
449                 }
450
451             scroll();
452         }
453
454     } else if ( state == Start || state == Inited ) {
455         initialize_queue();
456         state = Running;
457     }
458
459     if ( load_queue.size() ) {
460         FG_LOG( FG_TERRAIN, FG_DEBUG, "Load queue not empty, loading a tile" );
461
462         FGLoadRec pending = load_queue.front();
463         load_queue.pop_front();
464         load_tile( pending.b, pending.cache_index );
465     }
466
467     if ( scenery.center == Point3D(0.0) ) {
468         // initializing
469         // cout << "initializing ... " << endl;
470         sgdVec3 tmp_abs_view_pos;
471         sgVec3 tmp_view_pos;
472
473         Point3D geod_pos = Point3D( longitude * DEG_TO_RAD,
474                                     latitude * DEG_TO_RAD,
475                                     0.0);
476         Point3D tmp = sgGeodToCart( geod_pos );
477         scenery.center = tmp;
478         sgdSetVec3( tmp_abs_view_pos, tmp.x(), tmp.y(), tmp.z() );
479
480         // cout << "abs_view_pos = " << tmp_abs_view_pos << endl;
481         prep_ssg_nodes();
482         sgSetVec3( tmp_view_pos, 0.0, 0.0, 0.0 );
483         double tmp_elev;
484         if ( current_elev_ssg(tmp_abs_view_pos, tmp_view_pos, &tmp_elev) ) {
485             scenery.cur_elev = tmp_elev;
486         } else {
487             scenery.cur_elev = 0.0;
488         }
489     } else {
490         // cout << "abs view pos = " << current_view.abs_view_pos
491         //      << " view pos = " << current_view.view_pos << endl;
492         double tmp_elev;
493         if ( current_elev_ssg(globals->get_current_view()->get_abs_view_pos(),
494                               globals->get_current_view()->get_view_pos(),
495                               &tmp_elev) )
496         {
497             scenery.cur_elev = tmp_elev;
498         } else {
499             scenery.cur_elev = 0.0;
500         }  
501     }
502
503     // cout << "current elevation (ssg) == " << scenery.cur_elev << endl;
504
505     previous_bucket = current_bucket;
506     last_longitude = longitude;
507     last_latitude  = latitude;
508
509     return 1;
510 }
511
512 // Prepare the ssg nodes ... for each tile, set it's proper
513 // transform and update it's range selector based on current
514 // visibilty
515 void FGTileMgr::prep_ssg_node( int idx ) {
516 }
517
518 void FGTileMgr::prep_ssg_nodes( void ) {
519     FGTileEntry *t;
520     float ranges[2];
521     ranges[0] = 0.0f;
522     double vis = 0.0;
523
524 #ifndef FG_OLD_WEATHER
525     if ( WeatherDatabase != NULL ) {
526         vis = WeatherDatabase->getWeatherVisibility();
527     } else {
528         vis = 16000;
529     }
530 #else
531     vis = current_weather.get_visibility();
532 #endif
533     // cout << "visibility = " << vis << endl;
534
535     // traverse the potentially viewable tile list and update range
536     // selector and transform
537     for ( int i = 0; i < (int)global_tile_cache.get_size(); i++ ) {
538         t = global_tile_cache.get_tile( i );
539
540         if ( t->is_loaded() ) {
541             // set range selector (LOD trick) to be distance to center
542             // of tile + bounding radius
543
544             ranges[1] = vis + t->bounding_radius;
545             t->range_ptr->setRanges( ranges, 2 );
546
547             // calculate tile offset
548             t->SetOffset( scenery.center );
549
550             // calculate ssg transform
551             sgCoord sgcoord;
552             sgSetCoord( &sgcoord,
553                         t->offset.x(), t->offset.y(), t->offset.z(),
554                         0.0, 0.0, 0.0 );
555             t->transform_ptr->setTransform( &sgcoord );
556         }
557     }
558 }