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/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>
48 #include "SGOceanTile.hxx"
50 using namespace simgear;
52 /// Ok, this is a hack - we do not exactly know if it's an airport or not.
53 /// This feature might also vanish again later. This is currently to
54 /// support testing an external ai component that just loads the the airports
55 /// and supports ground queries on only these areas.
56 static bool isAirportBtg(const std::string& name)
60 if (name.substr(4, 8) != ".btg")
62 for (unsigned i = 0; i < 4; ++i) {
63 if (name[i] < 'A' || 'Z' < name[i])
70 static SGBucket bucketIndexFromFileName(const std::string& fileName)
72 // Extract the bucket from the filename
73 std::istringstream ss(osgDB::getNameLessExtension(fileName));
79 return SGBucket(index);
82 static bool hasOptionalValue(sg_gzifstream &in)
84 while ( (in.peek() != '\n') && (in.peek() != '\r')
85 && isspace(in.peek()) ) {
88 if ( isdigit(in.peek()) || (in.peek() == '-') ){
95 ReaderWriterSTG::ReaderWriterSTG()
97 supportsExtension("stg", "SimGear stg database format");
100 ReaderWriterSTG::~ReaderWriterSTG()
104 const char* ReaderWriterSTG::className() const
106 return "STG Database reader";
109 osgDB::ReaderWriter::ReadResult
110 ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* options) const
112 // We treat 123.stg different than ./123.stg.
113 // The difference is that ./123.stg as well as any absolute path
114 // really loads the given stg file and only this.
115 // In contrast 123.stg uses the search paths to load a set of stg
116 // files spread across the scenery directories.
117 if (osgDB::getSimpleFileName(fileName) != fileName)
118 return readStgFile(fileName, options);
120 // For stg meta files, we need options for the search path.
122 return ReadResult::FILE_NOT_FOUND;
126 SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
128 SGBucket bucket(bucketIndexFromFileName(fileName));
129 std::string basePath = bucket.gen_base_path();
131 osg::ref_ptr<osg::Group> group = new osg::Group;
133 // Stop scanning once an object base is found
134 bool foundBase = false;
135 // This is considered a meta file, so apply the scenery path search
136 const osgDB::FilePathList& filePathList = options->getDatabasePathList();
137 for (osgDB::FilePathList::const_iterator i = filePathList.begin();
138 i != filePathList.end() && !foundBase; ++i) {
140 objects.append("Objects");
141 objects.append(basePath);
142 objects.append(fileName);
143 if (readStgFile(objects.str(), bucket, *group, options))
147 terrain.append("Terrain");
148 terrain.append(basePath);
149 terrain.append(fileName);
150 if (readStgFile(terrain.str(), bucket, *group, options))
154 // ... or generate an ocean tile on the fly
156 SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
158 osg::ref_ptr<SGReaderWriterOptions> opt;
159 opt = SGReaderWriterOptions::copyOrCreate(options);
160 osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
162 group->addChild(node);
164 SG_LOG( SG_TERRAIN, SG_ALERT,
165 "Warning: failed to generate ocean tile!" );
172 osgDB::ReaderWriter::ReadResult
173 ReaderWriterSTG::readStgFile(const std::string& fileName, const osgDB::Options* options) const
175 // This is considered a real existing file.
176 // We still apply the search path algorithms for relative files.
177 osg::ref_ptr<osg::Group> group = new osg::Group;
178 std::string path = osgDB::findDataFile(fileName, options);
179 readStgFile(path, bucketIndexFromFileName(path), *group, options);
185 ReaderWriterSTG::readStgFile(const std::string& absoluteFileName,
186 const SGBucket& bucket,
187 osg::Group& group, const osgDB::Options* options) const
189 if (absoluteFileName.empty())
192 sg_gzifstream in( absoluteFileName );
196 SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
198 std::string filePath = osgDB::getFilePath(absoluteFileName);
200 osg::ref_ptr<SGReaderWriterOptions> staticOptions;
201 staticOptions = SGReaderWriterOptions::copyOrCreate(options);
202 staticOptions->getDatabasePathList().clear();
203 staticOptions->getDatabasePathList().push_back(filePath);
204 staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE);
206 osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
207 sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
208 sharedOptions->getDatabasePathList().clear();
210 SGPath path = filePath;
211 path.append(".."); path.append(".."); path.append("..");
212 sharedOptions->getDatabasePathList().push_back(path.str());
213 std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
214 sharedOptions->getDatabasePathList().push_back(fg_root);
216 // do only load airport btg files.
217 bool onlyAirports = options->getPluginStringData("SimGear::FG_ONLY_AIRPORTS") == "ON";
218 // do only load terrain btg files
219 bool onlyTerrain = options->getPluginStringData("SimGear::FG_ONLY_TERRAIN") == "ON";
221 simgear::AirportSignBuilder signBuilder(staticOptions->getMaterialLib(), bucket.get_center());
223 bool has_base = false;
224 while ( ! in.eof() ) {
229 if ( token.empty() || token[0] == '#' ) {
234 // Then there is always a name
238 SGPath path = filePath;
241 osg::ref_ptr<osg::Node> node;
242 if ( token == "OBJECT_BASE" ) {
243 // Load only once (first found)
244 SG_LOG( SG_TERRAIN, SG_BULK, " " << token << " " << name );
248 if (!onlyAirports || isAirportBtg(name)) {
249 node = osgDB::readRefNodeFile(path.str(),
250 staticOptions.get());
253 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
254 << ": Failed to load OBJECT_BASE '"
259 } else if ( token == "OBJECT" ) {
260 if (!onlyAirports || isAirportBtg(name)) {
261 node = osgDB::readRefNodeFile(path.str(),
262 staticOptions.get());
265 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
266 << ": Failed to load OBJECT '"
272 double lon, lat, elev, hdg;
273 in >> lon >> lat >> elev >> hdg;
276 if ( token == "OBJECT_STATIC" ) {
278 osg::ref_ptr<SGReaderWriterOptions> opt;
279 opt = new SGReaderWriterOptions(*staticOptions);
280 osg::ProxyNode* proxyNode = new osg::ProxyNode;
281 proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
282 /// Hmm, the findDataFile should happen downstream
283 std::string absName = osgDB::findDataFile(name, opt.get());
284 proxyNode->setFileName(0, absName);
285 if (SGPath(absName).lower_extension() == "ac")
287 proxyNode->setNodeMask( ~simgear::MODELLIGHT_BIT );
288 opt->setInstantiateEffects(true);
291 opt->setInstantiateEffects(false);
292 proxyNode->setDatabaseOptions(opt.get());
296 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
297 << ": Failed to load OBJECT_STATIC '"
302 } else if ( token == "OBJECT_SHARED" ) {
304 osg::ref_ptr<SGReaderWriterOptions> opt;
305 opt = new SGReaderWriterOptions(*sharedOptions);
306 /// Hmm, the findDataFile should happen in the downstream readers
307 std::string absName = osgDB::findDataFile(name, opt.get());
308 if (SGPath(absName).lower_extension() == "ac")
309 opt->setInstantiateEffects(true);
311 opt->setInstantiateEffects(false);
312 node = osgDB::readRefNodeFile(absName, opt.get());
313 if (SGPath(absName).lower_extension() == "ac")
314 node->setNodeMask( ~simgear::MODELLIGHT_BIT );
317 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
318 << ": Failed to load OBJECT_SHARED '"
323 } else if ( token == "OBJECT_SIGN" ) {
326 if ( hasOptionalValue(in) ){
330 signBuilder.addSign(SGGeod::fromDegM(lon, lat, elev), hdg, name, size);
332 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
333 << ": Unknown token '" << token << "'" );
338 matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
339 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
340 osg::Vec3(0, 0, 1)));
342 if ( hasOptionalValue(in) ){
343 double pitch(0.0), roll(0.0);
346 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(pitch),
347 osg::Vec3(0, 1, 0)));
348 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(roll),
349 osg::Vec3(1, 0, 0)));
352 osg::MatrixTransform* matrixTransform;
353 matrixTransform = new osg::MatrixTransform(matrix);
354 matrixTransform->setDataVariance(osg::Object::STATIC);
355 matrixTransform->addChild(node.get());
356 node = matrixTransform;
361 group.addChild(node.get());
366 if (signBuilder.getSignsGroup())
367 group.addChild(signBuilder.getSignsGroup());