]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/ReaderWriterSTG.cxx
stg: Add flag to load only specific parts of the stg files.
[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/material/mat.hxx>
44 #include <simgear/scene/material/matlib.hxx>
45 #include <simgear/scene/tgdb/apt_signs.hxx>
46 #include <simgear/scene/tgdb/obj.hxx>
47
48 #include "SGOceanTile.hxx"
49
50 using 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 static bool hasOptionalValue(sg_gzifstream &in)
83 {
84     while ( (in.peek() != '\n') && (in.peek() != '\r')
85             && isspace(in.peek()) ) {
86         in.get();
87     }
88     if ( isdigit(in.peek()) || (in.peek() == '-') ){
89         return true;
90     } else {
91         return false;
92     }
93 }
94
95 ReaderWriterSTG::ReaderWriterSTG()
96 {
97     supportsExtension("stg", "SimGear stg database format");
98 }
99
100 ReaderWriterSTG::~ReaderWriterSTG()
101 {
102 }
103
104 const char* ReaderWriterSTG::className() const
105 {
106     return "STG Database reader";
107 }
108
109 osgDB::ReaderWriter::ReadResult
110 ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* options) const
111 {
112     // We treat 123.stg different than ./123.stg.
113     // The difference is that ./123.stg as well as any absolute path
114     // really loads the given stg file and only this.
115     // In contrast 123.stg uses the search paths to load a set of stg
116     // files spread across the scenery directories.
117     if (osgDB::getSimpleFileName(fileName) != fileName)
118         return readStgFile(fileName, options);
119
120     // For stg meta files, we need options for the search path.
121     if (!options)
122         return ReadResult::FILE_NOT_FOUND;
123
124
125
126     SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
127
128     SGBucket bucket(bucketIndexFromFileName(fileName));
129     std::string basePath = bucket.gen_base_path();
130
131     osg::ref_ptr<osg::Group> group = new osg::Group;
132
133     // Stop scanning once an object base is found
134     bool foundBase = false;
135     // This is considered a meta file, so apply the scenery path search
136     const osgDB::FilePathList& filePathList = options->getDatabasePathList();
137     for (osgDB::FilePathList::const_iterator i = filePathList.begin();
138          i != filePathList.end() && !foundBase; ++i) {
139         SGPath objects(*i);
140         objects.append("Objects");
141         objects.append(basePath);
142         objects.append(fileName);
143         if (readStgFile(objects.str(), bucket, *group, options))
144             foundBase = true;
145         
146         SGPath terrain(*i);
147         terrain.append("Terrain");
148         terrain.append(basePath);
149         terrain.append(fileName);
150         if (readStgFile(terrain.str(), bucket, *group, options))
151             foundBase = true;
152     }
153     
154     // ... or generate an ocean tile on the fly
155     if (!foundBase) {
156         SG_LOG(SG_TERRAIN, SG_INFO, "  Generating ocean tile");
157         
158         osg::ref_ptr<SGReaderWriterOptions> opt;
159         opt = SGReaderWriterOptions::copyOrCreate(options);
160         osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
161         if ( node ) {
162             group->addChild(node);
163         } else {
164             SG_LOG( SG_TERRAIN, SG_ALERT,
165                     "Warning: failed to generate ocean tile!" );
166         }
167     }
168
169     return group.get();
170 }
171
172 osgDB::ReaderWriter::ReadResult
173 ReaderWriterSTG::readStgFile(const std::string& fileName, const osgDB::Options* options) const
174 {
175     // This is considered a real existing file.
176     // We still apply the search path algorithms for relative files.
177     osg::ref_ptr<osg::Group> group = new osg::Group;
178     std::string path = osgDB::findDataFile(fileName, options);
179     readStgFile(path, bucketIndexFromFileName(path), *group, options);
180
181     return group.get();
182 }
183
184 bool
185 ReaderWriterSTG::readStgFile(const std::string& absoluteFileName,
186                              const SGBucket& bucket,
187                              osg::Group& group, const osgDB::Options* options) const
188 {
189     if (absoluteFileName.empty())
190         return false;
191
192     sg_gzifstream in( absoluteFileName );
193     if ( !in.is_open() )
194         return false;
195
196     SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
197     
198     std::string filePath = osgDB::getFilePath(absoluteFileName);
199
200     osg::ref_ptr<SGReaderWriterOptions> staticOptions;
201     staticOptions = SGReaderWriterOptions::copyOrCreate(options);
202     staticOptions->getDatabasePathList().clear();
203     staticOptions->getDatabasePathList().push_back(filePath);
204     staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE);
205     
206     osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
207     sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
208     sharedOptions->getDatabasePathList().clear();
209     
210     SGPath path = filePath;
211     path.append(".."); path.append(".."); path.append("..");
212     sharedOptions->getDatabasePathList().push_back(path.str());
213     std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
214     sharedOptions->getDatabasePathList().push_back(fg_root);
215
216     // do only load airport btg files.
217     bool onlyAirports = options->getPluginStringData("SimGear::FG_ONLY_AIRPORTS") == "ON";
218     // do only load terrain btg files
219     bool onlyTerrain = options->getPluginStringData("SimGear::FG_ONLY_TERRAIN") == "ON";
220
221     simgear::AirportSignBuilder signBuilder(staticOptions->getMaterialLib(), bucket.get_center());
222   
223     bool has_base = false;
224     while ( ! in.eof() ) {
225         std::string token;
226         in >> token;
227         
228         // No comment
229         if ( token.empty() || token[0] == '#' ) {
230             in >> ::skipeol;
231             continue;
232         }
233         
234         // Then there is always a name
235         std::string name;
236         in >> name;
237         
238         SGPath path = filePath;
239         path.append(name);
240         
241         osg::ref_ptr<osg::Node> node;
242         if ( token == "OBJECT_BASE" ) {
243             // Load only once (first found)
244             SG_LOG( SG_TERRAIN, SG_BULK, "    " << token << " " << name );
245             
246             has_base = true;
247
248             if (!onlyAirports || isAirportBtg(name)) {
249                 node = osgDB::readRefNodeFile(path.str(),
250                                               staticOptions.get());
251                 
252                 if (!node.valid()) {
253                     SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
254                             << ": Failed to load OBJECT_BASE '"
255                             << name << "'" );
256                 }
257             }
258             
259         } else if ( token == "OBJECT" ) {
260             if (!onlyAirports || isAirportBtg(name)) {
261                 node = osgDB::readRefNodeFile(path.str(),
262                                               staticOptions.get());
263                 
264                 if (!node.valid()) {
265                     SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
266                             << ": Failed to load OBJECT '"
267                             << name << "'" );
268                 }
269             }
270             
271         } else {
272             double lon, lat, elev, hdg;
273             in >> lon >> lat >> elev >> hdg;
274             
275             // Always OK to load
276             if ( token == "OBJECT_STATIC" ) {
277                 if (!onlyTerrain) {
278                     osg::ref_ptr<SGReaderWriterOptions> opt;
279                     opt = new SGReaderWriterOptions(*staticOptions);
280                     osg::ProxyNode* proxyNode = new osg::ProxyNode;
281                     proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
282                     /// Hmm, the findDataFile should happen downstream
283                     std::string absName = osgDB::findDataFile(name, opt.get());
284                     proxyNode->setFileName(0, absName);
285                     if (SGPath(absName).lower_extension() == "ac")
286                     {
287                         proxyNode->setNodeMask( ~simgear::MODELLIGHT_BIT );
288                         opt->setInstantiateEffects(true);
289                     }
290                     else
291                         opt->setInstantiateEffects(false);
292                     proxyNode->setDatabaseOptions(opt.get());
293                     node = proxyNode;
294                     
295                     if (!node.valid()) {
296                         SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
297                                 << ": Failed to load OBJECT_STATIC '"
298                                 << name << "'" );
299                     }
300                 }
301                 
302             } else if ( token == "OBJECT_SHARED" ) {
303                 if (!onlyTerrain) {
304                     osg::ref_ptr<SGReaderWriterOptions> opt;
305                     opt = new SGReaderWriterOptions(*sharedOptions);
306                     /// Hmm, the findDataFile should happen in the downstream readers
307                     std::string absName = osgDB::findDataFile(name, opt.get());
308                     if (SGPath(absName).lower_extension() == "ac")
309                         opt->setInstantiateEffects(true);
310                     else
311                         opt->setInstantiateEffects(false);
312                     node = osgDB::readRefNodeFile(absName, opt.get());
313                     if (SGPath(absName).lower_extension() == "ac")
314                         node->setNodeMask( ~simgear::MODELLIGHT_BIT );
315                     
316                     if (!node.valid()) {
317                         SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
318                                 << ": Failed to load OBJECT_SHARED '"
319                                 << name << "'" );
320                     }
321                 }
322                 
323             } else if ( token == "OBJECT_SIGN" ) {
324                 int size(-1);
325
326                 if ( hasOptionalValue(in) ){
327                     in >> size;
328                 }
329                 if (!onlyTerrain)
330                     signBuilder.addSign(SGGeod::fromDegM(lon, lat, elev), hdg, name, size);
331             } else {
332                 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
333                         << ": Unknown token '" << token << "'" );
334             }
335             
336             if (node.valid()) {
337                 osg::Matrix matrix;
338                 matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
339                 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
340                                                osg::Vec3(0, 0, 1)));
341
342                 if ( hasOptionalValue(in) ){
343                     double pitch(0.0), roll(0.0);
344                     in >> pitch >> roll;
345
346                     matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(pitch),
347                                                    osg::Vec3(0, 1, 0)));
348                     matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(roll),
349                                                    osg::Vec3(1, 0, 0)));
350                 }
351
352                 osg::MatrixTransform* matrixTransform;
353                 matrixTransform = new osg::MatrixTransform(matrix);
354                 matrixTransform->setDataVariance(osg::Object::STATIC);
355                 matrixTransform->addChild(node.get());
356                 node = matrixTransform;
357             }
358         }
359         
360         if (node.valid())
361             group.addChild(node.get());
362         
363         in >> ::skipeol;
364     }
365   
366     if (signBuilder.getSignsGroup())
367         group.addChild(signBuilder.getSignsGroup());
368
369     return has_base;
370 }