From ec4b9f854227906f01dd5eea56a87ebb23889748 Mon Sep 17 00:00:00 2001
From: Peter Sadrozinski <psadrozinski@gmail.com>
Date: Sat, 30 Jul 2016 08:26:45 -0400
Subject: [PATCH] Alternative terrain engine based on pagedLOD - remove TileMgr
 from sub systems - add to btg terrain engine

---
 CMakeLists.txt                  |  21 ++
 src/AIModel/AIAircraft.cxx      |   3 +-
 src/FDM/groundcache.cxx         |   3 +-
 src/Main/CMakeLists.txt         |   7 +
 src/Main/fg_commands.cxx        |   5 +-
 src/Main/fg_init.cxx            |   4 +-
 src/Main/globals.cxx            |   6 -
 src/Main/globals.hxx            |   3 -
 src/Main/main.cxx               |   4 +-
 src/Main/options.cxx            |  35 ++-
 src/Scenery/CMakeLists.txt      |   5 +
 src/Scenery/scenery.cxx         | 117 ++++-----
 src/Scenery/scenery.hxx         |  24 +-
 src/Scenery/terrain.hxx         | 112 +++++++++
 src/Scenery/terrain_pgt.cxx     | 252 ++++++++++++++++++++
 src/Scenery/terrain_pgt.hxx     | 127 ++++++++++
 src/Scenery/terrain_stg.cxx     | 409 ++++++++++++++++++++++++++++++++
 src/Scenery/terrain_stg.hxx     | 126 ++++++++++
 src/Scenery/tilemgr.cxx         |  70 +++---
 src/Scenery/tilemgr.hxx         |  17 +-
 src/Viewer/fgviewer.cxx         |  10 +-
 utils/CMakeLists.txt            |   6 +
 utils/demconvert/CMakeLists.txt |   8 +
 utils/demconvert/demconvert.cxx | 219 +++++++++++++++++
 utils/fgelev/CMakeLists.txt     |   1 +
 utils/fgviewer/CMakeLists.txt   |   1 +
 26 files changed, 1432 insertions(+), 163 deletions(-)
 create mode 100644 src/Scenery/terrain.hxx
 create mode 100644 src/Scenery/terrain_pgt.cxx
 create mode 100644 src/Scenery/terrain_pgt.hxx
 create mode 100644 src/Scenery/terrain_stg.cxx
 create mode 100644 src/Scenery/terrain_stg.hxx
 create mode 100644 utils/demconvert/CMakeLists.txt
 create mode 100644 utils/demconvert/demconvert.cxx

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1bbab3ac7..2bac23aad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -167,6 +167,8 @@ option(ENABLE_YASIM      "Set to ON to build FlightGear with YASIM FDM (default)
 option(ENABLE_JSBSIM     "Set to ON to build FlightGear with JSBSim FDM (default)" ON)
 option(EVENT_INPUT       "Set to ON to build FlightGear with event-based Input support" ${EVENT_INPUT_DEFAULT})
 option(ENABLE_RTI        "Set to ON to build FlightGear with RTI support" OFF)
+option(ENABLE_GDAL       "Set to ON to build FlightGear with GDAL support" OFF)
+option(ENABLE_OPENMP     "Set to ON to build FlightGear with OpenMP compiler support" OFF)
 option(ENABLE_PROFILE    "Set to ON to build FlightGear with gperftools profiling support" OFF)
 option(SYSTEM_SQLITE     "Set to ON to build FlightGear with the system's SQLite3 library" OFF)
 option(ENABLE_IAX        "Set to ON to build FlightGear with IAXClient/fgcom built-in (default)" ON)
@@ -194,6 +196,7 @@ option(ENABLE_FLITE      "Set to ON to build the Flite text-to-speech module" ON
 option(ENABLE_QT         "Set to ON to build the internal Qt launcher" ON)
 option(ENABLE_TRAFFIC    "Set to ON to build the external traffic generator modules" ON)
 option(ENABLE_FGQCANVAS  "Set to ON to build the Qt-based remote canvas application" OFF)
+option(ENABLE_DEMCONVERT "Set to ON to build the dem conversion tool (default)" ON)
 
 include (DetectArch)
 
@@ -357,6 +360,24 @@ else()
     message(STATUS "RTI: DISABLED")
 endif(ENABLE_RTI)
 
+if(ENABLE_GDAL)
+    find_package(GDAL 2.0.0 REQUIRED)
+endif(ENABLE_GDAL)
+include_directories(${GDAL_INCLUDE_DIR})
+
+if (ENABLE_OPENMP)
+    find_package(OpenMP)
+    if(OPENMP_FOUND)
+        message(STATUS "OpenMP: ENABLED")
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
+    else()
+        message(STATUS "OpenMP: NOT FOUND")
+    endif()
+else()
+    message(STATUS "OpenMP: DISABLED")
+endif()
+
 if (ENABLE_SIMD)
   message(STATUS "SSE/SSE2 support: ENABLED")
 else()
diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx
index 3cb90a51c..0fc0bf5ae 100644
--- a/src/AIModel/AIAircraft.cxx
+++ b/src/AIModel/AIAircraft.cxx
@@ -25,7 +25,6 @@
 #include <Main/fg_props.hxx>
 #include <Main/globals.hxx>
 #include <Scenery/scenery.hxx>
-#include <Scenery/tilemgr.hxx>
 #include <Airports/dynamics.hxx>
 #include <Airports/airport.hxx>
 #include <Main/util.hxx>
@@ -551,7 +550,7 @@ void FGAIAircraft::getGroundElev(double dt) {
         }
 
         double range = 500.0;
-        if (globals->get_tile_mgr()->schedule_scenery(pos, range, 5.0))
+        if (globals->get_scenery()->schedule_scenery(pos, range, 5.0))
         {
             double alt;
             if (getGroundElevationM(SGGeod::fromGeodM(pos, 20000), alt, 0))
diff --git a/src/FDM/groundcache.cxx b/src/FDM/groundcache.cxx
index b391df41f..93bc5ae3b 100644
--- a/src/FDM/groundcache.cxx
+++ b/src/FDM/groundcache.cxx
@@ -67,7 +67,6 @@
 
 #include <Main/globals.hxx>
 #include <Scenery/scenery.hxx>
-#include <Scenery/tilemgr.hxx>
 
 #include "flight.hxx"
 
@@ -330,7 +329,7 @@ FGGroundCache::prepare_ground_cache(double startSimTime, double endSimTime,
     SGGeod geodPt = SGGeod::fromCart(pt);
     // Don't blow away the cache ground_radius and stuff if there's no
     // scenery
-    if (!globals->get_tile_mgr()->schedule_scenery(geodPt, rad, 1.0)) {
+    if (!globals->get_scenery()->schedule_scenery(geodPt, rad, 1.0)) {
         SG_LOG(SG_FLIGHT, SG_BULK, "prepare_ground_cache(): scenery_available "
                "returns false at " << geodPt << " " << pt << " " << rad);
         return false;
diff --git a/src/Main/CMakeLists.txt b/src/Main/CMakeLists.txt
index 492b6b9ff..ac47b0ddd 100644
--- a/src/Main/CMakeLists.txt
+++ b/src/Main/CMakeLists.txt
@@ -110,6 +110,12 @@ else()
 	set(HLA_LIBRARIES "")
 endif()
 
+if(GDAL_FOUND)
+	set(GDAL_LIBRARIES ${GDAL_LIBRARY})
+else()
+	set(GDAL_LIBRARIES "")
+endif()
+
 if(ENABLE_JSBSIM)
     # FIXME - remove once JSBSim doesn't expose private headers
     include_directories(${PROJECT_SOURCE_DIR}/src/FDM/JSBSim)
@@ -141,6 +147,7 @@ target_link_libraries(fgfs
 	${OPENGL_LIBRARIES}
 	${PLIB_LIBRARIES}
 	${HLA_LIBRARIES}
+	${GDAL_LIBRARIES}
 	${EVENT_INPUT_LIBRARIES}
 	${PLATFORM_LIBS}
 )
diff --git a/src/Main/fg_commands.cxx b/src/Main/fg_commands.cxx
index 72b7abc03..3e4761336 100644
--- a/src/Main/fg_commands.cxx
+++ b/src/Main/fg_commands.cxx
@@ -32,7 +32,6 @@
 #include <GUI/dialog.hxx>
 #include <Aircraft/replay.hxx>
 #include <Scenery/scenery.hxx>
-#include <Scenery/tilemgr.hxx>
 #include <Scripting/NasalSys.hxx>
 #include <Sound/sample_queue.hxx>
 #include <Airports/xmlloader.hxx>
@@ -547,8 +546,8 @@ do_materials_reload (const SGPropertyNode * arg)
     }  
 
     globals->set_matlib(new_matlib);    
-    FGTileMgr* tileManager = static_cast<FGTileMgr*>(globals->get_subsystem("tile-manager"));
-    tileManager->materialLibChanged();
+    FGScenery* scenery = static_cast<FGScenery*>(globals->get_scenery());
+    scenery->materialLibChanged();
     
     return true;
 }
diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx
index d0ca9f5ae..565c855d6 100644
--- a/src/Main/fg_init.cxx
+++ b/src/Main/fg_init.cxx
@@ -111,7 +111,6 @@
 #include <Navaids/navlist.hxx>
 #include <Scenery/scenery.hxx>
 #include <Scenery/SceneryPager.hxx>
-#include <Scenery/tilemgr.hxx>
 #include <Scripting/NasalSys.hxx>
 #include <Sound/voice.hxx>
 #include <Sound/soundmanager.hxx>
@@ -1029,7 +1028,7 @@ void fgStartNewReset()
         string_list::const_iterator it;
         for (it = names.begin(); it != names.end(); ++it) {
             if ((*it == "time") || (*it == "terrasync") || (*it == "events")
-                || (*it == "lighting") || (*it == FGTileMgr::subsystemName()) || (*it == FGScenery::subsystemName()))
+                || (*it == "lighting") || (*it == FGScenery::subsystemName()))
             {
                 continue;
             }
@@ -1052,7 +1051,6 @@ void fgStartNewReset()
     
     // order is important here since tile-manager shutdown needs to
     // access the scenery object
-    subsystemManger->remove(FGTileMgr::subsystemName());
     subsystemManger->remove(FGScenery::subsystemName());
 
     FGScenery::getPagerSingleton()->clearRequests();
diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx
index 7a4a4dc0e..e4218d51b 100644
--- a/src/Main/globals.cxx
+++ b/src/Main/globals.cxx
@@ -210,7 +210,6 @@ FGGlobals::~FGGlobals()
     subsystem_mgr->shutdown();
     subsystem_mgr->unbind();
 
-    subsystem_mgr->remove(FGTileMgr::subsystemName());
     // don't cancel the pager until after shutdown, since AIModels (and
     // potentially others) can queue delete requests on the pager.
     if (vw && vw->getDatabasePager()) {
@@ -756,11 +755,6 @@ FGScenery* FGGlobals::get_scenery () const
     return get_subsystem<FGScenery>();
 }
 
-FGTileMgr* FGGlobals::get_tile_mgr () const
-{
-    return get_subsystem<FGTileMgr>();
-}
-
 FGViewMgr *FGGlobals::get_viewmgr() const
 {
     return get_subsystem<FGViewMgr>();
diff --git a/src/Main/globals.hxx b/src/Main/globals.hxx
index b3b80c5cc..16eabe01b 100644
--- a/src/Main/globals.hxx
+++ b/src/Main/globals.hxx
@@ -59,7 +59,6 @@ class FGTACANList;
 class FGLocale;
 class FGRouteMgr;
 class FGScenery;
-class FGTileMgr;
 class FGViewMgr;
 class FGRenderer;
 
@@ -364,8 +363,6 @@ public:
     FGControls *get_controls() const;
 
     FGScenery * get_scenery () const;
-
-    FGTileMgr * get_tile_mgr () const;
   
     inline FGTACANList *get_channellist() const { return channellist; }
     inline void set_channellist( FGTACANList *c ) { channellist = c; }
diff --git a/src/Main/main.cxx b/src/Main/main.cxx
index 0b0db0954..4c7cbe1d4 100644
--- a/src/Main/main.cxx
+++ b/src/Main/main.cxx
@@ -58,7 +58,6 @@ extern bool global_crashRptEnabled;
 
 #include <Model/panelnode.hxx>
 #include <Scenery/scenery.hxx>
-#include <Scenery/tilemgr.hxx>
 #include <Sound/soundmanager.hxx>
 #include <Time/TimeManager.hxx>
 #include <GUI/gui.h>
@@ -313,8 +312,7 @@ static void fgIdleFunction ( void ) {
         globals->add_new_subsystem<FGScenery>(SGSubsystemMgr::DISPLAY);
         globals->get_scenery()->init();
         globals->get_scenery()->bind();
-        globals->add_new_subsystem<FGTileMgr>(SGSubsystemMgr::DISPLAY);
-        
+
         fgSplashProgress("creating-subsystems");
     } else if (( idle_state == 7 ) || (idle_state == 2007)) {
         bool isReset = (idle_state == 2007);
diff --git a/src/Main/options.cxx b/src/Main/options.cxx
index 31805048f..bcb5aae15 100644
--- a/src/Main/options.cxx
+++ b/src/Main/options.cxx
@@ -120,17 +120,17 @@ static int fgSetupProxy( const char *arg );
 void fgSetDefaults ()
 {
 
-				// Position (deliberately out of range)
+    // Position (deliberately out of range)
     fgSetDouble("/position/longitude-deg", 9999.0);
     fgSetDouble("/position/latitude-deg", 9999.0);
     fgSetDouble("/position/altitude-ft", -9999.0);
 
-				// Orientation
+    // Orientation
     fgSetDouble("/orientation/heading-deg", 9999.0);
     fgSetDouble("/orientation/roll-deg", 0.0);
     fgSetDouble("/orientation/pitch-deg", 0.424);
 
-				// Velocities
+    // Velocities
     fgSetDouble("/velocities/uBody-fps", 0.0);
     fgSetDouble("/velocities/vBody-fps", 0.0);
     fgSetDouble("/velocities/wBody-fps", 0.0);
@@ -140,7 +140,7 @@ void fgSetDefaults ()
     fgSetDouble("/velocities/airspeed-kt", 0.0);
     fgSetDouble("/velocities/mach", 0.0);
 
-                                // Presets
+    // Presets
     fgSetDouble("/sim/presets/longitude-deg", 9999.0);
     fgSetDouble("/sim/presets/latitude-deg", 9999.0);
     fgSetDouble("/sim/presets/altitude-ft", -9999.0);
@@ -162,7 +162,7 @@ void fgSetDefaults ()
     fgSetBool("/sim/presets/onground", true);
     fgSetBool("/sim/presets/trim", false);
 
-				// Miscellaneous
+    // Miscellaneous
     fgSetBool("/sim/startup/splash-screen", true);
     // we want mouse-pointer to have an undefined value if nothing is
     // specified so we can do the right thing for voodoo-1/2 cards.
@@ -170,7 +170,7 @@ void fgSetDefaults ()
     fgSetBool("/controls/flight/auto-coordination", false);
     fgSetString("/sim/logging/priority", "alert");
 
-				// Features
+    // Features
     fgSetBool("/sim/hud/color/antialiased", false);
     fgSetBool("/sim/hud/enable3d[1]", true);
     fgSetBool("/sim/hud/visibility[1]", false);
@@ -179,13 +179,21 @@ void fgSetDefaults ()
     fgSetBool("/sim/sound/working", true);
     fgSetBool("/sim/fgcom/enabled", false);
 
-				// Flight Model options
+    // Flight Model options
     fgSetString("/sim/flight-model", "jsb");
     fgSetString("/sim/aero", "c172");
     fgSetInt("/sim/model-hz", NEW_DEFAULT_MODEL_HZ);
     fgSetDouble("/sim/speed-up", 1.0);
 
-				// Rendering options
+    // Scenery
+    fgSetString("/sim/scenery/engine", "tilecache");
+
+    // ( scenery = pagedLOD )
+    fgSetString("/sim/scenery/lod-levels", "1 3 5 7 9");
+    fgSetString("/sim/scenery/lod-res", "1");
+    fgSetString("/sim/scenery/lod-texturing", "bluemarble");
+
+    // Rendering options
     fgSetString("/sim/rendering/fog", "nicest");
     fgSetBool("/environment/clouds/status", true);
     fgSetBool("/sim/startup/fullscreen", false);
@@ -204,16 +212,16 @@ void fgSetDefaults ()
     fgSetString("/sim/view-mode", "pilot");
     fgSetDouble("/sim/current-view/heading-offset-deg", 0);
 
-				// HUD options
+    // HUD options
     fgSetString("/sim/startup/units", "feet");
     fgSetString("/sim/hud/frame-stat-type", "tris");
 
-				// Time options
+    // Time options
     fgSetInt("/sim/startup/time-offset", 0);
     fgSetString("/sim/startup/time-offset-type", "system-offset");
     fgSetLong("/sim/time/cur-time-override", 0);
 
-                                // Freeze options
+    // Freeze options
     fgSetBool("/sim/freeze/master", false);
     fgSetBool("/sim/freeze/position", false);
     fgSetBool("/sim/freeze/clock", false);
@@ -1609,6 +1617,11 @@ struct OptionDesc {
     {"roc",                          true,  OPTION_FUNC,   "", false, "", fgOptRoc },
     {"fg-root",                      true,  OPTION_IGNORE,   "", false, "", 0 },
     {"fg-scenery",                   true,  OPTION_FUNC | OPTION_MULTI,   "", false, "", fgOptFgScenery },
+    {"terrain-engine",               true,  OPTION_STRING, "/sim/scenery/engine", false, "tilecache", 0 },
+    {"lod-levels",                   true,  OPTION_STRING, "/sim/scenery/lod-levels", false, "1 3 5 7", 0 },
+    {"lod-res",                      true,  OPTION_STRING, "/sim/scenery/lod-res", false, "1", 0 },
+    {"lod-texturing",                true,  OPTION_STRING, "/sim/scenery/lod-texturing", false, "bluemarble", 0 },
+    {"lod-range-mult",               true,  OPTION_STRING, "/sim/scenery/lod-range-mult", false, "2", 0 },
     {"fg-aircraft",                  true,  OPTION_IGNORE | OPTION_MULTI,   "", false, "", 0 },
     {"fdm",                          true,  OPTION_STRING, "/sim/flight-model", false, "", 0 },
     {"aero",                         true,  OPTION_STRING, "/sim/aero", false, "", 0 },
diff --git a/src/Scenery/CMakeLists.txt b/src/Scenery/CMakeLists.txt
index 3a4c9a7ec..7a4d0e3f8 100644
--- a/src/Scenery/CMakeLists.txt
+++ b/src/Scenery/CMakeLists.txt
@@ -4,6 +4,8 @@ set(SOURCES
 	SceneryPager.cxx
 	redout.cxx
 	scenery.cxx
+	terrain_stg.cxx
+	terrain_pgt.cxx
 	tilecache.cxx
 	tileentry.cxx
 	tilemgr.cxx
@@ -13,6 +15,9 @@ set(HEADERS
 	SceneryPager.hxx
 	redout.hxx
 	scenery.hxx
+	terrain.hxx
+	terrain_stg.hxx
+	terrain_pgt.hxx
 	tilecache.hxx
 	tileentry.hxx
 	tilemgr.hxx
diff --git a/src/Scenery/scenery.cxx b/src/Scenery/scenery.cxx
index b64c16d5c..767448006 100644
--- a/src/Scenery/scenery.cxx
+++ b/src/Scenery/scenery.cxx
@@ -57,8 +57,12 @@
 #include <Main/fg_props.hxx>
 #include <GUI/MouseCursor.hxx>
 
-#include "tilemgr.hxx"
 #include "scenery.hxx"
+#include "terrain_stg.hxx"
+
+#ifdef ENABLE_GDAL
+#include "terrain_pgt.hxx"
+#endif
 
 using namespace flightgear;
 using namespace simgear;
@@ -298,7 +302,7 @@ FGScenery::~FGScenery()
 
 
 // Initialize the Scenery Management system
-void FGScenery::init() {
+void FGScenery::init() {    
     // Already set up.
     if (_inited)
         return;
@@ -345,14 +349,36 @@ void FGScenery::init() {
     precipitation_branch->setName("Precipitation");
     scene_graph->addChild(precipitation_branch.get());
 
+    // initialize the terrian based on selected engine
+    std::string engine = fgGetString("/sim/scenery/engine", "tilecache" );
+    SG_LOG( SG_TERRAIN, SG_INFO, "Selected scenery is " << engine );
+
+    if ( engine == "pagedLOD" ) {
+#ifdef ENABLE_GDAL
+        _terrain = new FGPgtTerrain();
+#else
+        _terrain = new FGStgTerrain();
+#endif
+    } else {
+        _terrain = new FGStgTerrain();
+    }
+    _terrain->init( terrain_branch.get() );
+
     _listener = new ScenerySwitchListener(this);
 
     // Toggle the setup flag.
     _inited = true;
 }
 
+void FGScenery::reinit()
+{
+    _terrain->reinit();
+}
+
 void FGScenery::shutdown()
 {
+    _terrain->shutdown();
+    
     scene_graph = NULL;
     terrain_branch = NULL;
     models_branch = NULL;
@@ -366,17 +392,13 @@ void FGScenery::shutdown()
 
 
 void FGScenery::update(double dt)
-{
-    SG_UNUSED(dt);
-    // nothing here, don't call again
-    suspend();
+{    
+    _terrain->update(dt);
 }
 
-
 void FGScenery::bind() {
 }
 
-
 void FGScenery::unbind() {
 }
 
@@ -386,9 +408,8 @@ FGScenery::get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
                                 const simgear::BVHMaterial** material,
                                 const osg::Node* butNotFrom)
 {
-  SGGeod geod = SGGeod::fromCart(pos);
-  geod.setElevationM(geod.getElevationM() + max_altoff);
-  return get_elevation_m(geod, alt, material, butNotFrom);
+    return _terrain->get_cart_elevation_m(pos, max_altoff, alt,
+                                          material, butNotFrom);
 }
 
 bool
@@ -396,25 +417,8 @@ FGScenery::get_elevation_m(const SGGeod& geod, double& alt,
                            const simgear::BVHMaterial** material,
                            const osg::Node* butNotFrom)
 {
-  SGVec3d start = SGVec3d::fromGeod(geod);
-
-  SGGeod geodEnd = geod;
-  geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000));
-  SGVec3d end = SGVec3d::fromGeod(geodEnd);
-
-  FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
-  intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
-  get_scene_graph()->accept(intersectVisitor);
-
-  if (!intersectVisitor.getHaveHit())
-      return false;
-
-  geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd());
-  alt = geodEnd.getElevationM();
-  if (material)
-      *material = intersectVisitor.getMaterial();
-
-  return true;
+    return _terrain->get_elevation_m( geod, alt, material, 
+                                      butNotFrom );
 }
 
 bool
@@ -422,50 +426,22 @@ FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
                                         SGVec3d& nearestHit,
                                         const osg::Node* butNotFrom)
 {
-  // We assume that starting positions in the center of the earth are invalid
-  if ( norm1(pos) < 1 )
-    return false;
-
-  // Make really sure the direction is normalized, is really cheap compared to
-  // computation of ground intersection.
-  SGVec3d start = pos;
-  SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
-
-  FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
-  intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
-  get_scene_graph()->accept(intersectVisitor);
-
-  if (!intersectVisitor.getHaveHit())
-      return false;
-
-  nearestHit = intersectVisitor.getLineSegment().getEnd();
-  return true;
+    return _terrain->get_cart_ground_intersection( pos, dir, nearestHit, butNotFrom );
 }
 
 bool FGScenery::scenery_available(const SGGeod& position, double range_m)
 {
-  if(globals->get_tile_mgr()->schedule_scenery(position, range_m, 0.0))
-  {
-    double elev;
-    if (!get_elevation_m(SGGeod::fromGeodM(position, SG_MAX_ELEVATION_M), elev, 0, 0))
-      return false;
-    SGVec3f p = SGVec3f::fromGeod(SGGeod::fromGeodM(position, elev));
-    osg::FrameStamp* framestamp
-            = globals->get_renderer()->getViewer()->getFrameStamp();
-    simgear::CheckSceneryVisitor csnv(_pager, toOsg(p), range_m, framestamp);
-    // currently the PagedLODs will not be loaded by the DatabasePager
-    // while the splashscreen is there, so CheckSceneryVisitor force-loads
-    // missing objects in the main thread
-    get_scene_graph()->accept(csnv);
-    if(!csnv.isLoaded()) {
-        SG_LOG(SG_TERRAIN, SG_DEBUG, "FGScenery::scenery_available: waiting on CheckSceneryVisitor");
-        return false;
-    }
-    return true;
-  } else {
-    SG_LOG(SG_TERRAIN, SG_DEBUG, "FGScenery::scenery_available: waiting on tile manager");
-  }
-  return false;
+    return _terrain->scenery_available( position, range_m );
+}
+
+bool FGScenery::schedule_scenery(const SGGeod& position, double range_m, double duration)
+{
+    return _terrain->schedule_scenery( position, range_m, duration );
+}
+    
+void FGScenery::materialLibChanged()
+{
+    _terrain->materialLibChanged();
 }
 
 static osg::ref_ptr<SceneryPager> pager;
@@ -481,4 +457,3 @@ void FGScenery::resetPagerSingleton()
 {
     pager = NULL;
 }
-
diff --git a/src/Scenery/scenery.hxx b/src/Scenery/scenery.hxx
index c4f8cec36..6ae1e95e7 100644
--- a/src/Scenery/scenery.hxx
+++ b/src/Scenery/scenery.hxx
@@ -38,18 +38,21 @@
 #include <simgear/structure/subsystem_mgr.hxx>
 
 #include "SceneryPager.hxx"
+#include "terrain.hxx"
 
 namespace simgear {
 class BVHMaterial;
 }
 
+class FGTerrain;
+
 // Define a structure containing global scenery parameters
 class FGScenery : public SGSubsystem
 {
     class ScenerySwitchListener;
     friend class ScenerySwitchListener;
-  
 
+    
     // scene graph
     osg::ref_ptr<osg::Switch> scene_graph;
     osg::ref_ptr<osg::Group> terrain_branch;
@@ -61,13 +64,14 @@ class FGScenery : public SGSubsystem
     
     osg::ref_ptr<flightgear::SceneryPager> _pager;
     ScenerySwitchListener* _listener;
-public:
 
+public:
     FGScenery();
     ~FGScenery();
 
     // Implementation of SGSubsystem.
     void init ();
+    void reinit();
     void shutdown ();
     void bind ();
     void unbind ();
@@ -129,17 +133,21 @@ public:
     // the scenery is initialized.
     static flightgear::SceneryPager* getPagerSingleton();
     static void resetPagerSingleton();
-    
-    flightgear::SceneryPager* getPager() { return _pager.get(); }
 
+    flightgear::SceneryPager* getPager() { return _pager.get(); }
+    
+    // tile mgr api
+    bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0);
+    void materialLibChanged();
+    
     static const char* subsystemName() { return "scenery"; }
 
 private:
-    // The state of the scene graph.
+    // the terrain engine
+    FGTerrain* _terrain;
+
+    // The state of the scene graph.    
     bool _inited;
 };
 
-
 #endif // _SCENERY_HXX
-
-
diff --git a/src/Scenery/terrain.hxx b/src/Scenery/terrain.hxx
new file mode 100644
index 000000000..84a5f5e15
--- /dev/null
+++ b/src/Scenery/terrain.hxx
@@ -0,0 +1,112 @@
+// scenery.hxx -- data structures and routines for managing scenery.
+//
+// Written by Curtis Olson, started May 1997.
+//
+// Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
+//
+// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifndef _TERRAIN_HXX
+#define _TERRAIN_HXX
+
+
+#ifndef __cplusplus                                                          
+# error This library requires C++
+#endif                                   
+
+#include <osg/ref_ptr>
+#include <osg/Switch>
+
+#include <simgear/compiler.h>
+#include <simgear/math/SGMath.hxx>
+#include <simgear/scene/model/particles.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
+
+#include "scenery.hxx"
+#include "SceneryPager.hxx"
+#include "tilemgr.hxx"
+
+namespace simgear {
+class BVHMaterial;
+}
+
+// Define a structure containing global scenery parameters
+class FGTerrain
+{
+public:
+    FGTerrain() {};
+    ~FGTerrain() {};
+
+    // Implementation of SGSubsystem. - called from Scenery
+    virtual void init ( osg::Group* terrain ) = 0;
+    virtual void reinit() = 0;
+    virtual void shutdown () = 0;
+    virtual void bind () = 0;
+    virtual void unbind () = 0;
+    virtual void update (double dt) = 0;
+
+    /// Compute the elevation of the scenery at geodetic latitude lat,
+    /// geodetic longitude lon and not higher than max_alt.
+    /// If the exact flag is set to true, the scenery center is moved to
+    /// gain a higher accuracy of that query. The center is restored past
+    /// that to the original value.
+    /// The altitude hit is returned in the alt argument.
+    /// The method returns true if the scenery is available for the given
+    /// lat/lon pair. If there is no scenery for that point, the altitude
+    /// value is undefined. 
+    /// All values are meant to be in meters or degrees.
+    virtual bool get_elevation_m(const SGGeod& geod, double& alt,
+                                 const simgear::BVHMaterial** material,
+                                 const osg::Node* butNotFrom = 0) = 0;
+
+    /// Compute the elevation of the scenery below the cartesian point pos.
+    /// you the returned scenery altitude is not higher than the position
+    /// pos plus an offset given with max_altoff.
+    /// If the exact flag is set to true, the scenery center is moved to
+    /// gain a higher accuracy of that query. The center is restored past
+    /// that to the original value.
+    /// The altitude hit is returned in the alt argument.
+    /// The method returns true if the scenery is available for the given
+    /// lat/lon pair. If there is no scenery for that point, the altitude
+    /// value is undefined.
+    /// All values are meant to be in meters.
+    virtual bool get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
+                                      double& elevation,
+                                      const simgear::BVHMaterial** material,
+                                      const osg::Node* butNotFrom = 0) = 0;
+
+    /// Compute the nearest intersection point of the line starting from 
+    /// start going in direction dir with the terrain.
+    /// The input and output values should be in cartesian coordinates in the
+    /// usual earth centered wgs84 coordinate system. Units are meters.
+    /// On success, true is returned.
+    virtual bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir,
+                                              SGVec3d& nearestHit,
+                                              const osg::Node* butNotFrom = 0) = 0;
+    
+    /// Returns true if scenery is available for the given lat, lon position
+    /// within a range of range_m.
+    /// lat and lon are expected to be in degrees.
+    virtual bool scenery_available(const SGGeod& position, double range_m) = 0;
+
+    // tile mgr api
+    virtual bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0) = 0;
+    virtual void materialLibChanged() = 0;
+};
+
+#endif // _TERRAIN_HXX
diff --git a/src/Scenery/terrain_pgt.cxx b/src/Scenery/terrain_pgt.cxx
new file mode 100644
index 000000000..1a3d1e542
--- /dev/null
+++ b/src/Scenery/terrain_pgt.cxx
@@ -0,0 +1,252 @@
+// terrain_pgt.cxx -- data structures and routines for managing scenery.
+//
+// Written by Curtis Olson, started May 1997.
+//
+// Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
+//
+// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifdef ENABLE_GDAL
+
+#include <boost/lexical_cast.hpp>
+
+#include <simgear/scene/material/mat.hxx>
+#include <simgear/scene/util/SGReaderWriterOptions.hxx>
+
+#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
+#include <Viewer/splash.hxx>
+
+#include "terrain_pgt.hxx"
+#include "scenery.hxx"
+
+using namespace flightgear;
+using namespace simgear;
+
+using flightgear::SceneryPager;
+
+// Terrain Management system
+FGPgtTerrain::FGPgtTerrain() : 
+    _scenery_loaded(fgGetNode("/sim/sceneryloaded", true)),
+    _scenery_override(fgGetNode("/sim/sceneryloaded-override", true))
+{
+    _inited  = false;
+}
+
+FGPgtTerrain::~FGPgtTerrain()
+{
+}
+
+// Initialize the Scenery Management system
+void FGPgtTerrain::init( osg::Group* terrain ) {
+    // Already set up.
+    if (_inited)
+        return;
+
+    SG_LOG(SG_TERRAIN, SG_INFO, "FGPgtTerrain::init");
+
+    // remember the scene terrain branch on scenegraph
+    terrain_branch = terrain;
+
+    // load the whole planet tile - database pager handles 
+    // the quad tree / loading the highres tiles
+    osg::ref_ptr<simgear::SGReaderWriterOptions> options;
+
+    // drops the previous options reference
+    options = new simgear::SGReaderWriterOptions;
+    options->setPropertyNode(globals->get_props());
+
+    osgDB::FilePathList &fp = options->getDatabasePathList();
+    const PathList &sc = globals->get_fg_scenery();
+    fp.clear();
+    PathList::const_iterator it;
+    for (it = sc.begin(); it != sc.end(); ++it) {
+        fp.push_back(it->local8BitStr());
+    }
+
+    options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().local8BitStr());
+
+    options->setPluginStringData("SimGear::BARE_LOD_RANGE", fgGetString("/sim/rendering/static-lod/bare", boost::lexical_cast<string>(SG_OBJECT_RANGE_BARE)));
+    options->setPluginStringData("SimGear::ROUGH_LOD_RANGE", fgGetString("/sim/rendering/static-lod/rough", boost::lexical_cast<string>(SG_OBJECT_RANGE_ROUGH)));
+    options->setPluginStringData("SimGear::ROUGH_LOD_DETAILED", fgGetString("/sim/rendering/static-lod/detailed", boost::lexical_cast<string>(SG_OBJECT_RANGE_DETAILED)));
+    options->setPluginStringData("SimGear::RENDER_BUILDING_MESH", fgGetBool("/sim/rendering/building-mesh", false) ? "true" : "false");
+
+    options->setPluginStringData("SimGear::FG_EARTH", "ON");
+
+    // tunables
+    options->setPluginStringData("SimGear::SPT_PAGE_LEVELS", fgGetString("/sim/scenery/lod-levels", "1 3 5 7 9" ));
+    options->setPluginStringData("SimGear::SPT_RANGE_MULTIPLIER", fgGetString("/sim/scenery/lod-range-mult", "2" ));
+    options->setPluginStringData("SimGear::SPT_MESH_RESOLUTION", fgGetString("/sim/scenery/lod-res", "1" ));
+    options->setPluginStringData("SimGear::SPT_LOD_TEXTURING", fgGetString("/sim/scenery/lod-texturing", "bluemarble" ));
+    options->setMaterialLib(globals->get_matlib());
+
+    // a DEM can contain multiple levels from multiple locations
+    // priority is based on first found...
+    _dem = new SGDem;
+    if ( _dem ) {
+        for (osgDB::FilePathList::const_iterator i = fp.begin(); i != fp.end(); ++i) {
+            SGPath demPath(*i);
+            demPath.append("DEM");
+
+            int numLevels = _dem->addRoot(demPath);
+            if ( numLevels ) {
+                SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - dem path " << demPath << " has " << numLevels << " LOD Levels " );
+            } else {
+                SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - dem path " << demPath << " has NO LOD Levels " );
+            }
+        }
+
+        options->setDem(_dem);
+
+        SG_LOG(SG_TERRAIN, SG_INFO, "Terrain init - Load w180s90-360x180.pgt" );
+        osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile("w180s90-360x180.pgt", options.get());
+        if ( loadedModel ) {
+            terrain_branch->addChild( loadedModel.get() );
+
+            // Toggle the setup flag.
+            _inited = true;
+        }
+    }
+}
+
+void FGPgtTerrain::reinit()
+{
+
+}
+
+void FGPgtTerrain::shutdown()
+{
+    terrain_branch = NULL;
+
+    // Toggle the setup flag.
+    _inited = false;
+}
+
+
+void FGPgtTerrain::update(double dt)
+{
+    // scenery loading check, triggers after each sim (tile manager) reinit
+    if (!_scenery_loaded->getBoolValue())
+    {
+        bool fdmInited = fgGetBool("sim/fdm-initialized");
+        bool positionFinalized = fgGetBool("sim/position-finalized");
+        bool sceneryOverride = _scenery_override->getBoolValue();
+
+        // we are done if final position is set and the scenery & FDM are done.
+        // scenery-override can ignore the last two, but not position finalization.
+        if (positionFinalized && (sceneryOverride || fdmInited))
+        {
+            _scenery_loaded->setBoolValue(true);
+            fgSplashProgress("");
+        }
+        else
+        {
+            if (!positionFinalized) {
+                fgSplashProgress("finalize-position");
+            } else {
+                fgSplashProgress("loading-scenery");
+            }
+
+            // be nice to loader threads while waiting for initial scenery, reduce to 20fps
+            SGTimeStamp::sleepForMSec(50);
+        }
+    }
+}
+
+void FGPgtTerrain::bind() 
+{
+
+}
+
+void FGPgtTerrain::unbind() 
+{
+
+}
+
+bool
+FGPgtTerrain::get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
+                                   double& alt,
+                                   const simgear::BVHMaterial** material,
+                                   const osg::Node* butNotFrom)
+{
+    SGGeod geod = SGGeod::fromCart(pos);
+    geod.setElevationM(geod.getElevationM() + max_altoff);
+
+    return get_elevation_m(geod, alt, material, butNotFrom);
+}
+
+static simgear::BVHMaterial def_mat;
+
+bool
+FGPgtTerrain::get_elevation_m(const SGGeod& geod, double& alt,
+                              const simgear::BVHMaterial** material,
+                              const osg::Node* butNotFrom)
+{
+    alt = 100.0;
+    if (material) {
+      *material = &def_mat;
+    } else {
+//      SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::get_elevation_m: alt " << alt << " no material " );
+    }
+
+    return true;
+}
+
+bool FGPgtTerrain::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
+                                           SGVec3d& nearestHit,
+                                           const osg::Node* butNotFrom)
+{
+    return true;
+}
+
+bool FGPgtTerrain::scenery_available(const SGGeod& position, double range_m)
+{
+    if( schedule_scenery(position, range_m, 0.0) )
+    {
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool FGPgtTerrain::schedule_scenery(const SGGeod& position, double range_m, double duration)
+{
+    // sanity check (unfortunately needed!)
+    if (!position.isValid()) {
+        SG_LOG(SG_TERRAIN, SG_INFO, "FGSptTerrain::schedule_scenery - position invalid");
+        return false;
+    }
+
+    return true;
+}
+
+void FGPgtTerrain::materialLibChanged()
+{
+    // PSADRO: TODO - passing down new regional textures won't work.  these need to be set in the 
+    // lod tree at init time, as OSGDBPager generates the load request, not the tile cache.
+
+    // _options->setMaterialLib(globals->get_matlib());
+}
+
+#endif
diff --git a/src/Scenery/terrain_pgt.hxx b/src/Scenery/terrain_pgt.hxx
new file mode 100644
index 000000000..b91483287
--- /dev/null
+++ b/src/Scenery/terrain_pgt.hxx
@@ -0,0 +1,127 @@
+// terrain_pgt.hxx -- data structures and routines for managing scenery.
+//
+// Written by Curtis Olson, started May 1997.
+//
+// Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
+//
+// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifndef _TERRAIN_PGT_HXX
+#define _TERRAIN_PGT_HXX
+
+
+#ifndef __cplusplus                                                          
+# error This library requires C++
+#endif                                   
+
+#include <osg/ref_ptr>
+#include <osg/Switch>
+
+#include <simgear/compiler.h>
+#include <simgear/props/props.hxx>
+#include <simgear/math/SGMath.hxx>
+#include <simgear/scene/model/particles.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/scene/dem/SGDem.hxx>
+
+#include "terrain.hxx"
+//#include "SceneryPager.hxx"
+//#include "tilemgr.hxx"
+
+namespace simgear {
+class BVHMaterial;
+}
+
+// Define a structure containing global scenery parameters
+class FGPgtTerrain : public FGTerrain
+{
+public:
+
+    FGPgtTerrain();
+    ~FGPgtTerrain();
+
+    // Implementation of SGSubsystem.
+    void init ( osg::Group* terrain );
+    void reinit();
+    void shutdown ();
+    void bind ();
+    void unbind ();
+    void update (double dt);
+
+    /// Compute the elevation of the scenery at geodetic latitude lat,
+    /// geodetic longitude lon and not higher than max_alt.
+    /// If the exact flag is set to true, the scenery center is moved to
+    /// gain a higher accuracy of that query. The center is restored past
+    /// that to the original value.
+    /// The altitude hit is returned in the alt argument.
+    /// The method returns true if the scenery is available for the given
+    /// lat/lon pair. If there is no scenery for that point, the altitude
+    /// value is undefined. 
+    /// All values are meant to be in meters or degrees.
+    bool get_elevation_m(const SGGeod& geod, double& alt,
+                         const simgear::BVHMaterial** material,
+                         const osg::Node* butNotFrom = 0);
+
+    /// Compute the elevation of the scenery below the cartesian point pos.
+    /// you the returned scenery altitude is not higher than the position
+    /// pos plus an offset given with max_altoff.
+    /// If the exact flag is set to true, the scenery center is moved to
+    /// gain a higher accuracy of that query. The center is restored past
+    /// that to the original value.
+    /// The altitude hit is returned in the alt argument.
+    /// The method returns true if the scenery is available for the given
+    /// lat/lon pair. If there is no scenery for that point, the altitude
+    /// value is undefined.
+    /// All values are meant to be in meters.
+    bool get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
+                              double& elevation,
+                              const simgear::BVHMaterial** material,
+                              const osg::Node* butNotFrom = 0);
+
+    /// Compute the nearest intersection point of the line starting from 
+    /// start going in direction dir with the terrain.
+    /// The input and output values should be in cartesian coordinates in the
+    /// usual earth centered wgs84 coordinate system. Units are meters.
+    /// On success, true is returned.
+    bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir,
+                                      SGVec3d& nearestHit,
+                                      const osg::Node* butNotFrom = 0);
+
+    /// Returns true if scenery is available for the given lat, lon position
+    /// within a range of range_m.
+    /// lat and lon are expected to be in degrees.
+    bool scenery_available(const SGGeod& position, double range_m);
+
+    // tile mgr api
+    bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0);
+    void materialLibChanged();
+
+    static const char* subsystemName() { return "scenery"; }
+
+private:
+    // terrain branch of scene graph
+    osg::ref_ptr<osg::Group> terrain_branch;
+
+    SGPropertyNode_ptr _scenery_loaded, _scenery_override;
+
+    bool _inited;
+
+    SGDemPtr _dem;
+};
+
+#endif // _TERRAIN_PGT_HXX
diff --git a/src/Scenery/terrain_stg.cxx b/src/Scenery/terrain_stg.cxx
new file mode 100644
index 000000000..488cadbb8
--- /dev/null
+++ b/src/Scenery/terrain_stg.cxx
@@ -0,0 +1,409 @@
+// scenery.cxx -- data structures and routines for managing scenery.
+//
+// Written by Curtis Olson, started May 1997.
+//
+// Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
+//
+// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <osg/Camera>
+#include <osg/Transform>
+#include <osg/MatrixTransform>
+#include <osg/PositionAttitudeTransform>
+#include <osg/CameraView>
+#include <osg/LOD>
+
+#include <osgViewer/Viewer>
+
+#include <simgear/constants.h>
+#include <simgear/sg_inlines.h>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/scene/tgdb/userdata.hxx>
+#include <simgear/scene/material/matlib.hxx>
+#include <simgear/scene/material/mat.hxx>
+#include <simgear/scene/util/SGNodeMasks.hxx>
+#include <simgear/scene/util/OsgMath.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
+#include <simgear/scene/model/CheckSceneryVisitor.hxx>
+#include <simgear/scene/sky/sky.hxx>
+
+#include <simgear/bvh/BVHNode.hxx>
+#include <simgear/bvh/BVHLineSegmentVisitor.hxx>
+#include <simgear/structure/commands.hxx>
+
+#include <Viewer/renderer.hxx>
+#include <Main/fg_props.hxx>
+#include <GUI/MouseCursor.hxx>
+
+#include "terrain_stg.hxx"
+
+using namespace flightgear;
+using namespace simgear;
+
+class FGGroundPickCallback : public SGPickCallback {
+public:
+  FGGroundPickCallback() : SGPickCallback(PriorityScenery)
+  { }
+    
+  virtual bool buttonPressed( int button,
+                              const osgGA::GUIEventAdapter&,
+                              const Info& info )
+  {
+    // only on left mouse button
+    if (button != 0)
+      return false;
+
+    SGGeod geod = SGGeod::fromCart(info.wgs84);
+    SG_LOG( SG_TERRAIN, SG_INFO, "Got ground pick at " << geod );
+
+    SGPropertyNode *c = fgGetNode("/sim/input/click", true);
+    c->setDoubleValue("longitude-deg", geod.getLongitudeDeg());
+    c->setDoubleValue("latitude-deg", geod.getLatitudeDeg());
+    c->setDoubleValue("elevation-m", geod.getElevationM());
+    c->setDoubleValue("elevation-ft", geod.getElevationFt());
+    fgSetBool("/sim/signals/click", 1);
+
+    return true;
+  }
+};
+
+class FGSceneryIntersect : public osg::NodeVisitor {
+public:
+    FGSceneryIntersect(const SGLineSegmentd& lineSegment,
+                       const osg::Node* skipNode) :
+        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
+        _lineSegment(lineSegment),
+        _skipNode(skipNode),
+        _material(0),
+        _haveHit(false)
+    { }
+
+    bool getHaveHit() const
+    { return _haveHit; }
+    const SGLineSegmentd& getLineSegment() const
+    { return _lineSegment; }
+    const simgear::BVHMaterial* getMaterial() const
+    { return _material; }
+
+    virtual void apply(osg::Node& node)
+    {
+        if (&node == _skipNode)
+            return;
+        if (!testBoundingSphere(node.getBound()))
+            return;
+
+        addBoundingVolume(node);
+    }
+
+    virtual void apply(osg::Group& group)
+    {
+        if (&group == _skipNode)
+            return;
+        if (!testBoundingSphere(group.getBound()))
+            return;
+
+        traverse(group);
+        addBoundingVolume(group);
+    }
+
+    virtual void apply(osg::Transform& transform)
+    { handleTransform(transform); }
+    virtual void apply(osg::Camera& camera)
+    {
+        if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
+            return;
+        handleTransform(camera);
+    }
+    virtual void apply(osg::CameraView& transform)
+    { handleTransform(transform); }
+    virtual void apply(osg::MatrixTransform& transform)
+    { handleTransform(transform); }
+    virtual void apply(osg::PositionAttitudeTransform& transform)
+    { handleTransform(transform); }
+
+private:
+    void handleTransform(osg::Transform& transform)
+    {
+        if (&transform == _skipNode)
+            return;
+        // Hmm, may be this needs to be refined somehow ...
+        if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
+            return;
+
+        if (!testBoundingSphere(transform.getBound()))
+            return;
+
+        osg::Matrix inverseMatrix;
+        if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
+            return;
+        osg::Matrix matrix;
+        if (!transform.computeLocalToWorldMatrix(matrix, this))
+            return;
+
+        SGLineSegmentd lineSegment = _lineSegment;
+        bool haveHit = _haveHit;
+        const simgear::BVHMaterial* material = _material;
+
+        _haveHit = false;
+        _lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr()));
+
+        addBoundingVolume(transform);
+        traverse(transform);
+
+        if (_haveHit) {
+            _lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr()));
+        } else {
+            _lineSegment = lineSegment;
+            _material = material;
+            _haveHit = haveHit;
+        }
+    }
+
+    simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
+    {
+        SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
+        if (!userData)
+            return 0;
+        return userData->getBVHNode();
+    }
+    void addBoundingVolume(osg::Node& node)
+    {
+        simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
+        if (!bvNode)
+            return;
+
+        // Find ground intersection on the bvh nodes
+        simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment,
+                                                          0/*startTime*/);
+        bvNode->accept(lineSegmentVisitor);
+        if (!lineSegmentVisitor.empty()) {
+            _lineSegment = lineSegmentVisitor.getLineSegment();
+            _material = lineSegmentVisitor.getMaterial();
+            _haveHit = true;
+        }
+    }
+
+    bool testBoundingSphere(const osg::BoundingSphere& bound) const
+    {
+        if (!bound.valid())
+            return false;
+
+        SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius);
+        return intersects(_lineSegment, sphere);
+    }
+
+    SGLineSegmentd _lineSegment;
+    const osg::Node* _skipNode;
+
+    const simgear::BVHMaterial* _material;
+    bool _haveHit;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+// Terrain Management system
+FGStgTerrain::FGStgTerrain() :
+    _tilemgr()
+{
+    _inited  = false;
+}
+
+FGStgTerrain::~FGStgTerrain()
+{
+}
+
+
+// Initialize the Scenery Management system
+void FGStgTerrain::init( osg::Group* terrain ) {
+    // Already set up.
+    if (_inited)
+        return;
+
+    SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::init - init tilemgr");
+
+    // remember the scene terrain branch on scenegraph
+    terrain_branch = terrain;
+
+    // initialize the tile manager
+    _tilemgr.init();
+
+    // Toggle the setup flag.
+    _inited = true;
+}
+
+void FGStgTerrain::reinit()
+{
+    SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::reinit - reinit tilemgr");
+
+    _tilemgr.reinit();
+}
+
+void FGStgTerrain::shutdown()
+{
+    SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::shutdown - shutdown tilemgr");
+
+    _tilemgr.shutdown();
+
+    terrain_branch = NULL;
+
+    // Toggle the setup flag.
+    _inited = false;
+}
+
+
+void FGStgTerrain::update(double dt)
+{
+    _tilemgr.update(dt);
+}
+
+void FGStgTerrain::bind() 
+{
+    SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::bind - noop");
+}
+
+void FGStgTerrain::unbind() 
+{
+    SG_LOG(SG_TERRAIN, SG_INFO, "FGStgTerrain::unbind - noop");
+}
+
+bool
+FGStgTerrain::get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
+                                   double& alt,
+                                   const simgear::BVHMaterial** material,
+                                   const osg::Node* butNotFrom)
+{
+  bool ok;
+
+  SGGeod geod = SGGeod::fromCart(pos);
+  geod.setElevationM(geod.getElevationM() + max_altoff);
+
+  ok = get_elevation_m(geod, alt, material, butNotFrom);
+
+  return ok;
+}
+
+bool
+FGStgTerrain::get_elevation_m(const SGGeod& geod, double& alt,
+                              const simgear::BVHMaterial** material,
+                              const osg::Node* butNotFrom)
+{
+  SGVec3d start = SGVec3d::fromGeod(geod);
+
+  SGGeod geodEnd = geod;
+  geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000));
+  SGVec3d end = SGVec3d::fromGeod(geodEnd);
+
+  FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
+  intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
+  terrain_branch->accept(intersectVisitor);
+
+  if (!intersectVisitor.getHaveHit())
+      return false;
+
+  geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd());
+  alt = geodEnd.getElevationM();
+  if (material) {
+      *material = intersectVisitor.getMaterial();
+      SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::get_elevation_m: alt " << alt << " material " << *material );
+  } else {
+      SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::get_elevation_m: alt " << alt << " no material " );
+  }
+  
+  return true;
+}
+
+bool
+FGStgTerrain::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
+                                           SGVec3d& nearestHit,
+                                           const osg::Node* butNotFrom)
+{
+  SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::get_cart_ground_intersection");
+
+  // We assume that starting positions in the center of the earth are invalid
+  if ( norm1(pos) < 1 )
+    return false;
+
+  // Make really sure the direction is normalized, is really cheap compared to
+  // computation of ground intersection.
+  SGVec3d start = pos;
+  SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
+
+  FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
+  intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
+  terrain_branch->accept(intersectVisitor);
+
+  if (!intersectVisitor.getHaveHit())
+      return false;
+
+  nearestHit = intersectVisitor.getLineSegment().getEnd();
+  return true;
+}
+
+bool FGStgTerrain::scenery_available(const SGGeod& position, double range_m)
+{
+  if( schedule_scenery(position, range_m, 0.0) )
+  {
+    double elev;
+    if (!get_elevation_m(SGGeod::fromGeodM(position, SG_MAX_ELEVATION_M), elev, 0, 0))
+    {
+        SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - false" );
+        return false;
+    }
+    
+    SGVec3f p = SGVec3f::fromGeod(SGGeod::fromGeodM(position, elev));
+    osg::FrameStamp* framestamp
+            = globals->get_renderer()->getViewer()->getFrameStamp();
+
+    FGScenery* pSceneryManager = globals->get_scenery();
+    simgear::CheckSceneryVisitor csnv(pSceneryManager->getPager(), toOsg(p), range_m, framestamp);
+    // currently the PagedLODs will not be loaded by the DatabasePager
+    // while the splashscreen is there, so CheckSceneryVisitor force-loads
+    // missing objects in the main thread
+    terrain_branch->accept(csnv);
+    if(!csnv.isLoaded()) {
+        SG_LOG(SG_TERRAIN, SG_DEBUG, "FGScenery::scenery_available: waiting on CheckSceneryVisitor");
+        return false;
+    }
+
+    SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - true" );
+    return true;
+  } else {
+    SG_LOG(SG_TERRAIN, SG_DEBUG, "FGScenery::scenery_available: waiting on tile manager");
+  }
+  SG_LOG(SG_TERRAIN, SG_DEBUG, "FGStgTerrain::scenery_available - false" );
+  return false;
+}
+
+bool FGStgTerrain::schedule_scenery(const SGGeod& position, double range_m, double duration)
+{
+    SG_LOG(SG_TERRAIN, SG_BULK, "FGStgTerrain::schedule_scenery");
+
+    return _tilemgr.schedule_scenery( position, range_m, duration );
+}
+
+void FGStgTerrain::materialLibChanged()
+{
+    _tilemgr.materialLibChanged();
+}
diff --git a/src/Scenery/terrain_stg.hxx b/src/Scenery/terrain_stg.hxx
new file mode 100644
index 000000000..af7c3b490
--- /dev/null
+++ b/src/Scenery/terrain_stg.hxx
@@ -0,0 +1,126 @@
+// scenery.hxx -- data structures and routines for managing scenery.
+//
+// Written by Curtis Olson, started May 1997.
+//
+// Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
+//
+// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifndef _TERRAIN_STG_HXX
+#define _TERRAIN_STG_HXX
+
+
+#ifndef __cplusplus                                                          
+# error This library requires C++
+#endif                                   
+
+#include <osg/ref_ptr>
+#include <osg/Switch>
+
+#include <simgear/compiler.h>
+#include <simgear/math/SGMath.hxx>
+#include <simgear/scene/model/particles.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
+
+#include "terrain.hxx"
+#include "SceneryPager.hxx"
+#include "tilemgr.hxx"
+
+namespace simgear {
+class BVHMaterial;
+}
+
+// Define a structure containing global scenery parameters
+class FGStgTerrain : public FGTerrain
+{
+public:
+
+    FGStgTerrain();
+    ~FGStgTerrain();
+
+    // Implementation of SGSubsystem.
+    void init ( osg::Group* terrain );
+    void reinit();
+    void shutdown ();
+    void bind ();
+    void unbind ();
+    void update (double dt);
+
+    /// Compute the elevation of the scenery at geodetic latitude lat,
+    /// geodetic longitude lon and not higher than max_alt.
+    /// If the exact flag is set to true, the scenery center is moved to
+    /// gain a higher accuracy of that query. The center is restored past
+    /// that to the original value.
+    /// The altitude hit is returned in the alt argument.
+    /// The method returns true if the scenery is available for the given
+    /// lat/lon pair. If there is no scenery for that point, the altitude
+    /// value is undefined. 
+    /// All values are meant to be in meters or degrees.
+    bool get_elevation_m(const SGGeod& geod, double& alt,
+                         const simgear::BVHMaterial** material,
+                         const osg::Node* butNotFrom = 0);
+
+    /// Compute the elevation of the scenery below the cartesian point pos.
+    /// you the returned scenery altitude is not higher than the position
+    /// pos plus an offset given with max_altoff.
+    /// If the exact flag is set to true, the scenery center is moved to
+    /// gain a higher accuracy of that query. The center is restored past
+    /// that to the original value.
+    /// The altitude hit is returned in the alt argument.
+    /// The method returns true if the scenery is available for the given
+    /// lat/lon pair. If there is no scenery for that point, the altitude
+    /// value is undefined.
+    /// All values are meant to be in meters.
+    bool get_cart_elevation_m(const SGVec3d& pos, double max_altoff,
+                              double& elevation,
+                              const simgear::BVHMaterial** material,
+                              const osg::Node* butNotFrom = 0);
+
+    /// Compute the nearest intersection point of the line starting from 
+    /// start going in direction dir with the terrain.
+    /// The input and output values should be in cartesian coordinates in the
+    /// usual earth centered wgs84 coordinate system. Units are meters.
+    /// On success, true is returned.
+    bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir,
+                                      SGVec3d& nearestHit,
+                                      const osg::Node* butNotFrom = 0);
+    
+    /// Returns true if scenery is available for the given lat, lon position
+    /// within a range of range_m.
+    /// lat and lon are expected to be in degrees.
+    bool scenery_available(const SGGeod& position, double range_m);
+
+    // tile mgr api
+    bool schedule_scenery(const SGGeod& position, double range_m, double duration=0.0);
+    void materialLibChanged();
+    
+    static const char* subsystemName() { return "scenery"; }
+
+private:
+    // tile manager
+    FGTileMgr _tilemgr;
+    
+    // terrain branch of scene graph
+    osg::ref_ptr<osg::Group> terrain_branch;
+    
+    bool _inited;
+};
+
+#endif // _TERRAIN_STG_HXX
+
+
diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx
index ebd6c62cf..cb23a0212 100644
--- a/src/Scenery/tilemgr.cxx
+++ b/src/Scenery/tilemgr.cxx
@@ -65,12 +65,12 @@ public:
         _pagedLODMaximumProp(fgGetNode("/sim/rendering/max-paged-lod", true))
     {
         _useVBOsProp->addChangeListener(this, true);
-
+      
         _enableCacheProp->addChangeListener(this, true);
         if (_enableCacheProp->getType() == simgear::props::NONE) {
             _enableCacheProp->setBoolValue(true);
         }
-
+      
         if (_pagedLODMaximumProp->getType() == simgear::props::NONE) {
             // not set, use OSG default / environment value variable
             osg::ref_ptr<osgViewer::Viewer> viewer(globals->get_renderer()->getViewer());
@@ -79,14 +79,14 @@ public:
         }
         _pagedLODMaximumProp->addChangeListener(this, true);
     }
-
+    
     ~TileManagerListener()
     {
         _useVBOsProp->removeChangeListener(this);
         _enableCacheProp->removeChangeListener(this);
         _pagedLODMaximumProp->removeChangeListener(this);
     }
-
+    
     virtual void valueChanged(SGPropertyNode* prop)
     {
         if (prop == _useVBOsProp) {
@@ -101,7 +101,7 @@ public:
           viewer->getDatabasePager()->setTargetMaximumNumberOfPageLOD(v);
         }
     }
-
+    
 private:
     FGTileMgr* _manager;
     SGPropertyNode_ptr _useVBOsProp,
@@ -128,8 +128,7 @@ FGTileMgr::FGTileMgr():
 
 FGTileMgr::~FGTileMgr()
 {
-   }
-
+}
 
 // Initialize the Tile Manager subsystem
 void FGTileMgr::init()
@@ -160,10 +159,10 @@ void FGTileMgr::reinit()
   // drops the previous options reference
     _options = new simgear::SGReaderWriterOptions;
     _listener = new TileManagerListener(this);
-
+    
     materialLibChanged();
     _options->setPropertyNode(globals->get_props());
-
+    
     osgDB::FilePathList &fp = _options->getDatabasePathList();
     const PathList &sc = globals->get_fg_scenery();
     fp.clear();
