2
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
3
* Copyright 2008-2010 Pelican Mapping
6
* osgEarth is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU Lesser General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>
20
#include <osgEarthUtil/OceanSurfaceNode>
22
#include <osgEarth/FindNode>
23
#include <osgEarth/Notify>
24
#include <osgEarth/Registry>
25
#include <osgEarth/ShaderComposition>
26
#include <osgEarth/MapNode>
27
#include <osgEarth/FindNode>
29
#include <osg/Texture3D>
30
#include <osgDB/ReadFile>
35
#define LC "[OceanSurfaceNode] "
37
using namespace osgEarth;
38
using namespace osgEarth::Util;
40
typedef std::vector< osg::ref_ptr< osg::Image > > ImageList;
42
OceanSurfaceNode::OceanSurfaceNode() :
45
_oceanMaskLayerUID(-1),
46
_oceanSurfaceTextureUnit(-1),
47
_oceanSurfaceTextureApplied(false),
53
_oceanColor(osg::Vec4f(0,0,1,0)),
54
_oceanAnimationPeriod(6.0),
55
_oceanSurfaceImageSizeRadians(osg::PI/500.0)
59
getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanPeriod", osg::Uniform::FLOAT)->set(_period);
60
getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanAnimationPeriod", osg::Uniform::FLOAT)->set(_oceanAnimationPeriod);
62
osg::Uniform* oceanHeightUniform = getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanHeight", osg::Uniform::FLOAT);
63
oceanHeightUniform->set( _waveHeight);
64
oceanHeightUniform->setDataVariance( osg::Object::DYNAMIC);
66
//Initialize the ocean surface texture
67
_oceanSurfaceTexture = new osg::Texture3D();
68
_oceanSurfaceTexture->setWrap(osg::Texture::WRAP_S,osg::Texture::REPEAT);
69
_oceanSurfaceTexture->setWrap(osg::Texture::WRAP_T,osg::Texture::REPEAT);
70
_oceanSurfaceTexture->setWrap(osg::Texture::WRAP_R,osg::Texture::REPEAT);
71
_oceanSurfaceTexture->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::LINEAR);
72
_oceanSurfaceTexture->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::LINEAR);
76
OceanSurfaceNode::shadersDirty(bool value)
78
if ( _shadersDirty != value )
80
_shadersDirty = value;
81
ADJUST_UPDATE_TRAV_COUNT( this, _shadersDirty ? 1 : -1 );
86
OceanSurfaceNode::setOceanMaskImageLayer( const ImageLayer* layer )
88
if ( _maskLayer.get() != layer )
96
OceanSurfaceNode::getAdjustToMSL() const
102
OceanSurfaceNode::setAdjustToMSL(bool adjustToMSL)
104
if (_adjustToMSL != adjustToMSL)
106
_adjustToMSL = adjustToMSL;
107
shadersDirty( true );
112
OceanSurfaceNode::getOceanSurfaceImage() const
114
return _oceanSurfaceImage.get();
118
OceanSurfaceNode::setOceanSurfaceImage(osg::Image* image)
120
if (_oceanSurfaceImage.get() != image)
122
_oceanSurfaceImage = image;
123
_oceanSurfaceTexture->setImage( _oceanSurfaceImage.get() );
125
shadersDirty( true );
130
OceanSurfaceNode::getWaveHeight() const
136
OceanSurfaceNode::setWaveHeight(float waveHeight)
138
if (_waveHeight != waveHeight)
140
_waveHeight = waveHeight;
141
getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanHeight", osg::Uniform::FLOAT)->set(_waveHeight);
142
//TODO: consider rebuildShaders() instead..
147
OceanSurfaceNode::getMaxRange() const
153
OceanSurfaceNode::setMaxRange(float maxRange)
155
if (_maxRange != maxRange)
157
_maxRange = maxRange;
163
OceanSurfaceNode::getPeriod() const
169
OceanSurfaceNode::setPeriod(float period)
171
if (_period !=period)
174
getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanPeriod", osg::Uniform::FLOAT)->set(_period);
175
//TODO: consider rebuildShaders() instead..
180
OceanSurfaceNode::getEnabled() const
186
OceanSurfaceNode::setEnabled(bool enabled)
188
if (_enabled != enabled)
196
OceanSurfaceNode::getInvertMask() const
202
OceanSurfaceNode::setInvertMask(bool invertMask)
204
if (_invertMask != invertMask)
206
_invertMask = invertMask;
207
shadersDirty( true );
212
OceanSurfaceNode::setModulationColor( const osg::Vec4f& color )
214
if ( !_oceanColor.isSetTo( color ) )
217
shadersDirty( true );
222
OceanSurfaceNode::getModulationColor() const
224
return _oceanColor.value();
228
OceanSurfaceNode::getOceanAnimationPeriod() const
230
return _oceanAnimationPeriod;
234
OceanSurfaceNode::setOceanAnimationPeriod(float oceanAnimationPeriod)
236
if (_oceanAnimationPeriod != oceanAnimationPeriod)
238
_oceanAnimationPeriod = oceanAnimationPeriod;
239
getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanAnimationPeriod", osg::Uniform::FLOAT)->set(oceanAnimationPeriod);
240
//TODO: consider rebuildShaders() instead..
245
OceanSurfaceNode::getOceanSurfaceImageSizeRadians() const
247
return _oceanSurfaceImageSizeRadians;
251
OceanSurfaceNode::setOceanSurfaceImageSizeRadians(float size)
253
if (_oceanSurfaceImageSizeRadians != size)
255
_oceanSurfaceImageSizeRadians = size;
256
shadersDirty( true );
261
OceanSurfaceNode::traverse( osg::NodeVisitor& nv )
263
if ( _shadersDirty && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR )
266
shadersDirty( false );
269
osg::Group::traverse( nv );
272
#define MASK_SAMPLER_FUNC "osgearth_ocean_sampleMask"
275
OceanSurfaceNode::rebuildShaders()
277
// need the terrain engine so we can get at the compositor.
278
TerrainEngineNode* engine = osgEarth::findTopMostNodeOfType<TerrainEngineNode>( this );
280
OE_DEBUG << LC << "No terrain engine found in the map node; abort" << std::endl;
284
// access the compositor because we are going to be sampling map layers.
285
TextureCompositor* comp = engine->getTextureCompositor();
287
OE_INFO << LC << "No texture compositor found in the terrain engine; abort" << std::endl;
291
// reserve a texture unit for the surface texture (if we haven't already)
292
if ( !_oceanSurfaceTextureApplied && _oceanSurfaceTextureUnit < 0 && _oceanSurfaceTexture.valid() )
294
if ( comp->reserveTextureImageUnit( _oceanSurfaceTextureUnit ) )
296
getOrCreateStateSet()->setTextureAttributeAndModes(
297
_oceanSurfaceTextureUnit, _oceanSurfaceTexture.get(), osg::StateAttribute::ON);
298
_oceanSurfaceTextureApplied = true;
302
OE_WARN << LC << "Sorry, failed to allocate a texture image unit for the surface texture." << std::endl;
306
// create a VP to store our custom shader components.
307
osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
308
getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );
310
// if the ocean is disabled, just return without injecting any shaders.
314
// build the sampler function if necessary
315
osg::ref_ptr<const ImageLayer> safeMaskLayer = _maskLayer.get();
316
osg::Shader* maskSampler = 0L;
317
if ( safeMaskLayer.valid() )
319
maskSampler = comp->createSamplerFunction( safeMaskLayer->getUID(), MASK_SAMPLER_FUNC, osg::Shader::VERTEX );
321
vp->setShader( MASK_SAMPLER_FUNC, maskSampler );
324
// make the helper functions.
326
std::stringstream buf;
328
buf << "vec3 xyz_to_lat_lon_height(in vec3 xyz) \n"
330
<< " float X = xyz.x;\n"
331
<< " float Y = xyz.y;\n"
332
<< " float Z = xyz.z;\n"
333
<< " float _radiusEquator = 6378137.0;\n"
334
<< " float _radiusPolar = 6356752.3142;\n"
335
<< " float flattening = (_radiusEquator-_radiusPolar)/_radiusEquator;\n"
336
<< " float _eccentricitySquared = 2.0*flattening - flattening*flattening;\n"
337
<< " float p = sqrt(X*X + Y*Y);\n"
338
<< " float theta = atan(Z*_radiusEquator , (p*_radiusPolar));\n"
339
<< " float eDashSquared = (_radiusEquator*_radiusEquator - _radiusPolar*_radiusPolar)/(_radiusPolar*_radiusPolar);\n"
340
<< " float sin_theta = sin(theta);\n"
341
<< " float cos_theta = cos(theta);\n"
342
<< " float latitude = atan( (Z + eDashSquared*_radiusPolar*sin_theta*sin_theta*sin_theta), (p - _eccentricitySquared*_radiusEquator*cos_theta*cos_theta*cos_theta) );\n"
343
<< " float longitude = atan(Y,X);\n"
344
<< " float sin_latitude = sin(latitude);\n"
345
<< " float N = _radiusEquator / sqrt( 1.0 - _eccentricitySquared*sin_latitude*sin_latitude);\n"
346
<< " float height = p/cos(latitude) - N;\n"
347
<< " return vec3(longitude, latitude, height);\n"
351
std::string str = buf.str();
352
vp->setShader( "xyz_to_lat_lon_height", new osg::Shader(osg::Shader::VERTEX, str) );
355
// next make the vertex shader function that will morph the ocean verts and prepare
356
// the texture coordinates for the surface effects.
358
std::stringstream buf;
362
buf << "uniform float osg_SimulationTime; \n"
363
<< "uniform mat4 osg_ViewMatrixInverse;\n"
364
<< "uniform mat4 osg_ViewMatrix;\n"
365
<< "uniform float osgearth_OceanHeight;\n"
366
<< "uniform float osgearth_OceanPeriod;\n"
367
<< "uniform float osgearth_OceanAnimationPeriod;\n"
368
<< "varying float osgearth_OceanAlpha;\n"
369
<< "varying float osgearth_CameraRange; \n"
371
<< "vec3 xyz_to_lat_lon_height(in vec3 xyz); \n";
373
if ( _oceanSurfaceTextureApplied )
375
buf << "varying vec3 osgearth_oceanSurfaceTexCoord; \n";
380
buf << "vec4 " << MASK_SAMPLER_FUNC << "(); \n";
383
buf << "void osgearth_ocean_morphSurface() \n"
385
<< " mat4 modelMatrix = osg_ViewMatrixInverse * gl_ModelViewMatrix; \n"
386
<< " vec4 vert = modelMatrix * gl_Vertex; \n"
387
<< " vec3 vert3 = vec3(vert.x, vert.y, vert.z); \n"
388
<< " vec3 latlon = xyz_to_lat_lon_height(vert3); \n"
389
<< " osgearth_OceanAlpha = 1.0; \n";
393
buf << " osgearth_OceanAlpha = 1.0 - (" << MASK_SAMPLER_FUNC << "()).a; \n";
397
buf << " osgearth_OceanAlpha = 1.0 - osgearth_OceanAlpha; \n";
399
buf << " if ( osgearth_CameraRange <= " << _maxRange << " ) \n"
401
<< " float s = mix(1.0, 0.0, osgearth_CameraRange / " << _maxRange << "); \n" //Invert so it's between 0 and 1
402
<< " osgearth_OceanAlpha *= s; \n"
406
<< " osgearth_OceanAlpha = 0.0; \n"
409
<< " if (osgearth_OceanAlpha > 0.0) \n"
411
<< " float PI_2 = 3.14158 * 2.0; \n"
412
<< " float period = PI_2/osgearth_OceanPeriod; \n"
413
<< " float half_period = period / 2.0; \n"
414
<< " vec3 n = normalize(vert3);\n"
415
<< " float theta = (mod(latlon.x, period) / period) * PI_2; \n"
416
<< " float phi = (mod(latlon.y, half_period) / half_period) * PI_2; \n"
417
<< " float phase1 = osg_SimulationTime * 2.0; \n"
418
<< " float phase2 = osg_SimulationTime * 4.0; \n"
419
<< " float waveHeight = (osgearth_OceanAlpha) * osgearth_OceanHeight; \n"
420
<< " float scale1 = sin(theta + phase1) * waveHeight; \n"
421
<< " float scale2 = cos(phi + phase2) * waveHeight; \n"
422
<< " float scale3 = sin(theta + phase2) * cos(phi + phase1) * waveHeight * 1.6; \n"
423
<< " float scale = (scale1 + scale2 + scale3)/3.0; \n";
425
// flatten verts to MSL:
428
buf << " vec3 offset = n * -latlon.z; \n"
429
<< " vert += vec4( offset.xyz, 0 ); \n";
432
// apply the save scale:
433
buf << " n = n * scale; \n"
434
<< " vert += vec4(n.x, n.y,n.z,0); \n"
435
<< " vert = osg_ViewMatrix * vert; \n"
436
<< " gl_Position = gl_ProjectionMatrix * vert; \n"
439
// set up the coords for the surface texture:
440
if ( _oceanSurfaceTextureApplied )
442
buf << " osgearth_oceanSurfaceTexCoord.x = latlon.x / " << _oceanSurfaceImageSizeRadians << "; \n"
443
<< " osgearth_oceanSurfaceTexCoord.y = latlon.y / " << _oceanSurfaceImageSizeRadians << "; \n"
444
<< " osgearth_oceanSurfaceTexCoord.z = fract(osg_SimulationTime/osgearth_OceanAnimationPeriod); \n";
449
// add as a custom user function in the shader composition:
450
std::string vertSource = buf.str();
451
vp->setFunction( "osgearth_ocean_morphSurface", vertSource, osgEarth::ShaderComp::LOCATION_VERTEX_PRE_TEXTURING );
454
// now we need a fragment function that will apply the ocean surface texture.
455
if ( _oceanSurfaceTextureApplied )
457
getOrCreateStateSet()->getOrCreateUniform( "osgearth_oceanSurfaceTex", osg::Uniform::SAMPLER_3D )->set( _oceanSurfaceTextureUnit );
459
std::stringstream buf;
461
buf << "uniform sampler3D osgearth_oceanSurfaceTex; \n"
462
<< "varying vec3 osgearth_oceanSurfaceTexCoord; \n"
463
<< "varying float osgearth_OceanAlpha; \n"
465
<< "void osgearth_ocean_applySurfaceTex( inout vec4 color ) \n"
467
<< " vec4 texel = texture3D(osgearth_oceanSurfaceTex, osgearth_oceanSurfaceTexCoord); \n"
468
<< " color = vec4( mix( color.rgb, texel.rgb, texel.a * osgearth_OceanAlpha ), color.a); \n"
471
std::string str = buf.str();
472
vp->setFunction( "osgearth_ocean_applySurfaceTex", str, osgEarth::ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );