diff --git a/simgear/scene/sky/CloudShaderGeometry.cxx b/simgear/scene/sky/CloudShaderGeometry.cxx index 92d73f96..bed0b02d 100755 --- a/simgear/scene/sky/CloudShaderGeometry.cxx +++ b/simgear/scene/sky/CloudShaderGeometry.cxx @@ -29,6 +29,7 @@ using namespace osg; using namespace osgDB; +using namespace simgear; namespace simgear { @@ -72,14 +73,23 @@ void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const 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 diff --git a/simgear/scene/sky/CloudShaderGeometry.hxx b/simgear/scene/sky/CloudShaderGeometry.hxx index af0bc36d..57bd1720 100755 --- a/simgear/scene/sky/CloudShaderGeometry.hxx +++ b/simgear/scene/sky/CloudShaderGeometry.hxx @@ -33,6 +33,7 @@ #include #include +#include namespace simgear @@ -95,6 +96,7 @@ class CloudShaderGeometry : public osg::Drawable }; typedef std::vector CloudSpriteList; + CloudSpriteList _cloudsprites; void insert(CloudSprite* t) { _cloudsprites.push_back(t); } @@ -105,9 +107,6 @@ class CloudShaderGeometry : public osg::Drawable { return _cloudsprites.size(); } CloudSprite* getCloudSprite(unsigned i) const { return _cloudsprites[i]; } - CloudSpriteList _cloudsprites; - - typedef std::vector PositionSizeList; virtual void drawImplementation(osg::RenderInfo& renderInfo) const; virtual osg::BoundingBox computeBound() const diff --git a/simgear/scene/sky/cloud.cxx b/simgear/scene/sky/cloud.cxx index 84e4f5f8..9f86c0f0 100644 --- a/simgear/scene/sky/cloud.cxx +++ b/simgear/scene/sky/cloud.cxx @@ -277,6 +277,19 @@ SGCloudLayer::setCoverage (Coverage coverage) 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(); } } @@ -733,7 +746,3 @@ void SGCloudLayer::set_enable3dClouds(bool enable) { cloud_root->setChildValue(layer_root.get(), true); } } - -void SGCloudLayer::applyDensity() { - layer3D->applyDensity(); -} diff --git a/simgear/scene/sky/cloud.hxx b/simgear/scene/sky/cloud.hxx index 4a12cfd5..4b282a61 100644 --- a/simgear/scene/sky/cloud.hxx +++ b/simgear/scene/sky/cloud.hxx @@ -165,9 +165,6 @@ public: /** 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 diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index 4cbefd7d..18ecd53a 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -65,13 +65,11 @@ using namespace simgear; #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, @@ -144,7 +142,7 @@ SGCloudField::SGCloudField() : deltax(0.0), deltay(0.0), last_course(0.0), - last_density(0.0), + last_coverage(0.0), defined3D(false), reposition_count(0) { @@ -155,7 +153,6 @@ SGCloudField::SGCloudField() : rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin"); osg::ref_ptr quad_root = new osg::Group(); - osg::ref_ptr quad[BRANCH_SIZE][BRANCH_SIZE]; for (int i = 0; i < BRANCH_SIZE; i++) { for (int j = 0; j < BRANCH_SIZE; j++) { @@ -175,7 +172,7 @@ SGCloudField::SGCloudField() : // 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); } } @@ -189,7 +186,7 @@ SGCloudField::SGCloudField() : new osg::PositionAttitudeTransform; transform->addChild(quad_root.get()); transform->setPosition(osg::Vec3(x*fieldSize, y * fieldSize, 0.0)); - + field_transform->addChild(transform.get()); } } @@ -228,15 +225,16 @@ static int densTable[][10] = { {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; @@ -250,7 +248,7 @@ void SGCloudField::applyDensity(void) { } } - last_density = density; + last_coverage = coverage; } void SGCloudField::addCloud( SGVec3f& pos, SGNewCloud *cloud) { @@ -273,3 +271,16 @@ 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); + } + } + } +} + diff --git a/simgear/scene/sky/cloudfield.hxx b/simgear/scene/sky/cloudfield.hxx index 8cac812f..f5027b43 100644 --- a/simgear/scene/sky/cloudfield.hxx +++ b/simgear/scene/sky/cloudfield.hxx @@ -68,12 +68,14 @@ private: osg::ref_ptr field_root; osg::ref_ptr field_transform; osg::ref_ptr field_group[QUADTREE_SIZE][QUADTREE_SIZE]; + osg::ref_ptr quad[BRANCH_SIZE][BRANCH_SIZE]; + osg::ref_ptr 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: @@ -106,17 +108,21 @@ 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 float getCoverage(void) { return coverage; } + static void setCoverage(float coverage) { coverage = coverage; } - static void set_density(float density); - - 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 diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index 9e01cc57..d67ca971 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -58,8 +58,13 @@ using namespace simgear; using namespace osg; typedef std::map > StateSetMap; +typedef std::vector< osg::ref_ptr > GeodeList; +typedef std::map CloudMap; static StateSetMap cloudTextureMap; +static CloudMap cloudMap; +double SGNewCloud::sprite_density = 1.0; +int SGNewCloud::num_flavours = 10; static char vertexShaderSource[] = "#version 120\n" @@ -135,7 +140,8 @@ class SGCloudFogUpdateCallback : public osg::StateAttribute::Callback { } }; -SGNewCloud::SGNewCloud(const SGPath &tex_path, +SGNewCloud::SGNewCloud(string type, + const SGPath &tex_path, string tex, double min_w, double max_w, @@ -161,7 +167,8 @@ SGNewCloud::SGNewCloud(const SGPath &tex_path, 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); @@ -294,81 +301,121 @@ static float Rnd(float n) { } osg::ref_ptr 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 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++) - { - // 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); + GeodeList* g = (*iter).second; - // 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 (iter == cloudMap.end() || g->size() < num_flavours) + { + geode = new Geode; - 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; + CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_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; + + 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; } + diff --git a/simgear/scene/sky/newcloud.hxx b/simgear/scene/sky/newcloud.hxx index 76ded5e9..a4ff8be4 100644 --- a/simgear/scene/sky/newcloud.hxx +++ b/simgear/scene/sky/newcloud.hxx @@ -40,7 +40,8 @@ using std::vector; class SGNewCloud { public: - SGNewCloud(const SGPath &tex_path, + SGNewCloud(string type, + const SGPath &tex_path, string tex, double min_w, double max_w, @@ -54,12 +55,37 @@ public: int n, int nt_x, int nt_y); - - ~SGNewCloud(); + + ~SGNewCloud(); // Generate a Cloud osg::ref_ptr 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; @@ -75,14 +101,17 @@ private: int num_textures_x; int num_textures_y; const string texture; + const string name; osg::Geometry* quad; osg::ref_ptr 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 diff --git a/simgear/scene/sky/sky.cxx b/simgear/scene/sky/sky.cxx index 6772e260..b1f92126 100644 --- a/simgear/scene/sky/sky.cxx +++ b/simgear/scene/sky/sky.cxx @@ -188,18 +188,35 @@ SGSky::get_cloud_layer_count () const } 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 ); } diff --git a/simgear/scene/sky/sky.hxx b/simgear/scene/sky/sky.hxx index 899213d8..9e6a1b1b 100644 --- a/simgear/scene/sky/sky.hxx +++ b/simgear/scene/sky/sky.hxx @@ -422,6 +422,22 @@ public: */ 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); + };