]> git.mxchange.org Git - flightgear.git/commitdiff
Rewrote the tile scheme to use a "map" structure rather than "vector"
authorcurt <curt>
Sun, 3 Dec 2000 20:15:46 +0000 (20:15 +0000)
committercurt <curt>
Sun, 3 Dec 2000 20:15:46 +0000 (20:15 +0000)
structure.  The new approach is simpler, more flexible, and more dynamics.
We can now dynamically size the tile cache up and down.  Also, the range
of tiles to load is now dependent on visibility and is calculated to always
bring in enough tiles.

src/Scenery/Makefile.am
src/Scenery/newcache.cxx [new file with mode: 0644]
src/Scenery/newcache.hxx [new file with mode: 0644]
src/Scenery/tilecache.hxx
src/Scenery/tileentry.cxx
src/Scenery/tileentry.hxx
src/Scenery/tilemgr.cxx
src/Scenery/tilemgr.hxx

index 2476df6143cfd22d85d94b69b849f5a3d344481f..370762135301cbb60e464a5b7c503d106f5fcaed 100644 (file)
@@ -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 (file)
index 0000000..e9b848f
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#  include <windows.h>
+#endif
+
+#include <GL/glut.h>
+#include <simgear/xgl/xgl.h>
+
+#include <plib/ssg.h>          // plib include
+
+#include <simgear/bucket/newbucket.hxx>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/fgstream.hxx>
+#include <simgear/misc/fgpath.hxx>
+
+#include <Main/globals.hxx>
+#include <Objects/obj.hxx>
+#include <Scenery/scenery.hxx>  // 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 (file)
index 0000000..7d3c7b3
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#  include <windows.h>
+#endif
+
+#include <GL/glut.h>
+
+#include <map>
+
+#include <simgear/bucket/newbucket.hxx>
+#include <simgear/math/point3d.hxx>
+
+#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 
index d1905720ac4263247bc93c4d4b875c6454f8deda..4658a195fcc350f5a1aeb577c81dfc6881e6e085 100644 (file)
@@ -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 );
 };
 
 
index 573a5234658aeb6227b6b62fb26c7da1e3c148cc..973b918d07f3ee33006a713af8bd8bb0fc8b7a3b 100644 (file)
@@ -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);
 }
index 612b059e2760f2c43fe65f549bd0d3c8a761d7bb..95048b07988362805ba48baecaf88c7dbad365cc 100644 (file)
@@ -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 Point3D get_offset() const { return offset; }
+
+    // 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 );
     }
-    inline bool is_loaded() const { return state == Loaded; }
-    inline bool is_cached() const { return state == Cached; }
-
-    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; }
-
 
     // 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 
index 0266ca5b65508c10d5ebb9f0f55774a0f276d150..b9af69eb64ab7eea9dc198f0117ca0283dc9c021 100644 (file)
@@ -39,7 +39,6 @@
 #include <simgear/math/sg_geodesy.hxx>
 #include <simgear/math/vector.hxx>
 
-// #include <Aircraft/aircraft.hxx>
 #include <Main/globals.hxx>
 #include <Objects/obj.hxx>
 
@@ -49,8 +48,8 @@
 #  include <Weather/weather.hxx>
 #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;
-
-       // 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 needed buckets for loading
+void FGTileMgr::schedule_needed() {
+    double vis;
 
-// schedule a tile row(column) for loading
-void FGTileMgr::scroll( void )
-{
-    FG_LOG( FG_TERRAIN, FG_DEBUG, "schedule_row: Scrolling" );
+#ifndef FG_OLD_WEATHER
+    if ( WeatherDatabase != NULL ) {
+       vis = WeatherDatabase->getWeatherVisibility();
+    } else {
+       vis = 16000;
+    }
+#else
+    vis = current_weather.get_visibility();
+#endif
+    cout << "visibility = " << vis << endl;
 
-    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 ) );
+    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 );
+           }
        }
-       break;
-    default:
-       FG_LOG( FG_TERRAIN, FG_WARN, "UNKNOWN SCROLL DIRECTION in schedule_row" );
-       ;
     }
-    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
 
-            ranges[1] = vis + t->bounding_radius;
-            t->range_ptr->setRanges( ranges, 2 );
+    FGTileEntry *e;
+    Point3D p = scenery.center;
+    global_tile_cache.reset_traversal();
 
-            // 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();
+   }
 }
index dacc376344e071cebdf4e7fc7b88e86902f7c93d..b90c2df846ea944c8807e3c30c57b9f577f7285a 100644 (file)
 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(); }
 };