]> git.mxchange.org Git - simgear.git/commitdiff
Improved 3D clouds
authorStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Sat, 17 Dec 2011 13:02:54 +0000 (13:02 +0000)
committerStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Tue, 17 Jan 2012 22:14:57 +0000 (22:14 +0000)
1) Impostors
2) One drawable per cloud rather than a single drawable shared by all clouds.

simgear/scene/sky/CloudShaderGeometry.cxx
simgear/scene/sky/CloudShaderGeometry.hxx
simgear/scene/sky/cloudfield.cxx
simgear/scene/sky/cloudfield.hxx
simgear/scene/sky/newcloud.cxx
simgear/scene/sky/sky.cxx
simgear/scene/sky/sky.hxx

index b2ac925234834314f9df2958e4d2ed7e657ee608..f3bb8c8ab9997e2c6c7076175340191ae6a5b45d 100644 (file)
@@ -51,79 +51,94 @@ void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
     if (!_cloudsprites.size()) return;
     
     osg::State& state = *renderInfo.getState();
-    unsigned int contextID = state.getContextID();
-    SortData& sortData = _sortData[contextID];
+    
     int frameNumber = state.getFrameStamp()->getFrameNumber();
-
-    if (!sortData.spriteIdx)
-        sortData.spriteIdx = new SortData::SortItemList;
-    if (sortData.spriteIdx->size() < _cloudsprites.size()) {
-        for (unsigned i = sortData.spriteIdx->size(); i < (unsigned)_cloudsprites.size(); ++i)
-            sortData.spriteIdx->push_back(SortData::SortItem(i, 0.0f));
-        sortData.frameSorted = frameNumber - (sortData.skip_limit + 1);
-    }
+    unsigned int contextID = state.getContextID();    
+    SortData& sortData = _sortData[contextID];
+    Geometry* g = _geometry->asGeometry();
+    
     // If the cloud is already sorted, then it is likely to still be sorted.
     // Therefore we can avoid re-sorting it for a period. If it is still
     // sorted after that period, then we can wait for a longer period before
     // checking again. In this way, only clouds that are changing regularly
-    // are sorted.
-    if (frameNumber - sortData.skip_limit >= sortData.frameSorted) {
+    // are sorted.        
+    osg::Vec3Array* v = dynamic_cast<osg::Vec3Array*>(g->getVertexArray());
+    if ((v->size() > 4) && 
+        (frameNumber - sortData.skip_limit >= sortData.frameSorted)) {
         Matrix mvp = state.getModelViewMatrix() * state.getProjectionMatrix();
-        for (SortData::SortItemList::iterator itr = sortData.spriteIdx->begin(),
-                 end = sortData.spriteIdx->end();
-             itr != end;
-             ++itr) {
-            Vec4f projPos
-                = Vec4f(toOsg(_cloudsprites[itr->idx].position), 1.0f) * mvp;
-            itr->depth = projPos.z() / projPos.w();
+        
+        osg::Vec4Array* c = dynamic_cast<osg::Vec4Array*>(g->getColorArray());
+        osg::Vec2Array* t = dynamic_cast<osg::Vec2Array*>(g->getTexCoordArray(0));
+        Vec3f av[4];
+        Vec4f ac[4];
+        Vec2f at[4];        
+        
+        // Perform a single pass bubble sort of the array, 
+        // keeping track of whether we've had to make any changes
+        bool sorted = true;                      
+        for (unsigned int i = 4; i < v->size(); i = i + 4) {
+            // The position of the sprite is stored in the colour
+            // array, with the exception of the w() coordinate
+            // which is the z-scaling parameter.
+            Vec4f a = (*c)[i-4];
+            Vec4f aPos = Vec4f(a.x(), a.y(), a.z(), 1.0f) * mvp;
+            Vec4f b = (*c)[i];
+            Vec4f bPos = Vec4f(b.x(), b.y(), b.z(), 1.0f) * mvp;
+            
+            if ((aPos.z()/aPos.w()) < (bPos.z()/bPos.w() - 0.0001)) {
+                // a is non-trivially closer than b, so should be rendered
+                // later. Swap them around
+                for (int j = 0; j < 4; j++) {
+                    av[j] = (*v)[i+j-4];
+                    ac[j] = (*c)[i+j-4];
+                    at[j] = (*t)[i+j-4];
+                    
+                    (*v)[i+j -4] = (*v)[i+j];
+                    (*c)[i+j -4] = (*c)[i+j];
+                    (*t)[i+j -4] = (*t)[i+j];
+                    
+                    (*v)[i+j] = av[j];
+                    (*c)[i+j] = ac[j];
+                    (*t)[i+j] = at[j];
+                }
+                
+                // Indicate that the arrays were not sorted
+                // so we should check them next iteration
+                sorted = false;
+            }
         }
-        // Already sorted?
-        if (std::adjacent_find(sortData.spriteIdx->rbegin(),
-                               sortData.spriteIdx->rend(), SpriteComp())
-            == sortData.spriteIdx->rend()) {
+        
+        if (sorted) {
             // This cloud is sorted, so no need to re-sort.
+            
             sortData.skip_limit = sortData.skip_limit * 2;
             if (sortData.skip_limit > 30) {
                 // Jitter the skip frames to avoid synchronized sorts
                 // which will cause periodic frame-rate drops
                 sortData.skip_limit += sg_random() * 10;
             }
-            if (sortData.skip_limit > 128) {
-                // Maximum of every 128 frames (2 - 4 seconds)
-                sortData.skip_limit = 128 + sg_random() * 10;
+            if (sortData.skip_limit > 500) {
+                // Maximum of every 500 frames (10-20 seconds)
+                sortData.skip_limit = 500 + sg_random() * 10;
             }
-
         } else {
-            std::sort(sortData.spriteIdx->begin(), sortData.spriteIdx->end(),
-                      SpriteComp());
             sortData.skip_limit = 1;
         }
+        
         sortData.frameSorted = frameNumber;
     }
-
+    
     const Extensions* extensions = getExtensions(state.getContextID(),true);
-
-    for(SortData::SortItemList::const_iterator itr = sortData.spriteIdx->begin(),
-            end = sortData.spriteIdx->end();
-        itr != end;
-        ++itr) {
-        const CloudSprite& t = _cloudsprites[itr->idx];
-        GLfloat ua1[3] = { (GLfloat) t.texture_index_x/varieties_x,
-                           (GLfloat) t.texture_index_y/varieties_y,
-                           (GLfloat) t.width };
-        GLfloat ua2[3] = { (GLfloat) t.height,
-                                      (GLfloat) shade_factor,
-                           (GLfloat) cloud_height };
-        GLfloat ua3[3] = { (GLfloat) bottom_factor,
-                           (GLfloat) middle_factor,
-                           (GLfloat) top_factor };
-                           
-        extensions->glVertexAttrib3fv(USR_ATTR_1, ua1 );
-        extensions->glVertexAttrib3fv(USR_ATTR_2, ua2 );
-        extensions->glVertexAttrib3fv(USR_ATTR_3, ua3 );
-        glColor4f(t.position.x(), t.position.y(), t.position.z(), zscale);
-        _geometry->draw(renderInfo);
-    }
+    GLfloat ua1[3] = { (GLfloat) 1.0f,
+                       (GLfloat) shade_factor,
+                       (GLfloat) cloud_height };
+    GLfloat ua2[3] = { (GLfloat) bottom_factor,
+                       (GLfloat) middle_factor,
+                       (GLfloat) top_factor };
+                       
+    extensions->glVertexAttrib3fv(USR_ATTR_1, ua1 );
+    extensions->glVertexAttrib3fv(USR_ATTR_2, ua2 );
+    _geometry->draw(renderInfo);    
 }
 
 void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
@@ -145,6 +160,73 @@ void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
     _cloudsprites.push_back(CloudSprite(p, tx, ty, w, h));
 }
 
+void CloudShaderGeometry::generateGeometry()
+{
+    // Generate a set of geometries as a QuadStrip based on the list of sprites
+    int numsprites = _cloudsprites.size();
+    
+    // Create front and back polygons so we don't need to screw around
+    // with two-sided lighting in the shader.
+    osg::Vec3Array& v = *(new osg::Vec3Array(4 * numsprites));
+    osg::Vec4Array& c = *(new osg::Vec4Array(4 * numsprites));
+    osg::Vec2Array& t = *(new osg::Vec2Array(4 * numsprites));
+    
+    int idx = 0;
+
+    for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
+         iter != _cloudsprites.end();
+         ++iter) 
+    {
+    
+        float cw = 0.5f * iter->width;
+        float ch = 0.5f * iter->height;        
+        
+        // Create the vertices
+        v[4*idx  ].set(0.0f, -cw, -ch);
+        v[4*idx+1].set(0.0f,  cw, -ch);
+        v[4*idx+2].set(0.0f,  cw, ch);
+        v[4*idx+3].set(0.0f, -cw, ch);
+        
+        // Set the texture coords for each vertex
+        // from the texture index, and the number
+        // of textures in the image    
+        int x = iter->texture_index_x;
+        int y = iter->texture_index_y;
+        
+        t[4*idx  ].set( (float) x       / varieties_x, (float) y / varieties_y);
+        t[4*idx+1].set( (float) (x + 1) / varieties_x, (float) y / varieties_y);
+        t[4*idx+2].set( (float) (x + 1) / varieties_x, (float) (y + 1) / varieties_y);
+        t[4*idx+3].set( (float) x       / varieties_x, (float) (y + 1) / varieties_y);
+
+        // The color isn't actually use in lighting, but instead to indicate the center of rotation
+        c[4*idx  ].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
+        c[4*idx+1].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
+        c[4*idx+2].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
+        c[4*idx+3].set(iter->position.x(), iter->position.y(), iter->position.z(), zscale);
+        
+        idx++;      
+    }
+    
+    //Quads now created, add it to the geometry.
+    osg::Geometry* geom = new osg::Geometry;
+    geom->setVertexArray(&v);
+    geom->setTexCoordArray(0, &t);
+    
+    // The normal isn't actually use in lighting, so we simply bind overall.
+    osg::Vec3Array& n = *(new osg::Vec3Array(4));
+    n[0].set(1.0f, -1.0f, -1.0f);
+    n[1].set(1.0f,  1.0f, -1.0f);
+    n[2].set(1.0f,  1.0f,  1.0f);
+    n[3].set(1.0f, -1.0f,  1.0f);
+    
+    geom->setNormalArray(&n);
+    geom->setNormalBinding(Geometry::BIND_OVERALL);
+    geom->setColorArray(&c);
+    geom->setColorBinding(Geometry::BIND_PER_VERTEX);
+    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,numsprites*4));
+    _geometry = geom;
+}
+
 bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
 {
     bool iteratorAdvanced = false;
@@ -181,6 +263,7 @@ bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr)
                 ++fr;
             }
         }
