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 00020 #include <osgEarth/TextureCompositor> 00021 #include <osgEarth/TextureCompositorTexArray> 00022 #include <osgEarth/TextureCompositorMulti> 00023 #include <osgEarth/Capabilities> 00024 #include <osgEarth/ImageUtils> 00025 #include <osgEarth/Registry> 00026 #include <osg/Texture2DArray> 00027 #include <osg/Texture2D> 00028 #include <osg/Texture3D> 00029 #include <vector> 00030 00031 using namespace osgEarth; 00032 using namespace OpenThreads; 00033 00034 #define LC "[TextureCompositor] " 00035 00036 //--------------------------------------------------------------------------- 00037 00038 TextureLayout::TextureLayout() 00039 { 00040 //nop 00041 } 00042 00043 int 00044 TextureLayout::getSlot( UID layerUID, unsigned which, unsigned maxSlotsToSearch ) const 00045 { 00046 for( unsigned slot = 0; slot < _slots.size() && slot < maxSlotsToSearch; ++slot ) 00047 { 00048 if ( _slots[slot] == layerUID ) 00049 { 00050 if ( which == 0 ) 00051 return slot; 00052 else 00053 --which; 00054 } 00055 } 00056 return -1; 00057 } 00058 00059 int 00060 TextureLayout::getOrder( UID layerUID ) const 00061 { 00062 int slot = getSlot( layerUID, 0 ); 00063 RenderOrderVector::const_iterator i = std::find( _order.begin(), _order.end(), slot ); 00064 return i != _order.end() ? (int)(i-_order.begin()) : -1; 00065 } 00066 00067 int 00068 TextureLayout::getMaxUsedSlot() const 00069 { 00070 for( int i = _slots.size()-1; i >= 0; --i ) 00071 if ( i >= 0 ) 00072 return i; 00073 return -1; 00074 } 00075 00076 void 00077 TextureLayout::assignPrimarySlot( ImageLayer* layer, int orderIndex ) 00078 { 00079 int slot = -1; 00080 00081 bool found = false; 00082 for( TextureSlotVector::iterator i = _slots.begin(); i != _slots.end() && !found; ++i ) 00083 { 00084 slot = (int)(i - _slots.begin()); 00085 00086 // negative UID means the slot is empty. 00087 bool slotAvailable = (*i < 0) && (_reservedSlots.find(slot) == _reservedSlots.end()); 00088 if ( slotAvailable ) 00089 { 00090 // record this UID in the new slot: 00091 *i = layer->getUID(); 00092 00093 // record the render order of this slot: 00094 if ( orderIndex >= (int)_order.size() ) 00095 { 00096 _order.resize( orderIndex + 1, -1 ); 00097 _order[orderIndex] = slot; 00098 } 00099 else 00100 { 00101 if (_order[orderIndex] == -1) 00102 _order[orderIndex] = slot; 00103 else 00104 _order.insert(_order.begin() + orderIndex, slot); 00105 } 00106 00107 found = true; 00108 break; 00109 } 00110 } 00111 00112 if ( !found ) 00113 { 00114 // put the UID in the next available slot (that's not reserved). 00115 while( _reservedSlots.find(_slots.size()) != _reservedSlots.end() ) 00116 _slots.push_back( -1 ); 00117 00118 slot = _slots.size(); 00119 _slots.push_back( layer->getUID() ); 00120 _order.push_back( _slots.size() - 1 ); 00121 } 00122 00123 OE_INFO << LC << "Allocated SLOT " << slot << "; primary slot for layer \"" << layer->getName() << "\"" << std::endl; 00124 } 00125 00126 void 00127 TextureLayout::assignSecondarySlot( ImageLayer* layer ) 00128 { 00129 int slot = -1; 00130 bool found = false; 00131 00132 for( TextureSlotVector::iterator i = _slots.begin(); i != _slots.end() && !found; ++i ) 00133 { 00134 slot = (int)(i - _slots.begin()); 00135 00136 // negative UID means the slot is empty. 00137 bool slotAvailable = (*i < 0) && (_reservedSlots.find(slot) == _reservedSlots.end()); 00138 if ( slotAvailable ) 00139 { 00140 // record this UID in the new slot: 00141 *i = layer->getUID(); 00142 found = true; 00143 break; 00144 } 00145 } 00146 00147 if ( !found ) 00148 { 00149 // put the UID in the next available slot (that's not reserved). 00150 while( _reservedSlots.find(_slots.size()) != _reservedSlots.end() ) 00151 _slots.push_back( -1 ); 00152 00153 slot = _slots.size(); 00154 _slots.push_back( layer->getUID() ); 00155 } 00156 00157 OE_INFO << LC << "Allocated SLOT " << slot << "; secondary slot for layer \"" << layer->getName() << "\"" << std::endl; 00158 } 00159 00160 void 00161 TextureLayout::applyMapModelChange( const MapModelChange& change, bool reserveSeconarySlotIfNecessary ) 00162 { 00163 if ( change.getAction() == MapModelChange::ADD_IMAGE_LAYER ) 00164 { 00165 assignPrimarySlot( change.getImageLayer(), change.getFirstIndex() ); 00166 00167 bool blendingOn = change.getImageLayer()->getImageLayerOptions().lodBlending() == true; 00168 _lodBlending[ change.getImageLayer()->getUID() ] = blendingOn; 00169 00170 if ( blendingOn && reserveSeconarySlotIfNecessary ) 00171 { 00172 assignSecondarySlot( change.getImageLayer() ); 00173 } 00174 } 00175 00176 else if ( change.getAction() == MapModelChange::REMOVE_IMAGE_LAYER ) 00177 { 00178 for( int which = 0; which <= 1; ++which ) 00179 { 00180 int slot = getSlot( change.getLayer()->getUID(), which ); 00181 if ( slot < 0 ) 00182 break; 00183 00184 _slots[slot] = -1; 00185 00186 if ( which == 0 ) // primary slot; remove from render order: 00187 { 00188 for( RenderOrderVector::iterator j = _order.begin(); j != _order.end(); ) 00189 { 00190 if ( *j == slot ) 00191 j = _order.erase( j ); 00192 else 00193 ++j; 00194 } 00195 } 00196 } 00197 } 00198 00199 else if ( change.getAction() == MapModelChange::MOVE_IMAGE_LAYER ) 00200 { 00201 int fromIndex = getOrder( change.getLayer()->getUID() ); 00202 int toIndex = change.getSecondIndex(); 00203 00204 if ( fromIndex != toIndex ) 00205 { 00206 int slot = _order[fromIndex]; 00207 _order.erase( _order.begin() + fromIndex ); 00208 _order.insert( _order.begin() + toIndex, slot ); 00209 } 00210 } 00211 00212 //OE_INFO << LC << "Layout Slots: " << std::endl; 00213 //for( int i=0; i<_slots.size(); ++i ) 00214 // OE_INFO << LC << " Slot " << i << ": uid=" << _slots[i] << ", order=" << getOrder(_slots[i]) << std::endl; 00215 //OE_INFO << LC << "Layout Order: " << std::endl; 00216 //for( int i=0; i<_order.size(); ++i ) 00217 // OE_INFO << LC << " Ordr " << i << ": slot=" << _order[i] << std::endl; 00218 } 00219 00220 void 00221 TextureLayout::setReservedSlots( const std::set<int>& reservedSlots ) 00222 { 00223 _reservedSlots = reservedSlots; 00224 } 00225 00226 bool 00227 TextureLayout::isSlotAvailable( int i ) const 00228 { 00229 if ( (i < (int)_slots.size() && _slots[i] < 0) || i >= (int)_slots.size() ) 00230 { 00231 if ( _reservedSlots.find(i) == _reservedSlots.end() ) 00232 { 00233 return true; 00234 } 00235 } 00236 return false; 00237 } 00238 00239 bool 00240 TextureLayout::containsSecondarySlots( unsigned maxSlotsToSearch ) const 00241 { 00242 for( int slot = 0; slot < (int)_slots.size() && slot < (int)maxSlotsToSearch; ++slot ) 00243 { 00244 UID uid = _slots[slot]; 00245 if ( getSlot(uid, 0) != slot ) 00246 return true; 00247 } 00248 return false; 00249 } 00250 00251 bool 00252 TextureLayout::isBlendingEnabled( UID layerUID ) const 00253 { 00254 std::map<UID,bool>::const_iterator i = _lodBlending.find(layerUID); 00255 return i != _lodBlending.end() ? i->second : false; 00256 } 00257 00258 //--------------------------------------------------------------------------- 00259 00260 TextureCompositor::TextureCompositor(const TerrainOptions& options) : 00261 osg::Referenced( true ), 00262 _tech( options.compositingTechnique().value() ), 00263 _options( options ), 00264 _forceTech( false ) 00265 { 00266 // for debugging: 00267 if ( _tech == TerrainOptions::COMPOSITING_AUTO && ::getenv( "OSGEARTH_COMPOSITOR_TECH" ) ) 00268 { 00269 TerrainOptions::CompositingTechnique oldTech = _tech; 00270 std::string t( ::getenv( "OSGEARTH_COMPOSITOR_TECH" ) ); 00271 if ( t == "TEXTURE_ARRAY" ) _tech = TerrainOptions::COMPOSITING_TEXTURE_ARRAY; 00272 else if ( t == "MULTITEXTURE_GPU" ) _tech = TerrainOptions::COMPOSITING_MULTITEXTURE_GPU; 00273 else if ( t == "MULTIPASS" ) _tech = TerrainOptions::COMPOSITING_MULTIPASS; 00274 if ( oldTech != _tech ) 00275 _forceTech = true; 00276 } 00277 00278 init(); 00279 } 00280 00281 bool 00282 TextureCompositor::reserveTextureImageUnit( int& out_unit ) 00283 { 00284 //todo: move this into the impls!! 00285 00286 out_unit = -1; 00287 00288 //TODO: this only supports GPU texturing.... 00289 unsigned maxUnits = osgEarth::Registry::instance()->getCapabilities().getMaxGPUTextureUnits(); 00290 00291 if ( _tech == TerrainOptions::COMPOSITING_MULTITEXTURE_GPU ) 00292 { 00293 Threading::ScopedWriteLock exclusiveLock( _layoutMutex ); 00294 00295 for( unsigned i=0; i<maxUnits; ++i ) 00296 { 00297 if ( _layout.isSlotAvailable(i) ) 00298 { 00299 out_unit = i; 00300 _reservedUnits.insert( i ); 00301 _layout.setReservedSlots( _reservedUnits ); // in multitexture, slots == units 00302 return true; 00303 } 00304 } 00305 00306 // all taken, return false. 00307 return false; 00308 } 00309 00310 else if ( _tech == TerrainOptions::COMPOSITING_TEXTURE_ARRAY ) 00311 { 00312 // texture array reserved slots 0 and 1 (for primary and blending) 00313 for( unsigned i=2; i<maxUnits; ++i ) // 0 and 1 always reserved. 00314 { 00315 if ( _reservedUnits.find( i ) == _reservedUnits.end() ) 00316 { 00317 out_unit = i; 00318 _reservedUnits.insert( i ); 00319 return true; 00320 } 00321 } 00322 00323 // all taken, return false. 00324 return false; 00325 } 00326 00327 else // multipass... all image layers are locked at unit 0 00328 { 00329 // search for an unused unit. 00330 for( unsigned i=1; i<maxUnits; ++i ) // start at 1 because unit 0 is always reserved 00331 { 00332 if ( _reservedUnits.find( i ) == _reservedUnits.end() ) 00333 { 00334 out_unit = i; 00335 _reservedUnits.insert( i ); 00336 return true; 00337 } 00338 } 00339 00340 // all taken, return false. 00341 return false; 00342 } 00343 } 00344 00345 void 00346 TextureCompositor::releaseTextureImageUnit( int unit ) 00347 { 00348 _reservedUnits.erase( unit ); 00349 00350 if ( _tech == TerrainOptions::COMPOSITING_MULTITEXTURE_GPU ) 00351 { 00352 Threading::ScopedWriteLock exclusiveLock( _layoutMutex ); 00353 _layout.setReservedSlots( _reservedUnits ); 00354 } 00355 } 00356 00357 void 00358 TextureCompositor::applyMapModelChange( const MapModelChange& change ) 00359 { 00360 Threading::ScopedWriteLock exclusiveLock( _layoutMutex ); 00361 00362 _layout.applyMapModelChange( 00363 change, 00364 _impl.valid() ? _impl->blendingRequiresSecondarySlot() : false ); 00365 } 00366 00367 bool 00368 TextureCompositor::supportsLayerUpdate() const 00369 { 00370 return _impl.valid() ? _impl->supportsLayerUpdate() : false; 00371 } 00372 00373 GeoImage 00374 TextureCompositor::prepareImage( const GeoImage& image, const GeoExtent& tileExtent ) const 00375 { 00376 return _impl.valid() ? _impl->prepareImage( image, tileExtent ) : GeoImage::INVALID; 00377 } 00378 00379 GeoImage 00380 TextureCompositor::prepareSecondaryImage( const GeoImage& image, const GeoExtent& tileExtent ) const 00381 { 00382 return _impl.valid() ? _impl->prepareSecondaryImage( image, tileExtent ) : GeoImage::INVALID; 00383 } 00384 00385 void 00386 TextureCompositor::applyLayerUpdate(osg::StateSet* stateSet, 00387 UID layerUID, 00388 const GeoImage& preparedImage, 00389 const TileKey& tileKey, 00390 osg::StateSet* parentStateSet) const 00391 { 00392 if ( _impl.valid() ) 00393 { 00394 Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex ); 00395 _impl->applyLayerUpdate( stateSet, layerUID, preparedImage, tileKey, 00396 _layout, parentStateSet ); 00397 } 00398 } 00399 00400 void 00401 TextureCompositor::applyLayerRemoval(osg::StateSet* stateSet, 00402 UID layerUID ) const 00403 { 00404 if ( _impl.valid() ) 00405 _impl->applyLayerRemoval( stateSet, layerUID ); 00406 } 00407 00408 bool 00409 TextureCompositor::requiresUnitTextureSpace() const 00410 { 00411 return _impl.valid() ? _impl->requiresUnitTextureSpace() : false; 00412 } 00413 00414 bool 00415 TextureCompositor::usesShaderComposition() const 00416 { 00417 return _impl.valid() ? _impl->usesShaderComposition() : false; 00418 } 00419 00420 void 00421 TextureCompositor::updateMasterStateSet( osg::StateSet* stateSet ) const 00422 { 00423 if ( _impl.valid() ) 00424 { 00425 Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex ); 00426 _impl->updateMasterStateSet( stateSet, _layout ); 00427 } 00428 } 00429 00430 void 00431 TextureCompositor::assignTexCoordArray(osg::Geometry* geom, 00432 UID layerUID, 00433 osg::Vec2Array* texCoords ) const 00434 { 00435 if ( geom && texCoords ) 00436 { 00437 if ( _tech == TerrainOptions::COMPOSITING_MULTIPASS ) 00438 { 00439 geom->setTexCoordArray( 0, texCoords ); 00440 } 00441 else 00442 { 00443 int slot; 00444 { 00445 Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex ); 00446 slot = _layout.getSlot( layerUID ); 00447 } 00448 if ( slot >= 0 ) 00449 geom->setTexCoordArray( slot, texCoords ); 00450 } 00451 } 00452 } 00453 00454 int 00455 TextureCompositor::getRenderOrder( UID layerUID ) const 00456 { 00457 Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex ); 00458 return _layout.getOrder( layerUID ); 00459 } 00460 00461 osg::Shader* 00462 TextureCompositor::createSamplerFunction(UID layerUID, 00463 const std::string& functionName, 00464 osg::Shader::Type type ) const 00465 { 00466 osg::Shader* result = 0L; 00467 if ( _impl.valid() ) 00468 { 00469 Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex ); 00470 result = _impl->createSamplerFunction( layerUID, functionName, type, _layout ); 00471 } 00472 00473 if ( !result ) 00474 { 00475 std::string fname = !functionName.empty() ? functionName : "defaultSamplerFunction"; 00476 std::stringstream buf; 00477 buf << "vec4 " << functionName << "() { \n return vec4(0,0,0,0); \n } \n"; 00478 std::string str = buf.str(); 00479 result = new osg::Shader( type, str ); 00480 } 00481 00482 return result; 00483 } 00484 00485 void 00486 TextureCompositor::init() 00487 { 00488 if ( _impl.valid() ) // double-check pattern 00489 { 00490 return; // already initialized 00491 } 00492 00493 bool isAuto = _tech == TerrainOptions::COMPOSITING_AUTO; 00494 00495 const Capabilities& caps = Registry::instance()->getCapabilities(); 00496 00497 // MULTITEXTURE_GPU is the current default. 00498 00499 if (_tech == TerrainOptions::COMPOSITING_MULTITEXTURE_GPU || 00500 (isAuto && caps.supportsGLSL(1.20f) && caps.supportsMultiTexture()) ) 00501 { 00502 _tech = TerrainOptions::COMPOSITING_MULTITEXTURE_GPU; 00503 _impl = new TextureCompositorMultiTexture( true, _options ); 00504 OE_INFO << LC << "Compositing technique = MULTITEXTURE/GPU" << std::endl; 00505 } 00506 00507 #if OSG_VERSION_GREATER_OR_EQUAL( 2, 9, 8 ) 00508 00509 else 00510 if (_tech == TerrainOptions::COMPOSITING_TEXTURE_ARRAY || 00511 (isAuto && caps.supportsGLSL(1.30f) && caps.supportsTextureArrays()) ) 00512 { 00513 _tech = TerrainOptions::COMPOSITING_TEXTURE_ARRAY; 00514 _impl = new TextureCompositorTexArray( _options ); 00515 OE_INFO << LC << "Compositing technique = TEXTURE ARRAY" << std::endl; 00516 } 00517 00518 #endif // OSG_VERSION_GREATER_OR_EQUAL( 2, 9, 8 ) 00519 00520 else 00521 if ( _tech == TerrainOptions::COMPOSITING_MULTITEXTURE_FFP || (isAuto && caps.supportsMultiTexture()) ) 00522 { 00523 _tech = TerrainOptions::COMPOSITING_MULTITEXTURE_FFP; 00524 _impl = new TextureCompositorMultiTexture( false, _options ); 00525 OE_INFO << LC << "Compositing technique = MULTITEXTURE/FFP" << std::endl; 00526 } 00527 00528 // Fallback of last resort. The implementation is actually a NO-OP for multipass mode. 00529 else 00530 { 00531 _tech = TerrainOptions::COMPOSITING_MULTIPASS; 00532 _impl = 0L; 00533 OE_INFO << LC << "Compositing technique = MULTIPASS" << std::endl; 00534 } 00535 }