1 // tilemgr.cxx -- routines to handle dynamic management of scenery tiles
3 // Written by Curtis Olson, started January 1998.
5 // Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com
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.
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.
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.
33 #include <simgear/xgl/xgl.h>
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>
42 // #include <Aircraft/aircraft.hxx>
43 #include <Main/options.hxx>
44 #include <Main/views.hxx>
45 #include <Objects/obj.hxx>
47 #ifndef FG_OLD_WEATHER
48 # include <WeatherCM/FGLocalWeatherDatabase.h>
50 # include <Weather/weather.hxx>
53 #include "scenery.hxx"
54 #include "tilecache.hxx"
55 #include "tilemgr.hxx"
57 #define TEST_LAST_HIT_CACHE
59 extern ssgRoot *scene;
60 extern ssgBranch *terrain;
63 FGTileMgr global_tile_mgr;
66 // a temporary hack until we get everything rewritten with sgdVec3
67 static inline Point3D operator + (const Point3D& a, const sgdVec3 b)
69 return Point3D(a.x()+b[0], a.y()+b[1], a.z()+b[2]);
74 FGTileMgr::FGTileMgr ( void ):
81 FGTileMgr::~FGTileMgr ( void ) {
85 // Initialize the Tile Manager subsystem
86 int FGTileMgr::init( void ) {
87 FG_LOG( FG_TERRAIN, FG_INFO, "Initializing Tile Manager subsystem." );
89 if ( state != Start ) {
90 FG_LOG( FG_TERRAIN, FG_INFO,
91 "... Reinitializing." );
94 FG_LOG( FG_TERRAIN, FG_INFO,
95 "... First time through." );
98 global_tile_cache.init();
103 tile_diameter = current_options.get_tile_diameter();
104 FG_LOG( FG_TERRAIN, FG_INFO, "Tile Diameter = " << tile_diameter);
106 previous_bucket.make_bad();
107 current_bucket.make_bad();
109 scroll_direction = SCROLL_INIT;
112 longitude = latitude = -1000.0;
113 last_longitude = last_latitude = -1000.0;
119 // schedule a tile for loading
120 int FGTileMgr::sched_tile( const FGBucket& b ) {
121 // see if tile already exists in the cache
122 int cache_index = global_tile_cache.exists( b );
124 if ( cache_index >= 0 ) {
125 // tile exists in cache, reenable it.
126 // cout << "REENABLING DISABLED TILE" << endl;
127 FGTileEntry *t = global_tile_cache.get_tile( cache_index );
128 t->select_ptr->select( 1 );
131 // find the next available cache entry and mark it as
133 cache_index = global_tile_cache.next_avail();
134 FGTileEntry *t = global_tile_cache.get_tile( cache_index );
135 t->mark_scheduled_for_use();
137 // register a load request
140 request.cache_index = cache_index;
141 load_queue.push_back( request );
149 void FGTileMgr::load_tile( const FGBucket& b, int cache_index) {
151 FG_LOG( FG_TERRAIN, FG_DEBUG, "Loading tile " << b );
152 global_tile_cache.fill_in(cache_index, b);
153 FG_LOG( FG_TERRAIN, FG_DEBUG, "Loaded for cache index: " << cache_index );
157 // Determine scenery altitude via ssg. Normally this just happens
158 // when we render the scene, but we'd also like to be able to do this
159 // explicitely. lat & lon are in radians. view_pos in current world
160 // coordinate translated near (0,0,0) (in meters.) Returns result in
164 FGTileMgr::current_elev_ssg( const Point3D& abs_view_pos,
165 const Point3D& view_pos )
169 sgdSetVec3(orig, view_pos.x(), view_pos.y(), view_pos.z() );
170 sgdSetVec3(dir, abs_view_pos.x(), abs_view_pos.y(), abs_view_pos.z() );
172 hit_list.Intersect( terrain, orig, dir );
176 double result = -9999;
178 int hitcount = hit_list.num_hits();
179 for ( int i = 0; i < hitcount; ++i ) {
180 geoc = fgCartToPolar3d( scenery.center + hit_list.get_point(i) );
181 double lat_geod, alt, sea_level_r;
182 fgGeocToGeod(geoc.lat(), geoc.radius(), &lat_geod,
184 if ( alt > result && alt < 10000 ) {
190 if ( result > -9000 ) {
191 scenery.cur_elev = result;
192 scenery.cur_radius = geoc.radius();
194 sgSetVec3(tmp, hit_list.get_normal(this_hit));
195 current_view.CurrentNormalInLocalPlane(tmp, tmp);
196 sgdSetVec3( scenery.cur_normal, tmp );
197 // cout << "NED: " << tmp[0] << " " << tmp[1] << " " << tmp[2] << endl;
200 FG_LOG( FG_TERRAIN, FG_INFO, "no terrain intersection" );
201 scenery.cur_elev = 0.0;
202 float *up = current_view.local_up;
203 sgdSetVec3(scenery.cur_normal, up[0], up[1], up[2]);
209 FGBucket FGTileMgr::BucketOffset( int dx, int dy )
211 double clat, clon, span;
212 if( scroll_direction == SCROLL_INIT ) {
213 // use current latitude and longitude
214 // walk dy units in the lat direction
215 clat = current_bucket.get_center_lat() + dy * FG_BUCKET_SPAN;
217 // find the lon span for the new latitude
218 span = bucket_span( clat );
220 // walk dx units in the lon direction
221 clon = longitude + dx * span;
223 // use previous latitude and longitude
224 // walk dy units in the lat direction
225 clat = previous_bucket.get_center_lat() + dy * FG_BUCKET_SPAN;
227 // find the lon span for the new latitude
228 span = bucket_span( clat );
230 // walk dx units in the lon direction
231 clon = last_longitude + dx * span;
234 while ( clon < -180.0 ) clon += 360.0;
235 while ( clon >= 180.0 ) clon -= 360.0;
236 pending.set_bucket( clon, clat );
238 FG_LOG( FG_TERRAIN, FG_DEBUG, " fgBucketOffset " << pending );
243 // schedule a tile row(column) for loading
244 void FGTileMgr::scroll( void )
246 FG_LOG( FG_TERRAIN, FG_DEBUG, "schedule_row: Scrolling" );
250 switch( scroll_direction ) {
252 FG_LOG( FG_TERRAIN, FG_DEBUG,
253 " (North) Loading " << tile_diameter << " tiles" );
254 dw = tile_diameter / 2;
256 for ( i = 0; i < tile_diameter; i++ ) {
257 sched_tile( BucketOffset( i - dw, dh ) );
261 FG_LOG( FG_TERRAIN, FG_DEBUG,
262 " (East) Loading " << tile_diameter << " tiles" );
263 dh = tile_diameter / 2;
265 for ( i = 0; i < tile_diameter; i++ ) {
266 sched_tile( BucketOffset( dw, i - dh ) );
270 FG_LOG( FG_TERRAIN, FG_DEBUG,
271 " (South) Loading " << tile_diameter << " tiles" );
272 dw = tile_diameter / 2;
274 for ( i = 0; i < tile_diameter; i++ ) {
275 sched_tile( BucketOffset( i - dw, dh ) );
279 FG_LOG( FG_TERRAIN, FG_DEBUG,
280 " (West) Loading " << tile_diameter << " tiles" );
281 dh = tile_diameter / 2;
283 for ( i = 0; i < tile_diameter; i++ ) {
284 sched_tile( BucketOffset( dw, i - dh ) );
288 FG_LOG( FG_TERRAIN, FG_WARN, "UNKNOWN SCROLL DIRECTION in schedule_row" );
291 FG_LOG( FG_TERRAIN, FG_DEBUG, "\tschedule_row returns" );
295 void FGTileMgr::initialize_queue()
297 // First time through or we have teleported, initialize the
298 // system and load all relavant tiles
300 FG_LOG( FG_TERRAIN, FG_INFO, "Updating Tile list for " << current_bucket );
301 FG_LOG( FG_TERRAIN, FG_INFO, " Updating Tile list for " << current_bucket );
302 FG_LOG( FG_TERRAIN, FG_INFO, " Loading "
303 << tile_diameter * tile_diameter << " tiles" );
306 scroll_direction = SCROLL_INIT;
308 // wipe/initialize tile cache
309 global_tile_cache.init();
310 previous_bucket.make_bad();
312 // build the local area list and schedule tiles for loading
314 // start with the center tile and work out in concentric
317 sched_tile( current_bucket );
319 for ( i = 3; i <= tile_diameter; i = i + 2 ) {
324 for ( j = -span; j <= span; ++j ) {
325 sched_tile( BucketOffset( j, -span ) );
329 for ( j = -span; j <= span; ++j ) {
330 sched_tile( BucketOffset( j, span ) );
334 for ( j = -span + 1; j <= span - 1; ++j ) {
335 sched_tile( BucketOffset( -span, j ) );
336 sched_tile( BucketOffset( span, j ) );
341 // Now force a load of the center tile and inner ring so we
342 // have something to see in our first frame.
343 for ( i = 0; i < 9; ++i ) {
344 if ( load_queue.size() ) {
345 FG_LOG( FG_TERRAIN, FG_DEBUG,
346 "Load queue not empty, loading a tile" );
348 FGLoadRec pending = load_queue.front();
349 load_queue.pop_front();
350 load_tile( pending.b, pending.cache_index );
356 // forced emptying of the queue
357 // This is necessay to keep bookeeping straight for the
358 // tile_cache -- which actually handles all the
360 void FGTileMgr::destroy_queue() {
361 while( load_queue.size() ) {
362 FG_LOG( FG_TERRAIN, FG_INFO,
363 "Load queue not empty, popping a tile" );
364 FGLoadRec pending = load_queue.front();
365 load_queue.pop_front();
366 FGTileEntry *t = global_tile_cache.get_tile( pending.cache_index );
367 // just t->mark_unused() should be enough
368 // but a little paranoia doesn't hurt us here
369 if(t->is_scheduled_for_use())
372 load_tile( pending.b, pending.cache_index );
377 // given the current lon/lat (in degrees), fill in the array of local
378 // chunks. If the chunk isn't already in the cache, then read it from
380 int FGTileMgr::update( double lon, double lat ) {
381 // FG_LOG( FG_TERRAIN, FG_DEBUG, "FGTileMgr::update()" );
383 // FGInterface *f = current_aircraft.fdm_state;
385 // lonlat for this update
386 // longitude = f->get_Longitude() * RAD_TO_DEG;
387 // latitude = f->get_Latitude() * RAD_TO_DEG;
390 // FG_LOG( FG_TERRAIN, FG_DEBUG, "lon "<< lonlat[LON] <<
391 // " lat " << lonlat[LAT] );
393 current_bucket.set_bucket( longitude, latitude );
394 // FG_LOG( FG_TERRAIN, FG_DEBUG, "Updating Tile list for " << current_bucket );
396 tile_index = global_tile_cache.exists(current_bucket);
397 // FG_LOG( FG_TERRAIN, FG_DEBUG, "tile index " << tile_index );
399 if ( tile_index >= 0 ) {
400 current_tile = global_tile_cache.get_tile(tile_index);
401 scenery.next_center = current_tile->center;
403 FG_LOG( FG_TERRAIN, FG_WARN, "Tile not found (Ok if initializing)" );
406 if ( state == Running ) {
407 if( current_bucket == previous_bucket) {
408 FG_LOG( FG_TERRAIN, FG_DEBUG, "Same bucket as last time" );
409 scroll_direction = SCROLL_NONE;
411 // We've moved to a new bucket, we need to scroll our
412 // structures, and load in the new tiles
413 // CURRENTLY THIS ASSUMES WE CAN ONLY MOVE TO ADJACENT TILES.
414 // AT ULTRA HIGH SPEEDS THIS ASSUMPTION MAY NOT BE VALID IF
415 // THE AIRCRAFT CAN SKIP A TILE IN A SINGLE ITERATION.
417 if ( (current_bucket.get_lon() > previous_bucket.get_lon()) ||
418 ( (current_bucket.get_lon() == previous_bucket.get_lon()) &&
419 (current_bucket.get_x() > previous_bucket.get_x()) ) )
421 scroll_direction = SCROLL_EAST;
423 else if ( (current_bucket.get_lon() < previous_bucket.get_lon()) ||
424 ( (current_bucket.get_lon() == previous_bucket.get_lon()) &&
425 (current_bucket.get_x() < previous_bucket.get_x()) ) )
427 scroll_direction = SCROLL_WEST;
430 if ( (current_bucket.get_lat() > previous_bucket.get_lat()) ||
431 ( (current_bucket.get_lat() == previous_bucket.get_lat()) &&
432 (current_bucket.get_y() > previous_bucket.get_y()) ) )
434 scroll_direction = SCROLL_NORTH;
436 else if ( (current_bucket.get_lat() < previous_bucket.get_lat()) ||
437 ( (current_bucket.get_lat() == previous_bucket.get_lat()) &&
438 (current_bucket.get_y() < previous_bucket.get_y()) ) )
440 scroll_direction = SCROLL_SOUTH;
446 } else if ( state == Start || state == Inited ) {
451 if ( load_queue.size() ) {
452 FG_LOG( FG_TERRAIN, FG_DEBUG, "Load queue not empty, loading a tile" );
454 FGLoadRec pending = load_queue.front();
455 load_queue.pop_front();
456 load_tile( pending.b, pending.cache_index );
459 if ( scenery.center == Point3D(0.0) ) {
461 // cout << "initializing ... " << endl;
462 Point3D geod_pos = Point3D( longitude * DEG_TO_RAD,
463 latitude * DEG_TO_RAD,
465 Point3D tmp_abs_view_pos = fgGeodToCart( geod_pos );
466 scenery.center = tmp_abs_view_pos;
467 // cout << "abs_view_pos = " << tmp_abs_view_pos << endl;
469 current_elev_ssg( tmp_abs_view_pos,
472 // cout << "abs view pos = " << current_view.abs_view_pos
473 // << " view pos = " << current_view.view_pos << endl;
474 current_elev_ssg( current_view.abs_view_pos,
475 current_view.view_pos );
478 // cout << "current elevation (ssg) == " << scenery.cur_elev << endl;
480 previous_bucket = current_bucket;
481 last_longitude = longitude;
482 last_latitude = latitude;
487 // Prepare the ssg nodes ... for each tile, set it's proper
488 // transform and update it's range selector based on current
490 void FGTileMgr::prep_ssg_node( int idx ) {
493 void FGTileMgr::prep_ssg_nodes( void ) {
499 #ifndef FG_OLD_WEATHER
500 if ( WeatherDatabase != NULL ) {
501 vis = WeatherDatabase->getWeatherVisibility();
506 vis = current_weather.get_visibility();
508 // cout << "visibility = " << vis << endl;
510 // traverse the potentially viewable tile list and update range
511 // selector and transform
512 for ( int i = 0; i < (int)global_tile_cache.get_size(); i++ ) {
513 t = global_tile_cache.get_tile( i );
515 if ( t->is_loaded() ) {
516 // set range selector (LOD trick) to be distance to center
517 // of tile + bounding radius
519 ranges[1] = vis + t->bounding_radius;
520 t->range_ptr->setRanges( ranges, 2 );
522 // calculate tile offset
523 t->SetOffset( scenery.center );
525 // calculate ssg transform
527 sgSetCoord( &sgcoord,
528 t->offset.x(), t->offset.y(), t->offset.z(),
530 t->transform_ptr->setTransform( &sgcoord );