]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/sky/cloudfield.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[simgear.git] / simgear / scene / sky / cloudfield.cxx
index 2c048a97864343e4a6ca95f8bc250b36b1c1d75e..45ae15f83c4b4cd9feb71f7d376b28e5578b0d0d 100644 (file)
 #  include <simgear_config.h>
 #endif
 
+#include <osg/Fog>
+#include <osg/Texture2D>
+#include <osg/PositionAttitudeTransform>
+#include <osg/Vec4f>
+#include <osgSim/Impostor>
+
 #include <simgear/compiler.h>
 
-#include <plib/sg.h>
 #include <simgear/math/sg_random.h>
 #include <simgear/math/sg_geodesy.hxx>
-#include <simgear/math/polar3d.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
 
 #include <algorithm>
 #include <vector>
+#include <iostream>
+
+using namespace std;
 
-SG_USING_STD(vector);
+using std::vector;
 
-#include <simgear/environment/visual_enviro.hxx>
+//#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"
@@ -53,480 +63,351 @@ SG_USING_STD(vector);
 #  endif
 #endif
 
+using namespace simgear;
 
-#if defined (__CYGWIN__)
-#include <ieeefp.h>
-#endif
-
-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) {
-#if 0
-       return cacheResolution;
-#endif
-        return 0;
+float SGCloudField::view_distance = 20000.0f;
+bool SGCloudField::wrap = true;
+float SGCloudField::MAX_CLOUD_DEPTH = 2000.0f;
+bool SGCloudField::use_impostors = false;
+float SGCloudField::lod1_range = 8000.0f;
+float SGCloudField::lod2_range = 4000.0f;
+float SGCloudField::impostor_distance = 15000.0f;
+
+int impostorcount = 0;
+int lodcount = 0;
+int cloudcount = 0;
+
+SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
+
+
+// Reposition the cloud layer at the specified origin and orientation
+bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
+                              double dt, int asl, float speed, float direction ) {
+    // Determine any movement of the placed clouds
+    if (placed_root->getNumChildren() == 0) return false;
+
+    SGVec3<double> cart;
+    SGGeod new_pos = SGGeod::fromRadFt(lon, lat, 0.0f);
+                            
+    SGGeodesy::SGGeodToCart(new_pos, cart);
+    osg::Vec3f osg_pos = toOsg(cart);
+    osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
+    
+    // Always update the altitude transform, as this allows
+    // the clouds to rise and fall smoothly depending on environment updates.
+    altitude_transform->setPosition(osg::Vec3f(0.0f, 0.0f, (float) asl));
+    
+    // Similarly, always determine the effects of the wind
+    osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
+                                 sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
+                                 0.0f);
+    osg::Vec3f windosg = field_transform->getAttitude() * wind;
+    field_transform->setPosition(field_transform->getPosition() + windosg);
+    
+    if (!wrap) {
+        // If we're not wrapping the cloudfield, then we make no effort to reposition
+        return false;
+    }
+        
+    if ((old_pos - osg_pos).length() > fieldSize*2) {
+        // Big movement - reposition centered to current location.
+        field_transform->setPosition(osg_pos);
+        field_transform->setAttitude(orient);
+        old_pos = osg_pos;
+    } else {        
+        osg::Quat fta =  field_transform->getAttitude();
+        osg::Quat ftainv = field_transform->getAttitude().inverse();
+        
+        // delta is the vector from the old position to the new position in cloud-coords
+        // osg::Vec3f delta = ftainv * (osg_pos - old_pos);
+        old_pos = osg_pos;
+                
+        // Check if any of the clouds should be moved.
+        for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
+            itr != end;
+            ++itr) {
+              
+             osg::ref_ptr<osg::PositionAttitudeTransform> pat = itr->second;
+            
+             if (pat == 0) {
+                continue;
+             }
+             
+             osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition();
+                                      
+             // Determine the vector from the new position to the cloud in cloud-space.
+             osg::Vec3f w =  ftainv * (currpos - toOsg(cart));               
+              
+             // Determine a course if required. Note that this involves some axis translation.
+             float x = 0.0;
+             float y = 0.0;
+             if (w.x() >  0.6*fieldSize) { y =  fieldSize; }
+             if (w.x() < -0.6*fieldSize) { y = -fieldSize; }
+             if (w.y() >  0.6*fieldSize) { x = -fieldSize; }
+             if (w.y() < -0.6*fieldSize) { x =  fieldSize; }
+              
+             if ((x != 0.0) || (y != 0.0)) {
+                 removeCloudFromTree(pat);
+                 SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() + 
+                                                  fta * pat->getPosition()));
+                 addCloudToTree(pat, p, x, y);                
+            }
+        }        
+    }
+    
+    // Render the clouds in order from farthest away layer to nearest one.
+    field_root->getStateSet()->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
+    return true;
 }
 
