]> git.mxchange.org Git - simgear.git/commitdiff
Merge branch 'timoore/effects'
authorTim Moore <timoore33@gmail.com>
Sun, 20 Dec 2009 15:07:00 +0000 (16:07 +0100)
committerTim Moore <timoore33@gmail.com>
Sun, 20 Dec 2009 15:07:00 +0000 (16:07 +0100)
Conflicts:
simgear/scene/model/model.cxx
simgear/scene/sky/newcloud.cxx

1  2 
configure.ac
simgear/scene/model/model.cxx
simgear/scene/sky/CloudShaderGeometry.cxx
simgear/scene/sky/newcloud.cxx

diff --combined configure.ac
index e82399f41fa3ba34730881dfd9924f8830ed0cff,3e18ea0a179d8610f784d5a0d01cb4fac1473aa9..ca19e4cf542dd54494d4c3c44ef553192ccc6d14
@@@ -37,7 -37,7 +37,7 @@@ AC_PROG_CX
  AC_PROG_RANLIB
  AC_PROG_INSTALL
  AC_PROG_LN_S
- AX_BOOST_BASE([1.34.0])
+ AX_BOOST_BASE([1.37.0])
  
  if test "x$BOOST_CPPFLAGS" != "x-I/usr/include" ; then
     CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
@@@ -215,7 -215,6 +215,7 @@@ LIBS="
  
  dnl check for some default libraries
  AC_SEARCH_LIBS(cos, m)
 +AC_SEARCH_LIBS(clock_gettime, rt)
  
  base_LIBS="$LIBS"
  
@@@ -430,6 -429,8 +430,6 @@@ AC_TRY_RUN(
  #define MIN_PLIB_VERSION 185
  
  int main() {
 -    int major, minor, micro;
 -
      if ( PLIB_VERSION < MIN_PLIB_VERSION ) {
         return -1;
      }
index d41529ad5c8b8fbf2cdeeabb508d5003c566afe4,9ca65c89d23fb323df12be01845a8c24ad15631b..59f33694956d600a31858e939f93bae7fd17a568
@@@ -7,14 -7,27 +7,28 @@@
  #include <simgear_config.h>
  #endif
  
+ #include <utility>
+ #include <boost/foreach.hpp>
  #include <osg/ref_ptr>
+ #include <osgDB/FileNameUtils>
+ #include <osgDB/FileUtils>
+ #include <osgDB/ReaderWriter>
  #include <osgDB/ReadFile>
  #include <osgDB/SharedStateManager>
  
 +#include <simgear/math/SGMath.hxx>
+ #include <simgear/scene/material/Effect.hxx>
+ #include <simgear/scene/material/EffectGeode.hxx>
  #include <simgear/scene/util/SGSceneFeatures.hxx>
+ #include <simgear/scene/util/SGSceneUserData.hxx>
+ #include <simgear/scene/util/CopyOp.hxx>
+ #include <simgear/scene/util/SplicingVisitor.hxx>
  
  #include <simgear/structure/exception.hxx>
+ #include <simgear/structure/Singleton.hxx>
  #include <simgear/props/props.hxx>
  #include <simgear/props/props_io.hxx>
  #include <simgear/props/condition.hxx>
@@@ -60,4 -73,262 +74,262 @@@ SGLoadTexture2D(bool staticTexture, con
    return texture.release();
  }
  
+ namespace simgear
+ {
+ using namespace std;
+ using namespace osg;
+ using simgear::CopyOp;
+ Node* copyModel(Node* model)
+ {
+     const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL
+                                      & ~CopyOp::DEEP_COPY_TEXTURES
+                                      & ~CopyOp::DEEP_COPY_IMAGES
+                                      & ~CopyOp::DEEP_COPY_STATESETS
+                                      & ~CopyOp::DEEP_COPY_STATEATTRIBUTES
+                                      & ~CopyOp::DEEP_COPY_ARRAYS
+                                      & ~CopyOp::DEEP_COPY_PRIMITIVES
+                                      // This will preserve display lists ...
+                                      & ~CopyOp::DEEP_COPY_DRAWABLES
+                                      & ~CopyOp::DEEP_COPY_SHAPES);
+     return (CopyOp(flags))(model);
+ }
+ TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
+     NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
+     _pathList(pathList)
+ {
+ }
+ void TextureUpdateVisitor::apply(Node& node)
+ {
+     StateSet* stateSet = cloneStateSet(node.getStateSet());
+     if (stateSet)
+         node.setStateSet(stateSet);
+     traverse(node);
+ }
+ void TextureUpdateVisitor::apply(Drawable& drawable)
+ {
+     StateSet* stateSet = cloneStateSet(drawable.getStateSet());
+     if (stateSet)
+         drawable.setStateSet(stateSet);
+ }
+ Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
+ {
+     using namespace osgDB;
+     const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
+     if (!texture)
+         return 0;
+     const Image* image = texture->getImage();
+     const string* fullFilePath = 0;
+     if (image) {
+         // The currently loaded file name
+         fullFilePath = &image->getFileName();
+     } else {
+         fullFilePath = &texture->getName();
+     }
+     // The short name
+     string fileName = getSimpleFileName(*fullFilePath);
+     if (fileName.empty())
+         return 0;
+     // The name that should be found with the current database path
+     string fullLiveryFile = findFileInPath(fileName, _pathList);
+     // If it is empty or they are identical then there is nothing to do
+     if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
+         return 0;
+     Image* newImage = readImageFile(fullLiveryFile);
+     if (!newImage)
+         return 0;
+     CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
+     Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
+     if (!newTexture) {
+         return 0;
+     } else {
+         newTexture->setImage(newImage);
+         return newTexture;
+     }
+ }
+ StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
+ {
+     typedef std::pair<int, Texture2D*> Tex2D;
+     vector<Tex2D> newTextures;
+     StateSet* result = 0;
+     if (!stateSet)
+         return 0;
+     int numUnits = stateSet->getTextureAttributeList().size();
+     if (numUnits > 0) {
+         for (int i = 0; i < numUnits; ++i) {
+             const StateAttribute* attr
+                 = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
+             Texture2D* newTexture = textureReplace(i, attr);
+             if (newTexture)
+                 newTextures.push_back(Tex2D(i, newTexture));
+         }
+         if (!newTextures.empty()) {
+             result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
+             for (vector<Tex2D>::iterator i = newTextures.begin();
+                  i != newTextures.end();
+                  ++i) {
+                 result->setTextureAttribute(i->first, i->second);
+             }
+         }
+     }
+     return result;
+ }
+ UserDataCopyVisitor::UserDataCopyVisitor() :
+     NodeVisitor(NodeVisitor::NODE_VISITOR,
+                 NodeVisitor::TRAVERSE_ALL_CHILDREN)
+ {
+ }
+ void UserDataCopyVisitor::apply(Node& node)
+ {
+     ref_ptr<SGSceneUserData> userData;
+     userData = SGSceneUserData::getSceneUserData(&node);
+     if (userData.valid()) {
+         SGSceneUserData* newUserData  = new SGSceneUserData(*userData);
+         newUserData->setVelocity(0);
+         node.setUserData(newUserData);
+     }
+     node.traverse(*this);
+ }
+ namespace
+ {
+ class MakeEffectVisitor : public SplicingVisitor
+ {
+ public:
+     typedef std::map<string, SGPropertyNode_ptr> EffectMap;
+     using SplicingVisitor::apply;
+     MakeEffectVisitor(const osgDB::ReaderWriter::Options* options = 0)
+         : _options(options)
+     {
+     }
+     virtual void apply(osg::Group& node);
+     virtual void apply(osg::Geode& geode);
+     EffectMap& getEffectMap() { return _effectMap; }
+     const EffectMap& getEffectMap() const { return _effectMap; }
+     void setDefaultEffect(SGPropertyNode* effect)
+     {
+         _currentEffectParent = effect;
+     }
+     SGPropertyNode* getDefaultEffect() { return _currentEffectParent; }
+ protected:
+     EffectMap _effectMap;
+     SGPropertyNode_ptr _currentEffectParent;
+     osg::ref_ptr<const osgDB::ReaderWriter::Options> _options;
+ };
+ void MakeEffectVisitor::apply(osg::Group& node)
+ {
+     SGPropertyNode_ptr savedEffectRoot;
+     const string& nodeName = node.getName();
+     bool restoreEffect = false;
+     if (!nodeName.empty()) {
+         EffectMap::iterator eitr = _effectMap.find(nodeName);
+         if (eitr != _effectMap.end()) {
+             savedEffectRoot = _currentEffectParent;
+             _currentEffectParent = eitr->second;
+             restoreEffect = true;
+         }
+     }
+     SplicingVisitor::apply(node);
+     // If a new node was created, copy the user data too.
+     ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&node);
+     if (userData.valid() && _childStack.back().back().get() != &node)
+         _childStack.back().back()->setUserData(new SGSceneUserData(*userData));
+     if (restoreEffect)
+         _currentEffectParent = savedEffectRoot;
+ }
+ void MakeEffectVisitor::apply(osg::Geode& geode)
+ {
+     if (pushNode(getNewNode(geode)))
+         return;
+     osg::StateSet* ss = geode.getStateSet();
+     if (!ss) {
+         pushNode(&geode);
+         return;
+     }
+     SGPropertyNode_ptr ssRoot = new SGPropertyNode;
+     makeParametersFromStateSet(ssRoot, ss);
+     SGPropertyNode_ptr effectRoot = new SGPropertyNode;
+     effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent);
+     Effect* effect = makeEffect(effectRoot, true, _options);
+     EffectGeode* eg = dynamic_cast<EffectGeode*>(&geode);
+     if (eg) {
+         eg->setEffect(effect);
+     } else {
+         eg = new EffectGeode;
+         eg->setEffect(effect);
+         ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&geode);
+         if (userData.valid())
+             eg->setUserData(new SGSceneUserData(*userData));
+         for (int i = 0; i < geode.getNumDrawables(); ++i)
+             eg->addDrawable(geode.getDrawable(i));
+     }
+     pushResultNode(&geode, eg);
+ }
+ }
+ namespace
+ {
+ class DefaultEffect : public simgear::Singleton<DefaultEffect>
+ {
+ public:
+     DefaultEffect()
+     {
+         _effect = new SGPropertyNode;
+         makeChild(_effect.ptr(), "inherits-from")
+             ->setStringValue("Effects/model-default");
+     }
+     virtual ~DefaultEffect() {}
+     SGPropertyNode* getEffect() { return _effect.ptr(); }
+ protected:
+     SGPropertyNode_ptr _effect;
+ };
+ }
+ ref_ptr<Node> instantiateEffects(osg::Node* modelGroup,
+                                  PropertyList& effectProps,
+                                  const osgDB::ReaderWriter::Options* options)
+ {
+     SGPropertyNode_ptr defaultEffectPropRoot;
+     MakeEffectVisitor visitor(options);
+     MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap();
+     for (PropertyList::iterator itr = effectProps.begin(),
+              end = effectProps.end();
+          itr != end;
+         ++itr)
+     {
+         SGPropertyNode_ptr configNode = *itr;
+         std::vector<SGPropertyNode_ptr> objectNames =
+             configNode->getChildren("object-name");
+         SGPropertyNode* defaultNode = configNode->getChild("default");
+         if (defaultNode && defaultNode->getValue<bool>())
+             defaultEffectPropRoot = configNode;
+         BOOST_FOREACH(SGPropertyNode_ptr objNameNode, objectNames) {
+             emap.insert(make_pair(objNameNode->getStringValue(), configNode));
+         }
+         configNode->removeChild("default");
+         configNode->removeChildren("object-name");
+     }
+     if (!defaultEffectPropRoot)
+         defaultEffectPropRoot = DefaultEffect::instance()->getEffect();
+     visitor.setDefaultEffect(defaultEffectPropRoot.ptr());
+     modelGroup->accept(visitor);
+     osg::NodeList& result = visitor.getResults();
+     return ref_ptr<Node>(result[0].get());
+ }
+ }
  // end of model.cxx
