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/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