From cbd8e3acc4eab6724b5be32f64eeeb0530e207ed Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sun, 8 Nov 2020 22:35:33 +0000 Subject: [PATCH] WS3.0 : Initial technique to load and render WS3.0 scenery --- simgear/scene/tgdb/VPBTechnique.cxx | 1558 +++++++++++++++++++++++++++ simgear/scene/tgdb/VPBTechnique.hxx | 130 +++ 2 files changed, 1688 insertions(+) create mode 100644 simgear/scene/tgdb/VPBTechnique.cxx create mode 100644 simgear/scene/tgdb/VPBTechnique.hxx diff --git a/simgear/scene/tgdb/VPBTechnique.cxx b/simgear/scene/tgdb/VPBTechnique.cxx new file mode 100644 index 00000000..3d76f07a --- /dev/null +++ b/simgear/scene/tgdb/VPBTechnique.cxx @@ -0,0 +1,1558 @@ +// VPBTechnique.cxx -- VirtualPlanetBuilder Effects technique +// +// Copyright (C) 2020 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 +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include + +#include "VPBTechnique.hxx" + +using namespace osgTerrain; +using namespace simgear; + +VPBTechnique::VPBTechnique() +{ + setFilterBias(0); + setFilterWidth(0.1); + setFilterMatrixAs(GAUSSIAN); +} + +VPBTechnique::VPBTechnique(const SGReaderWriterOptions* options) +{ + setFilterBias(0); + setFilterWidth(0.1); + setFilterMatrixAs(GAUSSIAN); + setOptions(options); + +} + +VPBTechnique::VPBTechnique(const VPBTechnique& gt,const osg::CopyOp& copyop): + TerrainTechnique(gt,copyop) +{ + setFilterBias(gt._filterBias); + setFilterWidth(gt._filterWidth); + setFilterMatrix(gt._filterMatrix); + setOptions(gt._options); +} + +VPBTechnique::~VPBTechnique() +{ +} + +void VPBTechnique::setFilterBias(float filterBias) +{ + _filterBias = filterBias; + if (!_filterBiasUniform) _filterBiasUniform = new osg::Uniform("filterBias",_filterBias); + else _filterBiasUniform->set(filterBias); +} + +void VPBTechnique::setFilterWidth(float filterWidth) +{ + _filterWidth = filterWidth; + if (!_filterWidthUniform) _filterWidthUniform = new osg::Uniform("filterWidth",_filterWidth); + else _filterWidthUniform->set(filterWidth); +} + +void VPBTechnique::setFilterMatrix(const osg::Matrix3& matrix) +{ + _filterMatrix = matrix; + if (!_filterMatrixUniform) _filterMatrixUniform = new osg::Uniform("filterMatrix",_filterMatrix); + else _filterMatrixUniform->set(_filterMatrix); +} + +void VPBTechnique::setOptions(const SGReaderWriterOptions* options) +{ + _options = simgear::SGReaderWriterOptions::copyOrCreate(options); + _options->setLoadOriginHint(simgear::SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS); +} + +void VPBTechnique::setFilterMatrixAs(FilterType filterType) +{ + switch(filterType) + { + case(SMOOTH): + setFilterMatrix(osg::Matrix3(0.0, 0.5/2.5, 0.0, + 0.5/2.5, 0.5/2.5, 0.5/2.5, + 0.0, 0.5/2.5, 0.0)); + break; + case(GAUSSIAN): + setFilterMatrix(osg::Matrix3(0.0, 1.0/8.0, 0.0, + 1.0/8.0, 4.0/8.0, 1.0/8.0, + 0.0, 1.0/8.0, 0.0)); + break; + case(SHARPEN): + setFilterMatrix(osg::Matrix3(0.0, -1.0, 0.0, + -1.0, 5.0, -1.0, + 0.0, -1.0, 0.0)); + break; + + }; +} + +void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded) +{ + OSG_INFO<<"Doing VPBTechnique::init()"< lock(_writeBufferMutex); + + osg::ref_ptr tile = _terrainTile; + + if (dirtyMask==0) return; + + osg::ref_ptr buffer = new BufferData; + + Locator* masterLocator = computeMasterLocator(); + + osg::Vec3d centerModel = computeCenterModel(*buffer, masterLocator); + + if ((dirtyMask & TerrainTile::IMAGERY_DIRTY)==0) + { + generateGeometry(*buffer, masterLocator, centerModel); + + osg::ref_ptr read_buffer = _currentBufferData; + + osg::StateSet* stateset = read_buffer->_geode->getStateSet(); + if (stateset) + { + buffer->_geode->setStateSet(stateset); + } + else + { + applyColorLayers(*buffer); + applyTransparency(*buffer); + } + } + else + { + generateGeometry(*buffer, masterLocator, centerModel); + applyColorLayers(*buffer); + applyTransparency(*buffer); + } + + if (buffer->_transform.valid()) buffer->_transform->setThreadSafeRefUnref(true); + + if (!_currentBufferData || !assumeMultiThreaded) + { + // no currentBufferData so we must be the first init to be applied + _currentBufferData = buffer; + } + else + { + // there is already an active _currentBufferData so we'll request that this gets swapped on next frame. + _newBufferData = buffer; + if (_terrainTile->getTerrain()) _terrainTile->getTerrain()->updateTerrainTileOnNextFrame(_terrainTile); + } + + _terrainTile->setDirtyMask(0); +} + +Locator* VPBTechnique::computeMasterLocator() +{ + osgTerrain::Layer* elevationLayer = _terrainTile->getElevationLayer(); + osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(0); + + Locator* elevationLocator = elevationLayer ? elevationLayer->getLocator() : 0; + Locator* colorLocator = colorLayer ? colorLayer->getLocator() : 0; + + Locator* masterLocator = elevationLocator ? elevationLocator : colorLocator; + if (!masterLocator) + { + OSG_NOTICE<<"Problem, no locator found in any of the terrain layers"<getElevationLayer(); + osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(0); + + Locator* elevationLocator = elevationLayer ? elevationLayer->getLocator() : 0; + Locator* colorLocator = colorLayer ? colorLayer->getLocator() : 0; + + if (!elevationLocator) elevationLocator = masterLocator; + if (!colorLocator) colorLocator = masterLocator; + + osg::Vec3d bottomLeftNDC(DBL_MAX, DBL_MAX, 0.0); + osg::Vec3d topRightNDC(-DBL_MAX, -DBL_MAX, 0.0); + + if (elevationLayer) + { + if (elevationLocator!= masterLocator) + { + masterLocator->computeLocalBounds(*elevationLocator, bottomLeftNDC, topRightNDC); + } + else + { + bottomLeftNDC.x() = osg::minimum(bottomLeftNDC.x(), 0.0); + bottomLeftNDC.y() = osg::minimum(bottomLeftNDC.y(), 0.0); + topRightNDC.x() = osg::maximum(topRightNDC.x(), 1.0); + topRightNDC.y() = osg::maximum(topRightNDC.y(), 1.0); + } + } + + if (colorLayer) + { + if (colorLocator!= masterLocator) + { + masterLocator->computeLocalBounds(*colorLocator, bottomLeftNDC, topRightNDC); + } + else + { + bottomLeftNDC.x() = osg::minimum(bottomLeftNDC.x(), 0.0); + bottomLeftNDC.y() = osg::minimum(bottomLeftNDC.y(), 0.0); + topRightNDC.x() = osg::maximum(topRightNDC.x(), 1.0); + topRightNDC.y() = osg::maximum(topRightNDC.y(), 1.0); + } + } + + OSG_INFO<<"bottomLeftNDC = "<size() + 1; + _vertices->push_back(v); + _normals->push_back(n); + // OSG_NOTICE<<"setVertex("< _vertices; + osg::ref_ptr _normals; + osg::ref_ptr _elevations; + + osg::ref_ptr _boundaryVertices; + +}; + +VertexNormalGenerator::VertexNormalGenerator(Locator* masterLocator, const osg::Vec3d& centerModel, int numRows, int numColumns, float scaleHeight, bool createSkirt): + _masterLocator(masterLocator), + _centerModel(centerModel), + _numRows(numRows), + _numColumns(numColumns), + _scaleHeight(scaleHeight) +{ + int numVerticesInBody = numColumns*numRows; + int numVerticesInSkirt = createSkirt ? numColumns*2 + numRows*2 - 4 : 0; + int numVertices = numVerticesInBody+numVerticesInSkirt; + + _indices.resize((_numRows+2)*(_numColumns+2),0); + + _vertices = new osg::Vec3Array; + _vertices->reserve(numVertices); + + _normals = new osg::Vec3Array; + _normals->reserve(numVertices); + + _elevations = new osg::FloatArray; + _elevations->reserve(numVertices); + + _boundaryVertices = new osg::Vec3Array; + _boundaryVertices->reserve(_numRows*2 + _numColumns*2 + 4); +} + +void VertexNormalGenerator::populateCenter(osgTerrain::Layer* elevationLayer, LayerToTexCoordMap& layerToTexCoordMap) +{ + // OSG_NOTICE<getNumRows()!=static_cast(_numRows)) || + (elevationLayer->getNumColumns()!=static_cast(_numColumns)) ); + + for(int j=0; j<_numRows; ++j) + { + for(int i=0; i<_numColumns; ++i) + { + osg::Vec3d ndc( ((double)i)/(double)(_numColumns-1), ((double)j)/(double)(_numRows-1), 0.0); + + bool validValue = true; + if (elevationLayer) + { + float value = 0.0f; + if (sampled) validValue = elevationLayer->getInterpolatedValidValue(ndc.x(), ndc.y(), value); + else validValue = elevationLayer->getValidValue(i,j,value); + ndc.z() = value*_scaleHeight; + } + + if (validValue) + { + osg::Vec3d model; + _masterLocator->convertLocalToModel(ndc, model); + + for(VertexNormalGenerator::LayerToTexCoordMap::iterator itr = layerToTexCoordMap.begin(); + itr != layerToTexCoordMap.end(); + ++itr) + { + osg::Vec2Array* texcoords = itr->second.first.get(); + osgTerrain::ImageLayer* imageLayer(dynamic_cast(itr->first)); + + if (imageLayer != NULL) + { + Locator* colorLocator = itr->second.second; + if (colorLocator != _masterLocator) + { + osg::Vec3d color_ndc; + Locator::convertLocalCoordBetween(*_masterLocator, ndc, *colorLocator, color_ndc); + (*texcoords).push_back(osg::Vec2(color_ndc.x(), color_ndc.y())); + } + else + { + (*texcoords).push_back(osg::Vec2(ndc.x(), ndc.y())); + } + } + else + { + osgTerrain::ContourLayer* contourLayer(dynamic_cast(itr->first)); + + bool texCoordSet = false; + if (contourLayer) + { + osg::TransferFunction1D* transferFunction = contourLayer->getTransferFunction(); + if (transferFunction) + { + float difference = transferFunction->getMaximum()-transferFunction->getMinimum(); + if (difference != 0.0f) + { + osg::Vec3d color_ndc; + osgTerrain::Locator* colorLocator(itr->second.second); + + if (colorLocator != _masterLocator) + { + Locator::convertLocalCoordBetween(*_masterLocator,ndc,*colorLocator,color_ndc); + } + else + { + color_ndc = ndc; + } + + color_ndc[2] /= _scaleHeight; + + (*texcoords).push_back(osg::Vec2((color_ndc[2]-transferFunction->getMinimum())/difference,0.0f)); + texCoordSet = true; + } + } + } + if (!texCoordSet) + { + (*texcoords).push_back(osg::Vec2(0.0f,0.0f)); + } + } + } + + if (_elevations.valid()) + { + (*_elevations).push_back(ndc.z()); + } + + // compute the local normal + osg::Vec3d ndc_one = ndc; ndc_one.z() += 1.0; + osg::Vec3d model_one; + _masterLocator->convertLocalToModel(ndc_one, model_one); + model_one = model_one - model; + model_one.normalize(); + + setVertex(i, j, osg::Vec3(model-_centerModel), model_one); + } + } + } +} + +void VertexNormalGenerator::populateLeftBoundary(osgTerrain::Layer* elevationLayer) +{ + // OSG_NOTICE<<" VertexNormalGenerator::populateLeftBoundary("<getNumRows()!=static_cast(_numRows)) || + (elevationLayer->getNumColumns()!=static_cast(_numColumns)) ); + + for(int j=0; j<_numRows; ++j) + { + for(int i=-1; i<=0; ++i) + { + osg::Vec3d ndc( ((double)i)/(double)(_numColumns-1), ((double)j)/(double)(_numRows-1), 0.0); + osg::Vec3d left_ndc( 1.0+ndc.x(), ndc.y(), 0.0); + + bool validValue = true; + if (elevationLayer) + { + float value = 0.0f; + if (sampled) validValue = elevationLayer->getInterpolatedValidValue(left_ndc.x(), left_ndc.y(), value); + else validValue = elevationLayer->getValidValue((_numColumns-1)+i,j,value); + ndc.z() = value*_scaleHeight; + + ndc.z() += 0.f; + } + if (validValue) + { + osg::Vec3d model; + _masterLocator->convertLocalToModel(ndc, model); + + // compute the local normal + osg::Vec3d ndc_one = ndc; ndc_one.z() += 1.0; + osg::Vec3d model_one; + _masterLocator->convertLocalToModel(ndc_one, model_one); + model_one = model_one - model; + model_one.normalize(); + + setVertex(i, j, osg::Vec3(model-_centerModel), model_one); + // OSG_NOTICE<<" setVertex("<getNumRows()!=static_cast(_numRows)) || + (elevationLayer->getNumColumns()!=static_cast(_numColumns)) ); + + for(int j=0; j<_numRows; ++j) + { + for(int i=_numColumns-1; i<_numColumns+1; ++i) + { + osg::Vec3d ndc( ((double)i)/(double)(_numColumns-1), ((double)j)/(double)(_numRows-1), 0.0); + osg::Vec3d right_ndc(ndc.x()-1.0, ndc.y(), 0.0); + + bool validValue = true; + if (elevationLayer) + { + float value = 0.0f; + if (sampled) validValue = elevationLayer->getInterpolatedValidValue(right_ndc.x(), right_ndc.y(), value); + else validValue = elevationLayer->getValidValue(i-(_numColumns-1),j,value); + ndc.z() = value*_scaleHeight; + } + + if (validValue) + { + osg::Vec3d model; + _masterLocator->convertLocalToModel(ndc, model); + + // compute the local normal + osg::Vec3d ndc_one = ndc; ndc_one.z() += 1.0; + osg::Vec3d model_one; + _masterLocator->convertLocalToModel(ndc_one, model_one); + model_one = model_one - model; + model_one.normalize(); + + setVertex(i, j, osg::Vec3(model-_centerModel), model_one); + // OSG_NOTICE<<" setVertex("<getNumRows()!=static_cast(_numRows)) || + (elevationLayer->getNumColumns()!=static_cast(_numColumns)) ); + + for(int j=_numRows-1; j<_numRows+1; ++j) + { + for(int i=0; i<_numColumns; ++i) + { + osg::Vec3d ndc( ((double)i)/(double)(_numColumns-1), ((double)j)/(double)(_numRows-1), 0.0); + osg::Vec3d above_ndc( ndc.x(), ndc.y()-1.0, 0.0); + + bool validValue = true; + if (elevationLayer) + { + float value = 0.0f; + if (sampled) validValue = elevationLayer->getInterpolatedValidValue(above_ndc.x(), above_ndc.y(), value); + else validValue = elevationLayer->getValidValue(i,j-(_numRows-1),value); + ndc.z() = value*_scaleHeight; + } + + if (validValue) + { + osg::Vec3d model; + _masterLocator->convertLocalToModel(ndc, model); + + // compute the local normal + osg::Vec3d ndc_one = ndc; ndc_one.z() += 1.0; + osg::Vec3d model_one; + _masterLocator->convertLocalToModel(ndc_one, model_one); + model_one = model_one - model; + model_one.normalize(); + + setVertex(i, j, osg::Vec3(model-_centerModel), model_one); + // OSG_NOTICE<<" setVertex("<getNumRows()!=static_cast(_numRows)) || + (elevationLayer->getNumColumns()!=static_cast(_numColumns)) ); + + for(int j=-1; j<=0; ++j) + { + for(int i=0; i<_numColumns; ++i) + { + osg::Vec3d ndc( ((double)i)/(double)(_numColumns-1), ((double)j)/(double)(_numRows-1), 0.0); + osg::Vec3d below_ndc( ndc.x(), 1.0+ndc.y(), 0.0); + + bool validValue = true; + if (elevationLayer) + { + float value = 0.0f; + if (sampled) validValue = elevationLayer->getInterpolatedValidValue(below_ndc.x(), below_ndc.y(), value); + else validValue = elevationLayer->getValidValue(i,(_numRows-1)+j,value); + ndc.z() = value*_scaleHeight; + } + + if (validValue) + { + osg::Vec3d model; + _masterLocator->convertLocalToModel(ndc, model); + + // compute the local normal + osg::Vec3d ndc_one = ndc; ndc_one.z() += 1.0; + osg::Vec3d model_one; + _masterLocator->convertLocalToModel(ndc_one, model_one); + model_one = model_one - model; + model_one.normalize(); + + setVertex(i, j, osg::Vec3(model-_centerModel), model_one); + // OSG_NOTICE<<" setVertex("<=0) computeNormal(i, j, (*_normals)[vi]); + else OSG_NOTICE<<"Not computing normal, vi="<getTerrain(); + osgTerrain::Layer* elevationLayer = _terrainTile->getElevationLayer(); + + // Determine the correct Effect for this, based on a material lookup taking into account + // the lat/lon of the center. + SGPropertyNode_ptr effectProp; + SGMaterialLibPtr matlib = _options->getMaterialLib(); + + if (matlib) { + // Determine the center of the tile, sadly non-trivial. + 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); + SG_LOG(SG_TERRAIN, SG_DEBUG, "Applying VPB material " << loc); + SGMaterialCache* matcache = _options->getMaterialLib()->generateMatCache(loc); + SGMaterial* mat = matcache->find("ws30"); + delete matcache; + + if (mat) { + effectProp = new SGPropertyNode(); + makeChild(effectProp, "inherits-from")->setStringValue(mat->get_effect_name()); + //effectPropeffect->getChild("texture", 0, true)->getChild("image", 0, true)->setStringValue("Terrain/forest1a.png"); + //SG_LOG(SG_TERRAIN, SG_ALERT, "Created VPB effect property " << mat->get_effect_name()); + } else { + SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get effect for VPB - no matching material in library"); + effectProp = new SGPropertyNode; + makeChild(effectProp.ptr(), "inherits-from")->setStringValue("Effects/model-default"); + } + } else { + SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get effect for VPB - no material library available"); + effectProp = new SGPropertyNode; + makeChild(effectProp.ptr(), "inherits-from")->setStringValue("Effects/model-default"); + } + + buffer._geode = new EffectGeode(); + if(buffer._transform.valid()) + buffer._transform->addChild(buffer._geode.get()); + + buffer._geometry = new osg::Geometry; + buffer._geode->addDrawable(buffer._geometry.get()); + + osg::ref_ptr effect = makeEffect(effectProp, true, _options); + buffer._geode->setEffect(effect.get()); + + osg::Geometry* geometry = buffer._geometry.get(); + + unsigned int numRows = 20; + unsigned int numColumns = 20; + + if (elevationLayer) + { + numColumns = elevationLayer->getNumColumns(); + numRows = elevationLayer->getNumRows(); + } + + float sampleRatio = terrain ? terrain->getSampleRatio() : 1.0f; + + // OSG_NOTICE<<"Sample ratio="<minimumNumColumns) && (numRows>minimumNumRows)) + { + unsigned int originalNumColumns = numColumns; + unsigned int originalNumRows = numRows; + + numColumns = std::max((unsigned int) (float(originalNumColumns)*sqrtf(sampleRatio)), minimumNumColumns); + numRows = std::max((unsigned int) (float(originalNumRows)*sqrtf(sampleRatio)),minimumNumRows); + } + + + + bool treatBoundariesToValidDataAsDefaultValue = _terrainTile->getTreatBoundariesToValidDataAsDefaultValue(); + OSG_INFO<<"TreatBoundariesToValidDataAsDefaultValue="<(elevationLayer); + if (hfl && hfl->getHeightField()) + { + skirtHeight = hfl->getHeightField()->getSkirtHeight(); + } + + bool createSkirt = skirtHeight != 0.0f; + + + float scaleHeight = terrain ? terrain->getVerticalScale() : 1.0f; + + // construct the VertexNormalGenerator which will manage the generation and the vertices and normals + VertexNormalGenerator VNG(masterLocator, centerModel, numRows, numColumns, scaleHeight, createSkirt); + + unsigned int numVertices = VNG.capacity(); + + // allocate and assign vertices + geometry->setVertexArray(VNG._vertices.get()); + + // allocate and assign normals + geometry->setNormalArray(VNG._normals.get(), osg::Array::BIND_PER_VERTEX); + + + // allocate and assign tex coords + // typedef std::pair< osg::ref_ptr, Locator* > TexCoordLocatorPair; + // typedef std::map< Layer*, TexCoordLocatorPair > LayerToTexCoordMap; + + VertexNormalGenerator::LayerToTexCoordMap layerToTexCoordMap; + for(unsigned int layerNum=0; layerNum<_terrainTile->getNumColorLayers(); ++layerNum) + { + osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(layerNum); + if (colorLayer) + { + VertexNormalGenerator::LayerToTexCoordMap::iterator itr = layerToTexCoordMap.find(colorLayer); + if (itr!=layerToTexCoordMap.end()) + { + geometry->setTexCoordArray(layerNum, itr->second.first.get()); + } + else + { + + Locator* locator = colorLayer->getLocator(); + if (!locator) + { + osgTerrain::SwitchLayer* switchLayer = dynamic_cast(colorLayer); + if (switchLayer) + { + if (switchLayer->getActiveLayer()>=0 && + static_cast(switchLayer->getActiveLayer())getNumLayers() && + switchLayer->getLayer(switchLayer->getActiveLayer())) + { + locator = switchLayer->getLayer(switchLayer->getActiveLayer())->getLocator(); + } + } + } + + VertexNormalGenerator::TexCoordLocatorPair& tclp = layerToTexCoordMap[colorLayer]; + tclp.first = new osg::Vec2Array; + tclp.first->reserve(numVertices); + tclp.second = locator ? locator : masterLocator; + geometry->setTexCoordArray(layerNum, tclp.first.get()); + } + } + } + + // allocate and assign color + osg::ref_ptr colors = new osg::Vec4Array(1); + (*colors)[0].set(1.0f,1.0f,1.0f,1.0f); + + geometry->setColorArray(colors.get(), osg::Array::BIND_OVERALL); + + // + // populate vertex and tex coord arrays + // + VNG.populateCenter(elevationLayer, layerToTexCoordMap); + + if (terrain && terrain->getEqualizeBoundaries()) + { + TileID tileID = _terrainTile->getTileID(); + + osg::ref_ptr left_tile = terrain->getTile(TileID(tileID.level, tileID.x-1, tileID.y)); + osg::ref_ptr right_tile = terrain->getTile(TileID(tileID.level, tileID.x+1, tileID.y)); + osg::ref_ptr top_tile = terrain->getTile(TileID(tileID.level, tileID.x, tileID.y+1)); + osg::ref_ptr bottom_tile = terrain->getTile(TileID(tileID.level, tileID.x, tileID.y-1)); + +#if 0 + osg::ref_ptr top_left_tile = terrain->getTile(TileID(tileID.level, tileID.x-1, tileID.y+1)); + osg::ref_ptr top_right_tile = terrain->getTile(TileID(tileID.level, tileID.x+1, tileID.y+1)); + osg::ref_ptr bottom_left_tile = terrain->getTile(TileID(tileID.level, tileID.x-1, tileID.y-1)); + osg::ref_ptr bottom_right_tile = terrain->getTile(TileID(tileID.level, tileID.x+1, tileID.y-1)); +#endif + VNG.populateLeftBoundary(left_tile.valid() ? left_tile->getElevationLayer() : 0); + VNG.populateRightBoundary(right_tile.valid() ? right_tile->getElevationLayer() : 0); + VNG.populateAboveBoundary(top_tile.valid() ? top_tile->getElevationLayer() : 0); + VNG.populateBelowBoundary(bottom_tile.valid() ? bottom_tile->getElevationLayer() : 0); + + _neighbours.clear(); + + bool updateNeighboursImmediately = false; + + if (left_tile.valid()) addNeighbour(left_tile.get()); + if (right_tile.valid()) addNeighbour(right_tile.get()); + if (top_tile.valid()) addNeighbour(top_tile.get()); + if (bottom_tile.valid()) addNeighbour(bottom_tile.get()); + +#if 0 + if (bottom_left_tile.valid()) addNeighbour(bottom_left_tile.get()); + if (bottom_right_tile.valid()) addNeighbour(bottom_right_tile.get()); + if (top_left_tile.valid()) addNeighbour(top_left_tile.get()); + if (top_right_tile.valid()) addNeighbour(top_right_tile.get()); +#endif + + if (left_tile.valid()) + { + if (left_tile->getTerrainTechnique()==0 || !(left_tile->getTerrainTechnique()->containsNeighbour(_terrainTile))) + { + int dirtyMask = left_tile->getDirtyMask() | TerrainTile::LEFT_EDGE_DIRTY; + if (updateNeighboursImmediately) left_tile->init(dirtyMask, true); + else left_tile->setDirtyMask(dirtyMask); + } + } + if (right_tile.valid()) + { + if (right_tile->getTerrainTechnique()==0 || !(right_tile->getTerrainTechnique()->containsNeighbour(_terrainTile))) + { + int dirtyMask = right_tile->getDirtyMask() | TerrainTile::RIGHT_EDGE_DIRTY; + if (updateNeighboursImmediately) right_tile->init(dirtyMask, true); + else right_tile->setDirtyMask(dirtyMask); + } + } + if (top_tile.valid()) + { + if (top_tile->getTerrainTechnique()==0 || !(top_tile->getTerrainTechnique()->containsNeighbour(_terrainTile))) + { + int dirtyMask = top_tile->getDirtyMask() | TerrainTile::TOP_EDGE_DIRTY; + if (updateNeighboursImmediately) top_tile->init(dirtyMask, true); + else top_tile->setDirtyMask(dirtyMask); + } + } + + if (bottom_tile.valid()) + { + if (bottom_tile->getTerrainTechnique()==0 || !(bottom_tile->getTerrainTechnique()->containsNeighbour(_terrainTile))) + { + int dirtyMask = bottom_tile->getDirtyMask() | TerrainTile::BOTTOM_EDGE_DIRTY; + if (updateNeighboursImmediately) bottom_tile->init(dirtyMask, true); + else bottom_tile->setDirtyMask(dirtyMask); + } + } + +#if 0 + if (bottom_left_tile.valid()) + { + if (!(bottom_left_tile->getTerrainTechnique()->containsNeighbour(_terrainTile))) + { + int dirtyMask = bottom_left_tile->getDirtyMask() | TerrainTile::BOTTOM_LEFT_CORNER_DIRTY; + if (updateNeighboursImmediately) bottom_left_tile->init(dirtyMask, true); + else bottom_left_tile->setDirtyMask(dirtyMask); + } + } + + if (bottom_right_tile.valid()) + { + if (!(bottom_right_tile->getTerrainTechnique()->containsNeighbour(_terrainTile))) + { + int dirtyMask = bottom_right_tile->getDirtyMask() | TerrainTile::BOTTOM_RIGHT_CORNER_DIRTY; + if (updateNeighboursImmediately) bottom_right_tile->init(dirtyMask, true); + else bottom_right_tile->setDirtyMask(dirtyMask); + } + } + + if (top_right_tile.valid()) + { + if (!(top_right_tile->getTerrainTechnique()->containsNeighbour(_terrainTile))) + { + int dirtyMask = top_right_tile->getDirtyMask() | TerrainTile::TOP_RIGHT_CORNER_DIRTY; + if (updateNeighboursImmediately) top_right_tile->init(dirtyMask, true); + else top_right_tile->setDirtyMask(dirtyMask); + } + } + + if (top_left_tile.valid()) + { + if (!(top_left_tile->getTerrainTechnique()->containsNeighbour(_terrainTile))) + { + int dirtyMask = top_left_tile->getDirtyMask() | TerrainTile::TOP_LEFT_CORNER_DIRTY; + if (updateNeighboursImmediately) top_left_tile->init(dirtyMask, true); + else top_left_tile->setDirtyMask(dirtyMask); + } + } +#endif + } + + osg::ref_ptr skirtVectors = new osg::Vec3Array((*VNG._normals)); + VNG.computeNormals(); + + // + // populate the primitive data + // + bool swapOrientation = !(masterLocator->orientationOpenGL()); + bool smallTile = numVertices < 65536; + + // OSG_NOTICE<<"smallTile = "< elements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_TRIANGLES)) : + static_cast(new osg::DrawElementsUInt(GL_TRIANGLES)); + + elements->reserveElements((numRows-1) * (numColumns-1) * 6); + + geometry->addPrimitiveSet(elements.get()); + + unsigned int i, j; + for(j=0; j=0) ++numValid; + if (i01>=0) ++numValid; + if (i10>=0) ++numValid; + if (i11>=0) ++numValid; + + if (numValid==4) + { + // optimize which way to put the diagonal by choosing to + // place it between the two corners that have the least curvature + // relative to each other. + float dot_00_11 = (*VNG._normals)[i00] * (*VNG._normals)[i11]; + float dot_01_10 = (*VNG._normals)[i01] * (*VNG._normals)[i10]; + if (dot_00_11 > dot_01_10) + { + elements->addElement(i01); + elements->addElement(i00); + elements->addElement(i11); + + elements->addElement(i00); + elements->addElement(i10); + elements->addElement(i11); + } + else + { + elements->addElement(i01); + elements->addElement(i00); + elements->addElement(i10); + + elements->addElement(i01); + elements->addElement(i10); + elements->addElement(i11); + } + } + else if (numValid==3) + { + if (i00>=0) elements->addElement(i00); + if (i01>=0) elements->addElement(i01); + if (i11>=0) elements->addElement(i11); + if (i10>=0) elements->addElement(i10); + } + } + } + + + if (createSkirt) + { + osg::ref_ptr vertices = VNG._vertices.get(); + osg::ref_ptr normals = VNG._normals.get(); + + osg::ref_ptr skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); + + // create bottom skirt vertices + int r,c; + r=0; + for(c=0;c(numColumns);++c) + { + int orig_i = VNG.vertex_index(c,r); + if (orig_i>=0) + { + unsigned int new_i = vertices->size(); // index of new index of added skirt point + osg::Vec3 new_v = (*vertices)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight; + (*vertices).push_back(new_v); + if (normals.valid()) (*normals).push_back((*normals)[orig_i]); + + for(VertexNormalGenerator::LayerToTexCoordMap::iterator itr = layerToTexCoordMap.begin(); + itr != layerToTexCoordMap.end(); + ++itr) + { + itr->second.first->push_back((*itr->second.first)[orig_i]); + } + + skirtDrawElements->addElement(orig_i); + skirtDrawElements->addElement(new_i); + } + else + { + if (skirtDrawElements->getNumIndices()!=0) + { + geometry->addPrimitiveSet(skirtDrawElements.get()); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); + } + + } + } + + if (skirtDrawElements->getNumIndices()!=0) + { + geometry->addPrimitiveSet(skirtDrawElements.get()); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); + } + + // create right skirt vertices + c=numColumns-1; + for(r=0;r(numRows);++r) + { + int orig_i = VNG.vertex_index(c,r); // index of original vertex of grid + if (orig_i>=0) + { + unsigned int new_i = vertices->size(); // index of new index of added skirt point + osg::Vec3 new_v = (*vertices)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight; + (*vertices).push_back(new_v); + if (normals.valid()) (*normals).push_back((*normals)[orig_i]); + for(VertexNormalGenerator::LayerToTexCoordMap::iterator itr = layerToTexCoordMap.begin(); + itr != layerToTexCoordMap.end(); + ++itr) + { + itr->second.first->push_back((*itr->second.first)[orig_i]); + } + + skirtDrawElements->addElement(orig_i); + skirtDrawElements->addElement(new_i); + } + else + { + if (skirtDrawElements->getNumIndices()!=0) + { + geometry->addPrimitiveSet(skirtDrawElements.get()); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); + } + + } + } + + if (skirtDrawElements->getNumIndices()!=0) + { + geometry->addPrimitiveSet(skirtDrawElements.get()); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); + } + + // create top skirt vertices + r=numRows-1; + for(c=numColumns-1;c>=0;--c) + { + int orig_i = VNG.vertex_index(c,r); // index of original vertex of grid + if (orig_i>=0) + { + unsigned int new_i = vertices->size(); // index of new index of added skirt point + osg::Vec3 new_v = (*vertices)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight; + (*vertices).push_back(new_v); + if (normals.valid()) (*normals).push_back((*normals)[orig_i]); + for(VertexNormalGenerator::LayerToTexCoordMap::iterator itr = layerToTexCoordMap.begin(); + itr != layerToTexCoordMap.end(); + ++itr) + { + itr->second.first->push_back((*itr->second.first)[orig_i]); + } + + skirtDrawElements->addElement(orig_i); + skirtDrawElements->addElement(new_i); + } + else + { + if (skirtDrawElements->getNumIndices()!=0) + { + geometry->addPrimitiveSet(skirtDrawElements.get()); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); + } + + } + } + + if (skirtDrawElements->getNumIndices()!=0) + { + geometry->addPrimitiveSet(skirtDrawElements.get()); + skirtDrawElements = smallTile ? + static_cast(new osg::DrawElementsUShort(GL_QUAD_STRIP)) : + static_cast(new osg::DrawElementsUInt(GL_QUAD_STRIP)); + } + + // create left skirt vertices + c=0; + for(r=numRows-1;r>=0;--r) + { + int orig_i = VNG.vertex_index(c,r); // index of original vertex of grid + if (orig_i>=0) + { + unsigned int new_i = vertices->size(); // index of new index of added skirt point + osg::Vec3 new_v = (*vertices)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight; + (*vertices).push_back(new_v); + if (normals.valid()) (*normals).push_back((*normals)[orig_i]); + for(VertexNormalGenerator::LayerToTexCoordMap::iterator itr = layerToTexCoordMap.begin(); + itr != layerToTexCoordMap.end(); + ++itr) + { + itr->second.first->push_back((*itr->second.first)[orig_i]); + } + + skirtDrawElements->addElement(orig_i); + skirtDrawElements->addElement(new_i); + } + else + { + if (skirtDrawElements->getNumIndices()!=0) + { + geometry->addPrimitiveSet(skirtDrawElements.get()); + skirtDrawElements = new osg::DrawElementsUShort(GL_QUAD_STRIP); + } + } + } + + if (skirtDrawElements->getNumIndices()!=0) + { + geometry->addPrimitiveSet(skirtDrawElements.get()); + } + } + + + geometry->setUseDisplayList(false); + geometry->setUseVertexBufferObjects(true); + +#if 0 + { + osgUtil::VertexCacheMissVisitor vcmv_before; + osgUtil::VertexCacheMissVisitor vcmv_after; + osgUtil::VertexCacheVisitor vcv; + osgUtil::VertexAccessOrderVisitor vaov; + + vcmv_before.doGeometry(*geometry); + vcv.optimizeVertices(*geometry); + vaov.optimizeOrder(*geometry); + vcmv_after.doGeometry(*geometry); +#if 0 + OSG_NOTICE<<"vcmv_before.triangles="< builder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); + buffer._geode->accept(*builder); + //osg::Timer_t after = osg::Timer::instance()->tick(); + //OSG_NOTICE<<"KdTree build time "<delta_m(before, after)< LayerToTextureMap; + LayerToTextureMap layerToTextureMap; + + for(unsigned int layerNum=0; layerNum<_terrainTile->getNumColorLayers(); ++layerNum) + { + osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(layerNum); + if (!colorLayer) continue; + + osgTerrain::SwitchLayer* switchLayer = dynamic_cast(colorLayer); + if (switchLayer) + { + if (switchLayer->getActiveLayer()<0 || + static_cast(switchLayer->getActiveLayer())>=switchLayer->getNumLayers()) + { + continue; + } + + colorLayer = switchLayer->getLayer(switchLayer->getActiveLayer()); + if (!colorLayer) continue; + } + + osg::Image* image = colorLayer->getImage(); + if (!image) continue; + + osgTerrain::ImageLayer* imageLayer = dynamic_cast(colorLayer); + osgTerrain::ContourLayer* contourLayer = dynamic_cast(colorLayer); + if (imageLayer) + { + osg::StateSet* stateset = buffer._geode->getOrCreateStateSet(); + + osg::Texture2D* texture2D = dynamic_cast(layerToTextureMap[colorLayer]); + if (!texture2D) + { + texture2D = new osg::Texture2D; + texture2D->setImage(image); + texture2D->setMaxAnisotropy(16.0f); + texture2D->setResizeNonPowerOfTwoHint(false); + + texture2D->setFilter(osg::Texture::MIN_FILTER, colorLayer->getMinFilter()); + texture2D->setFilter(osg::Texture::MAG_FILTER, colorLayer->getMagFilter()); + + 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)) + { + OSG_INFO<<"Disabling mipmapping for non power of two tile size("<s()<<", "<t()<<")"<setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + } + + + layerToTextureMap[colorLayer] = texture2D; + + } + + stateset->setTextureAttributeAndModes(layerNum, texture2D, osg::StateAttribute::ON); + + } + else if (contourLayer) + { + osg::StateSet* stateset = buffer._geode->getOrCreateStateSet(); + + osg::Texture1D* texture1D = dynamic_cast(layerToTextureMap[colorLayer]); + if (!texture1D) + { + texture1D = new osg::Texture1D; + texture1D->setImage(image); + texture1D->setResizeNonPowerOfTwoHint(false); + texture1D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); + texture1D->setFilter(osg::Texture::MAG_FILTER, colorLayer->getMagFilter()); + + layerToTextureMap[colorLayer] = texture1D; + } + + stateset->setTextureAttributeAndModes(layerNum, texture1D, osg::StateAttribute::ON); + + } + } +} + +void VPBTechnique::applyTransparency(BufferData& buffer) +{ + TerrainTile::BlendingPolicy blendingPolicy = _terrainTile->getBlendingPolicy(); + if (blendingPolicy == TerrainTile::INHERIT && _terrainTile->getTerrain()) + { + OSG_INFO<<"VPBTechnique::applyTransparency() inheriting policy from Terrain"<getTerrain()->getBlendingPolicy(); + } + + if (blendingPolicy == TerrainTile::INHERIT) + { + OSG_INFO<<"VPBTechnique::applyTransparency() policy is INHERIT, defaulting to ENABLE_BLENDING_WHEN_ALPHA_PRESENT"<getNumColorLayers(); ++i) + { + osg::Image* image = (_terrainTile->getColorLayer(i)!=0) ? _terrainTile->getColorLayer(i)->getImage() : 0; + if (image) + { + enableBlending = image->isImageTranslucent(); + break; + } + } + } + + if (enableBlending) + { + osg::StateSet* stateset = buffer._geode->getOrCreateStateSet(); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + } + +} + +void VPBTechnique::update(osgUtil::UpdateVisitor* uv) +{ + if (_terrainTile) _terrainTile->osg::Group::traverse(*uv); + + if (_newBufferData.valid()) + { + _currentBufferData = _newBufferData; + _newBufferData = 0; + } +} + + +void VPBTechnique::cull(osgUtil::CullVisitor* cv) +{ + if (_currentBufferData.valid()) + { + if (_currentBufferData->_transform.valid()) + { + _currentBufferData->_transform->accept(*cv); + } + } +} + + +void VPBTechnique::traverse(osg::NodeVisitor& nv) +{ + if (!_terrainTile) return; + + // if app traversal update the frame count. + if (nv.getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR) + { + if (_terrainTile->getDirty()) _terrainTile->init(_terrainTile->getDirtyMask(), false); + + osgUtil::UpdateVisitor* uv = nv.asUpdateVisitor(); + if (uv) + { + update(uv); + return; + } + } + else if (nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR) + { + osgUtil::CullVisitor* cv = nv.asCullVisitor(); + if (cv) + { + cull(cv); + return; + } + } + + + if (_terrainTile->getDirty()) + { + OSG_INFO<<"******* Doing init ***********"<init(_terrainTile->getDirtyMask(), false); + } + + if (_currentBufferData.valid()) + { + if (_currentBufferData->_transform.valid()) _currentBufferData->_transform->accept(nv); + } +} + + +void VPBTechnique::cleanSceneGraph() +{ +} + +void VPBTechnique::releaseGLObjects(osg::State* state) const +{ + if (_currentBufferData.valid() && _currentBufferData->_transform.valid()) _currentBufferData->_transform->releaseGLObjects(state); + if (_newBufferData.valid() && _newBufferData->_transform.valid()) _newBufferData->_transform->releaseGLObjects(state); +} + diff --git a/simgear/scene/tgdb/VPBTechnique.hxx b/simgear/scene/tgdb/VPBTechnique.hxx new file mode 100644 index 00000000..75cbab30 --- /dev/null +++ b/simgear/scene/tgdb/VPBTechnique.hxx @@ -0,0 +1,130 @@ +// VPBTechnique.hxx -- VirtualPlanetBuilder Effects technique +// +// Copyright (C) 2020 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 VPBTECHNIQUE +#define VPBTECHNIQUE 1 + +#include +#include +#include + +#include +#include + +#include + +using namespace osgTerrain; + +namespace simgear { + +class VPBTechnique : public TerrainTechnique +{ + public: + + VPBTechnique(); + VPBTechnique(const SGReaderWriterOptions* options); + + /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ + VPBTechnique(const VPBTechnique&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); + + META_Object(osgTerrain, VPBTechnique); + + virtual void init(int dirtyMask, bool assumeMultiThreaded); + + virtual Locator* computeMasterLocator(); + + + virtual void update(osgUtil::UpdateVisitor* nv); + + virtual void cull(osgUtil::CullVisitor* nv); + + /** Traverse the terain subgraph.*/ + virtual void traverse(osg::NodeVisitor& nv); + + virtual void cleanSceneGraph(); + + void setFilterBias(float filterBias); + float getFilterBias() const { return _filterBias; } + + void setFilterWidth(float filterWidth); + float getFilterWidth() const { return _filterWidth; } + + void setFilterMatrix(const osg::Matrix3& matrix); + osg::Matrix3& getFilterMatrix() { return _filterMatrix; } + const osg::Matrix3& getFilterMatrix() const { return _filterMatrix; } + + enum FilterType + { + GAUSSIAN, + SMOOTH, + SHARPEN + }; + + void setFilterMatrixAs(FilterType filterType); + + void setOptions(const SGReaderWriterOptions* options); + + /** If State is non-zero, this function releases any associated OpenGL objects for + * the specified graphics context. Otherwise, releases OpenGL objects + * for all graphics contexts. */ + virtual void releaseGLObjects(osg::State* = 0) const; + + + protected: + + virtual ~VPBTechnique(); + + class BufferData : public osg::Referenced + { + public: + BufferData() {} + + osg::ref_ptr _transform; + osg::ref_ptr _geode; + osg::ref_ptr _geometry; + + protected: + ~BufferData() {} + }; + + virtual osg::Vec3d computeCenter(BufferData& buffer, Locator* masterLocator); + virtual osg::Vec3d computeCenterModel(BufferData& buffer, Locator* masterLocator); + + virtual void generateGeometry(BufferData& buffer, Locator* masterLocator, const osg::Vec3d& centerModel); + + virtual void applyColorLayers(BufferData& buffer); + + virtual void applyTransparency(BufferData& buffer); + + + OpenThreads::Mutex _writeBufferMutex; + osg::ref_ptr _currentBufferData; + osg::ref_ptr _newBufferData; + + float _filterBias; + osg::ref_ptr _filterBiasUniform; + float _filterWidth; + osg::ref_ptr _filterWidthUniform; + osg::Matrix3 _filterMatrix; + osg::ref_ptr _filterMatrixUniform; + osg::ref_ptr _options; +}; + +} + +#endif