scenery: rework stg loading code.

This change reworks the code block loading the stg files.
It fixes a long standing problem where we end up loading
similar models from different entries of the scenery
path at the same geographic location. This usually results
in models that appear to have z figting, even if the isolated
model does not show z fighting at all.
As a result the lookup sequence for models and scenery has
changed a bit. This should be closer to what it supposed to
be and did not show any problems on the areas and sceneries
I tested.
This commit is contained in:
Mathias Froehlich
2012-03-06 23:06:15 +01:00
parent 69debc6777
commit 3693e3600c

View File

@@ -70,6 +70,16 @@ ModelRegistryCallbackProxy<LoadOnlyCallback> g_sptCallbackProxy("spt");
}
static SGBucket getBucketFromFileName(const std::string& fileName)
{
std::istringstream ss(osgDB::getNameLessExtension(fileName));
long index;
ss >> index;
if (ss.fail())
return SGBucket();
return SGBucket(index);
}
// Constructor
TileEntry::TileEntry ( const SGBucket& b )
: tile_bucket( b ),
@@ -107,18 +117,6 @@ TileEntry::~TileEntry ()
{
}
static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
double lon, double elev, double hdg)
{
SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
obj_pos = makeZUpFrame(geod);
// hdg is not a compass heading, but a counter-clockwise rotation
// around the Z axis
obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
0.0, 0.0, 1.0));
}
// Update the ssg transform node for this tile so it can be
// properly drawn relative to our (0,0,0) point
void TileEntry::prep_ssg_node(float vis) {
@@ -130,259 +128,227 @@ void TileEntry::prep_ssg_node(float vis) {
_node->setRange( 0, 0, vis + bounding_radius );
}
typedef enum {
OBJECT,
OBJECT_SHARED,
OBJECT_STATIC,
OBJECT_SIGN,
OBJECT_RUNWAY_SIGN
} object_type;
// storage class for deferred object processing in TileEntry::load()
struct Object {
Object(object_type t, const string& token, const SGPath& p,
std::istream& in)
: type(t), path(p)
{
in >> name;
if (type != OBJECT)
in >> lon >> lat >> elev >> hdg;
in >> ::skipeol;
if (type == OBJECT)
SG_LOG(SG_TERRAIN, SG_BULK, " " << token << " " << name);
else
SG_LOG(SG_TERRAIN, SG_BULK, " " << token << " " << name << " lon=" <<
lon << " lat=" << lat << " elev=" << elev << " hdg=" << hdg);
}
object_type type;
string name;
SGPath path;
double lon, lat, elev, hdg;
};
// Work in progress... load the tile based entirely by name cuz that's
// what we'll want to do with the database pager.
osg::Node*
TileEntry::loadTileByFileName(const string& fileName,
const osgDB::Options* options)
{
std::string index_str = osgDB::getNameLessExtension(fileName);
index_str = osgDB::getSimpleFileName(index_str);
SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
long tileIndex;
{
std::istringstream idxStream(index_str);
idxStream >> tileIndex;
// 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.
// 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.
std::string simpleFileName = osgDB::getSimpleFileName(fileName);
SGBucket bucket = getBucketFromFileName(simpleFileName);
bool file_mode = false;
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.
// We still apply the search path algorithms for relative files.
absoluteFileName[0] = osgDB::findDataFile(fileName, options);
// Do not generate an ocean tile if we have no btg
file_mode = true;
}
SGBucket tile_bucket(tileIndex);
const string basePath = tile_bucket.gen_base_path();
std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
osg::ref_ptr<SGReaderWriterOptions> opt;
opt = SGReaderWriterOptions::copyOrCreate(options);
bool found_tile_base = false;
osg::ref_ptr<osg::Group> group = new osg::Group;
for (unsigned i = 0; i < 2; ++i) {
SGPath object_base;
vector<const Object*> objects;
if (absoluteFileName[i].empty())
continue;
SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << index_str );
osgDB::FilePathList path_list=options->getDatabasePathList();
// Make sure we find the original filename here...
std::string filePath = osgDB::getFilePath(fileName);
if (!filePath.empty()) {
SGPath p(filePath);
p.append("..");
p.append("..");
path_list.push_front(p.str());
}
// scan and parse all files and store information
for (unsigned int i = 0; i < path_list.size(); i++) {
// If we found a terrain tile in Terrain/, we have to process the
// Objects/ dir in the same group, too, before we can stop scanning.
// FGGlobals::set_fg_scenery() inserts an empty string to path_list
// as marker.
if (path_list[i].empty()) {
if (found_tile_base)
break;
else
continue;
}
bool has_base = false;
SGPath tile_path = path_list[i];
tile_path.append(basePath);
SGPath basename = tile_path;
basename.append( index_str );
SG_LOG( SG_TERRAIN, SG_DEBUG, " Trying " << basename.str() );
// Check for master .stg (scene terra gear) file
SGPath stg_name = basename;
stg_name.concat( ".stg" );
sg_gzifstream in( stg_name.str() );
sg_gzifstream in( absoluteFileName[i] );
if ( !in.is_open() )
continue;
SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName[i]);
std::string filePath = osgDB::getFilePath(absoluteFileName[i]);
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());
sharedOptions->getDatabasePathList().push_back(fg_root);
bool has_base = false;
while ( ! in.eof() ) {
string token;
std::string token;
in >> token;
// No comment
if ( token.empty() || token[0] == '#' ) {
in >> ::skipeol;
continue;
in >> ::skipeol;
continue;
}
// Load only once (first found)
// 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" ) {
string name;
in >> name >> ::skipws;
// 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());
object_base = tile_path;
object_base.append(name);
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)");
// Load only if base is not in another file
} else if ( token == "OBJECT" ) {
if (!found_tile_base || has_base)
objects.push_back(new Object(OBJECT, token, tile_path, in));
else {
string name;
in >> name >> ::skipeol;
// 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)");
<< name << " (skipped)");
}
// Always OK to load
} else if ( token == "OBJECT_STATIC" ) {
objects.push_back(new Object(OBJECT_STATIC, token, tile_path, in));
} else if ( token == "OBJECT_SHARED" ) {
objects.push_back(new Object(OBJECT_SHARED, token, tile_path, in));
} else if ( token == "OBJECT_SIGN" ) {
objects.push_back(new Object(OBJECT_SIGN, token, tile_path, in));
} else if ( token == "OBJECT_RUNWAY_SIGN" ) {
objects.push_back(new Object(OBJECT_RUNWAY_SIGN, token, tile_path, in));
} else {
SG_LOG( SG_TERRAIN, SG_DEBUG,
"Unknown token '" << token << "' in " << stg_name.str() );
in >> ::skipws;
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;
}
}
osg::ref_ptr<SGReaderWriterOptions> opt;
opt = SGReaderWriterOptions::copyOrCreate(options);
// obj_load() will generate ground lighting for us ...
osg::Group* new_tile = new osg::Group;
if (found_tile_base) {
// load tile if found ...
osg::ref_ptr<osg::Node> node;
node = osgDB::readRefNodeFile(object_base.str(), opt.get());
if (node.valid())
new_tile->addChild(node);
} else {
if (!found_tile_base && !file_mode) {
// ... or generate an ocean tile on the fly
SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
osg::Node* node = SGOceanTile(tile_bucket, opt->getMaterialLib());
osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
if ( node ) {
new_tile->addChild(node);
group->addChild(node);
} else {
SG_LOG( SG_TERRAIN, SG_ALERT,
"Warning: failed to generate ocean tile!" );
}
}
// now that we have a valid center, process all the objects
for (unsigned int j = 0; j < objects.size(); j++) {
const Object *obj = objects[j];
if (obj->type == OBJECT) {
SGPath custom_path = obj->path;
custom_path.append( obj->name );
osg::ref_ptr<osg::Node> node;
node = osgDB::readRefNodeFile(custom_path.str(), opt.get());
if (node.valid())
new_tile->addChild(node);
} else if (obj->type == OBJECT_SHARED || obj->type == OBJECT_STATIC) {
// object loading is deferred to main render thread,
// but lets figure out the paths right now.
SGPath custom_path;
if ( obj->type == OBJECT_STATIC ) {
custom_path = obj->path;
} else {
// custom_path = globals->get_fg_root();
}
custom_path.append( obj->name );
osg::ref_ptr<osg::Node> model;
if(_modelLoader)
model = _modelLoader->loadTileModel(custom_path.str(),
obj->type == OBJECT_SHARED);
else
model = osgDB::readRefNodeFile(custom_path.str(), opt.get());
if (model.valid()) {
osg::Matrix obj_pos;
WorldCoordinate( obj_pos, obj->lat, obj->lon, obj->elev, obj->hdg );
osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
obj_trans->setDataVariance(osg::Object::STATIC);
obj_trans->setMatrix( obj_pos );
// wire as much of the scene graph together as we can
new_tile->addChild( obj_trans );
obj_trans->addChild(model.get());
}
} else if (obj->type == OBJECT_SIGN || obj->type == OBJECT_RUNWAY_SIGN) {
// load the object itself
SGPath custom_path = obj->path;
custom_path.append( obj->name );
osg::Node *custom_obj = 0;
if (obj->type == OBJECT_SIGN)
custom_obj = SGMakeSign(opt->getMaterialLib(), obj->name);
else
custom_obj = SGMakeRunwaySign(opt->getMaterialLib(), obj->name);
// wire the pieces together
if ( custom_obj != NULL ) {
osg::Matrix obj_pos;
WorldCoordinate( obj_pos, obj->lat, obj->lon, obj->elev, obj->hdg );
osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
obj_trans->setDataVariance(osg::Object::STATIC);
obj_trans->setMatrix( obj_pos );
obj_trans->addChild( custom_obj );
new_tile->addChild( obj_trans );
}
}
delete obj;
}
return new_tile;
return group.release();
}
void