+        geom.generateGeometry();
     }
     return iteratorAdvanced;
 }
index 1b63f805f98043a344865482e3c1b5290dce393a..3825c843b7d4ac69cd36ab072a2eac0a0b6ef32e 100644 (file)
@@ -100,12 +100,9 @@ class CloudShaderGeometry : public osg::Drawable
             return _bbox;
         }
         
-        void setGeometry(osg::Drawable* geometry)
-        {
-            _geometry = geometry;
-        }
-        
         void addSprite(const SGVec3f& p, int tx, int ty, float w, float h, float cull);
+        void generateGeometry();
+        void rebuildGeometry();
                 
         osg::ref_ptr<osg::Drawable> _geometry;
 
index 0ff07144efe23cbfa1997074752a527c6cf98934..164f2b92dd4a6bda8ebcb34aa9c08662886cbcb3 100644 (file)
@@ -28,6 +28,7 @@
 #include <osg/Texture2D>
 #include <osg/PositionAttitudeTransform>
 #include <osg/Vec4f>
+#include <osgSim/Impostor>
 
 #include <simgear/compiler.h>
 
@@ -68,9 +69,15 @@ float SGCloudField::fieldSize = 50000.0f;
 double SGCloudField::timer_dt = 0.0;
 float SGCloudField::view_distance = 20000.0f;
 bool SGCloudField::wrap = true;
