From: Mathias Froehlich Date: Tue, 6 Mar 2012 22:06:15 +0000 (+0100) Subject: scenery: rework stg loading code. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=3693e3600ca02bcec03efcb48f1f7b4452e3eb6f;p=simgear.git 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. --- diff --git a/simgear/scene/tgdb/TileEntry.cxx b/simgear/scene/tgdb/TileEntry.cxx index 166da43d..b9c726e9 100644 --- a/simgear/scene/tgdb/TileEntry.cxx +++ b/simgear/scene/tgdb/TileEntry.cxx @@ -70,6 +70,16 @@ ModelRegistryCallbackProxy 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); - - long tileIndex; - { - std::istringstream idxStream(index_str); - idxStream >> tileIndex; - } - SGBucket tile_bucket(tileIndex); - const string basePath = tile_bucket.gen_base_path(); - - bool found_tile_base = false; - - SGPath object_base; - vector objects; - - 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) + 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. + // 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 - continue; + } } + } 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; + } - bool has_base = false; + std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT"); + osg::ref_ptr opt; + opt = SGReaderWriterOptions::copyOrCreate(options); - SGPath tile_path = path_list[i]; - tile_path.append(basePath); + bool found_tile_base = false; + osg::ref_ptr group = new osg::Group; + for (unsigned i = 0; i < 2; ++i) { - SGPath basename = tile_path; - basename.append( index_str ); + if (absoluteFileName[i].empty()) + continue; - SG_LOG( SG_TERRAIN, SG_DEBUG, " Trying " << basename.str() ); + sg_gzifstream in( absoluteFileName[i] ); + if ( !in.is_open() ) + continue; + SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName[i]); - // Check for master .stg (scene terra gear) file - SGPath stg_name = basename; - stg_name.concat( ".stg" ); + std::string filePath = osgDB::getFilePath(absoluteFileName[i]); - sg_gzifstream in( stg_name.str() ); - if ( !in.is_open() ) - continue; + osg::ref_ptr staticOptions; + staticOptions = SGReaderWriterOptions::copyOrCreate(options); + staticOptions->getDatabasePathList().clear(); + staticOptions->getDatabasePathList().push_back(filePath); + staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE); + + osg::ref_ptr 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 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; - - object_base = tile_path; - object_base.append(name); - + + 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)"); - // 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 { + 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 << "'" ); + } - } else if ( token == "OBJECT_SIGN" ) { - objects.push_back(new Object(OBJECT_SIGN, token, tile_path, in)); + 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; + } + } - } else if ( token == "OBJECT_RUNWAY_SIGN" ) { - objects.push_back(new Object(OBJECT_RUNWAY_SIGN, token, tile_path, in)); + if (node.valid()) + group->addChild(node.get()); - } else { - SG_LOG( SG_TERRAIN, SG_DEBUG, - "Unknown token '" << token << "' in " << stg_name.str() ); - in >> ::skipws; - } + in >> ::skipeol; } } - osg::ref_ptr 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 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 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 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