]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/ReaderWriterSTG.cxx
pt_lights: add some randomness to the blinking interval of the hold short lights
[simgear.git] / simgear / scene / tgdb / ReaderWriterSTG.cxx
1 // tileentry.cxx -- routines to handle a scenery tile
2 //
3 // Written by Curtis Olson, started May 1998.
4 //
5 // Copyright (C) 1998 - 2001  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21
22 #ifdef HAVE_CONFIG_H
23 #  include <simgear_config.h>
24 #endif
25
26 #include "ReaderWriterSTG.hxx"
27
28 #include <osg/MatrixTransform>
29 #include <osg/ProxyNode>
30 #include <osgUtil/LineSegmentIntersector>
31 #include <osgUtil/IntersectionVisitor>
32
33 #include <osgDB/FileNameUtils>
34 #include <osgDB/FileUtils>
35 #include <osgDB/Registry>
36 #include <osgDB/ReaderWriter>
37 #include <osgDB/ReadFile>
38
39 #include <simgear/bucket/newbucket.hxx>
40 #include <simgear/debug/logstream.hxx>
41 #include <simgear/misc/sgstream.hxx>
42 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
43 #include <simgear/scene/util/RenderConstants.hxx>
44 #include <simgear/scene/util/OsgMath.hxx>
45 #include <simgear/scene/tgdb/apt_signs.hxx>
46 #include <simgear/scene/tgdb/obj.hxx>
47
48 #include "SGOceanTile.hxx"
49
50 namespace simgear {
51
52 /// Ok, this is a hack - we do not exactly know if it's an airport or not.
53 /// This feature might also vanish again later. This is currently to
54 /// support testing an external ai component that just loads the the airports
55 /// and supports ground queries on only these areas.
56 static bool isAirportBtg(const std::string& name)
57 {
58     if (name.size() < 8)
59         return false;
60     if (name.substr(4, 8) != ".btg")
61         return false;
62     for (unsigned i = 0; i < 4; ++i) {
63         if (name[i] < 'A' || 'Z' < name[i])
64             continue;
65         return true;
66     }
67     return false;
68 }
69
70 static SGBucket bucketIndexFromFileName(const std::string& fileName)
71 {
72   // Extract the bucket from the filename
73   std::istringstream ss(osgDB::getNameLessExtension(fileName));
74   long index;
75   ss >> index;
76   if (ss.fail())
77     return SGBucket();
78   
79   return SGBucket(index);
80 }
81
82 struct ReaderWriterSTG::_ModelBin {
83     struct _Object {
84         std::string _errorLocation;
85         std::string _token;
86         std::string _name;
87         osg::ref_ptr<SGReaderWriterOptions> _options;
88     };
89     struct _ObjectStatic {
90         _ObjectStatic() : _agl(false), _proxy(false), _lon(0), _lat(0), _elev(0), _hdg(0), _pitch(0), _roll(0) { }
91         std::string _errorLocation;
92         std::string _token;
93         std::string _name;
94         bool _agl;
95         bool _proxy;
96         double _lon, _lat, _elev;
97         double _hdg, _pitch, _roll;
98         osg::ref_ptr<SGReaderWriterOptions> _options;
99     };
100     struct _Sign {
101         _Sign() : _agl(false), _lon(0), _lat(0), _elev(0), _hdg(0), _size(-1) { }
102         std::string _errorLocation;
103         std::string _token;
104         std::string _name;
105         bool _agl;
106         double _lon, _lat, _elev;
107         double _hdg;
108         int _size;
109     };
110
111     _ModelBin() :
112         _foundBase(false)
113     { }
114
115     SGReaderWriterOptions* sharedOptions(const std::string& filePath, const osgDB::Options* options)
116     {
117         osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
118         sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
119         sharedOptions->getDatabasePathList().clear();
120
121         SGPath path = filePath;
122         path.append(".."); path.append(".."); path.append("..");
123         sharedOptions->getDatabasePathList().push_back(path.str());
124         std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
125         sharedOptions->getDatabasePathList().push_back(fg_root);
126
127         return sharedOptions.release();
128     }
129     SGReaderWriterOptions* staticOptions(const std::string& filePath, const osgDB::Options* options)
130     {
131         osg::ref_ptr<SGReaderWriterOptions> staticOptions;
132         staticOptions = SGReaderWriterOptions::copyOrCreate(options);
133         staticOptions->getDatabasePathList().clear();
134
135         staticOptions->getDatabasePathList().push_back(filePath);
136         staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE);
137
138         return staticOptions.release();
139     }
140
141     double elevation(osg::Group& group, const SGGeod& geod)
142     {
143         SGVec3d start = SGVec3d::fromGeod(SGGeod::fromGeodM(geod, 10000));
144         SGVec3d end = SGVec3d::fromGeod(SGGeod::fromGeodM(geod, -1000));
145         
146         osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector;
147         intersector = new osgUtil::LineSegmentIntersector(toOsg(start), toOsg(end));
148         osgUtil::IntersectionVisitor visitor(intersector.get());
149         group.accept(visitor);
150         
151         if (!intersector->containsIntersections())
152             return 0;
153         
154         SGVec3d cart = toSG(intersector->getFirstIntersection().getWorldIntersectPoint());
155         return SGGeod::fromCart(cart).getElevationM();
156     }
157     
158     bool read(const std::string& absoluteFileName, const osgDB::Options* options)
159     {
160         if (absoluteFileName.empty())
161             return false;
162
163         sg_gzifstream stream(absoluteFileName);
164         if (!stream.is_open())
165             return false;
166
167         SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
168     
169         std::string filePath = osgDB::getFilePath(absoluteFileName);
170
171         // do only load airport btg files.
172         bool onlyAirports = options->getPluginStringData("SimGear::FG_ONLY_AIRPORTS") == "ON";
173         // do only load terrain btg files
174         bool onlyTerrain = options->getPluginStringData("SimGear::FG_ONLY_TERRAIN") == "ON";
175         
176         while (!stream.eof()) {
177             // read a line
178             std::string line;
179             std::getline(stream, line);
180             
181             // strip comments
182             std::string::size_type hash_pos = line.find('#');
183             if (hash_pos != std::string::npos)
184                 line.resize(hash_pos);
185             
186             // and process further
187             std::stringstream in(line);
188             
189             std::string token;
190             in >> token;
191             
192             // No comment
193             if (token.empty())
194                 continue;
195             
196             // Then there is always a name
197             std::string name;
198             in >> name;
199             
200             SGPath path = filePath;
201             path.append(name);
202             
203             if (token == "OBJECT_BASE") {
204                 // Load only once (first found)
205                 SG_LOG( SG_TERRAIN, SG_BULK, "    " << token << " " << name );
206                 _foundBase = true;
207                 if (!onlyAirports || isAirportBtg(name)) {
208                     _Object obj;
209                     obj._errorLocation = absoluteFileName;
210                     obj._token = token;
211                     obj._name = path.str();
212                     obj._options = staticOptions(filePath, options);
213                     _objectList.push_back(obj);
214                 }
215                 
216             } else if (token == "OBJECT") {
217                 if (!onlyAirports || isAirportBtg(name)) {
218                     _Object obj;
219                     obj._errorLocation = absoluteFileName;
220                     obj._token = token;
221                     obj._name = path.str();
222                     obj._options = staticOptions(filePath, options);
223                     _objectList.push_back(obj);
224                 }
225                 
226             } else {
227                 // Always OK to load
228                 if (token == "OBJECT_STATIC" || token == "OBJECT_STATIC_AGL") {
229                     if (!onlyTerrain) {
230                         osg::ref_ptr<SGReaderWriterOptions> opt;
231                         opt = staticOptions(filePath, options);
232                         if (SGPath(name).lower_extension() == "ac")
233                             opt->setInstantiateEffects(true);
234                         else
235                             opt->setInstantiateEffects(false);
236                         _ObjectStatic obj;
237                         obj._errorLocation = absoluteFileName;
238                         obj._token = token;
239                         obj._name = name;
240                         obj._agl = (token == "OBJECT_STATIC_AGL");
241                         obj._proxy = true;
242                         in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg >> obj._pitch >> obj._roll;
243                         obj._options = opt;
244                         _objectStaticList.push_back(obj);
245                     }
246                         
247                 } else if (token == "OBJECT_SHARED" || token == "OBJECT_SHARED_AGL") {
248                     if (!onlyTerrain) {
249                         osg::ref_ptr<SGReaderWriterOptions> opt;
250                         opt = staticOptions(filePath, options);
251                         if (SGPath(name).lower_extension() == "ac")
252                             opt->setInstantiateEffects(true);
253                         else
254                             opt->setInstantiateEffects(false);
255                         _ObjectStatic obj;
256                         obj._errorLocation = absoluteFileName;
257                         obj._token = token;
258                         obj._name = name;
259                         obj._agl = (token == "OBJECT_SHARED_AGL");
260                         obj._proxy = false;
261                         in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg >> obj._pitch >> obj._roll;
262                         obj._options = opt;
263                         _objectStaticList.push_back(obj);
264                     }
265
266                 } else if (token == "OBJECT_SIGN" || token == "OBJECT_SIGN_AGL") {
267                     if (!onlyTerrain) {
268                         _Sign sign;
269                         sign._token = token;
270                         sign._name = name;
271                         sign._agl = (token == "OBJECT_SIGN_AGL");
272                         in >> sign._lon >> sign._lat >> sign._elev >> sign._hdg >> sign._size;
273                         _signList.push_back(sign);
274                     }
275
276                 } else {
277                     SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
278                             << ": Unknown token '" << token << "'" );
279                 }
280             }
281         }
282         
283         return true;
284     }
285     
286     osg::Node* load(const SGBucket& bucket, const osgDB::Options* opt)
287     {
288         osg::ref_ptr<SGReaderWriterOptions> options;
289         options = SGReaderWriterOptions::copyOrCreate(opt);
290
291         osg::ref_ptr<osg::Group> group = new osg::Group;
292         group->setDataVariance(osg::Object::STATIC);
293
294         if (_foundBase) {
295             for (std::list<_Object>::iterator i = _objectList.begin(); i != _objectList.end(); ++i) {
296                 osg::ref_ptr<osg::Node> node;
297                 node = osgDB::readRefNodeFile(i->_name, i->_options.get());
298                 if (!node.valid()) {
299                     SG_LOG(SG_TERRAIN, SG_ALERT, i->_errorLocation << ": Failed to load "
300                            << i->_token << " '" << i->_name << "'");
301                     continue;
302                 }
303                 group->addChild(node.get());
304             }
305         } else {
306             SG_LOG(SG_TERRAIN, SG_INFO, "  Generating ocean tile");
307             
308             osg::Node* node = SGOceanTile(bucket, options->getMaterialLib());
309             if (node) {
310                 group->addChild(node);
311             } else {
312                 SG_LOG( SG_TERRAIN, SG_ALERT,
313                         "Warning: failed to generate ocean tile!" );
314             }
315         }
316
317         for (std::list<_ObjectStatic>::iterator i = _objectStaticList.begin(); i != _objectStaticList.end(); ++i) {
318             if (!i->_agl)
319                 continue;
320             i->_elev += elevation(*group, SGGeod::fromDeg(i->_lon, i->_lat));
321         }
322         
323         for (std::list<_Sign>::iterator i = _signList.begin(); i != _signList.end(); ++i) {
324             if (!i->_agl)
325                 continue;
326             i->_elev += elevation(*group, SGGeod::fromDeg(i->_lon, i->_lat));
327         }
328         
329         for (std::list<_ObjectStatic>::iterator i = _objectStaticList.begin(); i != _objectStaticList.end(); ++i) {
330             osg::ref_ptr<osg::Node> node;
331             if (i->_proxy)  {
332                 osg::ref_ptr<osg::ProxyNode> proxy = new osg::ProxyNode;
333                 proxy->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
334                 proxy->setFileName(0, i->_name);
335                 proxy->setDatabaseOptions(i->_options.get());
336                 node = proxy;
337             } else {
338                 node = osgDB::readRefNodeFile(i->_name, i->_options.get());
339                 if (!node.valid()) {
340                     SG_LOG(SG_TERRAIN, SG_ALERT, i->_errorLocation << ": Failed to load "
341                            << i->_token << " '" << i->_name << "'");
342                     continue;
343                 }
344             }
345             if (SGPath(i->_name).lower_extension() == "ac")
346                 node->setNodeMask(~simgear::MODELLIGHT_BIT);
347             
348             osg::Matrix matrix;
349             matrix = makeZUpFrame(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev));
350             matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_hdg), osg::Vec3(0, 0, 1)));
351             matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_pitch), osg::Vec3(0, 1, 0)));
352             matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_roll), osg::Vec3(1, 0, 0)));
353             
354             osg::MatrixTransform* matrixTransform;
355             matrixTransform = new osg::MatrixTransform(matrix);
356             matrixTransform->setDataVariance(osg::Object::STATIC);
357             matrixTransform->addChild(node.get());
358             group->addChild(matrixTransform);
359         }
360         
361         simgear::AirportSignBuilder signBuilder(options->getMaterialLib(), bucket.get_center());
362         for (std::list<_Sign>::iterator i = _signList.begin(); i != _signList.end(); ++i)
363             signBuilder.addSign(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev), i->_hdg, i->_name, i->_size);
364         if (signBuilder.getSignsGroup())
365             group->addChild(signBuilder.getSignsGroup());
366         
367         return group.release();
368     }
369         
370     bool _foundBase;
371     std::list<_Object> _objectList;
372     std::list<_ObjectStatic> _objectStaticList;
373     std::list<_Sign> _signList;
374 };
375
376 ReaderWriterSTG::ReaderWriterSTG()
377 {
378     supportsExtension("stg", "SimGear stg database format");
379 }
380
381 ReaderWriterSTG::~ReaderWriterSTG()
382 {
383 }
384
385 const char* ReaderWriterSTG::className() const
386 {
387     return "STG Database reader";
388 }
389
390 osgDB::ReaderWriter::ReadResult
391 ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* options) const
392 {
393     _ModelBin modelBin;
394     SGBucket bucket(bucketIndexFromFileName(fileName));
395
396     // We treat 123.stg different than ./123.stg.
397     // The difference is that ./123.stg as well as any absolute path
398     // really loads the given stg file and only this.
399     // In contrast 123.stg uses the search paths to load a set of stg
400     // files spread across the scenery directories.
401     if (osgDB::getSimpleFileName(fileName) != fileName) {
402         if (!modelBin.read(fileName, options))
403             return ReadResult::FILE_NOT_FOUND;
404     } else {
405         // For stg meta files, we need options for the search path.
406         if (!options)
407             return ReadResult::FILE_NOT_FOUND;
408         
409         SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
410         
411         std::string basePath = bucket.gen_base_path();
412         
413         // Stop scanning once an object base is found
414         // This is considered a meta file, so apply the scenery path search
415         const osgDB::FilePathList& filePathList = options->getDatabasePathList();
416         for (osgDB::FilePathList::const_iterator i = filePathList.begin();
417              i != filePathList.end() && !modelBin._foundBase; ++i) {
418             SGPath objects(*i);
419             objects.append("Objects");
420             objects.append(basePath);
421             objects.append(fileName);
422             modelBin.read(objects.str(), options);
423             
424             SGPath terrain(*i);
425             terrain.append("Terrain");
426             terrain.append(basePath);
427             terrain.append(fileName);
428             modelBin.read(terrain.str(), options);
429         }
430     }
431
432     return modelBin.load(bucket, options);
433 }
434
435 }