osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/OverlayDecorator.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/OverlayDecorator>
00020 #include <osgEarth/FindNode>
00021 #include <osgEarth/Registry>
00022 #include <osgEarth/TextureCompositor>
00023 #include <osg/Texture2D>
00024 #include <osg/TexEnv>
00025 #include <osg/BlendFunc>
00026 #include <osg/ComputeBoundsVisitor>
00027 #include <osgShadow/ConvexPolyhedron>
00028 #include <osgUtil/LineSegmentIntersector>
00029 #include <iomanip>
00030 #include <stack>
00031 
00032 #define LC "[OverlayDecorator] "
00033 
00034 using namespace osgEarth;
00035 
00036 //---------------------------------------------------------------------------
00037 
00038 namespace
00039 {
00043     class MyConvexPolyhedron : public osgShadow::ConvexPolyhedron
00044     {
00045     public:       
00046         bool intersects(const osg::BoundingSphere& bs) const
00047         {
00048             for( Faces::const_iterator i = _faces.begin(); i != _faces.end(); ++i )
00049             {
00050                 osg::Plane up = i->plane;
00051                 up.makeUnitLength();
00052                 if ( up.distance( bs.center() ) < -bs.radius() )
00053                     return false;
00054             }
00055             return true;
00056         }
00057 
00058         bool intersects(const osg::BoundingBox& box) const
00059         {
00060             for( Faces::const_iterator i = _faces.begin(); i != _faces.end(); ++i )
00061             {
00062                 osg::Plane up = i->plane;
00063                 up.makeUnitLength();
00064 
00065                 if ( up.intersect(box) < 0 )
00066                     return false;
00067             }
00068             return true;
00069         }
00070     };
00071 
00080     struct CoarsePolytopeIntersector : public osg::NodeVisitor
00081     {
00082         CoarsePolytopeIntersector(const MyConvexPolyhedron& polytope, osg::BoundingBox& out_bbox)
00083             : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
00084               _bbox(out_bbox),
00085               _original( polytope ),
00086               _coarse( true )
00087         {
00088             _polytopeStack.push( polytope );
00089             _matrixStack.push( osg::Matrix::identity() );
00090         }
00091 
00092         void apply( osg::Node& node )
00093         {
00094             const osg::BoundingSphere& bs = node.getBound();
00095             if ( _polytopeStack.top().intersects( bs ) )
00096             {
00097                 traverse( node );
00098             }
00099         }
00100 
00101         void apply( osg::Geode& node )
00102         {
00103             const osg::BoundingSphere& bs = node.getBound();
00104 
00105             if ( _polytopeStack.top().intersects( bs ) )
00106             {
00107                 if ( _coarse )
00108                 {
00109                     _bbox.expandBy(
00110                         osg::BoundingSphere( bs.center() * _matrixStack.top(), bs.radius() ) );
00111                 }
00112                 else
00113                 {
00114                     for( unsigned i=0; i < node.getNumDrawables(); ++i )
00115                     {
00116                         applyDrawable( node.getDrawable(i) );
00117                     }
00118                 }
00119             }
00120         }
00121 
00122         void applyDrawable( osg::Drawable* drawable )
00123         {
00124             const osg::BoundingBox& box = drawable->getBound();
00125 
00126             if ( _polytopeStack.top().intersects( box ) )
00127             {
00128                 osg::Vec3d bmin = osg::Vec3(box.xMin(), box.yMin(), box.zMin()) * _matrixStack.top();
00129                 osg::Vec3d bmax = osg::Vec3(box.xMax(), box.yMax(), box.zMax()) * _matrixStack.top();
00130                   
00131                 _bbox.expandBy( osg::BoundingBox(bmin, bmax) );
00132             }
00133         }
00134 
00135         void apply( osg::Transform& transform )
00136         {
00137             osg::Matrixd matrix;
00138             transform.computeLocalToWorldMatrix( matrix, this );
00139 
00140             _matrixStack.push( matrix );
00141             _polytopeStack.push( _original );
00142             _polytopeStack.top().transform( osg::Matrixd::inverse( matrix ), matrix );
00143 
00144             traverse(transform);
00145 
00146             _matrixStack.pop();
00147             _polytopeStack.pop();
00148         }
00149 
00150         osg::BoundingBox& _bbox;
00151         MyConvexPolyhedron _original;
00152         std::stack<MyConvexPolyhedron> _polytopeStack;
00153         std::stack<osg::Matrixd> _matrixStack;
00154         bool _coarse;
00155     };
00156 
00166     void
00167     getMinMaxExtentInSilhouette(const osg::Vec3d& cam, const osg::Vec3d& look, 
00168                                 std::vector<osg::Vec3d>& verts,
00169                                 double& out_eMin, double& out_eMax )
00170     {
00171         double minSqrDist2D = DBL_MAX;
00172         double maxSqrDist2D = -DBL_MAX;
00173         osg::Plane plane( look, cam );
00174 
00175         for( std::vector<osg::Vec3d>::iterator i = verts.begin(); i != verts.end(); ++i )
00176         {
00177             osg::Vec3d& point = *i;
00178 
00179             // project the vert onto the camera plane:
00180             double signedDist = plane.distance( point );
00181             point += (-plane.getNormal() * signedDist);
00182 
00183             // then calculate the 2D distance to the camera:
00184             double sqrDist2D = (cam-point).length2();
00185             if ( sqrDist2D > maxSqrDist2D )
00186                 maxSqrDist2D = sqrDist2D;
00187             if ( sqrDist2D < minSqrDist2D )
00188                 minSqrDist2D = sqrDist2D;
00189         }
00190 
00191         out_eMin = sqrt( minSqrDist2D );
00192         out_eMax = sqrt( maxSqrDist2D );
00193     }
00194     
00198     void
00199     getMinMaxExtentInSilhouette(const osg::Vec3d& cam, const osg::Vec3d& look, 
00200                                 const osg::BoundingBox& bbox,
00201                                 double& out_eMin, double& out_eMax )
00202     {
00203         std::vector<osg::Vec3d> verts(8);
00204         verts[0].set( bbox.xMin(), bbox.yMin(), bbox.zMin() );
00205         verts[1].set( bbox.xMin(), bbox.yMin(), bbox.zMax() );
00206         verts[2].set( bbox.xMin(), bbox.yMax(), bbox.zMin() );
00207         verts[3].set( bbox.xMin(), bbox.yMax(), bbox.zMax() );
00208         verts[4].set( bbox.xMax(), bbox.yMin(), bbox.zMin() );
00209         verts[5].set( bbox.xMax(), bbox.yMin(), bbox.zMax() );
00210         verts[6].set( bbox.xMax(), bbox.yMax(), bbox.zMin() );
00211         verts[7].set( bbox.xMax(), bbox.yMax(), bbox.zMax() );
00212         getMinMaxExtentInSilhouette( cam, look, verts, out_eMin, out_eMax );
00213     }
00214 }
00215 
00216 //---------------------------------------------------------------------------
00217 
00218 OverlayDecorator::OverlayDecorator() :
00219 _textureUnit  ( 1 ),
00220 _textureSize  ( 1024 ),
00221 _useShaders   ( false ),
00222 _useWarping   ( false ),
00223 _warp         ( 1.0f ),
00224 _visualizeWarp( false ),
00225 _mipmapping   ( true ),
00226 _rttBlending  ( true )
00227 {
00228     // nop
00229 }
00230 
00231 void
00232 OverlayDecorator::reinit()
00233 {
00234     if ( !_engine.valid() ) return;
00235 
00236     if ( _overlayGraph.valid() )
00237     {
00238         // apply the user-request texture unit, if applicable:
00239         if ( _explicitTextureUnit.isSet() )
00240         {
00241             if ( !_textureUnit.isSet() || *_textureUnit != *_explicitTextureUnit )
00242             {
00243                 _textureUnit = *_explicitTextureUnit;
00244             }
00245         }
00246 
00247         // otherwise, automatically allocate a texture unit if necessary:
00248         else if ( !_textureUnit.isSet() && _useShaders )
00249         {
00250             int texUnit;
00251             if ( _engine->getTextureCompositor()->reserveTextureImageUnit( texUnit ) )
00252             {
00253                 _textureUnit = texUnit;
00254                 OE_INFO << LC << "Reserved texture image unit " << *_textureUnit << std::endl;
00255             }
00256             else
00257             {
00258                 OE_WARN << LC << "Uh oh, no texture image units available." << std::endl;
00259             }
00260         }
00261 
00262         if ( _textureUnit.isSet() )
00263         {
00264             _projTexture = new osg::Texture2D();
00265             _projTexture->setTextureSize( *_textureSize, *_textureSize );
00266             _projTexture->setInternalFormat( GL_RGBA8 );
00267             _projTexture->setSourceFormat( GL_RGBA );
00268             _projTexture->setSourceType( GL_UNSIGNED_BYTE );
00269             _projTexture->setFilter( osg::Texture::MIN_FILTER, _mipmapping? osg::Texture::LINEAR_MIPMAP_LINEAR : osg::Texture::LINEAR );
00270             _projTexture->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR );
00271             _projTexture->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER );
00272             _projTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER );
00273             _projTexture->setWrap( osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_BORDER );
00274             _projTexture->setBorderColor( osg::Vec4(0,0,0,0) );
00275 
00276             // set up the RTT camera:
00277             _rttCamera = new osg::Camera();
00278             _rttCamera->setClearColor( osg::Vec4f(0,0,0,0) );
00279             // this ref frame causes the RTT to inherit its viewpoint from above (in order to properly
00280             // process PagedLOD's etc. -- it doesn't affect the perspective of the RTT camera though)
00281             _rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT );
00282             _rttCamera->setViewport( 0, 0, *_textureSize, *_textureSize );
00283             _rttCamera->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR );
00284             _rttCamera->setRenderOrder( osg::Camera::PRE_RENDER );
00285             _rttCamera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
00286             _rttCamera->attach( osg::Camera::COLOR_BUFFER, _projTexture.get(), 0, 0, _mipmapping );
00287             _rttCamera->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
00288 
00289             if ( _rttBlending )
00290             {
00291                 osg::BlendFunc* blendFunc = new osg::BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
00292                 _rttCamera->getOrCreateStateSet()->setAttributeAndModes(blendFunc, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
00293             }
00294             else
00295             {
00296                 _rttCamera->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
00297             }
00298             //Enable blending on the RTT camera with pre-multiplied alpha.
00299             //osg::BlendFunc* blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
00300                 
00301 
00302             // texture coordinate generator:
00303             _texGenNode = new osg::TexGenNode();
00304             _texGenNode->setTextureUnit( *_textureUnit );
00305             _texGenNode->getTexGen()->setMode( osg::TexGen::EYE_LINEAR );
00306             
00307             // attach the overlay graph to the RTT camera.
00308             if ( _overlayGraph.valid() && ( _overlayGraph->getNumParents() == 0 || _overlayGraph->getParent(0) != _rttCamera.get() ))
00309             {
00310                 if ( _rttCamera->getNumChildren() > 0 )
00311                     _rttCamera->replaceChild( 0, _overlayGraph.get() );
00312                 else
00313                     _rttCamera->addChild( _overlayGraph.get() );
00314             }
00315         }
00316     }
00317 
00318     // assemble the subgraph stateset:
00319     _subgraphStateSet = new osg::StateSet();
00320 
00321     if ( _overlayGraph.valid() && _textureUnit.isSet() )
00322     {
00323         // set up the subgraph to receive the projected texture:
00324         _subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON );
00325         _subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON );
00326         _subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON );
00327         _subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON );
00328         _subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, _projTexture.get(), osg::StateAttribute::ON );
00329 
00330         // decalling (note: this has no effect when using shaders.. remove? -gw)
00331         //osg::TexEnv* env = new osg::TexEnv();
00332         //env->setMode( osg::TexEnv::DECAL );
00333         //_subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, env, osg::StateAttribute::ON );
00334         
00335         // set up the shaders
00336         if ( _useShaders )
00337         {            
00338             initSubgraphShaders( _subgraphStateSet.get() );
00339             initRTTShaders( _rttCamera->getOrCreateStateSet() );
00340 
00341             _warpUniform = this->getOrCreateStateSet()->getOrCreateUniform( "warp", osg::Uniform::FLOAT );
00342             _warpUniform->set( 1.0f );
00343         }
00344     }
00345 }
00346 
00347 void
00348 OverlayDecorator::initRTTShaders( osg::StateSet* set )
00349 {
00350     //TODO: convert this to VP so the overlay graph can use shadercomp too.
00351     osg::Program* program = new osg::Program();
00352     program->setName( "OverlayDecorator RTT shader" );
00353     set->setAttributeAndModes( program, osg::StateAttribute::ON );
00354 
00355     std::stringstream buf;
00356     buf << "#version 110 \n";
00357 
00358     if ( _useWarping )
00359     {
00360         buf << "uniform float warp; \n"
00361 
00362             // because the built-in pow() is busted
00363             << "float mypow( in float x, in float y ) \n"
00364             << "{ \n"
00365             << "    return x/(x+y-y*x); \n"
00366             << "} \n"
00367 
00368             << "vec4 warpVertex( in vec4 src ) \n"
00369             << "{ \n"
00370             //      normalize to [-1..1], then take the absolute values since we
00371             //      want to apply the warping in [0..1] on each side of zero:
00372             << "    vec2 srct = vec2( abs(src.x)/src.w, abs(src.y)/src.w ); \n"
00373             << "    vec2 sign = vec2( src.x > 0.0 ? 1.0 : -1.0, src.y > 0.0 ? 1.0 : -1.0 ); \n"
00374 
00375             //      apply the deformation using a "deceleration" curve:
00376             << "    vec2 srcp = vec2( 1.0-mypow(1.0-srct.x,warp), 1.0-mypow(1.0-srct.y,warp) ); \n"
00377 
00378             //      re-apply the sign. no need to un-normalize, just use w=1 instead
00379             << "    return vec4( sign.x*srcp.x, sign.y*srcp.y, src.z/src.w, 1.0 ); \n"
00380             << "} \n"
00381 
00382             << "void main() \n"
00383             << "{ \n"
00384             << "    gl_Position = warpVertex( gl_ModelViewProjectionMatrix * gl_Vertex ); \n"
00385             << "    gl_FrontColor = gl_Color; \n"
00386             << "    gl_TexCoord[0] = gl_MultiTexCoord0;\n"
00387             << "} \n";
00388     }
00389 
00390     else // no vertex warping
00391     {
00392         buf << "void main() \n"
00393             << "{ \n"
00394             << "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n"
00395             << "    gl_FrontColor = gl_Color; \n"
00396             << "    gl_TexCoord[0] = gl_MultiTexCoord0;\n"
00397             << "} \n";
00398     }
00399 
00400     std::string vertSource = buf.str();
00401     program->addShader( new osg::Shader( osg::Shader::VERTEX, vertSource ) );
00402 
00403     std::stringstream fragBuf;
00404     fragBuf    << "#version 110 \n"
00405                << "uniform sampler2D texture_0; \n"
00406                << "void main() \n"
00407                << "{\n"                              
00408                << "    vec4 tex = texture2D(texture_0, gl_TexCoord[0].xy);\n"
00409                << "    vec3 mixed_color = mix(gl_Color.rgb, tex.rgb, tex.a);\n"
00410                //<< "    gl_FragColor = vec4(mixed_color, gl_Color.a); \n"
00411                << "    gl_FragColor = vec4(mixed_color, gl_Color.a); \n"
00412                << "}\n";
00413     
00414     std::string fragSource = fragBuf.str();
00415     
00416     program->addShader( new osg::Shader( osg::Shader::FRAGMENT, fragSource ) );
00417     set->addUniform(new osg::Uniform("texture_0",0));
00418 }
00419 
00420 void
00421 OverlayDecorator::initSubgraphShaders( osg::StateSet* set )
00422 {
00423     VirtualProgram* vp = new VirtualProgram();
00424     vp->setName( "OverlayDecorator subgraph shader" );
00425     set->setAttributeAndModes( vp, osg::StateAttribute::ON );
00426 
00427     // sampler for projected texture:
00428     set->getOrCreateUniform( "osgearth_overlay_ProjTex", osg::Uniform::SAMPLER_2D )->set( *_textureUnit );
00429 
00430     // the texture projection matrix uniform.
00431     _texGenUniform = set->getOrCreateUniform( "osgearth_overlay_TexGenMatrix", osg::Uniform::FLOAT_MAT4 );
00432 
00433     std::stringstream buf;
00434 
00435     // vertex shader - subgraph
00436     buf << "#version 110 \n"
00437         << "uniform mat4 osgearth_overlay_TexGenMatrix; \n"
00438         << "uniform mat4 osg_ViewMatrixInverse; \n"
00439 
00440         << "void osgearth_overlay_vertex(void) \n"
00441         << "{ \n"
00442         << "    gl_TexCoord["<< *_textureUnit << "] = osgearth_overlay_TexGenMatrix * osg_ViewMatrixInverse * gl_ModelViewMatrix * gl_Vertex; \n"
00443         << "} \n";
00444 
00445     std::string vertexSource = buf.str();
00446     vp->setFunction( "osgearth_overlay_vertex", vertexSource, ShaderComp::LOCATION_VERTEX_POST_LIGHTING );
00447 
00448     // fragment shader - subgraph
00449     buf.str("");
00450     buf << "#version 110 \n"
00451         << "uniform sampler2D osgearth_overlay_ProjTex; \n";
00452 
00453     if ( _useWarping )
00454     {
00455         buf << "uniform float warp; \n"
00456 
00457             // because the built-in pow() is busted
00458             << "float mypow( in float x, in float y ) \n"
00459             << "{ \n"
00460             << "    return x/(x+y-y*x); \n"
00461             << "} \n"
00462 
00463             << "vec2 warpTexCoord( in vec2 src ) \n"
00464             << "{ \n"
00465             //      incoming tex coord is [0..1], so we scale to [-1..1]
00466             << "    vec2 srcn = vec2( src.x*2.0 - 1.0, src.y*2.0 - 1.0 ); \n" 
00467 
00468             //      we want to work in the [0..1] space on each side of 0, so can the abs
00469             //      and store the signs for later:
00470             << "    vec2 srct = vec2( abs(srcn.x), abs(srcn.y) ); \n"
00471             << "    vec2 sign = vec2( srcn.x > 0.0 ? 1.0 : -1.0, srcn.y > 0.0 ? 1.0 : -1.0 ); \n"
00472 
00473             //      apply the deformation using a deceleration curve:
00474             << "    vec2 srcp = vec2( 1.0-mypow(1.0-srct.x,warp), 1.0-mypow(1.0-srct.y,warp) ); \n"
00475 
00476             //      reapply the sign, and scale back to [0..1]:
00477             << "    vec2 srcr = vec2( sign.x*srcp.x, sign.y*srcp.y ); \n"
00478             << "    return vec2( 0.5*(srcr.x + 1.0), 0.5*(srcr.y + 1.0) ); \n"
00479             << "} \n";
00480     }
00481 
00482     buf << "void osgearth_overlay_fragment( inout vec4 color ) \n"
00483         << "{ \n"
00484         << "    vec2 texCoord = gl_TexCoord["<< *_textureUnit << "].xy / gl_TexCoord["<< *_textureUnit << "].q; \n";
00485 
00486     if ( _useWarping && !_visualizeWarp )
00487         buf  << "    texCoord = warpTexCoord( texCoord ); \n";
00488 
00489     buf << "    vec4 texel = texture2D(osgearth_overlay_ProjTex, texCoord); \n"  
00490         << "    color = vec4( mix( color.rgb, texel.rgb, texel.a ), color.a); \n"
00491         << "} \n";
00492 
00493     std::string fragmentSource = buf.str();
00494     vp->setFunction( "osgearth_overlay_fragment", fragmentSource, ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
00495 }
00496 
00497 void
00498 OverlayDecorator::setOverlayGraph( osg::Node* node )
00499 {
00500     if ( _overlayGraph.get() != node )
00501     {
00502         if ( _overlayGraph.valid() && node == 0L )
00503         {
00504             ADJUST_UPDATE_TRAV_COUNT( this, -1 );
00505         }
00506         else if ( !_overlayGraph.valid() && node != 0L )
00507         {
00508             ADJUST_UPDATE_TRAV_COUNT( this, 1 );
00509         }
00510 
00511         _overlayGraph = node;
00512         reinit();
00513     }
00514 }
00515 
00516 void
00517 OverlayDecorator::setTextureSize( int texSize )
00518 {
00519     if ( texSize != _textureSize.value() )
00520     {
00521         _textureSize = texSize;
00522         reinit();
00523     }
00524 }
00525 
00526 void
00527 OverlayDecorator::setTextureUnit( int texUnit )
00528 {
00529     if ( !_explicitTextureUnit.isSet() || texUnit != _explicitTextureUnit.value() )
00530     {
00531         _explicitTextureUnit = texUnit;
00532         reinit();
00533     }
00534 }
00535 
00536 void
00537 OverlayDecorator::setMipmapping( bool value )
00538 {
00539     if ( value != _mipmapping )
00540     {
00541         _mipmapping = value;
00542         reinit();
00543 
00544         if ( _mipmapping )
00545             OE_INFO << LC << "Overlay mipmapping " << (value?"enabled":"disabled") << std::endl;
00546     }
00547 }
00548 
00549 void
00550 OverlayDecorator::setVertexWarping( bool value )
00551 {
00552     if ( value != _useWarping )
00553     {
00554         _useWarping = value;
00555         reinit();
00556         
00557         if ( _useWarping )
00558             OE_INFO << LC << "Vertex warping " << (value?"enabled":"disabled")<< std::endl;
00559     }
00560 }
00561 
00562 void
00563 OverlayDecorator::setOverlayBlending( bool value )
00564 {
00565     if ( value != _rttBlending )
00566     {
00567         _rttBlending = value;
00568         reinit();
00569         
00570         if ( _rttBlending )
00571             OE_INFO << LC << "Overlay blending " << (value?"enabled":"disabled")<< std::endl;
00572     }
00573 }
00574 
00575 void
00576 OverlayDecorator::onInstall( TerrainEngineNode* engine )
00577 {
00578     _engine = engine;
00579 
00580     // establish the earth's major axis:
00581     MapInfo info(engine->getMap());
00582     _isGeocentric = info.isGeocentric();
00583     _ellipsoid = info.getProfile()->getSRS()->getEllipsoid();
00584 
00585     // the maximum extent (for projected maps only)
00586     if ( !_isGeocentric )
00587     {
00588         const GeoExtent& extent = info.getProfile()->getExtent();
00589         _maxProjectedMapExtent = osg::maximum( extent.width(), extent.height() );
00590     }
00591 
00592     // see whether we want shader support:
00593     // TODO: this is not stricty correct; you might still want to use shader overlays
00594     // in multipass mode, AND you might want FFP overlays in multitexture-FFP mode.
00595     _useShaders = engine->getTextureCompositor()->usesShaderComposition();
00596 
00597     if ( !_textureSize.isSet() )
00598     {
00599         unsigned maxSize = Registry::instance()->getCapabilities().getMaxFastTextureSize();
00600         _textureSize.init( osg::minimum( 4096u, maxSize ) );
00601 
00602         OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl;
00603     }
00604 
00605     // rebuild dynamic elements.
00606     reinit();
00607 }
00608 
00609 void
00610 OverlayDecorator::onUninstall( TerrainEngineNode* engine )
00611 {
00612     if ( !_explicitTextureUnit.isSet() && _textureUnit.isSet() )
00613     {
00614         _engine->getTextureCompositor()->releaseTextureImageUnit( *_textureUnit );
00615         _textureUnit.unset();
00616     }
00617 
00618     _engine = 0L;
00619 }
00620 
00621 void
00622 OverlayDecorator::updateRTTCamera( osg::NodeVisitor& nv )
00623 {
00624     static osg::Matrix normalizeMatrix = 
00625         osg::Matrix::translate(1.0,1.0,1.0) * osg::Matrix::scale(0.5,0.5,0.5);
00626 
00627     // configure the RTT camera:
00628     _rttCamera->setViewMatrix( _rttViewMatrix );
00629     _rttCamera->setProjectionMatrix( _rttProjMatrix );
00630 
00631     osg::Matrix MVPT = _projectorViewMatrix * _projectorProjMatrix * normalizeMatrix;
00632 
00633     //_texGenNode->getTexGen()->setMode( osg::TexGen::EYE_LINEAR ); // moved to initialization
00634     _texGenNode->getTexGen()->setPlanesFromMatrix( MVPT );
00635     
00636     // uniform update:
00637     if ( _useShaders )
00638     {
00639         _texGenUniform->set( MVPT );
00640         if ( _useWarping )
00641             _warpUniform->set( _warp );
00642     }
00643 }
00644 
00645 void
00646 OverlayDecorator::cull( osgUtil::CullVisitor* cv )
00647 {
00648     static int s_frame = 1;
00649 
00650     osg::Vec3 eye = cv->getEyePoint();
00651 
00652     double eyeLen;
00653     osg::Vec3d worldUp;
00654 
00655     // height above sea level
00656     double hasl;
00657 
00658     // weight of the HASL value when calculating extent compensation
00659     double haslWeight;
00660 
00661     // approximate distance to the visible horizon
00662     double horizonDistance; 
00663 
00664     // distance to the horizon, projected into the RTT camera's tangent plane.
00665     double horizonDistanceInRTTPlane;
00666 
00667     if ( _isGeocentric )
00668     {
00669         double lat, lon;
00670         _ellipsoid->convertXYZToLatLongHeight( eye.x(), eye.y(), eye.z(), lat, lon, hasl );
00671         hasl = osg::maximum( hasl, 100.0 );
00672 
00673         worldUp = _ellipsoid->computeLocalUpVector(eye.x(), eye.y(), eye.z());
00674 
00675         eyeLen = eye.length();
00676 
00677         // radius of the earth under the eyepoint
00678         double radius = eyeLen - hasl; 
00679         horizonDistance = sqrt( 2.0 * radius * hasl ); 
00680     
00681         // calculate the distance to the horizon, projected into the RTT camera plane.
00682         // This is the maximum limit of eMax since there is no point in drawing overlay
00683         // data beyond the visible horizon.
00684         double pitchAngleOfHorizon_rad = acos( horizonDistance/eyeLen );
00685         horizonDistanceInRTTPlane = horizonDistance * sin( pitchAngleOfHorizon_rad );
00686     }
00687     else // projected map
00688     {
00689         hasl = eye.z();
00690         hasl = osg::maximum( hasl, 100.0 );
00691         worldUp.set( 0.0, 0.0, 1.0 );
00692         eyeLen = hasl * 2.0;
00693 
00694         // there is no maximum horizon distance in a projected map
00695         horizonDistance = DBL_MAX;
00696         horizonDistanceInRTTPlane = DBL_MAX;
00697 
00698         _rttViewMatrix = osg::Matrixd::lookAt( eye, eye-worldUp*hasl, osg::Vec3(0,1,0) );
00699     }
00700 
00701     // create a "weighting" that weights HASL against the camera's pitch.
00702     osg::Vec3d lookVector = cv->getLookVectorLocal();
00703     haslWeight = osg::absolute(worldUp * lookVector);
00704 
00705     // unit look-vector of the eye:
00706     osg::Vec3d from, to, up;
00707     const osg::Matrix& mvMatrix = *cv->getModelViewMatrix();
00708     mvMatrix.getLookAt( from, to, up, eyeLen);
00709     osg::Vec3 camLookVec = to-from;
00710     camLookVec.normalize();
00711 
00712     // unit look-vector of the RTT camera:
00713     osg::Vec3d rttLookVec = -worldUp;
00714 
00715     // the minimum and maximum extents of the overlay ortho projector:
00716     double eMin = 0.1;
00717     double eMax = DBL_MAX;
00718 
00719     // Save and reset the current near/far planes before traversing the subgraph.
00720     // We do this because we want a projection matrix that includes ONLY the clip
00721     // planes from the subgraph, and not anything traversed up to this point.
00722     double zSavedNear = cv->getCalculatedNearPlane();
00723     double zSavedFar  = cv->getCalculatedFarPlane();
00724 
00725     cv->setCalculatedNearPlane( FLT_MAX );
00726     cv->setCalculatedFarPlane( -FLT_MAX );
00727 
00728     // cull the subgraph here. This doubles as the subgraph's official cull traversal
00729     // and a gathering of its clip planes.
00730     cv->pushStateSet( _subgraphStateSet.get() );
00731     osg::Group::traverse( *cv );
00732     cv->popStateSet();
00733 
00734     // Pull a copy of the projection matrix; we will use this to calculate the optimum
00735     // projected texture extent
00736     osg::Matrixd projMatrix = *cv->getProjectionMatrix();
00737 
00738     // Clamp the projection matrix to the newly calculated clip planes. This prevents
00739     // any "leakage" from outside the subraph.
00740     double zNear = cv->getCalculatedNearPlane();
00741     double zFar  = cv->getCalculatedFarPlane();
00742     cv->clampProjectionMatrix( projMatrix, zNear, zFar );
00743 
00744     //OE_NOTICE << std::fixed << "zNear = " << zNear << ", zFar = " << zFar << std::endl;
00745 
00746     if ( _isGeocentric )
00747     {
00748         // in geocentric mode, clamp the far clip plane to the horizon.
00749         double maxDistance = (1.0 - haslWeight)  * horizonDistance  + haslWeight * hasl;
00750         maxDistance *= 1.5;
00751         if (zFar - zNear >= maxDistance)
00752             zFar = zNear + maxDistance;
00753 
00754         cv->clampProjectionMatrix( projMatrix, zNear, zFar );
00755     }
00756 
00757     // restore the clip planes in the cull visitor, now that we have our subgraph
00758     // projection matrix.
00759     cv->setCalculatedNearPlane( osg::minimum(zSavedNear, zNear) );
00760     cv->setCalculatedFarPlane( osg::maximum(zSavedFar, zFar) );
00761        
00762     // contruct the polyhedron representing the viewing frustum.
00763     //osgShadow::ConvexPolyhedron frustumPH;
00764     MyConvexPolyhedron frustumPH;
00765     frustumPH.setToUnitFrustum( true, true );
00766     osg::Matrixd MVP = *cv->getModelViewMatrix() * projMatrix;
00767     osg::Matrixd inverseMVP;
00768     inverseMVP.invert(MVP);
00769     frustumPH.transform( inverseMVP, MVP );
00770 
00771     // make a polyhedron representing the viewing frustum of the overlay, and cut it to
00772     // intersect the viewing frustum:
00773     osgShadow::ConvexPolyhedron visiblePH;
00774 
00775     // get the bounds of the overlay graph model. 
00776     osg::BoundingBox visibleOverlayBBox;
00777     CoarsePolytopeIntersector cpi( frustumPH, visibleOverlayBBox );
00778     _overlayGraph->accept( cpi );
00779     visiblePH.setToBoundingBox( visibleOverlayBBox );
00780 
00781     // this intersects the viewing frustum with the subgraph's bounding box, basically giving us
00782     // a "minimal" polyhedron containing all potentially visible geometry. (It can't be truly 
00783     // minimal without clipping at the geometry level, but that would probably be too expensive.)
00784     visiblePH.cut( frustumPH );
00785 
00786     // calculate the extents for our orthographic RTT camera (clamping it to the
00787     // visible horizon)
00788     std::vector<osg::Vec3d> verts;
00789     visiblePH.getPoints( verts );
00790 
00791     if ( _isGeocentric )
00792     {
00793         // for a geocentric map, try to place the RTT camera position at an optimal point
00794         // that will minimize the span of the RTT texture. Take the centroid of the 
00795         // visible polyhedron and clamp it's distance to the eyepoint by half the horizon
00796         // distance.
00797         osg::BoundingBox box = visiblePH.computeBoundingBox();
00798         osg::Vec3d bc = box.center();
00799         osg::Vec3d eye2bc = eye - bc;
00800         if ( eye2bc.length() > horizonDistance )
00801         {
00802             eye2bc.normalize();
00803             bc = eye + eye2bc * 0.5*horizonDistance;
00804         }
00805         
00806         rttLookVec = -bc;
00807         rttLookVec.normalize();
00808 
00809         double new_eMax;
00810         getMinMaxExtentInSilhouette( bc, rttLookVec, verts, eMin, new_eMax );
00811         eMax = std::min( eMax, new_eMax );
00812         _rttViewMatrix = osg::Matrixd::lookAt( bc, osg::Vec3d(0,0,0), osg::Vec3d(0,0,1) );
00813         _rttProjMatrix = osg::Matrixd::ortho( -eMax, eMax, -eMax, eMax, -eyeLen, bc.length() );
00814 
00815         //OE_INFO << std::fixed << std::setprecision(1)
00816         //    << "eMax = " << eMax
00817         //    << ", bc = " << bc.x() << ", " << bc.y() << ", " << bc.z()
00818         //    << ", eye = " << eye.x() << ", " << eye.y() << ", " << eye.z()
00819         //    << ", eyeLen = " << eyeLen
00820         //    << std::endl;
00821     }
00822     else
00823     {
00824         // for a projected map, just point the RTT straight down at the camera position.
00825         // TODO: this could be optimized, probably.
00826         double new_eMax;
00827         getMinMaxExtentInSilhouette( from, osg::Vec3d(0,0,-1), verts, eMin, new_eMax );   
00828         eMax = std::min( eMax, new_eMax ); 
00829         _rttProjMatrix = osg::Matrix::ortho( -eMax, eMax, -eMax, eMax, -eyeLen, eyeLen );
00830     }
00831 
00832     //OE_NOTICE << LC << "EMIN = " << eMin << ", EMAX = " << eMax << std::endl;
00833 
00834     if ( _useWarping )
00835     {
00836         // calculate the warping paramaters. This uses shaders to warp the verts and
00837         // tex coords to favor data closer to the camera when necessary.
00838 
00839     #define WARP_LIMIT 3.0
00840 
00841         double pitchStrength = ( camLookVec * rttLookVec ); // eye pitch relative to rtt pitch
00842         double devStrength = 1.0 - (pitchStrength*pitchStrength);
00843         double haslStrength = 1.0 - osg::clampBetween( hasl/1e6, 0.0, 1.0 );
00844 
00845         _warp = 1.0 + devStrength * haslStrength * WARP_LIMIT;
00846 
00847         if ( _visualizeWarp )
00848             _warp = 4.0;
00849 
00850 #if 0
00851         OE_INFO << LC << std::fixed
00852             << "hasl=" << hasl
00853             << ", eMin=" << eMin
00854             << ", eMax=" << eMax
00855             << ", eyeLen=" << eyeLen
00856             //<< ", ratio=" << ratio
00857             //<< ", dev=" << devStrength
00858             //<< ", has=" << haeStrength
00859             << ", warp=" << _warp
00860             << std::endl;
00861 #endif
00862     }
00863 
00864 #if 0
00865     if ( s_frame++ % 100 == 0 )
00866     {
00867         osgShadow::ConvexPolyhedron rttPH;
00868         rttPH.setToUnitFrustum( true, true );
00869         osg::Matrixd MVP = _rttViewMatrix * _rttProjMatrix;
00870         osg::Matrixd inverseMVP;
00871         inverseMVP.invert(MVP);
00872         rttPH.transform( inverseMVP, MVP );
00873         rttPH.dumpGeometry();
00874     }
00875 #endif
00876 
00877     // projector matrices are the same as for the RTT camera. Tim was right.
00878     _projectorViewMatrix = _rttViewMatrix;
00879     _projectorProjMatrix = _rttProjMatrix;
00880 }
00881 
00882 void
00883 OverlayDecorator::traverse( osg::NodeVisitor& nv )
00884 {
00885     bool isCull = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR;
00886 
00887     if ( _overlayGraph.valid() && _textureUnit.isSet() )
00888     {
00889         if ( isCull )
00890         {
00891             osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
00892             if ( cv )
00893             {
00894                 cull( cv );
00895             }
00896             _rttCamera->accept( nv );
00897             
00898             // note: texgennode doesn't need a cull, and the subgraph
00899             // is traversed in cull().
00900         }
00901 
00902         else
00903         {
00904             if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR )
00905             {
00906                 updateRTTCamera( nv );
00907             }
00908             _rttCamera->accept( nv );
00909             _texGenNode->accept( nv );
00910 
00911             osg::Group::traverse( nv );
00912         }    
00913     }
00914     else
00915     {
00916         osgUtil::CullVisitor* cv = 0L;
00917         if ( isCull )
00918             cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
00919 
00920         if ( cv )
00921             cv->pushStateSet( _subgraphStateSet.get() );
00922 
00923         osg::Group::traverse( nv );
00924 
00925         if ( cv )
00926             cv->popStateSet();
00927     }
00928 }
00929 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines