WS30: Initial coastlines

This commit is contained in:
Stuart Buchanan
2021-05-03 15:43:59 +01:00
parent 8464ef48e8
commit fc93788ab1
9 changed files with 716 additions and 73 deletions

View File

@@ -1,8 +1,9 @@
include (SimGearComponent)
set(HEADERS
GroundLightManager.hxx
AreaFeatureBin.hxx
CoastlineBin.hxx
GroundLightManager.hxx
LineFeatureBin.hxx
ReaderWriterSPT.hxx
ReaderWriterSTG.hxx
@@ -22,6 +23,7 @@ set(HEADERS
ShaderGeometry.hxx
TreeBin.hxx
VPBElevationSlice.hxx
VPBTileBounds.hxx
VPBTechnique.hxx
apt_signs.hxx
obj.hxx
@@ -30,8 +32,9 @@ set(HEADERS
)
set(SOURCES
GroundLightManager.cxx
AreaFeatureBin.cxx
CoastlineBin.cxx
GroundLightManager.cxx
LineFeatureBin.cxx
ReaderWriterSPT.cxx
ReaderWriterSTG.cxx
@@ -42,6 +45,7 @@ set(SOURCES
ShaderGeometry.cxx
TreeBin.cxx
VPBElevationSlice.cxx
VPBTileBounds.cxx
VPBTechnique.cxx
apt_signs.cxx
obj.cxx

View File

@@ -0,0 +1,99 @@
/* -*-c++-*-
*
* Copyright (C) 2008 Stuart Buchanan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <simgear/debug/logstream.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/math/SGGeod.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include "CoastlineBin.hxx"
using namespace osg;
namespace simgear
{
CoastlineBin::CoastlineBin(const SGPath& absoluteFileName)
{
if (!absoluteFileName.exists()) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Coastline list file " << absoluteFileName << " does not exist.");
return;
}
sg_gzifstream stream(absoluteFileName);
if (!stream.is_open()) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to open " << absoluteFileName);
return;
}
while (!stream.eof()) {
// read a line. Each line defines a coast line feature, consisting of a series of lon/lat positions.
// Or a comment, starting with #
std::string line;
std::getline(stream, line);
// strip comments
std::string::size_type hash_pos = line.find('#');
if (hash_pos != std::string::npos)
line.resize(hash_pos);
// and process further
std::stringstream in(line);
if (in.bad() || in.fail()) {
SG_LOG(SG_TERRAIN, SG_WARN, "Error parsing area entry in: " << absoluteFileName << " line: \"" << line << "\"");
continue;
}
std::list<osg::Vec3d> nodes;
while (true) {
double lon = 0.0f, lat=0.0f;
in >> lon >> lat;
if (in.bad() || in.fail()) {
break;
}
SGVec3d tmp;
SGGeodesy::SGGeodToCart(SGGeod::fromDeg(lon, lat), tmp);
nodes.push_back(toOsg(tmp));
}
if (nodes.size() > 1) {
insert(Coastline(nodes));
} else {
SG_LOG(SG_TERRAIN, SG_WARN, "Coastline definition with fewer than two lon/lat nodes : " << absoluteFileName << " line: \"" << line << "\"");
}
}
stream.close();
};
}

View File

@@ -0,0 +1,69 @@
/* -*-c++-*-
*
* Copyright (C) 2021 Stuart Buchanan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#ifndef COAST_BIN_HXX
#define COAST_BIN_HXX
#include <vector>
#include <string>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Matrix>
#include <simgear/misc/sg_path.hxx>
namespace simgear
{
class CoastlineBin {
public:
CoastlineBin() = default;
CoastlineBin(const SGPath& absoluteFileName);
~CoastlineBin() = default;
struct Coastline {
const std::list<osg::Vec3d> _nodes;
Coastline(const std::list<osg::Vec3d> nodes) :
_nodes(nodes)
{
}
};
typedef std::list<Coastline> CoastlineList;
void insert(const Coastline& t) {
_coastFeatureList.push_back(t);
}
const CoastlineList getCoastlines() const {
return _coastFeatureList;
}
private:
CoastlineList _coastFeatureList;
};
typedef std::list<CoastlineBin> CoastlineBinList;
}
#endif

View File

@@ -48,7 +48,7 @@ public:
const float _b;
const float _c;
const float _d;
LineFeature(const std::list<osg::Vec3d> nodes, const float w, const int attributes, const float a, const float b, const float c, const float d) :
LineFeature(const std::list<osg::Vec3d> nodes, const float w, const int attributes=0, const float a=0.0, const float b=0.0, const float c=0.0, const float d=0.0) :
_nodes(nodes), _width(w), _attributes(attributes), _a(a), _b(b), _c(c), _d(d)
{
}

View File

@@ -72,6 +72,7 @@
#define TREE_LIST "TREE_LIST"
#define LINE_FEATURE_LIST "LINE_FEATURE_LIST"
#define AREA_FEATURE_LIST "AREA_FEATURE_LIST"
#define COASTLINE_LIST "COASTLINE_LIST"
namespace simgear {
@@ -167,6 +168,12 @@ struct ReaderWriterSTG::_ModelBin {
SGBucket _bucket;
};
struct _CoastlineList {
_CoastlineList() { }
std::string _filename;
SGBucket _bucket;
};
class DelayLoadReadFileCallback : public OptionsReadFileCallback {
private:
@@ -353,6 +360,20 @@ struct ReaderWriterSTG::_ModelBin {
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();
}
@@ -363,6 +384,7 @@ struct ReaderWriterSTG::_ModelBin {
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
@@ -672,6 +694,11 @@ struct ReaderWriterSTG::_ModelBin {
in >> areaFeaturelist._material;
areaFeaturelist._bucket = bucketIndexFromFileName(absoluteFileName.file_base().c_str());
_areaFeatureListList.push_back(areaFeaturelist);
} else if (token == COASTLINE_LIST) {
_CoastlineList coastFeaturelist;
coastFeaturelist._filename = path.utf8Str();
coastFeaturelist._bucket = bucketIndexFromFileName(absoluteFileName.file_base().c_str());
_coastFeatureListList.push_back(coastFeaturelist);
} else {
// Check registered callback for token. Keep lock until callback completed to make sure it will not be
// executed after a thread successfully executed removeSTGObjectHandler()
@@ -790,50 +817,58 @@ struct ReaderWriterSTG::_ModelBin {
i->_elev += elevation(*terrainGroup, SGGeod::fromDeg(i->_lon, i->_lat));
}
if (_objectStaticList.empty() && _signList.empty() && _buildingListList.empty() && _treeListList.empty() && _lineFeatureListList.empty() && _areaFeatureListList.empty()) {
if (_objectStaticList.empty() &&
_signList.empty() &&
_buildingListList.empty() &&
_treeListList.empty() &&
_lineFeatureListList.empty() &&
_areaFeatureListList.empty() &&
_coastFeatureListList.empty())
{
// The simple case, just return the terrain group
return terrainGroup.release();
} else {
osg::PagedLOD* pagedLOD = new osg::PagedLOD;
pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER);
std::string name = string("pagedObjectLOD ").append(bucket.gen_index_str());
pagedLOD->setName(name);
// This should be visible in any case.
// If this is replaced by some lower level of detail, the parent LOD node handles this.
pagedLOD->addChild(terrainGroup, 0, std::numeric_limits<float>::max());
pagedLOD->setMinimumExpiryTime(0, pagedLODExpiry);
// we just need to know about the read file callback that itself holds the data
osg::ref_ptr<DelayLoadReadFileCallback> readFileCallback = new DelayLoadReadFileCallback;
readFileCallback->_objectStaticList = _objectStaticList;
readFileCallback->_buildingList = _buildingListList;
readFileCallback->_treeList = _treeListList;
readFileCallback->_signList = _signList;
readFileCallback->_options = options;
readFileCallback->_bucket = bucket;
if (vpb_active && vpb_node) {
readFileCallback->_lineFeatureList = _lineFeatureListList;
readFileCallback->_areaFeatureList = _areaFeatureListList;
readFileCallback->_terrainNode = vpb_node;
}
osg::ref_ptr<osgDB::Options> callbackOptions = new osgDB::Options;
callbackOptions->setReadFileCallback(readFileCallback.get());
pagedLOD->setDatabaseOptions(callbackOptions.get());
pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy name - use the stored data in the read file callback");
// Objects may end up displayed up to 2x the object range.
pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 2.0 * _object_range_rough);
pagedLOD->setMinimumExpiryTime(pagedLOD->getNumChildren(), pagedLODExpiry);
pagedLOD->setRadius(SG_TILE_RADIUS);
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Center: " << pagedLOD->getCenter().x() << "," << pagedLOD->getCenter().y() << "," << pagedLOD->getCenter().z() );
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Range: " << (2.0 * _object_range_rough));
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Radius: " << SG_TILE_RADIUS);
return pagedLOD;
}
osg::PagedLOD* pagedLOD = new osg::PagedLOD;
pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER);
std::string name = string("pagedObjectLOD ").append(bucket.gen_index_str());
pagedLOD->setName(name);
// This should be visible in any case.
// If this is replaced by some lower level of detail, the parent LOD node handles this.
pagedLOD->addChild(terrainGroup, 0, std::numeric_limits<float>::max());
pagedLOD->setMinimumExpiryTime(0, pagedLODExpiry);
// we just need to know about the read file callback that itself holds the data
osg::ref_ptr<DelayLoadReadFileCallback> readFileCallback = new DelayLoadReadFileCallback;
readFileCallback->_objectStaticList = _objectStaticList;
readFileCallback->_buildingList = _buildingListList;
readFileCallback->_treeList = _treeListList;
readFileCallback->_signList = _signList;
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<osgDB::Options> callbackOptions = new osgDB::Options;
callbackOptions->setReadFileCallback(readFileCallback.get());
pagedLOD->setDatabaseOptions(callbackOptions.get());
pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy name - use the stored data in the read file callback");
// Objects may end up displayed up to 2x the object range.
pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 2.0 * _object_range_rough);
pagedLOD->setMinimumExpiryTime(pagedLOD->getNumChildren(), pagedLODExpiry);
pagedLOD->setRadius(SG_TILE_RADIUS);
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Center: " << pagedLOD->getCenter().x() << "," << pagedLOD->getCenter().y() << "," << pagedLOD->getCenter().z() );
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Range: " << (2.0 * _object_range_rough));
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile " << bucket.gen_index_str() << " PagedLOD Radius: " << SG_TILE_RADIUS);
return pagedLOD;
}
double _object_range_bare;
@@ -847,6 +882,7 @@ struct ReaderWriterSTG::_ModelBin {
std::list<_TreeList> _treeListList;
std::list<_LineFeatureList> _lineFeatureListList;
std::list<_AreaFeatureList> _areaFeatureListList;
std::list<_CoastlineList> _coastFeatureListList;
};
ReaderWriterSTG::ReaderWriterSTG()

View File

@@ -45,6 +45,7 @@
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/scene/tgdb/VPBElevationSlice.hxx>
#include <simgear/scene/tgdb/VPBTileBounds.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
@@ -169,6 +170,7 @@ void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded)
applyTrees(*buffer, masterLocator);
applyLineFeatures(*buffer, masterLocator);
applyAreaFeatures(*buffer, masterLocator);
applyCoastline(*buffer, masterLocator);
}
}
else
@@ -177,7 +179,7 @@ void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded)
applyColorLayers(*buffer, masterLocator);
applyTrees(*buffer, masterLocator);
applyLineFeatures(*buffer, masterLocator);
applyAreaFeatures(*buffer, masterLocator);
applyCoastline(*buffer, masterLocator);
}
if (buffer->_transform.valid()) buffer->_transform->setThreadSafeRefUnref(true);
@@ -1298,6 +1300,8 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator)
}
}
SG_LOG(SG_TERRAIN, SG_DEBUG, "VPB Image level:" << _terrainTile->getTileID().level << " " << image->s() << "x" << image->t() << " mipmaps:" << image->getNumMipmapLevels() << " format:" << image->getInternalTextureFormat());
osg::ref_ptr<osg::Texture2D> texture2D = new osg::Texture2D;
texture2D->setImage(image);
texture2D->setMaxAnisotropy(16.0f);
@@ -1309,16 +1313,6 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator)
texture2D->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE);
texture2D->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP_TO_EDGE);
bool mipMapping = !(texture2D->getFilter(osg::Texture::MIN_FILTER)==osg::Texture::LINEAR || texture2D->getFilter(osg::Texture::MIN_FILTER)==osg::Texture::NEAREST);
bool s_NotPowerOfTwo = image->s()==0 || (image->s() & (image->s() - 1));
bool t_NotPowerOfTwo = image->t()==0 || (image->t() & (image->t() - 1));
if (mipMapping && (s_NotPowerOfTwo || t_NotPowerOfTwo))
{
SG_LOG(SG_TERRAIN, SG_ALERT, "Disabling mipmapping for non power of two tile size("<<image->s()<<", "<<image->t()<<")");
texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
}
osg::StateSet* stateset = buffer._landGeode->getOrCreateStateSet();
stateset->setTextureAttributeAndModes(0, texture2D, osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(1, atlas.image, osg::StateAttribute::ON);
@@ -1680,23 +1674,24 @@ void VPBTechnique::applyLineFeatures(BufferData& buffer, Locator* masterLocator)
void VPBTechnique::generateLineFeature(BufferData& buffer, Locator* masterLocator, LineFeatureBin::LineFeature road, osg::Vec3d modelCenter, osg::Vec3Array* v, osg::Vec2Array* t, osg::Vec3Array* n, unsigned int xsize, unsigned int ysize)
{
if (road._nodes.size() < 2) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Coding error - LineFeatureBin::LineFeature with fewer than two nodes");
return;
}
osg::Vec3d ma, mb;
std::list<osg::Vec3d> roadPoints;
auto road_iter = road._nodes.begin();
// We're in Earth-centered coordinates, so "up" is simply directly away from (0,0,0)
osg::Vec3d up = modelCenter;
up.normalize();
TileBounds tileBounds(masterLocator, up);
std::list<osg::Vec3d> nodes = tileBounds.clipToTile(road._nodes);
// We need at least two node to make a road.
if (nodes.size() < 2) return;
osg::Vec3d ma, mb;
std::list<osg::Vec3d> roadPoints;
auto road_iter = nodes.begin();
ma = getMeshIntersection(buffer, masterLocator, *road_iter - modelCenter, up);
road_iter++;
for (; road_iter != road._nodes.end(); road_iter++) {
for (; road_iter != nodes.end(); road_iter++) {
mb = getMeshIntersection(buffer, masterLocator, *road_iter - modelCenter, up);
auto esl = VPBElevationSlice::computeVPBElevationSlice(buffer._landGeometry, ma, mb, up);
@@ -1929,6 +1924,203 @@ void VPBTechnique::generateAreaFeature(BufferData& buffer, Locator* masterLocato
}
}
void VPBTechnique::applyCoastline(BufferData& buffer, Locator* masterLocator)
{
unsigned int coast_features_lod_range = 4;
float coastWidth = 40.0;
const unsigned int tileLevel = _terrainTile->getTileID().level;
const SGPropertyNode* propertyNode = _options->getPropertyNode().get();
if (propertyNode) {
const SGPropertyNode* static_lod = propertyNode->getNode("/sim/rendering/static-lod");
coast_features_lod_range = static_lod->getIntValue("coastline-lod-level", coast_features_lod_range);
coastWidth = static_lod->getIntValue("coastline-width", coastWidth);
}
if (tileLevel < coast_features_lod_range) {
// Do not generate coasts for tiles too far away
return;
}
const SGMaterialLibPtr matlib = _options->getMaterialLib();
if (! matlib) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to get materials library to generate areas");
return;
}
// Get all appropriate coasts. 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 coasts = std::find_if(_coastFeatureLists.begin(), _coastFeatureLists.end(), [bucket](BucketCoastlineBinList b){return (b.first == bucket);});
if (coasts == _coastFeatureLists.end()) return;
// We're in Earth-centered coordinates, so "up" is simply directly away from (0,0,0)
osg::Vec3d up = world;
up.normalize();
TileBounds tileBounds(masterLocator, up);
SGMaterialCache* matcache = matlib->generateMatCache(loc, _options);
SGMaterial* mat = matcache->find("ws30coastline");
if (!mat) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to find material ws30coastline at " << loc << " " << bucket);
return;
}
unsigned int xsize = mat->get_xsize();
unsigned int ysize = mat->get_ysize();
// Generate a geometry for this set of coasts.
osg::Vec3Array* v = new osg::Vec3Array;
osg::Vec2Array* t = new osg::Vec2Array;
osg::Vec3Array* n = new osg::Vec3Array;
osg::Vec4Array* c = new osg::Vec4Array;
for (; coasts != _coastFeatureLists.end(); ++coasts) {
const CoastlineBinList coastBins = coasts->second;
for (auto rb = coastBins.begin(); rb != coastBins.end(); ++rb)
{
auto coastFeatures = rb->getCoastlines();
for (auto r = coastFeatures.begin(); r != coastFeatures.end(); ++r) {
auto clipped = tileBounds.clipToTile(r->_nodes);
if (clipped.size() > 1) {
// We need at least two points to render a line.
LineFeatureBin::LineFeature line = LineFeatureBin::LineFeature(clipped, coastWidth);
generateCoastlineFeature(buffer, masterLocator, line, world, v, t, n, xsize, ysize);
}
}
}
}
if (v->size() == 0) {
SG_LOG(SG_TERRAIN, SG_ALERT, "No vertices for coastline! " << loc << " " << bucket);
return;
}
SG_LOG(SG_TERRAIN, SG_ALERT, "Generating coastline of " << v->size() << " vertices.");
c->push_back(osg::Vec4(1.0,1.0,1.0,1.0));
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
geometry->setVertexArray(v);
geometry->setTexCoordArray(0, t, osg::Array::BIND_PER_VERTEX);
geometry->setTexCoordArray(1, t, osg::Array::BIND_PER_VERTEX);
geometry->setNormalArray(n, osg::Array::BIND_PER_VERTEX);
geometry->setColorArray(c, osg::Array::BIND_OVERALL);
geometry->setUseDisplayList( false );
geometry->setUseVertexBufferObjects( true );
geometry->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLES, 0, v->size()) );
EffectGeode* geode = new EffectGeode;
geode->addDrawable(geometry);
geode->setMaterial(mat);
geode->setEffect(mat->get_one_effect(0));
geode->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
buffer._transform->addChild(geode);
}
void VPBTechnique::generateCoastlineFeature(BufferData& buffer, Locator* masterLocator, LineFeatureBin::LineFeature coastline, osg::Vec3d modelCenter, osg::Vec3Array* v, osg::Vec2Array* t, osg::Vec3Array* n, unsigned int xsize, unsigned int ysize)
{
if (coastline._nodes.size() < 2) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Coding error - LineFeatureBin::LineFeature with fewer than two nodes");
return;
}
osg::Vec3d ma, mb;
std::list<osg::Vec3d> coastlinePoints;
auto coastline_iter = coastline._nodes.begin();
// We're in Earth-centered coordinates, so "up" is simply directly away from (0,0,0)
osg::Vec3d up = modelCenter;
up.normalize();
ma = getMeshIntersection(buffer, masterLocator, *coastline_iter - modelCenter, up);
coastline_iter++;
for (; coastline_iter != coastline._nodes.end(); coastline_iter++) {
mb = getMeshIntersection(buffer, masterLocator, *coastline_iter - modelCenter, up);
auto esl = VPBElevationSlice::computeVPBElevationSlice(buffer._landGeometry, ma, mb, up);
for(auto eslitr = esl.begin(); eslitr != esl.end(); ++eslitr) {
coastlinePoints.push_back(*eslitr);
}
// Now traverse the next segment
ma = mb;
}
if (coastlinePoints.size() == 0) return;
// We now have a series of points following the topography of the elevation mesh.
auto iter = coastlinePoints.begin();
osg::Vec3d start = *iter;
iter++;
osg::Vec3d last_spanwise = (*iter - start)^ up;
last_spanwise.normalize();
float yTexBaseA = 0.0f;
float yTexBaseB = 0.0f;
for (; iter != coastlinePoints.end(); iter++) {
osg::Vec3d end = *iter;
// Ignore tiny segments - likely artifacts of the elevation slicer
if ((end - start).length2() < 5.0) continue;
// Find a spanwise vector
osg::Vec3d spanwise = ((end-start) ^ up);
spanwise.normalize();
// Define the coastline extents. Angle it in slightly on the seaward side
const osg::Vec3d a = start + up;
const osg::Vec3d b = start + last_spanwise * coastline._width;
const osg::Vec3d c = end + up;
const osg::Vec3d d = end + spanwise * coastline._width;
// Determine the x and y texture coordinates for the edges
const float xTex = 0.5 * coastline._width / xsize;
const float yTexA = yTexBaseA + (c-a).length() / ysize;
const float yTexB = yTexBaseB + (d-b).length() / ysize;
// Now generate two triangles, .
v->push_back(a);
v->push_back(b);
v->push_back(c);
t->push_back(osg::Vec2d(0, yTexBaseA));
t->push_back(osg::Vec2d(xTex, yTexBaseB));
t->push_back(osg::Vec2d(0, yTexA));
v->push_back(b);
v->push_back(d);
v->push_back(c);
t->push_back(osg::Vec2d(xTex, yTexBaseB));
t->push_back(osg::Vec2d(xTex, yTexB));
t->push_back(osg::Vec2d(0, yTexBaseA));
// Normal is straight from the quad
osg::Vec3d normal = (end-start)^spanwise;
normal.normalize();
for (unsigned int i = 0; i < 6; i++) n->push_back(normal);
start = end;
yTexBaseA = yTexA;
yTexBaseB = yTexB;
last_spanwise = spanwise;
}
}
// Find the intersection of a given SGGeod with the terrain mesh
osg::Vec3d VPBTechnique::getMeshIntersection(BufferData& buffer, Locator* masterLocator, osg::Vec3d pt, osg::Vec3d up)
{
@@ -2145,6 +2337,35 @@ void VPBTechnique::addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaLi
terrainNode->accept(ftv);
}
void VPBTechnique::addCoastlineList(SGBucket bucket, CoastlineBinList coastline, osg::ref_ptr<osg::Node> terrainNode)
{
if (coastline.empty()) return;
// Block to mutex the List alone
{
const std::lock_guard<std::mutex> lock(VPBTechnique::_coastFeatureLists_mutex); // Lock the _lineFeatureLists for this scope
_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)
{
SG_LOG(SG_TERRAIN, SG_DEBUG, "Erasing all roads with entry " << bucket);
@@ -2152,8 +2373,7 @@ void VPBTechnique::unloadFeatures(SGBucket bucket)
// C++ 20...
//std::erase_if(_lineFeatureLists, [bucket](BucketLineFeatureBinList p) { return p.first == bucket; } );
//std::erase_if(_lineFeatureLists, [bucket](BucketAreaFeatureBinList p) { return p.first == bucket; } );
//std::erase_if(_coastFeatureLists, [bucket](BucketCoastlineBinList p) { return p.first == bucket; } );
}

View File

@@ -33,6 +33,7 @@
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/tgdb/AreaFeatureBin.hxx>
#include <simgear/scene/tgdb/LineFeatureBin.hxx>
#include <simgear/scene/tgdb/CoastlineBin.hxx>
using namespace osgTerrain;
@@ -95,9 +96,10 @@ class VPBTechnique : public TerrainTechnique
static void removeElevationConstraint(osg::ref_ptr<osg::Node> constraint);
static osg::Vec3d checkAgainstElevationConstraints(osg::Vec3d origin, osg::Vec3d vertex, float vertex_gap);
// LineFeatures and AreaFeaturesare 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 addAreaFeatureList(SGBucket bucket, AreaFeatureBinList areaList, osg::ref_ptr<osg::Node> terrainNode);
static void addCoastlineList(SGBucket bucket, CoastlineBinList areaList, osg::ref_ptr<osg::Node> terrainNode);
static void unloadFeatures(SGBucket bucket);
protected:
@@ -136,8 +138,9 @@ class VPBTechnique : public TerrainTechnique
virtual void applyTrees(BufferData& buffer, Locator* masterLocator);
virtual void applyLineFeatures(BufferData& buffer, Locator* masterLocator);
virtual void generateLineFeature(BufferData& buffer, Locator*
masterLocator, LineFeatureBin::LineFeature road,
virtual void generateLineFeature(BufferData& buffer,
Locator* masterLocator,
LineFeatureBin::LineFeature road,
osg::Vec3d modelCenter,
osg::Vec3Array* v,
osg::Vec2Array* t,
@@ -146,8 +149,9 @@ class VPBTechnique : public TerrainTechnique
unsigned int ysize);
virtual void applyAreaFeatures(BufferData& buffer, Locator* masterLocator);
virtual void generateAreaFeature(BufferData& buffer, Locator*
masterLocator, AreaFeatureBin::AreaFeature area,
virtual void generateAreaFeature(BufferData& buffer,
Locator* masterLocator,
AreaFeatureBin::AreaFeature area,
osg::Vec3d modelCenter,
osg::Geometry* geometry,
osg::Vec3Array* v,
@@ -156,6 +160,17 @@ class VPBTechnique : public TerrainTechnique
unsigned int xsize,
unsigned int ysize);
virtual void applyCoastline(BufferData& buffer, Locator* masterLocator);
virtual void generateCoastlineFeature(BufferData& buffer,
Locator* masterLocator,
LineFeatureBin::LineFeature coastLine,
osg::Vec3d modelCenter,
osg::Vec3Array* v,
osg::Vec2Array* t,
osg::Vec3Array* n,
unsigned int xsize,
unsigned int ysize);
virtual osg::Vec3d getMeshIntersection(BufferData& buffer, Locator* masterLocator, osg::Vec3d pt, osg::Vec3d up);
OpenThreads::Mutex _writeBufferMutex;
@@ -176,6 +191,7 @@ class VPBTechnique : public TerrainTechnique
typedef std::pair<SGBucket, LineFeatureBinList> BucketLineFeatureBinList;
typedef std::pair<SGBucket, AreaFeatureBinList> BucketAreaFeatureBinList;
typedef std::pair<SGBucket, CoastlineBinList> BucketCoastlineBinList;
inline static std::list<BucketLineFeatureBinList> _lineFeatureLists;
inline static std::mutex _lineFeatureLists_mutex; // protects the _lineFeatureLists;
@@ -183,8 +199,12 @@ class VPBTechnique : public TerrainTechnique
inline static std::list<BucketAreaFeatureBinList> _areaFeatureLists;
inline static std::mutex _areaFeatureLists_mutex; // protects the _areaFeatureLists;
inline static std::list<BucketCoastlineBinList> _coastFeatureLists;
inline static std::mutex _coastFeatureLists_mutex; // protects the _areaFeatureLists;
};
};
}
#endif

View File

@@ -0,0 +1,146 @@
// VPBTileBounds.cxx -- VirtualPlanetBuilder Tile bounds for clipping
//
// Copyright (C) 2021 Stuart Buchanan
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "VPBTileBounds.hxx"
#include <simgear/debug/logstream.hxx>
TileBounds::TileBounds(Locator *locator, osg::Vec3d up) {
// Determine the corners of the tile;
locator->convertLocalToModel(osg::Vec3d(0.0, 0.0, 0.0), v00);
locator->convertLocalToModel(osg::Vec3d(1.0, 0.0, 0.0), v10);
locator->convertLocalToModel(osg::Vec3d(0.0, 1.0, 0.0), v01);
locator->convertLocalToModel(osg::Vec3d(1.0, 1.0, 0.0), v11);
corner[0] = v10;
corner[1] = v11;
corner[2] = v01;
corner[3] = v00;
// Determine the normals of the planes defining the vertical edges of the tiles.
// This can be found using the cross product of the horizontal line and an appropriate "up"
// vector. We will approximate the last using the center rather than working out different
// "up" vectors for each edge individually. We assume that (0,0) in local coords is bottom left.
south = (v10 - v00) ^ up;
east = (v11 - v10) ^ up;
north = (v01 - v11) ^ up;
west = (v00 - v01) ^ up;
}
std::list<osg::Vec3d> TileBounds::clipToTile(std::list<osg::Vec3d> points) {
std::list<osg::Vec3d> lreturn;
bool last_in = false;
auto last_pt = points.begin();
for (auto p = points.begin(); p != points.end(); p++) {
if (insideTile(*p)) {
/*
if ((last_in == false) && (last_pt != p)) {
// Last point was outside the border of the tile, so we need to
// add the actual intersection as an additional point if required.
lreturn.push_back(getTileIntersection(*p, *last_pt));
}
*/
// Last point wasn't in, but we need to include it to ensure we get an
// intersection with the edge of the tile.
if (last_in == false) lreturn.push_back(*last_pt);
// Point is within bounds, so add it.
lreturn.push_back(*p);
last_in = true;
} else if (last_in) {
// Point is outside bounds, but last point was inside. So add it to ensure
// we catch the intersection with the mesh edge.
lreturn.push_back(*p);
last_in = false;
}
last_pt = p;
}
return lreturn;
}
bool TileBounds::insideTile(osg::Vec3d pt) {
float s = south * (pt - v00);
float e = east * (pt - v10);
float n = north * (pt - v11);
float w = west * (pt - v01);
return ((s<0) && (e<0) && (n<0) && (w<0));
}
// Get the Tile intersection, and also the corner of the tile to the right of the intersection.
// This corner can be used to define the seaward edge of some coastline.
osg::Vec3d TileBounds::getTileIntersection(osg::Vec3d inside, osg::Vec3d outside) {
if (! insideTile(inside)) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Invalid VPB Tile intersection - \"inside\" point not inside!");
return inside;
}
if (insideTile(outside)) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Invalid VPB Tile intersection - \"outside\" point not outside!");
return outside;
}
// Simply clip against each of the planes in turn.
osg::Vec3d intersect = outside;
intersect = getPlaneIntersection(inside, intersect, south, v00);
intersect = getPlaneIntersection(inside, intersect, east, v10);
intersect = getPlaneIntersection(inside, intersect, north, v11);
intersect = getPlaneIntersection(inside, intersect, west, v01);
return intersect;
}
osg::Vec3d TileBounds::getPlaneIntersection(osg::Vec3d inside, osg::Vec3d outside, osg::Vec3d normal, osg::Vec3d plane) {
// Check for any intersection at all first
osg::Vec3d n = normal;
osg::Vec3d p0 = plane;
osg::Vec3d l0 = inside;
osg::Vec3d l = outside-inside;
if (fabs(l * n) < 0.01) return outside;
float d = ((p0 - l0) * n) / (l * n);
return l0 + l * d;
/*
float i = normal * (outside - plane);
if (i < 0) {
// There's an intersection, so calculate it
double d = ((plane - inside) * normal) / ((outside - inside) * normal);
return inside + (outside - inside) * d;
} else {
// No intersection
return outside;
}
*/
}

View File

@@ -0,0 +1,49 @@
// VPBTileBounds.hxx -- VirtualPlanetBuilder Tile bounds for clipping
//
// Copyright (C) 2021 Stuart Buchanan
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef VPBTILEBOUNDS
#define VPBTILEBOUNDS 1
#include <osg/Geometry>
#include <osgTerrain/TerrainTile>
using namespace osgTerrain;
class TileBounds {
public:
TileBounds(Locator *locator, osg::Vec3d up);
virtual std::list<osg::Vec3d> clipToTile(std::list<osg::Vec3d> points);
protected:
virtual bool insideTile(osg::Vec3d pt);
virtual osg::Vec3d getTileIntersection(osg::Vec3d inside, osg::Vec3d outside);
virtual osg::Vec3d getPlaneIntersection(osg::Vec3d inside, osg::Vec3d outside, osg::Vec3d normal, osg::Vec3d plane);
// Corners of the tile
osg::Vec3d v00, v01, v10, v11;
osg::Vec3d corner[4];
// Plan normals for the tile bounds
osg::Vec3d north, east, south, west;
};
#endif