]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/sky/cloudfield.cxx
simgear/scene/sky/sky.cxx: Include sg_inlines.h with simgear/ prefix as all other...
[simgear.git] / simgear / scene / sky / cloudfield.cxx
index 04b32db76285883ecc85544659985986b81c7e15..7d738563f5ac936e2ccdf5e160033c0dd7e464fc 100644 (file)
@@ -16,7 +16,7 @@
 //
 // You should have received a copy of the GNU General Public License
 // along with this program; if not, write to the Free Software
-// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //
 //
 
 #  include <simgear_config.h>
 #endif
 
+#include <osg/Fog>
+#include <osg/Texture2D>
+#include <osg/PositionAttitudeTransform>
+#include <osg/Vec4f>
+
 #include <simgear/compiler.h>
 
-#include <plib/sg.h>
-#include <plib/ssg.h>
 #include <simgear/math/sg_random.h>
 #include <simgear/math/sg_geodesy.hxx>
-#include <simgear/math/polar3d.hxx>
 
-#include STL_ALGORITHM
+#include <algorithm>
 #include <vector>
 
-SG_USING_STD(vector);
+using std::vector;
 
 #include <simgear/environment/visual_enviro.hxx>
+#include <simgear/scene/util/RenderConstants.hxx>
+#include <simgear/scene/util/SGUpdateVisitor.hxx>
 #include "sky.hxx"
 #include "newcloud.hxx"
 #include "cloudfield.hxx"
@@ -54,192 +58,158 @@ SG_USING_STD(vector);
 #  endif
 #endif
 
-extern SGSky *thesky;
+using namespace simgear;
 
-static list_of_culledCloud inViewClouds;
 
-// visibility distance for clouds in meters
-float SGCloudField::CloudVis = 25000.0f;
-bool SGCloudField::enable3D = false;
-// fieldSize must be > CloudVis or we can destroy the impostor cache
-// a cloud must only be seen once or the impostor will be generated for each of his positions
-double SGCloudField::fieldSize = 50000.0;
-float SGCloudField::density = 100.0;
+float SGCloudField::fieldSize = 50000.0f;
 double SGCloudField::timer_dt = 0.0;
-sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
-
-static int last_cache_size = 1*1024;
-static int cacheResolution = 64;
-static sgVec3 last_sunlight={0.0f, 0.0f, 0.0f};
-
-int SGCloudField::get_CacheResolution(void) {
-       return cacheResolution;
-}
-
-void SGCloudField::set_CacheResolution(int resolutionPixels) {
-       if(cacheResolution == resolutionPixels)
-               return;
-       cacheResolution = resolutionPixels;
-       if(enable3D) {
-               int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
-               if(count == 0)
-                       count = 1;
-               SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
-       }
-}
-
-int SGCloudField::get_CacheSize(void) { 
-       return SGNewCloud::cldCache->queryCacheSize(); 
-}
-
-void SGCloudField::set_CacheSize(int sizeKb) {
-       // apply in rendering option dialog
-       if(last_cache_size == sizeKb)
-               return;
-       if(sizeKb == 0)
-               return;
-       if(sizeKb)
-               last_cache_size = sizeKb;
-       if(enable3D) {
-               int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
-               if(count == 0)
-                       count = 1;
-               SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
-       }
-}
-void SGCloudField::set_CloudVis(float distance) {
-       if( distance <= fieldSize )
-               SGCloudField::CloudVis = distance;
-}
-void SGCloudField::set_density(float density) {
-       SGCloudField::density = density;
-}
-void SGCloudField::set_enable3dClouds(bool enable) {
-       if(enable3D == enable)
-               return;
-       enable3D = enable;
-       if(enable) {
-               int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
-               if(count == 0)
-                       count = 1;
-               SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
-       } else {
-               SGNewCloud::cldCache->setCacheSize(0);
-       }
-}
+float SGCloudField::view_distance = 20000.0f;
+SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
 
 // reposition the cloud layer at the specified origin and orientation
