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 <osgEarth/TextureCompositorTexArray> 00020 00021 // only in newer OSG versions. 00022 #if OSG_VERSION_GREATER_OR_EQUAL( 2, 9, 8 ) 00023 00024 #include <sstream> 00025 00026 #include <osgEarth/ImageUtils> 00027 #include <osgEarth/Registry> 00028 #include <osgEarth/ShaderComposition> 00029 #include <osgEarth/SparseTexture2DArray> 00030 #include <osgEarth/ShaderUtils> 00031 00032 using namespace osgEarth; 00033 00034 #define LC "[TextureCompositorTexArray] " 00035 00036 00037 //------------------------------------------------------------------------ 00038 00039 namespace 00040 { 00041 static osg::Shader* 00042 s_createTextureFragShaderFunction( const TextureLayout& layout, bool blending, float blendTime ) 00043 { 00044 int numSlots = layout.getMaxUsedSlot() + 1; 00045 00046 std::stringstream buf; 00047 00048 buf << "#version 130 \n" 00049 << "#extension GL_EXT_gpu_shader4 : enable \n"; 00050 00051 00052 if ( blending ) 00053 { 00054 buf << "#extension GL_ARB_shader_texture_lod : enable \n" 00055 << "uniform float osgearth_SlotStamp[ " << numSlots << "]; \n" 00056 << "uniform float osg_FrameTime;\n" 00057 << "uniform float osgearth_LODRangeFactor;\n\n"; 00058 } 00059 00060 buf << "uniform sampler2DArray tex0; \n"; 00061 00062 if ( blending ) 00063 buf << "uniform sampler2DArray tex1;\n"; 00064 00065 buf << "uniform float region[ " << 8*numSlots << "]; \n" 00066 << "uniform float osgearth_ImageLayerOpacity[" << numSlots << "]; \n" 00067 << "uniform bool osgearth_ImageLayerEnabled[" << numSlots << "]; \n" 00068 << "uniform float osgearth_ImageLayerRange[" << 2*numSlots << "]; \n" 00069 << "uniform float osgearth_ImageLayerAttenuation; \n" 00070 << "varying float osgearth_CameraRange; \n" 00071 00072 << "void osgearth_frag_applyTexturing( inout vec4 color ) \n" 00073 << "{ \n" 00074 << " vec3 color3 = color.rgb; \n" 00075 << " float u, v, dmin, dmax, atten_min, atten_max, age; \n" 00076 << " vec4 texel; \n"; 00077 00078 const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots(); 00079 const TextureLayout::RenderOrderVector& order = layout.getRenderOrder(); 00080 00081 for( unsigned int i = 0; i < order.size(); ++i ) 00082 { 00083 int slot = order[i]; 00084 int q = 2 * i; 00085 int r = 8 * slot; 00086 UID uid = slots[slot]; 00087 00088 buf << " if (osgearth_ImageLayerEnabled["<< i << "]) \n" 00089 << " { \n" 00090 << " u = region["<< r <<"] + (region["<< r+2 <<"] * gl_TexCoord[0].s); \n" 00091 << " v = region["<< r+1 <<"] + (region["<< r+3 <<"] * gl_TexCoord[0].t); \n" 00092 << " dmin = osgearth_CameraRange - osgearth_ImageLayerRange["<< q << "]; \n" 00093 << " dmax = osgearth_CameraRange - osgearth_ImageLayerRange["<< q+1 <<"]; \n" 00094 << " if (dmin >= 0 && dmax <= 0.0) \n" 00095 << " { \n" 00096 << " atten_max = -clamp( dmax, -osgearth_ImageLayerAttenuation, 0 ) / osgearth_ImageLayerAttenuation; \n" 00097 << " atten_min = clamp( dmin, 0, osgearth_ImageLayerAttenuation ) / osgearth_ImageLayerAttenuation; \n"; 00098 00099 if ( layout.isBlendingEnabled(uid) ) 00100 { 00101 float invBlendTime = 1.0f/blendTime; 00102 00103 buf << " age = "<< invBlendTime << " * min( "<< blendTime << ", osg_FrameTime - osgearth_SlotStamp[" << slot << "] ); \n" 00104 << " age = clamp(age, 0.0, 1.0);\n" 00105 << " float pu, pv;\n" 00106 << " pu = region["<< r+4 <<"] + (region["<< r+6 <<"] * gl_TexCoord[0].s); \n" 00107 << " pv = region["<< r+5 <<"] + (region["<< r+7 <<"] * gl_TexCoord[0].t); \n" 00108 00109 << " vec3 texCoord = vec3(pu, pv, " << slot <<");\n;\n" 00110 << " vec4 texel0 = texture2DArray( tex0, vec3(u, v, " << slot << ") );\n" 00111 << " vec4 texel1 = texture2DArray( tex1, vec3(pu, pv, " << slot << ") );\n" 00112 << " float mixval = age * osgearth_LODRangeFactor;\n" 00113 00114 // pre-multiply alpha before mixing: 00115 << " texel0.rgb *= texel0.a; \n" 00116 << " texel1.rgb *= texel1.a; \n" 00117 << " texel = mix(texel1, texel0, mixval); \n" 00118 00119 // revert to non-pre-multiplies alpha (assumes openGL state uses non-pre-mult alpha) 00120 << " if (texel.a > 0.0) { \n" 00121 << " texel.rgb /= texel.a; \n" 00122 << " } \n"; 00123 } 00124 else 00125 { 00126 buf << " texel = texture2DArray( tex0, vec3(u,v,"<< slot <<") ); \n"; 00127 } 00128 00129 buf << " color3 = mix(color3, texel.rgb, texel.a * osgearth_ImageLayerOpacity["<< i <<"] * atten_max * atten_min); \n" 00130 << " } \n" 00131 << " } \n" 00132 ; 00133 } 00134 00135 buf << " color = vec4(color3.rgb, color.a); \n" 00136 << "} \n"; 00137 00138 std::string str = buf.str(); 00139 return new osg::Shader( osg::Shader::FRAGMENT, str ); 00140 } 00141 } 00142 00143 //------------------------------------------------------------------------ 00144 00145 namespace 00146 { 00147 osg::Texture2DArray* 00148 s_getTexture( osg::StateSet* stateSet, const TextureLayout& layout, 00149 int unit, unsigned textureSize ) 00150 { 00151 osg::Texture2DArray* tex = static_cast<osg::Texture2DArray*>( 00152 stateSet->getTextureAttribute( unit, osg::StateAttribute::TEXTURE ) ); 00153 00154 // if the texture array doesn't exist, create it anew. 00155 if ( !tex ) 00156 { 00157 tex = new SparseTexture2DArray(); 00158 tex->setSourceFormat( GL_RGBA ); 00159 tex->setInternalFormat( GL_RGBA8 ); 00160 tex->setTextureWidth( textureSize ); 00161 tex->setTextureHeight( textureSize ); 00162 00163 // configure the mipmapping 00164 tex->setMaxAnisotropy(16.0f); 00165 tex->setResizeNonPowerOfTwoHint(false); 00166 tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); 00167 tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); 00168 00169 // configure the wrapping 00170 tex->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE); 00171 tex->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP_TO_EDGE); 00172 00173 stateSet->setTextureAttribute( unit, tex, osg::StateAttribute::ON ); 00174 } 00175 00176 // grow the texture array if necessary. 00177 int requiredDepth = layout.getMaxUsedSlot() + 1; 00178 if ( tex->getTextureDepth() < requiredDepth ) 00179 tex->setTextureDepth( requiredDepth ); 00180 00181 const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots(); 00182 00183 // null out any empty slots (to save memory, i guess) 00184 for( int i=0; i < tex->getTextureDepth(); ++i ) 00185 { 00186 if ( i < (int)slots.size() && slots[i] < 0 ) 00187 tex->setImage( i, 0L ); 00188 } 00189 00190 return tex; 00191 } 00192 00193 osg::Uniform* ensureSampler(osg::StateSet* ss, int unit) 00194 { 00195 std::stringstream sstream; 00196 sstream << "tex" << unit; 00197 std::string str = sstream.str(); 00198 osg::ref_ptr<osg::Uniform> sampler = ss->getUniform(str); 00199 int samplerUnit = -1; 00200 if (sampler.valid() && sampler->getType() == osg::Uniform::SAMPLER_2D_ARRAY) 00201 sampler->get(samplerUnit); 00202 if (samplerUnit == -1 || samplerUnit != unit) 00203 { 00204 sampler = new osg::Uniform(osg::Uniform::SAMPLER_2D_ARRAY, str); 00205 sampler->set(unit); 00206 ss->addUniform(sampler.get()); 00207 } 00208 return sampler.get(); 00209 } 00210 00211 void assignImage(osg::Texture2DArray* texture, int slot, osg::Image* image) 00212 { 00213 // We have to dirty() the image because otherwise the texture2d 00214 // array implementation will not recognize it as new data. 00215 image->dirty(); 00216 texture->setImage( slot, image ); 00217 00218 if (ImageUtils::isPowerOfTwo( image ) && !(!image->isMipmap() && ImageUtils::isCompressed(image))) 00219 { 00220 if ( texture->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR_MIPMAP_LINEAR ) 00221 texture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); 00222 } 00223 else if ( texture->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR ) 00224 { 00225 texture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR ); 00226 } 00227 } 00228 00229 void getImageTransform( 00230 const GeoExtent& tileExtent, 00231 const GeoExtent& imageExtent, 00232 osg::Vec4& transform) 00233 { 00234 if (!tileExtent.isValid() || !imageExtent.isValid()) 00235 return; 00236 00237 transform[0] = (tileExtent.xMin() - imageExtent.xMin()) / imageExtent.width(); 00238 transform[1] = (tileExtent.yMin() - imageExtent.yMin()) / imageExtent.height(); 00239 transform[2] = tileExtent.width() / imageExtent.width(); 00240 transform[3] = tileExtent.height() / imageExtent.height(); 00241 } 00242 } 00243 00244 //------------------------------------------------------------------------ 00245 00246 TextureCompositorTexArray::TextureCompositorTexArray( const TerrainOptions& options ) : 00247 _lodTransitionTime( *options.lodTransitionTime() ) 00248 { 00249 //nop 00250 } 00251 00252 GeoImage 00253 TextureCompositorTexArray::prepareImage( const GeoImage& layerImage, const GeoExtent& tileExtent, unsigned textureSize ) const 00254 { 00255 const osg::Image* image = layerImage.getImage(); 00256 if (!image) 00257 return GeoImage::INVALID; 00258 00259 if (image->getPixelFormat() != GL_RGBA || 00260 image->getInternalTextureFormat() != GL_RGBA8 || 00261 image->s() != textureSize || 00262 image->t() != textureSize ) 00263 { 00264 // Because all tex2darray layers must be identical in format, let's use RGBA. 00265 osg::ref_ptr<osg::Image> newImage = ImageUtils::convertToRGBA8( image ); 00266 00267 // TODO: revisit. For now let's just settle on 256 (again, all layers must be the same size) 00268 if ( image->s() != textureSize || image->t() != textureSize ) 00269 { 00270 osg::ref_ptr<osg::Image> resizedImage; 00271 if ( ImageUtils::resizeImage( newImage.get(), textureSize, textureSize, resizedImage ) ) 00272 newImage = resizedImage.get(); 00273 } 00274 00275 return GeoImage( newImage.get(), layerImage.getExtent() ); 00276 } 00277 else 00278 { 00279 return layerImage; 00280 } 00281 } 00282 00283 void 00284 TextureCompositorTexArray::applyLayerUpdate(osg::StateSet* stateSet, 00285 UID layerUID, 00286 const GeoImage& preparedImage, 00287 const TileKey& tileKey, 00288 const TextureLayout& layout, 00289 osg::StateSet* parentStateSet) const 00290 { 00291 GeoExtent tileExtent(tileKey.getExtent()); 00292 int slot = layout.getSlot( layerUID ); 00293 if ( slot < 0 ) 00294 return; // means the layer no longer exists 00295 00296 // access the texture array, creating or growing it if necessary: 00297 osg::Texture2DArray* texture = s_getTexture( stateSet, layout, 0, 00298 textureSize() ); 00299 ensureSampler( stateSet, 0 ); 00300 // assign the new image at the proper position in the texture array. 00301 osg::Image* image = preparedImage.getImage(); 00302 assignImage(texture, slot, image); 00303 00304 // update the region uniform to reflect the geo extent of the image: 00305 const GeoExtent& imageExtent = preparedImage.getExtent(); 00306 osg::Vec4 tileTransform; 00307 getImageTransform(tileExtent, imageExtent, tileTransform); 00308 00309 // access the region uniform, creating or growing it if necessary: 00310 ArrayUniform regionUni( "region", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot()+1 ); 00311 if ( regionUni.isValid() ) 00312 { 00313 int layerOffset = slot * 8; 00314 for (int i = 0; i < 4; ++i) 00315 regionUni.setElement( layerOffset + i, tileTransform[i]); 00316 //region->dirty(); 00317 } 00318 00319 if ( layout.isBlendingEnabled( layerUID ) && regionUni.isValid() ) 00320 { 00321 osg::Uniform* secondarySampler = ensureSampler( stateSet, 1 ); 00322 osg::Texture2DArray* parentTexture = 0; 00323 const unsigned parentLayerOffset = slot * 8 + 4; 00324 if ( parentStateSet ) 00325 { 00326 ArrayUniform parentRegion( "region", osg::Uniform::FLOAT, parentStateSet, layout.getMaxUsedSlot()+1 ); 00327 00328 //osg::Uniform* parentRegion = s_getRegionUniform( parentStateSet, 00329 // layout ); 00330 GeoExtent parentExtent(tileKey.createParentKey().getExtent()); 00331 float widthRatio, heightRatio; 00332 parentRegion.getElement(slot * 8 + 2, widthRatio); 00333 parentRegion.getElement(slot * 8 + 3, heightRatio); 00334 float parentImageWidth = parentExtent.width() / widthRatio; 00335 float parentImageHeight = parentExtent.height() / heightRatio; 00336 float xRatio, yRatio; 00337 parentRegion.getElement(slot * 8, xRatio); 00338 parentRegion.getElement(slot * 8 + 1, yRatio); 00339 float ParentImageXmin = parentExtent.xMin() - xRatio * parentImageWidth; 00340 float ParentImageYmin = parentExtent.yMin() - yRatio * parentImageHeight; 00341 regionUni.setElement(parentLayerOffset, 00342 static_cast<float>((tileExtent.xMin() - ParentImageXmin) / parentImageWidth)); 00343 regionUni.setElement(parentLayerOffset + 1, 00344 static_cast<float>((tileExtent.yMin() - ParentImageYmin) / parentImageHeight)); 00345 regionUni.setElement(parentLayerOffset + 2, 00346 static_cast<float>(tileExtent.width() / parentImageWidth)); 00347 regionUni.setElement(parentLayerOffset + 3, 00348 static_cast<float>(tileExtent.height() / parentImageHeight)); 00349 //regionUni.dirty(); 00350 parentTexture = static_cast<osg::Texture2DArray*>(parentStateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE)); 00351 } 00352 else 00353 { 00354 // setting the parent transform values to -1 disabled blending for this layer. #hack -gw 00355 for (int i = 0; i < 4; ++i) 00356 regionUni.setElement(parentLayerOffset + i, tileTransform[i]); 00357 } 00358 00359 if (parentTexture) 00360 stateSet->setTextureAttribute(1, parentTexture, osg::StateAttribute::ON); 00361 else 00362 secondarySampler->set(0); 00363 00364 // update the timestamp on the image layer to support fade-in blending. 00365 float now = (float)osg::Timer::instance()->delta_s( osg::Timer::instance()->getStartTick(), osg::Timer::instance()->tick() ); 00366 ArrayUniform stampUniform( "osgearth_SlotStamp", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot()+1 ); 00367 stampUniform.setElement( slot, now ); 00368 } 00369 } 00370 00371 void 00372 TextureCompositorTexArray::updateMasterStateSet( osg::StateSet* stateSet, const TextureLayout& layout ) const 00373 { 00374 VirtualProgram* vp = static_cast<VirtualProgram*>( stateSet->getAttribute(osg::StateAttribute::PROGRAM) ); 00375 00376 vp->setShader( 00377 "osgearth_frag_applyTexturing", 00378 s_createTextureFragShaderFunction(layout, true, _lodTransitionTime ) ); 00379 } 00380 00381 osg::Shader* 00382 TextureCompositorTexArray::createSamplerFunction(UID layerUID, 00383 const std::string& functionName, 00384 osg::Shader::Type type, 00385 const TextureLayout& layout ) const 00386 { 00387 osg::Shader* result = 0L; 00388 00389 int slot = layout.getSlot( layerUID ); 00390 if ( slot >= 0 ) 00391 { 00392 int r = 8 * slot; 00393 00394 std::string texCoord = 00395 type == osg::Shader::VERTEX ? "gl_MultiTexCoord0" : 00396 "gl_TexCoord[0]"; 00397 00398 std::stringstream buf; 00399 00400 buf << "#version 130 \n" 00401 << "#extension GL_EXT_gpu_shader4 : enable \n" 00402 00403 << "uniform sampler2DArray tex0; \n" 00404 << "uniform float[] region; \n" 00405 00406 << "vec4 " << functionName << "() \n" 00407 << "{ \n" 00408 << " float u = region["<< r <<"] + (region["<< r+2 <<"] * "<< texCoord <<".s); \n" 00409 << " float v = region["<< r+1 <<"] + (region["<< r+3 <<"] * "<< texCoord <<".t); \n" 00410 << " return texture2DArray( tex0, vec3(u,v,"<< slot <<") ); \n" 00411 << "} \n"; 00412 00413 std::string str = buf.str(); 00414 result = new osg::Shader( type, str ); 00415 } 00416 return result; 00417 } 00418 00419 //------------------------------------------------------------------------ 00420 00421 #endif // OSG_VERSION_GREATER_OR_EQUAL( 2, 9, 8 )