-float SGCloudField::RADIUS_LEVEL_1 = 5000.0f;
-float SGCloudField::RADIUS_LEVEL_2 = 2000.0f;
 float SGCloudField::MAX_CLOUD_DEPTH = 2000.0f;
+bool SGCloudField::use_impostors = true;
+float SGCloudField::lod1_range = 10000.0f;
+float SGCloudField::lod2_range = 5000.0f;
+float SGCloudField::impostor_distance = 10000.0f;
+
+int impostorcount = 0;
+int lodcount = 0;
+int cloudcount = 0;
 
 SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
 
@@ -165,6 +172,9 @@ SGCloudField::SGCloudField() :
     field_transform->addChild(altitude_transform.get());
     placed_root = new osg::Group();
     altitude_transform->addChild(placed_root);
+    impostorcount = 0;
+    lodcount = 0;
+    cloudcount = 0;
 }
     
 SGCloudField::~SGCloudField() {
@@ -182,12 +192,12 @@ void SGCloudField::clear(void) {
     cloud_hash.clear();
 }
 
-void SGCloudField::applyVisRange(void)
+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, view_distance + RADIUS_LEVEL_1 + RADIUS_LEVEL_2 + MAX_CLOUD_DEPTH);
+            lodnode1->setRange(j, 0.0f, lod1_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);
@@ -217,15 +227,19 @@ void SGCloudField::removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransfo
 {
     osg::ref_ptr<osg::Group> lodnode = transform->getParent(0);
     lodnode->removeChild(transform);
+    cloudcount--;
 
-    // Clean up the LOD nodes if required
     if (lodnode->getNumChildren() == 0) {
         osg::ref_ptr<osg::Group> lodnode1 = lodnode->getParent(0);
-            
+        osg::ref_ptr<osgSim::Impostor> impostornode = (osgSim::Impostor*) lodnode1->getParent(0);
+
         lodnode1->removeChild(lodnode);
-            
-        if (lodnode1->getNumChildren() == 0) {
-            placed_root->removeChild(lodnode1);
+        lodcount--;
+
+        if (lodnode1->getNumChildren() == 0) {        
+          impostornode->removeChild(lodnode1);        
+          placed_root->removeChild(impostornode);
+          impostorcount--;
         }
     }
 }
@@ -291,39 +305,55 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
     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() < RADIUS_LEVEL_1*RADIUS_LEVEL_1) {
-            // New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
-            found = true;
-        }
+        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 (!found) {
-        lodnode1 = new osg::LOD();
-        placed_root->addChild(lodnode1.get());
+        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 {
+          lodnode1 = new osg::LOD();
+          placed_root->addChild(lodnode1.get());
+        }
+        impostorcount++;
     }
 
     // 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++) {
