osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include "SinglePassTerrainTechnique" 00020 #include "Terrain" 00021 #include "Tile" 00022 00023 #include <osgEarth/Cube> 00024 #include <osgEarth/ImageUtils> 00025 00026 #include <osg/Point> 00027 #include <osg/Program> 00028 #include <osg/io_utils> 00029 #include <osg/StateSet> 00030 #include <osg/Program> 00031 #include <osg/Math> 00032 #include <osg/Timer> 00033 #include <osg/Version> 00034 #include <osgUtil/Tessellator> 00035 #include <osgUtil/SmoothingVisitor> 00036 00037 #include <osgEarthSymbology/Geometry> 00038 #include <osgEarthSymbology/MeshConsolidator> 00039 00040 #include <sstream> 00041 00042 using namespace osgEarth; 00043 using namespace osgEarth::Symbology; 00044 using namespace OpenThreads; 00045 00046 #define LC "[SinglePassTechnique] " 00047 00048 #define MATCH_TOLERANCE 0.000001 00049 00050 // -------------------------------------------------------------------------- 00051 00052 namespace 00053 { 00054 struct MaskRecord 00055 { 00056 osg::ref_ptr<osg::Vec3dArray> _boundary; 00057 osg::Vec3d _ndcMin, _ndcMax; 00058 osg::Geometry* _geom; 00059 00060 MaskRecord(osg::Vec3dArray* boundary, osg::Vec3d& ndcMin, osg::Vec3d& ndcMax, osg::Geometry* geom) : _boundary(boundary), _ndcMin(ndcMin), _ndcMax(ndcMax), _geom(geom) { } 00061 }; 00062 00063 typedef std::vector<MaskRecord> MaskRecordVector; 00064 } 00065 00066 // -------------------------------------------------------------------------- 00067 00068 SinglePassTerrainTechnique::SinglePassTerrainTechnique( TextureCompositor* compositor ) : 00069 CustomTerrainTechnique(), 00070 _verticalScaleOverride(1.0f), 00071 _initCount(0), 00072 _pendingFullUpdate( false ), 00073 _pendingGeometryUpdate(false), 00074 _optimizeTriangleOrientation(true), 00075 _texCompositor( compositor ), 00076 _frontGeodeInstalled( false ), 00077 _debug( false ) 00078 { 00079 this->setThreadSafeRefUnref(true); 00080 } 00081 00082 SinglePassTerrainTechnique::SinglePassTerrainTechnique(const SinglePassTerrainTechnique& rhs, const osg::CopyOp& copyop): 00083 CustomTerrainTechnique( rhs, copyop ), 00084 _verticalScaleOverride( rhs._verticalScaleOverride ), 00085 _initCount( 0 ), 00086 _pendingFullUpdate( false ), 00087 _pendingGeometryUpdate( false ), 00088 _optimizeTriangleOrientation( rhs._optimizeTriangleOrientation ), 00089 _texCompositor( rhs._texCompositor.get() ), 00090 _frontGeodeInstalled( rhs._frontGeodeInstalled ), 00091 _debug( rhs._debug ), 00092 _parentTile( rhs._parentTile ) 00093 { 00094 //NOP 00095 } 00096 00097 SinglePassTerrainTechnique::~SinglePassTerrainTechnique() 00098 { 00099 //nop 00100 } 00101 00102 void 00103 SinglePassTerrainTechnique::setVerticalScaleOverride( float value ) 00104 { 00105 _verticalScaleOverride = value; 00106 } 00107 00108 float 00109 SinglePassTerrainTechnique::getVerticalScaleOverride() const 00110 { 00111 return _verticalScaleOverride; 00112 } 00113 00114 void 00115 SinglePassTerrainTechnique::setOptimizeTriangleOrientation(bool optimizeTriangleOrientation) 00116 { 00117 _optimizeTriangleOrientation = optimizeTriangleOrientation; 00118 } 00119 00120 bool 00121 SinglePassTerrainTechnique::getOptimizeTriangleOrientation() const 00122 { 00123 return _optimizeTriangleOrientation; 00124 } 00125 00126 void 00127 SinglePassTerrainTechnique::init() 00128 { 00129 compile( TileUpdate(TileUpdate::UPDATE_ALL), 0L ); 00130 applyTileUpdates(); 00131 } 00132 00133 void 00134 SinglePassTerrainTechnique::compile( const TileUpdate& update, ProgressCallback* progress ) 00135 { 00136 // safety check 00137 if ( !_tile ) 00138 { 00139 OE_WARN << LC << "Illegal; terrain tile is null" << std::endl; 00140 return; 00141 } 00142 00143 //if ( _debug ) 00144 //{ 00145 // OE_NOTICE << LC << "compile() " << std::endl; 00146 //} 00147 00148 // serialize access to the compilation procedure. 00149 OpenThreads::ScopedLock<Mutex> exclusiveLock( _compileMutex ); 00150 00151 // make a frame to use during compilation. 00152 TileFrame tilef( _tile ); 00153 00154 // establish the master tile locator if this is the first compilation: 00155 if ( !_masterLocator.valid() || !_transform.valid() ) 00156 { 00157 _masterLocator = static_cast<GeoLocator*>( tilef._locator.get() ); 00158 _masterLocator->convertLocalToModel( osg::Vec3(.5,.5,0), _centerModel ); 00159 00160 _transform = new osg::MatrixTransform( osg::Matrix::translate(_centerModel) ); 00161 // this is a placeholder so that we can always just call setChild(0) later. 00162 _transform->addChild( new osg::Group ); 00163 } 00164 00165 // see whether a full update is required. 00166 bool partialUpdateOK = _texCompositor->supportsLayerUpdate() && _frontGeodeInstalled; 00167 00168 // handle image layer addition or update: 00169 if (partialUpdateOK && 00170 ( update.getAction() == TileUpdate::ADD_IMAGE_LAYER || update.getAction() == TileUpdate::UPDATE_IMAGE_LAYER )) 00171 { 00172 prepareImageLayerUpdate( update.getLayerUID(), tilef ); 00173 00174 // conditionally regenerate the texture coordinates for this layer. 00175 // TODO: optimize this with a method that ONLY regenerates the texture coordinates. 00176 if ( !_texCompositor->requiresUnitTextureSpace() ) 00177 { 00178 osg::ref_ptr<osg::StateSet> stateSet = _backGeode.valid() ? _backGeode->getStateSet() : 0L; 00179 _backGeode = createGeometry( tilef ); 00180 _backGeode->setStateSet( stateSet.get() ); 00181 00182 _pendingGeometryUpdate = true; 00183 } 00184 } 00185 00186 else if (partialUpdateOK && update.getAction() == TileUpdate::MOVE_IMAGE_LAYER ) 00187 { 00188 //nop - layer re-ordering happens entirely in the texture compositor. 00189 } 00190 00191 //TODO: we should not need to check supportsLayerUpdate here, but it is not working properly in 00192 // multitexture mode (white tiles show up). Need to investigate and fix. 00193 else if ( partialUpdateOK && update.getAction() == TileUpdate::UPDATE_ELEVATION ) 00194 { 00195 osg::ref_ptr<osg::StateSet> stateSet = _backGeode.valid() ? _backGeode->getStateSet() : 0L; 00196 _backGeode = createGeometry( tilef ); 00197 _backGeode->setStateSet( stateSet.get() ); 00198 00199 _pendingGeometryUpdate = true; 00200 } 00201 00202 else // all other update types 00203 { 00204 // give the engine a chance to bail out before generating geometry 00205 if ( progress && progress->isCanceled() ) 00206 { 00207 _backGeode = 0L; 00208 return; 00209 } 00210 00211 // create the geometry and texture coordinates for this tile in a new buffer 00212 _backGeode = createGeometry( tilef ); 00213 if ( !_backGeode.valid() ) 00214 { 00215 OE_WARN << LC << "createGeometry returned NULL" << std::endl; 00216 return; 00217 } 00218 00219 // give the engine a chance to bail out before building the texture stateset: 00220 if ( progress && progress->isCanceled() ) 00221 { 00222 _backGeode = 0L; 00223 return; 00224 } 00225 00226 // create the stateset for this tile, which contains all the texture information. 00227 osg::StateSet* stateSet = createStateSet( tilef ); 00228 if ( stateSet ) 00229 { 00230 _backGeode->setStateSet( stateSet ); 00231 } 00232 00233 // give the engine a chance to bail out before swapping buffers 00234 if ( progress && progress->isCanceled() ) 00235 { 00236 _backGeode = 0L; 00237 return; 00238 } 00239 00240 _initCount++; 00241 if ( _initCount > 1 ) 00242 OE_WARN << LC << "Tile was fully build " << _initCount << " times" << std::endl; 00243 00244 if ( _backGeode.valid() && !_backGeode->getStateSet() ) 00245 OE_WARN << LC << "ILLEGAL! no stateset in BackGeode!!" << std::endl; 00246 00247 _pendingFullUpdate = true; 00248 } 00249 } 00250 00251 // from the UPDATE traversal thread: 00252 bool 00253 SinglePassTerrainTechnique::applyTileUpdates() 00254 { 00255 bool applied = false; 00256 00257 // serialize access to the compilation mechanism. 00258 OpenThreads::ScopedLock<Mutex> exclusiveLock( _compileMutex ); 00259 00260 // process a pending buffer swap: 00261 if ( _pendingFullUpdate ) 00262 { 00263 if ( _backGeode->getStateSet() == 0L ) 00264 OE_WARN << LC << "ILLEGAL: backGeode has no stateset" << std::endl; 00265 00266 _transform->setChild( 0, _backGeode.get() ); 00267 _frontGeodeInstalled = true; 00268 _backGeode = 0L; 00269 _pendingFullUpdate = false; 00270 _pendingGeometryUpdate = false; 00271 applied = true; 00272 } 00273 00274 else 00275 { 00276 // process any pending LIVE geometry updates: 00277 if ( _pendingGeometryUpdate ) 00278 { 00279 osg::Geode* frontGeode = getFrontGeode(); 00280 00281 if (frontGeode) 00282 { 00283 00284 if ( _texCompositor->requiresUnitTextureSpace() ) 00285 { 00286 // in "unit-texture-space" mode, we can take the shortcut of just updating 00287 // the geometry VBOs. The texture coordinates never change. 00288 for( unsigned int i=0; i<_backGeode->getNumDrawables(); ++i ) 00289 { 00290 osg::Geometry* backGeom = static_cast<osg::Geometry*>( _backGeode->getDrawable(i) ); 00291 osg::Vec3Array* backVerts = static_cast<osg::Vec3Array*>( backGeom->getVertexArray() ); 00292 00293 osg::Geometry* frontGeom = static_cast<osg::Geometry*>( frontGeode->getDrawable(i) ); 00294 osg::Vec3Array* frontVerts = static_cast<osg::Vec3Array*>( frontGeom->getVertexArray() ); 00295 00296 if ( backVerts->size() == frontVerts->size() ) 00297 { 00298 // simple VBO update: 00299 std::copy( backVerts->begin(), backVerts->end(), frontVerts->begin() ); 00300 frontVerts->dirty(); 00301 00302 osg::Vec3Array* backNormals = static_cast<osg::Vec3Array*>( backGeom->getNormalArray() ); 00303 if ( backNormals ) 00304 { 00305 osg::Vec3Array* frontNormals = static_cast<osg::Vec3Array*>( frontGeom->getNormalArray() ); 00306 std::copy( backNormals->begin(), backNormals->end(), frontNormals->begin() ); 00307 frontNormals->dirty(); 00308 } 00309 00310 osg::Vec2Array* backTexCoords = static_cast<osg::Vec2Array*>( backGeom->getTexCoordArray(0) ); 00311 if ( backTexCoords ) 00312 { 00313 osg::Vec2Array* frontTexCoords = static_cast<osg::Vec2Array*>( frontGeom->getTexCoordArray(0) ); 00314 std::copy( backTexCoords->begin(), backTexCoords->end(), frontTexCoords->begin() ); 00315 frontTexCoords->dirty(); 00316 } 00317 } 00318 else 00319 { 00320 frontGeom->setVertexArray( backVerts ); 00321 frontGeom->setTexCoordArray( 0, backGeom->getTexCoordArray( 0 ) ); // TODO: un-hard-code 00322 if ( backGeom->getNormalArray() ) 00323 frontGeom->setNormalArray( backGeom->getNormalArray() ); 00324 } 00325 } 00326 } 00327 else 00328 { 00329 // copy the drawables from the back buffer to the front buffer. By doing this, 00330 // we don't touch the front geode's stateset (which contains the textures) and 00331 // therefore they don't get re-applied. 00332 for( unsigned int i=0; i<_backGeode->getNumDrawables(); ++i ) 00333 { 00334 frontGeode->setDrawable( i, _backGeode->getDrawable( i ) ); 00335 } 00336 } 00337 } 00338 00339 _pendingGeometryUpdate = false; 00340 _backGeode = 0L; 00341 applied = true; 00342 } 00343 00344 // process any pending LIVE per-layer updates: 00345 osg::StateSet* parentStateSet = 0; 00346 00347 if ( !_pendingImageLayerUpdates.empty() ) 00348 { 00349 parentStateSet = getParentStateSet(); 00350 } 00351 00352 while( _pendingImageLayerUpdates.size() > 0 ) 00353 { 00354 const ImageLayerUpdate& update = _pendingImageLayerUpdates.front(); 00355 00356 osg::ref_ptr< osg::Geode > frontGeode = getFrontGeode(); 00357 if (frontGeode.valid()) 00358 { 00359 _texCompositor->applyLayerUpdate( 00360 frontGeode->getStateSet(), 00361 update._layerUID, 00362 update._image, 00363 _tileKey, 00364 update._isRealData ? parentStateSet : 0L ); 00365 } 00366 00367 _pendingImageLayerUpdates.pop(); 00368 applied = true; 00369 00370 } 00371 } 00372 00373 if ( _debug ) 00374 { 00375 OE_NOTICE << "applyTileUpdates()" << std::endl; 00376 } 00377 00378 return applied; 00379 } 00380 00381 void 00382 SinglePassTerrainTechnique::prepareImageLayerUpdate( UID layerUID, const TileFrame& tilef ) 00383 { 00384 CustomColorLayer layer; 00385 if ( tilef.getCustomColorLayer( layerUID, layer ) ) 00386 { 00387 GeoImage geoImage, secondaryImage; 00388 00389 if ( createGeoImage( layer, geoImage ) ) 00390 { 00391 ImageLayerUpdate update; 00392 00393 update._image = _texCompositor->prepareImage( geoImage, _tileExtent ); 00394 update._layerUID = layerUID; 00395 update._isRealData = !layer.isFallbackData(); 00396 00397 if ( update._image.valid() ) 00398 _pendingImageLayerUpdates.push( update ); 00399 } 00400 00401 } 00402 } 00403 00404 bool 00405 SinglePassTerrainTechnique::createGeoImage( const CustomColorLayer& colorLayer, 00406 GeoImage& image) const 00407 { 00408 osg::ref_ptr<const GeoLocator> layerLocator = dynamic_cast<const GeoLocator*>( colorLayer.getLocator() ); 00409 if ( layerLocator.valid() ) 00410 { 00411 if ( layerLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC ) 00412 layerLocator = layerLocator->getGeographicFromGeocentric(); 00413 00414 const GeoExtent& imageExtent = layerLocator->getDataExtent(); 00415 image = GeoImage( colorLayer.getImage(), imageExtent ); //const_cast<osg::Image*>(colorLayer.getImage()), imageExtent ); 00416 return true; 00417 } 00418 return false; 00419 } 00420 00421 osg::StateSet* 00422 SinglePassTerrainTechnique::getActiveStateSet() const 00423 { 00424 OpenThreads::ScopedLock<Mutex> exclusiveLock( const_cast<SinglePassTerrainTechnique*>(this)->_compileMutex ); 00425 00426 osg::StateSet* result = 0L; 00427 osg::Geode* front = getFrontGeode(); 00428 if ( front ) 00429 result = front->getStateSet(); 00430 if ( !result && _backGeode.valid() ) 00431 result = _backGeode->getStateSet(); 00432 00433 return result; 00434 } 00435 00436 osg::StateSet* 00437 SinglePassTerrainTechnique::getParentStateSet() const 00438 { 00439 osg::StateSet* parentStateSet = 0; 00440 osg::ref_ptr<Tile> parentTile_safe = _parentTile.get(); 00441 if ( parentTile_safe.valid() ) 00442 { 00443 return static_cast<SinglePassTerrainTechnique*>(_parentTile->getTerrainTechnique())->getActiveStateSet(); 00444 } 00445 else return 0L; 00446 } 00447 00448 osg::StateSet* 00449 SinglePassTerrainTechnique::createStateSet( const TileFrame& tilef ) 00450 { 00451 // establish the tile extent. we will calculate texture coordinate offset/scale based on this 00452 if ( !_tileExtent.isValid() ) 00453 { 00454 osg::ref_ptr<GeoLocator> tileLocator = dynamic_cast<GeoLocator*>( tilef._locator.get() ); // _terrainTile->getLocator() ); 00455 if ( tileLocator.valid() ) 00456 { 00457 if ( tileLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC ) 00458 tileLocator = tileLocator->getGeographicFromGeocentric(); 00459 00460 _tileExtent = tileLocator->getDataExtent(); 00461 } 00462 _tileKey = tilef._tileKey; 00463 } 00464 00465 osg::StateSet* stateSet = new osg::StateSet(); 00466 osg::StateSet* parentStateSet = getParentStateSet(); 00467 00468 for( ColorLayersByUID::const_iterator i = tilef._colorLayers.begin(); i != tilef._colorLayers.end(); ++i ) 00469 { 00470 const CustomColorLayer& colorLayer = i->second; 00471 00472 bool isRealData = !colorLayer.isFallbackData(); 00473 00474 GeoImage image; 00475 if ( createGeoImage( colorLayer, image ) ) 00476 { 00477 image = _texCompositor->prepareImage( image, _tileExtent ); 00478 00479 _texCompositor->applyLayerUpdate( 00480 stateSet, 00481 colorLayer.getUID(), 00482 image, 00483 _tileKey, 00484 isRealData ? parentStateSet : 0L ); 00485 } 00486 } 00487 00488 return stateSet; 00489 } 00490 00491 void 00492 SinglePassTerrainTechnique::calculateSampling( unsigned int& out_rows, unsigned int& out_cols, double& out_i, double& out_j ) 00493 { 00494 osgTerrain::Layer* elevationLayer = _tile->getElevationLayer(); 00495 00496 out_rows = elevationLayer->getNumRows(); 00497 out_cols = elevationLayer->getNumColumns(); 00498 out_i = 1.0; 00499 out_j = 1.0; 00500 00501 float sampleRatio = _tile->getTerrain() ? _tile->getTerrain()->getSampleRatio() : 1.0f; 00502 if ( sampleRatio != 1.0f ) 00503 { 00504 unsigned int originalNumColumns = out_cols; 00505 unsigned int originalNumRows = out_rows; 00506 00507 out_cols = osg::maximum((unsigned int) (float(originalNumColumns)*sqrtf(sampleRatio)), 4u); 00508 out_rows = osg::maximum((unsigned int) (float(originalNumRows)*sqrtf(sampleRatio)),4u); 00509 00510 out_i = double(originalNumColumns-1)/double(out_cols-1); 00511 out_j = double(originalNumRows-1)/double(out_rows-1); 00512 } 00513 } 00514 00515 namespace 00516 { 00517 struct GeoLocatorComp 00518 { 00519 bool operator()( const GeoLocator* lhs, const GeoLocator* rhs ) const 00520 { 00521 return rhs && lhs && lhs->isEquivalentTo( *rhs ); 00522 } 00523 }; 00524 00525 typedef std::pair< const GeoLocator*, osg::Vec2Array* > LocatorTexCoordPair; 00526 00527 struct LocatorToTexCoordTable : public std::list<LocatorTexCoordPair> { 00528 osg::Vec2Array* find( const GeoLocator* key ) const { 00529 for( const_iterator i = begin(); i != end(); ++i ) { 00530 if ( i->first->isEquivalentTo( *key ) ) 00531 return i->second; 00532 } 00533 return 0L; 00534 } 00535 }; 00536 00537 struct RenderLayer { 00538 CustomColorLayer _layer; 00539 osg::ref_ptr<const GeoLocator> _locator; 00540 osg::ref_ptr<osg::Vec2Array> _texCoords; 00541 osg::ref_ptr<osg::Vec2Array> _skirtTexCoords; 00542 osg::ref_ptr<osg::Vec2Array> _stitchTexCoords; 00543 osg::ref_ptr<osg::Vec2Array> _stitchSkirtTexCoords; 00544 bool _ownsTexCoords; 00545 RenderLayer() : _ownsTexCoords(false) { } 00546 }; 00547 00548 typedef std::vector< RenderLayer > RenderLayerVector; 00549 } 00550 00551 osg::Geode* 00552 SinglePassTerrainTechnique::createGeometry( const TileFrame& tilef ) 00553 { 00554 osg::ref_ptr<GeoLocator> masterTextureLocator = _masterLocator.get(); 00555 //GeoLocator* geoMasterLocator = dynamic_cast<GeoLocator*>(_masterLocator.get()); 00556 00557 bool isCube = dynamic_cast<CubeFaceLocator*>(_masterLocator.get()) != NULL; 00558 00559 // If we have a geocentric locator, get a geographic version of it to avoid converting 00560 // to/from geocentric when computing texture coordinats 00561 if (!isCube && /*geoMasterLocator && */ _masterLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC) 00562 { 00563 masterTextureLocator = masterTextureLocator->getGeographicFromGeocentric(); 00564 } 00565 00566 osgTerrain::Layer* elevationLayer = _tile->getElevationLayer(); 00567 00568 // fire up a brand new geode. 00569 osg::Geode* geode = new osg::Geode(); 00570 geode->setThreadSafeRefUnref(true); 00571 00572 // setting the geometry to DYNAMIC means its draw will not overlap the next frame's update/cull 00573 // traversal - which could access the buffer without a mutex 00574 00575 osg::Geometry* surface = new osg::Geometry(); 00576 surface->setThreadSafeRefUnref(true); // TODO: probably unnecessary. 00577 surface->setDataVariance( osg::Object::DYNAMIC ); 00578 surface->setUseDisplayList(false); 00579 surface->setUseVertexBufferObjects(true); 00580 geode->addDrawable( surface ); 00581 00582 osg::Geometry* skirt = new osg::Geometry(); 00583 skirt->setThreadSafeRefUnref(true); // TODO: probably unnecessary. 00584 skirt->setDataVariance( osg::Object::DYNAMIC ); 00585 skirt->setUseDisplayList(false); 00586 skirt->setUseVertexBufferObjects(true); 00587 geode->addDrawable( skirt ); 00588 00589 osg::ref_ptr<GeoLocator> geoLocator = _masterLocator; 00590 // Avoid coordinates conversion when GEOCENTRIC, so get a GEOGRAPHIC version of Locator 00591 if (_masterLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC) { 00592 geoLocator = _masterLocator->getGeographicFromGeocentric(); 00593 } 00594 00595 float scaleHeight = 00596 _verticalScaleOverride != 1.0? _verticalScaleOverride : 00597 _tile->getTerrain() ? _tile->getTerrain()->getVerticalScale() : 00598 1.0f; 00599 00600 MaskRecordVector masks; 00601 for (MaskLayerVector::const_iterator it = tilef._masks.begin(); it != tilef._masks.end(); ++it) 00602 { 00603 // When displaying Plate Carre, Heights have to be converted from meters to degrees. 00604 // This is also true for mask feature 00605 // TODO: adjust this calculation based on the actual EllipsoidModel. 00606 float scale = scaleHeight; 00607 if (_masterLocator->getCoordinateSystemType() == osgEarth::GeoLocator::GEOGRAPHIC) 00608 scale = scaleHeight / 111319.0f; 00609 00610 // TODO: Get the map SRS if possible instead of masterLocator's one 00611 osg::Vec3dArray* boundary = (*it)->getOrCreateBoundary(scale, _masterLocator->getDataExtent().getSRS()); 00612 00613 if ( boundary ) 00614 { 00615 osg::Vec3d min, max; 00616 min = max = boundary->front(); 00617 00618 for (osg::Vec3dArray::iterator it = boundary->begin(); it != boundary->end(); ++it) 00619 { 00620 if (it->x() < min.x()) 00621 min.x() = it->x(); 00622 00623 if (it->y() < min.y()) 00624 min.y() = it->y(); 00625 00626 if (it->x() > max.x()) 00627 max.x() = it->x(); 00628 00629 if (it->y() > max.y()) 00630 max.y() = it->y(); 00631 } 00632 00633 osg::Vec3d min_ndc, max_ndc; 00634 geoLocator->convertModelToLocal(min, min_ndc); 00635 geoLocator->convertModelToLocal(max, max_ndc); 00636 00637 bool x_match = ((min_ndc.x() >= 0.0 && max_ndc.x() <= 1.0) || 00638 (min_ndc.x() <= 0.0 && max_ndc.x() > 0.0) || 00639 (min_ndc.x() < 1.0 && max_ndc.x() >= 1.0)); 00640 00641 bool y_match = ((min_ndc.y() >= 0.0 && max_ndc.y() <= 1.0) || 00642 (min_ndc.y() <= 0.0 && max_ndc.y() > 0.0) || 00643 (min_ndc.y() < 1.0 && max_ndc.y() >= 1.0)); 00644 00645 if (x_match && y_match) 00646 { 00647 osg::Geometry* mask_geom = new osg::Geometry(); 00648 mask_geom->setThreadSafeRefUnref(true); 00649 mask_geom->setDataVariance( osg::Object::DYNAMIC ); 00650 mask_geom->setUseDisplayList(false); 00651 mask_geom->setUseVertexBufferObjects(true); 00652 //mask_geom->getOrCreateStateSet()->setAttribute(new osg::Point( 5.0f ), osg::StateAttribute::ON); 00653 geode->addDrawable(mask_geom); 00654 00655 masks.push_back(MaskRecord(boundary, min_ndc, max_ndc, mask_geom)); 00656 } 00657 } 00658 } 00659 00660 osg::Geometry* stitching_skirts = 0L; 00661 osg::Vec3Array* ss_verts = 0L; 00662 if (masks.size() > 0) 00663 { 00664 stitching_skirts = new osg::Geometry(); 00665 stitching_skirts->setThreadSafeRefUnref(true); 00666 stitching_skirts->setDataVariance( osg::Object::DYNAMIC ); 00667 stitching_skirts->setUseDisplayList(false); 00668 stitching_skirts->setUseVertexBufferObjects(true); 00669 //stitching_skirts->getOrCreateStateSet()->setAttribute(new osg::Point( 5.0f ), osg::StateAttribute::ON); 00670 geode->addDrawable( stitching_skirts); 00671 00672 ss_verts = new osg::Vec3Array(); 00673 stitching_skirts->setVertexArray(ss_verts); 00674 } 00675 00676 00677 unsigned int numRows = 20; 00678 unsigned int numColumns = 20; 00679 00680 if (elevationLayer) 00681 { 00682 numColumns = elevationLayer->getNumColumns(); 00683 numRows = elevationLayer->getNumRows(); 00684 } 00685 00686 double i_sampleFactor, j_sampleFactor; 00687 calculateSampling( numColumns, numRows, i_sampleFactor, j_sampleFactor ); 00688 00689 float skirtHeight = 0.0f; 00690 osgTerrain::HeightFieldLayer* hfl = dynamic_cast<osgTerrain::HeightFieldLayer*>(elevationLayer); 00691 if (hfl && hfl->getHeightField()) 00692 { 00693 skirtHeight = hfl->getHeightField()->getSkirtHeight(); 00694 } 00695 00696 bool createSkirt = skirtHeight != 0.0f; 00697 00698 unsigned int numVerticesInSurface = numColumns*numRows; 00699 unsigned int numVerticesInSkirt = createSkirt ? (2 * (numColumns*2 + numRows*2 - 4)) : 0; 00700 //unsigned int numVertices = numVerticesInBody+numVerticesInSkirt; 00701 00702 // allocate and assign vertices 00703 osg::ref_ptr<osg::Vec3Array> surfaceVerts = new osg::Vec3Array; 00704 surfaceVerts->reserve( numVerticesInSurface ); 00705 surface->setVertexArray( surfaceVerts.get() ); 00706 00707 // allocate and assign normals 00708 osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array(); 00709 normals->reserve(numVerticesInSurface); 00710 surface->setNormalArray(normals.get()); 00711 surface->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); 00712 00713 // skirt texture coordinates, if applicable: 00714 osg::Vec2Array* unifiedSkirtTexCoords = 0L; 00715 00716 // stitching skirt texture coordinates, if applicable: 00717 osg::Vec2Array* unifiedStitchSkirtTexCoords = 0L; 00718 00719 // allocate and assign texture coordinates 00720 osg::Vec2Array* unifiedSurfaceTexCoords = 0L; 00721 00722 //int numColorLayers = _tile->getNumColorLayers(); 00723 RenderLayerVector renderLayers; 00724 00725 if ( _texCompositor->requiresUnitTextureSpace() ) 00726 { 00727 // for a unified unit texture space, just make a single texture coordinate array. 00728 unifiedSurfaceTexCoords = new osg::Vec2Array(); 00729 unifiedSurfaceTexCoords->reserve( numVerticesInSurface ); 00730 surface->setTexCoordArray( 0, unifiedSurfaceTexCoords ); 00731 if (createSkirt) 00732 { 00733 unifiedSkirtTexCoords = new osg::Vec2Array(); 00734 unifiedSkirtTexCoords->reserve( numVerticesInSkirt ); 00735 skirt->setTexCoordArray( 0, unifiedSkirtTexCoords ); 00736 } 00737 00738 if (masks.size() > 0) 00739 { 00740 unifiedStitchSkirtTexCoords = new osg::Vec2Array(); 00741 //unifiedStitchSkirtTexCoords->reserve( ? ); 00742 stitching_skirts->setTexCoordArray( 0, unifiedStitchSkirtTexCoords ); 00743 } 00744 } 00745 00746 else // if ( !_texCompositor->requiresUnitTextureSpace() ) 00747 { 00748 LocatorToTexCoordTable locatorToTexCoordTable; 00749 renderLayers.reserve( tilef._colorLayers.size() ); 00750 00751 // build a list of "render layers", in slot order, sharing texture coordinate 00752 // arrays wherever possible. 00753 for( ColorLayersByUID::const_iterator i = tilef._colorLayers.begin(); i != tilef._colorLayers.end(); ++i ) 00754 { 00755 const CustomColorLayer& colorLayer = i->second; 00756 RenderLayer r; 00757 r._layer = colorLayer; 00758 00759 const GeoLocator* locator = dynamic_cast<const GeoLocator*>( r._layer.getLocator() ); 00760 if ( locator ) 00761 { 00762 r._texCoords = locatorToTexCoordTable.find( locator ); 00763 if ( !r._texCoords.valid() ) 00764 { 00765 r._texCoords = new osg::Vec2Array(); 00766 r._texCoords->reserve( numVerticesInSurface ); 00767 r._ownsTexCoords = true; 00768 locatorToTexCoordTable.push_back( LocatorTexCoordPair(locator, r._texCoords.get()) ); 00769 } 00770 00771 r._skirtTexCoords = new osg::Vec2Array(); 00772 r._skirtTexCoords->reserve( numVerticesInSkirt ); 00773 00774 if ( masks.size() > 0 ) 00775 { 00776 r._stitchTexCoords = new osg::Vec2Array(); 00777 r._stitchSkirtTexCoords = new osg::Vec2Array(); 00778 } 00779 00780 r._locator = locator; 00781 if ( locator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC ) 00782 { 00783 const GeoLocator* geo = dynamic_cast<const GeoLocator*>(locator); 00784 if ( geo ) 00785 r._locator = geo->getGeographicFromGeocentric(); 00786 } 00787 00788 _texCompositor->assignTexCoordArray( surface, colorLayer.getUID(), r._texCoords.get() ); 00789 _texCompositor->assignTexCoordArray( skirt, colorLayer.getUID(), r._skirtTexCoords.get() ); 00790 00791 for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) 00792 _texCompositor->assignTexCoordArray( (*mr)._geom, colorLayer.getUID(), r._stitchTexCoords.get() ); 00793 00794 if (stitching_skirts) 00795 _texCompositor->assignTexCoordArray( stitching_skirts, colorLayer.getUID(), r._stitchSkirtTexCoords.get() ); 00796 00797 //surface->setTexCoordArray( renderLayers.size(), r._texCoords ); 00798 renderLayers.push_back( r ); 00799 } 00800 else 00801 { 00802 OE_WARN << LC << "Found a Locator, but it wasn't a GeoLocator." << std::endl; 00803 } 00804 } 00805 } 00806 00807 osg::ref_ptr<osg::FloatArray> elevations = new osg::FloatArray; 00808 if (elevations.valid()) elevations->reserve(numVerticesInSurface); 00809 00810 // allocate and assign color 00811 osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1); 00812 (*colors)[0].set(1.0f,1.0f,1.0f,1.0f); 00813 00814 surface->setColorArray(colors.get()); 00815 surface->setColorBinding(osg::Geometry::BIND_OVERALL); 00816 00817 typedef std::vector<int> Indices; 00818 Indices indices(numVerticesInSurface, -1); 00819 00820 // populate vertex and tex coord arrays 00821 unsigned int i, j; //, k=0; 00822 for(j=0; j<numRows; ++j) 00823 { 00824 for(i=0; i<numColumns; ++i) // ++k) 00825 { 00826 unsigned int iv = j*numColumns + i; 00827 osg::Vec3d ndc( ((double)i)/(double)(numColumns-1), ((double)j)/(double)(numRows-1), 0.0); 00828 00829 bool validValue = true; 00830 00831 unsigned int i_equiv = i_sampleFactor==1.0 ? i : (unsigned int) (double(i)*i_sampleFactor); 00832 00833 unsigned int j_equiv = j_sampleFactor==1.0 ? j : (unsigned int) (double(j)*j_sampleFactor); 00834 00835 if (elevationLayer) 00836 { 00837 float value = 0.0f; 00838 validValue = elevationLayer->getValidValue(i_equiv,j_equiv, value); 00839 ndc.z() = value*scaleHeight; 00840 } 00841 00842 //Invalidate if point falls within mask bounding box 00843 if (validValue && masks.size() > 0) 00844 { 00845 for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) 00846 { 00847 if(ndc.x() >= (*mr)._ndcMin.x() && ndc.x() <= (*mr)._ndcMax.x() && 00848 ndc.y() >= (*mr)._ndcMin.y() && ndc.y() <= (*mr)._ndcMax.y()) 00849 { 00850 validValue = false; 00851 indices[iv] = -2; 00852 break; 00853 } 00854 } 00855 } 00856 00857 if (validValue) 00858 { 00859 //indices[iv] = k; 00860 indices[iv] = surfaceVerts->size(); 00861 00862 osg::Vec3d model; 00863 _masterLocator->convertLocalToModel(ndc, model); 00864 00865 //(*surfaceVerts)[k] = model - centerModel; 00866 (*surfaceVerts).push_back(model - _centerModel); 00867 00868 if ( _texCompositor->requiresUnitTextureSpace() ) 00869 { 00870 // the unified unit texture space requires a single, untransformed unit coord [0..1] 00871 (*unifiedSurfaceTexCoords).push_back( osg::Vec2( ndc.x(), ndc.y() ) ); 00872 } 00873 else 00874 { 00875 // the separate texture space requires separate transformed texcoords for each layer. 00876 for( RenderLayerVector::const_iterator r = renderLayers.begin(); r != renderLayers.end(); ++r ) 00877 { 00878 if ( r->_ownsTexCoords ) 00879 { 00880 //if ( r->_locator.get() != _masterLocator.get() ) 00881 //if ( r->_locator->isEr->_locator != masterTextureLocator _masterLocator.get() ) 00882 if ( !r->_locator->isEquivalentTo( *masterTextureLocator.get() ) ) 00883 { 00884 osg::Vec3d color_ndc; 00885 osgTerrain::Locator::convertLocalCoordBetween( *masterTextureLocator.get(), ndc, *r->_locator.get(), color_ndc ); 00886 r->_texCoords->push_back( osg::Vec2( color_ndc.x(), color_ndc.y() ) ); 00887 } 00888 else 00889 { 00890 r->_texCoords->push_back( osg::Vec2( ndc.x(), ndc.y() ) ); 00891 } 00892 } 00893 } 00894 } 00895 00896 if (elevations.valid()) 00897 { 00898 (*elevations).push_back(ndc.z()); 00899 } 00900 00901 // compute the local normal 00902 osg::Vec3d ndc_one = ndc; ndc_one.z() += 1.0; 00903 osg::Vec3d model_one; 00904 _masterLocator->convertLocalToModel(ndc_one, model_one); 00905 model_one = model_one - model; 00906 model_one.normalize(); 00907 00908 //(*normals)[k] = model_one; 00909 (*normals).push_back(model_one); 00910 } 00911 } 00912 } 00913 00914 00915 for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) 00916 { 00917 int min_i = (int)floor((*mr)._ndcMin.x() * (double)(numColumns-1)); 00918 if (min_i < 0) min_i = 0; 00919 if (min_i >= numColumns) min_i = numColumns - 1; 00920 00921 int min_j = (int)floor((*mr)._ndcMin.y() * (double)(numRows-1)); 00922 if (min_j < 0) min_j = 0; 00923 if (min_j >= numColumns) min_j = numColumns - 1; 00924 00925 int max_i = (int)ceil((*mr)._ndcMax.x() * (double)(numColumns-1)); 00926 if (max_i < 0) max_i = 0; 00927 if (max_i >= numColumns) max_i = numColumns - 1; 00928 00929 int max_j = (int)ceil((*mr)._ndcMax.y() * (double)(numRows-1)); 00930 if (max_j < 0) max_j = 0; 00931 if (max_j >= numColumns) max_j = numColumns - 1; 00932 00933 if (min_i >= 0 && max_i >= 0 && min_j >= 0 && max_j >= 0) 00934 { 00935 int num_i = max_i - min_i + 1; 00936 int num_j = max_j - min_j + 1; 00937 00938 osg::ref_ptr<osgEarth::Symbology::Polygon> maskSkirtPoly = new osgEarth::Symbology::Polygon(); 00939 maskSkirtPoly->resize(num_i * 2 + num_j * 2 - 4); 00940 for (int i = 0; i < num_i; i++) 00941 { 00942 //int index = indices[min_j*numColumns + i + min_i]; 00943 { 00944 osg::Vec3d ndc( ((double)(i + min_i))/(double)(numColumns-1), ((double)min_j)/(double)(numRows-1), 0.0); 00945 00946 if (elevationLayer) 00947 { 00948 unsigned int i_equiv = i_sampleFactor==1.0 ? i + min_i : (unsigned int) (double(i + min_i)*i_sampleFactor); 00949 unsigned int j_equiv = j_sampleFactor==1.0 ? min_j : (unsigned int) (double(min_j)*j_sampleFactor); 00950 00951 float value = 0.0f; 00952 if (elevationLayer->getValidValue(i_equiv,j_equiv, value)) 00953 ndc.z() = value*scaleHeight; 00954 } 00955 00956 (*maskSkirtPoly)[i] = ndc; 00957 } 00958 00959 //index = indices[max_j*numColumns + i + min_i]; 00960 { 00961 osg::Vec3d ndc( ((double)(i + min_i))/(double)(numColumns-1), ((double)max_j)/(double)(numRows-1), 0.0); 00962 00963 if (elevationLayer) 00964 { 00965 unsigned int i_equiv = i_sampleFactor==1.0 ? i + min_i : (unsigned int) (double(i + min_i)*i_sampleFactor); 00966 unsigned int j_equiv = j_sampleFactor==1.0 ? max_j : (unsigned int) (double(max_j)*j_sampleFactor); 00967 00968 float value = 0.0f; 00969 if (elevationLayer->getValidValue(i_equiv,j_equiv, value)) 00970 ndc.z() = value*scaleHeight; 00971 } 00972 00973 (*maskSkirtPoly)[i + (2 * num_i + num_j - 3) - 2 * i] = ndc; 00974 } 00975 } 00976 for (int j = 0; j < num_j - 2; j++) 00977 { 00978 //int index = indices[(min_j + j + 1)*numColumns + max_i]; 00979 { 00980 osg::Vec3d ndc( ((double)max_i)/(double)(numColumns-1), ((double)(min_j + j + 1))/(double)(numRows-1), 0.0); 00981 00982 if (elevationLayer) 00983 { 00984 unsigned int i_equiv = i_sampleFactor==1.0 ? max_i : (unsigned int) (double(max_i)*i_sampleFactor); 00985 unsigned int j_equiv = j_sampleFactor==1.0 ? min_j + j + 1 : (unsigned int) (double(min_j + j + 1)*j_sampleFactor); 00986 00987 float value = 0.0f; 00988 if (elevationLayer->getValidValue(i_equiv,j_equiv, value)) 00989 ndc.z() = value*scaleHeight; 00990 } 00991 00992 (*maskSkirtPoly)[j + num_i] = ndc; 00993 } 00994 00995 //index = indices[(min_j + j + 1)*numColumns + min_i]; 00996 { 00997 osg::Vec3d ndc( ((double)min_i)/(double)(numColumns-1), ((double)(min_j + j + 1))/(double)(numRows-1), 0.0); 00998 00999 if (elevationLayer) 01000 { 01001 unsigned int i_equiv = i_sampleFactor==1.0 ? min_i : (unsigned int) (double(min_i)*i_sampleFactor); 01002 unsigned int j_equiv = j_sampleFactor==1.0 ? min_j + j + 1 : (unsigned int) (double(min_j + j + 1)*j_sampleFactor); 01003 01004 float value = 0.0f; 01005 if (elevationLayer->getValidValue(i_equiv,j_equiv, value)) 01006 ndc.z() = value*scaleHeight; 01007 } 01008 01009 (*maskSkirtPoly)[j + (2 * num_i + 2 * num_j - 5) - 2 * j] = ndc; 01010 } 01011 } 01012 01013 //Create local polygon representing mask 01014 osg::ref_ptr<osgEarth::Symbology::Polygon> maskPoly = new osgEarth::Symbology::Polygon(); 01015 for (osg::Vec3dArray::iterator it = (*mr)._boundary->begin(); it != (*mr)._boundary->end(); ++it) 01016 { 01017 osg::Vec3d local; 01018 geoLocator->convertModelToLocal(*it, local); 01019 maskPoly->push_back(local); 01020 } 01021 01022 01023 //Change the following two #if statements to see mask skirt polygons 01024 //before clipping and adjusting 01025 #if 1 01026 //Do a diff on the polygons to get the actual mask skirt 01027 osg::ref_ptr<osgEarth::Symbology::Geometry> outPoly; 01028 maskSkirtPoly->difference(maskPoly.get(), outPoly); 01029 #else 01030 osg::ref_ptr<osgEarth::Symbology::Geometry> outPoly = maskSkirtPoly; 01031 #endif 01032 01033 osg::Vec3Array* outVerts = new osg::Vec3Array(); 01034 01035 osg::Geometry* stitch_geom = (*mr)._geom; 01036 stitch_geom->setVertexArray(outVerts); 01037 01038 bool multiParent = false; 01039 if (outPoly.valid()) 01040 multiParent = outPoly->getType() == osgEarth::Symbology::Geometry::TYPE_MULTI; 01041 01042 std::vector<int> skirtIndices; 01043 01044 osgEarth::Symbology::GeometryIterator i( outPoly.get(), false ); 01045 while( i.hasMore() ) 01046 { 01047 osgEarth::Symbology::Geometry* part = i.next(); 01048 if (!part) 01049 continue; 01050 01051 if (part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON) 01052 { 01053 osg::Vec3Array* partVerts = part->toVec3Array(); 01054 outVerts->insert(outVerts->end(), partVerts->begin(), partVerts->end()); 01055 stitch_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, outVerts->size() - partVerts->size(), partVerts->size())); 01056 skirtIndices.push_back(outVerts->size()); 01057 01058 if (!multiParent) 01059 { 01060 osg::ref_ptr<osgEarth::Symbology::Polygon> holePoly = static_cast<osgEarth::Symbology::Polygon*>(outPoly.get()); 01061 if (holePoly) 01062 { 01063 osgEarth::Symbology::RingCollection holes = holePoly->getHoles(); 01064 01065 for (osgEarth::Symbology::RingCollection::iterator hit = holes.begin(); hit != holes.end(); ++hit) 01066 { 01067 (*hit)->rewind(osgEarth::Symbology::Ring::ORIENTATION_CCW); 01068 outVerts->insert(outVerts->end(), (*hit)->begin(), (*hit)->end()); 01069 stitch_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, outVerts->size() - (*hit)->size(), (*hit)->size())); 01070 skirtIndices.push_back(outVerts->size()); 01071 } 01072 } 01073 } 01074 } 01075 } 01076 01077 if (stitch_geom->getNumPrimitiveSets() > 0) 01078 { 01079 #if 1 01080 // Tessellate mask skirt 01081 osg::ref_ptr<osgUtil::Tessellator> tscx=new osgUtil::Tessellator; 01082 tscx->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY); 01083 tscx->setBoundaryOnly(false); 01084 tscx->setWindingType(osgUtil::Tessellator::TESS_WINDING_ODD); 01085 tscx->retessellatePolygons(*stitch_geom); 01086 01087 // Assign normals to the stitching polygon: -gw 01088 osg::Vec3Array* sgVerts = dynamic_cast<osg::Vec3Array*>(stitch_geom->getVertexArray()); 01089 osg::Vec3Array* sgNormals = new osg::Vec3Array(sgVerts->size()); 01090 stitch_geom->setNormalArray( sgNormals ); 01091 stitch_geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); 01092 01093 // calculate the normal and convert to model space. 01094 for( unsigned v=0; v<sgVerts->size(); ++v ) 01095 { 01096 const osg::Vec3& vert = (*sgVerts)[v]; 01097 osg::Vec3d local_one(vert); 01098 osg::Vec3d model; 01099 _masterLocator->convertLocalToModel( local_one, model ); 01100 local_one.z() += 1.0; 01101 osg::Vec3d model_one; 01102 _masterLocator->convertLocalToModel( local_one, model_one ); 01103 model_one = model_one - model; 01104 model_one.normalize(); 01105 (*sgNormals)[v] = model_one; 01106 } 01107 01108 //Initialize tex coords 01109 osg::Vec2Array* unifiedStitchTexCoords = 0L; 01110 if (_texCompositor->requiresUnitTextureSpace()) 01111 { 01112 unifiedStitchTexCoords = new osg::Vec2Array(); 01113 unifiedStitchTexCoords->reserve(outVerts->size()); 01114 stitch_geom->setTexCoordArray(0, unifiedStitchTexCoords); 01115 } 01116 else if ( renderLayers.size() > 0 ) 01117 { 01118 for (unsigned int i = 0; i < renderLayers.size(); ++i) 01119 { 01120 renderLayers[i]._stitchTexCoords->reserve(outVerts->size()); 01121 } 01122 } 01123 01124 01125 //Retrieve z values for mask skirt verts 01126 std::vector<int> isZSet; 01127 for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it) 01128 { 01129 int zSet = 0; 01130 01131 //Look for verts that belong to the original mask skirt polygon 01132 for (osgEarth::Symbology::Polygon::iterator mit = maskSkirtPoly->begin(); mit != maskSkirtPoly->end(); ++mit) 01133 { 01134 if (osg::absolute((*mit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*mit).y() - (*it).y()) < MATCH_TOLERANCE) 01135 { 01136 (*it).z() = (*mit).z(); 01137 zSet += 1; 01138 break; 01139 } 01140 } 01141 01142 //Look for verts that belong to the mask polygon 01143 for (osgEarth::Symbology::Polygon::iterator mit = maskPoly->begin(); mit != maskPoly->end(); ++mit) 01144 { 01145 if (osg::absolute((*mit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*mit).y() - (*it).y()) < MATCH_TOLERANCE) 01146 { 01147 (*it).z() = (*mit).z(); 01148 zSet += 2; 01149 break; 01150 } 01151 } 01152 01153 isZSet.push_back(zSet); 01154 } 01155 01156 //Any mask skirt verts that are still unset are newly created verts where the skirt 01157 //meets the mask. Find the mask segment the point lies along and calculate the 01158 //appropriate z value for the point. 01159 // 01160 //Now that all the z values are set, convert each vert into model coords. 01161 // 01162 //Also, while we are iterating through the verts, set up tex coords. 01163 int count = 0; 01164 for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it) 01165 { 01166 //If the z-value was set from a mask vertex there is no need to change it. If 01167 //it was set from a vertex from the stitching polygon it may need overriden if 01168 //the vertex lies along a mask edge. Or if it is unset, it will need to be set. 01169 if (isZSet[count] < 2) 01170 { 01171 osg::Vec3d p2 = *it; 01172 double closestZ = 0.0; 01173 double closestRatio = DBL_MAX; 01174 for (osgEarth::Symbology::Polygon::iterator mit = maskPoly->begin(); mit != maskPoly->end(); ++mit) 01175 { 01176 osg::Vec3d p1 = *mit; 01177 osg::Vec3d p3 = mit == --maskPoly->end() ? maskPoly->front() : (*(mit + 1)); 01178 01179 //Truncated vales to compensate for accuracy issues 01180 double p1x = ((int)(p1.x() * 1000000)) / 1000000.0L; 01181 double p3x = ((int)(p3.x() * 1000000)) / 1000000.0L; 01182 double p2x = ((int)(p2.x() * 1000000)) / 1000000.0L; 01183 01184 double p1y = ((int)(p1.y() * 1000000)) / 1000000.0L; 01185 double p3y = ((int)(p3.y() * 1000000)) / 1000000.0L; 01186 double p2y = ((int)(p2.y() * 1000000)) / 1000000.0L; 01187 01188 if ((p1x < p3x ? p2x >= p1x && p2x <= p3x : p2x >= p3x && p2x <= p1x) && 01189 (p1y < p3y ? p2y >= p1y && p2y <= p3y : p2y >= p3y && p2y <= p1y)) 01190 { 01191 double l1 =(osg::Vec2d(p2.x(), p2.y()) - osg::Vec2d(p1.x(), p1.y())).length(); 01192 double lt = (osg::Vec2d(p3.x(), p3.y()) - osg::Vec2d(p1.x(), p1.y())).length(); 01193 double zmag = p3.z() - p1.z(); 01194 01195 double foundZ = (l1 / lt) * zmag + p1.z(); 01196 01197 double mRatio = 1.0; 01198 if (osg::absolute(p1x - p3x) < MATCH_TOLERANCE) 01199 { 01200 if (osg::absolute(p1x-p2x) < MATCH_TOLERANCE) 01201 mRatio = 0.0; 01202 } 01203 else 01204 { 01205 double m1 = p1x == p2x ? 0.0 : (p2y - p1y) / (p2x - p1x); 01206 double m2 = p1x == p3x ? 0.0 : (p3y - p1y) / (p3x - p1x); 01207 mRatio = m2 == 0.0 ? m1 : osg::absolute(1.0L - m1 / m2); 01208 } 01209 01210 if (mRatio < 0.01) 01211 { 01212 (*it).z() = foundZ; 01213 isZSet[count] = 2; 01214 break; 01215 } 01216 else if (mRatio < closestRatio) 01217 { 01218 closestRatio = mRatio; 01219 closestZ = foundZ; 01220 } 01221 } 01222 } 01223 01224 if (!isZSet[count] && closestRatio < DBL_MAX) 01225 { 01226 (*it).z() = closestZ; 01227 isZSet[count] = 2; 01228 } 01229 } 01230 01231 if (!isZSet[count]) 01232 OE_WARN << LC << "Z-value not set for stitching polygon vertex" << std::endl; 01233 01234 count++; 01235 01236 //Convert to model coords 01237 osg::Vec3d model; 01238 _masterLocator->convertLocalToModel(*it, model); 01239 model = model - _centerModel; 01240 (*it).set(model.x(), model.y(), model.z()); 01241 01242 //Setup tex coords 01243 osg::Vec3d ndc; 01244 _masterLocator->convertModelToLocal(*it + _centerModel, ndc); 01245 01246 if (_texCompositor->requiresUnitTextureSpace()) 01247 { 01248 unifiedStitchTexCoords->push_back(osg::Vec2(ndc.x(), ndc.y())); 01249 } 01250 else if (renderLayers.size() > 0) 01251 { 01252 for (unsigned int i = 0; i < renderLayers.size(); ++i) 01253 { 01254 if (!renderLayers[i]._locator->isEquivalentTo(*masterTextureLocator.get())) 01255 { 01256 osg::Vec3d color_ndc; 01257 osgTerrain::Locator::convertLocalCoordBetween(*masterTextureLocator.get(), ndc, *renderLayers[i]._locator.get(), color_ndc); 01258 renderLayers[i]._stitchTexCoords->push_back(osg::Vec2(color_ndc.x(), color_ndc.y())); 01259 } 01260 else 01261 { 01262 renderLayers[i]._stitchTexCoords->push_back(osg::Vec2(ndc.x(), ndc.y())); 01263 } 01264 } 01265 } 01266 } 01267 #else 01268 for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it) 01269 { 01270 //Convert to model coords 01271 osg::Vec3d model; 01272 _masterLocator->convertLocalToModel(*it, model); 01273 model = model - _centerModel; 01274 (*it).set(model.x(), model.y(), model.z()); 01275 } 01276 #endif 01277 01278 //Create stitching skirts 01279 if (createSkirt && skirtIndices.size() > 0) 01280 { 01281 ss_verts->reserve(ss_verts->size() + outVerts->size() * 4 + skirtIndices.size() * 2); 01282 01283 //Add a primative set for each continuous skirt strip 01284 for (int p=0; p < skirtIndices.size(); p++) 01285 { 01286 int cursor = ss_verts->size(); 01287 01288 int outStart = p == 0 ? 0 : skirtIndices[p-1]; 01289 for (int i=outStart; i < skirtIndices[p]; i++) 01290 { 01291 ss_verts->push_back((*outVerts)[i]); 01292 ss_verts->push_back((*outVerts)[i] - (*sgNormals)[i] * skirtHeight); 01293 01294 if ( _texCompositor->requiresUnitTextureSpace() ) 01295 { 01296 unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] ); 01297 unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] ); 01298 } 01299 else if ( renderLayers.size() > 0 ) 01300 { 01301 for (unsigned int r = 0; r < renderLayers.size(); ++r) 01302 { 01303 const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[i]; 01304 renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); 01305 renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); 01306 } 01307 } 01308 } 01309 01310 //Add the first vert again to complete the loop 01311 ss_verts->push_back((*outVerts)[outStart]); 01312 ss_verts->push_back((*outVerts)[outStart] - (*sgNormals)[outStart] * skirtHeight); 01313 01314 if ( _texCompositor->requiresUnitTextureSpace() ) 01315 { 01316 unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[outStart] ); 01317 unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[outStart] ); 01318 } 01319 else if ( renderLayers.size() > 0 ) 01320 { 01321 for (unsigned int r = 0; r < renderLayers.size(); ++r) 01322 { 01323 const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[outStart]; 01324 renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); 01325 renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); 01326 } 01327 } 01328 01329 //Now go back the opposite direction to create a skirt facing the other direction 01330 for (int i=skirtIndices[p] - 1; i >= outStart; i--) 01331 { 01332 ss_verts->push_back((*outVerts)[i]); 01333 ss_verts->push_back((*outVerts)[i] - (*sgNormals)[i] * skirtHeight); 01334 01335 if ( _texCompositor->requiresUnitTextureSpace() ) 01336 { 01337 unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] ); 01338 unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] ); 01339 } 01340 else if ( renderLayers.size() > 0 ) 01341 { 01342 for (unsigned int r = 0; r < renderLayers.size(); ++r) 01343 { 01344 const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[i]; 01345 renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); 01346 renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); 01347 } 01348 } 01349 } 01350 01351 stitching_skirts->addPrimitiveSet(new osg::DrawArrays( GL_TRIANGLE_STRIP, cursor, ss_verts->size() - cursor)); 01352 } 01353 } 01354 } 01355 } 01356 } 01357 01358 // populate primitive sets 01359 bool swapOrientation = !(_masterLocator->orientationOpenGL()); 01360 01361 osg::ref_ptr<osg::DrawElementsUInt> elements = new osg::DrawElementsUInt(GL_TRIANGLES); 01362 elements->reserve((numRows-1) * (numColumns-1) * 6); 01363 01364 surface->addPrimitiveSet(elements.get()); 01365 01366 osg::ref_ptr<osg::Vec3Array> skirtVectors = new osg::Vec3Array( *normals ); 01367 01368 if (!normals) 01369 createSkirt = false; 01370 01371 // New separated skirts. 01372 if ( createSkirt ) 01373 { 01374 // build the verts first: 01375 osg::Vec3Array* skirtVerts = new osg::Vec3Array(); 01376 osg::Vec3Array* skirtNormals = new osg::Vec3Array(); 01377 01378 skirtVerts->reserve( numVerticesInSkirt ); 01379 skirtNormals->reserve( numVerticesInSkirt ); 01380 01381 Indices skirtBreaks; 01382 skirtBreaks.push_back(0); 01383 01384 // bottom: 01385 for( unsigned int c=0; c<numColumns-1; ++c ) 01386 { 01387 int orig_i = indices[c]; 01388 01389 //int offset = 0; 01390 //while (orig_i < 0 && offset < numRows - 1) 01391 // orig_i = indices[c + ++offset * numColumns]; 01392 01393 if (orig_i < 0) 01394 { 01395 if (skirtBreaks.back() != skirtVerts->size()) 01396 skirtBreaks.push_back(skirtVerts->size()); 01397 } 01398 else 01399 { 01400 skirtVerts->push_back( (*surfaceVerts)[orig_i] ); 01401 skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); 01402 skirtNormals->push_back( (*normals)[orig_i] ); 01403 skirtNormals->push_back( (*normals)[orig_i] ); 01404 01405 01406 if ( _texCompositor->requiresUnitTextureSpace() ) 01407 { 01408 unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); 01409 unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); 01410 } 01411 else if ( renderLayers.size() > 0 ) 01412 { 01413 for (unsigned int i = 0; i < renderLayers.size(); ++i) 01414 { 01415 const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i]; 01416 renderLayers[i]._skirtTexCoords->push_back( tc ); 01417 renderLayers[i]._skirtTexCoords->push_back( tc ); 01418 } 01419 } 01420 } 01421 } 01422 01423 // right: 01424 for( unsigned int r=0; r<numRows-1; ++r ) 01425 { 01426 int orig_i = indices[r*numColumns+(numColumns-1)]; 01427 if (orig_i < 0) 01428 { 01429 if (skirtBreaks.back() != skirtVerts->size()) 01430 skirtBreaks.push_back(skirtVerts->size()); 01431 } 01432 else 01433 { 01434 skirtVerts->push_back( (*surfaceVerts)[orig_i] ); 01435 skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); 01436 skirtNormals->push_back( (*normals)[orig_i] ); 01437 skirtNormals->push_back( (*normals)[orig_i] ); 01438 01439 if ( _texCompositor->requiresUnitTextureSpace() ) 01440 { 01441 unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); 01442 unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); 01443 } 01444 else if ( renderLayers.size() > 0 ) 01445 { 01446 for (unsigned int i = 0; i < renderLayers.size(); ++i) 01447 { 01448 const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i]; 01449 renderLayers[i]._skirtTexCoords->push_back( tc ); 01450 renderLayers[i]._skirtTexCoords->push_back( tc ); 01451 } 01452 } 01453 } 01454 } 01455 01456 // top: 01457 for( int c=numColumns-1; c>0; --c ) 01458 { 01459 int orig_i = indices[(numRows-1)*numColumns+c]; 01460 if (orig_i < 0) 01461 { 01462 if (skirtBreaks.back() != skirtVerts->size()) 01463 skirtBreaks.push_back(skirtVerts->size()); 01464 } 01465 else 01466 { 01467 skirtVerts->push_back( (*surfaceVerts)[orig_i] ); 01468 skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); 01469 skirtNormals->push_back( (*normals)[orig_i] ); 01470 skirtNormals->push_back( (*normals)[orig_i] ); 01471 01472 if ( _texCompositor->requiresUnitTextureSpace() ) 01473 { 01474 unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); 01475 unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); 01476 } 01477 else if ( renderLayers.size() > 0 ) 01478 { 01479 for (unsigned int i = 0; i < renderLayers.size(); ++i) 01480 { 01481 const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i]; 01482 renderLayers[i]._skirtTexCoords->push_back( tc ); 01483 renderLayers[i]._skirtTexCoords->push_back( tc ); 01484 } 01485 } 01486 } 01487 } 01488 01489 // left: 01490 for( int r=numRows-1; r>=0; --r ) 01491 { 01492 int orig_i = indices[r*numColumns]; 01493 if (orig_i < 0) 01494 { 01495 if (skirtBreaks.back() != skirtVerts->size()) 01496 skirtBreaks.push_back(skirtVerts->size()); 01497 } 01498 else 01499 { 01500 skirtVerts->push_back( (*surfaceVerts)[orig_i] ); 01501 skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); 01502 skirtNormals->push_back( (*normals)[orig_i] ); 01503 skirtNormals->push_back( (*normals)[orig_i] ); 01504 01505 if ( _texCompositor->requiresUnitTextureSpace() ) 01506 { 01507 unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); 01508 unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); 01509 } 01510 else if ( renderLayers.size() > 0 ) 01511 { 01512 for (unsigned int i = 0; i < renderLayers.size(); ++i) 01513 { 01514 const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i]; 01515 renderLayers[i]._skirtTexCoords->push_back( tc ); 01516 renderLayers[i]._skirtTexCoords->push_back( tc ); 01517 } 01518 } 01519 } 01520 } 01521 01522 skirt->setVertexArray( skirtVerts ); 01523 skirt->setNormalArray( skirtNormals ); 01524 skirt->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); 01525 01526 //Add a primative set for each continuous skirt strip 01527 skirtBreaks.push_back(skirtVerts->size()); 01528 for (int p=1; p < skirtBreaks.size(); p++) 01529 skirt->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_STRIP, skirtBreaks[p-1], skirtBreaks[p] - skirtBreaks[p-1] ) ); 01530 } 01531 01532 bool recalcNormals = elevationLayer != NULL; 01533 01534 //Clear out the normals 01535 if (recalcNormals) 01536 { 01537 osg::Vec3Array::iterator nitr; 01538 for(nitr = normals->begin(); 01539 nitr!=normals->end(); 01540 ++nitr) 01541 { 01542 nitr->set(0.0f,0.0f,0.0f); 01543 } 01544 } 01545 01546 for(j=0; j<numRows-1; ++j) 01547 { 01548 for(i=0; i<numColumns-1; ++i) 01549 { 01550 int i00; 01551 int i01; 01552 if (swapOrientation) 01553 { 01554 i01 = j*numColumns + i; 01555 i00 = i01+numColumns; 01556 } 01557 else 01558 { 01559 i00 = j*numColumns + i; 01560 i01 = i00+numColumns; 01561 } 01562 01563 int i10 = i00+1; 01564 int i11 = i01+1; 01565 01566 // remap indices to final vertex positions 01567 i00 = indices[i00]; 01568 i01 = indices[i01]; 01569 i10 = indices[i10]; 01570 i11 = indices[i11]; 01571 01572 unsigned int numValid = 0; 01573 if (i00>=0) ++numValid; 01574 if (i01>=0) ++numValid; 01575 if (i10>=0) ++numValid; 01576 if (i11>=0) ++numValid; 01577 01578 if (numValid==4) 01579 { 01580 01581 bool VALID = true; 01582 for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) { 01583 float min_i = (*mr)._ndcMin.x() * (double)(numColumns-1); 01584 float min_j = (*mr)._ndcMin.y() * (double)(numRows-1); 01585 float max_i = (*mr)._ndcMax.x() * (double)(numColumns-1); 01586 float max_j = (*mr)._ndcMax.y() * (double)(numRows-1); 01587 01588 // We test if mask is completely in square 01589 if(i+1 >= min_i && i <= max_i && j+1 >= min_j && j <= max_j) { 01590 VALID = false; 01591 break; 01592 } 01593 } 01594 01595 if (VALID) { 01596 float e00 = (*elevations)[i00]; 01597 float e10 = (*elevations)[i10]; 01598 float e01 = (*elevations)[i01]; 01599 float e11 = (*elevations)[i11]; 01600 01601 osg::Vec3f &v00 = (*surfaceVerts)[i00]; 01602 osg::Vec3f &v10 = (*surfaceVerts)[i10]; 01603 osg::Vec3f &v01 = (*surfaceVerts)[i01]; 01604 osg::Vec3f &v11 = (*surfaceVerts)[i11]; 01605 01606 if (!_optimizeTriangleOrientation || (e00-e11)<fabsf(e01-e10)) 01607 { 01608 elements->push_back(i01); 01609 elements->push_back(i00); 01610 elements->push_back(i11); 01611 01612 elements->push_back(i00); 01613 elements->push_back(i10); 01614 elements->push_back(i11); 01615 01616 if (recalcNormals) 01617 { 01618 osg::Vec3 normal1 = (v00-v01) ^ (v11-v01); 01619 (*normals)[i01] += normal1; 01620 (*normals)[i00] += normal1; 01621 (*normals)[i11] += normal1; 01622 01623 osg::Vec3 normal2 = (v10-v00)^(v11-v00); 01624 (*normals)[i00] += normal2; 01625 (*normals)[i10] += normal2; 01626 (*normals)[i11] += normal2; 01627 } 01628 } 01629 else 01630 { 01631 elements->push_back(i01); 01632 elements->push_back(i00); 01633 elements->push_back(i10); 01634 01635 elements->push_back(i01); 01636 elements->push_back(i10); 01637 elements->push_back(i11); 01638 01639 if (recalcNormals) 01640 { 01641 osg::Vec3 normal1 = (v00-v01) ^ (v10-v01); 01642 (*normals)[i01] += normal1; 01643 (*normals)[i00] += normal1; 01644 (*normals)[i10] += normal1; 01645 01646 osg::Vec3 normal2 = (v10-v01)^(v11-v01); 01647 (*normals)[i01] += normal2; 01648 (*normals)[i10] += normal2; 01649 (*normals)[i11] += normal2; 01650 } 01651 } 01652 } 01653 } 01654 // As skirtPoly is filling the mask bbox, we don't need to create isolated triangle 01655 /*else if (numValid==3) 01656 { 01657 int validIndices[3]; 01658 int indexPtr = 0; 01659 if (i00>=0) 01660 { 01661 elements->push_back(i00); 01662 validIndices[indexPtr++] = i00; 01663 } 01664 01665 if (i01>=0) 01666 { 01667 elements->push_back(i01); 01668 validIndices[indexPtr++] = i01; 01669 } 01670 01671 if (i11>=0) 01672 { 01673 elements->push_back(i11); 01674 validIndices[indexPtr++] = i11; 01675 } 01676 01677 if (i10>=0) 01678 { 01679 elements->push_back(i10); 01680 validIndices[indexPtr++] = i10; 01681 } 01682 01683 if (recalcNormals) 01684 { 01685 osg::Vec3f &v1 = (*surfaceVerts)[validIndices[0]]; 01686 osg::Vec3f &v2 = (*surfaceVerts)[validIndices[1]]; 01687 osg::Vec3f &v3 = (*surfaceVerts)[validIndices[2]]; 01688 osg::Vec3f normal = (v2 - v1) ^ (v3 - v1); 01689 (*normals)[validIndices[0]] += normal; 01690 (*normals)[validIndices[1]] += normal; 01691 (*normals)[validIndices[2]] += normal; 01692 } 01693 } */ 01694 } 01695 } 01696 01697 //Normalize the normals 01698 if (recalcNormals) 01699 { 01700 osg::Vec3Array::iterator nitr; 01701 for(nitr = normals->begin(); 01702 nitr!=normals->end(); 01703 ++nitr) 01704 { 01705 nitr->normalize(); 01706 } 01707 } 01708 01709 01710 01711 MeshConsolidator::run( *surface ); 01712 01713 if ( skirt ) 01714 MeshConsolidator::run( *skirt ); 01715 01716 for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) 01717 MeshConsolidator::run( *((*mr)._geom) ); 01718 01719 01720 if (osgDB::Registry::instance()->getBuildKdTreesHint()==osgDB::ReaderWriter::Options::BUILD_KDTREES && 01721 osgDB::Registry::instance()->getKdTreeBuilder()) 01722 { 01723 osg::ref_ptr<osg::KdTreeBuilder> builder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); 01724 geode->accept(*builder); 01725 } 01726 01727 return geode; 01728 } 01729 01730 void 01731 SinglePassTerrainTechnique::traverse(osg::NodeVisitor& nv) 01732 { 01733 if ( !_tile ) 01734 return; 01735 01736 if ( _transform.valid() ) 01737 { 01738 _transform->accept( nv ); 01739 } 01740 } 01741 01742 void 01743 SinglePassTerrainTechnique::releaseGLObjects(osg::State* state) const 01744 { 01745 SinglePassTerrainTechnique* ncThis = const_cast<SinglePassTerrainTechnique*>(this); 01746 01747 Threading::ScopedWriteLock lock( static_cast<Tile*>(ncThis->_tile)->getTileLayersMutex() ); 01748 01749 if ( _transform.valid() ) 01750 { 01751 _transform->releaseGLObjects( state ); 01752 } 01753 01754 if ( _backGeode.valid() ) 01755 { 01756 _backGeode->releaseGLObjects(state); 01757 ncThis->_backGeode = 0L; 01758 } 01759 }