}
FGAIBase::~FGAIBase() {
+ // Unregister that one at the scenery manager
+ globals->get_scenery()->unregister_placement_transform(aip.getTransform());
+
globals->get_scenery()->get_scene_graph()->removeKid(aip.getSceneGraph());
// unbind();
SGPropertyNode *root = globals->get_props()->getNode("ai/models", true);
aip.setVisible(true);
invisible = false;
globals->get_scenery()->get_scene_graph()->addKid(aip.getSceneGraph());
+ // Register that one at the scenery manager
+ globals->get_scenery()->register_placement_transform(aip.getTransform());
} else {
if (model_path != "") {
SG_LOG(SG_INPUT, SG_WARN, "AIBase: Could not load model.");
_model->deRef(); // Ought to check valid?
//cout << "Removing model from scene graph..." << endl;
globals->get_scenery()->get_scene_graph()->removeKid(_aip.getSceneGraph());
+ // Unregister that one at the scenery manager
+ globals->get_scenery()->unregister_placement_transform(_aip.getTransform());
+
//cout << "Done!" << endl;
}
_aip.init(_model);
_aip.setVisible(false);
globals->get_scenery()->get_scene_graph()->addKid(_aip.getSceneGraph());
+ // Register that one at the scenery manager
+ globals->get_scenery()->register_placement_transform(_aip.getTransform());
+
}
void FGAIEntity::Update(double dt) {
FGGroundCache::prepare_ground_cache(double ref_time, const double pt[3],
double rad)
{
+ Point3D old_cntr = globals->get_scenery()->get_center();
+ Point3D cntr(pt[0], pt[1], pt[2]);
+ globals->get_scenery()->set_center( cntr );
+
// Empty cache.
cache_root.removeAllKids();
ground_radius = 0.0;
// We need the offset to the scenery scenery center.
sgdVec3 doffset;
- Point3D psc = globals->get_tile_mgr()->get_current_center();
+ Point3D psc = globals->get_scenery()->get_center();
sgdSetVec3(doffset, psc[0], psc[1], psc[2]);
sgdSubVec3(doffset, doffset, pt);
sgMakeTransMat4(xform, offset);
- // Walk the terrain branch for now.
+ // Walk the scene graph and extract solid ground triangles and carrier data.
ssgBranch *terrain = globals->get_scenery()->get_scene_graph();
cache_fill(terrain, xform, &acSphere, down, &wireSphere);
SG_LOG(SG_FLIGHT, SG_WARN, "prepare_ground_cache(): trying to build cache "
"without any scenery below the aircraft" );
+ globals->get_scenery()->set_center( old_cntr );
+
return found_ground;
}
double visibility_meters = fgGetDouble("/environment/visibility-m");
FGViewer *current_view = globals->get_current_view();
+ // Let the scenery center follow the current view position with
+ // 30m increments.
+ //
+ // Having the scenery center near the view position will eliminate
+ // jitter of objects which are placed very near the view position
+ // and haveing it's center near that view position.
+ // So the 3d insruments of the aircraft will not jitter with this.
+ //
+ // Following the view position exactly would introduce jitter of
+ // the scenery tiles (they would be from their center up to 10000m
+ // to the view and this will introduce roundoff too). By stepping
+ // at 30m incements the roundoff error of the scenery tiles is
+ // still present, but we will make exactly the same roundoff error
+ // at each frame until the center is switched to a new
+ // position. This roundoff is still visible but you will most
+ // propably not notice.
+ double *vp = globals->get_current_view()->get_absolute_view_pos();
+ Point3D cntr(vp[0], vp[1], vp[2]);
+ if (30.0*30.0 < cntr.distance3Dsquared(globals->get_scenery()->get_center())) {
+ globals->get_scenery()->set_next_center( cntr );
+ }
+
// get the location data for the primary FDM (now hardcoded to ac model)...
SGLocation *acmodel_loc = NULL;
acmodel_loc = (SGLocation *)globals->
acmodel_loc->
get_absolute_view_pos(globals->
get_scenery()->get_center()) );
+ globals->get_scenery()->set_center( cntr );
+
// save results of update in SGLocation for fdm...
if ( globals->get_scenery()->get_cur_elev() > -9990 ) {
acmodel_loc->
SGLocation *view_location = globals->get_current_view()->getSGLocation();
globals->get_tile_mgr()->update( view_location, visibility_meters,
current_view->get_absolute_view_pos() );
+ globals->get_scenery()->set_center( cntr );
// save results of update in SGLocation for fdm...
if ( globals->get_scenery()->get_cur_elev() > -9990 ) {
current_view->getSGLocation()->
FGViewer *current__view = globals->get_current_view();
// calculate our current position in cartesian space
- globals->get_scenery()->set_center( globals->get_scenery()->get_next_center() );
+ Point3D cntr = globals->get_scenery()->get_next_center();
+ globals->get_scenery()->set_center(cntr);
+ current__view->set_scenery_center(cntr);
if ( refresh_camera_settings ) {
// update view port
void
FGViewer::updateFromModelLocation (SGLocation * location)
{
- sgCopyMat4(LOCAL, location->getCachedTransformMatrix());
+ Point3D center = globals->get_scenery()->get_next_center();
+ sgCopyMat4(LOCAL, location->getTransformMatrix(center));
}
void
FGViewer::updateAtModelLocation (SGLocation * location)
{
- sgCopyMat4(ATLOCAL,
- location->getCachedTransformMatrix());
+ Point3D center = globals->get_scenery()->get_next_center();
+ sgCopyMat4(ATLOCAL, location->getTransformMatrix(center));
}
void
dampEyeData(roll_deg, pitch_deg, heading_deg);
location->setPosition( lon_deg, lat_deg, alt_ft );
location->setOrientation( roll_deg, pitch_deg, heading_deg );
- sgCopyMat4(LOCAL,
- location->getTransformMatrix(globals->get_scenery()->get_center()));
+ Point3D center = globals->get_scenery()->get_next_center();
+ sgCopyMat4(LOCAL, location->getTransformMatrix(center));
+}
+
+void
+FGViewer::set_scenery_center(const Point3D& center)
+{
+ _location->set_tile_center(center);
+ _location->getTransformMatrix(center);
+ if (_type == FG_LOOKAT) {
+ _target_location->set_tile_center(center);
+ _target_location->getTransformMatrix(center);
+ }
+ set_dirty();
}
// recalc() is done every time one of the setters is called (making the
_target_roll_deg, _target_pitch_deg, _target_heading_deg );
}
// calculate the "at" target object positon relative to eye or view's tile center...
+ Point3D center = globals->get_scenery()->get_next_center();
sgdVec3 dVec3;
- sgdSetVec3(dVec3, _location->get_tile_center()[0], _location->get_tile_center()[1], _location->get_tile_center()[2]);
+ sgdSetVec3(dVec3, center[0], center[1], center[2]);
sgdSubVec3(dVec3,
- _target_location->get_absolute_view_pos(globals->get_scenery()->get_center()),
+ _target_location->get_absolute_view_pos(center),
dVec3 );
sgSetVec3(at_pos, dVec3[0], dVec3[1], dVec3[2]);
FGViewer::copyLocationData()
{
// Get our friendly vectors from the eye location...
+ sgdCopyVec3(_absolute_view_pos,
+ _location->get_absolute_view_pos(globals->get_scenery()->get_next_center()));
sgCopyVec3(_zero_elev_view_pos, _location->get_zero_elev());
sgCopyVec3(_relative_view_pos, _location->get_view_pos());
- sgdCopyVec3(_absolute_view_pos,
- _location->get_absolute_view_pos(globals->get_scenery()->get_center()));
sgCopyMat4(UP, _location->getCachedUpMatrix());
sgCopyVec3(_world_up, _location->get_world_up());
// these are the vectors that the sun and moon code like to get...
_ground_level_nearplane_m = near_m;
}
+ void set_scenery_center(const Point3D& center);
+
//////////////////////////////////////////////////////////////////////
// Part 5: misc setters and getters
//////////////////////////////////////////////////////////////////////
#include <string.h> // strcmp
#include <plib/sg.h>
+#include <plib/ssg.h>
+
+#include <simgear/compiler.h>
+
+#include <Model/acmodel.hxx>
#include "viewmgr.hxx"
#include "fg_props.hxx"
set_view( newview );
// copy in view data
copyToCurrent ();
+
+ // Copy the fdm's position into the SGLocation which is shared with
+ // some views ...
+ globals->get_aircraft_model()->update(0);
+ // Do the update ...
+ update(0);
}
return views[i];
}
inline FGViewer *next_view() {
- ++current;
- if ( current >= (int)views.size() ) {
- current = 0;
- }
- copyToCurrent();
+ setView((current+1 < (int)views.size()) ? (current + 1) : 0);
return views[current];
}
inline FGViewer *prev_view() {
- --current;
- if ( current < 0 ) {
- current = views.size() - 1;
- }
+ setView((0 < current) ? (current - 1) : (views.size() - 1));
return views[current];
}
FGAircraftModel::~FGAircraftModel ()
{
+ // Unregister that one at the scenery manager
+ if (_aircraft)
+ globals->get_scenery()->unregister_placement_transform(_aircraft->getTransform());
+
delete _aircraft;
delete _scene;
// SSG will delete it
_scene->addKid(_aircraft->getSceneGraph());
_selector->addKid(_aircraft->getSceneGraph());
globals->get_scenery()->get_aircraft_branch()->addKid(_selector);
+
+ // Register that one at the scenery manager
+ globals->get_scenery()->register_placement_transform(_aircraft->getTransform());
}
void
#endif
#include <vector>
+#include <string>
SG_USING_STD(string);
SG_USING_STD(vector);
// Add this model to the global scene graph
globals->get_scenery()->get_scene_graph()->addKid(model->getSceneGraph());
+ // Register that one at the scenery manager
+ globals->get_scenery()->register_placement_transform(model->getTransform());
+
+
// Save this instance for updating
add_instance(instance);
}
FGModelMgr::Instance::~Instance ()
{
+ // Unregister that one at the scenery manager
+ globals->get_scenery()->unregister_placement_transform(model->getTransform());
+
delete model;
}
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
+#include <simgear/scene/model/placementtrans.hxx>
#include <Main/fg_props.hxx>
void FGScenery::unbind() {
fgUntie("/environment/ground-elevation-m");
}
+
+void FGScenery::set_center( Point3D p ) {
+ center = p;
+ sgdVec3 c;
+ sgdSetVec3(c, p.x(), p.y(), p.z());
+ placement_list_type::iterator it = _placement_list.begin();
+ while (it != _placement_list.end()) {
+ (*it)->setSceneryCenter(c);
+ ++it;
+ }
+}
+
+void FGScenery::register_placement_transform(ssgPlacementTransform *trans) {
+ trans->ref();
+ _placement_list.push_back(trans);
+ sgdVec3 c;
+ sgdSetVec3(c, center.x(), center.y(), center.z());
+ trans->setSceneryCenter(c);
+}
+
+void FGScenery::unregister_placement_transform(ssgPlacementTransform *trans) {
+ placement_list_type::iterator it = _placement_list.begin();
+ while (it != _placement_list.end()) {
+ if ((*it) == trans) {
+ (*it)->deRef();
+ it = _placement_list.erase(it);
+ } else
+ ++it;
+ }
+}
# error This library requires C++
#endif
+#include <list>
#include <plib/sg.h>
+#include <simgear/compiler.h>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/math/point3d.hxx>
+SG_USING_STD(list);
class ssgRoot;
class ssgBranch;
+class ssgPlacementTransform;
// Define a structure containing global scenery parameters
ssgBranch *models_branch;
ssgBranch *aircraft_branch;
+ // list of all placement transform, used to move the scenery center on the fly.
+ typedef list<ssgPlacementTransform*> placement_list_type;
+ placement_list_type _placement_list;
+
public:
FGScenery();
inline void set_cur_elev( double e ) { cur_elev = e; }
inline Point3D get_center() const { return center; }
- inline void set_center( Point3D p ) { center = p; }
+ void set_center( Point3D p );
inline Point3D get_next_center() const { return next_center; }
inline void set_next_center( Point3D p ) { next_center = p; }
inline void set_aircraft_branch (ssgBranch *t) {
aircraft_branch = t;
}
+
+ void register_placement_transform(ssgPlacementTransform *trans);
+ void unregister_placement_transform(ssgPlacementTransform *trans);
};
#include <simgear/scene/tgdb/apt_signs.hxx>
#include <simgear/scene/tgdb/obj.hxx>
#include <simgear/scene/tgdb/vasi.hxx>
+#include <simgear/scene/model/placementtrans.hxx>
#include <Aircraft/aircraft.hxx>
#include <Include/general.hxx>
FGTileEntry::FGTileEntry ( const SGBucket& b )
: center( Point3D( 0.0 ) ),
tile_bucket( b ),
- terra_transform( new ssgTransform ),
- vasi_lights_transform( new ssgTransform ),
- rwy_lights_transform( new ssgTransform ),
- taxi_lights_transform( new ssgTransform ),
+ terra_transform( new ssgPlacementTransform ),
+ vasi_lights_transform( new ssgPlacementTransform ),
+ rwy_lights_transform( new ssgPlacementTransform ),
+ taxi_lights_transform( new ssgPlacementTransform ),
terra_range( new ssgRangeSelector ),
vasi_lights_selector( new ssgSelector ),
rwy_lights_selector( new ssgSelector ),
void FGTileEntry::prep_ssg_node( const Point3D& p, sgVec3 up, float vis) {
if ( !loaded ) return;
- SetOffset( p );
-
// visibility can change from frame to frame so we update the
// range selector cutoff's each time.
terra_range->setRange( 0, SG_ZERO );
gnd_lights_range->setRange( 1, vis * 1.5 + bounding_radius );
}
- sgVec3 sgTrans;
- sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
- terra_transform->setTransform( sgTrans );
+ sgdVec3 sgdTrans;
+ sgdSetVec3( sgdTrans, center.x(), center.y(), center.z() );
FGLight *l = (FGLight *)(globals->get_subsystem("lighting"));
if ( gnd_lights_transform ) {
agl = globals->get_current_view()->getAltitudeASL_ft()
* SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev();
- // sgTrans just happens to be the
- // vector from scenery center to the center of this tile which
- // is what we want to calculate the distance of
- sgVec3 to;
- sgCopyVec3( to, sgTrans );
- double dist = sgLengthVec3( to );
+ // Compute the distance of the scenery center from the view position.
+ double dist = center.distance3D(p);
if ( general.get_glDepthBits() > 16 ) {
sgScaleVec3( lift_vec, 10.0 + agl / 100.0 + dist / 10000 );
sgScaleVec3( lift_vec, 10.0 + agl / 20.0 + dist / 5000 );
}
- sgVec3 lt_trans;
- sgCopyVec3( lt_trans, sgTrans );
-
- sgAddVec3( lt_trans, lift_vec );
- gnd_lights_transform->setTransform( lt_trans );
+ sgdVec3 dlt_trans;
+ sgdCopyVec3( dlt_trans, sgdTrans );
+ sgdVec3 dlift_vec;
+ sgdSetVec3( dlift_vec, lift_vec );
+ sgdAddVec3( dlt_trans, dlift_vec );
+ gnd_lights_transform->setTransform( dlt_trans );
// select which set of lights based on sun angle
float sun_angle = l->get_sun_angle() * SGD_RADIANS_TO_DEGREES;
sgScaleVec3( lift_vec, 0.25 + agl / 150.0 );
}
- sgVec3 lt_trans;
- sgCopyVec3( lt_trans, sgTrans );
-
- sgAddVec3( lt_trans, lift_vec );
- vasi_lights_transform->setTransform( lt_trans );
+ sgdVec3 dlt_trans;
+ sgdCopyVec3( dlt_trans, sgdTrans );
+ sgdVec3 dlift_vec;
+ sgdSetVec3( dlift_vec, lift_vec );
+ sgdAddVec3( dlt_trans, dlift_vec );
+ vasi_lights_transform->setTransform( dlt_trans );
// generally, vasi lights are always on
vasi_lights_selector->select(0x01);
sgScaleVec3( lift_vec, 0.25 + agl / 150.0 );
}
- sgVec3 lt_trans;
- sgCopyVec3( lt_trans, sgTrans );
-
- sgAddVec3( lt_trans, lift_vec );
- rwy_lights_transform->setTransform( lt_trans );
+ sgdVec3 dlt_trans;
+ sgdCopyVec3( dlt_trans, sgdTrans );
+ sgdVec3 dlift_vec;
+ sgdSetVec3( dlift_vec, lift_vec );
+ sgdAddVec3( dlt_trans, dlift_vec );
+ rwy_lights_transform->setTransform( dlt_trans );
// turn runway lights on/off based on sun angle and visibility
float sun_angle = l->get_sun_angle() * SGD_RADIANS_TO_DEGREES;
sgScaleVec3( lift_vec, 0.25 + agl / 150.0 );
}
- sgVec3 lt_trans;
- sgCopyVec3( lt_trans, sgTrans );
-
- sgAddVec3( lt_trans, lift_vec );
- taxi_lights_transform->setTransform( lt_trans );
+ sgdVec3 dlt_trans;
+ sgdCopyVec3( dlt_trans, sgdTrans );
+ sgdVec3 dlift_vec;
+ sgdSetVec3( dlift_vec, lift_vec );
+ sgdAddVec3( dlt_trans, dlift_vec );
+ taxi_lights_transform->setTransform( dlt_trans );
// turn taxi lights on/off based on sun angle and visibility
float sun_angle = l->get_sun_angle() * SGD_RADIANS_TO_DEGREES;
terra_transform->addKid( terra_range );
// calculate initial tile offset
- SetOffset( globals->get_scenery()->get_center() );
- sgCoord sgcoord;
- sgSetCoord( &sgcoord,
- offset.x(), offset.y(), offset.z(),
- 0.0, 0.0, 0.0 );
- terra_transform->setTransform( &sgcoord );
+ sgdVec3 sgdTrans;
+ sgdSetVec3( sgdTrans, center.x(), center.y(), center.z() );
+ terra_transform->setTransform( sgdTrans );
+
+ sgdVec3 sgdCenter;
+ Point3D p = globals->get_scenery()->get_center();
+ sgdSetVec3( sgdCenter, p.x(), p.y(), p.z() );
+ terra_transform->setSceneryCenter( sgdCenter );
+ globals->get_scenery()->register_placement_transform(terra_transform);
+
// terrain->addKid( terra_transform );
// Add ground lights to scene graph if any exist
gnd_lights_range = NULL;
if ( light_pts->getNum() ) {
SG_LOG( SG_TERRAIN, SG_DEBUG, "generating lights" );
- gnd_lights_transform = new ssgTransform;
+ gnd_lights_transform = new ssgPlacementTransform;
gnd_lights_range = new ssgRangeSelector;
gnd_lights_brightness = new ssgSelector;
ssgLeaf *lights;
gnd_lights_range->addKid( gnd_lights_brightness );
gnd_lights_transform->addKid( gnd_lights_range );
- gnd_lights_transform->setTransform( &sgcoord );
+ gnd_lights_transform->setTransform( sgdTrans );
+ gnd_lights_transform->setSceneryCenter( sgdCenter );
+ globals->get_scenery()->register_placement_transform(gnd_lights_transform);
}
// Update vasi lights transform
if ( vasi_lights_transform->getNumKids() > 0 ) {
- vasi_lights_transform->setTransform( &sgcoord );
+ vasi_lights_transform->setTransform( sgdTrans );
+ vasi_lights_transform->setSceneryCenter( sgdCenter );
+ globals->get_scenery()->register_placement_transform(vasi_lights_transform);
}
// Update runway lights transform
if ( rwy_lights_transform->getNumKids() > 0 ) {
- rwy_lights_transform->setTransform( &sgcoord );
+ rwy_lights_transform->setTransform( sgdTrans );
+ rwy_lights_transform->setSceneryCenter( sgdCenter );
+ globals->get_scenery()->register_placement_transform(rwy_lights_transform);
}
// Update taxi lights transform
if ( taxi_lights_transform->getNumKids() > 0 ) {
- taxi_lights_transform->setTransform( &sgcoord );
+ taxi_lights_transform->setTransform( sgdTrans );
+ taxi_lights_transform->setSceneryCenter( sgdCenter );
+ globals->get_scenery()->register_placement_transform(taxi_lights_transform);
}
}
SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! terra_transform = " << terra_transform );
}
+ // Unregister that one at the scenery manager
+ globals->get_scenery()->unregister_placement_transform(terra_transform);
+
// find the terrain branch parent
int pcount = terra_transform->getNumParents();
if ( pcount > 0 ) {
// find the ground lighting branch
if ( gnd_lights_transform ) {
+ // Unregister that one at the scenery manager
+ globals->get_scenery()->unregister_placement_transform(gnd_lights_transform);
pcount = gnd_lights_transform->getNumParents();
if ( pcount > 0 ) {
// find the first parent (should only be one)
// find the vasi lighting branch
if ( vasi_lights_transform ) {
+ // Unregister that one at the scenery manager
+ globals->get_scenery()->unregister_placement_transform(vasi_lights_transform);
pcount = vasi_lights_transform->getNumParents();
if ( pcount > 0 ) {
// find the first parent (should only be one)
// find the runway lighting branch
if ( rwy_lights_transform ) {
+ // Unregister that one at the scenery manager
+ globals->get_scenery()->unregister_placement_transform(rwy_lights_transform);
pcount = rwy_lights_transform->getNumParents();
if ( pcount > 0 ) {
// find the first parent (should only be one)
// find the taxi lighting branch
if ( taxi_lights_transform ) {
+ // Unregister that one at the scenery manager
+ globals->get_scenery()->unregister_placement_transform(taxi_lights_transform);
pcount = taxi_lights_transform->getNumParents();
if ( pcount > 0 ) {
// find the first parent (should only be one)
class ssgLeaf;
class ssgBranch;
class ssgTransform;
+class ssgPlacementTransform;
class ssgSelector;
class ssgRangeSelector;
class ssgVertexArray;
// global tile culling data
Point3D center;
double bounding_radius;
- Point3D offset;
// this tile's official location in the world
SGBucket tile_bucket;
// - kidn(fan)
// pointer to ssg transform for this tile
- ssgTransform *terra_transform;
- ssgTransform *vasi_lights_transform;
- ssgTransform *rwy_lights_transform;
- ssgTransform *taxi_lights_transform;
- ssgTransform *gnd_lights_transform;
+ ssgPlacementTransform *terra_transform;
+ ssgPlacementTransform *vasi_lights_transform;
+ ssgPlacementTransform *rwy_lights_transform;
+ ssgPlacementTransform *taxi_lights_transform;
+ ssgPlacementTransform *gnd_lights_transform;
// pointer to ssg range selector for this tile
ssgRangeSelector *terra_range;
// completely freed.
bool free_tile();
- // Calculate this tile's offset
- void SetOffset( const Point3D& p)
- {
- offset = center - p;
- }
-
- // Return this tile's offset
- 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
void prep_ssg_node( const Point3D& p, sgVec3 up, float vis);
/**
* return the SSG Transform node for the terrain
*/
- inline ssgTransform *get_terra_transform() { return terra_transform; }
+ inline ssgPlacementTransform *get_terra_transform() { return terra_transform; }
inline double get_timestamp() const { return timestamp; }
inline void set_timestamp( double time_ms ) { timestamp = time_ms; }
// << current_bucket );
fgSetInt( "/environment/current-tile-id", current_bucket.gen_index() );
- // set global scenery center from current tile center
- current_tile = tile_cache.get_tile( current_bucket );
- if ( current_tile != NULL ) {
- globals->get_scenery()->set_next_center( current_tile->center );
- } else {
- SG_LOG( SG_TERRAIN, SG_WARN, "Tile not found (Ok if initializing)" );
- globals->get_scenery()->set_next_center( Point3D(0.0) );
- }
-
// do tile load scheduling.
// Note that we need keep track of both viewer buckets and fdm buckets.
if ( state == Running ) {
Point3D center = location->get_tile_center();
if (center == Point3D(0.0))
return;
- current_center = center;
float *up = location->get_world_up();
FGTileEntry *e;
* tile cache
*/
FGNewCache tile_cache;
- /**
- * and its center
- */
- Point3D current_center;
/**
* Queue tiles for loading.
// based on current visibilty void prep_ssg_nodes( float
// visibility_meters );
void prep_ssg_nodes( SGLocation *location, float visibility_meters );
- const Point3D get_current_center(void) const { return current_center; }
// Set flag with event manager so that non-moving view refreshes
// tiles...