scenery: Accumulate stg files until we find an OBJECT_BASE.

This should recover most of the old scenery loading behavior
before the last change. The z-fighting due to model duplication
does not happen over solid ground. Sea tiles are still broken.
This commit is contained in:
Mathias Froehlich 2012-03-11 21:32:35 +01:00
parent 0a96f4e145
commit 5a5d65134e
2 changed files with 200 additions and 197 deletions

View File

@ -32,6 +32,7 @@
#include <osg/MatrixTransform> #include <osg/MatrixTransform>
#include <osg/Math> #include <osg/Math>
#include <osg/NodeCallback> #include <osg/NodeCallback>
#include <osg/ProxyNode>
#include <osgDB/FileNameUtils> #include <osgDB/FileNameUtils>
#include <osgDB/ReaderWriter> #include <osgDB/ReaderWriter>
@ -59,7 +60,7 @@
using std::string; using std::string;
using namespace simgear; using namespace simgear;
ModelLoadHelper *TileEntry::_modelLoader=0; static ModelLoadHelper *_modelLoader=0;
namespace { namespace {
osgDB::RegisterReaderWriterProxy<ReaderWriterSTG> g_readerWriterSTGProxy; osgDB::RegisterReaderWriterProxy<ReaderWriterSTG> g_readerWriterSTGProxy;
@ -80,6 +81,173 @@ static SGBucket getBucketFromFileName(const std::string& fileName)
return SGBucket(index); return SGBucket(index);
} }
static bool
loadStgFile(const std::string& absoluteFileName, osg::Group& group, const osgDB::Options* options)
{
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);
bool has_base = false;
while ( ! in.eof() ) {
std::string token;
in >> token;
// No comment
if ( token.empty() || token[0] == '#' ) {
in >> ::skipeol;
continue;
}
// Then there is always a name
std::string name;
in >> name;
SGPath path = filePath;
path.append(name);
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;
node = osgDB::readRefNodeFile(path.str(),
staticOptions.get());
if (!node.valid()) {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
<< ": Failed to load OBJECT_BASE '"
<< name << "'" );
}
} else if ( token == "OBJECT" ) {
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" ) {
/// Hmm, the findDataFile should happen downstream
std::string absName = osgDB::findDataFile(name,
staticOptions.get());
if(_modelLoader) {
node = _modelLoader->loadTileModel(absName, false);
} else {
osg::ref_ptr<SGReaderWriterOptions> opt;
opt = new SGReaderWriterOptions(*staticOptions);
if (SGPath(absName).lower_extension() == "ac")
opt->setInstantiateEffects(true);
else
opt->setInstantiateEffects(false);
node = osgDB::readRefNodeFile(absName, opt.get());
}
if (!node.valid()) {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
<< ": Failed to load OBJECT_STATIC '"
<< name << "'" );
}
} else if ( token == "OBJECT_SHARED" ) {
if(_modelLoader) {
node = _modelLoader->loadTileModel(name, true);
} else {
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());
osg::ProxyNode* proxyNode = new osg::ProxyNode;
proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
proxyNode->setFileName(0, absName);
if (SGPath(absName).lower_extension() == "ac")
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_SHARED '"
<< name << "'" );
}
} else if ( token == "OBJECT_SIGN" ) {
node = SGMakeSign(staticOptions->getMaterialLib(), name);
} else if ( token == "OBJECT_RUNWAY_SIGN" ) {
node = SGMakeRunwaySign(staticOptions->getMaterialLib(), name);
} else {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
<< ": Unknown token '" << token << "'" );
}
if (node.valid() && token != "OBJECT") {
osg::Matrix matrix;
matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
osg::Vec3(0, 0, 1)));
osg::MatrixTransform* matrixTransform;
matrixTransform = new osg::MatrixTransform(matrix);
matrixTransform->setDataVariance(osg::Object::STATIC);
matrixTransform->addChild(node.get());
node = matrixTransform;
}
}
if (node.valid())
group.addChild(node.get());
in >> ::skipeol;
}
return has_base;
}
void
TileEntry::setModelLoadHelper(ModelLoadHelper *m)
{
_modelLoader=m;
}
// Constructor // Constructor
TileEntry::TileEntry ( const SGBucket& b ) TileEntry::TileEntry ( const SGBucket& b )
: tile_bucket( b ), : tile_bucket( b ),
@ -134,10 +302,6 @@ TileEntry::loadTileByFileName(const string& fileName,
{ {
SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName); SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
// Space for up to two stg file names.
// There is usually one in the Terrain and one in the Objects subdirectory.
std::string absoluteFileName[2];
// We treat 123.stg different than ./123.stg. // We treat 123.stg different than ./123.stg.
// The difference is that ./123.stg as well as any absolute path // The difference is that ./123.stg as well as any absolute path
// really loads the given stg file and only this. // really loads the given stg file and only this.
@ -145,200 +309,42 @@ TileEntry::loadTileByFileName(const string& fileName,
// files spread across the scenery directories. // files spread across the scenery directories.
std::string simpleFileName = osgDB::getSimpleFileName(fileName); std::string simpleFileName = osgDB::getSimpleFileName(fileName);
SGBucket bucket = getBucketFromFileName(simpleFileName); SGBucket bucket = getBucketFromFileName(simpleFileName);
bool file_mode = false; osg::ref_ptr<osg::Group> group = new osg::Group;
if (simpleFileName == fileName && options) { if (simpleFileName != fileName || !options) {
// This is considered a meta file, so apply the scenery path search
const osgDB::FilePathList& filePathList = options->getDatabasePathList();
std::string basePath = bucket.gen_base_path();
for (osgDB::FilePathList::const_iterator i = filePathList.begin();
i != filePathList.end(); ++i) {
SGPath terrain(*i);
terrain.append("Terrain");
terrain.append(basePath);
terrain.append(simpleFileName);
SGPath objects(*i);
objects.append("Objects");
objects.append(basePath);
objects.append(simpleFileName);
if (terrain.isFile() || objects.isFile()) {
absoluteFileName[0] = terrain.str();
absoluteFileName[1] = objects.str();
break;
}
}
} else {
// This is considered a real existing file. // This is considered a real existing file.
// We still apply the search path algorithms for relative files. // We still apply the search path algorithms for relative files.
absoluteFileName[0] = osgDB::findDataFile(fileName, options); loadStgFile(osgDB::findDataFile(fileName, options), *group, options);
// Do not generate an ocean tile if we have no btg return group.release();
file_mode = true;
} }
std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT"); // This is considered a meta file, so apply the scenery path search
osg::ref_ptr<SGReaderWriterOptions> opt; const osgDB::FilePathList& filePathList = options->getDatabasePathList();
opt = SGReaderWriterOptions::copyOrCreate(options); std::string basePath = bucket.gen_base_path();
// Stop scanning once an object base is found
bool found_tile_base = false; bool foundBase = false;
osg::ref_ptr<osg::Group> group = new osg::Group; for (osgDB::FilePathList::const_iterator i = filePathList.begin();
for (unsigned i = 0; i < 2; ++i) { i != filePathList.end() && !foundBase; ++i) {
SGPath objects(*i);
if (absoluteFileName[i].empty()) objects.append("Objects");
continue; objects.append(basePath);
objects.append(simpleFileName);
sg_gzifstream in( absoluteFileName[i] ); if (loadStgFile(objects.str(), *group, options))
if ( !in.is_open() ) foundBase = true;
continue;
SGPath terrain(*i);
SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName[i]); terrain.append("Terrain");
terrain.append(basePath);
std::string filePath = osgDB::getFilePath(absoluteFileName[i]); terrain.append(simpleFileName);
if (loadStgFile(terrain.str(), *group, options))
osg::ref_ptr<SGReaderWriterOptions> staticOptions; foundBase = true;
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());
sharedOptions->getDatabasePathList().push_back(fg_root);
bool has_base = false;
while ( ! in.eof() ) {
std::string token;
in >> token;
// No comment
if ( token.empty() || token[0] == '#' ) {
in >> ::skipeol;
continue;
}
// Then there is always a name
std::string name;
in >> name;
SGPath path = filePath;
path.append(name);
osg::ref_ptr<osg::Node> node;
if ( token == "OBJECT_BASE" ) {
// Load only once (first found)
SG_LOG( SG_TERRAIN, SG_BULK, " " << token << " " << name );
if (!found_tile_base) {
found_tile_base = true;
has_base = true;
node = osgDB::readRefNodeFile(path.str(),
staticOptions.get());
if (!node.valid()) {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
<< ": Failed to load OBJECT_BASE '"
<< name << "'" );
}
} else
SG_LOG(SG_TERRAIN, SG_BULK, " (skipped)");
} else if ( token == "OBJECT" ) {
// Load only if base is not in another file
if (!found_tile_base || has_base) {
node = osgDB::readRefNodeFile(path.str(),
staticOptions.get());
if (!node.valid()) {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
<< ": Failed to load OBJECT '"
<< name << "'" );
}
} else {
SG_LOG(SG_TERRAIN, SG_BULK, " " << token << " "
<< name << " (skipped)");
}
} else {
double lon, lat, elev, hdg;
in >> lon >> lat >> elev >> hdg;
// Always OK to load
if ( token == "OBJECT_STATIC" ) {
/// Hmm, the findDataFile should happen downstream
std::string absName = osgDB::findDataFile(name,
staticOptions.get());
if(_modelLoader) {
node = _modelLoader->loadTileModel(absName, false);
} else {
node = osgDB::readRefNodeFile(absName,
staticOptions.get());
}
if (!node.valid()) {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
<< ": Failed to load OBJECT_STATIC '"
<< name << "'" );
}
} else if ( token == "OBJECT_SHARED" ) {
if(_modelLoader) {
node = _modelLoader->loadTileModel(name, true);
} else {
/// Hmm, the findDataFile should happen in the downstream readers
std::string absName = osgDB::findDataFile(name,
sharedOptions.get());
node = osgDB::readRefNodeFile(absName,
sharedOptions.get());
}
if (!node.valid()) {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
<< ": Failed to load OBJECT_SHARED '"
<< name << "'" );
}
} else if ( token == "OBJECT_SIGN" ) {
node = SGMakeSign(opt->getMaterialLib(), name);
} else if ( token == "OBJECT_RUNWAY_SIGN" ) {
node = SGMakeRunwaySign(opt->getMaterialLib(), name);
} else {
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
<< ": Unknown token '" << token << "'" );
}
if (node.valid() && token != "OBJECT") {
osg::Matrix matrix;
matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
osg::Vec3(0, 0, 1)));
osg::MatrixTransform* matrixTransform;
matrixTransform = new osg::MatrixTransform(matrix);
matrixTransform->setDataVariance(osg::Object::STATIC);
matrixTransform->addChild(node.get());
node = matrixTransform;
}
}
if (node.valid())
group->addChild(node.get());
in >> ::skipeol;
}
} }
if (!found_tile_base && !file_mode) { // ... or generate an ocean tile on the fly
// ... or generate an ocean tile on the fly if (!foundBase) {
SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile"); 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()); osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
if ( node ) { if ( node ) {
group->addChild(node); group->addChild(node);
@ -347,7 +353,6 @@ TileEntry::loadTileByFileName(const string& fileName,
"Warning: failed to generate ocean tile!" ); "Warning: failed to generate ocean tile!" );
} }
} }
return group.release(); return group.release();
} }

View File

@ -80,8 +80,6 @@ private:
/** Time when tile expires. */ /** Time when tile expires. */
double _time_expired; double _time_expired;
static ModelLoadHelper *_modelLoader;
public: public:
// Constructor // Constructor
@ -91,7 +89,7 @@ public:
// Destructor // Destructor
~TileEntry(); ~TileEntry();
static void setModelLoadHelper(ModelLoadHelper *m) { _modelLoader=m; } static void setModelLoadHelper(ModelLoadHelper *m);
// Update the ssg transform node for this tile so it can be // Update the ssg transform node for this tile so it can be
// properly drawn relative to our (0,0,0) point // properly drawn relative to our (0,0,0) point