@@ -172,11 +171,11 @@ void FGTileMgr::reinit()
         fp.push_back(it->local8BitStr());
     }
     _options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().local8BitStr());
-
+    
     if (_terra_sync) {
       _options->setPluginStringData("SimGear::TERRASYNC_ROOT", globals->get_terrasync_dir().local8BitStr());
     }
-
+    
     if (!_disableNasalHooks->getBoolValue())
       _options->setModelData(new FGNasalModelDataProxy);
 
@@ -197,7 +196,7 @@ void FGTileMgr::reinit()
     }
 
     _options->setSceneryPathSuffixes(scenerySuffixes);
-
+  
     if (state != Start)
     {
       // protect against multiple scenery reloads and properly reset flags,
@@ -207,25 +206,25 @@ void FGTileMgr::reinit()
         return;
       }
     }
-
+  
     _scenery_loaded->setBoolValue(false);
     fgSetDouble("/sim/startup/splash-alpha", 1.0);
-
+    
     materialLibChanged();
 
     // remove all old scenery nodes from scenegraph and clear cache
     osg::Group* group = globals->get_scenery()->get_terrain_branch();
     group->removeChildren(0, group->getNumChildren());
     tile_cache.init();
-
+    
     // clear OSG cache, except on initial start-up
     if (state != Start)
     {
         osgDB::Registry::instance()->clearObjectCache();
     }
