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