+    
+    for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {      
         lodnode = (osg::LOD*) lodnode1->getChild(j);
-        if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) {
+        if ((lodnode->getCenter() - pos).length2() < lod2_range*lod2_range) {
             // We've found the right leaf LOD node
             found = true;
         }
     }
 
     if (!found) {
-        // No suitable leave node was found, so we need to add one.
+        // No suitable leaf node was found, so we need to add one.
         lodnode = new osg::LOD();
-        lodnode1->addChild(lodnode, 0.0f, view_distance + RADIUS_LEVEL_1 + RADIUS_LEVEL_2 + MAX_CLOUD_DEPTH);
-    }
-
+        lodnode1->addChild(lodnode, 0.0f, lod1_range + view_distance + MAX_CLOUD_DEPTH);
+        lodcount++;
+    } 
+    
     transform->setPosition(pos);
-    lodnode->addChild(transform.get(), 0.0f, view_distance + MAX_CLOUD_DEPTH);
+    lodnode->addChild(transform.get(), 0.0f, view_distance);
+    cloudcount++;
+    SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "Impostors: " << impostorcount <<
+                                     " LoD: " << lodcount << 
+                                     " Clouds: " << cloudcount);
 
     lodnode->dirtyBound();
     lodnode1->dirtyBound();
index 1199c9c51f46a0be1ceca26bbaff0ae2e2be997f..28095c4b97321521d0cdbd79e4239bce141a4964 100644 (file)
@@ -72,16 +72,12 @@ private:
 
   float Rnd(float);
         
-  // Radius of the LoD nodes for the dynamic quadtrees.
-  static float RADIUS_LEVEL_1;
-  static float RADIUS_LEVEL_2;
-
   // Theoretical maximum cloud depth, used for fudging the LoD
   // ranges to ensure that clouds become visible at maximum range
   static float MAX_CLOUD_DEPTH;
 
-       // this is a relative position only, with that we can move all clouds at once
-       SGVec3f relative_position;
+  // this is a relative position only, with that we can move all clouds at once
+  SGVec3f relative_position;
 
   osg::ref_ptr<osg::Group> field_root;
   osg::ref_ptr<osg::Group> placed_root;
@@ -150,6 +146,10 @@ public:
   static SGVec3f view_vec, view_X, view_Y;
 
   static float view_distance;
+  static float impostor_distance;
+  static float lod1_range;
+  static float lod2_range;
+  static bool use_impostors;
   static double timer_dt;
   static float fieldSize;
   static bool wrap;
@@ -157,9 +157,18 @@ public:
   static bool getWrap(void) { return wrap; }
   static void setWrap(bool w) { wrap = w; }
 
+  static float getImpostorDistance(void) { return impostor_distance; }
+  static void setImpostorDistance(float d) { impostor_distance = d; }
+  static float getLoD1Range(void) { return lod1_range; }
+  static void setLoD1Range(int d) { lod1_range = d; }
+  static float getLoD2Range(void) { return lod2_range; }
+  static void setLoD2Range(int d) { lod2_range = d; }
+  static void setUseImpostors(bool b) { use_impostors = b; }
+  static bool getUseImpostors(void) { return use_impostors; }
+  
   static float getVisRange(void) { return view_distance; }
   static void setVisRange(float d) { view_distance = d; }
-  void applyVisRange(void);
+  void applyVisAndLoDRange(void);
 
   static osg::Fog* getFog()
   {
index d78380d2927fb2b9a57d8ca6f0c043d6de056b61..b27704dac40d29d8b8b21d86b938b4b555a75246 100644 (file)
@@ -119,58 +119,11 @@ SGNewCloud::SGNewCloud(const SGPath &texture_root, const SGPropertyNode *cld_def
     } else {
         effect = iter->second.get();
     }
-    quad = createOrthQuad(min_sprite_width, min_sprite_height,
-                          num_textures_x, num_textures_y);
 }
 
 SGNewCloud::~SGNewCloud() {
 }
 
-osg::Geometry* SGNewCloud::createOrthQuad(float w, float h, int varieties_x, int varieties_y)
-{
-    // Create front and back polygons so we don't need to screw around
-    // with two-sided lighting in the shader.
-    osg::Vec3Array& v = *(new osg::Vec3Array(4));
-    osg::Vec3Array& n = *(new osg::Vec3Array(4));
-    osg::Vec2Array& t = *(new osg::Vec2Array(4));
-    
-    float cw = w*0.5f;
-    float ch = h*0.5f;
-    
-    v[0].set(0.0f, -cw, -ch);
-    v[1].set(0.0f,  cw, -ch);
-    v[2].set(0.0f,  cw, ch);
-    v[3].set(0.0f, -cw, ch);
-    
-    // The texture coordinate range is not the
-    // entire coordinate space - as the texture
-    // has a number of different clouds on it.
-    float tx = 1.0f/varieties_x;
-    float ty = 1.0f/varieties_y;
-
-    t[0].set(0.0f, 0.0f);
-    t[1].set(  tx, 0.0f);
-    t[2].set(  tx, ty);
-    t[3].set(0.0f, ty);
-
-    // The normal isn't actually use in lighting.
-    n[0].set(1.0f, -1.0f, -1.0f);
-    n[1].set(1.0f,  1.0f, -1.0f);
-    n[2].set(1.0f,  1.0f,  1.0f);
-    n[3].set(1.0f, -1.0f,  1.0f);
-
-    osg::Geometry *geom = new osg::Geometry;
-
-    geom->setVertexArray(&v);
-    geom->setTexCoordArray(0, &t);
-    geom->setNormalArray(&n);
-    geom->setNormalBinding(Geometry::BIND_PER_VERTEX);
-    // No color for now; that's used to pass the position.
-    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
-
-    return geom;
-}
-
 #if 0
 // return a random number between -n/2 and n/2, tending to 0
 static float Rnd(float n) {
@@ -240,28 +193,28 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
             z = height * cos(elev) * 0.5f + height * 0.5f;
         }
         
-        // Determine the height and width as scaling factors on the minimum size (used to create the quad).
-        float sprite_width = 1.0f + sg_random() * (max_sprite_width - min_sprite_width) / min_sprite_width;
-        float sprite_height = 1.0f + sg_random() * (max_sprite_height - min_sprite_height) / min_sprite_height;
+        // Determine the height and width
+        float sprite_width = min_sprite_width + sg_random() * (max_sprite_width - min_sprite_width);
+        float sprite_height = min_sprite_height + sg_random() * (max_sprite_height - min_sprite_height);
         
         // Sprites are never taller than square.
-        if (sprite_height * min_sprite_height > sprite_width * min_sprite_width)
+        if (sprite_height > sprite_width )
         {
-            sprite_height = sprite_width * min_sprite_width / min_sprite_height;
+            sprite_height = sprite_width;
         }
 
         if (i == 0) {
             // The center sprite is always maximum size to fill up any holes.
-            sprite_width = 1.0f + (max_sprite_width - min_sprite_width) / min_sprite_width;
-            sprite_height = 1.0f + (max_sprite_height - min_sprite_height) / min_sprite_height;
+            sprite_width = max_sprite_width;
+            sprite_height = max_sprite_height;
         }
         
         // If the center of the sprite is less than half the sprite heightthe sprite will extend 
         // below the bottom of the cloud and must be shifted upwards. This is particularly important 
         // for cumulus clouds which have a very well defined base.
-        if (z < 0.5f * sprite_height * min_sprite_height)
+        if (z < 0.5f * sprite_height)
         {
-            z = 0.5f * sprite_height * min_sprite_height;          
+            z = 0.5f * sprite_height;
         }        
 
         // Determine the sprite texture indexes.
@@ -287,7 +240,7 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
                       cull_distance_squared);
     }
     
-    sg->setGeometry(quad);
+    sg->generateGeometry();
     geode->addDrawable(sg);
     geode->setName("3D cloud");
     geode->setEffect(effect.get());
