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