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