-void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt, float direction, float speed) {
-    sgMat4 T1, LON, LAT;
-    sgVec3 axis;
-
-    sgMakeTransMat4( T1, p );
-
-    sgSetVec3( axis, 0.0, 0.0, 1.0 );
-    sgMakeRotMat4( LON, lon * SGD_RADIANS_TO_DEGREES, axis );
-
-    sgSetVec3( axis, 0.0, 1.0, 0.0 );
-    sgMakeRotMat4( LAT, 90.0 - lat * SGD_RADIANS_TO_DEGREES, axis );
-
-    sgMat4 TRANSFORM;
-
-    sgCopyMat4( TRANSFORM, T1 );
-    sgPreMultMat4( TRANSFORM, LON );
-    sgPreMultMat4( TRANSFORM, LAT );
-
-    sgCoord layerpos;
-    sgSetCoord( &layerpos, TRANSFORM );
-
-       sgMakeCoordMat4( transform, &layerpos );
-
-
-       this->alt = alt;
-
-       // simulate clouds movement from wind
-    double sp_dist = speed*dt;
-    if (sp_dist > 0) {
-        double bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
-        double by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
-               relative_position[SG_X] += bx;
-               relative_position[SG_Y] += by;
-    }
-
-    if ( lon != last_lon || lat != last_lat || sp_dist != 0 ) {
-        Point3D start( last_lon, last_lat, 0.0 );
-        Point3D dest( lon, lat, 0.0 );
-        double course = 0.0, dist = 0.0;
-
-        calc_gc_course_dist( dest, start, &course, &dist );
-        // if start and dest are too close together,
-        // calc_gc_course_dist() can return a course of "nan".  If
-        // this happens, lets just use the last known good course.
-        // This is a hack, and it would probably be better to make
-        // calc_gc_course_dist() more robust.
-        if ( isnan(course) ) {
-            course = last_course;
-        } else {
-            last_course = course;
+bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
+                              double dt, int asl )
+{
+    osg::Matrix T, LON, LAT;
+    
+    // Always update the altitude transform, as this allows
+    // the clouds to rise and fall smoothly depending on environment updates.
+    altitude_transform->setPosition(osg::Vec3d(0.0, 0.0, (double) asl));
+    
+    // Calculating the reposition information is expensive. 
+    // Only perform the reposition every 60 frames.
+    reposition_count = (reposition_count + 1) % 60;
+    if ((reposition_count != 0) || !defined3D) return false;
+    
+    SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
+    
+    double dist = SGGeodesy::distanceM(cld_pos, pos);
+    
+    if (dist > (fieldSize * 2)) {
+        // First time or very large distance
+        SGVec3<double> cart;
+        SGGeodesy::SGGeodToCart(SGGeod::fromRad(lon, lat), cart);
+        T.makeTranslate(toOsg(cart));
+        
+        LON.makeRotate(lon, osg::Vec3(0, 0, 1));
+        LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
+
+        field_transform->setMatrix( LAT*LON*T );
+        cld_pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
+    } else if (dist > fieldSize) {
+        // Distance requires repositioning of cloud field.
+        // We can easily work out the direction to reposition
+        // from the course between the cloud position and the
+        // camera position.
+        SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
+        
+        float crs = SGGeoc::courseDeg(cld_pos, pos);
+        if ((crs < 45.0) || (crs > 315.0)) {
+            SGGeodesy::advanceRadM(cld_pos, 0.0, fieldSize, cld_pos);
         }
-
-        // calculate cloud movement due to external forces
-        double ax = 0.0, ay = 0.0;
-
-        if (dist > 0.0) {
-            ax = cos(course) * dist;
-            ay = sin(course) * dist;
+        
+        if ((crs > 45.0) && (crs < 135.0)) {
+            SGGeodesy::advanceRadM(cld_pos, SGD_PI_2, fieldSize, cld_pos);
         }
 
-               deltax += ax;
-               deltay += ay;
-
-        last_lon = lon;
-        last_lat = lat;
+        if ((crs > 135.0) && (crs < 225.0)) {
+            SGGeodesy::advanceRadM(cld_pos, SGD_PI, fieldSize, cld_pos);
+        }
+        
+        if ((crs > 225.0) && (crs < 315.0)) {
+            SGGeodesy::advanceRadM(cld_pos, SGD_PI + SGD_PI_2, fieldSize, cld_pos);
+        }
+        
+        SGVec3<double> cart;
+        SGGeodesy::SGGeodToCart(SGGeod::fromRad(cld_pos.getLongitudeRad(), cld_pos.getLatitudeRad()), cart);
+        T.makeTranslate(toOsg(cart));
+        
+        LON.makeRotate(cld_pos.getLongitudeRad(), osg::Vec3(0, 0, 1));
+        LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - cld_pos.getLatitudeRad(), osg::Vec3(0, 1, 0));
+
+        field_transform->setMatrix( LAT*LON*T );
     }
+    
+    // Render the clouds in order from farthest away layer to nearest one.
+    field_root->getStateSet()->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
 
-
-       // correct the frustum with the right far plane
-       ssgContext *context = ssgGetCurrentContext();
-       frustum = *context->getFrustum();
-
-       float w, h;
-       sgEnviro.getFOV( w, h );
-       frustum.setFOV( w, h );
-       frustum.setNearFar(1.0, CloudVis);
-       timer_dt = dt;
+    return true;
 }
 
 SGCloudField::SGCloudField() :
-       draw_in_3d(true),
-       last_density(0.0),
+        field_root(new osg::Group),
+        field_transform(new osg::MatrixTransform),
+        altitude_transform(new osg::PositionAttitudeTransform),
        deltax(0.0),
        deltay(0.0),
-       last_course(0.0)
+       last_course(0.0),
+       last_coverage(0.0),
+        coverage(0.0),
+        reposition_count(0),
+        defined3D(false)
 {
-       sgSetVec3( relative_position, 0,0,0);
-       theField.reserve(200);
-       inViewClouds.reserve(200);
-        sg_srandom_time_10();
+    cld_pos = SGGeoc();
+    field_root->addChild(field_transform.get());
+    field_root->setName("3D Cloud field root");
+    osg::StateSet *rootSet = field_root->getOrCreateStateSet();
+    rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
+    rootSet->setAttributeAndModes(getFog());
+    
+    osg::ref_ptr<osg::Group> quad_root = new osg::Group();
+    
+    for (int i = 0; i < BRANCH_SIZE; i++) {
+        for (int j = 0; j < BRANCH_SIZE; j++) {
+            quad[i][j] = new osg::LOD();
+            quad[i][j]->setName("Quad");
+            quad_root->addChild(quad[i][j].get());
+        }
+    }
+    
+    int leafs = QUADTREE_SIZE / BRANCH_SIZE;
+
+    for (int x = 0; x < QUADTREE_SIZE; x++) {
+        for (int y = 0; y < QUADTREE_SIZE; y++) {
+            field_group[x][y]= new osg::Switch;
+            field_group[x][y]->setName("3D cloud group");
+            
+            // Work out where to put this node in the quad tree
+            int i = x / leafs;
+            int j = y / leafs;
+            quad[i][j]->addChild(field_group[x][y].get(), 0.0f, view_distance);
+        }
+    }
+    
+    field_transform->addChild(altitude_transform.get());
+            
+    // We duplicate the defined field group in a 3x3 array. This way,
+    // we can simply shift entire groups around.
+    // TODO: "Bend" the edge groups so when shifted they line up.
+    // Currently the clouds "jump down" when we reposition them.
+    for(int x = -1 ; x <= 1 ; x++) {
+        for(int y = -1 ; y <= 1 ; y++ ) {
+            osg::ref_ptr<osg::PositionAttitudeTransform> transform =
+                    new osg::PositionAttitudeTransform;
+            transform->addChild(quad_root.get());
+            transform->setPosition(osg::Vec3(x*fieldSize, y * fieldSize, 0.0));
+            
+            altitude_transform->addChild(transform.get());
+        }
+    }
 }
 
 SGCloudField::~SGCloudField() {
-       list_of_Cloud::iterator iCloud;
-       for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
-               delete iCloud->aCloud;
-       }
-       theField.clear();
 }
 
 
 void SGCloudField::clear(void) {
-       list_of_Cloud::iterator iCloud;
-       for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
-               delete iCloud->aCloud;
-       }
-       theField.clear();
-       // force a recompute of density on first redraw
-       last_density = 0.0;
-       // true to come back in set density after layer is built
-       draw_in_3d = true;
+    for (int x = 0; x < QUADTREE_SIZE; x++) {
+        for (int y = 0; y < QUADTREE_SIZE; y++) {
+            int num_children = field_group[x][y]->getNumChildren();
+            field_group[x][y]->removeChildren(0, num_children);
+        }
+    }
+    
+    SGCloudField::defined3D = false;
 }
 
 // use a table or else we see poping when moving the slider...
