1 // fgelev.cxx -- compute scenery elevation
3 // Copyright (C) 2009 - 2012 Mathias Froehlich
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <osg/ArgumentParser>
29 #include <osg/PagedLOD>
30 #include <osg/ProxyNode>
31 #include <osg/Transform>
32 #include <osgDB/ReadFile>
34 #include <simgear/props/props.hxx>
35 #include <simgear/props/props_io.hxx>
36 #include <simgear/scene/material/matlib.hxx>
37 #include <simgear/scene/util/SGNodeMasks.hxx>
38 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
39 #include <simgear/scene/util/SGSceneFeatures.hxx>
40 #include <simgear/scene/util/SGSceneUserData.hxx>
41 #include <simgear/scene/tgdb/userdata.hxx>
42 #include <simgear/scene/model/ModelRegistry.hxx>
43 #include <simgear/misc/ResourceManager.hxx>
44 #include <simgear/scene/bvh/BVHNode.hxx>
45 #include <simgear/scene/bvh/BVHLineSegmentVisitor.hxx>
46 #include <simgear/scene/util/OsgMath.hxx>
48 class FGSceneryIntersect : public osg::NodeVisitor {
50 FGSceneryIntersect(const SGLineSegmentd& lineSegment) :
51 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
52 _lineSegment(lineSegment),
56 bool getHaveHit() const
58 const SGLineSegmentd& getLineSegment() const
59 { return _lineSegment; }
61 virtual void apply(osg::Node& node)
63 if (!testBoundingSphere(node.getBound()))
66 addBoundingVolume(node);
69 virtual void apply(osg::Group& group)
71 if (!testBoundingSphere(group.getBound()))
75 addBoundingVolume(group);
78 virtual void apply(osg::Transform& transform)
79 { handleTransform(transform); }
80 virtual void apply(osg::Camera& camera)
82 if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
84 handleTransform(camera);
87 virtual void apply(osg::ProxyNode& proxyNode)
89 unsigned numFileNames = proxyNode.getNumFileNames();
90 for (unsigned i = 0; i < numFileNames; ++i) {
91 if (i < proxyNode.getNumChildren() && proxyNode.getChild(i))
93 // FIXME evaluate pagedLOD.getDatabasePath()
94 osg::ref_ptr<osg::Node> node;
95 node = osgDB::readNodeFile(proxyNode.getFileName(i),
96 static_cast<const osgDB::Options*>(proxyNode.getDatabaseOptions()));
98 node = new osg::Group;
99 if (i < proxyNode.getNumChildren())
100 proxyNode.setChild(i, node);
102 proxyNode.addChild(node);
105 apply(static_cast<osg::Group&>(proxyNode));
107 virtual void apply(osg::PagedLOD& pagedLOD)
109 float range = std::numeric_limits<float>::max();
110 unsigned numFileNames = pagedLOD.getNumFileNames();
111 for (unsigned i = 0; i < numFileNames; ++i) {
112 if (range < pagedLOD.getMaxRange(i))
114 range = pagedLOD.getMaxRange(i);
117 for (unsigned i = pagedLOD.getNumChildren(); i < numFileNames; ++i) {
118 if (i < pagedLOD.getNumChildren() && pagedLOD.getChild(i))
120 osg::ref_ptr<osg::Node> node;
121 if (pagedLOD.getMaxRange(i) <= range) {
122 // FIXME evaluate pagedLOD.getDatabasePath()
123 node = osgDB::readNodeFile(pagedLOD.getFileName(i),
124 static_cast<const osgDB::Options*>(pagedLOD.getDatabaseOptions()));
127 node = new osg::Group;
128 pagedLOD.addChild(node.get());
131 apply(static_cast<osg::LOD&>(pagedLOD));
135 void handleTransform(osg::Transform& transform)
137 // Hmm, may be this needs to be refined somehow ...
138 if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
141 if (!testBoundingSphere(transform.getBound()))
144 osg::Matrix inverseMatrix;
145 if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
148 if (!transform.computeLocalToWorldMatrix(matrix, this))
151 SGLineSegmentd lineSegment = _lineSegment;
152 bool haveHit = _haveHit;
155 _lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr()));
157 addBoundingVolume(transform);
161 _lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr()));
163 _lineSegment = lineSegment;
168 simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
170 SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
173 return userData->getBVHNode();
175 void addBoundingVolume(osg::Node& node)
177 simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
181 // Find ground intersection on the bvh nodes
182 simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment,
184 bvNode->accept(lineSegmentVisitor);
185 if (!lineSegmentVisitor.empty()) {
186 _lineSegment = lineSegmentVisitor.getLineSegment();
191 bool testBoundingSphere(const osg::BoundingSphere& bound) const
196 SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius);
197 return intersects(_lineSegment, sphere);
200 SGLineSegmentd _lineSegment;
205 main(int argc, char** argv)
207 /// Read arguments and environment variables.
209 // use an ArgumentParser object to manage the program arguments.
210 osg::ArgumentParser arguments(&argc, argv);
213 if (arguments.read("--fg-root", fg_root)) {
214 } else if (const char *fg_root_env = std::getenv("FG_ROOT")) {
215 fg_root = fg_root_env;
220 std::string fg_scenery;
221 if (arguments.read("--fg-scenery", fg_scenery)) {
222 } else if (const char *fg_scenery_env = std::getenv("FG_SCENERY")) {
223 fg_scenery = fg_scenery_env;
225 SGPath path(fg_root);
226 path.append("Scenery");
227 fg_scenery = path.str();
230 SGSharedPtr<SGPropertyNode> props = new SGPropertyNode;
232 SGPath preferencesFile = fg_root;
233 preferencesFile.append("preferences.xml");
234 readProperties(preferencesFile.str(), props);
236 // In case of an error, at least make summer :)
237 props->getNode("sim/startup/season", true)->setStringValue("summer");
239 SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear preferences.\n"
240 << "Probably FG_ROOT is not properly set.");
244 while (arguments.read("--config", config)) {
246 readProperties(config, props);
248 SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading config file \"" << config
249 << "\" given on the command line.");
253 std::string prop, value;
254 while (arguments.read("--prop", prop, value)) {
255 props->setStringValue(prop, value);
258 /// now set up the simgears required model stuff
260 simgear::ResourceManager::instance()->addBasePath(fg_root, simgear::ResourceManager::PRIORITY_DEFAULT);
261 // Just reference simgears reader writer stuff so that the globals get
262 // pulled in by the linker ...
263 simgear::ModelRegistry::instance();
265 sgUserDataInit(props.get());
266 SGSceneFeatures::instance()->setTextureCompression(SGSceneFeatures::DoNotUseCompression);
267 SGMaterialLib* ml = new SGMaterialLib;
268 SGPath mpath(fg_root);
269 mpath.append("Materials/default/materials.xml");
271 ml->load(fg_root, mpath.str(), props);
273 SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear materials.\n"
274 << "Probably FG_ROOT is not properly set.");
276 simgear::SGModelLib::init(fg_root, props);
278 // Set up the reader/writer options
279 osg::ref_ptr<simgear::SGReaderWriterOptions> options;
280 if (osgDB::Options* ropt = osgDB::Registry::instance()->getOptions())
281 options = new simgear::SGReaderWriterOptions(*ropt);
283 options = new simgear::SGReaderWriterOptions;
284 osgDB::convertStringPathIntoFilePathList(fg_scenery,
285 options->getDatabasePathList());
286 options->setMaterialLib(ml);
287 options->setPropertyNode(props);
288 options->setPluginStringData("SimGear::FG_ROOT", fg_root);
289 options->setPluginStringData("SimGear::BOUNDINGVOLUMES", "ON");
291 // Here, all arguments are processed
292 arguments.reportRemainingOptionsAsUnrecognized();
293 arguments.writeErrorMessages(std::cerr);
295 /// Read the whole world paged model.
296 osg::ref_ptr<osg::Node> loadedModel;
297 loadedModel = osgDB::readNodeFile("w180s90-360x180.spt", options.get());
299 // if no model has been successfully loaded report failure.
300 if (!loadedModel.valid()) {
301 SG_LOG(SG_GENERAL, SG_ALERT, arguments.getApplicationName()
302 << ": No data loaded");
306 // now handle all the position pairs
307 for(int i = 1; i < arguments.argc(); ++i) {
308 if (arguments.isOption(i))
311 std::istringstream ss(arguments[i]);
314 ss >> lon >> sep >> lat;
319 SGVec3d start = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, 10000));
320 SGVec3d end = SGVec3d::fromGeod(SGGeod::fromDegM(lon, lat, -1000));
321 FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end));
322 intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
323 loadedModel->accept(intersectVisitor);
325 std::cout << arguments[i] << ": ";
326 if (!intersectVisitor.getHaveHit()) {
327 std::cout << "-1000" << std::endl;
329 SGGeod geod = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd());
330 std::cout << std::fixed << std::setprecision(3) << geod.getElevationM() << std::endl;