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:
+120
-116
@@ -675,138 +675,142 @@ bool SGCloudLayer::repaint( const SGVec3f& fog_color ) {
|
|||||||
bool SGCloudLayer::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
|
bool SGCloudLayer::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
|
||||||
double alt, double dt )
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cout << "asl_offset = " << asl_offset[0] << "," << asl_offset[1]
|
if (getCoverage() != SGCloudLayer::SG_CLOUD_CLEAR)
|
||||||
// << "," << asl_offset[2] << endl;
|
{
|
||||||
asl_offset += toOsg(p);
|
// combine p and asl (meters) to get translation offset
|
||||||
// cout << " asl_offset = " << asl_offset[0] << "," << asl_offset[1]
|
osg::Vec3 asl_offset(toOsg(up));
|
||||||
// << "," << asl_offset[2] << endl;
|
asl_offset.normalize();
|
||||||
|
if ( alt <= layer_asl ) {
|
||||||
osg::Matrix T, LON, LAT;
|
asl_offset *= layer_asl;
|
||||||
// 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 );
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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 {
|
} else {
|
||||||
last_course = course;
|
asl_offset *= layer_asl + layer_thickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate cloud movement due to external forces
|
// cout << "asl_offset = " << asl_offset[0] << "," << asl_offset[1]
|
||||||
double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0;
|
// << "," << asl_offset[2] << endl;
|
||||||
|
asl_offset += toOsg(p);
|
||||||
|
// cout << " asl_offset = " << asl_offset[0] << "," << asl_offset[1]
|
||||||
|
// << "," << asl_offset[2] << endl;
|
||||||
|
|
||||||
if (dist > 0.0) {
|
osg::Matrix T, LON, LAT;
|
||||||
ax = -cos(course) * dist;
|
// Translate to zero elevation
|
||||||
ay = sin(course) * dist;
|
// Point3D zero_elev = current_view.get_cur_zero_elev();
|
||||||
}
|
T.makeTranslate( asl_offset );
|
||||||
|
|
||||||
if (sp_dist > 0) {
|
// printf(" Translated to %.2f %.2f %.2f\n",
|
||||||
bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
|
// zero_elev.x, zero_elev.y, zero_elev.z );
|
||||||
by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
double xoff = (ax + bx) / (2 * scale);
|
// xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
|
||||||
double yoff = (ay + by) / (2 * scale);
|
// 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 );
|
||||||
|
|
||||||
// const float layer_scale = layer_span / scale;
|
// The layers need to be drawn in order because they are
|
||||||
|
// translucent, but OSG transparency sorting doesn't work because
|
||||||
// cout << "xoff = " << xoff << ", yoff = " << yoff << endl;
|
// the cloud polys are huge. However, the ordering is simple: the
|
||||||
base[0] += xoff;
|
// bottom polys should be drawn from high altitude to low, and the
|
||||||
|
// top polygons from low to high. The altitude can be used
|
||||||
// the while loops can lead to *long* pauses if base[0] comes
|
// directly to order the polygons!
|
||||||
// with a bogus value.
|
group_bottom->getStateSet()->setRenderBinDetails(-(int)layer_asl,
|
||||||
// while ( base[0] > 1.0 ) { base[0] -= 1.0; }
|
"RenderBin");
|
||||||
// while ( base[0] < 0.0 ) { base[0] += 1.0; }
|
group_top->getStateSet()->setRenderBinDetails((int)layer_asl,
|
||||||
if ( base[0] > -10.0 && base[0] < 10.0 ) {
|
"RenderBin");
|
||||||
base[0] -= (int)base[0];
|
if ( alt <= layer_asl ) {
|
||||||
|
layer_root->setSingleChildOn(0);
|
||||||
|
} else if ( alt >= layer_asl + layer_thickness ) {
|
||||||
|
layer_root->setSingleChildOn(1);
|
||||||
} else {
|
} else {
|
||||||
SG_LOG(SG_ASTRO, SG_DEBUG,
|
layer_root->setAllChildrenOff();
|
||||||
"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
|
// now calculate update texture coordinates
|
||||||
// with a bogus value.
|
SGGeod pos = SGGeod::fromRad(lon, lat);
|
||||||
// while ( base[1] > 1.0 ) { base[1] -= 1.0; }
|
if ( last_pos == SGGeod() ) {
|
||||||
// while ( base[1] < 0.0 ) { base[1] += 1.0; }
|
last_pos = pos;
|
||||||
if ( base[1] > -10.0 && base[1] < 10.0 ) {
|
}
|
||||||
base[1] -= (int)base[1];
|
|
||||||
} else {
|
double sp_dist = speed*dt;
|
||||||
SG_LOG(SG_ASTRO, SG_DEBUG,
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double xoff = (ax + bx) / (2 * scale);
|
||||||
|
double yoff = (ay + by) / (2 * scale);
|
||||||
|
|
||||||
|
|
||||||
|
// const float layer_scale = layer_span / scale;
|
||||||
|
|
||||||
|
// 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] <<
|
"Error: base = " << base[0] << "," << base[1] <<
|
||||||
" course = " << course << " dist = " << dist );
|
" 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);
|
layer3D->reposition( p, up, lon, lat, dt, layer_asl, speed, direction);
|
||||||
|
|||||||
@@ -72,16 +72,17 @@ double SGNewCloud::sprite_density = 1.0;
|
|||||||
SGNewCloud::SGNewCloud(const SGPath &texture_root, const SGPropertyNode *cld_def)
|
SGNewCloud::SGNewCloud(const SGPath &texture_root, const SGPropertyNode *cld_def)
|
||||||
{
|
{
|
||||||
min_width = cld_def->getDoubleValue("min-cloud-width-m", 500.0);
|
min_width = cld_def->getDoubleValue("min-cloud-width-m", 500.0);
|
||||||
max_width = cld_def->getDoubleValue("max-cloud-width-m", 1000.0);
|
max_width = cld_def->getDoubleValue("max-cloud-width-m", min_width*2);
|
||||||
min_height = cld_def->getDoubleValue("min-cloud-height-m", min_width);
|
min_height = cld_def->getDoubleValue("min-cloud-height-m", 400.0);
|
||||||
max_height = cld_def->getDoubleValue("max-cloud-height-m", max_width);
|
max_height = cld_def->getDoubleValue("max-cloud-height-m", min_height*2);
|
||||||
min_sprite_width = cld_def->getDoubleValue("min-sprite-width-m", 200.0);
|
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);
|
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", min_sprite_width);
|
min_sprite_height = cld_def->getDoubleValue("min-sprite-height-m", 150);
|
||||||
max_sprite_height = cld_def->getDoubleValue("max-sprite-height-m", max_sprite_width);
|
max_sprite_height = cld_def->getDoubleValue("max-sprite-height-m", min_sprite_height*1.5);
|
||||||
num_sprites = cld_def->getIntValue("num-sprites", 20);
|
num_sprites = cld_def->getIntValue("num-sprites", 20);
|
||||||
num_textures_x = cld_def->getIntValue("num-textures-x", 4);
|
num_textures_x = cld_def->getIntValue("num-textures-x", 4);
|
||||||
num_textures_y = cld_def->getIntValue("num-textures-y", 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);
|
bottom_shade = cld_def->getDoubleValue("bottom-shade", 1.0);
|
||||||
zscale = cld_def->getDoubleValue("z-scale", 1.0);
|
zscale = cld_def->getDoubleValue("z-scale", 1.0);
|
||||||
texture = cld_def->getStringValue("texture", "cl_cumulus.png");
|
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;
|
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
|
// 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 sprite size because the width/height is used to define the limits of
|
||||||
@@ -186,20 +191,22 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
|
|||||||
// Determine the position of the sprite. Rather than being completely random,
|
// Determine the position of the sprite. Rather than being completely random,
|
||||||
// we place them on the surface of a distorted sphere. However, we place
|
// 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
|
// 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;
|
float x, y, z;
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
x = 0;
|
x = 0;
|
||||||
y = 0;
|
y = 0;
|
||||||
z = 0;
|
z = height * 0.5;
|
||||||
} else {
|
} else {
|
||||||
double theta = sg_random() * SGD_2PI;
|
double theta = sg_random() * SGD_2PI;
|
||||||
double elev = sg_random() * SGD_PI;
|
double elev = sg_random() * SGD_PI;
|
||||||
x = width * cos(theta) * 0.5f * sin(elev);
|
x = width * cos(theta) * 0.5f * sin(elev);
|
||||||
y = width * sin(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).
|
// Determine the height and width as scaling factors on the minimum size (used to create the quad).
|
||||||
@@ -218,16 +225,30 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
|
|||||||
sprite_height = 1.0f + (max_sprite_height - min_sprite_height) / min_sprite_height;
|
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.
|
// Determine the sprite texture indexes.
|
||||||
int index_x = (int) floor(sg_random() * num_textures_x);
|
int index_x = (int) floor(sg_random() * num_textures_x);
|
||||||
if (index_x == num_textures_x) { index_x--; }
|
if (index_x == num_textures_x) { index_x--; }
|
||||||
|
|
||||||
// The y index depends on the positing of the sprite within the cloud.
|
int index_y = (int) floor(sg_random() * num_textures_y);
|
||||||
// This allows cloud designers to have particular sprites for the base
|
|
||||||
// and tops of the cloud.
|
if (height_map_texture) {
|
||||||
int index_y = (int) floor((z / height + 0.5f) * num_textures_y);
|
// 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--; }
|
if (index_y == num_textures_y) { index_y--; }
|
||||||
|
|
||||||
|
|
||||||
sg->addSprite(SGVec3f(x, y, z),
|
sg->addSprite(SGVec3f(x, y, z),
|
||||||
index_x,
|
index_x,
|
||||||
index_y,
|
index_y,
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ private:
|
|||||||
double max_sprite_height;
|
double max_sprite_height;
|
||||||
double bottom_shade;
|
double bottom_shade;
|
||||||
double zscale;
|
double zscale;
|
||||||
|
bool height_map_texture;
|
||||||
int num_sprites;
|
int num_sprites;
|
||||||
int num_textures_x;
|
int num_textures_x;
|
||||||
int num_textures_y;
|
int num_textures_y;
|
||||||
|
|||||||
Reference in New Issue
Block a user