@@ -257,237 +227,76 @@ static int densTable[][10] = {
        {1,1,1,1,1,1,1,1,1,1}
 };
 
-// set the visible flag depending on density
-void SGCloudField::applyDensity(void) {
-       int row = (int) (density / 10.0);
-       int col = 0;
-    sgBox fieldBox;
-
-       list_of_Cloud::iterator iCloud;
-       for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
-               if(++col > 9)
-                       col = 0;
-               if( densTable[row][col] ) {
-                       iCloud->visible = true;
-            fieldBox.extend( *iCloud->aCloud->getCenter() );
-               } else
-                       iCloud->visible = false;
-       }
-       last_density = density;
-       draw_in_3d = ( theField.size() != 0);
-    sgVec3 center;
-    sgSubVec3( center, fieldBox.getMax(), fieldBox.getMin() );
-    sgScaleVec3( center, 0.5f );
-    center[1] = 0.0f;
-    field_sphere.setCenter( center );
-    field_sphere.setRadius( fieldSize * 0.5f * 1.414f );
-}
+void SGCloudField::applyCoverage(void) {
+
+        int row = (int) (coverage * 10.0);
+        if (row > 9) row = 9;
+        int col = 0;
+
+        if (coverage != last_coverage) {
+            for (int x = 0; x < QUADTREE_SIZE; x++) {
+                for (int y = 0; y < QUADTREE_SIZE; y++) {
+                // Switch on/off the children depending on the required coverage.
+                    int num_children = field_group[x][y]->getNumChildren();
+                    for (int i = 0; i < num_children; i++) {
+                        if (++col > 9) col = 0;
+                        if ( densTable[row][col] ) {
+                            field_group[x][y]->setValue(i, true);
+                        } else {
+                            field_group[x][y]->setValue(i, false);
+                        }
+                    }
+                }
+            }
+        }
 
-// add one cloud, data is not copied, ownership given
-void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
-       Cloud cl;
-       cl.aCloud = cloud;
-       cl.visible = true;
-       cloud->SetPos( pos );
-       sgCopyVec3( cl.pos, *cloud->getCenter() );
-       theField.push_back( cl );
+        last_coverage = coverage;
 }
 
