osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 00020 #include <osgEarthUtil/OceanSurfaceNode> 00021 00022 #include <osgEarth/FindNode> 00023 #include <osgEarth/Notify> 00024 #include <osgEarth/Registry> 00025 #include <osgEarth/ShaderComposition> 00026 #include <osgEarth/TextureCompositor> 00027 #include <osgEarth/MapNode> 00028 #include <osgEarth/FindNode> 00029 00030 #include <osg/Texture3D> 00031 #include <osgDB/ReadFile> 00032 00033 #include <sstream> 00034 #include <iomanip> 00035 00036 #define LC "[OceanSurfaceNode] " 00037 00038 using namespace osgEarth; 00039 using namespace osgEarth::Util; 00040 00041 typedef std::vector< osg::ref_ptr< osg::Image > > ImageList; 00042 00043 OceanSurfaceNode::OceanSurfaceNode() : 00044 _shadersDirty(false), 00045 _maxRange(800000), 00046 _oceanMaskLayerUID(-1), 00047 _oceanSurfaceTextureUnit(-1), 00048 _oceanSurfaceTextureApplied(false), 00049 _waveHeight(100), 00050 _period(1024), 00051 _enabled(true), 00052 _invertMask(false), 00053 _adjustToMSL(true), 00054 _oceanColor(osg::Vec4f(0,0,1,0)), 00055 _oceanAnimationPeriod(6.0), 00056 _oceanSurfaceImageSizeRadians(osg::PI/500.0) 00057 { 00058 rebuildShaders(); 00059 00060 getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanPeriod", osg::Uniform::FLOAT)->set(_period); 00061 getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanAnimationPeriod", osg::Uniform::FLOAT)->set(_oceanAnimationPeriod); 00062 00063 osg::Uniform* oceanHeightUniform = getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanHeight", osg::Uniform::FLOAT); 00064 oceanHeightUniform->set( _waveHeight); 00065 oceanHeightUniform->setDataVariance( osg::Object::DYNAMIC); 00066 00067 //Initialize the ocean surface texture 00068 _oceanSurfaceTexture = new osg::Texture3D(); 00069 _oceanSurfaceTexture->setWrap(osg::Texture::WRAP_S,osg::Texture::REPEAT); 00070 _oceanSurfaceTexture->setWrap(osg::Texture::WRAP_T,osg::Texture::REPEAT); 00071 _oceanSurfaceTexture->setWrap(osg::Texture::WRAP_R,osg::Texture::REPEAT); 00072 _oceanSurfaceTexture->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::LINEAR); 00073 _oceanSurfaceTexture->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::LINEAR); 00074 } 00075 00076 void 00077 OceanSurfaceNode::shadersDirty(bool value) 00078 { 00079 if ( _shadersDirty != value ) 00080 { 00081 _shadersDirty = value; 00082 ADJUST_UPDATE_TRAV_COUNT( this, _shadersDirty ? 1 : -1 ); 00083 } 00084 } 00085 00086 void 00087 OceanSurfaceNode::setOceanMaskImageLayer( const ImageLayer* layer ) 00088 { 00089 if ( _maskLayer.get() != layer ) 00090 { 00091 _maskLayer = layer; 00092 shadersDirty(true); 00093 } 00094 } 00095 00096 bool 00097 OceanSurfaceNode::getAdjustToMSL() const 00098 { 00099 return _adjustToMSL; 00100 } 00101 00102 void 00103 OceanSurfaceNode::setAdjustToMSL(bool adjustToMSL) 00104 { 00105 if (_adjustToMSL != adjustToMSL) 00106 { 00107 _adjustToMSL = adjustToMSL; 00108 shadersDirty( true ); 00109 } 00110 } 00111 00112 osg::Image* 00113 OceanSurfaceNode::getOceanSurfaceImage() const 00114 { 00115 return _oceanSurfaceImage.get(); 00116 } 00117 00118 void 00119 OceanSurfaceNode::setOceanSurfaceImage(osg::Image* image) 00120 { 00121 if (_oceanSurfaceImage.get() != image) 00122 { 00123 _oceanSurfaceImage = image; 00124 _oceanSurfaceTexture->setImage( _oceanSurfaceImage.get() ); 00125 00126 shadersDirty( true ); 00127 } 00128 } 00129 00130 float 00131 OceanSurfaceNode::getWaveHeight() const 00132 { 00133 return _waveHeight; 00134 } 00135 00136 void 00137 OceanSurfaceNode::setWaveHeight(float waveHeight) 00138 { 00139 if (_waveHeight != waveHeight) 00140 { 00141 _waveHeight = waveHeight; 00142 getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanHeight", osg::Uniform::FLOAT)->set(_waveHeight); 00143 //TODO: consider rebuildShaders() instead.. 00144 } 00145 } 00146 00147 float 00148 OceanSurfaceNode::getMaxRange() const 00149 { 00150 return _maxRange; 00151 } 00152 00153 void 00154 OceanSurfaceNode::setMaxRange(float maxRange) 00155 { 00156 if (_maxRange != maxRange) 00157 { 00158 _maxRange = maxRange; 00159 shadersDirty(true); 00160 } 00161 } 00162 00163 float 00164 OceanSurfaceNode::getPeriod() const 00165 { 00166 return _period; 00167 } 00168 00169 void 00170 OceanSurfaceNode::setPeriod(float period) 00171 { 00172 if (_period !=period) 00173 { 00174 _period = period; 00175 getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanPeriod", osg::Uniform::FLOAT)->set(_period); 00176 //TODO: consider rebuildShaders() instead.. 00177 } 00178 } 00179 00180 bool 00181 OceanSurfaceNode::getEnabled() const 00182 { 00183 return _enabled; 00184 } 00185 00186 void 00187 OceanSurfaceNode::setEnabled(bool enabled) 00188 { 00189 if (_enabled != enabled) 00190 { 00191 _enabled = enabled; 00192 shadersDirty(true); 00193 } 00194 } 00195 00196 bool 00197 OceanSurfaceNode::getInvertMask() const 00198 { 00199 return _invertMask; 00200 } 00201 00202 void 00203 OceanSurfaceNode::setInvertMask(bool invertMask) 00204 { 00205 if (_invertMask != invertMask) 00206 { 00207 _invertMask = invertMask; 00208 shadersDirty( true ); 00209 } 00210 } 00211 00212 void 00213 OceanSurfaceNode::setModulationColor( const osg::Vec4f& color ) 00214 { 00215 if ( !_oceanColor.isSetTo( color ) ) 00216 { 00217 _oceanColor = color; 00218 shadersDirty( true ); 00219 } 00220 } 00221 00222 osg::Vec4f 00223 OceanSurfaceNode::getModulationColor() const 00224 { 00225 return _oceanColor.value(); 00226 } 00227 00228 float 00229 OceanSurfaceNode::getOceanAnimationPeriod() const 00230 { 00231 return _oceanAnimationPeriod; 00232 } 00233 00234 void 00235 OceanSurfaceNode::setOceanAnimationPeriod(float oceanAnimationPeriod) 00236 { 00237 if (_oceanAnimationPeriod != oceanAnimationPeriod) 00238 { 00239 _oceanAnimationPeriod = oceanAnimationPeriod; 00240 getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanAnimationPeriod", osg::Uniform::FLOAT)->set(oceanAnimationPeriod); 00241 //TODO: consider rebuildShaders() instead.. 00242 } 00243 } 00244 00245 float 00246 OceanSurfaceNode::getOceanSurfaceImageSizeRadians() const 00247 { 00248 return _oceanSurfaceImageSizeRadians; 00249 } 00250 00251 void 00252 OceanSurfaceNode::setOceanSurfaceImageSizeRadians(float size) 00253 { 00254 if (_oceanSurfaceImageSizeRadians != size) 00255 { 00256 _oceanSurfaceImageSizeRadians = size; 00257 shadersDirty( true ); 00258 } 00259 } 00260 00261 void 00262 OceanSurfaceNode::traverse( osg::NodeVisitor& nv ) 00263 { 00264 if ( _shadersDirty && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR ) 00265 { 00266 rebuildShaders(); 00267 shadersDirty( false ); 00268 } 00269 00270 osg::Group::traverse( nv ); 00271 } 00272 00273 #define MASK_SAMPLER_FUNC "osgearth_ocean_sampleMask" 00274 00275 void 00276 OceanSurfaceNode::rebuildShaders() 00277 { 00278 // need the terrain engine so we can get at the compositor. 00279 TerrainEngineNode* engine = osgEarth::findTopMostNodeOfType<TerrainEngineNode>( this ); 00280 if ( !engine ) { 00281 OE_DEBUG << LC << "No terrain engine found in the map node; abort" << std::endl; 00282 return; 00283 } 00284 00285 // access the compositor because we are going to be sampling map layers. 00286 TextureCompositor* comp = engine->getTextureCompositor(); 00287 if ( !comp ) { 00288 OE_INFO << LC << "No texture compositor found in the terrain engine; abort" << std::endl; 00289 return; 00290 } 00291 00292 // reserve a texture unit for the surface texture (if we haven't already) 00293 if ( !_oceanSurfaceTextureApplied && _oceanSurfaceTextureUnit < 0 && _oceanSurfaceTexture.valid() ) 00294 { 00295 if ( comp->reserveTextureImageUnit( _oceanSurfaceTextureUnit ) ) 00296 { 00297 getOrCreateStateSet()->setTextureAttributeAndModes( 00298 _oceanSurfaceTextureUnit, _oceanSurfaceTexture.get(), osg::StateAttribute::ON); 00299 _oceanSurfaceTextureApplied = true; 00300 } 00301 else 00302 { 00303 OE_WARN << LC << "Sorry, failed to allocate a texture image unit for the surface texture." << std::endl; 00304 } 00305 } 00306 00307 // create a VP to store our custom shader components. 00308 osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram(); 00309 getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON ); 00310 00311 // if the ocean is disabled, just return without injecting any shaders. 00312 if ( !_enabled ) 00313 return; 00314 00315 // build the sampler function if necessary 00316 osg::ref_ptr<const ImageLayer> safeMaskLayer = _maskLayer.get(); 00317 osg::Shader* maskSampler = 0L; 00318 if ( safeMaskLayer.valid() ) 00319 { 00320 maskSampler = comp->createSamplerFunction( safeMaskLayer->getUID(), MASK_SAMPLER_FUNC, osg::Shader::VERTEX ); 00321 if ( maskSampler ) 00322 vp->setShader( MASK_SAMPLER_FUNC, maskSampler ); 00323 } 00324 00325 // make the helper functions. 00326 { 00327 std::stringstream buf; 00328 00329 buf << "vec3 xyz_to_lat_lon_height(in vec3 xyz) \n" 00330 << "{ \n" 00331 << " float X = xyz.x;\n" 00332 << " float Y = xyz.y;\n" 00333 << " float Z = xyz.z;\n" 00334 << " float _radiusEquator = 6378137.0;\n" 00335 << " float _radiusPolar = 6356752.3142;\n" 00336 << " float flattening = (_radiusEquator-_radiusPolar)/_radiusEquator;\n" 00337 << " float _eccentricitySquared = 2.0*flattening - flattening*flattening;\n" 00338 << " float p = sqrt(X*X + Y*Y);\n" 00339 << " float theta = atan(Z*_radiusEquator , (p*_radiusPolar));\n" 00340 << " float eDashSquared = (_radiusEquator*_radiusEquator - _radiusPolar*_radiusPolar)/(_radiusPolar*_radiusPolar);\n" 00341 << " float sin_theta = sin(theta);\n" 00342 << " float cos_theta = cos(theta);\n" 00343 << " float latitude = atan( (Z + eDashSquared*_radiusPolar*sin_theta*sin_theta*sin_theta), (p - _eccentricitySquared*_radiusEquator*cos_theta*cos_theta*cos_theta) );\n" 00344 << " float longitude = atan(Y,X);\n" 00345 << " float sin_latitude = sin(latitude);\n" 00346 << " float N = _radiusEquator / sqrt( 1.0 - _eccentricitySquared*sin_latitude*sin_latitude);\n" 00347 << " float height = p/cos(latitude) - N;\n" 00348 << " return vec3(longitude, latitude, height);\n" 00349 << "} \n" 00350 << "\n"; 00351 00352 std::string str = buf.str(); 00353 vp->setShader( "xyz_to_lat_lon_height", new osg::Shader(osg::Shader::VERTEX, str) ); 00354 } 00355 00356 // next make the vertex shader function that will morph the ocean verts and prepare 00357 // the texture coordinates for the surface effects. 00358 { 00359 std::stringstream buf; 00360 00361 buf << std::fixed; 00362 00363 buf << "uniform float osg_SimulationTime; \n" 00364 << "uniform mat4 osg_ViewMatrixInverse;\n" 00365 << "uniform mat4 osg_ViewMatrix;\n" 00366 << "uniform float osgearth_OceanHeight;\n" 00367 << "uniform float osgearth_OceanPeriod;\n" 00368 << "uniform float osgearth_OceanAnimationPeriod;\n" 00369 << "varying float osgearth_OceanAlpha;\n" 00370 << "varying float osgearth_CameraRange; \n" 00371 00372 << "vec3 xyz_to_lat_lon_height(in vec3 xyz); \n"; 00373 00374 if ( _oceanSurfaceTextureApplied ) 00375 { 00376 buf << "varying vec3 osgearth_oceanSurfaceTexCoord; \n"; 00377 } 00378 00379 if ( maskSampler ) 00380 { 00381 buf << "vec4 " << MASK_SAMPLER_FUNC << "(); \n"; 00382 } 00383 00384 buf << "void osgearth_ocean_morphSurface() \n" 00385 << "{ \n" 00386 << " mat4 modelMatrix = osg_ViewMatrixInverse * gl_ModelViewMatrix; \n" 00387 << " vec4 vert = modelMatrix * gl_Vertex; \n" 00388 << " vec3 vert3 = vec3(vert.x, vert.y, vert.z); \n" 00389 << " vec3 latlon = xyz_to_lat_lon_height(vert3); \n" 00390 << " osgearth_OceanAlpha = 1.0; \n"; 00391 00392 if ( maskSampler ) 00393 { 00394 buf << " osgearth_OceanAlpha = 1.0 - (" << MASK_SAMPLER_FUNC << "()).a; \n"; 00395 } 00396 00397 if ( _invertMask ) 00398 buf << " osgearth_OceanAlpha = 1.0 - osgearth_OceanAlpha; \n"; 00399 00400 buf << " if ( osgearth_CameraRange <= " << _maxRange << " ) \n" 00401 << " { \n" 00402 << " float s = mix(1.0, 0.0, osgearth_CameraRange / " << _maxRange << "); \n" //Invert so it's between 0 and 1 00403 << " osgearth_OceanAlpha *= s; \n" 00404 << " } \n" 00405 << " else \n" 00406 << " { \n" 00407 << " osgearth_OceanAlpha = 0.0; \n" 00408 << " } \n" 00409 00410 << " if (osgearth_OceanAlpha > 0.0) \n" 00411 << " { \n" 00412 << " float PI_2 = 3.14158 * 2.0; \n" 00413 << " float period = PI_2/osgearth_OceanPeriod; \n" 00414 << " float half_period = period / 2.0; \n" 00415 << " vec3 n = normalize(vert3);\n" 00416 << " float theta = (mod(latlon.x, period) / period) * PI_2; \n" 00417 << " float phi = (mod(latlon.y, half_period) / half_period) * PI_2; \n" 00418 << " float phase1 = osg_SimulationTime * 2.0; \n" 00419 << " float phase2 = osg_SimulationTime * 4.0; \n" 00420 << " float waveHeight = (osgearth_OceanAlpha) * osgearth_OceanHeight; \n" 00421 << " float scale1 = sin(theta + phase1) * waveHeight; \n" 00422 << " float scale2 = cos(phi + phase2) * waveHeight; \n" 00423 << " float scale3 = sin(theta + phase2) * cos(phi + phase1) * waveHeight * 1.6; \n" 00424 << " float scale = (scale1 + scale2 + scale3)/3.0; \n"; 00425 00426 // flatten verts to MSL: 00427 if ( _adjustToMSL ) 00428 { 00429 buf << " vec3 offset = n * -latlon.z; \n" 00430 << " vert += vec4( offset.xyz, 0 ); \n"; 00431 } 00432 00433 // apply the save scale: 00434 buf << " n = n * scale; \n" 00435 << " vert += vec4(n.x, n.y,n.z,0); \n" 00436 << " vert = osg_ViewMatrix * vert; \n" 00437 << " gl_Position = gl_ProjectionMatrix * vert; \n" 00438 << " }\n"; 00439 00440 // set up the coords for the surface texture: 00441 if ( _oceanSurfaceTextureApplied ) 00442 { 00443 buf << " osgearth_oceanSurfaceTexCoord.x = latlon.x / " << _oceanSurfaceImageSizeRadians << "; \n" 00444 << " osgearth_oceanSurfaceTexCoord.y = latlon.y / " << _oceanSurfaceImageSizeRadians << "; \n" 00445 << " osgearth_oceanSurfaceTexCoord.z = fract(osg_SimulationTime/osgearth_OceanAnimationPeriod); \n"; 00446 } 00447 00448 buf << "}\n"; 00449 00450 // add as a custom user function in the shader composition: 00451 std::string vertSource = buf.str(); 00452 vp->setFunction( "osgearth_ocean_morphSurface", vertSource, osgEarth::ShaderComp::LOCATION_VERTEX_PRE_TEXTURING ); 00453 } 00454 00455 // now we need a fragment function that will apply the ocean surface texture. 00456 if ( _oceanSurfaceTextureApplied ) 00457 { 00458 getOrCreateStateSet()->getOrCreateUniform( "osgearth_oceanSurfaceTex", osg::Uniform::SAMPLER_3D )->set( _oceanSurfaceTextureUnit ); 00459 00460 std::stringstream buf; 00461 00462 buf << "uniform sampler3D osgearth_oceanSurfaceTex; \n" 00463 << "varying vec3 osgearth_oceanSurfaceTexCoord; \n" 00464 << "varying float osgearth_OceanAlpha; \n" 00465 00466 << "void osgearth_ocean_applySurfaceTex( inout vec4 color ) \n" 00467 << "{ \n" 00468 << " vec4 texel = texture3D(osgearth_oceanSurfaceTex, osgearth_oceanSurfaceTexCoord); \n" 00469 << " color = vec4( mix( color.rgb, texel.rgb, texel.a * osgearth_OceanAlpha ), color.a); \n" 00470 << "} \n"; 00471 00472 std::string str = buf.str(); 00473 vp->setFunction( "osgearth_ocean_applySurfaceTex", str, osgEarth::ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING ); 00474 } 00475 } 00476