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