-
-static float Rnd(float n) {
-       return n * (-0.5f + sg_random());
+void SGCloudField::addCloud( SGVec3f& pos, SGNewCloud *cloud) {
+        defined3D = true;
+        osg::ref_ptr<osg::Geode> geode = cloud->genCloud();
+        
+        // Determine which quadtree to put it in.
+        int x = (int) floor((pos.x() + fieldSize/2.0) * QUADTREE_SIZE / fieldSize);
+        if (x >= QUADTREE_SIZE) x = (QUADTREE_SIZE - 1);
+        if (x < 0) x = 0;
+        
+        int y = (int) floor((pos.y() + fieldSize/2.0) * QUADTREE_SIZE / fieldSize);
+        if (y >= QUADTREE_SIZE) y = (QUADTREE_SIZE - 1);
+        if (y < 0) y = 0;
+        
+        osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
+
+        transform->setPosition(toOsg(pos));
+        transform->addChild(geode.get());
+        
+        field_group[x][y]->addChild(transform.get(), true);
 }
 
-// for debug only
-// build a field of cloud of size 25x25 km, its a grid of 11x11 clouds
-void SGCloudField::buildTestLayer(void) {
-
-       const float s = 2250.0f;
-
-       for( int z = -5 ; z <= 5 ; z++) {
-               for( int x = -5 ; x <= 5 ; x++ ) {
-                       SGNewCloud *cloud = new SGNewCloud(SGNewCloud::CLFamilly_cu);
-                       cloud->new_cu();
-                       sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s};
-            addCloud(pos, cloud);
-               }
-       }
-       applyDensity();
+void SGCloudField::applyVisRange(void) { 
+    
+    for (int x = 0; x < BRANCH_SIZE; x++) {
+        for (int y = 0; y < BRANCH_SIZE; y++) {
+            int num_children = quad[x][y]->getNumChildren(); 
+            for (int i = 0; i < num_children; i++) { 
+                quad[x][y]->setRange(i, 0.0f, view_distance);
+            }
+        }
+    }
 }
 
-// cull all clouds of a tiled field
-void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
-       list_of_Cloud::iterator iCloud;
-
-    sgSphere tile_sphere;
-    tile_sphere.setRadius( field_sphere.getRadius() );
-    sgVec3 tile_center;
-    sgSubVec3( tile_center, field_sphere.getCenter(), eyePos );
-    tile_sphere.setCenter( tile_center );
-    tile_sphere.orthoXform(mat);
-    if( frustum.contains( & tile_sphere ) == SG_OUTSIDE )
-        return;
-
-       for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
-               sgVec3 dist;
-               sgSphere sphere;
-               if( ! iCloud->visible )
-                       continue;
-               sgSubVec3( dist, iCloud->pos, eyePos );
-               sphere.setCenter(dist[0], dist[2], dist[1] + eyePos[1]);
-               float radius = iCloud->aCloud->getRadius();
-               sphere.setRadius(radius);
-               sphere.orthoXform(mat);
-               if( frustum.contains( & sphere ) != SG_OUTSIDE ) {
-                       float squareDist = dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2];
-                       culledCloud tmp;
-                       tmp.aCloud = iCloud->aCloud;
-                       sgCopyVec3( tmp.eyePos, eyePos ); 
-                       // save distance for later sort, opposite distance because we want back to front
-                       tmp.dist   =  - squareDist;
-                       tmp.heading = -SG_PI/2.0 - atan2( dist[0], dist[2] ); // + SG_PI;
-                       tmp.alt         = iCloud->pos[1];
-                       inViewClouds.push_back(tmp);
-                       if( squareDist - radius*radius < 100*100 )
-                               sgEnviro.set_view_in_cloud(true);
-               }
-       }
-
+SGCloudField::CloudFog::CloudFog()
+{
+    fog = new osg::Fog;
+    fog->setMode(osg::Fog::EXP2);
+    fog->setDataVariance(osg::Object::DYNAMIC);
 }
 
