1 // tileentry.cxx -- routines to handle a scenery tile
3 // Written by Curtis Olson, started May 1998.
5 // Copyright (C) 1998 - 2001 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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.
23 # include <simgear_config.h>
26 #include "ReaderWriterSTG.hxx"
28 #include <osg/MatrixTransform>
29 #include <osg/ProxyNode>
31 #include <osgDB/FileNameUtils>
32 #include <osgDB/FileUtils>
33 #include <osgDB/Registry>
34 #include <osgDB/ReaderWriter>
35 #include <osgDB/ReadFile>
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>
46 #include "SGOceanTile.hxx"
48 using namespace simgear;
50 static SGBucket getBucketFromFileName(const std::string& fileName)
52 std::istringstream ss(osgDB::getNameLessExtension(fileName));
57 return SGBucket(index);
61 loadStgFile(const std::string& absoluteFileName, osg::Group& group, const osgDB::Options* options)
63 if (absoluteFileName.empty())
66 sg_gzifstream in( absoluteFileName );
70 SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
72 std::string filePath = osgDB::getFilePath(absoluteFileName);
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);
80 osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
81 sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
82 sharedOptions->getDatabasePathList().clear();
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);
90 bool has_base = false;
91 while ( ! in.eof() ) {
96 if ( token.empty() || token[0] == '#' ) {
101 // Then there is always a name
105 SGPath path = filePath;
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 );
114 node = osgDB::readRefNodeFile(path.str(),
115 staticOptions.get());
118 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
119 << ": Failed to load OBJECT_BASE '"
123 } else if ( token == "OBJECT" ) {
124 node = osgDB::readRefNodeFile(path.str(),
125 staticOptions.get());
128 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
129 << ": Failed to load OBJECT '"
134 double lon, lat, elev, hdg;
135 in >> lon >> lat >> elev >> hdg;
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);
147 opt->setInstantiateEffects(false);
148 node = osgDB::readRefNodeFile(absName, opt.get());
151 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
152 << ": Failed to load OBJECT_STATIC '"
156 } else if ( token == "OBJECT_SHARED" ) {
157 osg::ref_ptr<SGReaderWriterOptions> opt;
158 opt = new SGReaderWriterOptions(*sharedOptions);
160 /// Hmm, the findDataFile should happen in the downstream readers
161 std::string absName = osgDB::findDataFile(name, opt.get());
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);
169 opt->setInstantiateEffects(false);
170 proxyNode->setDatabaseOptions(opt.get());
174 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
175 << ": Failed to load OBJECT_SHARED '"
179 } else if ( token == "OBJECT_SIGN" ) {
180 node = SGMakeSign(staticOptions->getMaterialLib(), name);
182 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
183 node = SGMakeRunwaySign(staticOptions->getMaterialLib(), name);
186 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
187 << ": Unknown token '" << token << "'" );
192 matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
193 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
194 osg::Vec3(0, 0, 1)));
196 osg::MatrixTransform* matrixTransform;
197 matrixTransform = new osg::MatrixTransform(matrix);
198 matrixTransform->setDataVariance(osg::Object::STATIC);
199 matrixTransform->addChild(node.get());
200 node = matrixTransform;
205 group.addChild(node.get());
214 loadTileByFileName(const string& fileName, const osgDB::Options* options)
216 SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
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();
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) {
241 objects.append("Objects");
242 objects.append(basePath);
243 objects.append(simpleFileName);
244 if (loadStgFile(objects.str(), *group, options))
248 terrain.append("Terrain");
249 terrain.append(basePath);
250 terrain.append(simpleFileName);
251 if (loadStgFile(terrain.str(), *group, options))
255 // ... or generate an ocean tile on the fly
257 SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
259 osg::ref_ptr<SGReaderWriterOptions> opt;
260 opt = SGReaderWriterOptions::copyOrCreate(options);
261 osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
263 group->addChild(node);
265 SG_LOG( SG_TERRAIN, SG_ALERT,
266 "Warning: failed to generate ocean tile!" );
269 return group.release();
272 ReaderWriterSTG::ReaderWriterSTG()
274 supportsExtension("stg", "SimGear stg database format");
277 ReaderWriterSTG::~ReaderWriterSTG()
281 const char* ReaderWriterSTG::className() const
283 return "STG Database reader";
286 //#define SLOW_PAGER 1
291 osgDB::ReaderWriter::ReadResult
292 ReaderWriterSTG::readNode(const std::string& fileName,
293 const osgDB::Options* options) const
295 osg::Node* result = loadTileByFileName(fileName, options);
296 // For debugging race conditions
303 return ReadResult::FILE_NOT_HANDLED;