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})