osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/TextureCompositorTexArray.cpp

Go to the documentation of this file.
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 )
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines