using namespace osg;
using namespace osgDB;
+using namespace simgear;
namespace simgear
{
p = q;
}
+
if (sorted)
{
// This cloud is sorted, so no need to re-sort.
- // Maximum of every 128 frames (2 - 4 seconds)
skip_info->skip_limit = skip_info->skip_limit * 2;
+
+ if (skip_info->skip_limit > 30)
+ {
+ // Jitter the skip frames to avoid synchronized sorts
+ // which will cause periodic frame-rate drops
+ skip_info->skip_limit += sg_random() * 10;
+ }
+
if (skip_info->skip_limit > 128)
{
- skip_info->skip_limit = 128;
+ // Maximum of every 128 frames (2 - 4 seconds)
+ skip_info->skip_limit = 128 + sg_random() * 10;
}
}
else
#include <osg/Vec4>
#include <simgear/math/SGMath.hxx>
+#include <simgear/math/sg_random.h>
namespace simgear
};
typedef std::vector<CloudSprite*> CloudSpriteList;
+ CloudSpriteList _cloudsprites;
void insert(CloudSprite* t)
{ _cloudsprites.push_back(t); }
{ return _cloudsprites.size(); }
CloudSprite* getCloudSprite(unsigned i) const
{ return _cloudsprites[i]; }
- CloudSpriteList _cloudsprites;
-
- typedef std::vector<osg::Vec4> PositionSizeList;
virtual void drawImplementation(osg::RenderInfo& renderInfo) const;
virtual osg::BoundingBox computeBound() const
if (coverage != layer_coverage) {
layer_coverage = coverage;
rebuild();
+
+ double coverage_norm = 0.0;
+ if( coverage == SG_CLOUD_FEW)
+ coverage_norm = 2.0/8.0; // <1-2
+ else if( coverage == SG_CLOUD_SCATTERED )
+ coverage_norm = 4.0/8.0; // 3-4
+ else if( coverage == SG_CLOUD_BROKEN )
+ coverage_norm = 6.0/8.0; // 5-7
+ else if( coverage == SG_CLOUD_OVERCAST )
+ coverage_norm = 8.0/8.0; // 8
+
+ layer3D->setCoverage(coverage_norm);
+ layer3D->applyCoverage();
}
}
cloud_root->setChildValue(layer_root.get(), true);
}
}
-
-void SGCloudLayer::applyDensity() {
- layer3D->applyDensity();
-}
/** Enable/disable 3D clouds in this layer */
void set_enable3dClouds(bool enable);
- /** Set 3D cloud density in this layer */
- void applyDensity();
-
/**
* repaint the cloud colors based on the specified fog_color
* @param fog_color the fog color
#endif
float SGCloudField::fieldSize = 50000.0f;
-float SGCloudField::density = 100.0f;
+float SGCloudField::coverage = 1.0f;
double SGCloudField::timer_dt = 0.0;
+float SGCloudField::view_distance = 20000.0f;
sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
-void SGCloudField::set_density(float density) {
- SGCloudField::density = density;
-}
// reposition the cloud layer at the specified origin and orientation
bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
deltax(0.0),
deltay(0.0),
last_course(0.0),
- last_density(0.0),
+ last_coverage(0.0),
defined3D(false),
reposition_count(0)
{
rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
osg::ref_ptr<osg::Group> quad_root = new osg::Group();
- osg::ref_ptr<osg::LOD> quad[BRANCH_SIZE][BRANCH_SIZE];
for (int i = 0; i < BRANCH_SIZE; i++) {
for (int j = 0; j < BRANCH_SIZE; j++) {
// 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, 20000.0f);
+ quad[i][j]->addChild(field_group[x][y].get(), 0.0f, view_distance);
}
}
new osg::PositionAttitudeTransform;
transform->addChild(quad_root.get());
transform->setPosition(osg::Vec3(x*fieldSize, y * fieldSize, 0.0));
-
+
field_transform->addChild(transform.get());
}
}
{1,1,1,1,1,1,1,1,1,1}
};
-void SGCloudField::applyDensity(void) {
+void SGCloudField::applyCoverage(void) {
- int row = (int) (density / 10.0);
+ int row = (int) (coverage * 10.0);
+ if (row > 10) row = 9;
int col = 0;
- if (density != last_density) {
+ 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 density.
+ // 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;
}
}
- last_density = density;
+ last_coverage = coverage;
}
void SGCloudField::addCloud( SGVec3f& pos, SGNewCloud *cloud) {
field_group[x][y]->addChild(transform.get(), true);
}
+
+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);
+ }
+ }
+ }
+}
+
osg::ref_ptr<osg::Group> field_root;
osg::ref_ptr<osg::MatrixTransform> field_transform;
osg::ref_ptr<osg::Switch> field_group[QUADTREE_SIZE][QUADTREE_SIZE];
+ osg::ref_ptr<osg::LOD> quad[BRANCH_SIZE][BRANCH_SIZE];
+
osg::ref_ptr<osg::LOD> field_lod;
double deltax, deltay, alt;
double last_course;
sgSphere field_sphere;
- float last_density;
+ float last_coverage;
SGGeoc cld_pos;
int reposition_count;
public:
static sgVec3 view_vec, view_X, view_Y;
- static float density;
- static double timer_dt;
+ static float coverage;
+ static float view_distance;
+ static double timer_dt;
static float fieldSize;
bool defined3D;
- static float get_density(void) { return density; }
-
- static void set_density(float density);
+ static float getCoverage(void) { return coverage; }
+ static void setCoverage(float coverage) { coverage = coverage; }
- void applyDensity(void);
+ static float getVisRange(void) { return view_distance; }
+ static void setVisRange(float d) { view_distance = d; }
+
+ void applyCoverage(void);
+ void applyVisRange(void);
};
#endif // _CLOUDFIELD_HXX
using namespace osg;
typedef std::map<std::string, osg::ref_ptr<osg::StateSet> > StateSetMap;
+typedef std::vector< osg::ref_ptr<osg::Geode> > GeodeList;
+typedef std::map<std::string, GeodeList*> CloudMap;
static StateSetMap cloudTextureMap;
+static CloudMap cloudMap;
+double SGNewCloud::sprite_density = 1.0;
+int SGNewCloud::num_flavours = 10;
static char vertexShaderSource[] =
"#version 120\n"
}
};
-SGNewCloud::SGNewCloud(const SGPath &tex_path,
+SGNewCloud::SGNewCloud(string type,
+ const SGPath &tex_path,
string tex,
double min_w,
double max_w,
num_sprites(n),
num_textures_x(nt_x),
num_textures_y(nt_y),
- texture(tex)
+ texture(tex),
+ name(type)
{
// Create a new StateSet for the texture, if required.
StateSetMap::iterator iter = cloudTextureMap.find(texture);
}
osg::ref_ptr<Geode> SGNewCloud::genCloud() {
- Geode* geode = new Geode;
- CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height);
+ CloudMap::iterator iter = cloudMap.find(name);
+ osg::ref_ptr<osg::Geode> geode;
- // Determine how big this specific cloud instance is. Note that we subtract
- // the sprite size because the width/height is used to define the limits of
- // the center of the sprites, not their edges.
- float width = min_width + sg_random() * (max_width - min_width) - min_sprite_width;
- float height = min_height + sg_random() * (max_height - min_height) - min_sprite_height;
+ // We generate up to num_flavours of different versions
+ // of the same cloud before we start re-using them. This
+ // allows us to strike a balance between performance and
+ // visual complexity.
- // Determine the cull distance. This is used to remove sprites that are too close together.
- // The value is squared as we use vector calculations.
- float cull_distance_squared = min_sprite_height * min_sprite_height * 0.05f;
-
- for (int i = 0; i < num_sprites; i++)
+ GeodeList* g = (*iter).second;
+
+ if (iter == cloudMap.end() || g->size() < num_flavours)
{
- // 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.
- float x, y, z;
+ geode = new Geode;
- 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;
- } else {
- double theta = sg_random() * SGD_2PI;
- 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;
- }
+ CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height);
- SGVec3f *pos = new SGVec3f(x, y, z);
-
- // 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 how big this specific cloud instance is. Note that we subtract
+ // the sprite size because the width/height is used to define the limits of
+ // the center of the sprites, not their edges.
+ float width = min_width + sg_random() * (max_width - min_width) - min_sprite_width;
+ float height = min_height + sg_random() * (max_height - min_height) - min_sprite_height;
+
+ // Determine the cull distance. This is used to remove sprites that are too close together.
+ // 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;
- if (i == 2) {
- // 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;
+ 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.
+ 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;
+ } else {
+ double theta = sg_random() * SGD_2PI;
+ 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;
+ }
+
+ SGVec3f *pos = new SGVec3f(x, y, z);
+
+ // 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) {
+ // 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;
+ 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);
+ if (index_y == num_textures_y) { index_y--; }
+
+ sg->addSprite(*pos,
+ index_x,
+ index_y,
+ sprite_width,
+ sprite_height,
+ bottom_shade,
+ cull_distance_squared,
+ height * 0.5f);
}
- // 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);
- if (index_y == num_textures_y) { index_y--; }
+ sg->setGeometry(quad);
+ geode->addDrawable(sg);
+ geode->setName("3D cloud");
+ geode->setStateSet(stateSet.get());
- sg->addSprite(*pos,
- index_x,
- index_y,
- sprite_width,
- sprite_height,
- bottom_shade,
- cull_distance_squared,
- height * 0.5f);
+ if (iter == cloudMap.end())
+ {
+ // This is the first of this cloud to be generated.
+ GeodeList* geodelist = new GeodeList;
+ geodelist->push_back(geode);
+ cloudMap.insert(CloudMap::value_type(name, geodelist));
+ }
+ else
+ {
+ // Add the new cloud to the list of geodes
+ (*iter).second->push_back(geode.get());
+ }
+
+ } else {
+
+ int index = sg_random() * num_flavours;
+ if (index == num_flavours) index--;
+
+ geode = iter->second->at(index);
}
- sg->setGeometry(quad);
- geode->addDrawable(sg);
- geode->setName("3D cloud");
- geode->setStateSet(stateSet.get());
return geode;
}
+
class SGNewCloud {
public:
- SGNewCloud(const SGPath &tex_path,
+ SGNewCloud(string type,
+ const SGPath &tex_path,
string tex,
double min_w,
double max_w,
int n,
int nt_x,
int nt_y);
-
- ~SGNewCloud();
+
+ ~SGNewCloud();
// Generate a Cloud
osg::ref_ptr<osg::Geode> genCloud ();
+ static double getDensity(void)
+ {
+ return sprite_density;
+ }
+
+ // Set the sprite density
+ static void setDensity(double d)
+ {
+ sprite_density = d;
+ }
+
+ static int getNumFlavours(void)
+ {
+ return num_flavours;
+ }
+
+ // Set the number of flavours of this cloud.
+ // This is the number of different instances
+ // to generate.
+ static void setNumFlavours(int d)
+ {
+ num_flavours = d;
+ }
+
+
private:
double min_width;
int num_textures_x;
int num_textures_y;
const string texture;
+ const string name;
osg::Geometry* quad;
osg::ref_ptr<osg::StateSet> stateSet;
+ static double sprite_density;
+ static int num_flavours;
osg::Geometry* createOrthQuad(float w, float h, int varieties_x, int varieties_y);
-public:
-
};
+
+
#endif // _NEWCLOUD_HXX
}
double SGSky::get_3dCloudDensity() const {
- return SGCloudField::get_density();
+ return SGNewCloud::getDensity();
}
void SGSky::set_3dCloudDensity(double density)
{
- SGCloudField::set_density(density);
+ SGNewCloud::setDensity(density);
+}
- for ( unsigned i = 0; i < cloud_layers.size(); ++i ) {
- cloud_layers[i]->applyDensity();
+float SGSky::get_3dCloudVisRange() const {
+ return SGCloudField::getVisRange();
+}
+
+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();
}
}
+float SGSky::get_3dCloudNumFlavours() const {
+ return (float) SGNewCloud::getNumFlavours();
+}
+
+void SGSky::set_3dCloudNumFlavours(float n)
+{
+ SGNewCloud::setNumFlavours((int) n);
+}
+
void SGSky::texture_path( const string& path ) {
tex_path = SGPath( path );
}
*/
virtual void set_3dCloudDensity(double density);
+ /** Get 3D cloud visibility range*/
+ virtual float get_3dCloudVisRange() const;
+
+ /** Set 3D cloud visibility range
+ * @param density 3D cloud visibility range
+ */
+ virtual void set_3dCloudVisRange(float vis);
+
+ /** Get 3D cloud number of flavours*/
+ virtual float get_3dCloudNumFlavours() const;
+
+ /** Set 3D cloud number of flavours
+ * @param density 3D cloud number of flavours
+ */
+ virtual void set_3dCloudNumFlavours(float n);
+
};