-
+    
     state = Inited;
-
+    
     previous_bucket.make_bad();
     current_bucket.make_bad();
     scheduled_visibility = 100.0;
@@ -293,7 +292,7 @@ void FGTileMgr::schedule_needed(const SGBucket& curr_bucket, double vis)
             "scheduling needed tiles for " << curr_bucket
            << ", tile-width-m:" << tile_width << ", tile-height-m:" << tile_height);
 
-
+    
     // cout << "tile width = " << tile_width << "  tile_height = "
     //      << tile_height << endl;
 
@@ -310,7 +309,7 @@ void FGTileMgr::schedule_needed(const SGBucket& curr_bucket, double vis)
     // cout << "max cache size = " << tile_cache.get_max_cache_size()
     //      << " current cache size = " << tile_cache.get_size() << endl;
 
-    // clear flags of all tiles belonging to the previous view set
+    // clear flags of all tiles belonging to the previous view set 
     tile_cache.clear_current_view();
 
     // update timestamps, so all tiles scheduled now are *newer* than any tile previously loaded
@@ -332,10 +331,10 @@ void FGTileMgr::schedule_needed(const SGBucket& curr_bucket, double vis)
             if (!b.isValid()) {
                 continue;
             }
-
+            
             float priority = (-1.0) * (x*x+y*y);
             sched_tile( b, priority, true, 0.0 );