-void SGCloudField::set_CacheResolution(int resolutionPixels) {
-#if 0
-       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);
-       }
-#endif
+SGCloudField::SGCloudField() :
+        field_root(new osg::Group),
+        field_transform(new osg::PositionAttitudeTransform),
+        altitude_transform(new osg::PositionAttitudeTransform)
+{
+    old_pos = osg::Vec3f(0.0f, 0.0f, 0.0f);
+    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());
+    
+    field_transform->addChild(altitude_transform.get());
+    placed_root = new osg::Group();
+    altitude_transform->addChild(placed_root);
+    impostorcount = 0;
+    lodcount = 0;
+    cloudcount = 0;
 }
-
-int SGCloudField::get_CacheSize(void) { 
-#if 0
-       return SGNewCloud::cldCache->queryCacheSize(); 
-#endif
-        return 0;
+    
+SGCloudField::~SGCloudField() {
 }
 
-void SGCloudField::set_CacheSize(int sizeKb) {
-#if 0
-       // 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);
-       }
-#endif
-}
-void SGCloudField::set_CloudVis(float distance) {
-#if 0
-       if( distance <= fieldSize )
-               SGCloudField::CloudVis = distance;
-#endif
+
+void SGCloudField::clear(void) {
+
+    for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
+        itr != end;
+        ++itr) {
+        removeCloudFromTree(itr->second);
+    }
+    
+    cloud_hash.clear();
 }
-void SGCloudField::set_density(float density) {
-#if 0
-       SGCloudField::density = density;
-#endif
+
+void SGCloudField::applyVisAndLoDRange(void)
+{
+    for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) {
+        osg::ref_ptr<osg::LOD> lodnode1 = (osg::LOD*) placed_root->getChild(i);
+        for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) {
+            lodnode1->setRange(j, 0.0f, lod1_range + lod2_range + view_distance + MAX_CLOUD_DEPTH);
+            osg::ref_ptr<osg::LOD> lodnode2 = (osg::LOD*) lodnode1->getChild(j);
+            for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) {
+                lodnode2->setRange(k, 0.0f, view_distance + MAX_CLOUD_DEPTH);
+            }
+        }
+    }
 }
-void SGCloudField::set_enable3dClouds(bool enable) {
-#if 0
-       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);
-       }
-#endif
+            
+bool SGCloudField::addCloud(float lon, float lat, float alt, int index, osg::ref_ptr<EffectGeode> geode) {
+  return addCloud(lon, lat, alt, 0.0f, 0.0f, index, geode);
 }
 
-// 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) {
-#if 0
-    sgMat4 T1, LON, LAT;
-    sgVec3 axis;
-
-    sgMakeTransMat4( T1, p );
+bool SGCloudField::addCloud(float lon, float lat, float alt, float x, float y, int index, osg::ref_ptr<EffectGeode> geode) {
+    // If this cloud index already exists, don't replace it.
+    if (cloud_hash[index]) return false;
 
-    sgSetVec3( axis, 0.0, 0.0, 1.0 );
-    sgMakeRotMat4( LON, lon * SGD_RADIANS_TO_DEGREES, axis );
+    osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
 
-    sgSetVec3( axis, 0.0, 1.0, 0.0 );
-    sgMakeRotMat4( LAT, 90.0 - lat * SGD_RADIANS_TO_DEGREES, axis );
+    transform->addChild(geode.get());
+    addCloudToTree(transform, lon, lat, alt, x, y);
+    cloud_hash[index] = transform;
+    return true;
+}
 
-    sgMat4 TRANSFORM;
+// Remove a give cloud from inside the tree, without removing it from the cloud hash
+void SGCloudField::removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform)
+{
+    if (transform == 0)
+    {
+        // Ooops!
+        return;
+    }
+    osg::ref_ptr<osg::Group> lodnode = transform->getParent(0);
+    lodnode->removeChild(transform);
+    cloudcount--;
 
-    sgCopyMat4( TRANSFORM, T1 );
-    sgPreMultMat4( TRANSFORM, LON );
-    sgPreMultMat4( TRANSFORM, LAT );
+    if (lodnode->getNumChildren() == 0) {
+        osg::ref_ptr<osg::Group> lodnode1 = lodnode->getParent(0);
+        osg::ref_ptr<osgSim::Impostor> impostornode = (osgSim::Impostor*) lodnode1->getParent(0);
 
-    sgCoord layerpos;
-    sgSetCoord( &layerpos, TRANSFORM );
+        lodnode1->removeChild(lodnode);
+        lodcount--;
 
-       sgMakeCoordMat4( transform, &layerpos );
+        if (lodnode1->getNumChildren() == 0) {
+          impostornode->removeChild(lodnode1);
+          placed_root->removeChild(impostornode);
+          impostorcount--;
+        }
+    }
+}
 
+void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
+                                  float lon, float lat, float alt, float x, float y) {
+    
+    // Get the base position
+    SGGeod loc = SGGeod::fromDegFt(lon, lat, alt);
+    addCloudToTree(transform, loc, x, y);      
+}
 
-       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;
+void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
+                                  SGGeod loc, float x, float y) {
+        
+    float alt = loc.getElevationFt();
+    // Determine any shift by x/y
+    if ((x != 0.0f) || (y != 0.0f)) {
+        double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x); 
+        double dst = sqrt(x*x + y*y);
+        double endcrs;
+        
+        SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f);            
+        SGGeodesy::direct(base_pos, crs, dst, loc, endcrs);
+    }
+    
+    // The direct call provides the position at 0 alt, so adjust as required.      
+    loc.setElevationFt(alt);    
+    addCloudToTree(transform, loc);    
+}
+        
+        
+void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc) {        
+    // Work out where this cloud should go in OSG coordinates.
+    SGVec3<double> cart;
+    SGGeodesy::SGGeodToCart(loc, cart);
+    osg::Vec3f pos = toOsg(cart);
+
+
+    if (old_pos == osg::Vec3f(0.0f, 0.0f, 0.0f)) {
+        // First setup.
+        SGVec3<double> fieldcenter;
+        SGGeodesy::SGGeodToCart(SGGeod::fromDegFt(loc.getLongitudeDeg(), loc.getLatitudeDeg(), 0.0f), fieldcenter);
+        // Convert to the scenegraph orientation where we just rotate around
+        // the y axis 180 degrees.
+        osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
+        
+        osg::Vec3f osg_pos = toOsg(fieldcenter);            
+        
+        field_transform->setPosition(osg_pos);
+        field_transform->setAttitude(orient);
+        old_pos = osg_pos;
+    }
+    
+    // Convert position to cloud-coordinates
+    pos = pos - field_transform->getPosition();
+    pos = field_transform->getAttitude().inverse() * pos;    
+
+    // We have a two level dynamic quad tree which the cloud will be added
+    // to. If there are no appropriate nodes in the quad tree, they are
+    // created as required.
+    bool found = false;
+    osg::ref_ptr<osg::LOD> lodnode1;
+    osg::ref_ptr<osg::LOD> lodnode;
+    osg::ref_ptr<osgSim::Impostor> impostornode;
+
+    for (unsigned int i = 0; (!found) && (i < placed_root->getNumChildren()); i++) {
+        lodnode1 = (osg::LOD*) placed_root->getChild(i);
+        if ((lodnode1->getCenter() - pos).length2() < lod1_range*lod1_range) {
+          // New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
+          found = true;
+        }         
     }
 
-    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;
+    if (!found) {
+        if (use_impostors) {
+          impostornode = new osgSim::Impostor();
+          impostornode->setImpostorThreshold(impostor_distance);
+          //impostornode->setImpostorThresholdToBound();
+          //impostornode->setCenter(pos);                
+          placed_root->addChild(impostornode.get());
+          lodnode1 = (osg::ref_ptr<osg::LOD>) impostornode;
         } else {
-            last_course = course;
+          lodnode1 = new osg::LOD();
+          placed_root->addChild(lodnode1.get());
         }
+        impostorcount++;
+    }
 
-        // 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;
+    // Now check if there is a second level LOD node at an appropriate distance
+    found = false;
+    
+    for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {      
+        lodnode = (osg::LOD*) lodnode1->getChild(j);
+        if ((lodnode->getCenter() - pos).length2() < lod2_range*lod2_range) {
+            // We've found the right leaf LOD node
+            found = true;
         }
-
-               deltax += ax;
-               deltay += ay;
-
-        last_lon = lon;
-        last_lat = lat;
     }
 
-
-       // 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;
-#endif
-}
-
-SGCloudField::SGCloudField() :
-       deltax(0.0),
-       deltay(0.0),
-       last_course(0.0),
-       last_density(0.0),
-       draw_in_3d(true)
-{
-#if 0
-       sgSetVec3( relative_position, 0,0,0);
-       theField.reserve(200);
-       inViewClouds.reserve(200);
-        sg_srandom_time_10();
-#else
-       draw_in_3d = false;
-#endif
-}
-
-SGCloudField::~SGCloudField() {
-#if 0
-       list_of_Cloud::iterator iCloud;
-       for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
-               delete iCloud->aCloud;
-       }
-       theField.clear();
-#endif
+    if (!found) {
+        // No suitable leaf node was found, so we need to add one.
+        lodnode = new osg::LOD();
+        lodnode1->addChild(lodnode, 0.0f, lod1_range + lod2_range + view_distance + MAX_CLOUD_DEPTH);
+        lodcount++;
+    } 
+    
+    transform->setPosition(pos);
+    lodnode->addChild(transform.get(), 0.0f, view_distance + MAX_CLOUD_DEPTH);
+    cloudcount++;
+    SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "Impostors: " << impostorcount <<
+                                     " LoD: " << lodcount << 
+                                     " Clouds: " << cloudcount);
+
+    lodnode->dirtyBound();
+    lodnode1->dirtyBound();
+    field_root->dirtyBound();
 }