index 10d50acbeb903e5dfbe10512ec9e91b3473baf8c,24056ebc47bb9c6cb71a7176b1539c96b19bfb7a..229710db32ba4fa96dad76aa3ec4bf0a59c5820d
@@@ -40,7 -40,7 +40,7 @@@ struct SpriteCom
      bool operator() (const CloudShaderGeometry::SortData::SortItem& lhs,
                       const CloudShaderGeometry::SortData::SortItem& rhs) const
      {
 -        return lhs.depth < rhs.depth;
 +        return lhs.depth > rhs.depth;
      }
  };
  }
@@@ -108,11 -108,11 +108,11 @@@ void CloudShaderGeometry::drawImplement
          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,
 -                           t.width };
 -        GLfloat ua2[3] = { (GLfloat)t.height,
 -                           t.shade,
 +        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) t.shade,
                             (GLfloat) t.cloud_height };
          extensions->glVertexAttrib3fv(USR_ATTR_1, ua1 );
          extensions->glVertexAttrib3fv(USR_ATTR_2, ua2 );
      }
  }
  
- void CloudShaderGeometry::addSprite(SGVec3f& p, int tx, int ty,
+ void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
                                      float w, float h,
                                      float s, float cull, float cloud_height)
  {
      // Only add the sprite if it is further than the cull distance to all other sprites
 +    // except for the center sprite.  
      for (CloudShaderGeometry::CloudSpriteList::iterator iter = _cloudsprites.begin();
           iter != _cloudsprites.end();
           ++iter) 
      {
 -        if (distSqr(iter->position, p) < cull) {
 +        if ((iter != _cloudsprites.begin()) &&
 +          (distSqr(iter->position, p) < cull)) {
              // Too close - cull it
              return;
          }
      }
 +
      _cloudsprites.push_back(CloudSprite(p, tx, ty, w, h, s, cloud_height));
  }
  
index d641b2c92827c8e49f0a8bb17f764775c95a48c7,1518b13306a2f3b58070c7cf6d3c631c682b2ce5..f4eb8de272c6fb801ca3e4962bf9989798398adf
@@@ -44,6 -44,7 +44,7 @@@
  #include <simgear/math/sg_random.h>
  #include <simgear/misc/sg_path.hxx>
  #include <simgear/misc/PathOptions.hxx>
+ #include <simgear/props/props.hxx>
  #include <simgear/scene/model/model.hxx>
  #include <simgear/scene/util/StateAttributeFactory.hxx>
  #include <simgear/scene/util/SGUpdateVisitor.hxx>
  using namespace simgear;
  using namespace osg;
  
- typedef std::map<std::string, osg::ref_ptr<osg::StateSet> > StateSetMap;
+ namespace
+ {
+ typedef std::map<std::string, osg::ref_ptr<Effect> > EffectMap;
+ EffectMap effectMap;
+ }
  
- StateSetMap cloudTextureMap;
  double SGNewCloud::sprite_density = 1.0;
  
- static char vertexShaderSource[] = 
-     "#version 120\n"
-     "\n"
-     "varying float fogFactor;\n"
-     "attribute vec3 usrAttr1;\n"
-     "attribute vec3 usrAttr2;\n"
-     "float textureIndexX = usrAttr1.r;\n"
-     "float textureIndexY = usrAttr1.g;\n"
-     "float wScale = usrAttr1.b;\n"
-     "float hScale = usrAttr2.r;\n"
-     "float shade = usrAttr2.g;\n"
-     "float cloud_height = usrAttr2.b;\n"
-     "void main(void)\n"
-     "{\n"
-     "  gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);\n"
-     "  vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);\n"
-     "  vec4 l  = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);\n"
-     "  vec3 u = normalize(ep.xyz - l.xyz);\n"
- // Find a rotation matrix that rotates 1,0,0 into u. u, r and w are
- // the columns of that matrix.
-     "  vec3 absu = abs(u);\n"
-     "  vec3 r = normalize(vec3(-u.y, u.x, 0));\n"
-     "  vec3 w = cross(u, r);\n"
- // Do the matrix multiplication by [ u r w pos]. Assume no
- // scaling in the homogeneous component of pos.
-     "  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
-     "  gl_Position.xyz = gl_Vertex.x * u;\n"
-     "  gl_Position.xyz += gl_Vertex.y * r * wScale;\n"
-     "  gl_Position.xyz += gl_Vertex.z * w * hScale;\n"
-     "  gl_Position.xyz += gl_Color.xyz;\n"
- // Determine a lighting normal based on the vertex position from the
- // center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
-     "  float n = dot(normalize(- gl_LightSource[0].position.xyz), normalize(mat3x3(gl_ModelViewMatrix) * (- gl_Position.xyz)));\n"
- // Determine the position - used for fog and shading calculations        
-     "  vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);\n"
-     "  float fogCoord = abs(ecPosition.z);\n"
-     "  float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);\n"
- // Final position of the sprite
-     "  gl_Position = gl_ModelViewProjectionMatrix * gl_Position;\n"
- // Determine the shading of the sprite based on its vertical position and position relative to the sun.
-     "  n = min(smoothstep(-0.5, 0.0, n), fract);\n"
- // Determine the shading based on a mixture from the backlight to the front
-     "  vec4 backlight = gl_LightSource[0].diffuse * shade;\n"
-     "  gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);\n"
-     "  gl_FrontColor += gl_FrontLightModelProduct.sceneColor;\n"
- // As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
-     "  gl_FrontColor.a = min(smoothstep(10.0, 100.0, fogCoord), 1 - smoothstep(15000.0, 20000.0, fogCoord));\n"
-     "  gl_BackColor = gl_FrontColor;\n"
- // Fog doesn't affect clouds as much as other objects.        
-     "  fogFactor = exp( -gl_Fog.density * fogCoord * 0.5);\n"
-     "  fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
-     "}\n";
- static char fragmentShaderSource[] = 
-     "uniform sampler2D baseTexture; \n"
-     "varying float fogFactor;\n"
-     "\n"
-     "void main(void)\n"
-     "{\n"
-     "  vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n"
-     "  vec4 finalColor = base * gl_Color;\n"
-     "  gl_FragColor.rgb = mix(gl_Fog.color.rgb, finalColor.rgb, fogFactor );\n"
-     "  gl_FragColor.a = mix(0.0, finalColor.a, fogFactor);\n"
-     "}\n";
  
  SGNewCloud::SGNewCloud(string type,
                         const SGPath &tex_path, 
          texture(tex),
          name(type)
  {
-     // Create a new StateSet for the texture, if required.
-     StateSetMap::iterator iter = SGCloudField::cloudTextureMap.find(texture);
-     if (iter == SGCloudField::cloudTextureMap.end()) {
-         stateSet = new osg::StateSet;
-                 
-         osg::ref_ptr<osgDB::ReaderWriter::Options> options = makeOptionsFromPath(tex_path);
-                 
-         osg::Texture2D *tex = new osg::Texture2D;
-         tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
-         tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
-         tex->setImage(osgDB::readImageFile(texture, options.get()));
-                 
-         StateAttributeFactory* attribFactory = StateAttributeFactory::instance();
-         
-         stateSet->setMode(GL_LIGHTING,  osg::StateAttribute::ON);
-         stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
-         
-         // Fog handling
-         stateSet->setAttributeAndModes(attribFactory->getSmoothShadeModel());
-         stateSet->setAttributeAndModes(attribFactory->getStandardBlendFunc());
-         stateSet->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
-         stateSet->setRenderBinDetails(osg::StateSet::TRANSPARENT_BIN, "DepthSortedBin");
-         // Turn off z buffer writes. Standard hack for
-         // semi-transparent geometry to avoid sorting / flickering
-         // artifacts.
-         stateSet->setAttributeAndModes(attribFactory->getDepthWritesDisabled());
-         static ref_ptr<AlphaFunc> alphaFunc;
-         static ref_ptr<Program> program;
-         static ref_ptr<Uniform> baseTextureSampler;
-         static ref_ptr<Material> material;
-                   
-         // Generate the shader etc, if we don't already have one.
-         if (!program.valid()) {
-             alphaFunc = new AlphaFunc;
-             alphaFunc->setFunction(AlphaFunc::GREATER,0.01f);
-             program  = new Program;
-             baseTextureSampler = new osg::Uniform("baseTexture", 0);
-             Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource);
-             program->addShader(vertex_shader);
-             program->addBindAttribLocation("usrAttr1", CloudShaderGeometry::USR_ATTR_1);
-             program->addBindAttribLocation("usrAttr2", CloudShaderGeometry::USR_ATTR_2);
-             Shader* fragment_shader = new Shader(Shader::FRAGMENT, fragmentShaderSource);
-             program->addShader(fragment_shader);
-             material = new Material;
-             // DonĀ“t track vertex color
-             material->setColorMode(Material::OFF);
-             
-             // We don't actually use the material information either - see shader.
-             material->setAmbient(Material::FRONT_AND_BACK,
-                                  Vec4(0.5f, 0.5f, 0.5f, 1.0f));
-             material->setDiffuse(Material::FRONT_AND_BACK,
-                                  Vec4(0.5f, 0.5f, 0.5f, 1.0f));
-         }
-                   
-         stateSet->setAttributeAndModes(alphaFunc.get());
-         stateSet->setAttribute(program.get());
-         stateSet->addUniform(baseTextureSampler.get());
-         stateSet->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON);
-         stateSet->setAttribute(material.get());
-                 
-         // Add the newly created texture to the map for use later.
-         SGCloudField::cloudTextureMap.insert(StateSetMap::value_type(texture,  stateSet));
+     // Create a new Effect for the texture, if required.
+     EffectMap::iterator iter = effectMap.find(texture);
+     if (iter == effectMap.end()) {
+         SGPropertyNode_ptr pcloudEffect = new SGPropertyNode;
+         makeChild(pcloudEffect, "inherits-from")->setValue("Effects/cloud");
+         setValue(makeChild(makeChild(makeChild(pcloudEffect, "parameters"),
+                                      "texture"),
+                            "image"),
+                  texture);
+         osg::ref_ptr<osgDB::ReaderWriter::Options> options
+             = makeOptionsFromPath(tex_path);
+         if ((effect = makeEffect(pcloudEffect, true, options)))
+             effectMap.insert(EffectMap::value_type(texture, effect));
      } else {
-         stateSet = iter->second.get();
+         effect = iter->second.get();
      }
-     
-     quad = createOrthQuad(min_sprite_width, min_sprite_height, num_textures_x, num_textures_y);
+     quad = createOrthQuad(min_sprite_width, min_sprite_height,
+                           num_textures_x, num_textures_y);
  }
  
  SGNewCloud::~SGNewCloud() {
@@@ -285,9 -174,9 +174,9 @@@ static float Rnd(float n) 
  }
  #endif
  
- osg::ref_ptr<Geode> SGNewCloud::genCloud() {
+ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
      
-     osg::ref_ptr<osg::Geode> geode = new Geode;
+     osg::ref_ptr<EffectGeode> geode = new EffectGeode;
          
      CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height);
      
      // The value is squared as we use vector calculations.
      float cull_distance_squared = min_sprite_height * min_sprite_height * 0.1f;
      
 -    // The number of sprites we actually used is a function of the (user-controlled) density
 -    int n_sprites = num_sprites * sprite_density;
 +    // The number of sprites we actually use is a function of the (user-controlled) density
 +    int n_sprites = num_sprites * sprite_density * (0.5 + sg_random());
      
      for (int i = 0; i < n_sprites; i++)
      {
          // Determine the position of the sprite. Rather than being completely random,
          // we place them on the surface of a distorted sphere. However, we place
 -        // the first and second sprites on the top and bottom, and the third in the
 -        // center of the sphere (and at maximum size) to ensure good coverage and
 -        // reduce the chance of there being "holes" in our cloud.
 +        // the first sprite in the center of the sphere (and at maximum size) to
 +      // ensure good coverage and reduce the chance of there being "holes" in our
 +
          float x, y, z;
 -        
 +
          if (i == 0) {
 -            x = 0;
 -            y = 0;
 -            z = height * 0.5f;
 -        } else if (i == 1) {
 -            x = 0;
 -            y = 0;
 -            z = - height * 0.5f;
 -        } else if (i == 2) {
              x = 0;
              y = 0;
              z = 0;
              double elev  = sg_random() * SGD_PI;
              x = width * cos(theta) * 0.5f * sin(elev);
              y = width * sin(theta) * 0.5f * sin(elev);
 -            z = height * cos(elev) * 0.5f; 
 +            z = height * cos(elev) * 0.5f;
          }
          
-         SGVec3f *pos = new SGVec3f(x, y, z); 
-         // Determine the height and width as scaling factors on the minimum size (used to create the quad).
+         // 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;
 -        
 -        if (i == 2) {
 +
 +        // Sprites are never taller than square.
 +        if (sprite_height * min_sprite_height > sprite_width * min_sprite_width)
 +        {
 +            sprite_height = sprite_width * min_sprite_width / min_sprite_height;
 +        }
 +
 +        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;
          }
 -        
 -        // Determine the sprite texture indexes;
 +
 +        // Determine the sprite texture indexes.
          int index_x = (int) floor(sg_random() * num_textures_x);
          if (index_x == num_textures_x) { index_x--; }
 -        
 -        int index_y = (int) floor(sg_random() * num_textures_y);
 +
 +        // The y index depends on the positing of the sprite within the cloud.
 +        // This allows cloud designers to have particular sprites for the base
 +        // and tops of the cloud.
 +        int index_y = (int) floor((z / height + 0.5f) * num_textures_y);
          if (index_y == num_textures_y) { index_y--; }
          
-         sg->addSprite(*pos
+         sg->addSprite(SGVec3f(x, y, z)
                      index_x, 
                      index_y, 
                      sprite_width, 
      sg->setGeometry(quad);
      geode->addDrawable(sg);
      geode->setName("3D cloud");
-     geode->setStateSet(stateSet.get());
+     geode->setEffect(effect.get());
      
      return geode;
  }