#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/OsgMath.hxx>
-#include <simgear/scene/material/mat.hxx>
-#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/tgdb/apt_signs.hxx>
#include <simgear/scene/tgdb/obj.hxx>
#include "SGOceanTile.hxx"
-using namespace simgear;
+namespace simgear {
/// Ok, this is a hack - we do not exactly know if it's an airport or not.
/// This feature might also vanish again later. This is currently to
return SGBucket(index);
}
-static bool hasOptionalValue(sg_gzifstream &in)
-{
- while ( (in.peek() != '\n') && (in.peek() != '\r')
- && isspace(in.peek()) ) {
- in.get();
+struct ReaderWriterSTG::_ModelBin {
+ struct _Object {
+ std::string _errorLocation;
+ std::string _token;
+ std::string _name;
+ osg::ref_ptr<SGReaderWriterOptions> _options;
+ };
+ struct _ObjectStatic {
+ _ObjectStatic() : _proxy(false), _lon(0), _lat(0), _elev(0), _hdg(0), _pitch(0), _roll(0) { }
+ std::string _errorLocation;
+ std::string _token;
+ std::string _name;
+ bool _proxy;
+ double _lon, _lat, _elev;
+ double _hdg, _pitch, _roll;
+ osg::ref_ptr<SGReaderWriterOptions> _options;
+ };
+ struct _Sign {
+ _Sign() : _lon(0), _lat(0), _elev(0), _hdg(0), _size(-1) { }
+ std::string _errorLocation;
+ std::string _token;
+ std::string _name;
+ double _lon, _lat, _elev;
+ double _hdg;
+ int _size;
+ };
+
+ _ModelBin() :
+ _foundBase(false)
+ { }
+
+ SGReaderWriterOptions* sharedOptions(const std::string& filePath, const osgDB::Options* options)
+ {
+ osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
+ sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
+ sharedOptions->getDatabasePathList().clear();
+
+ SGPath path = filePath;
+ path.append(".."); path.append(".."); path.append("..");
+ sharedOptions->getDatabasePathList().push_back(path.str());
+ std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
+ sharedOptions->getDatabasePathList().push_back(fg_root);
+
+ return sharedOptions.release();
}
- if ( isdigit(in.peek()) || (in.peek() == '-') ){
+ SGReaderWriterOptions* staticOptions(const std::string& filePath, const osgDB::Options* options)
+ {
+ osg::ref_ptr<SGReaderWriterOptions> staticOptions;
+ staticOptions = SGReaderWriterOptions::copyOrCreate(options);
+ staticOptions->getDatabasePathList().clear();
+
+ staticOptions->getDatabasePathList().push_back(filePath);
+ staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE);
+
+ return staticOptions.release();
+ }
+
+ bool read(const std::string& absoluteFileName, const osgDB::Options* options)
+ {
+ if (absoluteFileName.empty())
+ return false;
+
+ sg_gzifstream stream(absoluteFileName);
+ if (!stream.is_open())
+ return false;
+
+ SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
+
+ std::string filePath = osgDB::getFilePath(absoluteFileName);
+
+ // do only load airport btg files.
+ bool onlyAirports = options->getPluginStringData("SimGear::FG_ONLY_AIRPORTS") == "ON";
+ // do only load terrain btg files
+ bool onlyTerrain = options->getPluginStringData("SimGear::FG_ONLY_TERRAIN") == "ON";
+
+ while (!stream.eof()) {
+ // read a line
+ std::string line;
+ std::getline(stream, line);
+
+ // strip comments
+ std::string::size_type hash_pos = line.find('#');
+ if (hash_pos != std::string::npos)
+ line.resize(hash_pos);
+
+ // and process further
+ std::stringstream in(line);
+
+ std::string token;
+ in >> token;
+
+ // No comment
+ if (token.empty())
+ continue;
+
+ // Then there is always a name
+ std::string name;
+ in >> name;
+
+ SGPath path = filePath;
+ path.append(name);
+
+ if (token == "OBJECT_BASE") {
+ // Load only once (first found)
+ SG_LOG( SG_TERRAIN, SG_BULK, " " << token << " " << name );
+ _foundBase = true;
+ if (!onlyAirports || isAirportBtg(name)) {
+ _Object obj;
+ obj._errorLocation = absoluteFileName;
+ obj._token = token;
+ obj._name = path.str();
+ obj._options = staticOptions(filePath, options);
+ _objectList.push_back(obj);
+ }
+
+ } else if (token == "OBJECT") {
+ if (!onlyAirports || isAirportBtg(name)) {
+ _Object obj;
+ obj._errorLocation = absoluteFileName;
+ obj._token = token;
+ obj._name = path.str();
+ obj._options = staticOptions(filePath, options);
+ _objectList.push_back(obj);
+ }
+
+ } else {
+ // Always OK to load
+ if (token == "OBJECT_STATIC") {
+ if (!onlyTerrain) {
+ osg::ref_ptr<SGReaderWriterOptions> opt;
+ opt = staticOptions(filePath, options);
+ if (SGPath(name).lower_extension() == "ac")
+ opt->setInstantiateEffects(true);
+ else
+ opt->setInstantiateEffects(false);
+ _ObjectStatic obj;
+ obj._errorLocation = absoluteFileName;
+ obj._token = token;
+ obj._name = name;
+ obj._proxy = true;
+ in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg >> obj._pitch >> obj._roll;
+ obj._options = opt;
+ _objectStaticList.push_back(obj);
+ }
+
+ } else if (token == "OBJECT_SHARED") {
+ if (!onlyTerrain) {
+ osg::ref_ptr<SGReaderWriterOptions> opt;
+ opt = staticOptions(filePath, options);
+ if (SGPath(name).lower_extension() == "ac")
+ opt->setInstantiateEffects(true);
+ else
+ opt->setInstantiateEffects(false);
+ _ObjectStatic obj;
+ obj._errorLocation = absoluteFileName;
+ obj._token = token;
+ obj._name = name;
+ obj._proxy = false;
+ in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg >> obj._pitch >> obj._roll;
+ obj._options = opt;
+ _objectStaticList.push_back(obj);
+ }
+
+ } else if (token == "OBJECT_SIGN") {
+ if (!onlyTerrain) {
+ _Sign sign;
+ sign._token = token;
+ sign._name = name;
+ in >> sign._lon >> sign._lat >> sign._elev >> sign._hdg >> sign._size;
+ _signList.push_back(sign);
+ }
+
+ } else {
+ SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
+ << ": Unknown token '" << token << "'" );
+ }
+ }
+ }
+
return true;
- } else {
- return false;
}
-}
+
+ osg::Node* load(const SGBucket& bucket, const osgDB::Options* opt)
+ {
+ osg::ref_ptr<SGReaderWriterOptions> options;
+ options = SGReaderWriterOptions::copyOrCreate(opt);
+
+ osg::ref_ptr<osg::Group> group = new osg::Group;
+ group->setDataVariance(osg::Object::STATIC);
+
+ if (_foundBase) {
+ for (std::list<_Object>::iterator i = _objectList.begin(); i != _objectList.end(); ++i) {
+ osg::ref_ptr<osg::Node> node;
+ node = osgDB::readRefNodeFile(i->_name, i->_options.get());
+ if (!node.valid()) {
+ SG_LOG(SG_TERRAIN, SG_ALERT, i->_errorLocation << ": Failed to load "
+ << i->_token << " '" << i->_name << "'");
+ continue;
+ }
+ group->addChild(node.get());
+ }
+ } else {
+ SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
+
+ osg::Node* node = SGOceanTile(bucket, options->getMaterialLib());
+ if (node) {
+ group->addChild(node);
+ } else {
+ SG_LOG( SG_TERRAIN, SG_ALERT,
+ "Warning: failed to generate ocean tile!" );
+ }
+ }
+
+ for (std::list<_ObjectStatic>::iterator i = _objectStaticList.begin(); i != _objectStaticList.end(); ++i) {
+ osg::ref_ptr<osg::Node> node;
+ if (i->_proxy) {
+ osg::ref_ptr<osg::ProxyNode> proxy = new osg::ProxyNode;
+ proxy->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
+ proxy->setFileName(0, i->_name);
+ proxy->setDatabaseOptions(i->_options.get());
+ node = proxy;
+ } else {
+ node = osgDB::readRefNodeFile(i->_name, i->_options.get());
+ if (!node.valid()) {
+ SG_LOG(SG_TERRAIN, SG_ALERT, i->_errorLocation << ": Failed to load "
+ << i->_token << " '" << i->_name << "'");
+ continue;
+ }
+ }
+ if (SGPath(i->_name).lower_extension() == "ac")
+ node->setNodeMask(~simgear::MODELLIGHT_BIT);
+
+ osg::Matrix matrix;
+ matrix = makeZUpFrame(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev));
+ matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_hdg), osg::Vec3(0, 0, 1)));
+ matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_pitch), osg::Vec3(0, 1, 0)));
+ matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_roll), osg::Vec3(1, 0, 0)));
+
+ osg::MatrixTransform* matrixTransform;
+ matrixTransform = new osg::MatrixTransform(matrix);
+ matrixTransform->setDataVariance(osg::Object::STATIC);
+ matrixTransform->addChild(node.get());
+ group->addChild(matrixTransform);
+ }
+
+ simgear::AirportSignBuilder signBuilder(options->getMaterialLib(), bucket.get_center());
+ for (std::list<_Sign>::iterator i = _signList.begin(); i != _signList.end(); ++i)
+ signBuilder.addSign(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev), i->_hdg, i->_name, i->_size);
+ if (signBuilder.getSignsGroup())
+ group->addChild(signBuilder.getSignsGroup());
+
+ return group.release();
+ }
+
+ bool _foundBase;
+ std::list<_Object> _objectList;
+ std::list<_ObjectStatic> _objectStaticList;
+ std::list<_Sign> _signList;
+};
ReaderWriterSTG::ReaderWriterSTG()
{
osgDB::ReaderWriter::ReadResult
ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* options) const
{
+ _ModelBin modelBin;
+ SGBucket bucket(bucketIndexFromFileName(fileName));
+
// We treat 123.stg different than ./123.stg.
// The difference is that ./123.stg as well as any absolute path
// really loads the given stg file and only this.
// In contrast 123.stg uses the search paths to load a set of stg
// files spread across the scenery directories.
- if (osgDB::getSimpleFileName(fileName) != fileName)
- return readStgFile(fileName, options);
-
- // For stg meta files, we need options for the search path.
- if (!options)
- return ReadResult::FILE_NOT_FOUND;
-
-
-
- SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
-
- SGBucket bucket(bucketIndexFromFileName(fileName));
- std::string basePath = bucket.gen_base_path();
-
- osg::ref_ptr<osg::Group> group = new osg::Group;
-
- // Stop scanning once an object base is found
- bool foundBase = false;
- // This is considered a meta file, so apply the scenery path search
- const osgDB::FilePathList& filePathList = options->getDatabasePathList();
- for (osgDB::FilePathList::const_iterator i = filePathList.begin();
- i != filePathList.end() && !foundBase; ++i) {
- SGPath objects(*i);
- objects.append("Objects");
- objects.append(basePath);
- objects.append(fileName);
- if (readStgFile(objects.str(), bucket, *group, options))
- foundBase = true;
-
- SGPath terrain(*i);
- terrain.append("Terrain");
- terrain.append(basePath);
- terrain.append(fileName);
- if (readStgFile(terrain.str(), bucket, *group, options))
- foundBase = true;
- }
-
- // ... or generate an ocean tile on the fly
- if (!foundBase) {
- SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
-
- osg::ref_ptr<SGReaderWriterOptions> opt;
- opt = SGReaderWriterOptions::copyOrCreate(options);
- osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
- if ( node ) {
- group->addChild(node);
- } else {
- SG_LOG( SG_TERRAIN, SG_ALERT,
- "Warning: failed to generate ocean tile!" );
- }
- }
-
- return group.get();
-}
-
-osgDB::ReaderWriter::ReadResult
-ReaderWriterSTG::readStgFile(const std::string& fileName, const osgDB::Options* options) const
-{
- // This is considered a real existing file.
- // We still apply the search path algorithms for relative files.
- osg::ref_ptr<osg::Group> group = new osg::Group;
- std::string path = osgDB::findDataFile(fileName, options);
- readStgFile(path, bucketIndexFromFileName(path), *group, options);
-
- return group.get();
-}
-
-bool
-ReaderWriterSTG::readStgFile(const std::string& absoluteFileName,
- const SGBucket& bucket,
- osg::Group& group, const osgDB::Options* options) const
-{
- if (absoluteFileName.empty())
- return false;
-
- sg_gzifstream in( absoluteFileName );
- if ( !in.is_open() )
- return false;
-
- SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
-
- std::string filePath = osgDB::getFilePath(absoluteFileName);
-
- osg::ref_ptr<SGReaderWriterOptions> staticOptions;
- staticOptions = SGReaderWriterOptions::copyOrCreate(options);
- staticOptions->getDatabasePathList().clear();
- staticOptions->getDatabasePathList().push_back(filePath);
- staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE);
-
- osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
- sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
- sharedOptions->getDatabasePathList().clear();
-
- SGPath path = filePath;
- path.append(".."); path.append(".."); path.append("..");
- sharedOptions->getDatabasePathList().push_back(path.str());
- std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
- sharedOptions->getDatabasePathList().push_back(fg_root);
-
- // do only load airport btg files.
- bool onlyAirports = options->getPluginStringData("SimGear::FG_ONLY_AIRPORTS") == "ON";
- // do only load terrain btg files
- bool onlyTerrain = options->getPluginStringData("SimGear::FG_ONLY_TERRAIN") == "ON";
-
- simgear::AirportSignBuilder signBuilder(staticOptions->getMaterialLib(), bucket.get_center());
-
- bool has_base = false;
- while ( ! in.eof() ) {
- std::string token;
- in >> token;
-
- // No comment
- if ( token.empty() || token[0] == '#' ) {
- in >> ::skipeol;
- continue;
- }
+ if (osgDB::getSimpleFileName(fileName) != fileName) {
+ if (!modelBin.read(fileName, options))
+ return ReadResult::FILE_NOT_FOUND;
+ } else {
+ // For stg meta files, we need options for the search path.
+ if (!options)
+ return ReadResult::FILE_NOT_FOUND;
- // Then there is always a name
- std::string name;
- in >> name;
+ SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
- SGPath path = filePath;
- path.append(name);
+ std::string basePath = bucket.gen_base_path();
- osg::ref_ptr<osg::Node> node;
- if ( token == "OBJECT_BASE" ) {
- // Load only once (first found)
- SG_LOG( SG_TERRAIN, SG_BULK, " " << token << " " << name );
-
- has_base = true;
-
- if (!onlyAirports || isAirportBtg(name)) {
- node = osgDB::readRefNodeFile(path.str(),
- staticOptions.get());
-
- if (!node.valid()) {
- SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
- << ": Failed to load OBJECT_BASE '"
- << name << "'" );
- }
- }
+ // Stop scanning once an object base is found
+ // This is considered a meta file, so apply the scenery path search
+ const osgDB::FilePathList& filePathList = options->getDatabasePathList();
+ for (osgDB::FilePathList::const_iterator i = filePathList.begin();
+ i != filePathList.end() && !modelBin._foundBase; ++i) {
+ SGPath objects(*i);
+ objects.append("Objects");
+ objects.append(basePath);
+ objects.append(fileName);
+ modelBin.read(objects.str(), options);
- } else if ( token == "OBJECT" ) {
- if (!onlyAirports || isAirportBtg(name)) {
- node = osgDB::readRefNodeFile(path.str(),
- staticOptions.get());
-
- if (!node.valid()) {
- SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
- << ": Failed to load OBJECT '"
- << name << "'" );
- }
- }
-
- } else {
- double lon, lat, elev, hdg;
- in >> lon >> lat >> elev >> hdg;
-
- // Always OK to load
- if ( token == "OBJECT_STATIC" ) {
- if (!onlyTerrain) {
- osg::ref_ptr<SGReaderWriterOptions> opt;
- opt = new SGReaderWriterOptions(*staticOptions);
- osg::ProxyNode* proxyNode = new osg::ProxyNode;
- proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
- /// Hmm, the findDataFile should happen downstream
- std::string absName = osgDB::findDataFile(name, opt.get());
- proxyNode->setFileName(0, absName);
- if (SGPath(absName).lower_extension() == "ac")
- {
- proxyNode->setNodeMask( ~simgear::MODELLIGHT_BIT );
- opt->setInstantiateEffects(true);
- }
- else
- opt->setInstantiateEffects(false);
- proxyNode->setDatabaseOptions(opt.get());
- node = proxyNode;
-
- if (!node.valid()) {
- SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
- << ": Failed to load OBJECT_STATIC '"
- << name << "'" );
- }
- }
-
- } else if ( token == "OBJECT_SHARED" ) {
- if (!onlyTerrain) {
- osg::ref_ptr<SGReaderWriterOptions> opt;
- opt = new SGReaderWriterOptions(*sharedOptions);
- /// Hmm, the findDataFile should happen in the downstream readers
- std::string absName = osgDB::findDataFile(name, opt.get());
- if (SGPath(absName).lower_extension() == "ac")
- opt->setInstantiateEffects(true);
- else
- opt->setInstantiateEffects(false);
- node = osgDB::readRefNodeFile(absName, opt.get());
- if (SGPath(absName).lower_extension() == "ac")
- node->setNodeMask( ~simgear::MODELLIGHT_BIT );
-
- if (!node.valid()) {
- SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
- << ": Failed to load OBJECT_SHARED '"
- << name << "'" );
- }
- }
-
- } else if ( token == "OBJECT_SIGN" ) {
- int size(-1);
-
- if ( hasOptionalValue(in) ){
- in >> size;
- }
- if (!onlyTerrain)
- signBuilder.addSign(SGGeod::fromDegM(lon, lat, elev), hdg, name, size);
- } else {
- SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
- << ": Unknown token '" << token << "'" );
- }
-
- if (node.valid()) {
- osg::Matrix matrix;
- matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
- matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
- osg::Vec3(0, 0, 1)));
-
- if ( hasOptionalValue(in) ){
- double pitch(0.0), roll(0.0);
- in >> pitch >> roll;
-
- matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(pitch),
- osg::Vec3(0, 1, 0)));
- matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(roll),
- osg::Vec3(1, 0, 0)));
- }
-
- osg::MatrixTransform* matrixTransform;
- matrixTransform = new osg::MatrixTransform(matrix);
- matrixTransform->setDataVariance(osg::Object::STATIC);
- matrixTransform->addChild(node.get());
- node = matrixTransform;
- }
+ SGPath terrain(*i);
+ terrain.append("Terrain");
+ terrain.append(basePath);
+ terrain.append(fileName);
+ modelBin.read(terrain.str(), options);
}
-
- if (node.valid())
- group.addChild(node.get());
-
- in >> ::skipeol;
}
-
- if (signBuilder.getSignsGroup())
- group.addChild(signBuilder.getSignsGroup());
- return has_base;
+ return modelBin.load(bucket, options);
+}
+
}