1
0
Fork 0

Created an FGTileMgr class to encapsulate the high level tile management

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.
This commit is contained in:
curt 1999-06-13 05:58:02 +00:00
parent 392e8c09c8
commit 2c6db0ec35
9 changed files with 280 additions and 122 deletions

View 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();

View 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();

View 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);

View 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

View 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 ) {

View 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();
}

View 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; }
};

View file

@ -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" );
}
}

View file

@ -29,29 +29,92 @@
# 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);
// 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