-
+            
             if (_terra_sync) {
                 _terra_sync->scheduleTile(b);
             }
@@ -356,7 +355,7 @@ void FGTileMgr::update_queues(bool& isDownloadingScenery)
     TileEntry *e;
     int loading=0;
     int sz=0;
-
+    
     tile_cache.set_current_time( current_time );
     tile_cache.reset_traversal();
 
@@ -369,7 +368,7 @@ void FGTileMgr::update_queues(bool& isDownloadingScenery)
             // Set the ssg transform and update it's range selector
             // based on current visibilty
             e->prep_ssg_node(vis);
-
+            
             if (!e->is_loaded()) {
                 bool nonExpiredOrCurrent = !e->is_expired(current_time) || e->is_current_view();
                 bool downloading = isTileDirSyncing(e->tileFileName);
@@ -400,7 +399,7 @@ void FGTileMgr::update_queues(bool& isDownloadingScenery)
       dropTiles = true;
       drop_count = sz; // no limit on tiles to drop
     }
-
+  
     if (dropTiles)
     {
         long drop_index = _enableCache ? tile_cache.get_drop_tile() :
@@ -412,14 +411,14 @@ void FGTileMgr::update_queues(bool& isDownloadingScenery)
             SG_LOG(SG_TERRAIN, SG_DEBUG, "Dropping:" << old->get_tile_bucket());
 
             tile_cache.clear_entry(drop_index);
-
+            
             osg::ref_ptr<osg::Object> subgraph = old->getNode();
             old->removeFromSceneGraph();
             delete old;
             // zeros out subgraph ref_ptr, so subgraph is owned by
             // the pager and will be deleted in the pager thread.
             _pager->queueDeleteRequest(subgraph);
-
+          
             if (!_enableCache)
                 drop_index = tile_cache.get_first_expired_tile();
             // limit tiles dropped to drop_count
@@ -448,8 +447,8 @@ void FGTileMgr::update(double)
         bool fdmInited = fgGetBool("sim/fdm-initialized");
         bool positionFinalized = fgGetBool("sim/position-finalized");
         bool sceneryOverride = _scenery_override->getBoolValue();
-
-
+        
+        
     // we are done if final position is set and the scenery & FDM are done.
     // scenery-override can ignore the last two, but not position finalization.
         if (positionFinalized && (sceneryOverride || (isSceneryLoaded() && fdmInited)))
@@ -466,7 +465,7 @@ void FGTileMgr::update(double)
             } else {
                 fgSplashProgress("loading-scenery");
             }
