~ubuntu-branches/debian/sid/osgearth/sid

« back to all changes in this revision

Viewing changes to src/osgEarthUtil/OceanSurfaceNode.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Pirmin Kalberer
  • Date: 2011-07-14 22:13:36 UTC
  • Revision ID: james.westby@ubuntu.com-20110714221336-94igk9rskxveh794
Tags: upstream-2.0+dfsg
ImportĀ upstreamĀ versionĀ 2.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*-c++-*- */
 
2
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 
3
* Copyright 2008-2010 Pelican Mapping
 
4
* http://osgearth.org
 
5
*
 
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.
 
10
*
 
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.
 
15
*
 
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/>
 
18
*/
 
19
 
 
20
#include <osgEarthUtil/OceanSurfaceNode>
 
21
 
 
22
#include <osgEarth/FindNode>
 
23
#include <osgEarth/Notify>
 
24
#include <osgEarth/Registry>
 
25
#include <osgEarth/ShaderComposition>
 
26
#include <osgEarth/MapNode>
 
27
#include <osgEarth/FindNode>
 
28
 
 
29
#include <osg/Texture3D>
 
30
#include <osgDB/ReadFile>
 
31
 
 
32
#include <sstream>
 
33
#include <iomanip>
 
34
 
 
35
#define LC "[OceanSurfaceNode] "
 
36
 
 
37
using namespace osgEarth;
 
38
using namespace osgEarth::Util;
 
39
 
 
40
typedef std::vector< osg::ref_ptr< osg::Image > > ImageList;
 
41
 
 
42
OceanSurfaceNode::OceanSurfaceNode() :
 
43
_shadersDirty(false),
 
44
_maxRange(800000),
 
45
_oceanMaskLayerUID(-1),
 
46
_oceanSurfaceTextureUnit(-1),
 
47
_oceanSurfaceTextureApplied(false),
 
48
_waveHeight(100),
 
49
_period(1024),
 
50
_enabled(true),
 
51
_invertMask(false),
 
52
_adjustToMSL(true),
 
53
_oceanColor(osg::Vec4f(0,0,1,0)),
 
54
_oceanAnimationPeriod(6.0),
 
55
_oceanSurfaceImageSizeRadians(osg::PI/500.0)
 
56
{
 
57
    rebuildShaders();
 
58
 
 
59
    getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanPeriod", osg::Uniform::FLOAT)->set(_period);   
 
60
    getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanAnimationPeriod", osg::Uniform::FLOAT)->set(_oceanAnimationPeriod); 
 
61
 
 
62
    osg::Uniform* oceanHeightUniform = getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanHeight", osg::Uniform::FLOAT);
 
63
    oceanHeightUniform->set( _waveHeight);
 
64
    oceanHeightUniform->setDataVariance( osg::Object::DYNAMIC);
 
65
 
 
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);
 
73
}
 
74
 
 
75
void
 
76
OceanSurfaceNode::shadersDirty(bool value)
 
77
{
 
78
    if ( _shadersDirty != value )
 
79
    {
 
80
        _shadersDirty = value;
 
81
        ADJUST_UPDATE_TRAV_COUNT( this, _shadersDirty ? 1 : -1 );
 
82
    }
 
83
}
 
84
 
 
85
void
 
86
OceanSurfaceNode::setOceanMaskImageLayer( const ImageLayer* layer )
 
87
{
 
88
    if ( _maskLayer.get() != layer )
 
89
    {
 
90
        _maskLayer = layer;
 
91
        shadersDirty(true);
 
92
    }
 
93
}
 
94
 
 
95
bool
 
96
OceanSurfaceNode::getAdjustToMSL() const
 
97
{
 
98
        return _adjustToMSL;
 
99
}
 
100
 
 
101
void
 
102
OceanSurfaceNode::setAdjustToMSL(bool adjustToMSL)
 
103
{
 
104
        if (_adjustToMSL != adjustToMSL)
 
105
        {
 
106
                _adjustToMSL = adjustToMSL;
 
107
        shadersDirty( true );
 
108
        }
 
109
}
 
