Further enhancements/bug fixes to the 3D clouds:

1) Default values for [min|max]_[cloud|sprite]_[width|height] changed to be
   more logical.

2) Cloud bounding box expanded slightly to ensure they aren't over-culled

3) Cloud location now defines the _base_ of the cloud.

4) Sprites that would extend below the bottom of the cloud are now shifted
   upwards to ensure no cloud appears below the base.
This commit is contained in:
Stuart Buchanan
2011-08-21 19:18:24 +01:00
parent ee06c12374
commit 2f381c22e3
3 changed files with 153 additions and 127 deletions

View File

@@ -675,138 +675,142 @@ bool SGCloudLayer::repaint( const SGVec3f& fog_color ) {
bool SGCloudLayer::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
double alt, double dt )
{
// combine p and asl (meters) to get translation offset
osg::Vec3 asl_offset(toOsg(up));
asl_offset.normalize();
if ( alt <= layer_asl ) {
asl_offset *= layer_asl;
} else {
asl_offset *= layer_asl + layer_thickness;
}
if (getCoverage() != SGCloudLayer::SG_CLOUD_CLEAR)
{
// combine p and asl (meters) to get translation offset
osg::Vec3 asl_offset(toOsg(up));
asl_offset.normalize();
if ( alt <= layer_asl ) {
asl_offset *= layer_asl;
} else {
asl_offset *= layer_asl + layer_thickness;
}
// cout << "asl_offset = " << asl_offset[0] << "," << asl_offset[1]
// << "," << asl_offset[2] << endl;
asl_offset += toOsg(p);
// cout << " asl_offset = " << asl_offset[0] << "," << asl_offset[1]
// << "," << asl_offset[2] << endl;
// cout << "asl_offset = " << asl_offset[0] << "," << asl_offset[1]
// << "," << asl_offset[2] << endl;
asl_offset += toOsg(p);
// cout << " asl_offset = " << asl_offset[0] << "," << asl_offset[1]
// << "," << asl_offset[2] << endl;
osg::Matrix T, LON, LAT;
// Translate to zero elevation
// Point3D zero_elev = current_view.get_cur_zero_elev();
T.makeTranslate( asl_offset );
osg::Matrix T, LON, LAT;
// Translate to zero elevation
// Point3D zero_elev = current_view.get_cur_zero_elev();
T.makeTranslate( asl_offset );
// printf(" Translated to %.2f %.2f %.2f\n",
// zero_elev.x, zero_elev.y, zero_elev.z );
// printf(" Translated to %.2f %.2f %.2f\n",
// zero_elev.x, zero_elev.y, zero_elev.z );
// Rotate to proper orientation
// printf(" lon = %.2f lat = %.2f\n",
// lon * SGD_RADIANS_TO_DEGREES,
// lat * SGD_RADIANS_TO_DEGREES);
LON.makeRotate(lon, osg::Vec3(0, 0, 1));
// Rotate to proper orientation
// printf(" lon = %.2f lat = %.2f\n",
// lon * SGD_RADIANS_TO_DEGREES,
// lat * SGD_RADIANS_TO_DEGREES);
LON.makeRotate(lon, osg::Vec3(0, 0, 1));
// xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
// 0.0, 1.0, 0.0 );
LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
// xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
// 0.0, 1.0, 0.0 );
LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
layer_transform->setMatrix( LAT*LON*T );
layer_transform->setMatrix( LAT*LON*T );
// The layers need to be drawn in order because they are
// translucent, but OSG transparency sorting doesn't work because
// the cloud polys are huge. However, the ordering is simple: the
// bottom polys should be drawn from high altitude to low, and the
// top polygons from low to high. The altitude can be used
// directly to order the polygons!
group_bottom->getStateSet()->setRenderBinDetails(-(int)layer_asl,
"RenderBin");
group_top->getStateSet()->setRenderBinDetails((int)layer_asl,
"RenderBin");
if ( alt <= layer_asl ) {
layer_root->setSingleChildOn(0);
} else if ( alt >= layer_asl + layer_thickness ) {
layer_root->setSingleChildOn(1);
} else {
layer_root->setAllChildrenOff();
}
// The layers need to be drawn in order because they are
// translucent, but OSG transparency sorting doesn't work because
// the cloud polys are huge. However, the ordering is simple: the
// bottom polys should be drawn from high altitude to low, and the
// top polygons from low to high. The altitude can be used
// directly to order the polygons!
group_bottom->getStateSet()->setRenderBinDetails(-(int)layer_asl,
"RenderBin");
group_top->getStateSet()->setRenderBinDetails((int)layer_asl,
"RenderBin");
if ( alt <= layer_asl ) {
layer_root->setSingleChildOn(0);
} else if ( alt >= layer_asl + layer_thickness ) {
layer_root->setSingleChildOn(1);
} else {
layer_root->setAllChildrenOff();
}
// now calculate update texture coordinates
SGGeod pos = SGGeod::fromRad(lon, lat);
if ( last_pos == SGGeod() ) {
last_pos = pos;
}
double sp_dist = speed*dt;
if ( lon != last_pos.getLongitudeRad() || lat != last_pos.getLatitudeRad() || sp_dist != 0 ) {
double course = SGGeodesy::courseDeg(last_pos, pos) * SG_DEGREES_TO_RADIANS,
dist = SGGeodesy::distanceM(last_pos, pos);
// now calculate update texture coordinates
SGGeod pos = SGGeod::fromRad(lon, lat);
if ( last_pos == SGGeod() ) {
last_pos = pos;
}
// 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;
} else {
last_course = course;
}
double sp_dist = speed*dt;
if ( lon != last_pos.getLongitudeRad() || lat != last_pos.getLatitudeRad() || sp_dist != 0 ) {
double course = SGGeodesy::courseDeg(last_pos, pos) * SG_DEGREES_TO_RADIANS,
dist = SGGeodesy::distanceM(last_pos, pos);
// calculate cloud movement due to external forces
double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0;
// 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;
} else {
last_course = course;
}
if (dist > 0.0) {
ax = -cos(course) * dist;
ay = sin(course) * dist;
}
// calculate cloud movement due to external forces
double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0;
if (dist > 0.0) {
ax = -cos(course) * dist;
ay = sin(course) * dist;
}
if (sp_dist > 0) {
bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
}
if (sp_dist > 0) {
bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
}
double xoff = (ax + bx) / (2 * scale);
double yoff = (ay + by) / (2 * scale);
double xoff = (ax + bx) / (2 * scale);
double yoff = (ay + by) / (2 * scale);
// const float layer_scale = layer_span / scale;
// const float layer_scale = layer_span / scale;
// cout << "xoff = " << xoff << ", yoff = " << yoff << endl;
base[0] += xoff;
// cout << "xoff = " << xoff << ", yoff = " << yoff << endl;
base[0] += xoff;
// the while loops can lead to *long* pauses if base[0] comes
// with a bogus value.
// while ( base[0] > 1.0 ) { base[0] -= 1.0; }
// while ( base[0] < 0.0 ) { base[0] += 1.0; }
if ( base[0] > -10.0 && base[0] < 10.0 ) {
base[0] -= (int)base[0];
} else {
SG_LOG(SG_ASTRO, SG_DEBUG,
"Error: base = " << base[0] << "," << base[1] <<
" course = " << course << " dist = " << dist );
base[0] = 0.0;
}
base[1] += yoff;
// the while loops can lead to *long* pauses if base[0] comes
// with a bogus value.
// while ( base[1] > 1.0 ) { base[1] -= 1.0; }
// while ( base[1] < 0.0 ) { base[1] += 1.0; }
if ( base[1] > -10.0 && base[1] < 10.0 ) {
base[1] -= (int)base[1];
} else {
SG_LOG(SG_ASTRO, SG_DEBUG,
// the while loops can lead to *long* pauses if base[0] comes
// with a bogus value.
// while ( base[0] > 1.0 ) { base[0] -= 1.0; }
// while ( base[0] < 0.0 ) { base[0] += 1.0; }
if ( base[0] > -10.0 && base[0] < 10.0 ) {
base[0] -= (int)base[0];
} else {
SG_LOG(SG_ASTRO, SG_DEBUG,
"Error: base = " << base[0] << "," << base[1] <<
" course = " << course << " dist = " << dist );
base[1] = 0.0;
base[0] = 0.0;
}
base[1] += yoff;
// the while loops can lead to *long* pauses if base[0] comes
// with a bogus value.
// while ( base[1] > 1.0 ) { base[1] -= 1.0; }
// while ( base[1] < 0.0 ) { base[1] += 1.0; }
if ( base[1] > -10.0 && base[1] < 10.0 ) {
base[1] -= (int)base[1];
} else {
SG_LOG(SG_ASTRO, SG_DEBUG,
"Error: base = " << base[0] << "," << base[1] <<
" course = " << course << " dist = " << dist );
base[1] = 0.0;
}
// cout << "base = " << base[0] << "," << base[1] << endl;
setTextureOffset(base);
last_pos = pos;
}
// cout << "base = " << base[0] << "," << base[1] << endl;
setTextureOffset(base);
last_pos = pos;
}
layer3D->reposition( p, up, lon, lat, dt, layer_asl, speed, direction);

View File

@@ -72,16 +72,17 @@ double SGNewCloud::sprite_density = 1.0;
SGNewCloud::SGNewCloud(const SGPath &texture_root, const SGPropertyNode *cld_def)
{
min_width = cld_def->getDoubleValue("min-cloud-width-m", 500.0);
max_width = cld_def->getDoubleValue("max-cloud-width-m", 1000.0);
min_height = cld_def->getDoubleValue("min-cloud-height-m", min_width);
max_height = cld_def->getDoubleValue("max-cloud-height-m", max_width);
max_width = cld_def->getDoubleValue("max-cloud-width-m", min_width*2);
min_height = cld_def->getDoubleValue("min-cloud-height-m", 400.0);
max_height = cld_def->getDoubleValue("max-cloud-height-m", min_height*2);
min_sprite_width = cld_def->getDoubleValue("min-sprite-width-m", 200.0);
max_sprite_width = cld_def->getDoubleValue("max-sprite-width-m", min_sprite_width);
min_sprite_height = cld_def->getDoubleValue("min-sprite-height-m", min_sprite_width);
max_sprite_height = cld_def->getDoubleValue("max-sprite-height-m", max_sprite_width);
max_sprite_width = cld_def->getDoubleValue("max-sprite-width-m", min_sprite_width*1.5);
min_sprite_height = cld_def->getDoubleValue("min-sprite-height-m", 150);
max_sprite_height = cld_def->getDoubleValue("max-sprite-height-m", min_sprite_height*1.5);
num_sprites = cld_def->getIntValue("num-sprites", 20);
num_textures_x = cld_def->getIntValue("num-textures-x", 4);
num_textures_y = cld_def->getIntValue("num-textures-y", 4);
height_map_texture = cld_def->getBoolValue("height-map-texture", false);
bottom_shade = cld_def->getDoubleValue("bottom-shade", 1.0);
zscale = cld_def->getDoubleValue("z-scale", 1.0);
texture = cld_def->getStringValue("texture", "cl_cumulus.png");
@@ -166,7 +167,11 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
osg::ref_ptr<EffectGeode> geode = new EffectGeode;
CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height, zscale);
CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x,
num_textures_y,
max_width + max_sprite_width,
max_height + max_sprite_height,
zscale);
// 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
@@ -186,26 +191,28 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
// 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 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
// ensure good coverage and reduce the chance of there being "holes" in the
// middle of our cloud. Also note that (0,0,0) defines the _bottom_ of the
// cloud, not the middle.
float x, y, z;
if (i == 0) {
x = 0;
y = 0;
z = 0;
z = height * 0.5;
} 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;
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;
// Sprites are never taller than square.
if (sprite_height * min_sprite_height > sprite_width * min_sprite_width)
{
@@ -217,17 +224,31 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
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;
}
// 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)
{
z = 0.5f * 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--; }
// 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);
int index_y = (int) floor(sg_random() * num_textures_y);
if (height_map_texture) {
// The y index depends on the position of the sprite within the cloud.
// This allows cloud designers to have particular sprites for the base
// and tops of the cloud.
index_y = (int) floor((z / height + 0.5f) * num_textures_y);
}
if (index_y == num_textures_y) { index_y--; }
sg->addSprite(SGVec3f(x, y, z),
index_x,
index_y,

View File

@@ -71,6 +71,7 @@ private:
double max_sprite_height;
double bottom_shade;
double zscale;
bool height_map_texture;
int num_sprites;
int num_textures_x;
int num_textures_y;