diff --git a/Simulator/Main/GLUTkey.cxx b/Simulator/Main/GLUTkey.cxx index a3c35bd79..58b72be81 100644 --- a/Simulator/Main/GLUTkey.cxx +++ b/Simulator/Main/GLUTkey.cxx @@ -354,8 +354,14 @@ void GLUTspecialkey(int k, int x, int y) { if( (toggle_pause = !t->getPause()) ) t->togglePauseMode(); BusyCursor(0); - fgTileMgrInit(); - fgTileMgrUpdate(); + if( global_tile_mgr.init() ) { + // Load the local scenery data + global_tile_mgr.update(); + } else { + FG_LOG( FG_GENERAL, FG_ALERT, + "Error in Tile Manager initialization!" ); + exit(-1); + } BusyCursor(1); if(toggle_pause) t->togglePauseMode(); diff --git a/Simulator/Main/GLUTmain.cxx b/Simulator/Main/GLUTmain.cxx index fbfbfb5bd..9822d4db4 100644 --- a/Simulator/Main/GLUTmain.cxx +++ b/Simulator/Main/GLUTmain.cxx @@ -360,7 +360,7 @@ static void fgRenderFrame( void ) { // xglMaterialfv (GL_FRONT, GL_DIFFUSE, white); } - fgTileMgrRender(); + global_tile_mgr.render(); xglDisable( GL_TEXTURE_2D ); xglDisable( GL_FOG ); @@ -587,7 +587,7 @@ static void fgMainLoop( void ) { #endif // see if we need to load any new scenery tiles - fgTileMgrUpdate(); + global_tile_mgr.update(); // Process/manage pending events global_events.Process(); diff --git a/Simulator/Main/fg_init.cxx b/Simulator/Main/fg_init.cxx index f569e5032..149eebb78 100644 --- a/Simulator/Main/fg_init.cxx +++ b/Simulator/Main/fg_init.cxx @@ -205,9 +205,9 @@ int fgInitSubsystems( void ) exit(-1); } - if( fgTileMgrInit() ) { + if( global_tile_mgr.init() ) { // Load the local scenery data - fgTileMgrUpdate(); + global_tile_mgr.update(); } else { FG_LOG( FG_GENERAL, FG_ALERT, "Error in Tile Manager initialization!" ); exit(-1); @@ -438,9 +438,9 @@ void fgReInitSubsystems( void ) t->togglePauseMode(); fgInitPosition(); - if( fgTileMgrInit() ) { + if( global_tile_mgr.init() ) { // Load the local scenery data - fgTileMgrUpdate(); + global_tile_mgr.update(); } else { FG_LOG( FG_GENERAL, FG_ALERT, "Error in Tile Manager initialization!" ); exit(-1); diff --git a/Simulator/Scenery/tilecache.cxx b/Simulator/Scenery/tilecache.cxx index b18d2fab7..6e4acb4d2 100644 --- a/Simulator/Scenery/tilecache.cxx +++ b/Simulator/Scenery/tilecache.cxx @@ -78,7 +78,7 @@ FGTileCache::init( void ) << sizeof( e ) ); if ( target_cache_size > (int)tile_cache.size() ) { // FGTileEntry e; - e.used = false; + e.mark_unused(); int expansion_amt = target_cache_size - (int)tile_cache.size(); for ( i = 0; i < expansion_amt; ++i ) { tile_cache.push_back( e ); @@ -90,10 +90,10 @@ FGTileCache::init( void ) << tile_cache.size() ); for ( i = 0; i < (int)tile_cache.size(); i++ ) { - if ( tile_cache[i].used ) { + if ( !tile_cache[i].is_unused() ) { entry_free(i); } - tile_cache[i].used = false; + tile_cache[i].mark_unused(); tile_cache[i].tile_bucket.make_bad(); } FG_LOG( FG_TERRAIN, FG_DEBUG, " done with init()" ); @@ -120,7 +120,7 @@ FGTileCache::exists( const FGBucket& p ) // Fill in a tile cache entry with real data for the specified bucket void -FGTileCache::fill_in( int index, FGBucket& p ) +FGTileCache::fill_in( int index, const FGBucket& p ) { // Load the appropriate data file and build tile fragment list FGPath tile_path( current_options.get_fg_root() ); @@ -128,7 +128,7 @@ FGTileCache::fill_in( int index, FGBucket& p ) tile_path.append( p.gen_base_path() ); tile_path.append( p.gen_index_str() ); - tile_cache[index].used = true; + tile_cache[index].mark_loaded(); tile_cache[index].tile_bucket = p; fgObjLoad( tile_path.str(), &tile_cache[index] ); // tile_cache[ index ].ObjLoad( tile_path, p ); @@ -169,7 +169,7 @@ FGTileCache::next_avail( void ) max_index = 0; for ( i = 0; i < (int)tile_cache.size(); i++ ) { - if ( ! tile_cache[i].used ) { + if ( tile_cache[i].is_unused() ) { return(i); } else { // calculate approximate distance from view point diff --git a/Simulator/Scenery/tilecache.hxx b/Simulator/Scenery/tilecache.hxx index 7713584c8..c3692c260 100644 --- a/Simulator/Scenery/tilecache.hxx +++ b/Simulator/Scenery/tilecache.hxx @@ -75,7 +75,7 @@ public: void entry_free( int index ); // Fill in a tile cache entry with real data for the specified bucket - void fill_in( int index, FGBucket& p ); + void fill_in( int index, const FGBucket& p ); // Return a pointer to the specified tile cache entry FGTileEntry *get_tile( int index ) { diff --git a/Simulator/Scenery/tileentry.cxx b/Simulator/Scenery/tileentry.cxx index 0eee8cb48..d90f465f2 100644 --- a/Simulator/Scenery/tileentry.cxx +++ b/Simulator/Scenery/tileentry.cxx @@ -42,7 +42,7 @@ FG_USING_STD(mem_fun_ref); // Constructor FGTileEntry::FGTileEntry ( void ) : ncount(0), - used(false) + state(Unused) { nodes.clear(); } @@ -65,7 +65,7 @@ FGTileEntry::release_fragments() for_each( begin(), end(), mem_fun_ref( &fgFRAGMENT::deleteDisplayList )); fragment_list.erase( begin(), end() ); - used = false; + mark_unused(); } diff --git a/Simulator/Scenery/tileentry.hxx b/Simulator/Scenery/tileentry.hxx index 955298941..37000bf71 100644 --- a/Simulator/Scenery/tileentry.hxx +++ b/Simulator/Scenery/tileentry.hxx @@ -68,6 +68,15 @@ typedef point_list::const_iterator const_point_list_iterator; // Scenery tile class class FGTileEntry { +private: + + // Tile state + enum tile_state { + Unused = 0, + Scheduled = 1, + Loaded = 2 + }; + public: typedef vector < fgFRAGMENT > container; @@ -91,7 +100,7 @@ public: FGBucket tile_bucket; // the tile cache will mark here if the tile is being used - bool used; + tile_state state; container fragment_list; @@ -156,6 +165,12 @@ public: // so m[15] is unchanged } + inline bool is_unused() const { return state == Unused; } + inline bool is_loaded() const { return state == Loaded; } + + inline void mark_unused() { state = Unused; } + inline void mark_scheduled() { state = Scheduled; } + inline void mark_loaded() { state = Loaded; } }; diff --git a/Simulator/Scenery/tilemgr.cxx b/Simulator/Scenery/tilemgr.cxx index 94be439f9..86ebc4470 100644 --- a/Simulator/Scenery/tilemgr.cxx +++ b/Simulator/Scenery/tilemgr.cxx @@ -61,34 +61,24 @@ #endif -#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 -# define FG_MEM_COPY(to,from,n) bcopy(from, to, n) -#endif +// the tile manager +FGTileMgr global_tile_mgr; -// Tile loading state -enum fgTileLoadState { - START = 0, - INITED = 1, - RUNNING = 2 -}; +// Constructor +FGTileMgr::FGTileMgr ( void ): + state( Start ) +{ +} -// closest (potentially viewable) tiles, centered on current tile. -// This is an array of pointers to cache indexes. -int tiles[FG_LOCAL_X_Y]; - -static fgTileLoadState state = START; +// Destructor +FGTileMgr::~FGTileMgr ( void ) { +} // Initialize the Tile Manager subsystem -int fgTileMgrInit( void ) { +int FGTileMgr::init( void ) { FG_LOG( FG_TERRAIN, FG_INFO, "Initializing Tile Manager subsystem." ); // load default material library @@ -96,28 +86,39 @@ int fgTileMgrInit( void ) { material_mgr.load_lib(); } - state = INITED; + state = Inited; return 1; } -// load a tile -void fgTileMgrLoadTile( FGBucket& p, int *index) { - FGTileCache *c; - - c = &global_tile_cache; - - FG_LOG( FG_TERRAIN, FG_DEBUG, "Updating for bucket " << p ); - - // if not in cache, load tile into the next available slot - *index = c->exists(p); +// schedule a tile for loading +void FGTileMgr::sched_tile( const FGBucket& b, int *index ) { + // see if tile already exists in the cache + *index = global_tile_cache.exists( b ); if ( *index < 0 ) { - *index = c->next_avail(); - c->fill_in(*index, p); - } + // find the next availabel cache entry and mark it as scheduled + *index = global_tile_cache.next_avail(); + FGTileEntry *t = global_tile_cache.get_tile( *index ); + t->mark_scheduled(); - FG_LOG( FG_TERRAIN, FG_DEBUG, "Selected cache index: " << *index ); + // register a load request + FGLoadRec request; + request.b = b; + request.index = *index; + load_queue.push_back( request ); + } +} + + +// load a tile +void FGTileMgr::load_tile( const FGBucket& b, int cache_index) { + + 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 ); } @@ -139,7 +140,7 @@ static double point_line_dist_squared( const Point3D& tc, const Point3D& vp, // explicitely. lat & lon are in radians. abs_view_pos in meters. // Returns result in meters. double -fgTileMgrCurElevNEW( const FGBucket& p ) { +FGTileMgr::current_elev_new( const FGBucket& p ) { FGTileEntry *t; fgFRAGMENT *frag_ptr; Point3D abs_view_pos = current_view.get_abs_view_pos(); @@ -237,7 +238,7 @@ fgTileMgrCurElevNEW( const FGBucket& p ) { // explicitely. lat & lon are in radians. abs_view_pos in meters. // Returns result in meters. double -fgTileMgrCurElev( double lon, double lat, const Point3D& abs_view_pos ) { +FGTileMgr::current_elev( double lon, double lat, const Point3D& abs_view_pos ) { FGTileCache *c; FGTileEntry *t; fgFRAGMENT *frag_ptr; @@ -341,7 +342,7 @@ fgTileMgrCurElev( double lon, double lat, const Point3D& abs_view_pos ) { // given the current lon/lat, fill in the array of local chunks. If // the chunk isn't already in the cache, then read it from disk. -int fgTileMgrUpdate( void ) { +int FGTileMgr::update( void ) { FGTileCache *c; FGInterface *f; FGBucket p2; @@ -361,11 +362,11 @@ int fgTileMgrUpdate( void ) { dw = tile_diameter / 2; dh = tile_diameter / 2; - if ( (p1 == p_last) && (state == RUNNING) ) { + if ( (p1 == p_last) && (state == Running) ) { // same bucket as last time FG_LOG( FG_TERRAIN, FG_DEBUG, "Same bucket as last time" ); - } else if ( (state == START) || (state == INITED) ) { - state = RUNNING; + } else if ( (state == Start) || (state == Inited) ) { + state = Running; // First time through or we have teleporte, initialize the // system and load all relavant tiles @@ -380,16 +381,72 @@ int fgTileMgrUpdate( void ) { c->init(); p_last.make_bad(); - // build the local area list and update cache - for ( j = 0; j < tile_diameter; j++ ) { + // build the local area list and schedule tiles for loading + + // start with the center tile and work out in concentric + // "rings" + + p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG, + f->get_Latitude() * RAD_TO_DEG, + 0, 0 ); + sched_tile( p2, &tiles[(dh*tile_diameter) + dw]); + + for ( i = 3; i <= tile_diameter; i = i + 2 ) { + int span = i / 2; + + // bottom row + for ( j = -span; j <= span; ++j ) { + p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG, + f->get_Latitude() * RAD_TO_DEG, + j, -span ); + sched_tile( p2, &tiles[((dh-span)*tile_diameter) + dw+j]); + } + + // top row + for ( j = -span; j <= span; ++j ) { + p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG, + f->get_Latitude() * RAD_TO_DEG, + j, span ); + sched_tile( p2, &tiles[((dh+span)*tile_diameter) + dw+j]); + } + + // middle rows + for ( j = -span + 1; j <= span - 1; ++j ) { + p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG, + f->get_Latitude() * RAD_TO_DEG, + -span, j ); + sched_tile( p2, &tiles[((dh+j)*tile_diameter) + dw-span]); + p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG, + f->get_Latitude() * RAD_TO_DEG, + span, j ); + sched_tile( p2, &tiles[((dh+j)*tile_diameter) + dw+span]); + } + + } + + /* for ( j = 0; j < tile_diameter; j++ ) { for ( i = 0; i < tile_diameter; i++ ) { // fgBucketOffset(&p1, &p2, i - dw, j - dh); p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG, f->get_Latitude() * RAD_TO_DEG, i - dw, j -dh ); - fgTileMgrLoadTile( p2, &tiles[(j*tile_diameter) + i]); + sched_tile( p2, &tiles[(j*tile_diameter) + i]); + } + } */ + + // Now force a load of the center tile and inner ring so we + // have something to see in our first frame. + for ( i = 0; i < 9; ++i ) { + if ( load_queue.size() ) { + FG_LOG( FG_TERRAIN, FG_INFO, + "Load queue not empty, loading a tile" ); + + FGLoadRec pending = load_queue.front(); + load_queue.pop_front(); + load_tile( pending.b, pending.index ); } } + } else { // We've moved to a new bucket, we need to scroll our // structures, and load in the new tiles @@ -413,7 +470,7 @@ int fgTileMgrUpdate( void ) { // load in new column // fgBucketOffset(&p_last, &p2, dw + 1, j - dh); p2 = fgBucketOffset( last_lon, last_lat, dw + 1, j - dh ); - fgTileMgrLoadTile( p2, &tiles[(j*tile_diameter) + + sched_tile( p2, &tiles[(j*tile_diameter) + tile_diameter - 1]); } } else if ( (p1.get_lon() < p_last.get_lon()) || @@ -429,7 +486,7 @@ int fgTileMgrUpdate( void ) { // load in new column // fgBucketOffset(&p_last, &p2, -dw - 1, j - dh); p2 = fgBucketOffset( last_lon, last_lat, -dw - 1, j - dh ); - fgTileMgrLoadTile( p2, &tiles[(j*tile_diameter) + 0]); + sched_tile( p2, &tiles[(j*tile_diameter) + 0]); } } @@ -446,7 +503,7 @@ int fgTileMgrUpdate( void ) { // load in new column // fgBucketOffset(&p_last, &p2, i - dw, dh + 1); p2 = fgBucketOffset( last_lon, last_lat, i - dw, dh + 1); - fgTileMgrLoadTile( p2, &tiles[((tile_diameter-1) * + sched_tile( p2, &tiles[((tile_diameter-1) * tile_diameter) + i]); } } else if ( (p1.get_lat() < p_last.get_lat()) || @@ -462,18 +519,25 @@ int fgTileMgrUpdate( void ) { // load in new column // fgBucketOffset(&p_last, &p2, i - dw, -dh - 1); p2 = fgBucketOffset( last_lon, last_lat, i - dw, -dh - 1); - fgTileMgrLoadTile( p2, &tiles[0 + i]); + sched_tile( p2, &tiles[0 + i]); } } } + if ( load_queue.size() ) { + FG_LOG( FG_TERRAIN, FG_INFO, "Load queue not empty, loading a tile" ); + + FGLoadRec pending = load_queue.front(); + load_queue.pop_front(); + load_tile( pending.b, pending.index ); + } + // find our current elevation (feed in the current bucket to save work) Point3D geod_pos = Point3D( f->get_Longitude(), f->get_Latitude(), 0.0); Point3D tmp_abs_view_pos = fgGeodToCart(geod_pos); scenery.cur_elev = - fgTileMgrCurElev( f->get_Longitude(), f->get_Latitude(), - tmp_abs_view_pos ); + current_elev( f->get_Longitude(), f->get_Latitude(), tmp_abs_view_pos ); p_last = p1; last_lon = f->get_Longitude() * RAD_TO_DEG; @@ -671,7 +735,7 @@ update_tile_geometry( FGTileEntry *t, GLdouble *MODEL_VIEW) // Render the local tiles -void fgTileMgrRender( void ) { +void FGTileMgr::render( void ) { FGInterface *f; FGTileCache *c; FGTileEntry *t; @@ -706,58 +770,68 @@ void fgTileMgrRender( void ) { // fgPrintf( FG_TERRAIN, FG_DEBUG, "Index = %d\n", index); t = c->get_tile(index); - // calculate tile offset - t->SetOffset( scenery.center ); + if ( t->is_loaded() ) { - // Course (tile based) culling - if ( viewable(t->offset, t->bounding_radius) ) { - // at least a portion of this tile could be viewable + // calculate tile offset + t->SetOffset( scenery.center ); + + // Course (tile based) culling + if ( viewable(t->offset, t->bounding_radius) ) { + // at least a portion of this tile could be viewable - // Calculate the model_view transformation matrix for this tile - // This is equivalent to doing a glTranslatef(x, y, z); - t->update_view_matrix( v->get_MODEL_VIEW() ); + // Calculate the model_view transformation matrix for this tile + // This is equivalent to doing a glTranslatef(x, y, z); + t->update_view_matrix( v->get_MODEL_VIEW() ); - // xglPushMatrix(); - // xglTranslatef(t->offset.x, t->offset.y, t->offset.z); + // xglPushMatrix(); + // xglTranslatef(t->offset.x, t->offset.y, t->offset.z); - // traverse fragment list for tile - FGTileEntry::FragmentIterator current = t->begin(); - FGTileEntry::FragmentIterator last = t->end(); + // traverse fragment list for tile + FGTileEntry::FragmentIterator current = t->begin(); + FGTileEntry::FragmentIterator last = t->end(); - for ( ; current != last; ++current ) { - frag_ptr = &(*current); + for ( ; current != last; ++current ) { + frag_ptr = &(*current); - if ( frag_ptr->display_list >= 0 ) { - // Fine (fragment based) culling - frag_offset = frag_ptr->center - scenery.center; + if ( frag_ptr->display_list >= 0 ) { + // Fine (fragment based) culling + frag_offset = frag_ptr->center - scenery.center; - if ( viewable(frag_offset, frag_ptr->bounding_radius*2) ) { - // add to transient per-material property fragment list - // frag_ptr->tile_offset.x = t->offset.x; - // frag_ptr->tile_offset.y = t->offset.y; - // frag_ptr->tile_offset.z = t->offset.z; + if ( viewable(frag_offset, + frag_ptr->bounding_radius*2) ) + { + // add to transient per-material property + // fragment list - mtl_ptr = frag_ptr->material_ptr; - // printf(" lookup = %s\n", mtl_ptr->texture_name); - if ( ! mtl_ptr->append_sort_list( frag_ptr ) ) { - FG_LOG( FG_TERRAIN, FG_ALERT, - "Overran material sorting array" ); + // frag_ptr->tile_offset.x = t->offset.x; + // frag_ptr->tile_offset.y = t->offset.y; + // frag_ptr->tile_offset.z = t->offset.z; + + mtl_ptr = frag_ptr->material_ptr; + // printf(" lookup = %s\n", mtl_ptr->texture_name); + if ( ! mtl_ptr->append_sort_list( frag_ptr ) ) { + FG_LOG( FG_TERRAIN, FG_ALERT, + "Overran material sorting array" ); + } + + // xglCallList(frag_ptr->display_list); + drawn++; + } else { + // printf("Culled a fragment %.2f %.2f %.2f %.2f\n", + // frag_ptr->center.x, frag_ptr->center.y, + // frag_ptr->center.z, + // frag_ptr->bounding_radius); + culled++; } - - // xglCallList(frag_ptr->display_list); - drawn++; - } else { - // printf("Culled a fragment %.2f %.2f %.2f %.2f\n", - // frag_ptr->center.x, frag_ptr->center.y, - // frag_ptr->center.z, frag_ptr->bounding_radius); - culled++; } } - } - // xglPopMatrix(); + // xglPopMatrix(); + } else { + culled += t->fragment_list.size(); + } } else { - culled += t->fragment_list.size(); + FG_LOG( FG_TERRAIN, FG_DEBUG, "Skipping a not yet loaded tile" ); } } diff --git a/Simulator/Scenery/tilemgr.hxx b/Simulator/Scenery/tilemgr.hxx index 66d7be63c..7c1740e8b 100644 --- a/Simulator/Scenery/tilemgr.hxx +++ b/Simulator/Scenery/tilemgr.hxx @@ -29,29 +29,92 @@ # error This library requires C++ #endif +#include + +#include #include -// Initialize the Tile Manager subsystem -int fgTileMgrInit( void ); +FG_USING_STD(list); -// given the current lon/lat, fill in the array of local chunks. If -// the chunk isn't already in the cache, then read it from disk. -int fgTileMgrUpdate( void ); +#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 +# define FG_MEM_COPY(to,from,n) bcopy(from, to, n) +#endif -// Determine scenery altitude. Normally this just happens when we -// render the scene, but we'd also like to be able to do this -// explicitely. lat & lon are in radians. abs_view_pos in meters. -// Returns result in meters. -double fgTileMgrCurElevNEW( const FGBucket& p ); -double fgTileMgrCurElev( double lon, double lat, const Point3D& abs_view_pos ); +class FGLoadRec { + +public: + + FGBucket b; + int index; +}; -// Render the local tiles --- hack, hack, hack -void fgTileMgrRender( void ); +class FGTileMgr { + +private: + + // closest (potentially viewable) tiles, centered on current tile. + // This is an array of pointers to cache indexes. + int tiles[FG_LOCAL_X_Y]; + + // Tile loading state + enum load_state { + Start = 0, + Inited = 1, + Running = 2 + }; + + load_state state; + + // pending tile load queue + list < FGLoadRec > load_queue; + + // schedule a tile for loading + void sched_tile( const FGBucket& b, int *index ); + + // load a tile + void load_tile( const FGBucket& b, int cache_index ); + +public: + + // Constructor + FGTileMgr ( void ); + + // Destructor + ~FGTileMgr ( void ); + + // Initialize the Tile Manager subsystem + int init( void ); + + // given the current lon/lat, fill in the array of local chunks. + // If the chunk isn't already in the cache, then read it from + // disk. + int update( void ); + + // Determine scenery altitude. Normally this just happens when we + // render the scene, but we'd also like to be able to do this + // explicitely. lat & lon are in radians. abs_view_pos in + // meters. Returns result in meters. + double current_elev_new( const FGBucket& p ); + double current_elev( double lon, double lat, const Point3D& abs_view_pos ); + + // Render the local tiles --- hack, hack, hack + void render( void ); +}; + + +// the tile manager +extern FGTileMgr global_tile_mgr; #endif // _TILEMGR_HXX