From deb802f74a1c30c951336e551a5674b50543c694 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sun, 28 Mar 2021 19:50:26 +0100 Subject: [PATCH] WS30: Improve random vegetation, reduce frame paus - Generate vegetation based on underlying landclass material mapping - Reduce frame pauses by removing terrain init from update visitor - Fix a couple of small timing windows causing crashes. --- simgear/scene/tgdb/ReaderWriterSTG.cxx | 2 +- simgear/scene/tgdb/VPBTechnique.cxx | 347 ++++++++++++++----------- simgear/scene/tgdb/VPBTechnique.hxx | 5 +- 3 files changed, 196 insertions(+), 158 deletions(-) diff --git a/simgear/scene/tgdb/ReaderWriterSTG.cxx b/simgear/scene/tgdb/ReaderWriterSTG.cxx index 7eebc870..ca6ba984 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.cxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.cxx @@ -785,7 +785,7 @@ struct ReaderWriterSTG::_ModelBin { readFileCallback->_options = options; readFileCallback->_bucket = bucket; - if (vpb_active) { + if (vpb_active && vpb_node) { readFileCallback->_lineFeatureList = _lineFeatureListList; readFileCallback->_terrainNode = vpb_node; } diff --git a/simgear/scene/tgdb/VPBTechnique.cxx b/simgear/scene/tgdb/VPBTechnique.cxx index 50750365..cb4fc90f 100644 --- a/simgear/scene/tgdb/VPBTechnique.cxx +++ b/simgear/scene/tgdb/VPBTechnique.cxx @@ -277,6 +277,12 @@ osg::Vec3d VPBTechnique::computeCenterModel(BufferData& buffer, Locator* masterL return centerModel; } +const SGGeod VPBTechnique::computeCenterGeod(BufferData& buffer, Locator* masterLocator) +{ + const osg::Vec3d world = buffer._transform->getMatrix().getTrans(); + return SGGeod::fromCart(toSG(world)); +} + class VertexNormalGenerator { public: @@ -756,17 +762,12 @@ void VPBTechnique::generateGeometry(BufferData& buffer, Locator* masterLocator, // Determine the correct Effect for this, based on a material lookup taking into account // the lat/lon of the center. SGMaterialLibPtr matlib = _options->getMaterialLib(); - osg::Vec3d tileloc = computeCenter(buffer, masterLocator); - osg::Vec3d world; - masterLocator->convertLocalToModel(tileloc, world); - const SGVec3d world2 = SGVec3d(world.x(), world.y(), world.z()); - const SGGeod loc = SGGeod::fromCart(world2); + const SGGeod loc = computeCenterGeod(buffer, masterLocator); SGPropertyNode_ptr landEffectProp = new SGPropertyNode(); SGPropertyNode_ptr waterEffectProp = new SGPropertyNode(); if (matlib) { - // Determine the center of the tile, sadly non-trivial. SG_LOG(SG_TERRAIN, SG_DEBUG, "Applying VPB material " << loc); SGMaterialCache* matcache = _options->getMaterialLib()->generateMatCache(loc, _options); atlas = matcache->getAtlas(); @@ -1030,7 +1031,7 @@ void VPBTechnique::generateGeometry(BufferData& buffer, Locator* masterLocator, else if (numValid==3) { // If this is a triangle, we need to look for exactly 3 our of the four - // vertices to be in water, as the fourth will be fales, as above. + // vertices to be in water, as the fourth will be false, as above. unsigned int water_count = 0; if (w00) water_count++; if (w01) water_count++; @@ -1236,16 +1237,16 @@ void VPBTechnique::generateGeometry(BufferData& buffer, Locator* masterLocator, auto t = top_left - bottom_left; auto u = top_right - top_left; auto v = top_right - bottom_right; - tile_width = 0.5 * (s.length() + u.length()); - tile_height = 0.5 * (t.length() + v.length()); + buffer._width = 0.5 * (s.length() + u.length()); + buffer._height = 0.5 * (t.length() + v.length()); } SG_LOG(SG_TERRAIN, SG_DEBUG, "Tile Level " << _terrainTile->getTileID().level << " width " << tile_width << " height " << tile_height); - osg::ref_ptr twu = new osg::Uniform("tile_width", tile_width); + osg::ref_ptr twu = new osg::Uniform("tile_width", buffer._width); landStateSet->addUniform(twu); waterStateSet->addUniform(twu); - osg::ref_ptr thu = new osg::Uniform("tile_height", tile_height); + osg::ref_ptr thu = new osg::Uniform("tile_height", buffer._height); landStateSet->addUniform(thu); waterStateSet->addUniform(thu); @@ -1273,29 +1274,23 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator) if (!colorLayer) return; osg::Image* image = colorLayer->getImage(); - if (!image) return; + if (!image || ! image->valid()) return; // First time generating this texture, so process to change landclass IDs to texure indexes. SGPropertyNode_ptr landEffectProp; - osg::Vec3d world; - SGGeod loc; - // Determine the center of the tile, sadly non-trivial. - osg::Vec3d tileloc = computeCenter(buffer, masterLocator); - masterLocator->convertLocalToModel(tileloc, world); - const SGVec3d world2 = SGVec3d(world.x(), world.y(), world.z()); - loc = SGGeod::fromCart(world2); + const SGGeod loc = computeCenterGeod(buffer, masterLocator); SG_LOG(SG_TERRAIN, SG_DEBUG, "Applying VPB material " << loc); SGMaterialCache::Atlas atlas = matlib->generateMatCache(loc, _options)->getAtlas(); SGMaterialCache::AtlasIndex atlasIndex = atlas.index; // Set the "g" color channel to an index into the atlas index. - for (int s = 0; s < image->s(); s++) { - for (int t = 0; t < image->t(); t++) { - osg::Vec4 c = image->getColor(s, t); + for (unsigned int s = 0; s < (unsigned int) image->s(); s++) { + for (unsigned int t = 0; t < (unsigned int) image->t(); t++) { + osg::Vec4d c = image->getColor(s, t); int i = int(round(c.x() * 255.0)); - c.set(c.x(), (float) (atlasIndex[i] / 255.0), c.z(), c.w() ); + c.set(c.x(), (double) (atlasIndex[i] / 255.0), c.z(), c.w() ); image->setColor(c, s, t); } } @@ -1335,16 +1330,12 @@ double VPBTechnique::det2(const osg::Vec2d a, const osg::Vec2d b) } osg::Vec2d* VPBTechnique::_randomOffsets = 0; +int idx =0; -osg::Vec2d VPBTechnique::getRandomOffset(int lon, int lat) +osg::Vec2d VPBTechnique::getRandomOffset() { const unsigned initial_seed = 42; - const int N_rnd = 1; // 1 is a uniform random distribution, increase to 5 or - // more for plantation - const int bits = 5; - const int N = 1 << (2 * bits); - const int N_2 = 1 << bits; - const int mask = N_2 - 1; + const int N = 100; if (!_randomOffsets) { @@ -1354,31 +1345,20 @@ osg::Vec2d VPBTechnique::getRandomOffset(int lon, int lat) for (int i = 0; i < N; i++) { - double x = 0.0; - double y = 0.0; - - for (int j = 0; j < N_rnd; j++) - { - x += mt_rand(&seed); - y += mt_rand(&seed); - } - - x = x / N_rnd - 0.5; - y = y / N_rnd - 0.5; - _randomOffsets[i].set(x, y); + _randomOffsets[i].set(mt_rand(&seed), mt_rand(&seed)); } - } - return _randomOffsets[N_2 * (lat & mask) + (lon & mask)]; + idx = (idx + 1) % N; + return _randomOffsets[idx]; } + void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) { bool use_random_vegetation = false; float vegetation_density = 1.0; int vegetation_lod_level = 6; - float randomness = 1.0; SGPropertyNode* propertyNode = _options->getPropertyNode().get(); @@ -1393,80 +1373,39 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) return; } + // Determine tree spacing, assuming base density of 1 tree per 100m^2, though spacing + // is linear here, as is the /sim/rendering/vegetation-density property. + float spacing = 10.0 / vegetation_density; + SGMaterialLibPtr matlib = _options->getMaterialLib(); SGMaterial* mat = 0; - osg::Vec3d world; - SGGeod loc; - SGGeoc cloc; - if (matlib) - { - // Determine the center of the tile, sadly non-trivial. - osg::Vec3d tileloc = computeCenter(buffer, masterLocator); - masterLocator->convertLocalToModel(tileloc, world); - const SGVec3d world2 = SGVec3d(world.x(), world.y(), world.z()); - loc = SGGeod::fromCart(world2); - cloc = SGGeoc::fromCart(world2); - SG_LOG(SG_TERRAIN, SG_DEBUG, "Applying VPB material " << loc); - SGMaterialCache* matcache = _options->getMaterialLib()->generateMatCache(loc, _options); - mat = matcache->find("EvergreenForest"); - delete matcache; - } + if (!matlib) return; - if (!mat) { - SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to find EvergreenForestmaterial at " << loc); - return; - } + const SGGeod loc = computeCenterGeod(buffer, masterLocator); + SGMaterialCache* matcache = matlib->generateMatCache(loc, _options); - TreeBin* bin = new TreeBin(); - bin->texture = mat->get_tree_texture(); - bin->teffect = mat->get_tree_effect(); - bin->range = mat->get_tree_range(); - bin->width = mat->get_tree_width(); - bin->height = mat->get_tree_height(); - bin->texture_varieties = mat->get_tree_varieties(); - - const osg::Matrixd R_vert = osg::Matrixd::rotate( - M_PI / 2.0 - loc.getLatitudeRad(), osg::Vec3d(0.0, 1.0, 0.0), - loc.getLongitudeRad(), osg::Vec3d(0.0, 0.0, 1.0), - 0.0, osg::Vec3d(1.0, 0.0, 0.0)); + if (!matcache) return; const osg::Array* vertices = buffer._landGeometry->getVertexArray(); const osg::Array* texture_coords = buffer._landGeometry->getTexCoordArray(0); osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(0); osg::Image* image = colorLayer->getImage(); - // Just want the first primitive set, not the others which will be the "skirts" below. - const osg::PrimitiveSet* primSet = buffer._landGeometry->getPrimitiveSet(0); - const osg::DrawElements* drawElements = primSet->getDrawElements(); - const unsigned int triangle_count = drawElements->getNumPrimitives(); + if (!image || ! image->valid()) { + SG_LOG(SG_TERRAIN, SG_ALERT, "No landclass image for " << _terrainTile->getTileID().x << " " << _terrainTile->getTileID().y << " " << _terrainTile->getTileID().level); + return; + } - const double lon = loc.getLongitudeRad(); - const double lat = loc.getLatitudeRad(); - const double clon = cloc.getLongitudeRad(); - const double clat = cloc.getLatitudeRad(); - const double r_E_lat = /* 6356752.3 */ 6.375993e+06; - const double r_E_lon = /* 6378137.0 */ 6.389377e+06; - const double C = r_E_lon * cos(lat); - const double one_over_C = (fabs(C) > 1.0e-4) ? (1.0 / C) : 0.0; - const double one_over_r_E = 1.0 / r_E_lat; - const double delta_lat = sqrt(1000.0 / vegetation_density) / r_E_lat; - const double delta_lon = sqrt(1000.0 / vegetation_density) / (r_E_lon * cos(loc.getLatitudeRad())); - - const osg::Matrix rotation_vertices_c = osg::Matrix::rotate( - M_PI / 2 - clat, osg::Vec3d(0.0, 1.0, 0.0), - clon, osg::Vec3d(0.0, 0.0, 1.0), - 0.0, osg::Vec3d(1.0, 0.0, 0.0)); - - const osg::Matrix rotation_vertices_g = osg::Matrix::rotate( - M_PI / 2 - lat, osg::Vec3d(0.0, 1.0, 0.0), - lon, osg::Vec3d(0.0, 0.0, 1.0), - 0.0, osg::Vec3d(1.0, 0.0, 0.0)); + SGTreeBinList randomForest; const osg::Vec3* vertexPtr = static_cast(vertices->getDataPointer()); const osg::Vec2* texPtr = static_cast(texture_coords->getDataPointer()); - + const osg::PrimitiveSet* primSet = buffer._landGeometry->getPrimitiveSet(0); + const osg::DrawElements* drawElements = primSet->getDrawElements(); + const unsigned int triangle_count = drawElements->getNumPrimitives(); + for (unsigned int i = 0; i < triangle_count; i++) { const int i0 = drawElements->index(3 * i); @@ -1478,30 +1417,8 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) const osg::Vec3 v2 = vertexPtr[i2]; const osg::Vec3d v_0 = v0; - const osg::Vec3d v_x = v1 - v0; - const osg::Vec3d v_y = v2 - v0; - - osg::Vec3 n = v_x ^ v_y; - n /= n.length(); - - const osg::Vec3d v_0_g = R_vert * v0; - const osg::Vec3d v_1_g = R_vert * v1; - const osg::Vec3d v_2_g = R_vert * v2; - - const osg::Vec2d ll_0 = osg::Vec2d(v_0_g.y() * one_over_C + lon, -v_0_g.x() * one_over_r_E + lat); - const osg::Vec2d ll_1 = osg::Vec2d(v_1_g.y() * one_over_C + lon, -v_1_g.x() * one_over_r_E + lat); - const osg::Vec2d ll_2 = osg::Vec2d(v_2_g.y() * one_over_C + lon, -v_2_g.x() * one_over_r_E + lat); - - const osg::Vec2d ll_O = ll_0; - const osg::Vec2d ll_x = osg::Vec2d((v_1_g.y() - v_0_g.y()) * one_over_C, -(v_1_g.x() - v_0_g.x()) * one_over_r_E); - const osg::Vec2d ll_y = osg::Vec2d((v_2_g.y() - v_0_g.y()) * one_over_C, -(v_2_g.x() - v_0_g.x()) * one_over_r_E); - - const int off_x = ll_O.x() / delta_lon; - const int off_y = ll_O.y() / delta_lat; - const int min_lon = min(min(ll_0.x(), ll_1.x()), ll_2.x()) / delta_lon; - const int max_lon = max(max(ll_0.x(), ll_1.x()), ll_2.x()) / delta_lon; - const int min_lat = min(min(ll_0.y(), ll_1.y()), ll_2.y()) / delta_lat; - const int max_lat = max(max(ll_0.y(), ll_1.y()), ll_2.y()) / delta_lat; + osg::Vec3d v_x = v1 - v0; + osg::Vec3d v_y = v2 - v0; const osg::Vec2 t0 = texPtr[i0]; const osg::Vec2 t1 = texPtr[i1]; @@ -1511,46 +1428,162 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) const osg::Vec2d t_x = t1 - t0; const osg::Vec2d t_y = t2 - t0; - const double D = det2(ll_x, ll_y); + osg::Vec3 n = v_x ^ v_y; + n.normalize(); - for (int lat_int = min_lat - 1; lat_int <= max_lat + 1; lat_int++) - { - const double lat = (lat_int - off_y) * delta_lat; + osg::Vec3 up = buffer._transform->getMatrix().getTrans(); + up.normalize(); - for (int lon_int = min_lon - 1; lon_int <= max_lon + 1; lon_int++) - { - const double lon = (lon_int - off_x) * delta_lon; - const osg::Vec2d ro = getRandomOffset(lon_int, lat_int) * randomness; - const osg::Vec2d p(lon + delta_lon * ro.x(), lat + delta_lat * ro.y()); - const double x = det2(ll_x, p) / D; - const double y = det2(p, ll_y) / D; + double step_x = spacing / v_x.length(); + double step_y = spacing / v_y.length(); - if ((x < 0.0) || (y < 0.0) || (x + y > 1.0)) - { + for (float x = 0; x < 1.0; x += step_x) { + for (float y = 0; y < 1.0; y += step_y) { + + // If outside the triangle, ignore + if ((x + y) > 1.0) continue; + + osg::Vec2 t = osg::Vec2(t_0 + t_x * x + t_y * y); + unsigned int tx = (unsigned int) (image->s() * t.x()) % image->s(); + unsigned int ty = (unsigned int) (image->t() * t.y()) % image->t(); + + if (!image) { + SG_LOG(SG_TERRAIN, SG_ALERT, "Image disappeared under my feet."); continue; } - const osg::Vec2 tcp = t_x * x + t_y * y + t_0; - const osg::Vec4 tc = image->getColor(tcp); - const int tc_r = floor(tc.r() * 255.0 + 0.5); + const osg::Vec4 tc = image->getColor(tx, ty); + const int land_class = int(round(tc.x() * 255.0)); + mat = matcache->find(land_class); - if ((tc_r < 22) || (24 < tc_r)) - { + if ((land_class == 26) || (land_class == 27)) { + SG_LOG(SG_TERRAIN, SG_ALERT, "Placing trees for " << land_class << " " << mat->get_one_texture(0,0) << " " << mat->get_names()[0]); + } + + if (!mat) { + //SG_LOG(SG_TERRAIN, SG_ALERT, "Failed to find material for landclass " << land_class << " (" << tc.r() << ")"); continue; } - const osg::Vec3 vp = v_x * x + v_y * y + v_0; - bin->insert(SGVec3f(vp.x(), vp.y(), vp.z()), SGVec3f(n.x(), n.y(), n.z())); + // Check vegetation density against the material, noting we assumed 100m^2 above + if (mat->get_wood_coverage() <= 0) continue; + + float wood_coverage = 100.0 / mat->get_wood_coverage(); + if (getRandomOffset().x() > wood_coverage) continue; + + osg::Vec3 p = v_0 + v_x*x + v_y*y; + + + if (! mat->get_is_plantation()) { + // Add some randomness + osg::Vec2 offset = getRandomOffset(); + float new_x = x + step_x * (offset.x() - 0.5); + float new_y = y + step_y * (offset.y() - 0.5); + + // Another check to ensure we're not outside the triangle + if ((new_x + new_y) > 1.0) continue; + + + // Update both position and texture coords to account for the new position. + p = v_0 + v_x*new_x + v_y*new_y; + t = osg::Vec2(t_0 + t_x * new_x + t_y * new_y); + } + + // Check against any object mask + osg::Texture2D* object_mask = mat->get_one_object_mask(0); + if (object_mask != NULL) { + osg::Image* img = object_mask->getImage(); + if (!img || ! img->valid()) continue; + + // Texture coordinates run [0..1][0..1] across the entire tile whereas + // the texure itself has defined dimensions in m. + // We therefore need to use the tile width and height to determine the correct + // texture coordinate transformation. + float x_scale = buffer._width / 1000.0; + float y_scale = buffer._height / 1000.0; + + if (mat->get_xsize() > 0.0) { x_scale = buffer._width / mat->get_xsize(); } + if (mat->get_ysize() > 0.0) { y_scale = buffer._height / mat->get_ysize(); } + + unsigned int x = (unsigned int) (img->s() * t.x() * x_scale) % img->s(); + unsigned int y = (unsigned int) (img->t() * t.y() * y_scale) % img->t(); + + // green (for trees) channel + if (getRandomOffset().y() > img->getColor(x, y).g()) { + continue; + } + } + + // Ensure the slope isn't too steep by checking the + // cos of the angle between the slope normal and the + // vertical, conveniently just the dot product of the up and normal + float alpha = n * up; + float cos_zero_density_angle = mat->get_cos_tree_zero_density_slope_angle(); + float cos_max_density_angle = mat->get_cos_tree_max_density_slope_angle(); + + if (alpha < cos_zero_density_angle) { + continue; // Too steep for any vegetation + } + + if (alpha < cos_max_density_angle) { + // Reduce density on steep slopes + float slope_density = (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle); + if (getRandomOffset().y() > slope_density) continue; + } + + // It's a hard life being a possible tree! But we now have a valid tree position. + // So put it in the correct bin. + TreeBin* bin = NULL; + bool found = false; + + for (SGTreeBinList::iterator iter = randomForest.begin(); iter != randomForest.end(); iter++) { + + bin = *iter; + + if ((bin->texture == mat->get_tree_texture() ) && + (bin->teffect == mat->get_tree_effect() ) && + (bin->texture_varieties == mat->get_tree_varieties()) && + (bin->range == mat->get_tree_range() ) && + (bin->width == mat->get_tree_width() ) && + (bin->height == mat->get_tree_height() ) ) { + found = true; + break; + } + } + + if (!found) { + bin = new TreeBin(); + bin->texture = mat->get_tree_texture(); + SG_LOG(SG_TERRAIN, SG_DEBUG, "Tree texture " << bin->texture); + bin->teffect = mat->get_tree_effect(); + SG_LOG(SG_TERRAIN, SG_DEBUG, "Tree effect " << bin->teffect); + bin->range = mat->get_tree_range(); + bin->width = mat->get_tree_width(); + bin->height = mat->get_tree_height(); + bin->texture_varieties = mat->get_tree_varieties(); + randomForest.push_back(bin); + } + + bin->insert(SGVec3f(p.x(), p.y(), p.z()), SGVec3f(n.x(), n.y(), n.z())); } - } - } - SGTreeBinList randomForest; - randomForest.push_back(bin); - osg::Group* trees = createForest(randomForest, R_vert,_options, 1); - buffer._transform->addChild(trees); + if (randomForest.size() > 0) { + SG_LOG(SG_TERRAIN, SG_DEBUG, "Adding Random Forest " << randomForest.size()); + for (auto iter = randomForest.begin(); iter != randomForest.end(); iter++) { + TreeBin* treeBin = *iter; + SG_LOG(SG_TERRAIN, SG_DEBUG, " " << treeBin->texture << " " << treeBin->getNumTrees()); + } + + const osg::Matrixd R_vert = osg::Matrixd::rotate( + M_PI / 2.0 - loc.getLatitudeRad(), osg::Vec3d(0.0, 1.0, 0.0), + loc.getLongitudeRad(), osg::Vec3d(0.0, 0.0, 1.0), + 0.0, osg::Vec3d(1.0, 0.0, 0.0)); + + osg::Group* trees = createForest(randomForest, R_vert,_options, 1); + buffer._transform->addChild(trees); + } } void VPBTechnique::applyLineFeatures(BufferData& buffer, Locator* masterLocator) @@ -1578,7 +1611,6 @@ void VPBTechnique::applyLineFeatures(BufferData& buffer, Locator* masterLocator) const SGMaterialLibPtr matlib = _options->getMaterialLib(); SGMaterial* mat = 0; - const osg::Vec3d world = buffer._transform->getMatrix().getTrans(); if (! matlib) { SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to get materials library to generate roads"); @@ -1586,6 +1618,7 @@ void VPBTechnique::applyLineFeatures(BufferData& buffer, Locator* masterLocator) } // Get all appropriate roads. We assume that the VPB terrain tile is smaller than a Bucket size. + const osg::Vec3d world = buffer._transform->getMatrix().getTrans(); const SGGeod loc = SGGeod::fromCart(toSG(world)); const SGBucket bucket = SGBucket(loc); auto roads = std::find_if(_lineFeatureLists.begin(), _lineFeatureLists.end(), [bucket](BucketLineFeatureBinList b){return (b.first == bucket);}); @@ -1789,7 +1822,7 @@ void VPBTechnique::traverse(osg::NodeVisitor& nv) // if app traversal update the frame count. if (nv.getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR) { - if (_terrainTile->getDirty()) _terrainTile->init(_terrainTile->getDirtyMask(), false); + //if (_terrainTile->getDirty()) _terrainTile->init(_terrainTile->getDirtyMask(), false); update(nv); return; } @@ -1803,7 +1836,7 @@ void VPBTechnique::traverse(osg::NodeVisitor& nv) if (_terrainTile->getDirty()) { OSG_INFO<<"******* Doing init ***********"<init(_terrainTile->getDirtyMask(), false); + //_terrainTile->init(_terrainTile->getDirtyMask(), false); } if (_currentBufferData.valid()) @@ -1901,6 +1934,8 @@ osg::Vec3d VPBTechnique::checkAgainstElevationConstraints(osg::Vec3d origin, osg void VPBTechnique::addLineFeatureList(SGBucket bucket, LineFeatureBinList roadList, osg::ref_ptr terrainNode) { + if (roadList.empty()) return; + // Block to mutex the List alone { const std::lock_guard lock(VPBTechnique::_lineFeatureLists_mutex); // Lock the _lineFeatureLists for this scope diff --git a/simgear/scene/tgdb/VPBTechnique.hxx b/simgear/scene/tgdb/VPBTechnique.hxx index 59e9b144..4c382f37 100644 --- a/simgear/scene/tgdb/VPBTechnique.hxx +++ b/simgear/scene/tgdb/VPBTechnique.hxx @@ -112,6 +112,8 @@ class VPBTechnique : public TerrainTechnique osg::ref_ptr _waterGeode; osg::ref_ptr _landGeometry; osg::ref_ptr _waterGeometry; + float _width; + float _height; protected: ~BufferData() {} @@ -119,6 +121,7 @@ class VPBTechnique : public TerrainTechnique virtual osg::Vec3d computeCenter(BufferData& buffer, Locator* masterLocator); virtual osg::Vec3d computeCenterModel(BufferData& buffer, Locator* masterLocator); + const virtual SGGeod computeCenterGeod(BufferData& buffer, Locator* masterLocator); virtual void generateGeometry(BufferData& buffer, Locator* masterLocator, const osg::Vec3d& centerModel); @@ -126,7 +129,7 @@ class VPBTechnique : public TerrainTechnique virtual double det2(const osg::Vec2d a, const osg::Vec2d b); - static osg::Vec2d getRandomOffset(int lon, int lat); + static osg::Vec2d getRandomOffset(); virtual void applyTrees(BufferData& buffer, Locator* masterLocator);