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
deb802f74a
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.
This commit is contained in:
parent
16778e5c02
commit
1a6bf3c284
@ -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();
|
return group.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,10 +342,6 @@ struct ReaderWriterSTG::_ModelBin {
|
|||||||
std::list<_Sign> _signList;
|
std::list<_Sign> _signList;
|
||||||
std::list<_BuildingList> _buildingList;
|
std::list<_BuildingList> _buildingList;
|
||||||
std::list<_TreeList> _treeList;
|
std::list<_TreeList> _treeList;
|
||||||
std::list<_LineFeatureList> _lineFeatureList;
|
|
||||||
std::list<_AreaFeatureList> _areaFeatureList;
|
|
||||||
std::list<_CoastlineList> _coastFeatureList;
|
|
||||||
osg::ref_ptr<osg::Node> _terrainNode;
|
|
||||||
|
|
||||||
/// The original options to use for this bunch of models
|
/// The original options to use for this bunch of models
|
||||||
osg::ref_ptr<SGReaderWriterOptions> _options;
|
osg::ref_ptr<SGReaderWriterOptions> _options;
|
||||||
@ -745,6 +701,47 @@ struct ReaderWriterSTG::_ModelBin {
|
|||||||
|
|
||||||
bool vpb_active = SGSceneFeatures::instance()->getVPBActive();
|
bool vpb_active = SGSceneFeatures::instance()->getVPBActive();
|
||||||
if (vpb_active) {
|
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";
|
std::string filename = "vpb/" + bucket.gen_vpb_base() + ".osgb";
|
||||||
if (tile_map.count(filename) == 0) {
|
if (tile_map.count(filename) == 0) {
|
||||||
vpb_node = osgDB::readRefNodeFile(filename, options);
|
vpb_node = osgDB::readRefNodeFile(filename, options);
|
||||||
@ -846,13 +843,6 @@ struct ReaderWriterSTG::_ModelBin {
|
|||||||
readFileCallback->_options = options;
|
readFileCallback->_options = options;
|
||||||
readFileCallback->_bucket = bucket;
|
readFileCallback->_bucket = bucket;
|
||||||
|
|
||||||
if (vpb_active && vpb_node) {
|
|
||||||
readFileCallback->_lineFeatureList = _lineFeatureListList;
|
|
||||||
readFileCallback->_areaFeatureList = _areaFeatureListList;
|
|
||||||
readFileCallback->_coastFeatureList = _coastFeatureListList;
|
|
||||||
readFileCallback->_terrainNode = vpb_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<osgDB::Options> callbackOptions = new osgDB::Options;
|
osg::ref_ptr<osgDB::Options> callbackOptions = new osgDB::Options;
|
||||||
callbackOptions->setReadFileCallback(readFileCallback.get());
|
callbackOptions->setReadFileCallback(readFileCallback.get());
|
||||||
pagedLOD->setDatabaseOptions(callbackOptions.get());
|
pagedLOD->setDatabaseOptions(callbackOptions.get());
|
||||||
|
@ -137,13 +137,12 @@ void VPBTechnique::setFilterMatrixAs(FilterType filterType)
|
|||||||
void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded)
|
void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded)
|
||||||
{
|
{
|
||||||
if (!_terrainTile) return;
|
if (!_terrainTile) return;
|
||||||
|
if (dirtyMask==0) return;
|
||||||
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_writeBufferMutex);
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_writeBufferMutex);
|
||||||
|
|
||||||
osg::ref_ptr<TerrainTile> tile = _terrainTile;
|
osg::ref_ptr<TerrainTile> tile = _terrainTile;
|
||||||
|
|
||||||
if (dirtyMask==0) return;
|
|
||||||
|
|
||||||
osgTerrain::TileID tileID = tile->getTileID();
|
osgTerrain::TileID tileID = tile->getTileID();
|
||||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "Init of tile " << tileID.x << "," << tileID.y << " level " << tileID.level << " " << dirtyMask);
|
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);
|
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<osg::Node> _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<osg::Node> 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<osgTerrain::TerrainTile*>(&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
|
// 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
|
// 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.
|
// are significantly higher vertices that lie just outside the constraint model.
|
||||||
@ -2277,9 +2239,6 @@ void VPBTechnique::addElevationConstraint(osg::ref_ptr<osg::Node> constraint, os
|
|||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> lock(VPBTechnique::_constraint_mutex); // Lock the _constraintGroup for this scope
|
const std::lock_guard<std::mutex> lock(VPBTechnique::_constraint_mutex); // Lock the _constraintGroup for this scope
|
||||||
_constraintGroup->addChild(constraint.get());
|
_constraintGroup->addChild(constraint.get());
|
||||||
|
|
||||||
TerrainVisitor ftv(constraint, TerrainTile::ALL_DIRTY, 0);
|
|
||||||
terrain->accept(ftv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a previously added constraint. E.g on model unload.
|
// 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<osg::Node> terrainNode)
|
void VPBTechnique::addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList)
|
||||||
{
|
{
|
||||||
if (roadList.empty()) return;
|
if (roadList.empty()) return;
|
||||||
|
|
||||||
@ -2320,26 +2279,9 @@ void VPBTechnique::addLineFeatureList(SGBucket bucket, LineFeatureBinList roadLi
|
|||||||
_lineFeatureLists.push_back(std::pair(bucket, roadList));
|
_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<osg::Node> 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<osg::Node> terrainNode)
|
void VPBTechnique::addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaList)
|
||||||
{
|
{
|
||||||
if (areaList.empty()) return;
|
if (areaList.empty()) return;
|
||||||
|
|
||||||
@ -2348,27 +2290,9 @@ void VPBTechnique::addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaLi
|
|||||||
const std::lock_guard<std::mutex> lock(VPBTechnique::_areaFeatureLists_mutex); // Lock the _lineFeatureLists for this scope
|
const std::lock_guard<std::mutex> lock(VPBTechnique::_areaFeatureLists_mutex); // Lock the _lineFeatureLists for this scope
|
||||||
_areaFeatureLists.push_back(std::pair(bucket, areaList));
|
_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<osg::Node> 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<osg::Node> terrainNode)
|
void VPBTechnique::addCoastlineList(SGBucket bucket, CoastlineBinList coastline)
|
||||||
{
|
{
|
||||||
if (coastline.empty()) return;
|
if (coastline.empty()) return;
|
||||||
|
|
||||||
@ -2378,28 +2302,11 @@ void VPBTechnique::addCoastlineList(SGBucket bucket, CoastlineBinList coastline,
|
|||||||
_coastFeatureLists.push_back(std::pair(bucket, 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<osg::Node> 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)
|
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<std::mutex> lock(VPBTechnique::_lineFeatureLists_mutex); // Lock the _lineFeatureLists for this scope
|
const std::lock_guard<std::mutex> lock(VPBTechnique::_lineFeatureLists_mutex); // Lock the _lineFeatureLists for this scope
|
||||||
// C++ 20...
|
// C++ 20...
|
||||||
//std::erase_if(_lineFeatureLists, [bucket](BucketLineFeatureBinList p) { return p.first == bucket; } );
|
//std::erase_if(_lineFeatureLists, [bucket](BucketLineFeatureBinList p) { return p.first == bucket; } );
|
||||||
|
@ -97,9 +97,9 @@ class VPBTechnique : public TerrainTechnique
|
|||||||
static osg::Vec3d checkAgainstElevationConstraints(osg::Vec3d origin, osg::Vec3d vertex, float vertex_gap);
|
static osg::Vec3d checkAgainstElevationConstraints(osg::Vec3d origin, osg::Vec3d vertex, float vertex_gap);
|
||||||
|
|
||||||
// LineFeatures and AreaFeatures are draped over the underlying mesh.
|
// LineFeatures and AreaFeatures are draped over the underlying mesh.
|
||||||
static void addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList, osg::ref_ptr<osg::Node> terrainNode);
|
static void addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList);
|
||||||
static void addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaList, osg::ref_ptr<osg::Node> terrainNode);
|
static void addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaList);
|
||||||
static void addCoastlineList(SGBucket bucket, CoastlineBinList areaList, osg::ref_ptr<osg::Node> terrainNode);
|
static void addCoastlineList(SGBucket bucket, CoastlineBinList areaList);
|
||||||
static void unloadFeatures(SGBucket bucket);
|
static void unloadFeatures(SGBucket bucket);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
Loading…
Reference in New Issue
Block a user