-
-// Render a cloud field
-// because no field can have an infinite size (and we don't want to reach his border)
-// we draw this field and adjacent fields.
-// adjacent fields are not real, its the same field displaced by some offset
-void SGCloudField::Render(void) {
-    sgVec3 eyePos;
-       double relx, rely;
-
-       if( ! enable3D )
-               return;
-
-       if( last_density != density ) {
-               last_density = density;
-               applyDensity();
-       }
-
-       if( ! draw_in_3d )
-               return;
-
-       if( ! SGNewCloud::cldCache->isRttAvailable() )
-               return;
-
-       inViewClouds.clear();
-
-       glPushMatrix();
-       sgMat4 modelview, tmp, invtrans;
-
-       // try to find the sun position
-       sgTransposeNegateMat4( invtrans, transform );
-    sgVec3 lightVec;
-    ssgGetLight( 0 )->getPosition( lightVec );
-    sgXformVec3( lightVec, invtrans );
-
-       sgSetVec3(  SGNewCloud::modelSunDir, lightVec[0], lightVec[2], lightVec[1]);
-       // try to find the lighting data (not accurate)
-       sgVec4 diffuse, ambient;
-       ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
-       ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient );
-//     sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 1.0f);
-       sgScaleVec3 ( SGNewCloud::ambLight, ambient , 1.1f);
-       // trying something else : clouds are more yellow/red at dawn/dusk
-       // and added a bit of blue ambient
-    float *sun_color = thesky->get_sun_color();
-       sgScaleVec3 ( SGNewCloud::sunlight, sun_color , 0.4f);
-       SGNewCloud::ambLight[2] += 0.1f;
-
-       sgVec3 delta_light;
-       sgSubVec3(delta_light, last_sunlight, SGNewCloud::sunlight);
-       if( (fabs(delta_light[0]) + fabs(delta_light[1]) + fabs(delta_light[2])) > 0.05f ) {
-               sgCopyVec3( last_sunlight, SGNewCloud::sunlight );
-               // force the redraw of all the impostors
-               SGNewCloud::cldCache->invalidateCache();
-       }
-
-       // voodoo things on the matrix stack
-    ssgGetModelviewMatrix( modelview );
-       sgCopyMat4( tmp, transform );
-    sgPostMultMat4( tmp, modelview );
-
-       // cloud fields are tiled on the flat earth
-       // compute the position in the tile
-       relx = fmod( deltax + relative_position[SG_X], fieldSize );
-       rely = fmod( deltay + relative_position[SG_Y], fieldSize );
-
-       relx = fmod( relx + fieldSize, fieldSize );
-       rely = fmod( rely + fieldSize, fieldSize );
-       sgSetVec3( eyePos, relx, alt, rely);
-
-       sgSetVec3( view_X, tmp[0][0], tmp[1][0], tmp[2][0] );
-       sgSetVec3( view_Y, tmp[0][1], tmp[1][1], tmp[2][1] );
-       sgSetVec3( view_vec, tmp[0][2], tmp[1][2], tmp[2][2] );
-
-    ssgLoadModelviewMatrix( tmp );
-/* flat earth
-
-       ^
-       |
-       |    FFF
-       |    FoF
-       |    FFF
-       |
-       O----------->
-               o = we are here
-               F = adjacent fields
-*/
-
-       for(int x = -1 ; x <= 1 ; x++)
-               for(int y = -1 ; y <= 1 ; y++ ) {
-                       sgVec3 fieldPos;
-                       // pretend we are not where we are
-                       sgSetVec3(fieldPos, eyePos[0] + x*fieldSize, eyePos[1], eyePos[2] + y*fieldSize);
-                       cullClouds(fieldPos, tmp);
-               }
-       // sort all visible clouds back to front (because of transparency)
-       std::sort( inViewClouds.begin(), inViewClouds.end() );
-       // TODO:push states
-       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-    glEnable(GL_ALPHA_TEST);
-    glAlphaFunc(GL_GREATER, 0.0f);
-       glDisable(GL_CULL_FACE);
-       glEnable(GL_DEPTH_TEST);
-       glDepthMask( GL_FALSE );
-       glEnable(GL_SMOOTH);
-    glEnable(GL_BLEND);
-       glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
-       glEnable( GL_TEXTURE_2D );
-       glDisable( GL_FOG );
-    glDisable(GL_LIGHTING);
-
-       // test data: field = 11x11 cloud, 9 fields
-       // depending on position and view direction, perhaps 40 to 60 clouds not culled
-       list_of_culledCloud::iterator iCloud;
-       for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) {
-//             iCloud->aCloud->drawContainers();
-               iCloud->aCloud->Render(iCloud->eyePos);
-               sgEnviro.callback_cloud(iCloud->heading, iCloud->alt, 
-                       iCloud->aCloud->getRadius(), iCloud->aCloud->getFamilly(), - iCloud->dist, iCloud->aCloud->getId());
-       }
-
-       glBindTexture(GL_TEXTURE_2D, 0);
-    glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
-       glEnable( GL_FOG );
-       glEnable(GL_CULL_FACE);
-       glEnable(GL_DEPTH_TEST);
-
-       ssgLoadModelviewMatrix( modelview );
-
-       glPopMatrix();
-
+void SGCloudField::updateFog(double visibility, const osg::Vec4f& color)
+{
+    const double sqrt_m_log01 = sqrt(-log(0.01));
+    osg::Fog* fog = CloudFog::instance()->fog.get();
+    fog->setColor(color);
+    fog->setDensity(sqrt_m_log01 / visibility);
 }
-