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/tgdb/apt_signs.hxx>
44 #include <simgear/scene/tgdb/obj.hxx>
46 #include "SGOceanTile.hxx"
50 /// Ok, this is a hack - we do not exactly know if it's an airport or not.
51 /// This feature might also vanish again later. This is currently to
52 /// support testing an external ai component that just loads the the airports
53 /// and supports ground queries on only these areas.
54 static bool isAirportBtg(const std::string& name)
58 if (name.substr(4, 8) != ".btg")
60 for (unsigned i = 0; i < 4; ++i) {
61 if (name[i] < 'A' || 'Z' < name[i])
68 static SGBucket bucketIndexFromFileName(const std::string& fileName)
70 // Extract the bucket from the filename
71 std::istringstream ss(osgDB::getNameLessExtension(fileName));
77 return SGBucket(index);
80 struct ReaderWriterSTG::_ModelBin {
82 std::string _errorLocation;
85 osg::ref_ptr<SGReaderWriterOptions> _options;
87 struct _ObjectStatic {
88 _ObjectStatic() : _proxy(false), _lon(0), _lat(0), _elev(0), _hdg(0), _pitch(0), _roll(0) { }
89 std::string _errorLocation;
93 double _lon, _lat, _elev;
94 double _hdg, _pitch, _roll;
95 osg::ref_ptr<SGReaderWriterOptions> _options;
98 _Sign() : _lon(0), _lat(0), _elev(0), _hdg(0), _size(-1) { }
99 std::string _errorLocation;
102 double _lon, _lat, _elev;
111 SGReaderWriterOptions* sharedOptions(const std::string& filePath, const osgDB::Options* options)
113 osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
114 sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
115 sharedOptions->getDatabasePathList().clear();
117 SGPath path = filePath;
118 path.append(".."); path.append(".."); path.append("..");
119 sharedOptions->getDatabasePathList().push_back(path.str());
120 std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
121 sharedOptions->getDatabasePathList().push_back(fg_root);
123 return sharedOptions.release();
125 SGReaderWriterOptions* staticOptions(const std::string& filePath, const osgDB::Options* options)
127 osg::ref_ptr<SGReaderWriterOptions> staticOptions;
128 staticOptions = SGReaderWriterOptions::copyOrCreate(options);
129 staticOptions->getDatabasePathList().clear();
131 staticOptions->getDatabasePathList().push_back(filePath);
132 staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE);
134 return staticOptions.release();
137 bool read(const std::string& absoluteFileName, const osgDB::Options* options)
139 if (absoluteFileName.empty())
142 sg_gzifstream stream(absoluteFileName);
143 if (!stream.is_open())
146 SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
148 std::string filePath = osgDB::getFilePath(absoluteFileName);
150 // do only load airport btg files.
151 bool onlyAirports = options->getPluginStringData("SimGear::FG_ONLY_AIRPORTS") == "ON";
152 // do only load terrain btg files
153 bool onlyTerrain = options->getPluginStringData("SimGear::FG_ONLY_TERRAIN") == "ON";
155 while (!stream.eof()) {
158 std::getline(stream, line);
161 std::string::size_type hash_pos = line.find('#');
162 if (hash_pos != std::string::npos)
163 line.resize(hash_pos);
165 // and process further
166 std::stringstream in(line);
175 // Then there is always a name
179 SGPath path = filePath;
182 if (token == "OBJECT_BASE") {
183 // Load only once (first found)
184 SG_LOG( SG_TERRAIN, SG_BULK, " " << token << " " << name );
186 if (!onlyAirports || isAirportBtg(name)) {
188 obj._errorLocation = absoluteFileName;
190 obj._name = path.str();
191 obj._options = staticOptions(filePath, options);
192 _objectList.push_back(obj);
195 } else if (token == "OBJECT") {
196 if (!onlyAirports || isAirportBtg(name)) {
198 obj._errorLocation = absoluteFileName;
200 obj._name = path.str();
201 obj._options = staticOptions(filePath, options);
202 _objectList.push_back(obj);
207 if (token == "OBJECT_STATIC") {
209 osg::ref_ptr<SGReaderWriterOptions> opt;
210 opt = staticOptions(filePath, options);
211 if (SGPath(name).lower_extension() == "ac")
212 opt->setInstantiateEffects(true);
214 opt->setInstantiateEffects(false);
216 obj._errorLocation = absoluteFileName;
220 in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg >> obj._pitch >> obj._roll;
222 _objectStaticList.push_back(obj);
225 } else if (token == "OBJECT_SHARED") {
227 osg::ref_ptr<SGReaderWriterOptions> opt;
228 opt = staticOptions(filePath, options);
229 if (SGPath(name).lower_extension() == "ac")
230 opt->setInstantiateEffects(true);
232 opt->setInstantiateEffects(false);
234 obj._errorLocation = absoluteFileName;
238 in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg >> obj._pitch >> obj._roll;
240 _objectStaticList.push_back(obj);
243 } else if (token == "OBJECT_SIGN") {
248 in >> sign._lon >> sign._lat >> sign._elev >> sign._hdg >> sign._size;
249 _signList.push_back(sign);
253 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
254 << ": Unknown token '" << token << "'" );
262 osg::Node* load(const SGBucket& bucket, const osgDB::Options* opt)
264 osg::ref_ptr<SGReaderWriterOptions> options;
265 options = SGReaderWriterOptions::copyOrCreate(opt);
267 osg::ref_ptr<osg::Group> group = new osg::Group;
268 group->setDataVariance(osg::Object::STATIC);
271 for (std::list<_Object>::iterator i = _objectList.begin(); i != _objectList.end(); ++i) {
272 osg::ref_ptr<osg::Node> node;
273 node = osgDB::readRefNodeFile(i->_name, i->_options.get());
275 SG_LOG(SG_TERRAIN, SG_ALERT, i->_errorLocation << ": Failed to load "
276 << i->_token << " '" << i->_name << "'");
279 group->addChild(node.get());
282 SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
284 osg::Node* node = SGOceanTile(bucket, options->getMaterialLib());
286 group->addChild(node);
288 SG_LOG( SG_TERRAIN, SG_ALERT,
289 "Warning: failed to generate ocean tile!" );
293 for (std::list<_ObjectStatic>::iterator i = _objectStaticList.begin(); i != _objectStaticList.end(); ++i) {
294 osg::ref_ptr<osg::Node> node;
296 osg::ref_ptr<osg::ProxyNode> proxy = new osg::ProxyNode;
297 proxy->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
298 proxy->setFileName(0, i->_name);
299 proxy->setDatabaseOptions(i->_options.get());
302 node = osgDB::readRefNodeFile(i->_name, i->_options.get());
304 SG_LOG(SG_TERRAIN, SG_ALERT, i->_errorLocation << ": Failed to load "
305 << i->_token << " '" << i->_name << "'");
309 if (SGPath(i->_name).lower_extension() == "ac")
310 node->setNodeMask(~simgear::MODELLIGHT_BIT);
313 matrix = makeZUpFrame(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev));
314 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_hdg), osg::Vec3(0, 0, 1)));
315 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_pitch), osg::Vec3(0, 1, 0)));
316 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_roll), osg::Vec3(1, 0, 0)));
318 osg::MatrixTransform* matrixTransform;
319 matrixTransform = new osg::MatrixTransform(matrix);
320 matrixTransform->setDataVariance(osg::Object::STATIC);
321 matrixTransform->addChild(node.get());
322 group->addChild(matrixTransform);
325 simgear::AirportSignBuilder signBuilder(options->getMaterialLib(), bucket.get_center());
326 for (std::list<_Sign>::iterator i = _signList.begin(); i != _signList.end(); ++i)
327 signBuilder.addSign(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev), i->_hdg, i->_name, i->_size);
328 if (signBuilder.getSignsGroup())
329 group->addChild(signBuilder.getSignsGroup());
331 return group.release();
335 std::list<_Object> _objectList;
336 std::list<_ObjectStatic> _objectStaticList;
337 std::list<_Sign> _signList;
340 ReaderWriterSTG::ReaderWriterSTG()
342 supportsExtension("stg", "SimGear stg database format");
345 ReaderWriterSTG::~ReaderWriterSTG()
349 const char* ReaderWriterSTG::className() const
351 return "STG Database reader";
354 osgDB::ReaderWriter::ReadResult
355 ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* options) const
358 SGBucket bucket(bucketIndexFromFileName(fileName));
360 // We treat 123.stg different than ./123.stg.
361 // The difference is that ./123.stg as well as any absolute path
362 // really loads the given stg file and only this.
363 // In contrast 123.stg uses the search paths to load a set of stg
364 // files spread across the scenery directories.
365 if (osgDB::getSimpleFileName(fileName) != fileName) {
366 if (!modelBin.read(fileName, options))
367 return ReadResult::FILE_NOT_FOUND;
369 // For stg meta files, we need options for the search path.
371 return ReadResult::FILE_NOT_FOUND;
373 SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
375 std::string basePath = bucket.gen_base_path();
377 // Stop scanning once an object base is found
378 // This is considered a meta file, so apply the scenery path search
379 const osgDB::FilePathList& filePathList = options->getDatabasePathList();
380 for (osgDB::FilePathList::const_iterator i = filePathList.begin();
381 i != filePathList.end() && !modelBin._foundBase; ++i) {
383 objects.append("Objects");
384 objects.append(basePath);
385 objects.append(fileName);
386 modelBin.read(objects.str(), options);
389 terrain.append("Terrain");
390 terrain.append(basePath);
391 terrain.append(fileName);
392 modelBin.read(terrain.str(), options);
396 return modelBin.load(bucket, options);