WS30 - Improve line feature loading

- Mark tiles that require re-loading after .STG loading, removing a race
condition
- Only re-build line features rather than the entire tile
This commit is contained in:
Stuart Buchanan
2021-03-04 20:20:41 +00:00
parent 40ead1f71a
commit ce197ea828
4 changed files with 71 additions and 24 deletions

View File

@@ -82,7 +82,7 @@ LineFeatureBin::LineFeatureBin(const SGPath& absoluteFileName, const std::string
}
while (true) {
float lon = 0.0f, lat=0.0f;
double lon = 0.0f, lat=0.0f;
in >> lon >> lat;
if (in.bad() || in.fail()) {

View File

@@ -331,7 +331,7 @@ struct ReaderWriterSTG::_ModelBin {
lineFeatures.push_back(LineFeatureBin(path, b._material));
}
VPBTechnique::addLineFeatureList(_bucket, lineFeatures);
VPBTechnique::addLineFeatureList(_bucket, lineFeatures, _terrainNode);
}
return group.release();
@@ -343,6 +343,7 @@ struct ReaderWriterSTG::_ModelBin {
std::list<_BuildingList> _buildingList;
std::list<_TreeList> _treeList;
std::list<_LineFeatureList> _lineFeatureList;
osg::ref_ptr<osg::Node> _terrainNode;
/// The original options to use for this bunch of models
osg::ref_ptr<SGReaderWriterOptions> _options;
@@ -673,11 +674,12 @@ struct ReaderWriterSTG::_ModelBin {
return true;
}
std::map<std::string, bool> tile_map;
std::map<std::string, osg::ref_ptr<osg::Node> > tile_map;
osg::Node* load(const SGBucket& bucket, const osgDB::Options* opt)
{
osg::ref_ptr<SGReaderWriterOptions> options;
osg::ref_ptr<osg::Node> vpb_node;
options = SGReaderWriterOptions::copyOrCreate(opt);
float pagedLODExpiry = atoi(options->getPluginStringData("SimGear::PAGED_LOD_EXPIRY").c_str());
@@ -692,15 +694,17 @@ struct ReaderWriterSTG::_ModelBin {
if (vpb_active) {
std::string filename = "vpb/" + bucket.gen_vpb_base() + ".osgb";
if (tile_map.count(filename) == 0) {
auto vpb_node = osgDB::readRefNodeFile(filename, options);
vpb_node = osgDB::readRefNodeFile(filename, options);
if (!vpb_node.valid()) {
SG_LOG(SG_TERRAIN, SG_WARN, "Failure to load: " <<filename);
}
else {
terrainGroup->addChild(vpb_node);
tile_map[filename] = true;
tile_map[filename] = vpb_node;
SG_LOG(SG_TERRAIN, SG_INFO, "Loading: " << filename);
}
} else {
vpb_node = tile_map[filename];
}
// OBJECTs include airports
@@ -777,10 +781,15 @@ struct ReaderWriterSTG::_ModelBin {
readFileCallback->_objectStaticList = _objectStaticList;
readFileCallback->_buildingList = _buildingListList;
readFileCallback->_treeList = _treeListList;
readFileCallback->_lineFeatureList = _lineFeatureListList;
readFileCallback->_signList = _signList;
readFileCallback->_options = options;
readFileCallback->_bucket = bucket;
if (vpb_active) {
readFileCallback->_lineFeatureList = _lineFeatureListList;
readFileCallback->_terrainNode = vpb_node;
}
osg::ref_ptr<osgDB::Options> callbackOptions = new osgDB::Options;
callbackOptions->setReadFileCallback(readFileCallback.get());
pagedLOD->setDatabaseOptions(callbackOptions.get());

View File

@@ -46,7 +46,7 @@
#include <simgear/scene/model/model.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include "VPBTechnique.hxx"
#include "TreeBin.hxx"
@@ -150,6 +150,20 @@ void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded)
osg::Vec3d centerModel = computeCenterModel(*buffer, masterLocator);
// We use TerrainTile::IMAGERY_DIRTY to (re-)generate features from a .STG file, such as line features
// We use TerrainTile::ELEVATION_DIRTY to regenerate the geometry
if (dirtyMask & TerrainTile::ELEVATION_DIRTY) {
generateGeometry(*buffer, masterLocator, centerModel);
applyColorLayers(*buffer, masterLocator);
applyTrees(*buffer, masterLocator);
}
if (dirtyMask & TerrainTile::IMAGERY_DIRTY) {
applyLineFeatures(*buffer, masterLocator);
}
/*
if ((dirtyMask & TerrainTile::IMAGERY_DIRTY)==0)
{
generateGeometry(*buffer, masterLocator, centerModel);
@@ -175,7 +189,7 @@ void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded)
applyTrees(*buffer, masterLocator);
applyLineFeatures(*buffer, masterLocator);
}
*/
if (buffer->_transform.valid()) buffer->_transform->setThreadSafeRefUnref(true);
if (!_currentBufferData || !assumeMultiThreaded)
@@ -1749,17 +1763,16 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator)
void VPBTechnique::applyLineFeatures(BufferData& buffer, Locator* masterLocator)
{
int roads_lod_range = 6;
int line_features_lod_range = 6;
SGPropertyNode* propertyNode = _options->getPropertyNode().get();
if (propertyNode) {
roads_lod_range = propertyNode->getIntValue("/sim/rendering/static-lod/roads-lod-range", roads_lod_range);
line_features_lod_range = propertyNode->getIntValue("/sim/rendering/static-lod/line-features-lod-range", line_features_lod_range);
}
// Do not generate vegetation for tiles too far away
//if (_terrainTile->getTileID().level < roads_lod_range) {
if (_terrainTile->getTileID().level < roads_lod_range) {
if (_terrainTile->getTileID().level < line_features_lod_range) {
return;
}
@@ -1835,6 +1848,7 @@ void VPBTechnique::applyLineFeatures(BufferData& buffer, Locator* masterLocator)
geode->setMaterial(mat);
geode->setEffect(mat->get_one_effect(0));
geode->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
buffer._transform->addChild(geode);
}
}
@@ -1853,6 +1867,7 @@ void VPBTechnique::generateLineFeature(BufferData& buffer, Locator* masterLocato
auto road_iter = road._nodes.begin();
// This is expensive.
SGGeodesy::SGGeodToCart(SGGeod(*road_iter), tmp);
ma = toOsg(tmp) - modelCenter;
@@ -1864,6 +1879,8 @@ void VPBTechnique::generateLineFeature(BufferData& buffer, Locator* masterLocato
road_iter++;
for (; road_iter != road._nodes.end(); road_iter++) {
// This is expensive.
SGGeodesy::SGGeodToCart(SGGeod(*road_iter), tmp);
mb = toOsg(tmp) - modelCenter;
@@ -2029,14 +2046,16 @@ 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 contain a given constraint and therefore may need to be modified
// (e.g elevation lowered to ensure the terrain doesn't poke through an airport mesh)
// 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, orn roads generated)
class TerrainVisitor : public osg::NodeVisitor {
public:
osg::ref_ptr<osg::Node> _constraint; // Object to flatten the terrain under.
TerrainVisitor( osg::ref_ptr<osg::Node> node) :
int _dirtyMask; // Dirty mask to apply.
TerrainVisitor( osg::ref_ptr<osg::Node> node, int mask) :
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_constraint(node)
_constraint(node),
_dirtyMask(mask)
{ }
virtual ~TerrainVisitor()
{ }
@@ -2049,9 +2068,8 @@ class TerrainVisitor : public osg::NodeVisitor {
// Determine if the constraint should affect this tile.
const osg::BoundingSphere tileBB = tile->getBound();
if (tileBB.intersects(_constraint->getBound())) {
// Dirty any existing terrain tiles containing this constraint, which will force regeneration of the elevation mesh.
tile->setDirty(true);
//tile->init(tile->getDirtyMask(), true);
// Dirty any existing terrain tiles containing this constraint, which will force regeneration
tile->setDirtyMask(_dirtyMask);
}
} else {
traverse(node);
@@ -2067,7 +2085,7 @@ 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
_constraintGroup->addChild(constraint.get());
TerrainVisitor ftv(constraint);
TerrainVisitor ftv(constraint, TerrainTile::ALL_DIRTY);
terrain->accept(ftv);
}
@@ -2099,10 +2117,30 @@ osg::Vec3d VPBTechnique::checkAgainstElevationConstraints(osg::Vec3d origin, osg
}
}
void VPBTechnique::addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList)
void VPBTechnique::addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList, osg::ref_ptr<osg::Node> terrainNode)
{
const std::lock_guard<std::mutex> lock(VPBTechnique::_lineFeatureLists_mutex); // Lock the _lineFeatureLists for this scope
_lineFeatureLists.push_back(std::pair(bucket, roadList));
// Block to mutex the List alone
{
const std::lock_guard<std::mutex> lock(VPBTechnique::_lineFeatureLists_mutex); // Lock the _lineFeatureLists for this scope
_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);
//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())));
osg::BoundingBox bbox = osg::BoundingBox(toOsg(coord1), toOsg(coord2));
n->setInitialBound(bbox);
SG_LOG(SG_TERRAIN, SG_DEBUG, "Adding line features to " << bucket.gen_index_str());
TerrainVisitor ftv(n, TerrainTile::IMAGERY_DIRTY);
terrainNode->accept(ftv);
}
void VPBTechnique::unloadLineFeatures(SGBucket bucket)

View File

@@ -95,7 +95,7 @@ class VPBTechnique : public TerrainTechnique
static osg::Vec3d checkAgainstElevationConstraints(osg::Vec3d origin, osg::Vec3d vertex, float vertex_gap);
// LineFeatures are draped over the underlying mesh.
static void addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList);
static void addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList, osg::ref_ptr<osg::Node> terrainNode);
static void unloadLineFeatures(SGBucket bucket);
protected: