diff --git a/src/Scenery/Makefile.am b/src/Scenery/Makefile.am index 2476df614..370762135 100644 --- a/src/Scenery/Makefile.am +++ b/src/Scenery/Makefile.am @@ -2,8 +2,8 @@ noinst_LIBRARIES = libScenery.a libScenery_a_SOURCES = \ hitlist.cxx hitlist.hxx \ + newcache.cxx newcache.hxx \ scenery.cxx scenery.hxx \ - tilecache.cxx tilecache.hxx \ tileentry.cxx tileentry.hxx \ tilemgr.cxx tilemgr.hxx diff --git a/src/Scenery/newcache.cxx b/src/Scenery/newcache.cxx new file mode 100644 index 000000000..e9b848f13 --- /dev/null +++ b/src/Scenery/newcache.cxx @@ -0,0 +1,290 @@ +// newcache.cxx -- routines to handle scenery tile caching +// +// Written by Curtis Olson, started December 2000. +// +// Copyright (C) 2000 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include +#include + +#include // plib include + +#include +#include +#include +#include + +#include
+#include +#include // for scenery.center + +#include "newcache.hxx" +#include "tileentry.hxx" + +FG_USING_NAMESPACE(std); + +// a cheesy hack (to be fixed later) +extern ssgBranch *terrain; +extern ssgEntity *penguin; + + +// the tile cache +FGNewCache global_tile_cache; + + +// Constructor +FGNewCache::FGNewCache( void ) { + tile_cache.clear(); +} + + +// Destructor +FGNewCache::~FGNewCache( void ) { +} + + +// Free a tile cache entry +void FGNewCache::entry_free( long cache_index ) { + FG_LOG( FG_TERRAIN, FG_INFO, "FREEING CACHE ENTRY = " << cache_index ); + FGTileEntry *e = tile_cache[cache_index]; + e->free_tile(); + delete( e ); + tile_cache.erase( cache_index ); +} + + +// Initialize the tile cache subsystem +void FGNewCache::init( void ) { + FG_LOG( FG_TERRAIN, FG_INFO, "Initializing the tile cache." ); + + // expand cache if needed. For best results ... i.e. to avoid + // tile load problems and blank areas: + max_cache_size = 50; // a random number to start with + FG_LOG( FG_TERRAIN, FG_INFO, " max cache size = " + << max_cache_size ); + FG_LOG( FG_TERRAIN, FG_INFO, " current cache size = " + << tile_cache.size() ); + + tile_map_iterator current = tile_cache.begin(); + tile_map_iterator end = tile_cache.end(); + + for ( ; current != end; ++current ) { + long index = current->first; + cout << "clearing " << index << endl; + FGTileEntry *e = current->second; + e->tile_bucket.make_bad(); + entry_free(index); + } + + // and ... just in case we missed something ... + terrain->removeAllKids(); + + FG_LOG( FG_TERRAIN, FG_INFO, " done with init()" ); +} + + +// Search for the specified "bucket" in the cache +bool FGNewCache::exists( const FGBucket& b ) { + long tile_index = b.gen_index(); + tile_map_iterator it = tile_cache.find( tile_index ); + + return ( it != tile_cache.end() ); +} + + +#if 0 +static void print_refs( ssgSelector *sel, ssgTransform *trans, + ssgRangeSelector *range) +{ + cout << "selector -> " << sel->getRef() + << " transform -> " << trans->getRef() + << " range -> " << range->getRef() << endl; +} +#endif + + +// Fill in a tile cache entry with real data for the specified bucket +void FGNewCache::fill_in( const FGBucket& b ) { + FG_LOG( FG_TERRAIN, FG_INFO, "FILL IN CACHE ENTRY = " << b.gen_index() ); + + // clear out a distant entry in the cache if needed. + make_space(); + + // create the entry + FGTileEntry *e = new FGTileEntry; + + // register it in the cache + long tile_index = b.gen_index(); + tile_cache[tile_index] = e; + + // update the contents + e->center = Point3D( 0.0 ); + if ( e->vec3_ptrs.size() || e->vec2_ptrs.size() || e->index_ptrs.size() ) { + FG_LOG( FG_TERRAIN, FG_ALERT, + "Attempting to overwrite existing or" + << " not properly freed leaf data." ); + exit(-1); + } + + e->select_ptr = new ssgSelector; + e->transform_ptr = new ssgTransform; + e->range_ptr = new ssgRangeSelector; + e->tile_bucket = b; + + FGPath tile_path; + if ( globals->get_options()->get_fg_scenery() != "" ) { + tile_path.set( globals->get_options()->get_fg_scenery() ); + } else { + tile_path.set( globals->get_options()->get_fg_root() ); + tile_path.append( "Scenery" ); + } + tile_path.append( b.gen_base_path() ); + + // Load the appropriate data file + FGPath tile_base = tile_path; + tile_base.append( b.gen_index_str() ); + ssgBranch *new_tile = fgObjLoad( tile_base.str(), e, true ); + + if ( new_tile != NULL ) { + e->range_ptr->addKid( new_tile ); + } + + // load custom objects + cout << "CUSTOM OBJECTS" << endl; + + FGPath index_path = tile_path; + index_path.append( b.gen_index_str() ); + index_path.concat( ".ind" ); + + cout << "Looking in " << index_path.str() << endl; + + fg_gzifstream in( index_path.str() ); + + if ( in.is_open() ) { + string token, name; + + while ( ! in.eof() ) { + in >> token; + in >> name; +#if defined ( macintosh ) || defined ( _MSC_VER ) + in >> ::skipws; +#else + in >> skipws; +#endif + cout << "token = " << token << " name = " << name << endl; + + FGPath custom_path = tile_path; + custom_path.append( name ); + ssgBranch *custom_obj = fgObjLoad( custom_path.str(), e, false ); + if ( (new_tile != NULL) && (custom_obj != NULL) ) { + new_tile -> addKid( custom_obj ); + } + } + } + + e->transform_ptr->addKid( e->range_ptr ); + + // calculate initial tile offset + e->SetOffset( scenery.center ); + sgCoord sgcoord; + sgSetCoord( &sgcoord, + e->offset.x(), e->offset.y(), e->offset.z(), + 0.0, 0.0, 0.0 ); + e->transform_ptr->setTransform( &sgcoord ); + + e->select_ptr->addKid( e->transform_ptr ); + terrain->addKid( e->select_ptr ); + + e->select_ptr->select(1); +} + + +// Ensure at least one entry is free in the cache +void FGNewCache::make_space() { + FG_LOG( FG_TERRAIN, FG_INFO, "Make space in cache" ); + + + cout << "cache size = " << tile_cache.size() << endl; + cout << "max size = " << max_cache_size << endl; + + if ( (int)tile_cache.size() < max_cache_size ) { + // space in the cache, return + return; + } + + while ( (int)tile_cache.size() >= max_cache_size ) { + sgdVec3 abs_view_pos; + float dist; + float max_dist = 0.0; + int max_index = -1; + + // we need to free the furthest entry + tile_map_iterator current = tile_cache.begin(); + tile_map_iterator end = tile_cache.end(); + + for ( ; current != end; ++current ) { + long index = current->first; + FGTileEntry *e = current->second; + + // calculate approximate distance from view point + sgdCopyVec3( abs_view_pos, + globals->get_current_view()->get_abs_view_pos() ); + + FG_LOG( FG_TERRAIN, FG_DEBUG, "DIST Abs view pos = " + << abs_view_pos[0] << "," + << abs_view_pos[1] << "," + << abs_view_pos[2] ); + FG_LOG( FG_TERRAIN, FG_DEBUG, + " ref point = " << e->center ); + + sgdVec3 center; + sgdSetVec3( center, e->center.x(), e->center.y(), e->center.z() ); + dist = sgdDistanceVec3( center, abs_view_pos ); + + FG_LOG( FG_TERRAIN, FG_DEBUG, " distance = " << dist ); + + if ( dist > max_dist ) { + max_dist = dist; + max_index = index; + } + } + + // If we made it this far, then there were no open cache entries. + // We will instead free the furthest cache entry and return it's + // index. + + if ( max_index >= 0 ) { + FG_LOG( FG_TERRAIN, FG_DEBUG, " max_dist = " << max_dist ); + FG_LOG( FG_TERRAIN, FG_DEBUG, " index = " << max_index ); + entry_free( max_index ); + } else { + FG_LOG( FG_TERRAIN, FG_ALERT, "WHOOPS!!! Dying in next_avail()" ); + exit( -1 ); + } + } +} diff --git a/src/Scenery/newcache.hxx b/src/Scenery/newcache.hxx new file mode 100644 index 000000000..7d3c7b3b1 --- /dev/null +++ b/src/Scenery/newcache.hxx @@ -0,0 +1,129 @@ +// newcache.hxx -- routines to handle scenery tile caching +// +// Written by Curtis Olson, started December 2000. +// +// Copyright (C) 2000 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + + +#ifndef _NEWCACHE_HXX +#define _NEWCACHE_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include + +#include + +#include +#include + +#include "tileentry.hxx" + +FG_USING_STD(map); + + +typedef map < long, FGTileEntry * > tile_map; +typedef tile_map::iterator tile_map_iterator; +typedef tile_map::const_iterator const_tile_map_iterator; + + +// A class to store and manage a pile of tiles +class FGNewCache { + + // cache storage space + tile_map tile_cache; + + // maximum cache size + int max_cache_size; + + // pointers to allow an external linear traversal of cache entries + tile_map_iterator current; + + // Free a tile cache entry + void FGNewCache::entry_free( long cache_index ); + +public: + + // Constructor + FGNewCache( void ); + + // Destructor + ~FGNewCache( void ); + + // Initialize the tile cache subsystem + void init( void ); + + // Check if the specified "bucket" exists in the cache + bool exists( const FGBucket& b ); + + // Ensure at least one entry is free in the cache + void FGNewCache::make_space(); + + // Fill in a tile cache entry with real data for the specified bucket + void fill_in( const FGBucket& b ); + + // Return a pointer to the specified tile cache entry + inline FGTileEntry *get_tile( const long tile_index ) { + tile_map_iterator it = tile_cache.find( tile_index ); + if ( it != tile_cache.end() ) { + return it->second; + } else { + return NULL; + } + } + + // Return a pointer to the specified tile cache entry + inline FGTileEntry *get_tile( const FGBucket& b ) { + return get_tile( b.gen_index() ); + } + + // Return the cache size + inline size_t get_size() const { return tile_cache.size(); } + + // External linear traversal of cache + inline void reset_traversal() { current = tile_cache.begin(); } + inline bool at_end() { return current == tile_cache.end(); } + inline FGTileEntry *get_current() { + // cout << "index = " << current->first << endl; + return current->second; + } + inline void next() { ++current; } + + inline int get_max_cache_size() const { return max_cache_size; } + inline void set_max_cache_size( int m ) { max_cache_size = m; } +}; + + +// the tile cache +extern FGNewCache global_tile_cache; + + +#endif // _NEWCACHE_HXX diff --git a/src/Scenery/tilecache.hxx b/src/Scenery/tilecache.hxx index d1905720a..4658a195f 100644 --- a/src/Scenery/tilecache.hxx +++ b/src/Scenery/tilecache.hxx @@ -62,6 +62,9 @@ public: // Constructor FGTileCache( void ); + // Destructor + ~FGTileCache( void ); + // Initialize the tile cache subsystem void init( void ); @@ -84,9 +87,6 @@ public: // Return the cache size inline size_t get_size() const { return tile_cache.size(); } - - // Destructor - ~FGTileCache( void ); }; diff --git a/src/Scenery/tileentry.cxx b/src/Scenery/tileentry.cxx index 573a52346..973b918d0 100644 --- a/src/Scenery/tileentry.cxx +++ b/src/Scenery/tileentry.cxx @@ -40,9 +40,8 @@ FG_USING_STD(mem_fun_ref); // Constructor -FGTileEntry::FGTileEntry ( void ) - : ncount(0), - state(Unused) +FGTileEntry::FGTileEntry () + : ncount(0) { nodes.clear(); select_ptr = NULL; @@ -50,7 +49,7 @@ FGTileEntry::FGTileEntry ( void ) // Destructor -FGTileEntry::~FGTileEntry ( void ) { +FGTileEntry::~FGTileEntry () { // cout << "nodes = " << nodes.size() << endl;; // delete[] nodes; } @@ -81,9 +80,6 @@ void FGTileEntry::free_tile() { FG_LOG( FG_TERRAIN, FG_DEBUG, "FREEING TILE = (" << tile_bucket << ")" ); - // mark tile unused - mark_unused(); - FG_LOG( FG_TERRAIN, FG_DEBUG, " deleting " << nodes.size() << " nodes" ); nodes.clear(); @@ -142,21 +138,6 @@ void FGTileEntry::free_tile() { // when a tile is still in the cache, but not in the immediate draw // list, it can still remain in the scene graph, but we use a range // selector to disable it from ever being drawn. -void -FGTileEntry::ssg_disable() { - // cout << "TILE STATE = " << state << endl; - if ( state == Scheduled_for_use ) { - state = Scheduled_for_cache; - } else if ( state == Scheduled_for_cache ) { - // do nothing - } else if ( (state == Loaded) || (state == Cached) ) { - state = Cached; - // cout << "DISABLING SSG NODE" << endl; - select_ptr->select(0); - } else { - FG_LOG( FG_TERRAIN, FG_ALERT, - "Trying to disable an unused tile! Dying" ); - exit(-1); - } - // cout << "TILE STATE = " << state << endl; +void FGTileEntry::ssg_disable() { + select_ptr->select(0); } diff --git a/src/Scenery/tileentry.hxx b/src/Scenery/tileentry.hxx index 612b059e2..95048b079 100644 --- a/src/Scenery/tileentry.hxx +++ b/src/Scenery/tileentry.hxx @@ -66,25 +66,12 @@ typedef point_list::const_iterator const_point_list_iterator; // Scenery tile class class FGTileEntry { -private: - - // Tile state - enum tile_state { - Unused = 0, - Scheduled_for_use = 1, - Scheduled_for_cache = 2, - Loaded = 3, - Cached = 4 - }; - public: typedef vector < sgVec3 * > free_vec3_list; typedef vector < sgVec2 * > free_vec2_list; typedef vector < unsigned short * > free_index_list; -public: - // node list point_list nodes; int ncount; @@ -97,9 +84,6 @@ public: // this tile's official location in the world FGBucket tile_bucket; - // the tile cache will keep track here if the tile is being used - tile_state state; - // list of pointers to memory chunks that need to be freed when // tile entry goes away free_vec3_list vec3_ptrs; @@ -130,10 +114,10 @@ public: public: // Constructor - FGTileEntry ( void ); + FGTileEntry(); // Destructor - ~FGTileEntry ( void ); + ~FGTileEntry(); // Clean up the memory used by this tile and delete the arrays // used by ssg as well as the whole ssg branch @@ -146,23 +130,27 @@ public: } // Return this tile's offset - inline Point3D get_offset( void ) const { return offset; } - - inline bool is_unused() const { return state == Unused; } - inline bool is_scheduled_for_use() const { - return state == Scheduled_for_use; - } - inline bool is_scheduled_for_cache() const { - return state == Scheduled_for_cache; - } - inline bool is_loaded() const { return state == Loaded; } - inline bool is_cached() const { return state == Cached; } + inline Point3D get_offset() const { return offset; } - inline void mark_unused() { state = Unused; } - inline void mark_scheduled_for_use() { state = Scheduled_for_use; } - inline void mark_scheduled_for_cache() { state = Scheduled_for_use; } - inline void mark_loaded() { state = Loaded; } + // Update the ssg transform node for this tile so it can be + // properly drawn relative to our (0,0,0) point + inline void prep_ssg_node( const Point3D& p, float vis) { + SetOffset( p ); +// #define USE_UP_AND_COMING_PLIB_FEATURE +#ifdef USE_UP_AND_COMING_PLIB_FEATURE + range_ptr->setRange( 0, SG_ZERO ); + range_ptr->setRange( 1, vis + bounding_radius ); +#else + float ranges[2]; + ranges[0] = SG_ZERO; + ranges[1] = vis + bounding_radius; + range_ptr->setRanges( ranges, 2 ); +#endif + sgVec3 sgTrans; + sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() ); + transform_ptr->setTransform( sgTrans ); + } // when a tile is still in the cache, but not in the immediate // draw l ist, it can still remain in the scene graph, but we use @@ -171,9 +159,4 @@ public: }; -typedef vector < FGTileEntry > tile_list; -typedef tile_list::iterator tile_list_iterator; -typedef tile_list::const_iterator const_tile_list_iterator; - - #endif // _TILEENTRY_HXX diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index 0266ca5b6..b9af69eb6 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -39,7 +39,6 @@ #include #include -// #include #include
#include @@ -49,8 +48,8 @@ # include #endif +#include "newcache.hxx" #include "scenery.hxx" -#include "tilecache.hxx" #include "tilemgr.hxx" #define TEST_LAST_HIT_CACHE @@ -70,19 +69,19 @@ static inline Point3D operator + (const Point3D& a, const sgdVec3 b) // Constructor -FGTileMgr::FGTileMgr ( void ): +FGTileMgr::FGTileMgr(): state( Start ) { } // Destructor -FGTileMgr::~FGTileMgr ( void ) { +FGTileMgr::~FGTileMgr() { } // Initialize the Tile Manager subsystem -int FGTileMgr::init( void ) { +int FGTileMgr::init() { FG_LOG( FG_TERRAIN, FG_INFO, "Initializing Tile Manager subsystem." ); if ( state != Start ) { @@ -92,22 +91,16 @@ int FGTileMgr::init( void ) { } else { FG_LOG( FG_TERRAIN, FG_INFO, "... First time through." ); + global_tile_cache.init(); } - global_tile_cache.init(); hit_list.clear(); state = Inited; - tile_diameter = globals->get_options()->get_tile_diameter(); - FG_LOG( FG_TERRAIN, FG_INFO, "Tile Diameter = " << tile_diameter); - previous_bucket.make_bad(); current_bucket.make_bad(); - scroll_direction = SCROLL_INIT; - tile_index = -9999; - longitude = latitude = -1000.0; last_longitude = last_latitude = -1000.0; @@ -116,40 +109,32 @@ int FGTileMgr::init( void ) { // schedule a tile for loading -int FGTileMgr::sched_tile( const FGBucket& b ) { +void FGTileMgr::sched_tile( const FGBucket& b ) { // see if tile already exists in the cache - int cache_index = global_tile_cache.exists( b ); + FGTileEntry *t = global_tile_cache.get_tile( b ); - if ( cache_index >= 0 ) { + if ( t != NULL ) { // tile exists in cache, reenable it. // cout << "REENABLING DISABLED TILE" << endl; - FGTileEntry *t = global_tile_cache.get_tile( cache_index ); t->select_ptr->select( 1 ); - t->mark_loaded(); } else { - // find the next available cache entry and mark it as - // scheduled - cache_index = global_tile_cache.next_avail(); - FGTileEntry *t = global_tile_cache.get_tile( cache_index ); - t->mark_scheduled_for_use(); - // register a load request - FGLoadRec request; - request.b = b; - request.cache_index = cache_index; - load_queue.push_back( request ); + load_queue.push_back( b ); } - - return cache_index; } // load a tile -void FGTileMgr::load_tile( const FGBucket& b, int cache_index) { +void FGTileMgr::load_tile( const FGBucket& b ) { + // see if tile already exists in the cache + FGTileEntry *t = global_tile_cache.get_tile( b ); - FG_LOG( FG_TERRAIN, FG_DEBUG, "Loading tile " << b ); - global_tile_cache.fill_in(cache_index, b); - FG_LOG( FG_TERRAIN, FG_DEBUG, "Loaded for cache index: " << cache_index ); + if ( t == NULL ) { + FG_LOG( FG_TERRAIN, FG_DEBUG, "Loading tile " << b ); + global_tile_cache.fill_in( b ); + } else { + FG_LOG( FG_TERRAIN, FG_DEBUG, "Tile already in cache " << b ); + } } @@ -214,89 +199,65 @@ bool FGTileMgr::current_elev_ssg( sgdVec3 abs_view_pos, sgVec3 view_pos, } -FGBucket FGTileMgr::BucketOffset( int dx, int dy ) -{ - double clat, clon, span; - if( scroll_direction == SCROLL_INIT ) { - // use current latitude and longitude - // walk dy units in the lat direction - clat = current_bucket.get_center_lat() + dy * FG_BUCKET_SPAN; +// schedule a needed buckets for loading +void FGTileMgr::schedule_needed() { + double vis; - // find the lon span for the new latitude - span = bucket_span( clat ); - - // walk dx units in the lon direction - clon = longitude + dx * span; - } else { - // use previous latitude and longitude - // walk dy units in the lat direction - clat = previous_bucket.get_center_lat() + dy * FG_BUCKET_SPAN; - - // find the lon span for the new latitude - span = bucket_span( clat ); - - // walk dx units in the lon direction - clon = last_longitude + dx * span; - } - - while ( clon < -180.0 ) clon += 360.0; - while ( clon >= 180.0 ) clon -= 360.0; - pending.set_bucket( clon, clat ); - - FG_LOG( FG_TERRAIN, FG_DEBUG, " fgBucketOffset " << pending ); - return pending; -} - - -// schedule a tile row(column) for loading -void FGTileMgr::scroll( void ) -{ - FG_LOG( FG_TERRAIN, FG_DEBUG, "schedule_row: Scrolling" ); - - int i, dw, dh; - - switch( scroll_direction ) { - case SCROLL_NORTH: - FG_LOG( FG_TERRAIN, FG_DEBUG, - " (North) Loading " << tile_diameter << " tiles" ); - dw = tile_diameter / 2; - dh = dw + 1; - for ( i = 0; i < tile_diameter; i++ ) { - sched_tile( BucketOffset( i - dw, dh ) ); - } - break; - case SCROLL_EAST: - FG_LOG( FG_TERRAIN, FG_DEBUG, - " (East) Loading " << tile_diameter << " tiles" ); - dh = tile_diameter / 2; - dw = dh + 1; - for ( i = 0; i < tile_diameter; i++ ) { - sched_tile( BucketOffset( dw, i - dh ) ); - } - break; - case SCROLL_SOUTH: - FG_LOG( FG_TERRAIN, FG_DEBUG, - " (South) Loading " << tile_diameter << " tiles" ); - dw = tile_diameter / 2; - dh = -dw - 1; - for ( i = 0; i < tile_diameter; i++ ) { - sched_tile( BucketOffset( i - dw, dh ) ); - } - break; - case SCROLL_WEST: - FG_LOG( FG_TERRAIN, FG_DEBUG, - " (West) Loading " << tile_diameter << " tiles" ); - dh = tile_diameter / 2; - dw = -dh - 1; - for ( i = 0; i < tile_diameter; i++ ) { - sched_tile( BucketOffset( dw, i - dh ) ); - } - break; - default: - FG_LOG( FG_TERRAIN, FG_WARN, "UNKNOWN SCROLL DIRECTION in schedule_row" ); - ; +#ifndef FG_OLD_WEATHER + if ( WeatherDatabase != NULL ) { + vis = WeatherDatabase->getWeatherVisibility(); + } else { + vis = 16000; + } +#else + vis = current_weather.get_visibility(); +#endif + cout << "visibility = " << vis << endl; + + double clat = (int)current_bucket.get_center_lat(); + if ( clat > 0 ) { + clat = (int)clat + 0.5; + } else { + clat = (int)clat - 0.5; + } + double clat_rad = clat * DEG_TO_RAD; + double cos_lat = cos( clat_rad ); + double local_radius = cos_lat * EQUATORIAL_RADIUS_M; + double local_perimeter = 2.0 * local_radius * FG_PI; + double degree_width = local_perimeter / 360.0; + + // cout << "clat = " << clat << endl; + // cout << "clat (radians) = " << clat_rad << endl; + // cout << "cos(lat) = " << cos_lat << endl; + // cout << "local_radius = " << local_radius << endl; + // cout << "local_perimeter = " << local_perimeter << endl; + cout << "degree_width = " << degree_width << endl; + + double perimeter = 2.0 * EQUATORIAL_RADIUS_M * FG_PI; + double degree_height = perimeter / 360.0; + cout << "degree_height = " << degree_height << endl; + + double tile_width = current_bucket.get_width() * degree_width; + double tile_height = current_bucket.get_height() * degree_height; + cout << "tile width = " << tile_width << " tile_height = " << tile_height + << endl; + + xrange = (int)(vis / tile_width) + 1; + yrange = (int)(vis / tile_height) + 1; + if ( xrange < 1 ) { xrange = 1; } + if ( yrange < 1 ) { yrange = 1; } + cout << "xrange = " << xrange << " yrange = " << yrange << endl; + + global_tile_cache.set_max_cache_size( (2*xrange + 2) * (2*yrange + 2) ); + + for ( int x = -xrange; x <= xrange; ++x ) { + for ( int y = -yrange; y <= yrange; ++y ) { + FGBucket b = fgBucketOffset( longitude, latitude, x, y ); + if ( ! global_tile_cache.exists( b ) ) { + sched_tile( b ); + } + } } - FG_LOG( FG_TERRAIN, FG_DEBUG, "\tschedule_row returns" ); } @@ -306,15 +267,14 @@ void FGTileMgr::initialize_queue() // system and load all relavant tiles FG_LOG( FG_TERRAIN, FG_INFO, "Updating Tile list for " << current_bucket ); - FG_LOG( FG_TERRAIN, FG_INFO, " Updating Tile list for " << current_bucket ); FG_LOG( FG_TERRAIN, FG_INFO, " Loading " - << tile_diameter * tile_diameter << " tiles" ); + << xrange * yrange << " tiles" ); + cout << "tile cache size = " << global_tile_cache.get_size() << endl; int i; - scroll_direction = SCROLL_INIT; // wipe/initialize tile cache - global_tile_cache.init(); + // global_tile_cache.init(); previous_bucket.make_bad(); // build the local area list and schedule tiles for loading @@ -322,29 +282,7 @@ void FGTileMgr::initialize_queue() // start with the center tile and work out in concentric // "rings" - sched_tile( current_bucket ); - - for ( i = 3; i <= tile_diameter; i = i + 2 ) { - int j; - int span = i / 2; - - // bottom row - for ( j = -span; j <= span; ++j ) { - sched_tile( BucketOffset( j, -span ) ); - } - - // top row - for ( j = -span; j <= span; ++j ) { - sched_tile( BucketOffset( j, span ) ); - } - - // middle rows - for ( j = -span + 1; j <= span - 1; ++j ) { - sched_tile( BucketOffset( -span, j ) ); - sched_tile( BucketOffset( span, j ) ); - } - - } + schedule_needed(); // Now force a load of the center tile and inner ring so we // have something to see in our first frame. @@ -353,9 +291,9 @@ void FGTileMgr::initialize_queue() FG_LOG( FG_TERRAIN, FG_DEBUG, "Load queue not empty, loading a tile" ); - FGLoadRec pending = load_queue.front(); + FGBucket pending = load_queue.front(); load_queue.pop_front(); - load_tile( pending.b, pending.cache_index ); + load_tile( pending ); } } } @@ -366,19 +304,7 @@ void FGTileMgr::initialize_queue() // tile_cache -- which actually handles all the // (de)allocations void FGTileMgr::destroy_queue() { - while( load_queue.size() ) { - FG_LOG( FG_TERRAIN, FG_INFO, - "Load queue not empty, popping a tile" ); - FGLoadRec pending = load_queue.front(); - load_queue.pop_front(); - FGTileEntry *t = global_tile_cache.get_tile( pending.cache_index ); - // just t->mark_unused() should be enough - // but a little paranoia doesn't hurt us here - if(t->is_scheduled_for_use()) - t->mark_unused(); - else - load_tile( pending.b, pending.cache_index ); - } + load_queue.clear(); } @@ -386,7 +312,7 @@ void FGTileMgr::destroy_queue() { // chunks. If the chunk isn't already in the cache, then read it from // disk. int FGTileMgr::update( double lon, double lat ) { - // FG_LOG( FG_TERRAIN, FG_DEBUG, "FGTileMgr::update()" ); + FG_LOG( FG_TERRAIN, FG_DEBUG, "FGTileMgr::update()" ); // FGInterface *f = current_aircraft.fdm_state; @@ -401,67 +327,31 @@ int FGTileMgr::update( double lon, double lat ) { current_bucket.set_bucket( longitude, latitude ); // FG_LOG( FG_TERRAIN, FG_DEBUG, "Updating Tile list for " << current_bucket ); - tile_index = global_tile_cache.exists(current_bucket); - // FG_LOG( FG_TERRAIN, FG_DEBUG, "tile index " << tile_index ); - - if ( tile_index >= 0 ) { - current_tile = global_tile_cache.get_tile(tile_index); + if ( global_tile_cache.exists( current_bucket ) ) { + current_tile = global_tile_cache.get_tile( current_bucket ); scenery.next_center = current_tile->center; } else { FG_LOG( FG_TERRAIN, FG_WARN, "Tile not found (Ok if initializing)" ); } if ( state == Running ) { - if( current_bucket == previous_bucket) { - FG_LOG( FG_TERRAIN, FG_DEBUG, "Same bucket as last time" ); - scroll_direction = SCROLL_NONE; - } else { - // We've moved to a new bucket, we need to scroll our - // structures, and load in the new tiles - // CURRENTLY THIS ASSUMES WE CAN ONLY MOVE TO ADJACENT TILES. - // AT ULTRA HIGH SPEEDS THIS ASSUMPTION MAY NOT BE VALID IF - // THE AIRCRAFT CAN SKIP A TILE IN A SINGLE ITERATION. - - if ( (current_bucket.get_lon() > previous_bucket.get_lon()) || - ( (current_bucket.get_lon() == previous_bucket.get_lon()) && - (current_bucket.get_x() > previous_bucket.get_x()) ) ) - { - scroll_direction = SCROLL_EAST; - } - else if ( (current_bucket.get_lon() < previous_bucket.get_lon()) || - ( (current_bucket.get_lon() == previous_bucket.get_lon()) && - (current_bucket.get_x() < previous_bucket.get_x()) ) ) - { - scroll_direction = SCROLL_WEST; - } - - if ( (current_bucket.get_lat() > previous_bucket.get_lat()) || - ( (current_bucket.get_lat() == previous_bucket.get_lat()) && - (current_bucket.get_y() > previous_bucket.get_y()) ) ) - { - scroll_direction = SCROLL_NORTH; - } - else if ( (current_bucket.get_lat() < previous_bucket.get_lat()) || - ( (current_bucket.get_lat() == previous_bucket.get_lat()) && - (current_bucket.get_y() < previous_bucket.get_y()) ) ) - { - scroll_direction = SCROLL_SOUTH; - } - - scroll(); + if( current_bucket != previous_bucket) { + // We've moved to a new bucket, we need to schedule any + // needed tiles for loading. + schedule_needed(); } - } else if ( state == Start || state == Inited ) { initialize_queue(); state = Running; } if ( load_queue.size() ) { - FG_LOG( FG_TERRAIN, FG_DEBUG, "Load queue not empty, loading a tile" ); + FG_LOG( FG_TERRAIN, FG_INFO, "Load queue size = " << load_queue.size() + << " loading a tile" ); - FGLoadRec pending = load_queue.front(); + FGBucket pending = load_queue.front(); load_queue.pop_front(); - load_tile( pending.b, pending.cache_index ); + load_tile( pending ); } if ( scenery.center == Point3D(0.0) ) { @@ -509,20 +399,12 @@ int FGTileMgr::update( double lon, double lat ) { return 1; } -// Prepare the ssg nodes ... for each tile, set it's proper -// transform and update it's range selector based on current -// visibilty -void FGTileMgr::prep_ssg_node( int idx ) { -} -void FGTileMgr::prep_ssg_nodes( void ) { - FGTileEntry *t; - float ranges[2]; - ranges[0] = 0.0f; - double vis = 0.0; +void FGTileMgr::prep_ssg_nodes() { + float vis = 0.0; #ifndef FG_OLD_WEATHER - if ( WeatherDatabase != NULL ) { + if ( WeatherDatabase ) { vis = WeatherDatabase->getWeatherVisibility(); } else { vis = 16000; @@ -534,25 +416,18 @@ void FGTileMgr::prep_ssg_nodes( void ) { // traverse the potentially viewable tile list and update range // selector and transform - for ( int i = 0; i < (int)global_tile_cache.get_size(); i++ ) { - t = global_tile_cache.get_tile( i ); - if ( t->is_loaded() ) { - // set range selector (LOD trick) to be distance to center - // of tile + bounding radius + FGTileEntry *e; + Point3D p = scenery.center; + global_tile_cache.reset_traversal(); - ranges[1] = vis + t->bounding_radius; - t->range_ptr->setRanges( ranges, 2 ); - - // calculate tile offset - t->SetOffset( scenery.center ); - - // calculate ssg transform - sgCoord sgcoord; - sgSetCoord( &sgcoord, - t->offset.x(), t->offset.y(), t->offset.z(), - 0.0, 0.0, 0.0 ); - t->transform_ptr->setTransform( &sgcoord ); + while ( ! global_tile_cache.at_end() ) { + // cout << "processing a tile" << endl; + if ( (e = global_tile_cache.get_current()) ) { + e->prep_ssg_node( p, vis); + } else { + cout << "warning ... empty tile in cache" << endl; } - } + global_tile_cache.next(); + } } diff --git a/src/Scenery/tilemgr.hxx b/src/Scenery/tilemgr.hxx index dacc37634..b90c2df84 100644 --- a/src/Scenery/tilemgr.hxx +++ b/src/Scenery/tilemgr.hxx @@ -42,10 +42,6 @@ FG_USING_STD(list); -#define FG_LOCAL_X_Y 81 // max(o->tile_diameter) ** 2 - -#define FG_SQUARE( X ) ( (X) * (X) ) - #if defined(USE_MEM) || defined(WIN32) # define FG_MEM_COPY(to,from,n) memcpy(to, from, n) #else @@ -57,15 +53,6 @@ FG_USING_STD(list); class FGTileEntry; -class FGLoadRec { - -public: - - FGBucket b; - int cache_index; -}; - - class FGTileMgr { private: @@ -79,19 +66,8 @@ private: load_state state; - enum SCROLL_DIRECTION { - SCROLL_INIT = -1, - SCROLL_NONE = 0, - SCROLL_NORTH, - SCROLL_EAST, - SCROLL_SOUTH, - SCROLL_WEST, - }; - - SCROLL_DIRECTION scroll_direction; - // pending tile load queue - list < FGLoadRec > load_queue; + list < FGBucket > load_queue; // initialize the cache void initialize_queue(); @@ -101,16 +77,14 @@ private: // handles all the (de)allocations void destroy_queue(); - FGBucket BucketOffset( int dx, int dy ); - // schedule a tile for loading - int sched_tile( const FGBucket& b ); + void sched_tile( const FGBucket& b ); // load a tile - void load_tile( const FGBucket& b, int cache_index ); + void load_tile( const FGBucket& b ); - // schedule a tile row(column) for loading - void scroll( void ); + // schedule a needed buckets for loading + void FGTileMgr::schedule_needed(); // see comment at prep_ssg_nodes() void prep_ssg_node( int idx ); @@ -127,9 +101,8 @@ private: FGTileEntry *current_tile; - // index of current tile in tile cache; - long int tile_index; - int tile_diameter; + // x and y distance of tiles to load/draw + int xrange, yrange; // current longitude latitude double longitude; @@ -140,13 +113,13 @@ private: public: // Constructor - FGTileMgr ( void ); + FGTileMgr(); // Destructor - ~FGTileMgr ( void ); + ~FGTileMgr(); // Initialize the Tile Manager subsystem - int init( void ); + int init(); // given the current lon/lat (in degrees), fill in the array of // local chunks. If the chunk isn't already in the cache, then @@ -171,7 +144,7 @@ public: // Prepare the ssg nodes ... for each tile, set it's proper // transform and update it's range selector based on current // visibilty - void prep_ssg_nodes( void ); + void prep_ssg_nodes(); inline int queue_size() const { return load_queue.size(); } };