From 1a6bf3c28440c02a36c6d776fcb203da2c026b7d Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sun, 6 Jun 2021 22:10:52 +0100 Subject: [PATCH] WS30: Don't defer reading of line features Previously, reading LINE_FEATURE_LIST/AREA_FEATURE_LIST/COASTLINE_LIST files were not read with the .stg file, but deferred into the DelayLoadReadFileCallback. When the the Callback was executed, the files were read and appropriate terrain tiles dirtied for regeneration. However, re-generating dirtied tiles caused significant frame pauses and was disabled in commit deb802f74a1c30c951336e551a5674b50543c694 This meant that the Line/Area/Coastline features would only be rendered when the next LoD tile was generated. Which created a race condition on startup for the tile the user starts on, between the DelayLoadReadFileCallback and the tile itself. This commit reads these files at the same time as the initial terrain so that the VPB has all the data required when the tile is initially rendered. The downside is that line data is read for every tile, even if it is far away. As the line data is stored as a series of lat/lon floats, this is assumed to be OK. An alternative would be to re-instate the Visitor, and instead of dirtying existing terrain, generate the roads, and flag them to the UpdateVisitor to add to the terrain node. --- simgear/scene/tgdb/ReaderWriterSTG.cxx | 92 ++++++++++------------ simgear/scene/tgdb/VPBTechnique.cxx | 103 ++----------------------- simgear/scene/tgdb/VPBTechnique.hxx | 6 +- 3 files changed, 49 insertions(+), 152 deletions(-) diff --git a/simgear/scene/tgdb/ReaderWriterSTG.cxx b/simgear/scene/tgdb/ReaderWriterSTG.cxx index f8162595..a43832e2 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.cxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.cxx @@ -334,46 +334,6 @@ struct ReaderWriterSTG::_ModelBin { } } - if (!_lineFeatureList.empty()) { - - LineFeatureBinList lineFeatures; - - for (const auto& b : _lineFeatureList) { - // add the lineFeatures to the list - const auto path = SGPath(b._filename); - lineFeatures.push_back(LineFeatureBin(path, b._material)); - } - - VPBTechnique::addLineFeatureList(_bucket, lineFeatures, _terrainNode); - } - - if (!_areaFeatureList.empty()) { - - AreaFeatureBinList areaFeatures; - - for (const auto& b : _areaFeatureList) { - // add the lineFeatures to the list - const auto path = SGPath(b._filename); - areaFeatures.push_back(AreaFeatureBin(path, b._material)); - } - - VPBTechnique::addAreaFeatureList(_bucket, areaFeatures, _terrainNode); - } - - if (!_coastFeatureList.empty()) { - - CoastlineBinList coastFeatures; - - for (const auto& b : _coastFeatureList) { - // add the lineFeatures to the list - const auto path = SGPath(b._filename); - coastFeatures.push_back(CoastlineBin(path)); - } - - VPBTechnique::addCoastlineList(_bucket, coastFeatures, _terrainNode); - } - - return group.release(); } @@ -382,10 +342,6 @@ struct ReaderWriterSTG::_ModelBin { std::list<_Sign> _signList; std::list<_BuildingList> _buildingList; std::list<_TreeList> _treeList; - std::list<_LineFeatureList> _lineFeatureList; - std::list<_AreaFeatureList> _areaFeatureList; - std::list<_CoastlineList> _coastFeatureList; - osg::ref_ptr _terrainNode; /// The original options to use for this bunch of models osg::ref_ptr _options; @@ -745,6 +701,47 @@ struct ReaderWriterSTG::_ModelBin { bool vpb_active = SGSceneFeatures::instance()->getVPBActive(); if (vpb_active) { + + // Load any line area or coastline features, which we will need before we generate the tile. + if (!_lineFeatureListList.empty()) { + + LineFeatureBinList lineFeatures; + + for (const auto& b : _lineFeatureListList) { + // add the lineFeatures to the list + const auto path = SGPath(b._filename); + lineFeatures.push_back(LineFeatureBin(path, b._material)); + } + + VPBTechnique::addLineFeatureList(bucket, lineFeatures); + } + + if (!_areaFeatureListList.empty()) { + + AreaFeatureBinList areaFeatures; + + for (const auto& b : _areaFeatureListList) { + // add the lineFeatures to the list + const auto path = SGPath(b._filename); + areaFeatures.push_back(AreaFeatureBin(path, b._material)); + } + + VPBTechnique::addAreaFeatureList(bucket, areaFeatures); + } + + if (!_coastFeatureListList.empty()) { + + CoastlineBinList coastFeatures; + + for (const auto& b : _coastFeatureListList) { + // add the lineFeatures to the list + const auto path = SGPath(b._filename); + coastFeatures.push_back(CoastlineBin(path)); + } + + VPBTechnique::addCoastlineList(bucket, coastFeatures); + } + std::string filename = "vpb/" + bucket.gen_vpb_base() + ".osgb"; if (tile_map.count(filename) == 0) { vpb_node = osgDB::readRefNodeFile(filename, options); @@ -846,13 +843,6 @@ struct ReaderWriterSTG::_ModelBin { readFileCallback->_options = options; readFileCallback->_bucket = bucket; - if (vpb_active && vpb_node) { - readFileCallback->_lineFeatureList = _lineFeatureListList; - readFileCallback->_areaFeatureList = _areaFeatureListList; - readFileCallback->_coastFeatureList = _coastFeatureListList; - readFileCallback->_terrainNode = vpb_node; - } - osg::ref_ptr callbackOptions = new osgDB::Options; callbackOptions->setReadFileCallback(readFileCallback.get()); pagedLOD->setDatabaseOptions(callbackOptions.get()); diff --git a/simgear/scene/tgdb/VPBTechnique.cxx b/simgear/scene/tgdb/VPBTechnique.cxx index a8704a39..94891994 100644 --- a/simgear/scene/tgdb/VPBTechnique.cxx +++ b/simgear/scene/tgdb/VPBTechnique.cxx @@ -137,13 +137,12 @@ void VPBTechnique::setFilterMatrixAs(FilterType filterType) void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded) { if (!_terrainTile) return; + if (dirtyMask==0) return; OpenThreads::ScopedLock lock(_writeBufferMutex); osg::ref_ptr tile = _terrainTile; - if (dirtyMask==0) return; - osgTerrain::TileID tileID = tile->getTileID(); SG_LOG(SG_TERRAIN, SG_DEBUG, "Init of tile " << tileID.x << "," << tileID.y << " level " << tileID.level << " " << dirtyMask); @@ -2233,43 +2232,6 @@ void VPBTechnique::releaseGLObjects(osg::State* state) const if (_newBufferData.valid() && _newBufferData->_transform.valid()) _newBufferData->_transform->releaseGLObjects(state); } -// Simple vistor to check for any underlying terrain meshes that intersect with a given constraint therefore may need to be modified -// (e.g elevation lowered to ensure the terrain doesn't poke through an airport mesh, or line features generated) -class TerrainVisitor : public osg::NodeVisitor { - public: - osg::ref_ptr _constraint; // Object describing the volume to be modified. - int _dirtyMask; // Dirty mask to apply. - int _minLevel; // Minimum LoD level to modify. - TerrainVisitor( osg::ref_ptr node, int mask, int minLevel) : - osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), - _constraint(node), - _dirtyMask(mask), - _minLevel(minLevel) - { } - virtual ~TerrainVisitor() - { } - - void apply(osg::Node& node) - { - osgTerrain::TerrainTile* tile = dynamic_cast(&node); - if (tile) { - // Determine if the constraint should affect this tile. - const int level = tile->getTileID().level; - const osg::BoundingSphere tileBB = tile->getBound(); - if ((level >= _minLevel) && tileBB.intersects(_constraint->getBound())) { - // Dirty any existing terrain tiles containing this constraint, which will force regeneration - osgTerrain::TileID tileID = tile->getTileID(); - SG_LOG(SG_TERRAIN, SG_DEBUG, "Setting dirty mask for tile " << tileID.x << "," << tileID.y << " level " << tileID.level << " " << _dirtyMask); - tile->setDirtyMask(_dirtyMask); - } else if (tileBB.intersects(_constraint->getBound())) { - traverse(node); - } - } else { - traverse(node); - } - } -}; - // Add an osg object representing a contraint on the terrain mesh. The generated terrain mesh will not include any vertices that // lie above the constraint model. (Note that geometry may result in edges intersecting the constraint model in cases where there // are significantly higher vertices that lie just outside the constraint model. @@ -2277,9 +2239,6 @@ void VPBTechnique::addElevationConstraint(osg::ref_ptr constraint, os { const std::lock_guard lock(VPBTechnique::_constraint_mutex); // Lock the _constraintGroup for this scope _constraintGroup->addChild(constraint.get()); - - TerrainVisitor ftv(constraint, TerrainTile::ALL_DIRTY, 0); - terrain->accept(ftv); } // Remove a previously added constraint. E.g on model unload. @@ -2310,7 +2269,7 @@ osg::Vec3d VPBTechnique::checkAgainstElevationConstraints(osg::Vec3d origin, osg } } -void VPBTechnique::addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList, osg::ref_ptr terrainNode) +void VPBTechnique::addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList) { if (roadList.empty()) return; @@ -2320,26 +2279,9 @@ void VPBTechnique::addLineFeatureList(SGBucket bucket, LineFeatureBinList roadLi _lineFeatureLists.push_back(std::pair(bucket, roadList)); } - // We need to trigger a re-build of the appropriate Terrain tile, so create a pretend node and run the TerrainVisitor to - // "dirty" the TerrainTile that it intersects with. - osg::ref_ptr n = new osg::Node(); - - //SGVec3d coord1, coord2; - //SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon() -0.5*bucket.get_width(), bucket.get_center_lat() -0.5*bucket.get_height(), 0.0), coord1); - //SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon() +0.5*bucket.get_width(), bucket.get_center_lat() +0.5*bucket.get_height(), 0.0), coord2); - //osg::BoundingBox bbox = osg::BoundingBox(toOsg(coord1), toOsg(coord2)); - //n->setInitialBound(bbox); - - SGVec3d coord; - SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon(), bucket.get_center_lat(), 0.0), coord); - n->setInitialBound(osg::BoundingSphere(toOsg(coord), max(bucket.get_width_m(), bucket.get_height_m()))); - - SG_LOG(SG_TERRAIN, SG_DEBUG, "Adding line features to " << bucket.gen_index_str()); - TerrainVisitor ftv(n, TerrainTile::ALL_DIRTY, 0); - terrainNode->accept(ftv); } -void VPBTechnique::addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaList, osg::ref_ptr terrainNode) +void VPBTechnique::addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaList) { if (areaList.empty()) return; @@ -2348,27 +2290,9 @@ void VPBTechnique::addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaLi const std::lock_guard lock(VPBTechnique::_areaFeatureLists_mutex); // Lock the _lineFeatureLists for this scope _areaFeatureLists.push_back(std::pair(bucket, areaList)); } - - // We need to trigger a re-build of the appropriate Terrain tile, so create a pretend node and run the TerrainVisitor to - // "dirty" the TerrainTile that it intersects with. - osg::ref_ptr n = new osg::Node(); - - //SGVec3d coord1, coord2; - //SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon() -0.5*bucket.get_width(), bucket.get_center_lat() -0.5*bucket.get_height(), 0.0), coord1); - //SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon() +0.5*bucket.get_width(), bucket.get_center_lat() +0.5*bucket.get_height(), 0.0), coord2); - //osg::BoundingBox bbox = osg::BoundingBox(toOsg(coord1), toOsg(coord2)); - //n->setInitialBound(bbox); - - SGVec3d coord; - SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon(), bucket.get_center_lat(), 0.0), coord); - n->setInitialBound(osg::BoundingSphere(toOsg(coord), max(bucket.get_width_m(), bucket.get_height_m()))); - - SG_LOG(SG_TERRAIN, SG_DEBUG, "Adding line features to " << bucket.gen_index_str()); - TerrainVisitor ftv(n, TerrainTile::ALL_DIRTY, 0); - terrainNode->accept(ftv); } -void VPBTechnique::addCoastlineList(SGBucket bucket, CoastlineBinList coastline, osg::ref_ptr terrainNode) +void VPBTechnique::addCoastlineList(SGBucket bucket, CoastlineBinList coastline) { if (coastline.empty()) return; @@ -2378,28 +2302,11 @@ void VPBTechnique::addCoastlineList(SGBucket bucket, CoastlineBinList coastline, _coastFeatureLists.push_back(std::pair(bucket, coastline)); } - // We need to trigger a re-build of the appropriate Terrain tile, so create a pretend node and run the TerrainVisitor to - // "dirty" the TerrainTile that it intersects with. - osg::ref_ptr n = new osg::Node(); - - //SGVec3d coord1, coord2; - //SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon() -0.5*bucket.get_width(), bucket.get_center_lat() -0.5*bucket.get_height(), 0.0), coord1); - //SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon() +0.5*bucket.get_width(), bucket.get_center_lat() +0.5*bucket.get_height(), 0.0), coord2); - //osg::BoundingBox bbox = osg::BoundingBox(toOsg(coord1), toOsg(coord2)); - //n->setInitialBound(bbox); - - SGVec3d coord; - SGGeodesy::SGGeodToCart(SGGeod::fromDegM(bucket.get_center_lon(), bucket.get_center_lat(), 0.0), coord); - n->setInitialBound(osg::BoundingSphere(toOsg(coord), max(bucket.get_width_m(), bucket.get_height_m()))); - - SG_LOG(SG_TERRAIN, SG_DEBUG, "Adding line features to " << bucket.gen_index_str()); - TerrainVisitor ftv(n, TerrainTile::ALL_DIRTY, 0); - terrainNode->accept(ftv); } void VPBTechnique::unloadFeatures(SGBucket bucket) { - SG_LOG(SG_TERRAIN, SG_DEBUG, "Erasing all roads with entry " << bucket); + SG_LOG(SG_TERRAIN, SG_DEBUG, "Erasing all features with entry " << bucket); const std::lock_guard lock(VPBTechnique::_lineFeatureLists_mutex); // Lock the _lineFeatureLists for this scope // C++ 20... //std::erase_if(_lineFeatureLists, [bucket](BucketLineFeatureBinList p) { return p.first == bucket; } ); diff --git a/simgear/scene/tgdb/VPBTechnique.hxx b/simgear/scene/tgdb/VPBTechnique.hxx index 2f4f30c4..0e24cc26 100644 --- a/simgear/scene/tgdb/VPBTechnique.hxx +++ b/simgear/scene/tgdb/VPBTechnique.hxx @@ -97,9 +97,9 @@ class VPBTechnique : public TerrainTechnique static osg::Vec3d checkAgainstElevationConstraints(osg::Vec3d origin, osg::Vec3d vertex, float vertex_gap); // LineFeatures and AreaFeatures are draped over the underlying mesh. - static void addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList, osg::ref_ptr terrainNode); - static void addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaList, osg::ref_ptr terrainNode); - static void addCoastlineList(SGBucket bucket, CoastlineBinList areaList, osg::ref_ptr terrainNode); + static void addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList); + static void addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaList); + static void addCoastlineList(SGBucket bucket, CoastlineBinList areaList); static void unloadFeatures(SGBucket bucket); protected: