]> git.mxchange.org Git - flightgear.git/blob - utils/fgelev/fgelev.cxx
e2f4f9f4ff3e8688369e4b81cfebe85ab66e48b0
[flightgear.git] / utils / fgelev / fgelev.cxx
1 // fgelev.cxx -- compute scenery elevation
2 //
3 // Copyright (C) 2009 - 2012  Mathias Froehlich
4 //
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.
9 //
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.
14 //
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.
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <iostream>
24 #include <cstdlib>
25 #include <iomanip>
26
27 #include <osg/ArgumentParser>
28 #include <osg/Camera>
29 #include <osg/PagedLOD>
30 #include <osg/ProxyNode>
31 #include <osg/Transform>
32 #include <osgDB/ReadFile>
33
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>
47
48 class FGSceneryIntersect : public osg::NodeVisitor {
49 public:
50     FGSceneryIntersect(const SGLineSegmentd& lineSegment) :
51         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
52         _lineSegment(lineSegment),
53         _haveHit(false)
54     { }
55
56     bool getHaveHit() const
57     { return _haveHit; }
58     const SGLineSegmentd& getLineSegment() const
59     { return _lineSegment; }
60
61     virtual void apply(osg::Node& node)
62     {
63         if (!testBoundingSphere(node.getBound()))
64             return;
65
66         addBoundingVolume(node);
67     }
68
69     virtual void apply(osg::Group& group)
70     {
71         if (!testBoundingSphere(group.getBound()))
72             return;
73
74         traverse(group);
75         addBoundingVolume(group);
76     }
77
78     virtual void apply(osg::Transform& transform)
79     { handleTransform(transform); }
80     virtual void apply(osg::Camera& camera)
81     {
82         if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
83             return;
84         handleTransform(camera);
85     }
86
87     virtual void apply(osg::ProxyNode& proxyNode)
88     {
89         unsigned numFileNames = proxyNode.getNumFileNames();
90         for (unsigned i = 0; i < numFileNames; ++i) {
91             if (i < proxyNode.getNumChildren() && proxyNode.getChild(i))
92                 continue;
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()));
97             if (!node.valid())
98                 node = new osg::Group;
99             if (i < proxyNode.getNumChildren())
100                 proxyNode.setChild(i, node);
101             else
102                 proxyNode.addChild(node);
103         }
104
105         apply(static_cast<osg::Group&>(proxyNode));
106     }
107     virtual void apply(osg::PagedLOD& pagedLOD)
108     {
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))
113                 continue;
114             range = pagedLOD.getMaxRange(i);
115         }
116
117         for (unsigned i = pagedLOD.getNumChildren(); i < numFileNames; ++i) {
118             if (i < pagedLOD.getNumChildren() && pagedLOD.getChild(i))
119                 continue;
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()));
125             }
126             if (!node.valid())
127                 node = new osg::Group;
128             pagedLOD.addChild(node.get());
129         }
130
131         apply(static_cast<osg::LOD&>(pagedLOD));
132     }
133
134 private:
135     void handleTransform(osg::Transform& transform)
136     {
137         // Hmm, may be this needs to be refined somehow ...
138         if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
139             return;
140
141         if (!testBoundingSphere(transform.getBound()))
142             return;
143
144         osg::Matrix inverseMatrix;
145         if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
146             return;
147         osg::Matrix matrix;
148         if (!transform.computeLocalToWorldMatrix(matrix, this))
149             return;
150
151         SGLineSegmentd lineSegment = _lineSegment;
152         bool haveHit = _haveHit;
153
154         _haveHit = false;
155         _lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr()));
156
157         addBoundingVolume(transform);
158         traverse(transform);
159
160         if (_haveHit) {
161             _lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr()));
162         } else {
163             _lineSegment = lineSegment;
164             _haveHit = haveHit;
165         }
166     }
167
168     simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
169     {
170         SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
171         if (!userData)
172             return 0;
173         return userData->getBVHNode();
174     }
175     void addBoundingVolume(osg::Node& node)
176     {
177         simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
178         if (!bvNode)
179             return;
180
181         // Find ground intersection on the bvh nodes
182         simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment,
183                                                           0/*startTime*/);
184         bvNode->accept(lineSegmentVisitor);
185         if (!lineSegmentVisitor.empty()) {
186             _lineSegment = lineSegmentVisitor.getLineSegment();
187             _haveHit = true;
188         }
189     }
190
191     bool testBoundingSphere(const osg::BoundingSphere& bound) const
192     {
193         if (!bound.valid())
194             return false;
195
196         SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius);
197         return intersects(_lineSegment, sphere);
198     }
199
200     SGLineSegmentd _lineSegment;
201     bool _haveHit;
202 };
203
204 int
205 main(int argc, char** argv)
206 {
207     /// Read arguments and environment variables.
208
209     // use an ArgumentParser object to manage the program arguments.
210     osg::ArgumentParser arguments(&argc, argv);
211
212     std::string fg_root;
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;
216     } else {
217         fg_root = PKGLIBDIR;
218     }
219
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;
224     } else {
225         SGPath path(fg_root);
226         path.append("Scenery");
227         fg_scenery = path.str();
228     }
229
230     SGSharedPtr<SGPropertyNode> props = new SGPropertyNode;
231     try {
232         SGPath preferencesFile = fg_root;
233         preferencesFile.append("preferences.xml");
234         readProperties(preferencesFile.str(), props);
235     } catch (...) {
236         // In case of an error, at least make summer :)
237         props->getNode("sim/startup/season", true)->setStringValue("summer");
238
239         SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear preferences.\n"
240                << "Probably FG_ROOT is not properly set.");
241     }
242
243     std::string config;
244     while (arguments.read("--config", config)) {
245         try {
246             readProperties(config, props);
247         } catch (...) {
248             SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading config file \"" << config
249                    << "\" given on the command line.");
250         }
251     }
252
253     std::string prop, value;
254     while (arguments.read("--prop", prop, value)) {
255         props->setStringValue(prop, value);
256     }
257
258     /// now set up the simgears required model stuff
259
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();
264
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");
270     try {
271         ml->load(fg_root, mpath.str(), props);
272     } catch (...) {
273         SG_LOG(SG_GENERAL, SG_ALERT, "Problems loading FlightGear materials.\n"
274                << "Probably FG_ROOT is not properly set.");
275     }
276     simgear::SGModelLib::init(fg_root, props);
277
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);
282     else
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");
290
291     // Here, all arguments are processed
292     arguments.reportRemainingOptionsAsUnrecognized();
293     arguments.writeErrorMessages(std::cerr);
294
295     /// Read the whole world paged model.
296     osg::ref_ptr<osg::Node> loadedModel;
297     loadedModel = osgDB::readNodeFile("w180s90-360x180.spt", options.get());
298
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");
303         return EXIT_FAILURE;
304     }
305
306     // now handle all the position pairs
307     for(int i = 1; i < arguments.argc(); ++i) {
308         if (arguments.isOption(i))
309             continue;
310
311         std::istringstream ss(arguments[i]);
312         double lon, lat;
313         char sep;
314         ss >> lon >> sep >> lat;
315         if (ss.fail()) {
316             return EXIT_FAILURE;
317         }
318
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);
324
325         std::cout << arguments[i] << ": ";
326         if (!intersectVisitor.getHaveHit()) {
327             std::cout << "-1000" << std::endl;
328         } else {
329             SGGeod geod = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd());
330             std::cout << std::fixed << std::setprecision(3) << geod.getElevationM() << std::endl;
331         }
332     }
333
334     return EXIT_SUCCESS;
335 }