110
 
 
111
osg::Image*
 
112
OceanSurfaceNode::getOceanSurfaceImage() const
 
113
{
 
114
    return _oceanSurfaceImage.get();
 
115
}
 
116
 
 
117
void
 
118
OceanSurfaceNode::setOceanSurfaceImage(osg::Image* image)
 
119
{
 
120
    if (_oceanSurfaceImage.get() != image)
 
121
    {
 
122
        _oceanSurfaceImage = image;
 
123
        _oceanSurfaceTexture->setImage( _oceanSurfaceImage.get() );
 
124
        
 
125
        shadersDirty( true );
 
126
    }
 
127
}
 
128
 
 
129
float
 
130
OceanSurfaceNode::getWaveHeight() const
 
131
{
 
132
    return _waveHeight;
 
133
}
 
134
 
 
135
void
 
136
OceanSurfaceNode::setWaveHeight(float waveHeight)
 
137
{
 
138
    if (_waveHeight != waveHeight)
 
139
    {
 
140
        _waveHeight = waveHeight;
 
141
        getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanHeight", osg::Uniform::FLOAT)->set(_waveHeight);
 
142
        //TODO: consider rebuildShaders() instead..
 
143
    }
 
144
}
 
145
 
 
146
float
 
147
OceanSurfaceNode::getMaxRange() const
 
148
{
 
149
    return _maxRange;
 
150
}
 
151
 
 
152
void
 
153
OceanSurfaceNode::setMaxRange(float maxRange)
 
154
{
 
155
    if (_maxRange != maxRange)
 
156
    {
 
157
        _maxRange = maxRange;
 
158
        shadersDirty(true);
 
159
    }
 
160
}
 
161
 
 
162
float
 
163
OceanSurfaceNode::getPeriod() const
 
164
{
 
165
    return _period;
 
166
}
 
167
 
 
168
void
 
169
OceanSurfaceNode::setPeriod(float period)
 
170
{
 
171
    if (_period !=period)
 
172
    {
 
173
        _period = period;
 
174
        getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanPeriod", osg::Uniform::FLOAT)->set(_period); 
 
175
        //TODO: consider rebuildShaders() instead..    
 
176
    }
 
177
}
 
178
 
 
179
bool
 
180
OceanSurfaceNode::getEnabled() const
 
181
{
 
182
    return _enabled;
 
183
}
 
184
 
 
185
void
 
186
OceanSurfaceNode::setEnabled(bool enabled)
 
187
{
 
188
    if (_enabled != enabled)
 
189
    {
 
190
        _enabled = enabled;
 
191
        shadersDirty(true);
 
192
    }
 
193
}
 
194
 
 
195
bool
 
196
OceanSurfaceNode::getInvertMask() const
 
197
{
 
198
    return _invertMask;
 
199
}
 
200
 
 
201
void
 
202
OceanSurfaceNode::setInvertMask(bool invertMask)
 
203
{
 
204
    if (_invertMask != invertMask)
 
205
    {
 
206
        _invertMask = invertMask;
 
207
        shadersDirty( true );
 
208
    }
 
209
}
 
210
 
 
211
void
 
212
OceanSurfaceNode::setModulationColor( const osg::Vec4f& color )
 
213
{
 
214
    if ( !_oceanColor.isSetTo( color ) )
 
215
    {
 
216
        _oceanColor = color;
 
217
        shadersDirty( true );
 
218
    }
 
219
}
 
220
 
 
221
osg::Vec4f
 
222
OceanSurfaceNode::getModulationColor() const
 
223
{
 
224
    return _oceanColor.value();
 
225
}
 
226
 
 
227
float
 
228
OceanSurfaceNode::getOceanAnimationPeriod() const
 
229
{
 
230
    return _oceanAnimationPeriod;
 
231
}
 
232
 
 
233
void
 
234
OceanSurfaceNode::setOceanAnimationPeriod(float oceanAnimationPeriod)
 
235
{
 
236
    if (_oceanAnimationPeriod != oceanAnimationPeriod)
 
237
    {
 
238
        _oceanAnimationPeriod = oceanAnimationPeriod;
 
239
        getOrCreateStateSet()->getOrCreateUniform("osgearth_OceanAnimationPeriod", osg::Uniform::FLOAT)->set(oceanAnimationPeriod); 
 
240
        //TODO: consider rebuildShaders() instead..
 
241
    }
 
242
}
 
243
 
 
244
float
 
245
OceanSurfaceNode::getOceanSurfaceImageSizeRadians() const
 
246
{
 
247
    return _oceanSurfaceImageSizeRadians;
 
248
}
 
249
 
 
250
void
 
251
OceanSurfaceNode::setOceanSurfaceImageSizeRadians(float size)
 
252
{
 
253
    if (_oceanSurfaceImageSizeRadians != size)
 
254
    {
 
255
        _oceanSurfaceImageSizeRadians = size;
 
256
        shadersDirty( true );
 
257
    }
 
258
}
 
259
 
 
260
void
 
261
OceanSurfaceNode::traverse( osg::NodeVisitor& nv )
 
262
{
 
263
    if ( _shadersDirty && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR )
 
264
    {
 
265
        rebuildShaders();
 
266
        shadersDirty( false );
 
267
    }
 
268
 
 
269
    osg::Group::traverse( nv );
 
270
}
 
271
 
 
272
#define MASK_SAMPLER_FUNC "osgearth_ocean_sampleMask"
 
273
 
 
274
void
 
275
OceanSurfaceNode::rebuildShaders()
 
276
{
 
277
    // need the terrain engine so we can get at the compositor.
 
278
    TerrainEngineNode* engine = osgEarth::findTopMostNodeOfType<TerrainEngineNode>( this );
 
279
    if ( !engine ) {
 
280
        OE_DEBUG << LC << "No terrain engine found in the map node; abort" << std::endl;
 
281
        return;
 
282
    }
 
283
 
 
284
    // access the compositor because we are going to be sampling map layers.
 
285
    TextureCompositor* comp = engine->getTextureCompositor();
 
286
    if ( !comp ) {
 
287
        OE_INFO << LC << "No texture compositor found in the terrain engine; abort" << std::endl;
 
288
        return;
 
289
    }
 
290
 
 
291
    // reserve a texture unit for the surface texture (if we haven't already)
 
292
    if ( !_oceanSurfaceTextureApplied && _oceanSurfaceTextureUnit < 0 && _oceanSurfaceTexture.valid() )
 
293
    {
 
294
        if ( comp->reserveTextureImageUnit( _oceanSurfaceTextureUnit ) )
 
295
        {
 
296
            getOrCreateStateSet()->setTextureAttributeAndModes(
 
297
                _oceanSurfaceTextureUnit, _oceanSurfaceTexture.get(), osg::StateAttribute::ON);
 
298
            _oceanSurfaceTextureApplied = true;
 
299
        }
 
300
        else
 
301
        {
 
302
            OE_WARN << LC << "Sorry, failed to allocate a texture image unit for the surface texture." << std::endl;
 
303
        }
 
304
    }
 
305
 
 
306
    // create a VP to store our custom shader components.
 
307
    osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
 
308
    getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );
 
309
 
 
310
    // if the ocean is disabled, just return without injecting any shaders.
 
311
    if ( !_enabled )
 
312
        return;
 
313
 
 
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() )
 
318
    {
 
319
        maskSampler = comp->createSamplerFunction( safeMaskLayer->getUID(), MASK_SAMPLER_FUNC, osg::Shader::VERTEX );        
 
320
        if ( maskSampler )
 
321
            vp->setShader( MASK_SAMPLER_FUNC, maskSampler );
 
322
    }
 
323
 
 
324
    // make the helper functions.
 
325
    {
 
326
        std::stringstream buf;
 
327
 
 
328
        buf << "vec3 xyz_to_lat_lon_height(in vec3 xyz) \n"
 
329
            << "{ \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"
 
348
            << "} \n"
 
349
            << "\n";
 
350
 
 
351
        std::string str = buf.str();
 
352
        vp->setShader( "xyz_to_lat_lon_height", new osg::Shader(osg::Shader::VERTEX, str) );
 
353
    }
 
354
 
 
355
    // next make the vertex shader function that will morph the ocean verts and prepare
 
356
    // the texture coordinates for the surface effects.
 
357
    {
 
358
        std::stringstream buf;
 
359
 
 
360
        buf << std::fixed;
 
361
 
 
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"
 
370
 
 
371
            << "vec3 xyz_to_lat_lon_height(in vec3 xyz); \n";
 
372
 
 
373
        if ( _oceanSurfaceTextureApplied )
 
374
        {
 
375
            buf << "varying vec3 osgearth_oceanSurfaceTexCoord; \n";
 
376
        }
 
377
 
 
378
        if ( maskSampler )
 
379
        {
 
380
            buf << "vec4 " << MASK_SAMPLER_FUNC << "(); \n";
 
381
        }
 
382
 
 
383
        buf << "void osgearth_ocean_morphSurface() \n"
 
384
            << "{ \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";
 
390
 
 
391
        if ( maskSampler )
 
392
        {
 
393
            buf << "    osgearth_OceanAlpha = 1.0 - (" << MASK_SAMPLER_FUNC << "()).a; \n";
 
394
        }
 
395
 
 
396
        if ( _invertMask )
 
397
            buf << "    osgearth_OceanAlpha = 1.0 - osgearth_OceanAlpha; \n";
 
398
 
 
399
        buf << "   if ( osgearth_CameraRange <= " << _maxRange << " ) \n"
 
400
            << "   { \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"
 
403
            << "   } \n"
 
404
            << "   else \n"
 
405
            << "   { \n"
 
406
            << "        osgearth_OceanAlpha = 0.0; \n"
 
407
            << "   } \n"
 
408
 
 
409
            << "   if (osgearth_OceanAlpha > 0.0) \n"
 
410
            << "   { \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";
 
424
 
 
425
        // flatten verts to MSL:
 
426
        if ( _adjustToMSL )
 
427
        {
 
428
            buf << "        vec3 offset = n * -latlon.z; \n"
 
429
                << "        vert += vec4( offset.xyz, 0 ); \n";
 
430
        }
 
431
 
 
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"
 
437
            << "   }\n";
 
438
 
 
439
        // set up the coords for the surface texture:
 
440
        if ( _oceanSurfaceTextureApplied )
 
441
        {
 
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";
 
445
        }
 
446
 
 
447
        buf << "}\n";
 
448
 
 
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 );
 
452
    }
 
453
    
 
454
    // now we need a fragment function that will apply the ocean surface texture.
 
455
    if ( _oceanSurfaceTextureApplied )
 
456
    {
 
457
        getOrCreateStateSet()->getOrCreateUniform( "osgearth_oceanSurfaceTex", osg::Uniform::SAMPLER_3D )->set( _oceanSurfaceTextureUnit );
 
458
 
 
459
        std::stringstream buf;
 
460
 
 
461
        buf << "uniform sampler3D osgearth_oceanSurfaceTex; \n"
 
462
            << "varying vec3      osgearth_oceanSurfaceTexCoord; \n"
 
463
            << "varying float     osgearth_OceanAlpha; \n"
 
464
 
 
465
            << "void osgearth_ocean_applySurfaceTex( inout vec4 color ) \n"
 
466
            << "{ \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"
 
469
            << "} \n";
 
470
 
 
471
        std::string str = buf.str();
 
472
        vp->setFunction( "osgearth_ocean_applySurfaceTex", str, osgEarth::ShaderComp::LOCATION_FRAGMENT_PRE_LIGHTING );
 
473
    }
 
474
}
 
475