-
+            
             // be nice to loader threads while waiting for initial scenery, reduce to 20fps
             SGTimeStamp::sleepForMSec(50);
         }
@@ -506,7 +505,7 @@ void FGTileMgr::schedule_tiles_at(const SGGeod& location, double range_m)
             scheduled_visibility = range_m;
             schedule_needed(current_bucket, range_m);
         }
-
+        
         // save bucket
         previous_bucket = current_bucket;
     } else if ( state == Start || state == Inited ) {
@@ -533,7 +532,7 @@ bool FGTileMgr::schedule_scenery(const SGGeod& position, double range_m, double
 
     SGBucket bucket(position);
     available = sched_tile( bucket, priority, false, duration );
-
+  
     if ((!available)&&(duration==0.0)) {
         SG_LOG( SG_TERRAIN, SG_DEBUG, "schedule_scenery: Scheduling tile at bucket:" << bucket << " return false" );
         return false;
@@ -547,7 +546,7 @@ bool FGTileMgr::schedule_scenery(const SGGeod& position, double range_m, double
     double tile_r = 0.5*sqrt(tile_width*tile_width + tile_height*tile_height);
     double max_dist = tile_r + range_m;
     double max_dist2 = max_dist*max_dist;
-
+    
     int xrange = (int)fabs(range_m / tile_width) + 1;
     int yrange = (int)fabs(range_m / tile_height) + 1;
 
@@ -562,7 +561,7 @@ bool FGTileMgr::schedule_scenery(const SGGeod& position, double range_m, double
                 if (!b.isValid()) {
                     continue;
                 }
-
+                
                 double distance2 = distSqr(cartPos, SGVec3d::fromGeod(b.get_center()));
                 // Do not ask if it is just the next tile but way out of range.
                 if (distance2 <= max_dist2)
@@ -593,10 +592,11 @@ bool FGTileMgr::isTileDirSyncing(const std::string& tileFileName) const
     if (!_terra_sync) {
         return false;
     }
-
+    
     std::string nameWithoutExtension = tileFileName.substr(0, tileFileName.size() - 4);
     long int bucketIndex = simgear::strutils::to_int(nameWithoutExtension);
     SGBucket bucket(bucketIndex);
-
+    
     return _terra_sync->isTileDirPending(bucket.gen_base_path());
 }
+
diff --git a/src/Scenery/tilemgr.hxx b/src/Scenery/tilemgr.hxx
index 252a04908..299930562 100644
--- a/src/Scenery/tilemgr.hxx
+++ b/src/Scenery/tilemgr.hxx
@@ -26,7 +26,6 @@
 
 #include <simgear/compiler.h>
 
-#include <simgear/structure/subsystem_mgr.hxx>
 #include <simgear/bucket/newbucket.hxx>
 #include "SceneryPager.hxx"
 #include "tilecache.hxx"
@@ -42,7 +41,7 @@ class SGTerraSync;
 class SGReaderWriterOptions;
 }
 
-class FGTileMgr : public SGSubsystem {
+class FGTileMgr {
 
 private:
 
@@ -93,17 +92,16 @@ private:
     osg::ref_ptr<flightgear::SceneryPager> _pager;
 
     /// is caching of expired tiles enabled or not?
-    bool _enableCache;
+    bool _enableCache;    
 public:
     FGTileMgr();
     ~FGTileMgr();
 
     // Initialize the Tile Manager
-    virtual void init();
-    virtual void reinit();
-    virtual void shutdown();
-
-    virtual void update(double dt);
+    void init();
+    void reinit();
+    void shutdown();
+    void update(double dt);
 
     const SGBucket& get_current_bucket () const { return current_bucket; }
 
@@ -118,9 +116,6 @@ public:
     // notify the tile manahger the material library was reloaded,
     // so it can pass this through to its options object
     void materialLibChanged();
-
-    static const char* subsystemName() { return "tile-manager"; }
 };
 
-
 #endif // _TILEMGR_HXX
diff --git a/src/Viewer/fgviewer.cxx b/src/Viewer/fgviewer.cxx
index 0cc7accd7..48fab6e67 100644
--- a/src/Viewer/fgviewer.cxx
+++ b/src/Viewer/fgviewer.cxx
@@ -200,10 +200,6 @@ fgviewerMain(int argc, char** argv)
         throw sg_io_exception("Error loading materials file", mpath);
     }
 
-    FGScenery* scenery = globals->add_new_subsystem<FGScenery>();
-    scenery->init();
-    scenery->bind();
-
     // The file path list must be set in the registry.
     osgDB::Registry::instance()->getDataFilePathList() = filePathList;
 
@@ -212,7 +208,11 @@ fgviewerMain(int argc, char** argv)
     options->setMaterialLib(globals->get_matlib());
     options->setPropertyNode(globals->get_props());
     options->setPluginStringData("SimGear::PREVIEW", "ON");
-    
+
+    FGScenery* scenery = globals->add_new_subsystem<FGScenery>();
+    scenery->init();
+    scenery->bind();
+
     // read the scene from the list of file specified command line args.
     osg::ref_ptr<osg::Node> loadedModel;
     loadedModel = osgDB::readNodeFiles(dataFiles, options);
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index fd4b1bbb7..aa6aa0890 100644
--- a/utils/CMakeLists.txt
+++ b/utils/CMakeLists.txt
@@ -38,3 +38,9 @@ endif()
 if (ENABLE_FGQCANVAS)
   add_subdirectory(fgqcanvas)
 endif()
+
+if(ENABLE_DEMCONVERT)
+    if(GDALFOUND)
+        add_subdirectory(demconvert)
+    endif()
+endif()
diff --git a/utils/demconvert/CMakeLists.txt b/utils/demconvert/CMakeLists.txt
new file mode 100644
index 000000000..ad89bbf8e
--- /dev/null
+++ b/utils/demconvert/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_executable(demconvert demconvert.cxx )
+
+target_link_libraries(demconvert
+	SimGearScene SimGearCore
+    ${GDAL_LIBRARY}
+)
+
+install(TARGETS demconvert RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/utils/demconvert/demconvert.cxx b/utils/demconvert/demconvert.cxx
new file mode 100644
index 000000000..5fb120fd6
--- /dev/null
+++ b/utils/demconvert/demconvert.cxx
@@ -0,0 +1,219 @@
+// demconvert.cxx -- convert dem into lower resolutions
+//
+// Copyright (C) 2016 Peter Sadrozinski
+//
+// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fstream>
+#include <sstream>
+#include <iostream>
+#include <iomanip>
+#include <cmath>
+
+#include <osg/ArgumentParser>
+
+#include <simgear/misc/stdint.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/debug/logstream.hxx>
+
+#include <simgear/scene/dem/SGDem.hxx>
+#include <simgear/scene/dem/SGDemSession.hxx>
+
+int main(int argc, char** argv) 
+{
+    std::string demroot;
+    std::string inputvfp;
+    int tileWidth;
+    int tileHeight;
+    int resx, resy;
+    int overlap = 0;
+    SGDem dem;
+
+    osg::ApplicationUsage* usage = new osg::ApplicationUsage();
+    usage->setApplicationName("demconvert");
+    usage->setCommandLineUsage(
+        "Convert high resolution DEM to low res suitable for terrasync dl.");
+    usage->addCommandLineOption("--inputvfp <dir>", "input VFP root directory");
+    usage->addCommandLineOption("--demroot <dir>", "input/ouput DEM root directory");
+    usage->addCommandLineOption("--width <N>", "width (in degrees) of created tiles");
+    usage->addCommandLineOption("--height <N>", "height (in degrees) of created tiles");
+    usage->addCommandLineOption("--resx <N>", "resolution of created tiles (w/o overlap)");
+    usage->addCommandLineOption("--resy <N>", "resolution of created tiles (w/o overlap)");
+    usage->addCommandLineOption("--overlap <N>", "number of pixels of overlap");
+
+    // use an ArgumentParser object to manage the program arguments.
+    osg::ArgumentParser arguments(&argc, argv);
+    arguments.setApplicationUsage(usage);
+
+    sglog().setLogLevels( SG_TERRAIN, SG_INFO );
+
+    arguments.read("--inputvfp", inputvfp);
+    printf( "--inputvfp is %s\n", inputvfp.c_str() );
+    
+    arguments.read("--demroot", demroot);
+    if ( inputvfp.empty() && demroot.empty() ) {
+        arguments.reportError("--inputvfp or --demroot argument required.");
+    } else if ( !demroot.empty() ) {
+        SGPath s(demroot);
+        if (!s.isDir()) {
+            arguments.reportError(
+                "--demroot directory does not exist or is not directory.");
+        } else if (!s.canRead()) {
+            arguments.reportError(
+                "--demroot directory cannot be read. Check permissions.");
+        } else if (!s.canWrite()) {
+            arguments.reportError(
+                "--demroot directory cannot be written. Check permissions.");
+        } else if ( !dem.addRoot(s) ) {
+            // see if we specified input as raw directory
+            if ( inputvfp.empty() ) {
+                arguments.reportError(
+                    "--demroot directory is not a DEM heiarchy");
+            } else {
+                // create a new dem heiarchy
+                printf("Creating new dem heiarchy at %s\n", s.c_str() );
+                dem.createRoot(s);
+            }
+        }
+    } else {
+        SGPath s(inputvfp);
+        if (!s.isDir()) {
+            arguments.reportError(
+                "--inputvfp directory does not exist or is not directory.");
+        } else if (!s.canRead()) {
+            arguments.reportError(
+                "--inputvfp directory cannot be read. Check permissions.");
+        }
+    }
+
+    if (!arguments.read("--width", tileWidth)) {
+        arguments.reportError("--width argument required.");
+    } else {
+        if ( tileWidth < 1 || tileWidth > 60 ) 
+            arguments.reportError(
+                    "--width must be between 1 and 60");
+    }
+
+    if (!arguments.read("--height", tileHeight)) {
+        arguments.reportError("--height argument required.");
+    } else {
+        if ( tileHeight < 1 || tileHeight > 60 ) 
+            arguments.reportError(
+                    "--height must be between 1 and 60");
+    }
+
+    if (!arguments.read("--resx", resx)) {
+        arguments.reportError("--resx argument required.");
+    } else {
+        if ( resx < 2 ) 
+            arguments.reportError(
+                    "--resx must be between greater than 2");
+    }
+
+    if (!arguments.read("--resy", resy)) {
+        arguments.reportError("--resy argument required.");
+    } else {
+        if ( resy < 2 ) 
+            arguments.reportError(
+                    "--resy must be between greater than 2");
+    }
+
+    if (arguments.read("--overlap", overlap)) {
+        if ( overlap > (resx/2)  || overlap > (resy/2) )
+            arguments.reportError(
+                    "--overlap is greater than half tile");
+    }
+
+    if (arguments.errors()) {
+        arguments.writeErrorMessages(std::cout);
+        arguments.getApplicationUsage()->write(std::cout,
+                osg::ApplicationUsage::COMMAND_LINE_OPTION | 
+                osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE, 80, true);
+        return EXIT_FAILURE;
+    }
+
+    double lat_dec = (double)tileHeight / (double)resy;
+    double lon_inc = (double)tileWidth  / (double)resx;
+
+    SG_LOG( SG_TERRAIN, SG_INFO, "tileWidth: " << tileWidth);
+    SG_LOG( SG_TERRAIN, SG_INFO, "tileHeight: " << tileHeight);
+    SG_LOG( SG_TERRAIN, SG_INFO, "lon_inc: " << lon_inc);
+    SG_LOG( SG_TERRAIN, SG_INFO, "lat_dec: " << lat_dec);
+
+#define MIN_X   -180
+#define MIN_Y   -90
+#define MAX_X   180
+#define MAX_Y   90
+
+    // create a new dem level in demRoot
+    SGDemRoot* root = dem.getRoot(0);
+    if ( root ) {
+        int outLvl = root->createLevel( tileWidth, tileHeight, resx, resy, overlap, ".tiff" );
+        if ( outLvl >= 0 ) {
+            printf("SGDem::createLevel success\n");
+
+            // traverse the new tiles, 1 at a time
+            for ( int tilex = MIN_X; tilex < MAX_X; tilex += tileWidth ) {
+                for ( int tiley = MAX_Y; tiley > MIN_Y; tiley -= tileHeight ) {
+                    // traverse rows from north to south, then columns west to east
+                    double lonmin = (double)tilex;
+                    double lonmax = lonmin + (double)tileWidth;
+                    double latmax = (double)tiley;
+                    double latmin = latmax - (double)tileHeight;
+
+                    unsigned wo = SGDem::longitudeDegToOffset(lonmin);
+                    unsigned eo = SGDem::longitudeDegToOffset(lonmax);
+                    unsigned so = SGDem::latitudeDegToOffset(latmin);
+                    unsigned no = SGDem::latitudeDegToOffset(latmax);
+
+                    if ( !inputvfp.empty() ) {
+                        // read from vfp files
+                        printf("open session from raw directory\n");
+                        SGDemSession s = dem.openSession( SGGeod::fromDeg(lonmin, latmin), SGGeod::fromDeg(lonmax, latmax), SGPath(inputvfp) );
+                        printf("opened session from raw directory\n");
+
+                        // create a new dem tile for the new level
+                        SGDemTileRef tile = root->createTile( outLvl, (int)lonmin, (int)latmin, overlap, s );
+
+                        s.close();
+                    } else {
+                        // read session from DEM root - don't cache - include adjacent tiles
+                        fprintf( stderr, "open session from DEM level %d\n", outLvl-1);
+                        SGDemSession s = dem.openSession( wo, so, eo, no, outLvl-1, false );
+                        fprintf( stderr, "session has %d tiles\n", s.size() );
+
+                        // create a new dem tile for the new level
+                        SGDemTileRef tile = root->createTile( outLvl, (int)lonmin, (int)latmin, overlap, s );
+
+                        s.close();
+                    }
+                }
+            }
+
+            printf("SGDem::close Level \n");
+
+            root->closeLevel( outLvl );
+        } else {
+            printf("SGDem::createLevel failed\n");
+        }
+    } else {
+        printf("SGDem::getRoot failed\n");
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/utils/fgelev/CMakeLists.txt b/utils/fgelev/CMakeLists.txt
index b3c62715d..9e729ef4c 100644
--- a/utils/fgelev/CMakeLists.txt
+++ b/utils/fgelev/CMakeLists.txt
@@ -2,6 +2,7 @@ add_executable(fgelev fgelev.cxx)
 
 target_link_libraries(fgelev
 	SimGearScene SimGearCore
+    ${GDAL_LIBRARY}
 )
 
 install(TARGETS fgelev RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/utils/fgviewer/CMakeLists.txt b/utils/fgviewer/CMakeLists.txt
index caf8b61ca..cc258b0ed 100644
--- a/utils/fgviewer/CMakeLists.txt
+++ b/utils/fgviewer/CMakeLists.txt
@@ -49,5 +49,6 @@ endif()
 target_link_libraries(fgviewer
 	SimGearScene SimGearCore
 	${FGVIEWER_RTI_LIBRARIES}
+    ${GDAL_LIBRARY}
 )
 install(TARGETS fgviewer RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})