]> git.mxchange.org Git - flightgear.git/commitdiff
Created an FGTileMgr class to encapsulate the high level tile management
authorcurt <curt>
Sun, 13 Jun 1999 05:58:02 +0000 (05:58 +0000)
committercurt <curt>
Sun, 13 Jun 1999 05:58:02 +0000 (05:58 +0000)
  tasks.
Created a tile load queue.  When we init, or cross a tile boundary, we
  stuff the new tiles to be loaded on a load queue, and then only load
  one tile per frame.  This will need further refinement, but it is better
  than what we had.

Simulator/Main/GLUTkey.cxx
Simulator/Main/GLUTmain.cxx
Simulator/Main/fg_init.cxx
Simulator/Scenery/tilecache.cxx
Simulator/Scenery/tilecache.hxx
Simulator/Scenery/tileentry.cxx
Simulator/Scenery/tileentry.hxx
Simulator/Scenery/tilemgr.cxx
Simulator/Scenery/tilemgr.hxx

index a3c35bd79b5ab19d38c000f129691c84996d87e9..58b72be812caaf6f7c3b70433e6413cdd34774b3 100644 (file)
@@ -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();
index fbfbfb5bd9a1cb86bd6dfa96689eaf3cace1e9c8..9822d4db432cc0c3948f24fe575d7181ae707c2f 100644 (file)
@@ -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();
index f569e5032994f8b5389533ddc55481800fb7c1fd..149eebb780cd897189562efec1866ce66156367b 100644 (file)
@@ -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);
index b18d2fab7511d72796ea2cdbcf29bd204ca71884..6e4acb4d20a7ded2bc29ee67ad3db1a276c3543f 100644 (file)
@@ -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
index 7713584c8c7e7a074be0be952df09b996352e40e..c3692c2602f7e51337c324d831f3f97e2763e9a7 100644 (file)
@@ -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 ) {
index 0eee8cb48244d861312953205911ad7bdfe2047f..d90f465f263cb0e6a4926d707155d87a9d81c69d 100644 (file)
@@ -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();
 }
 
 
index 9552989419d613c1b6daf8e4233d9a9c0ca27b0e..37000bf7142f55111302e87a74ff8cb0c4333f6e 100644 (file)
@@ -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; }
 };
 
 
index 94be439f9842f930e5349ca07d12cc95c272a8e9..86ebc4470f08f271fcaec32bd97c4cec7df3b40d 100644 (file)
 #endif
 
 
-#define FG_LOCAL_X_Y         81  // max(o->tile_diameter) ** 2
+// the tile manager
+FGTileMgr global_tile_mgr;
 
-#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
-
-
-// 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;
+// 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 ) {
+       // 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();
+
+       // register a load request
+       FGLoadRec request;
+       request.b = b;
+       request.index = *index;
+       load_queue.push_back( request );
+    }
+}
 
-    c = &global_tile_cache;
 
-    FG_LOG( FG_TERRAIN, FG_DEBUG, "Updating for bucket " << p );
+// load a tile
+void FGTileMgr::load_tile( const FGBucket& b, int cache_index) {
+
+    FG_LOG( FG_TERRAIN, FG_DEBUG, "Loading tile " << b );
     
-    // if not in cache, load tile into the next available slot
-    *index = c->exists(p);
-    if ( *index < 0 ) {
-       *index = c->next_avail();
-       c->fill_in(*index, p);
-    }
+    global_tile_cache.fill_in(cache_index, b);
 
-    FG_LOG( FG_TERRAIN, FG_DEBUG, "Selected cache index: " << *index );
+    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() ) {
+
+           // 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
+           // 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 ( 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;
-
-                       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" );
+                   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;
+
+                           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" );
        }
     }
 
index 66d7be63c2699597f0292614a755840a17c7755d..7c1740e8be8ba1b9debee55d394e9cd5eccdc135 100644 (file)
 # error This library requires C++
 #endif                                   
 
+#include <Include/compiler.h>
+
+#include <list>
 
 #include <Bucket/newbucket.hxx>
 
 
-// Initialize the Tile Manager subsystem
-int fgTileMgrInit( void );
+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
+#  define FG_MEM_COPY(to,from,n)        bcopy(from, to, n)
+#endif
+
+
+class FGLoadRec {
+
+public:
+
+    FGBucket b;
+    int index;
+};
+
+
+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 fgTileMgrUpdate( 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 );
 
-// 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 );
+    // Render the local tiles --- hack, hack, hack
+    void render( void );
+};
 
 
-// Render the local tiles --- hack, hack, hack
-void fgTileMgrRender( void );
+// the tile manager
+extern FGTileMgr global_tile_mgr;
 
 
 #endif // _TILEMGR_HXX