|
osgEarth 2.1.1
|
Inheritance diagram for SinglePassTerrainTechnique:
Collaboration diagram for SinglePassTerrainTechnique:Classes | |
| struct | ImageLayerUpdate |
Public Member Functions | |
| SinglePassTerrainTechnique (TextureCompositor *compositor=0L) | |
| SinglePassTerrainTechnique (const SinglePassTerrainTechnique &, const osg::CopyOp ©op=osg::CopyOp::SHALLOW_COPY) | |
| META_Object (osgEarth, SinglePassTerrainTechnique) | |
| virtual void | init () |
| void | setParentTile (Tile *tile) |
| void | compile (const TileUpdate &updateSpec, ProgressCallback *progress) |
| bool | applyTileUpdates () |
| virtual void | traverse (osg::NodeVisitor &nv) |
| void | setVerticalScaleOverride (float value) |
| float | getVerticalScaleOverride () const |
| void | setOptimizeTriangleOrientation (bool optimizeTriangleOrientation) |
| bool | getOptimizeTriangleOrientation () const |
| virtual void | releaseGLObjects (osg::State *=0) const |
| osg::StateSet * | getActiveStateSet () const |
Protected Member Functions | |
| void | calculateSampling (unsigned int &out_rows, unsigned int &out_cols, double &out_i, double &out_j) |
Private Types | |
| typedef std::queue < ImageLayerUpdate > | ImageLayerUpdates |
| typedef std::map< UID, int > | LayerUIDtoIndexMap |
Private Member Functions | |
| virtual | ~SinglePassTerrainTechnique () |
| osg::Vec3d | computeCenterModel () |
| bool | createGeoImage (const CustomColorLayer &layer, GeoImage &image) const |
| osg::Geode * | createGeometry (const TileFrame &tilef) |
| osg::StateSet * | createStateSet (const TileFrame &tilef) |
| void | prepareImageLayerUpdate (int layerIndex, const TileFrame &tilef) |
| osg::Geode * | getFrontGeode () const |
| osg::StateSet * | getParentStateSet () const |
| int | getIndexOfColorLayerWithUID (UID uid) const |
Private Attributes | |
| bool | _debug |
| OpenThreads::Mutex | _compileMutex |
| osg::ref_ptr < osg::MatrixTransform > | _transform |
| osg::ref_ptr< osg::Geode > | _backGeode |
| osg::ref_ptr< osg::Uniform > | _imageLayerStampUniform |
| osg::Vec3d | _centerModel |
| float | _verticalScaleOverride |
| osg::ref_ptr< GeoLocator > | _masterLocator |
| int | _initCount |
| bool | _pendingFullUpdate |
| bool | _pendingGeometryUpdate |
| ImageLayerUpdates | _pendingImageLayerUpdates |
| LayerUIDtoIndexMap | _layerUIDtoIndexMap |
| GeoExtent | _tileExtent |
| TileKey | _tileKey |
| bool | _optimizeTriangleOrientation |
| osg::ref_ptr< const TextureCompositor > | _texCompositor |
| bool | _frontGeodeInstalled |
| osg::observer_ptr< Tile > | _parentTile |
A terrain technique that uses a single texture unit by compositing image layer textures on the GPU.
This technique works by creating a single "mosaic" texture and copying each image layer's texture into that mosaic. It then creates a uniform array that conveys the relative offset and scale information of each sub-texture to a shader. The shader then composites the approprate mosaic texels on the GPU.
Limitations:
This technique is limited by the maximum texture size your GPU will support, since it creates a single mosaic texture. For example, if your GPU's max texture size is 2048, this technique can support 64 256-pixel layers.
Definition at line 63 of file SinglePassTerrainTechnique.
typedef std::queue<ImageLayerUpdate> SinglePassTerrainTechnique::ImageLayerUpdates [private] |
Definition at line 150 of file SinglePassTerrainTechnique.
typedef std::map< UID, int > SinglePassTerrainTechnique::LayerUIDtoIndexMap [private] |
Definition at line 154 of file SinglePassTerrainTechnique.
| SinglePassTerrainTechnique::SinglePassTerrainTechnique | ( | TextureCompositor * | compositor = 0L | ) |
Definition at line 68 of file SinglePassTerrainTechnique.cpp.
: CustomTerrainTechnique(), _verticalScaleOverride(1.0f), _initCount(0), _pendingFullUpdate( false ), _pendingGeometryUpdate(false), _optimizeTriangleOrientation(true), _texCompositor( compositor ), _frontGeodeInstalled( false ), _debug( false ) { this->setThreadSafeRefUnref(true); }
| SinglePassTerrainTechnique::SinglePassTerrainTechnique | ( | const SinglePassTerrainTechnique & | rhs, |
| const osg::CopyOp & | copyop = osg::CopyOp::SHALLOW_COPY |
||
| ) |
Definition at line 82 of file SinglePassTerrainTechnique.cpp.
: CustomTerrainTechnique( rhs, copyop ), _verticalScaleOverride( rhs._verticalScaleOverride ), _initCount( 0 ), _pendingFullUpdate( false ), _pendingGeometryUpdate( false ), _optimizeTriangleOrientation( rhs._optimizeTriangleOrientation ), _texCompositor( rhs._texCompositor.get() ), _frontGeodeInstalled( rhs._frontGeodeInstalled ), _debug( rhs._debug ), _parentTile( rhs._parentTile ) { //NOP }
| SinglePassTerrainTechnique::~SinglePassTerrainTechnique | ( | ) | [private, virtual] |
Definition at line 97 of file SinglePassTerrainTechnique.cpp.
{
//nop
}
| bool SinglePassTerrainTechnique::applyTileUpdates | ( | ) | [virtual] |
Implements CustomTerrainTechnique.
Definition at line 253 of file SinglePassTerrainTechnique.cpp.
{
bool applied = false;
// serialize access to the compilation mechanism.
OpenThreads::ScopedLock<Mutex> exclusiveLock( _compileMutex );
// process a pending buffer swap:
if ( _pendingFullUpdate )
{
if ( _backGeode->getStateSet() == 0L )
OE_WARN << LC << "ILLEGAL: backGeode has no stateset" << std::endl;
_transform->setChild( 0, _backGeode.get() );
_frontGeodeInstalled = true;
_backGeode = 0L;
_pendingFullUpdate = false;
_pendingGeometryUpdate = false;
applied = true;
}
else
{
// process any pending LIVE geometry updates:
if ( _pendingGeometryUpdate )
{
osg::Geode* frontGeode = getFrontGeode();
if (frontGeode)
{
if ( _texCompositor->requiresUnitTextureSpace() )
{
// in "unit-texture-space" mode, we can take the shortcut of just updating
// the geometry VBOs. The texture coordinates never change.
for( unsigned int i=0; i<_backGeode->getNumDrawables(); ++i )
{
osg::Geometry* backGeom = static_cast<osg::Geometry*>( _backGeode->getDrawable(i) );
osg::Vec3Array* backVerts = static_cast<osg::Vec3Array*>( backGeom->getVertexArray() );
osg::Geometry* frontGeom = static_cast<osg::Geometry*>( frontGeode->getDrawable(i) );
osg::Vec3Array* frontVerts = static_cast<osg::Vec3Array*>( frontGeom->getVertexArray() );
if ( backVerts->size() == frontVerts->size() )
{
// simple VBO update:
std::copy( backVerts->begin(), backVerts->end(), frontVerts->begin() );
frontVerts->dirty();
osg::Vec3Array* backNormals = static_cast<osg::Vec3Array*>( backGeom->getNormalArray() );
if ( backNormals )
{
osg::Vec3Array* frontNormals = static_cast<osg::Vec3Array*>( frontGeom->getNormalArray() );
std::copy( backNormals->begin(), backNormals->end(), frontNormals->begin() );
frontNormals->dirty();
}
osg::Vec2Array* backTexCoords = static_cast<osg::Vec2Array*>( backGeom->getTexCoordArray(0) );
if ( backTexCoords )
{
osg::Vec2Array* frontTexCoords = static_cast<osg::Vec2Array*>( frontGeom->getTexCoordArray(0) );
std::copy( backTexCoords->begin(), backTexCoords->end(), frontTexCoords->begin() );
frontTexCoords->dirty();
}
}
else
{
frontGeom->setVertexArray( backVerts );
frontGeom->setTexCoordArray( 0, backGeom->getTexCoordArray( 0 ) ); // TODO: un-hard-code
if ( backGeom->getNormalArray() )
frontGeom->setNormalArray( backGeom->getNormalArray() );
}
}
}
else
{
// copy the drawables from the back buffer to the front buffer. By doing this,
// we don't touch the front geode's stateset (which contains the textures) and
// therefore they don't get re-applied.
for( unsigned int i=0; i<_backGeode->getNumDrawables(); ++i )
{
frontGeode->setDrawable( i, _backGeode->getDrawable( i ) );
}
}
}
_pendingGeometryUpdate = false;
_backGeode = 0L;
applied = true;
}
// process any pending LIVE per-layer updates:
osg::StateSet* parentStateSet = 0;
if ( !_pendingImageLayerUpdates.empty() )
{
parentStateSet = getParentStateSet();
}
while( _pendingImageLayerUpdates.size() > 0 )
{
const ImageLayerUpdate& update = _pendingImageLayerUpdates.front();
osg::ref_ptr< osg::Geode > frontGeode = getFrontGeode();
if (frontGeode.valid())
{
_texCompositor->applyLayerUpdate(
frontGeode->getStateSet(),
update._layerUID,
update._image,
_tileKey,
update._isRealData ? parentStateSet : 0L );
}
_pendingImageLayerUpdates.pop();
applied = true;
}
}
if ( _debug )
{
OE_NOTICE << "applyTileUpdates()" << std::endl;
}
return applied;
}
Here is the call graph for this function:
Here is the caller graph for this function:| void SinglePassTerrainTechnique::calculateSampling | ( | unsigned int & | out_rows, |
| unsigned int & | out_cols, | ||
| double & | out_i, | ||
| double & | out_j | ||
| ) | [protected] |
Definition at line 492 of file SinglePassTerrainTechnique.cpp.
{
osgTerrain::Layer* elevationLayer = _tile->getElevationLayer();
out_rows = elevationLayer->getNumRows();
out_cols = elevationLayer->getNumColumns();
out_i = 1.0;
out_j = 1.0;
float sampleRatio = _tile->getTerrain() ? _tile->getTerrain()->getSampleRatio() : 1.0f;
if ( sampleRatio != 1.0f )
{
unsigned int originalNumColumns = out_cols;
unsigned int originalNumRows = out_rows;
out_cols = osg::maximum((unsigned int) (float(originalNumColumns)*sqrtf(sampleRatio)), 4u);
out_rows = osg::maximum((unsigned int) (float(originalNumRows)*sqrtf(sampleRatio)),4u);
out_i = double(originalNumColumns-1)/double(out_cols-1);
out_j = double(originalNumRows-1)/double(out_rows-1);
}
}
Here is the call graph for this function:
Here is the caller graph for this function:| void SinglePassTerrainTechnique::compile | ( | const TileUpdate & | updateSpec, |
| ProgressCallback * | progress | ||
| ) | [virtual] |
Implements CustomTerrainTechnique.
Definition at line 134 of file SinglePassTerrainTechnique.cpp.
{
// safety check
if ( !_tile )
{
OE_WARN << LC << "Illegal; terrain tile is null" << std::endl;
return;
}
//if ( _debug )
//{
// OE_NOTICE << LC << "compile() " << std::endl;
//}
// serialize access to the compilation procedure.
OpenThreads::ScopedLock<Mutex> exclusiveLock( _compileMutex );
// make a frame to use during compilation.
TileFrame tilef( _tile );
// establish the master tile locator if this is the first compilation:
if ( !_masterLocator.valid() || !_transform.valid() )
{
_masterLocator = static_cast<GeoLocator*>( tilef._locator.get() );
_masterLocator->convertLocalToModel( osg::Vec3(.5,.5,0), _centerModel );
_transform = new osg::MatrixTransform( osg::Matrix::translate(_centerModel) );
// this is a placeholder so that we can always just call setChild(0) later.
_transform->addChild( new osg::Group );
}
// see whether a full update is required.
bool partialUpdateOK = _texCompositor->supportsLayerUpdate() && _frontGeodeInstalled;
// handle image layer addition or update:
if (partialUpdateOK &&
( update.getAction() == TileUpdate::ADD_IMAGE_LAYER || update.getAction() == TileUpdate::UPDATE_IMAGE_LAYER ))
{
prepareImageLayerUpdate( update.getLayerUID(), tilef );
// conditionally regenerate the texture coordinates for this layer.
// TODO: optimize this with a method that ONLY regenerates the texture coordinates.
if ( !_texCompositor->requiresUnitTextureSpace() )
{
osg::ref_ptr<osg::StateSet> stateSet = _backGeode.valid() ? _backGeode->getStateSet() : 0L;
_backGeode = createGeometry( tilef );
_backGeode->setStateSet( stateSet.get() );
_pendingGeometryUpdate = true;
}
}
else if (partialUpdateOK && update.getAction() == TileUpdate::MOVE_IMAGE_LAYER )
{
//nop - layer re-ordering happens entirely in the texture compositor.
}
//TODO: we should not need to check supportsLayerUpdate here, but it is not working properly in
// multitexture mode (white tiles show up). Need to investigate and fix.
else if ( partialUpdateOK && update.getAction() == TileUpdate::UPDATE_ELEVATION )
{
osg::ref_ptr<osg::StateSet> stateSet = _backGeode.valid() ? _backGeode->getStateSet() : 0L;
_backGeode = createGeometry( tilef );
_backGeode->setStateSet( stateSet.get() );
_pendingGeometryUpdate = true;
}
else // all other update types
{
// give the engine a chance to bail out before generating geometry
if ( progress && progress->isCanceled() )
{
_backGeode = 0L;
return;
}
// create the geometry and texture coordinates for this tile in a new buffer
_backGeode = createGeometry( tilef );
if ( !_backGeode.valid() )
{
OE_WARN << LC << "createGeometry returned NULL" << std::endl;
return;
}
// give the engine a chance to bail out before building the texture stateset:
if ( progress && progress->isCanceled() )
{
_backGeode = 0L;
return;
}
// create the stateset for this tile, which contains all the texture information.
osg::StateSet* stateSet = createStateSet( tilef );
if ( stateSet )
{
_backGeode->setStateSet( stateSet );
}
// give the engine a chance to bail out before swapping buffers
if ( progress && progress->isCanceled() )
{
_backGeode = 0L;
return;
}
_initCount++;
if ( _initCount > 1 )
OE_WARN << LC << "Tile was fully build " << _initCount << " times" << std::endl;
if ( _backGeode.valid() && !_backGeode->getStateSet() )
OE_WARN << LC << "ILLEGAL! no stateset in BackGeode!!" << std::endl;
_pendingFullUpdate = true;
}
}
Here is the call graph for this function:
Here is the caller graph for this function:| osg::Vec3d SinglePassTerrainTechnique::computeCenterModel | ( | ) | [private] |
| bool SinglePassTerrainTechnique::createGeoImage | ( | const CustomColorLayer & | layer, |
| GeoImage & | image | ||
| ) | const [private] |
Definition at line 405 of file SinglePassTerrainTechnique.cpp.
{
osg::ref_ptr<const GeoLocator> layerLocator = dynamic_cast<const GeoLocator*>( colorLayer.getLocator() );
if ( layerLocator.valid() )
{
if ( layerLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC )
layerLocator = layerLocator->getGeographicFromGeocentric();
const GeoExtent& imageExtent = layerLocator->getDataExtent();
image = GeoImage( colorLayer.getImage(), imageExtent ); //const_cast<osg::Image*>(colorLayer.getImage()), imageExtent );
return true;
}
return false;
}
Here is the call graph for this function:
Here is the caller graph for this function:| osg::Geode * SinglePassTerrainTechnique::createGeometry | ( | const TileFrame & | tilef | ) | [private] |
Definition at line 552 of file SinglePassTerrainTechnique.cpp.
{
osg::ref_ptr<GeoLocator> masterTextureLocator = _masterLocator.get();
//GeoLocator* geoMasterLocator = dynamic_cast<GeoLocator*>(_masterLocator.get());
bool isCube = dynamic_cast<CubeFaceLocator*>(_masterLocator.get()) != NULL;
// If we have a geocentric locator, get a geographic version of it to avoid converting
// to/from geocentric when computing texture coordinats
if (!isCube && /*geoMasterLocator && */ _masterLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC)
{
masterTextureLocator = masterTextureLocator->getGeographicFromGeocentric();
}
osgTerrain::Layer* elevationLayer = _tile->getElevationLayer();
// fire up a brand new geode.
osg::Geode* geode = new osg::Geode();
geode->setThreadSafeRefUnref(true);
// setting the geometry to DYNAMIC means its draw will not overlap the next frame's update/cull
// traversal - which could access the buffer without a mutex
osg::Geometry* surface = new osg::Geometry();
surface->setThreadSafeRefUnref(true); // TODO: probably unnecessary.
surface->setDataVariance( osg::Object::DYNAMIC );
surface->setUseDisplayList(false);
surface->setUseVertexBufferObjects(true);
geode->addDrawable( surface );
osg::Geometry* skirt = new osg::Geometry();
skirt->setThreadSafeRefUnref(true); // TODO: probably unnecessary.
skirt->setDataVariance( osg::Object::DYNAMIC );
skirt->setUseDisplayList(false);
skirt->setUseVertexBufferObjects(true);
geode->addDrawable( skirt );
osg::ref_ptr<GeoLocator> geoLocator = _masterLocator;
// Avoid coordinates conversion when GEOCENTRIC, so get a GEOGRAPHIC version of Locator
if (_masterLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC) {
geoLocator = _masterLocator->getGeographicFromGeocentric();
}
float scaleHeight =
_verticalScaleOverride != 1.0? _verticalScaleOverride :
_tile->getTerrain() ? _tile->getTerrain()->getVerticalScale() :
1.0f;
MaskRecordVector masks;
for (MaskLayerVector::const_iterator it = tilef._masks.begin(); it != tilef._masks.end(); ++it)
{
// When displaying Plate Carre, Heights have to be converted from meters to degrees.
// This is also true for mask feature
// TODO: adjust this calculation based on the actual EllipsoidModel.
float scale = scaleHeight;
if (_masterLocator->getCoordinateSystemType() == osgEarth::GeoLocator::GEOGRAPHIC)
scale = scaleHeight / 111319.0f;
// TODO: Get the map SRS if possible instead of masterLocator's one
osg::Vec3dArray* boundary = (*it)->getOrCreateBoundary(scale, _masterLocator->getDataExtent().getSRS());
if ( boundary )
{
osg::Vec3d min, max;
min = max = boundary->front();
for (osg::Vec3dArray::iterator it = boundary->begin(); it != boundary->end(); ++it)
{
if (it->x() < min.x())
min.x() = it->x();
if (it->y() < min.y())
min.y() = it->y();
if (it->x() > max.x())
max.x() = it->x();
if (it->y() > max.y())
max.y() = it->y();
}
osg::Vec3d min_ndc, max_ndc;
geoLocator->convertModelToLocal(min, min_ndc);
geoLocator->convertModelToLocal(max, max_ndc);
bool x_match = ((min_ndc.x() >= 0.0 && max_ndc.x() <= 1.0) ||
(min_ndc.x() <= 0.0 && max_ndc.x() > 0.0) ||
(min_ndc.x() < 1.0 && max_ndc.x() >= 1.0));
bool y_match = ((min_ndc.y() >= 0.0 && max_ndc.y() <= 1.0) ||
(min_ndc.y() <= 0.0 && max_ndc.y() > 0.0) ||
(min_ndc.y() < 1.0 && max_ndc.y() >= 1.0));
if (x_match && y_match)
{
osg::Geometry* mask_geom = new osg::Geometry();
mask_geom->setThreadSafeRefUnref(true);
mask_geom->setDataVariance( osg::Object::DYNAMIC );
mask_geom->setUseDisplayList(false);
mask_geom->setUseVertexBufferObjects(true);
//mask_geom->getOrCreateStateSet()->setAttribute(new osg::Point( 5.0f ), osg::StateAttribute::ON);
geode->addDrawable(mask_geom);
masks.push_back(MaskRecord(boundary, min_ndc, max_ndc, mask_geom));
}
}
}
osg::Geometry* stitching_skirts = 0L;
osg::Vec3Array* ss_verts = 0L;
if (masks.size() > 0)
{
stitching_skirts = new osg::Geometry();
stitching_skirts->setThreadSafeRefUnref(true);
stitching_skirts->setDataVariance( osg::Object::DYNAMIC );
stitching_skirts->setUseDisplayList(false);
stitching_skirts->setUseVertexBufferObjects(true);
//stitching_skirts->getOrCreateStateSet()->setAttribute(new osg::Point( 5.0f ), osg::StateAttribute::ON);
geode->addDrawable( stitching_skirts);
ss_verts = new osg::Vec3Array();
stitching_skirts->setVertexArray(ss_verts);
}
unsigned int numRows = 20;
unsigned int numColumns = 20;
if (elevationLayer)
{
numColumns = elevationLayer->getNumColumns();
numRows = elevationLayer->getNumRows();
}
double i_sampleFactor, j_sampleFactor;
calculateSampling( numColumns, numRows, i_sampleFactor, j_sampleFactor );
float skirtHeight = 0.0f;
osgTerrain::HeightFieldLayer* hfl = dynamic_cast<osgTerrain::HeightFieldLayer*>(elevationLayer);
if (hfl && hfl->getHeightField())
{
skirtHeight = hfl->getHeightField()->getSkirtHeight();
}
bool createSkirt = skirtHeight != 0.0f;
unsigned int numVerticesInSurface = numColumns*numRows;
unsigned int numVerticesInSkirt = createSkirt ? (2 * (numColumns*2 + numRows*2 - 4)) : 0;
//unsigned int numVertices = numVerticesInBody+numVerticesInSkirt;
// allocate and assign vertices
osg::ref_ptr<osg::Vec3Array> surfaceVerts = new osg::Vec3Array;
surfaceVerts->reserve( numVerticesInSurface );
surface->setVertexArray( surfaceVerts.get() );
// allocate and assign normals
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array();
normals->reserve(numVerticesInSurface);
surface->setNormalArray(normals.get());
surface->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
// skirt texture coordinates, if applicable:
osg::Vec2Array* unifiedSkirtTexCoords = 0L;
// stitching skirt texture coordinates, if applicable:
osg::Vec2Array* unifiedStitchSkirtTexCoords = 0L;
// allocate and assign texture coordinates
osg::Vec2Array* unifiedSurfaceTexCoords = 0L;
//int numColorLayers = _tile->getNumColorLayers();
RenderLayerVector renderLayers;
if ( _texCompositor->requiresUnitTextureSpace() )
{
// for a unified unit texture space, just make a single texture coordinate array.
unifiedSurfaceTexCoords = new osg::Vec2Array();
unifiedSurfaceTexCoords->reserve( numVerticesInSurface );
surface->setTexCoordArray( 0, unifiedSurfaceTexCoords );
if (createSkirt)
{
unifiedSkirtTexCoords = new osg::Vec2Array();
unifiedSkirtTexCoords->reserve( numVerticesInSkirt );
skirt->setTexCoordArray( 0, unifiedSkirtTexCoords );
}
if (masks.size() > 0)
{
unifiedStitchSkirtTexCoords = new osg::Vec2Array();
//unifiedStitchSkirtTexCoords->reserve( ? );
stitching_skirts->setTexCoordArray( 0, unifiedStitchSkirtTexCoords );
}
}
else // if ( !_texCompositor->requiresUnitTextureSpace() )
{
LocatorToTexCoordTable locatorToTexCoordTable;
renderLayers.reserve( tilef._colorLayers.size() );
// build a list of "render layers", in slot order, sharing texture coordinate
// arrays wherever possible.
for( ColorLayersByUID::const_iterator i = tilef._colorLayers.begin(); i != tilef._colorLayers.end(); ++i )
{
const CustomColorLayer& colorLayer = i->second;
RenderLayer r;
r._layer = colorLayer;
const GeoLocator* locator = dynamic_cast<const GeoLocator*>( r._layer.getLocator() );
if ( locator )
{
r._texCoords = locatorToTexCoordTable.find( locator );
if ( !r._texCoords.valid() )
{
r._texCoords = new osg::Vec2Array();
r._texCoords->reserve( numVerticesInSurface );
r._ownsTexCoords = true;
locatorToTexCoordTable.push_back( LocatorTexCoordPair(locator, r._texCoords.get()) );
}
r._skirtTexCoords = new osg::Vec2Array();
r._skirtTexCoords->reserve( numVerticesInSkirt );
if ( masks.size() > 0 )
{
r._stitchTexCoords = new osg::Vec2Array();
r._stitchSkirtTexCoords = new osg::Vec2Array();
}
r._locator = locator;
if ( locator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC )
{
const GeoLocator* geo = dynamic_cast<const GeoLocator*>(locator);
if ( geo )
r._locator = geo->getGeographicFromGeocentric();
}
_texCompositor->assignTexCoordArray( surface, colorLayer.getUID(), r._texCoords.get() );
_texCompositor->assignTexCoordArray( skirt, colorLayer.getUID(), r._skirtTexCoords.get() );
for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr)
_texCompositor->assignTexCoordArray( (*mr)._geom, colorLayer.getUID(), r._stitchTexCoords.get() );
if (stitching_skirts)
_texCompositor->assignTexCoordArray( stitching_skirts, colorLayer.getUID(), r._stitchSkirtTexCoords.get() );
//surface->setTexCoordArray( renderLayers.size(), r._texCoords );
renderLayers.push_back( r );
}
else
{
OE_WARN << LC << "Found a Locator, but it wasn't a GeoLocator." << std::endl;
}
}
}
osg::ref_ptr<osg::FloatArray> elevations = new osg::FloatArray;
if (elevations.valid()) elevations->reserve(numVerticesInSurface);
// allocate and assign color
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
(*colors)[0].set(1.0f,1.0f,1.0f,1.0f);
surface->setColorArray(colors.get());
surface->setColorBinding(osg::Geometry::BIND_OVERALL);
typedef std::vector<int> Indices;
Indices indices(numVerticesInSurface, -1);
// populate vertex and tex coord arrays
unsigned int i, j; //, k=0;
for(j=0; j<numRows; ++j)
{
for(i=0; i<numColumns; ++i) // ++k)
{
unsigned int iv = j*numColumns + i;
osg::Vec3d ndc( ((double)i)/(double)(numColumns-1), ((double)j)/(double)(numRows-1), 0.0);
bool validValue = true;
unsigned int i_equiv = i_sampleFactor==1.0 ? i : (unsigned int) (double(i)*i_sampleFactor);
unsigned int j_equiv = j_sampleFactor==1.0 ? j : (unsigned int) (double(j)*j_sampleFactor);
if (elevationLayer)
{
float value = 0.0f;
validValue = elevationLayer->getValidValue(i_equiv,j_equiv, value);
ndc.z() = value*scaleHeight;
}
//Invalidate if point falls within mask bounding box
if (validValue && masks.size() > 0)
{
for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr)
{
if(ndc.x() >= (*mr)._ndcMin.x() && ndc.x() <= (*mr)._ndcMax.x() &&
ndc.y() >= (*mr)._ndcMin.y() && ndc.y() <= (*mr)._ndcMax.y())
{
validValue = false;
indices[iv] = -2;
break;
}
}
}
if (validValue)
{
//indices[iv] = k;
indices[iv] = surfaceVerts->size();
osg::Vec3d model;
_masterLocator->convertLocalToModel(ndc, model);
//(*surfaceVerts)[k] = model - centerModel;
(*surfaceVerts).push_back(model - _centerModel);
if ( _texCompositor->requiresUnitTextureSpace() )
{
// the unified unit texture space requires a single, untransformed unit coord [0..1]
(*unifiedSurfaceTexCoords).push_back( osg::Vec2( ndc.x(), ndc.y() ) );
}
else
{
// the separate texture space requires separate transformed texcoords for each layer.
for( RenderLayerVector::const_iterator r = renderLayers.begin(); r != renderLayers.end(); ++r )
{
if ( r->_ownsTexCoords )
{
//if ( r->_locator.get() != _masterLocator.get() )
//if ( r->_locator->isEr->_locator != masterTextureLocator _masterLocator.get() )
if ( !r->_locator->isEquivalentTo( *masterTextureLocator.get() ) )
{
osg::Vec3d color_ndc;
osgTerrain::Locator::convertLocalCoordBetween( *masterTextureLocator.get(), ndc, *r->_locator.get(), color_ndc );
r->_texCoords->push_back( osg::Vec2( color_ndc.x(), color_ndc.y() ) );
}
else
{
r->_texCoords->push_back( osg::Vec2( ndc.x(), ndc.y() ) );
}
}
}
}
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();
//(*normals)[k] = model_one;
(*normals).push_back(model_one);
}
}
}
for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr)
{
int min_i = (int)floor((*mr)._ndcMin.x() * (double)(numColumns-1));
if (min_i < 0) min_i = 0;
if (min_i >= numColumns) min_i = numColumns - 1;
int min_j = (int)floor((*mr)._ndcMin.y() * (double)(numRows-1));
if (min_j < 0) min_j = 0;
if (min_j >= numColumns) min_j = numColumns - 1;
int max_i = (int)ceil((*mr)._ndcMax.x() * (double)(numColumns-1));
if (max_i < 0) max_i = 0;
if (max_i >= numColumns) max_i = numColumns - 1;
int max_j = (int)ceil((*mr)._ndcMax.y() * (double)(numRows-1));
if (max_j < 0) max_j = 0;
if (max_j >= numColumns) max_j = numColumns - 1;
if (min_i >= 0 && max_i >= 0 && min_j >= 0 && max_j >= 0)
{
int num_i = max_i - min_i + 1;
int num_j = max_j - min_j + 1;
osg::ref_ptr<osgEarth::Symbology::Polygon> maskSkirtPoly = new osgEarth::Symbology::Polygon();
maskSkirtPoly->resize(num_i * 2 + num_j * 2 - 4);
for (int i = 0; i < num_i; i++)
{
//int index = indices[min_j*numColumns + i + min_i];
{
osg::Vec3d ndc( ((double)(i + min_i))/(double)(numColumns-1), ((double)min_j)/(double)(numRows-1), 0.0);
if (elevationLayer)
{
unsigned int i_equiv = i_sampleFactor==1.0 ? i + min_i : (unsigned int) (double(i + min_i)*i_sampleFactor);
unsigned int j_equiv = j_sampleFactor==1.0 ? min_j : (unsigned int) (double(min_j)*j_sampleFactor);
float value = 0.0f;
if (elevationLayer->getValidValue(i_equiv,j_equiv, value))
ndc.z() = value*scaleHeight;
}
(*maskSkirtPoly)[i] = ndc;
}
//index = indices[max_j*numColumns + i + min_i];
{
osg::Vec3d ndc( ((double)(i + min_i))/(double)(numColumns-1), ((double)max_j)/(double)(numRows-1), 0.0);
if (elevationLayer)
{
unsigned int i_equiv = i_sampleFactor==1.0 ? i + min_i : (unsigned int) (double(i + min_i)*i_sampleFactor);
unsigned int j_equiv = j_sampleFactor==1.0 ? max_j : (unsigned int) (double(max_j)*j_sampleFactor);
float value = 0.0f;
if (elevationLayer->getValidValue(i_equiv,j_equiv, value))
ndc.z() = value*scaleHeight;
}
(*maskSkirtPoly)[i + (2 * num_i + num_j - 3) - 2 * i] = ndc;
}
}
for (int j = 0; j < num_j - 2; j++)
{
//int index = indices[(min_j + j + 1)*numColumns + max_i];
{
osg::Vec3d ndc( ((double)max_i)/(double)(numColumns-1), ((double)(min_j + j + 1))/(double)(numRows-1), 0.0);
if (elevationLayer)
{
unsigned int i_equiv = i_sampleFactor==1.0 ? max_i : (unsigned int) (double(max_i)*i_sampleFactor);
unsigned int j_equiv = j_sampleFactor==1.0 ? min_j + j + 1 : (unsigned int) (double(min_j + j + 1)*j_sampleFactor);
float value = 0.0f;
if (elevationLayer->getValidValue(i_equiv,j_equiv, value))
ndc.z() = value*scaleHeight;
}
(*maskSkirtPoly)[j + num_i] = ndc;
}
//index = indices[(min_j + j + 1)*numColumns + min_i];
{
osg::Vec3d ndc( ((double)min_i)/(double)(numColumns-1), ((double)(min_j + j + 1))/(double)(numRows-1), 0.0);
if (elevationLayer)
{
unsigned int i_equiv = i_sampleFactor==1.0 ? min_i : (unsigned int) (double(min_i)*i_sampleFactor);
unsigned int j_equiv = j_sampleFactor==1.0 ? min_j + j + 1 : (unsigned int) (double(min_j + j + 1)*j_sampleFactor);
float value = 0.0f;
if (elevationLayer->getValidValue(i_equiv,j_equiv, value))
ndc.z() = value*scaleHeight;
}
(*maskSkirtPoly)[j + (2 * num_i + 2 * num_j - 5) - 2 * j] = ndc;
}
}
//Create local polygon representing mask
osg::ref_ptr<osgEarth::Symbology::Polygon> maskPoly = new osgEarth::Symbology::Polygon();
for (osg::Vec3dArray::iterator it = (*mr)._boundary->begin(); it != (*mr)._boundary->end(); ++it)
{
osg::Vec3d local;
geoLocator->convertModelToLocal(*it, local);
maskPoly->push_back(local);
}
//Change the following two #if statements to see mask skirt polygons
//before clipping and adjusting
#if 1
//Do a diff on the polygons to get the actual mask skirt
osg::ref_ptr<osgEarth::Symbology::Geometry> outPoly;
maskSkirtPoly->difference(maskPoly.get(), outPoly);
#else
osg::ref_ptr<osgEarth::Symbology::Geometry> outPoly = maskSkirtPoly;
#endif
osg::Vec3Array* outVerts = new osg::Vec3Array();
osg::Geometry* stitch_geom = (*mr)._geom;
stitch_geom->setVertexArray(outVerts);
bool multiParent = false;
if (outPoly.valid())
multiParent = outPoly->getType() == osgEarth::Symbology::Geometry::TYPE_MULTI;
std::vector<int> skirtIndices;
osgEarth::Symbology::GeometryIterator i( outPoly.get(), false );
while( i.hasMore() )
{
osgEarth::Symbology::Geometry* part = i.next();
if (!part)
continue;
if (part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON)
{
osg::Vec3Array* partVerts = part->toVec3Array();
outVerts->insert(outVerts->end(), partVerts->begin(), partVerts->end());
stitch_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, outVerts->size() - partVerts->size(), partVerts->size()));
skirtIndices.push_back(outVerts->size());
if (!multiParent)
{
osg::ref_ptr<osgEarth::Symbology::Polygon> holePoly = static_cast<osgEarth::Symbology::Polygon*>(outPoly.get());
if (holePoly)
{
osgEarth::Symbology::RingCollection holes = holePoly->getHoles();
for (osgEarth::Symbology::RingCollection::iterator hit = holes.begin(); hit != holes.end(); ++hit)
{
(*hit)->rewind(osgEarth::Symbology::Ring::ORIENTATION_CCW);
outVerts->insert(outVerts->end(), (*hit)->begin(), (*hit)->end());
stitch_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, outVerts->size() - (*hit)->size(), (*hit)->size()));
skirtIndices.push_back(outVerts->size());
}
}
}
}
}
if (stitch_geom->getNumPrimitiveSets() > 0)
{
#if 1
// Tessellate mask skirt
osg::ref_ptr<osgUtil::Tessellator> tscx=new osgUtil::Tessellator;
tscx->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
tscx->setBoundaryOnly(false);
tscx->setWindingType(osgUtil::Tessellator::TESS_WINDING_ODD);
tscx->retessellatePolygons(*stitch_geom);
// Assign normals to the stitching polygon: -gw
osg::Vec3Array* sgVerts = dynamic_cast<osg::Vec3Array*>(stitch_geom->getVertexArray());
osg::Vec3Array* sgNormals = new osg::Vec3Array(sgVerts->size());
stitch_geom->setNormalArray( sgNormals );
stitch_geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX );
// calculate the normal and convert to model space.
for( unsigned v=0; v<sgVerts->size(); ++v )
{
const osg::Vec3& vert = (*sgVerts)[v];
osg::Vec3d local_one(vert);
osg::Vec3d model;
_masterLocator->convertLocalToModel( local_one, model );
local_one.z() += 1.0;
osg::Vec3d model_one;
_masterLocator->convertLocalToModel( local_one, model_one );
model_one = model_one - model;
model_one.normalize();
(*sgNormals)[v] = model_one;
}
//Initialize tex coords
osg::Vec2Array* unifiedStitchTexCoords = 0L;
if (_texCompositor->requiresUnitTextureSpace())
{
unifiedStitchTexCoords = new osg::Vec2Array();
unifiedStitchTexCoords->reserve(outVerts->size());
stitch_geom->setTexCoordArray(0, unifiedStitchTexCoords);
}
else if ( renderLayers.size() > 0 )
{
for (unsigned int i = 0; i < renderLayers.size(); ++i)
{
renderLayers[i]._stitchTexCoords->reserve(outVerts->size());
}
}
//Retrieve z values for mask skirt verts
std::vector<int> isZSet;
for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it)
{
int zSet = 0;
//Look for verts that belong to the original mask skirt polygon
for (osgEarth::Symbology::Polygon::iterator mit = maskSkirtPoly->begin(); mit != maskSkirtPoly->end(); ++mit)
{
if (osg::absolute((*mit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*mit).y() - (*it).y()) < MATCH_TOLERANCE)
{
(*it).z() = (*mit).z();
zSet += 1;
break;
}
}
//Look for verts that belong to the mask polygon
for (osgEarth::Symbology::Polygon::iterator mit = maskPoly->begin(); mit != maskPoly->end(); ++mit)
{
if (osg::absolute((*mit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*mit).y() - (*it).y()) < MATCH_TOLERANCE)
{
(*it).z() = (*mit).z();
zSet += 2;
break;
}
}
isZSet.push_back(zSet);
}
//Any mask skirt verts that are still unset are newly created verts where the skirt
//meets the mask. Find the mask segment the point lies along and calculate the
//appropriate z value for the point.
//
//Now that all the z values are set, convert each vert into model coords.
//
//Also, while we are iterating through the verts, set up tex coords.
int count = 0;
for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it)
{
//If the z-value was set from a mask vertex there is no need to change it. If
//it was set from a vertex from the stitching polygon it may need overriden if
//the vertex lies along a mask edge. Or if it is unset, it will need to be set.
if (isZSet[count] < 2)
{
osg::Vec3d p2 = *it;
double closestZ = 0.0;
double closestRatio = DBL_MAX;
for (osgEarth::Symbology::Polygon::iterator mit = maskPoly->begin(); mit != maskPoly->end(); ++mit)
{
osg::Vec3d p1 = *mit;
osg::Vec3d p3 = mit == --maskPoly->end() ? maskPoly->front() : (*(mit + 1));
//Truncated vales to compensate for accuracy issues
double p1x = ((int)(p1.x() * 1000000)) / 1000000.0L;
double p3x = ((int)(p3.x() * 1000000)) / 1000000.0L;
double p2x = ((int)(p2.x() * 1000000)) / 1000000.0L;
double p1y = ((int)(p1.y() * 1000000)) / 1000000.0L;
double p3y = ((int)(p3.y() * 1000000)) / 1000000.0L;
double p2y = ((int)(p2.y() * 1000000)) / 1000000.0L;
if ((p1x < p3x ? p2x >= p1x && p2x <= p3x : p2x >= p3x && p2x <= p1x) &&
(p1y < p3y ? p2y >= p1y && p2y <= p3y : p2y >= p3y && p2y <= p1y))
{
double l1 =(osg::Vec2d(p2.x(), p2.y()) - osg::Vec2d(p1.x(), p1.y())).length();
double lt = (osg::Vec2d(p3.x(), p3.y()) - osg::Vec2d(p1.x(), p1.y())).length();
double zmag = p3.z() - p1.z();
double foundZ = (l1 / lt) * zmag + p1.z();
double mRatio = 1.0;
if (osg::absolute(p1x - p3x) < MATCH_TOLERANCE)
{
if (osg::absolute(p1x-p2x) < MATCH_TOLERANCE)
mRatio = 0.0;
}
else
{
double m1 = p1x == p2x ? 0.0 : (p2y - p1y) / (p2x - p1x);
double m2 = p1x == p3x ? 0.0 : (p3y - p1y) / (p3x - p1x);
mRatio = m2 == 0.0 ? m1 : osg::absolute(1.0L - m1 / m2);
}
if (mRatio < 0.01)
{
(*it).z() = foundZ;
isZSet[count] = 2;
break;
}
else if (mRatio < closestRatio)
{
closestRatio = mRatio;
closestZ = foundZ;
}
}
}
if (!isZSet[count] && closestRatio < DBL_MAX)
{
(*it).z() = closestZ;
isZSet[count] = 2;
}
}
if (!isZSet[count])
OE_WARN << LC << "Z-value not set for stitching polygon vertex" << std::endl;
count++;
//Convert to model coords
osg::Vec3d model;
_masterLocator->convertLocalToModel(*it, model);
model = model - _centerModel;
(*it).set(model.x(), model.y(), model.z());
//Setup tex coords
osg::Vec3d ndc;
_masterLocator->convertModelToLocal(*it + _centerModel, ndc);
if (_texCompositor->requiresUnitTextureSpace())
{
unifiedStitchTexCoords->push_back(osg::Vec2(ndc.x(), ndc.y()));
}
else if (renderLayers.size() > 0)
{
for (unsigned int i = 0; i < renderLayers.size(); ++i)
{
if (!renderLayers[i]._locator->isEquivalentTo(*masterTextureLocator.get()))
{
osg::Vec3d color_ndc;
osgTerrain::Locator::convertLocalCoordBetween(*masterTextureLocator.get(), ndc, *renderLayers[i]._locator.get(), color_ndc);
renderLayers[i]._stitchTexCoords->push_back(osg::Vec2(color_ndc.x(), color_ndc.y()));
}
else
{
renderLayers[i]._stitchTexCoords->push_back(osg::Vec2(ndc.x(), ndc.y()));
}
}
}
}
#else
for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it)
{
//Convert to model coords
osg::Vec3d model;
_masterLocator->convertLocalToModel(*it, model);
model = model - _centerModel;
(*it).set(model.x(), model.y(), model.z());
}
#endif
//Create stitching skirts
if (createSkirt && skirtIndices.size() > 0)
{
ss_verts->reserve(ss_verts->size() + outVerts->size() * 4 + skirtIndices.size() * 2);
//Add a primative set for each continuous skirt strip
for (int p=0; p < skirtIndices.size(); p++)
{
int cursor = ss_verts->size();
int outStart = p == 0 ? 0 : skirtIndices[p-1];
for (int i=outStart; i < skirtIndices[p]; i++)
{
ss_verts->push_back((*outVerts)[i]);
ss_verts->push_back((*outVerts)[i] - (*sgNormals)[i] * skirtHeight);
if ( _texCompositor->requiresUnitTextureSpace() )
{
unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] );
unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] );
}
else if ( renderLayers.size() > 0 )
{
for (unsigned int r = 0; r < renderLayers.size(); ++r)
{
const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[i];
renderLayers[r]._stitchSkirtTexCoords->push_back( tc );
renderLayers[r]._stitchSkirtTexCoords->push_back( tc );
}
}
}
//Add the first vert again to complete the loop
ss_verts->push_back((*outVerts)[outStart]);
ss_verts->push_back((*outVerts)[outStart] - (*sgNormals)[outStart] * skirtHeight);
if ( _texCompositor->requiresUnitTextureSpace() )
{
unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[outStart] );
unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[outStart] );
}
else if ( renderLayers.size() > 0 )
{
for (unsigned int r = 0; r < renderLayers.size(); ++r)
{
const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[outStart];
renderLayers[r]._stitchSkirtTexCoords->push_back( tc );
renderLayers[r]._stitchSkirtTexCoords->push_back( tc );
}
}
//Now go back the opposite direction to create a skirt facing the other direction
for (int i=skirtIndices[p] - 1; i >= outStart; i--)
{
ss_verts->push_back((*outVerts)[i]);
ss_verts->push_back((*outVerts)[i] - (*sgNormals)[i] * skirtHeight);
if ( _texCompositor->requiresUnitTextureSpace() )
{
unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] );
unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] );
}
else if ( renderLayers.size() > 0 )
{
for (unsigned int r = 0; r < renderLayers.size(); ++r)
{
const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[i];
renderLayers[r]._stitchSkirtTexCoords->push_back( tc );
renderLayers[r]._stitchSkirtTexCoords->push_back( tc );
}
}
}
stitching_skirts->addPrimitiveSet(new osg::DrawArrays( GL_TRIANGLE_STRIP, cursor, ss_verts->size() - cursor));
}
}
}
}
}
// populate primitive sets
bool swapOrientation = !(_masterLocator->orientationOpenGL());
osg::ref_ptr<osg::DrawElementsUInt> elements = new osg::DrawElementsUInt(GL_TRIANGLES);
elements->reserve((numRows-1) * (numColumns-1) * 6);
surface->addPrimitiveSet(elements.get());
osg::ref_ptr<osg::Vec3Array> skirtVectors = new osg::Vec3Array( *normals );
if (!normals)
createSkirt = false;
// New separated skirts.
if ( createSkirt )
{
// build the verts first:
osg::Vec3Array* skirtVerts = new osg::Vec3Array();
osg::Vec3Array* skirtNormals = new osg::Vec3Array();
skirtVerts->reserve( numVerticesInSkirt );
skirtNormals->reserve( numVerticesInSkirt );
Indices skirtBreaks;
skirtBreaks.push_back(0);
// bottom:
for( unsigned int c=0; c<numColumns-1; ++c )
{
int orig_i = indices[c];
//int offset = 0;
//while (orig_i < 0 && offset < numRows - 1)
// orig_i = indices[c + ++offset * numColumns];
if (orig_i < 0)
{
if (skirtBreaks.back() != skirtVerts->size())
skirtBreaks.push_back(skirtVerts->size());
}
else
{
skirtVerts->push_back( (*surfaceVerts)[orig_i] );
skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight );
skirtNormals->push_back( (*normals)[orig_i] );
skirtNormals->push_back( (*normals)[orig_i] );
if ( _texCompositor->requiresUnitTextureSpace() )
{
unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] );
unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] );
}
else if ( renderLayers.size() > 0 )
{
for (unsigned int i = 0; i < renderLayers.size(); ++i)
{
const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i];
renderLayers[i]._skirtTexCoords->push_back( tc );
renderLayers[i]._skirtTexCoords->push_back( tc );
}
}
}
}
// right:
for( unsigned int r=0; r<numRows-1; ++r )
{
int orig_i = indices[r*numColumns+(numColumns-1)];
if (orig_i < 0)
{
if (skirtBreaks.back() != skirtVerts->size())
skirtBreaks.push_back(skirtVerts->size());
}
else
{
skirtVerts->push_back( (*surfaceVerts)[orig_i] );
skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight );
skirtNormals->push_back( (*normals)[orig_i] );
skirtNormals->push_back( (*normals)[orig_i] );
if ( _texCompositor->requiresUnitTextureSpace() )
{
unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] );
unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] );
}
else if ( renderLayers.size() > 0 )
{
for (unsigned int i = 0; i < renderLayers.size(); ++i)
{
const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i];
renderLayers[i]._skirtTexCoords->push_back( tc );
renderLayers[i]._skirtTexCoords->push_back( tc );
}
}
}
}
// top:
for( int c=numColumns-1; c>0; --c )
{
int orig_i = indices[(numRows-1)*numColumns+c];
if (orig_i < 0)
{
if (skirtBreaks.back() != skirtVerts->size())
skirtBreaks.push_back(skirtVerts->size());
}
else
{
skirtVerts->push_back( (*surfaceVerts)[orig_i] );
skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight );
skirtNormals->push_back( (*normals)[orig_i] );
skirtNormals->push_back( (*normals)[orig_i] );
if ( _texCompositor->requiresUnitTextureSpace() )
{
unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] );
unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] );
}
else if ( renderLayers.size() > 0 )
{
for (unsigned int i = 0; i < renderLayers.size(); ++i)
{
const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i];
renderLayers[i]._skirtTexCoords->push_back( tc );
renderLayers[i]._skirtTexCoords->push_back( tc );
}
}
}
}
// left:
for( int r=numRows-1; r>=0; --r )
{
int orig_i = indices[r*numColumns];
if (orig_i < 0)
{
if (skirtBreaks.back() != skirtVerts->size())
skirtBreaks.push_back(skirtVerts->size());
}
else
{
skirtVerts->push_back( (*surfaceVerts)[orig_i] );
skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight );
skirtNormals->push_back( (*normals)[orig_i] );
skirtNormals->push_back( (*normals)[orig_i] );
if ( _texCompositor->requiresUnitTextureSpace() )
{
unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] );
unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] );
}
else if ( renderLayers.size() > 0 )
{
for (unsigned int i = 0; i < renderLayers.size(); ++i)
{
const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i];
renderLayers[i]._skirtTexCoords->push_back( tc );
renderLayers[i]._skirtTexCoords->push_back( tc );
}
}
}
}
skirt->setVertexArray( skirtVerts );
skirt->setNormalArray( skirtNormals );
skirt->setNormalBinding( osg::Geometry::BIND_PER_VERTEX );
//Add a primative set for each continuous skirt strip
skirtBreaks.push_back(skirtVerts->size());
for (int p=1; p < skirtBreaks.size(); p++)
skirt->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_STRIP, skirtBreaks[p-1], skirtBreaks[p] - skirtBreaks[p-1] ) );
}
bool recalcNormals = elevationLayer != NULL;
//Clear out the normals
if (recalcNormals)
{
osg::Vec3Array::iterator nitr;
for(nitr = normals->begin();
nitr!=normals->end();
++nitr)
{
nitr->set(0.0f,0.0f,0.0f);
}
}
for(j=0; j<numRows-1; ++j)
{
for(i=0; i<numColumns-1; ++i)
{
int i00;
int i01;
if (swapOrientation)
{
i01 = j*numColumns + i;
i00 = i01+numColumns;
}
else
{
i00 = j*numColumns + i;
i01 = i00+numColumns;
}
int i10 = i00+1;
int i11 = i01+1;
// remap indices to final vertex positions
i00 = indices[i00];
i01 = indices[i01];
i10 = indices[i10];
i11 = indices[i11];
unsigned int numValid = 0;
if (i00>=0) ++numValid;
if (i01>=0) ++numValid;
if (i10>=0) ++numValid;
if (i11>=0) ++numValid;
if (numValid==4)
{
bool VALID = true;
for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) {
float min_i = (*mr)._ndcMin.x() * (double)(numColumns-1);
float min_j = (*mr)._ndcMin.y() * (double)(numRows-1);
float max_i = (*mr)._ndcMax.x() * (double)(numColumns-1);
float max_j = (*mr)._ndcMax.y() * (double)(numRows-1);
// We test if mask is completely in square
if(i+1 >= min_i && i <= max_i && j+1 >= min_j && j <= max_j) {
VALID = false;
break;
}
}
if (VALID) {
float e00 = (*elevations)[i00];
float e10 = (*elevations)[i10];
float e01 = (*elevations)[i01];
float e11 = (*elevations)[i11];
osg::Vec3f &v00 = (*surfaceVerts)[i00];
osg::Vec3f &v10 = (*surfaceVerts)[i10];
osg::Vec3f &v01 = (*surfaceVerts)[i01];
osg::Vec3f &v11 = (*surfaceVerts)[i11];
if (!_optimizeTriangleOrientation || (e00-e11)<fabsf(e01-e10))
{
elements->push_back(i01);
elements->push_back(i00);
elements->push_back(i11);
elements->push_back(i00);
elements->push_back(i10);
elements->push_back(i11);
if (recalcNormals)
{
osg::Vec3 normal1 = (v00-v01) ^ (v11-v01);
(*normals)[i01] += normal1;
(*normals)[i00] += normal1;
(*normals)[i11] += normal1;
osg::Vec3 normal2 = (v10-v00)^(v11-v00);
(*normals)[i00] += normal2;
(*normals)[i10] += normal2;
(*normals)[i11] += normal2;
}
}
else
{
elements->push_back(i01);
elements->push_back(i00);
elements->push_back(i10);
elements->push_back(i01);
elements->push_back(i10);
elements->push_back(i11);
if (recalcNormals)
{
osg::Vec3 normal1 = (v00-v01) ^ (v10-v01);
(*normals)[i01] += normal1;
(*normals)[i00] += normal1;
(*normals)[i10] += normal1;
osg::Vec3 normal2 = (v10-v01)^(v11-v01);
(*normals)[i01] += normal2;
(*normals)[i10] += normal2;
(*normals)[i11] += normal2;
}
}
}
}
// As skirtPoly is filling the mask bbox, we don't need to create isolated triangle
/*else if (numValid==3)
{
int validIndices[3];
int indexPtr = 0;
if (i00>=0)
{
elements->push_back(i00);
validIndices[indexPtr++] = i00;
}
if (i01>=0)
{
elements->push_back(i01);
validIndices[indexPtr++] = i01;
}
if (i11>=0)
{
elements->push_back(i11);
validIndices[indexPtr++] = i11;
}
if (i10>=0)
{
elements->push_back(i10);
validIndices[indexPtr++] = i10;
}
if (recalcNormals)
{
osg::Vec3f &v1 = (*surfaceVerts)[validIndices[0]];
osg::Vec3f &v2 = (*surfaceVerts)[validIndices[1]];
osg::Vec3f &v3 = (*surfaceVerts)[validIndices[2]];
osg::Vec3f normal = (v2 - v1) ^ (v3 - v1);
(*normals)[validIndices[0]] += normal;
(*normals)[validIndices[1]] += normal;
(*normals)[validIndices[2]] += normal;
}
} */
}
}
//Normalize the normals
if (recalcNormals)
{
osg::Vec3Array::iterator nitr;
for(nitr = normals->begin();
nitr!=normals->end();
++nitr)
{
nitr->normalize();
}
}
MeshConsolidator::run( *surface );
if ( skirt )
MeshConsolidator::run( *skirt );
for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr)
MeshConsolidator::run( *((*mr)._geom) );
if (osgDB::Registry::instance()->getBuildKdTreesHint()==osgDB::ReaderWriter::Options::BUILD_KDTREES &&
osgDB::Registry::instance()->getKdTreeBuilder())
{
osg::ref_ptr<osg::KdTreeBuilder> builder = osgDB::Registry::instance()->getKdTreeBuilder()->clone();
geode->accept(*builder);
}
return geode;
}
Here is the call graph for this function:
Here is the caller graph for this function:| osg::StateSet * SinglePassTerrainTechnique::createStateSet | ( | const TileFrame & | tilef | ) | [private] |
Definition at line 449 of file SinglePassTerrainTechnique.cpp.
{
// establish the tile extent. we will calculate texture coordinate offset/scale based on this
if ( !_tileExtent.isValid() )
{
osg::ref_ptr<GeoLocator> tileLocator = dynamic_cast<GeoLocator*>( tilef._locator.get() ); // _terrainTile->getLocator() );
if ( tileLocator.valid() )
{
if ( tileLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC )
tileLocator = tileLocator->getGeographicFromGeocentric();
_tileExtent = tileLocator->getDataExtent();
}
_tileKey = tilef._tileKey;
}
osg::StateSet* stateSet = new osg::StateSet();
osg::StateSet* parentStateSet = getParentStateSet();
for( ColorLayersByUID::const_iterator i = tilef._colorLayers.begin(); i != tilef._colorLayers.end(); ++i )
{
const CustomColorLayer& colorLayer = i->second;
bool isRealData = !colorLayer.isFallbackData();
GeoImage image;
if ( createGeoImage( colorLayer, image ) )
{
image = _texCompositor->prepareImage( image, _tileExtent );
_texCompositor->applyLayerUpdate(
stateSet,
colorLayer.getUID(),
image,
_tileKey,
isRealData ? parentStateSet : 0L );
}
}
return stateSet;
}
Here is the call graph for this function:
Here is the caller graph for this function:| osg::StateSet * SinglePassTerrainTechnique::getActiveStateSet | ( | ) | const |
Definition at line 422 of file SinglePassTerrainTechnique.cpp.
{
OpenThreads::ScopedLock<Mutex> exclusiveLock( const_cast<SinglePassTerrainTechnique*>(this)->_compileMutex );
osg::StateSet* result = 0L;
osg::Geode* front = getFrontGeode();
if ( front )
result = front->getStateSet();
if ( !result && _backGeode.valid() )
result = _backGeode->getStateSet();
return result;
}
Here is the call graph for this function:
Here is the caller graph for this function:| osg::Geode* SinglePassTerrainTechnique::getFrontGeode | ( | ) | const [inline, private] |
Definition at line 174 of file SinglePassTerrainTechnique.
{
if (_transform.valid() && _transform->getNumChildren() > 0)
return static_cast<osg::Geode*>( _transform->getChild(0) );
return NULL;
}
Here is the caller graph for this function:| int SinglePassTerrainTechnique::getIndexOfColorLayerWithUID | ( | UID | uid | ) | const [private] |
| bool SinglePassTerrainTechnique::getOptimizeTriangleOrientation | ( | ) | const [virtual] |
Implements CustomTerrainTechnique.
Definition at line 121 of file SinglePassTerrainTechnique.cpp.
{
return _optimizeTriangleOrientation;
}
| osg::StateSet * SinglePassTerrainTechnique::getParentStateSet | ( | ) | const [private] |
Definition at line 437 of file SinglePassTerrainTechnique.cpp.
{
osg::StateSet* parentStateSet = 0;
osg::ref_ptr<Tile> parentTile_safe = _parentTile.get();
if ( parentTile_safe.valid() )
{
return static_cast<SinglePassTerrainTechnique*>(_parentTile->getTerrainTechnique())->getActiveStateSet();
}
else return 0L;
}
Here is the call graph for this function:
Here is the caller graph for this function:| float SinglePassTerrainTechnique::getVerticalScaleOverride | ( | ) | const |
Gets the overriden vertical scale value.
Definition at line 109 of file SinglePassTerrainTechnique.cpp.
{
return _verticalScaleOverride;
}
| void SinglePassTerrainTechnique::init | ( | ) | [virtual] |
Implements TerrainTechnique.
Definition at line 127 of file SinglePassTerrainTechnique.cpp.
{
compile( TileUpdate(TileUpdate::UPDATE_ALL), 0L );
applyTileUpdates();
}
Here is the call graph for this function:| SinglePassTerrainTechnique::META_Object | ( | osgEarth | , |
| SinglePassTerrainTechnique | |||
| ) |
| void SinglePassTerrainTechnique::prepareImageLayerUpdate | ( | int | layerIndex, |
| const TileFrame & | tilef | ||
| ) | [private] |
Definition at line 382 of file SinglePassTerrainTechnique.cpp.
{
CustomColorLayer layer;
if ( tilef.getCustomColorLayer( layerUID, layer ) )
{
GeoImage geoImage, secondaryImage;
if ( createGeoImage( layer, geoImage ) )
{
ImageLayerUpdate update;
update._image = _texCompositor->prepareImage( geoImage, _tileExtent );
update._layerUID = layerUID;
update._isRealData = !layer.isFallbackData();
if ( update._image.valid() )
_pendingImageLayerUpdates.push( update );
}
}
}
Here is the call graph for this function:
Here is the caller graph for this function:| void SinglePassTerrainTechnique::releaseGLObjects | ( | osg::State * | state = 0 | ) | const [virtual] |
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.
Implements TerrainTechnique.
Definition at line 1743 of file SinglePassTerrainTechnique.cpp.
{
SinglePassTerrainTechnique* ncThis = const_cast<SinglePassTerrainTechnique*>(this);
Threading::ScopedWriteLock lock( static_cast<Tile*>(ncThis->_tile)->getTileLayersMutex() );
if ( _transform.valid() )
{
_transform->releaseGLObjects( state );
}
if ( _backGeode.valid() )
{
_backGeode->releaseGLObjects(state);
ncThis->_backGeode = 0L;
}
}
| void SinglePassTerrainTechnique::setOptimizeTriangleOrientation | ( | bool | optimizeTriangleOrientation | ) | [virtual] |
Sets whether to try to optimize the triangle orientation based on the elevation values. If false,
Implements CustomTerrainTechnique.
Definition at line 115 of file SinglePassTerrainTechnique.cpp.
{
_optimizeTriangleOrientation = optimizeTriangleOrientation;
}
| void SinglePassTerrainTechnique::setParentTile | ( | Tile * | tile | ) | [inline, virtual] |
Implements CustomTerrainTechnique.
Definition at line 84 of file SinglePassTerrainTechnique.
{ _parentTile = tile; }
| void SinglePassTerrainTechnique::setVerticalScaleOverride | ( | float | value | ) |
Sets a factor by which to scale elevation height values. By default, this object will get the vertical scale from the osgTerrain::Terrain with which the tile is associated. Setting this value overrides that (or sets it if there is no terrain).
Definition at line 103 of file SinglePassTerrainTechnique.cpp.
{
_verticalScaleOverride = value;
}
| void SinglePassTerrainTechnique::traverse | ( | osg::NodeVisitor & | nv | ) | [virtual] |
Traverse the terrain subgraph.
Implements TerrainTechnique.
Definition at line 1731 of file SinglePassTerrainTechnique.cpp.
{
if ( !_tile )
return;
if ( _transform.valid() )
{
_transform->accept( nv );
}
}
osg::ref_ptr<osg::Geode> SinglePassTerrainTechnique::_backGeode [private] |
Definition at line 135 of file SinglePassTerrainTechnique.
osg::Vec3d SinglePassTerrainTechnique::_centerModel [private] |
Definition at line 137 of file SinglePassTerrainTechnique.
OpenThreads::Mutex SinglePassTerrainTechnique::_compileMutex [private] |
Definition at line 132 of file SinglePassTerrainTechnique.
bool SinglePassTerrainTechnique::_debug [private] |
Definition at line 130 of file SinglePassTerrainTechnique.
bool SinglePassTerrainTechnique::_frontGeodeInstalled [private] |
Definition at line 164 of file SinglePassTerrainTechnique.
osg::ref_ptr<osg::Uniform> SinglePassTerrainTechnique::_imageLayerStampUniform [private] |
Definition at line 136 of file SinglePassTerrainTechnique.
int SinglePassTerrainTechnique::_initCount [private] |
Definition at line 141 of file SinglePassTerrainTechnique.
Definition at line 155 of file SinglePassTerrainTechnique.
osg::ref_ptr<GeoLocator> SinglePassTerrainTechnique::_masterLocator [private] |
Definition at line 139 of file SinglePassTerrainTechnique.
bool SinglePassTerrainTechnique::_optimizeTriangleOrientation [private] |
Definition at line 161 of file SinglePassTerrainTechnique.
osg::observer_ptr<Tile> SinglePassTerrainTechnique::_parentTile [private] |
Definition at line 184 of file SinglePassTerrainTechnique.
bool SinglePassTerrainTechnique::_pendingFullUpdate [private] |
Definition at line 142 of file SinglePassTerrainTechnique.
bool SinglePassTerrainTechnique::_pendingGeometryUpdate [private] |
Definition at line 143 of file SinglePassTerrainTechnique.
Definition at line 151 of file SinglePassTerrainTechnique.
osg::ref_ptr<const TextureCompositor> SinglePassTerrainTechnique::_texCompositor [private] |
Definition at line 163 of file SinglePassTerrainTechnique.
Definition at line 158 of file SinglePassTerrainTechnique.
TileKey SinglePassTerrainTechnique::_tileKey [private] |
Definition at line 159 of file SinglePassTerrainTechnique.
osg::ref_ptr<osg::MatrixTransform> SinglePassTerrainTechnique::_transform [private] |
Definition at line 134 of file SinglePassTerrainTechnique.
float SinglePassTerrainTechnique::_verticalScaleOverride [private] |
Definition at line 138 of file SinglePassTerrainTechnique.
1.7.3