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