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