index f22a9a6a5a906389c286304e46d566b079c4f9a0..84c446e3b8841b4c658d67ba7c7ac280f8429184 100644 (file)
@@ -233,7 +233,43 @@ void SGSky::set_3dCloudVisRange(float vis)
 {
     SGCloudField::setVisRange(vis);
     for ( int i = 0; i < (int)cloud_layers.size(); ++i ) {
-        cloud_layers[i]->get_layer3D()->applyVisRange();
+        cloud_layers[i]->get_layer3D()->applyVisAndLoDRange();
+    }
+}
+
+float SGSky::get_3dCloudImpostorDistance() const {
+    return SGCloudField::getImpostorDistance();
+}
+
+void SGSky::set_3dCloudImpostorDistance(float vis)
+{
+    SGCloudField::setImpostorDistance(vis);
+    for ( int i = 0; i < (int)cloud_layers.size(); ++i ) {
+        cloud_layers[i]->get_layer3D()->applyVisAndLoDRange();
+    }
+}
+
+float SGSky::get_3dCloudLoD1Range() const {
+    return SGCloudField::getLoD1Range();
+}
+
+void SGSky::set_3dCloudLoD1Range(float vis)
+{
+    SGCloudField::setLoD1Range(vis);
+    for ( int i = 0; i < (int)cloud_layers.size(); ++i ) {
+        cloud_layers[i]->get_layer3D()->applyVisAndLoDRange();
+    }
+}
+
+float SGSky::get_3dCloudLoD2Range() const {
+    return SGCloudField::getLoD2Range();
+}
+
+void SGSky::set_3dCloudLoD2Range(float vis)
+{
+    SGCloudField::setLoD2Range(vis);
+    for ( int i = 0; i < (int)cloud_layers.size(); ++i ) {
+        cloud_layers[i]->get_layer3D()->applyVisAndLoDRange();
     }
 }
 
@@ -246,6 +282,15 @@ void SGSky::set_3dCloudWrap(bool wrap)
     SGCloudField::setWrap(wrap);
 }
 
+bool SGSky::get_3dCloudUseImpostors() const {
+    return SGCloudField::getUseImpostors();
+}
+
+void SGSky::set_3dCloudUseImpostors(bool imp)
+{
+    SGCloudField::setUseImpostors(imp);
+}
+
 
 void SGSky::texture_path( const string& path ) {
        tex_path = SGPath( path );
index c4924100f06ee3242cd08cd3706b49009b3b42e6..2e84629f1ed8462c2b80e59eacdc6e99df6d5cfb 100644 (file)
@@ -437,6 +437,38 @@ public:
      */
     virtual void set_3dCloudVisRange(float vis);
 
+    /** Get 3D cloud impostor distance*/
+    virtual float get_3dCloudImpostorDistance() const;
+
+    /** Set 3D cloud impostor distance
+     * @param density 3D cloud impostor distance
+     */
+    virtual void set_3dCloudImpostorDistance(float vis);
+
+    /** Get 3D cloud LoD1 Range*/
+    virtual float get_3dCloudLoD1Range() const;
+
+    /** Set 3D cloud LoD1 Range
+     * @param vis LoD1 Range
+     */
+    virtual void set_3dCloudLoD1Range(float vis);
+
+    /** Get 3D cloud LoD2 Range*/
+    virtual float get_3dCloudLoD2Range() const;
+
+    /** Set 3D cloud LoD2 Range
+     * @param vis LoD2 Range
+     */
+    virtual void set_3dCloudLoD2Range(float vis);
+
+    /** Get 3D cloud impostor usage */
+    virtual bool get_3dCloudUseImpostors() const;
+
+    /** Set 3D cloud impostor usage
+     * @param wrap whether use impostors for 3D clouds
+     */
+    virtual void set_3dCloudUseImpostors(bool imp);
+
     /** Get 3D cloud wrapping */
     virtual bool get_3dCloudWrap() const;