diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 42422971c..70d89a487 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(TerraSync) add_subdirectory(fgviewer) +add_subdirectory(fgelev) add_subdirectory(GPSsmooth) if (FLTK_FOUND) diff --git a/utils/fgelev/CMakeLists.txt b/utils/fgelev/CMakeLists.txt new file mode 100644 index 000000000..90b52c001 --- /dev/null +++ b/utils/fgelev/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(fgelev fgelev.cxx) + +target_link_libraries(fgelev + ${SIMGEAR_LIBRARIES} + ${OPENSCENEGRAPH_LIBRARIES} + ${OPENGL_LIBRARIES} + ${SIMGEAR_CORE_LIBRARY_DEPENDENCIES} +) + +install(TARGETS fgelev RUNTIME DESTINATION bin) diff --git a/utils/fgelev/fgelev.cxx b/utils/fgelev/fgelev.cxx new file mode 100644 index 000000000..a4e2a714b --- /dev/null +++ b/utils/fgelev/fgelev.cxx @@ -0,0 +1,334 @@ +// fgelev.cxx -- compute scenery elevation +// +// Copyright (C) 2009 - 2012 Mathias Froehlich +// +// 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 +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class FGSceneryIntersect : public osg::NodeVisitor { +public: + FGSceneryIntersect(const SGLineSegmentd& lineSegment) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN), + _lineSegment(lineSegment), + _haveHit(false) + { } + + bool getHaveHit() const + { return _haveHit; } + const SGLineSegmentd& getLineSegment() const + { return _lineSegment; } + + virtual void apply(osg::Node& node) + { + if (!testBoundingSphere(node.getBound())) + return; + + addBoundingVolume(node); + } + + virtual void apply(osg::Group& group) + { + 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::ProxyNode& proxyNode) + { + unsigned numFileNames = proxyNode.getNumFileNames(); + for (unsigned i = 0; i < numFileNames; ++i) { + if (i < proxyNode.getNumChildren() && proxyNode.getChild(i)) + continue; + // FIXME evaluate pagedLOD.getDatabasePath() + osg::ref_ptr node; + node = osgDB::readNodeFile(proxyNode.getFileName(i), + static_cast(proxyNode.getDatabaseOptions())); + if (!node.valid()) + node = new osg::Group; + if (i < proxyNode.getNumChildren()) + proxyNode.setChild(i, node); + else + proxyNode.addChild(node); + } + + apply(static_cast(proxyNode)); + } + virtual void apply(osg::PagedLOD& pagedLOD) + { + float range = std::numeric_limits::max(); + unsigned numFileNames = pagedLOD.getNumFileNames(); + for (unsigned i = 0; i < numFileNames; ++i) { + if (range < pagedLOD.getMaxRange(i)) + continue; + range = pagedLOD.getMaxRange(i); + } + + for (unsigned i = pagedLOD.getNumChildren(); i < numFileNames; ++i) { + if (i < pagedLOD.getNumChildren() && pagedLOD.getChild(i)) + continue; + osg::ref_ptr node; + if (pagedLOD.getMaxRange(i) <= range) { + // FIXME evaluate pagedLOD.getDatabasePath() + node = osgDB::readNodeFile(pagedLOD.getFileName(i), + static_cast(pagedLOD.getDatabaseOptions())); + } + if (!node.valid()) + node = new osg::Group; + pagedLOD.addChild(node.get()); + } + + apply(static_cast(pagedLOD)); + } + +private: + void handleTransform(osg::Transform& transform) + { + // 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; + + _haveHit = false; + _lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr())); + + addBoundingVolume(transform); + traverse(transform); + + if (_haveHit) { + _lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr())); + } else { + _lineSegment = lineSegment; + _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(); + _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; + bool _haveHit; +}; + +int +main(int argc, char** argv) +{ + /// Read arguments and environment variables. + + // use an ArgumentParser object to manage the program arguments. + osg::ArgumentParser arguments(&argc, argv); + + std::string fg_root; + if (arguments.read("--fg-root", fg_root)) { + } else if (const char *fg_root_env = std::getenv("FG_ROOT")) { + fg_root = fg_root_env; + } else { + fg_root = PKGLIBDIR; + } + + std::string fg_scenery; + if (arguments.read("--fg-scenery", fg_scenery)) { + } else if (const char *fg_scenery_env = std::getenv("FG_SCENERY")) { + fg_scenery = fg_scenery_env; + } else { + SGPath path(fg_root); + path.append("Scenery"); + fg_scenery = path.str(); + } + + SGSharedPtr props = new SGPropertyNode; + try { + SGPath preferencesFile = fg_root; + preferencesFile.append("preferences.xml"); + readProperties(preferencesFile.str(), props); + } catch (...) { + // In case of an error, at least make summer :) + props->getNode("sim/startup/season", true)->setStringValue("summer"); + + SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear preferences.\n" + << "Probably FG_ROOT is not properly set."); + } + + std::string config; + while (arguments.read("--config", config)) { + try { + readProperties(config, props); + } catch (...) { + SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading config file \"" << config + << "\" given on the command line."); + } + } + + std::string prop, value; + while (arguments.read("--prop", prop, value)) { + props->setStringValue(prop, value); + } + + /// now set up the simgears required model stuff + + simgear::ResourceManager::instance()->addBasePath(fg_root, simgear::ResourceManager::PRIORITY_DEFAULT); + // Just reference simgears reader writer stuff so that the globals get + // pulled in by the linker ... + simgear::ModelRegistry::instance(); + + sgUserDataInit(props.get()); + SGSceneFeatures::instance()->setTextureCompression(SGSceneFeatures::DoNotUseCompression); + SGMaterialLib* ml = new SGMaterialLib; + SGPath mpath(fg_root); + mpath.append("Materials/default/materials.xml"); + try { + ml->load(fg_root, mpath.str(), props); + } catch (...) { + SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear materials.\n" + << "Probably FG_ROOT is not properly set."); + } + simgear::SGModelLib::init(fg_root, props); + + // Set up the reader/writer options + osg::ref_ptr options; + if (osgDB::Options* ropt = osgDB::Registry::instance()->getOptions()) + options = new simgear::SGReaderWriterOptions(*ropt); + else + options = new simgear::SGReaderWriterOptions; + osgDB::convertStringPathIntoFilePathList(fg_scenery, + options->getDatabasePathList()); + options->setMaterialLib(ml); + options->setPropertyNode(props); + options->setPluginStringData("SimGear::FG_ROOT", fg_root); + options->setPluginStringData("SimGear::BOUNDINGVOLUMES", "ON"); + + // Here, all arguments are processed + arguments.reportRemainingOptionsAsUnrecognized(); + arguments.writeErrorMessages(std::cerr); + + /// Read the whole world paged model. + osg::ref_ptr loadedModel; + loadedModel = osgDB::readNodeFile("w180s90-360x180.spt", options.get()); + + // if no model has been successfully loaded report failure. + if (!loadedModel.valid()) { + SG_LOG(SG_GENERAL, SG_ALERT, arguments.getApplicationName() + << ": No data loaded"); + return EXIT_FAILURE; + } + + // now handle all the position pairs + for(int i = 1; i < arguments.argc(); ++i) { + if (arguments.isOption(i)) + continue; + + std::istringstream ss(arguments[i]); + double lon, lat; + char sep; + ss >> lon >> sep >> lat; + if (ss.fail()) { + return EXIT_FAILURE; + } + + SGVec3d start = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, 10000)); + SGVec3d end = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, -1000)); + FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end)); + intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT); + loadedModel->accept(intersectVisitor); + + std::cout << arguments[i] << ": "; + if (!intersectVisitor.getHaveHit()) { + std::cout << "-1000" << std::endl; + } else { + SGGeod geod = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd()); + std::cout << std::fixed << std::setprecision(3) << geod.getElevationM() << std::endl; + } + } + + return EXIT_SUCCESS; +}