-
-
-void SGCloudField::clear(void) {
-#if 0
-       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;
-#endif
+        
+bool SGCloudField::deleteCloud(int identifier) {
+    osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
+    if (transform == 0) return false;
+        
+    removeCloudFromTree(transform);
+    cloud_hash.erase(identifier);
+
+    return true;
 }
-
-// use a table or else we see poping when moving the slider...
-static int densTable[][10] = {
-       {0,0,0,0,0,0,0,0,0,0},
-       {1,0,0,0,0,0,0,0,0,0},
-       {1,0,0,0,1,0,0,0,0,0},
-       {1,0,0,0,1,0,0,1,0,0}, // 30%
-       {1,0,1,0,1,0,0,1,0,0},
-       {1,0,1,0,1,0,1,1,0,0}, // 50%
-       {1,0,1,0,1,0,1,1,0,1},
-       {1,0,1,1,1,0,1,1,0,1}, // 70%
-       {1,1,1,1,1,0,1,1,0,1},
-       {1,1,1,1,1,0,1,1,1,1}, // 90%
-       {1,1,1,1,1,1,1,1,1,1}
-};
-
-// set the visible flag depending on density
-void SGCloudField::applyDensity(void) {
-#if 0
-       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 );
-#endif
+        
+bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt) {
+    return repositionCloud(identifier, lon, lat, alt, 0.0f, 0.0f);
 }
 
-// add one cloud, data is not copied, ownership given
-void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
-#if 0
-       Cloud cl;
-       cl.aCloud = cloud;
-       cl.visible = true;
-       cloud->SetPos( pos );
-       sgCopyVec3( cl.pos, *cloud->getCenter() );
-       theField.push_back( cl );
-#endif
-}
+bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt, float x, float y) {
+    osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
+    
+    if (transform == NULL) return false;
 
+    removeCloudFromTree(transform);
+    addCloudToTree(transform, lon, lat, alt, x, y);
+    return true;
+    }
 
-static float Rnd(float n) {
-       return n * (-0.5f + sg_random());
+bool SGCloudField::isDefined3D(void) {
+    return (! cloud_hash.empty());
 }
 
-// for debug only
-// build a field of cloud of size 25x25 km, its a grid of 11x11 clouds
-void SGCloudField::buildTestLayer(void) {
-#if 0
-       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();
-#endif
+SGCloudField::CloudFog::CloudFog() {
+    fog = new osg::Fog;
+    fog->setMode(osg::Fog::EXP2);
+    fog->setDataVariance(osg::Object::DYNAMIC);
 }
 
-// cull all clouds of a tiled field
-void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
-#if 0
-       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);
-               }
-       }
-#endif
+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);
 }
 
-
-// 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(float *sun_color) {
-    // sun_color used to depend on an extern SGSky *thesky definition
-    // above.  However, this is bad form for a library and it's much
-    // more clean to just pass in the needed value.  For reference, here is
-    // the old way that sun_color was fetched ...
-    // float *sun_color = thesky->get_sun_color();
-
-#if 0
-    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
-       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();
-#endif
-}