Fixes to 3D clouds
- Wrap 3D clouds (e.g. Global Weather) on the surface of a sphere rather than on a plane. Cloud altitude no-longer increases with distance from the start position - Fix altitude of individual clouds, which where being places approx 1/2 the sprite height too high. Note that this means sprite positions in the cloud are the center of the cloud rather than the bottom. - Improve LOD radii, which were far too large.
This commit is contained in:
@@ -68,8 +68,8 @@ float SGCloudField::fieldSize = 50000.0f;
|
|||||||
double SGCloudField::timer_dt = 0.0;
|
double SGCloudField::timer_dt = 0.0;
|
||||||
float SGCloudField::view_distance = 20000.0f;
|
float SGCloudField::view_distance = 20000.0f;
|
||||||
bool SGCloudField::wrap = true;
|
bool SGCloudField::wrap = true;
|
||||||
float SGCloudField::RADIUS_LEVEL_1 = 20000.0f;
|
float SGCloudField::RADIUS_LEVEL_1 = 5000.0f;
|
||||||
float SGCloudField::RADIUS_LEVEL_2 = 5000.0f;
|
float SGCloudField::RADIUS_LEVEL_2 = 1000.0f;
|
||||||
|
|
||||||
SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
|
SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
|
||||||
|
|
||||||
@@ -81,7 +81,9 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
|
|||||||
if (placed_root->getNumChildren() == 0) return false;
|
if (placed_root->getNumChildren() == 0) return false;
|
||||||
|
|
||||||
SGVec3<double> cart;
|
SGVec3<double> cart;
|
||||||
SGGeodesy::SGGeodToCart(SGGeod::fromRadFt(lon, lat, 0.0f), cart);
|
SGGeod new_pos = SGGeod::fromRadFt(lon, lat, 0.0f);
|
||||||
|
|
||||||
|
SGGeodesy::SGGeodToCart(new_pos, cart);
|
||||||
osg::Vec3f osg_pos = toOsg(cart);
|
osg::Vec3f osg_pos = toOsg(cart);
|
||||||
osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
|
osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
|
||||||
|
|
||||||
@@ -93,8 +95,6 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
|
|||||||
osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
|
osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
|
||||||
sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
|
sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
|
||||||
0.0f);
|
0.0f);
|
||||||
//cout << "Wind: " << direction << "@" << speed << "\n";
|
|
||||||
|
|
||||||
osg::Vec3f windosg = field_transform->getAttitude() * wind;
|
osg::Vec3f windosg = field_transform->getAttitude() * wind;
|
||||||
field_transform->setPosition(field_transform->getPosition() + windosg);
|
field_transform->setPosition(field_transform->getPosition() + windosg);
|
||||||
|
|
||||||
@@ -109,36 +109,37 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
|
|||||||
field_transform->setAttitude(orient);
|
field_transform->setAttitude(orient);
|
||||||
old_pos = osg_pos;
|
old_pos = osg_pos;
|
||||||
} else {
|
} else {
|
||||||
|
osg::Quat fta = field_transform->getAttitude();
|
||||||
|
osg::Quat ftainv = field_transform->getAttitude().inverse();
|
||||||
|
|
||||||
// delta is the vector from the old position to the new position in cloud-coords
|
// delta is the vector from the old position to the new position in cloud-coords
|
||||||
osg::Vec3f delta = field_transform->getAttitude().inverse() * (osg_pos - old_pos);
|
osg::Vec3f delta = ftainv * (osg_pos - old_pos);
|
||||||
//cout << "Delta: " << delta.length() << "\n";
|
old_pos = osg_pos;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) {
|
// Check if any of the clouds should be moved.
|
||||||
osg::ref_ptr<osg::LOD> lodnode1 = (osg::LOD*) placed_root->getChild(i);
|
for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
|
||||||
osg::Vec3f v = delta - lodnode1->getCenter();
|
itr != end;
|
||||||
|
++itr) {
|
||||||
|
|
||||||
if ((v.x() < -0.5*fieldSize) ||
|
osg::ref_ptr<osg::PositionAttitudeTransform> pat = itr->second;
|
||||||
(v.x() > 0.5*fieldSize) ||
|
osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition();
|
||||||
(v.y() < -0.5*fieldSize) ||
|
|
||||||
(v.y() > 0.5*fieldSize) ) {
|
|
||||||
//cout << "V: " << v.x() << ", " << v.y() << "\n";
|
|
||||||
|
|
||||||
osg::Vec3f shift = osg::Vec3f(0.0f, 0.0f, 0.0f);
|
// Determine the vector from the new position to the cloud in cloud-space.
|
||||||
if (v.x() > 0.5*fieldSize) { shift += osg::Vec3f(fieldSize, 0.0f, 0.0f); }
|
osg::Vec3f w = ftainv * (currpos - toOsg(cart));
|
||||||
if (v.x() < -0.5*fieldSize) { shift -= osg::Vec3f(fieldSize, 0.0f, 0.0f); }
|
|
||||||
|
|
||||||
if (v.y() > 0.5*fieldSize) { shift += osg::Vec3f(0.0f, fieldSize, 0.0f); }
|
// Determine a course if required. Note that this involves some axis translation.
|
||||||
if (v.y() < -0.5*fieldSize) { shift -= osg::Vec3f(0.0f, fieldSize, 0.0f); }
|
float x = 0.0;
|
||||||
|
float y = 0.0;
|
||||||
|
if (w.x() > 0.6*fieldSize) { y = fieldSize; }
|
||||||
|
if (w.x() < -0.6*fieldSize) { y = -fieldSize; }
|
||||||
|
if (w.y() > 0.6*fieldSize) { x = -fieldSize; }
|
||||||
|
if (w.y() < -0.6*fieldSize) { x = fieldSize; }
|
||||||
|
|
||||||
//cout << "Shift: " << shift.x() << ", " << shift.y() << "\n\n";
|
if ((x != 0.0) || (y != 0.0)) {
|
||||||
|
removeCloudFromTree(pat);
|
||||||
for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) {
|
SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() +
|
||||||
osg::ref_ptr<osg::LOD> lodnode2 = (osg::LOD*) lodnode1->getChild(j);
|
fta * pat->getPosition()));
|
||||||
for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) {
|
addCloudToTree(pat, p, x, y);
|
||||||
osg::ref_ptr<osg::PositionAttitudeTransform> pat =(osg::PositionAttitudeTransform*) lodnode2->getChild(k);
|
|
||||||
pat->setPosition(pat->getPosition() + shift);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,28 +232,32 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
|||||||
float lon, float lat, float alt, float x, float y) {
|
float lon, float lat, float alt, float x, float y) {
|
||||||
|
|
||||||
// Get the base position
|
// Get the base position
|
||||||
SGGeod loc = SGGeod::fromDegFt(lon, lat, 0.0);
|
SGGeod loc = SGGeod::fromDegFt(lon, lat, alt);
|
||||||
|
addCloudToTree(transform, loc, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
|
||||||
|
SGGeod loc, float x, float y) {
|
||||||
|
|
||||||
|
float alt = loc.getElevationFt();
|
||||||
// Determine any shift by x/y
|
// Determine any shift by x/y
|
||||||
if ((x != 0.0f) || (y != 0.0f)) {
|
if ((x != 0.0f) || (y != 0.0f)) {
|
||||||
double crs;
|
double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x);
|
||||||
|
|
||||||
if (y == 0.0f) {
|
|
||||||
crs = (x < 0.0f) ? -90.0 : 90.0;
|
|
||||||
} else {
|
|
||||||
crs = SG_RADIANS_TO_DEGREES * tan(x/y);
|
|
||||||
}
|
|
||||||
|
|
||||||
double dst = sqrt(x*x + y*y);
|
double dst = sqrt(x*x + y*y);
|
||||||
double endcrs;
|
double endcrs;
|
||||||
|
|
||||||
SGGeod base_pos = SGGeod::fromDegFt(lon, lat, 0.0f);
|
SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f);
|
||||||
SGGeodesy::direct(base_pos, crs, dst, loc, endcrs);
|
SGGeodesy::direct(base_pos, crs, dst, loc, endcrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The direct call provides the position at 0 alt, so adjust as required.
|
// The direct call provides the position at 0 alt, so adjust as required.
|
||||||
loc.setElevationFt(alt);
|
loc.setElevationFt(alt);
|
||||||
|
addCloudToTree(transform, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc) {
|
||||||
// Work out where this cloud should go in OSG coordinates.
|
// Work out where this cloud should go in OSG coordinates.
|
||||||
SGVec3<double> cart;
|
SGVec3<double> cart;
|
||||||
SGGeodesy::SGGeodToCart(loc, cart);
|
SGGeodesy::SGGeodToCart(loc, cart);
|
||||||
@@ -267,14 +272,15 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
|||||||
// the y axis 180 degrees.
|
// the y axis 180 degrees.
|
||||||
osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
|
osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
|
||||||
|
|
||||||
field_transform->setPosition(toOsg(fieldcenter));
|
osg::Vec3f osg_pos = toOsg(fieldcenter);
|
||||||
|
|
||||||
|
field_transform->setPosition(osg_pos);
|
||||||
field_transform->setAttitude(orient);
|
field_transform->setAttitude(orient);
|
||||||
old_pos = toOsg(fieldcenter);
|
old_pos = osg_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert position to cloud-coordinates
|
||||||
pos = pos - field_transform->getPosition();
|
pos = pos - field_transform->getPosition();
|
||||||
|
|
||||||
//pos = orient.inverse() * pos;
|
|
||||||
pos = field_transform->getAttitude().inverse() * pos;
|
pos = field_transform->getAttitude().inverse() * pos;
|
||||||
|
|
||||||
// We have a two level dynamic quad tree which the cloud will be added
|
// We have a two level dynamic quad tree which the cloud will be added
|
||||||
@@ -288,7 +294,6 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
|||||||
lodnode1 = (osg::LOD*) placed_root->getChild(i);
|
lodnode1 = (osg::LOD*) placed_root->getChild(i);
|
||||||
if ((lodnode1->getCenter() - pos).length2() < RADIUS_LEVEL_1*RADIUS_LEVEL_1) {
|
if ((lodnode1->getCenter() - pos).length2() < RADIUS_LEVEL_1*RADIUS_LEVEL_1) {
|
||||||
// New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
|
// New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
|
||||||
//cout << "Adding cloud to existing LoD level 1 node. Distance:" << (lodnode1->getCenter() - pos).length() << "\n";
|
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,7 +301,6 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
|||||||
if (!found) {
|
if (!found) {
|
||||||
lodnode1 = new osg::LOD();
|
lodnode1 = new osg::LOD();
|
||||||
placed_root->addChild(lodnode1.get());
|
placed_root->addChild(lodnode1.get());
|
||||||
//cout << "Adding cloud to new LoD node\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now check if there is a second level LOD node at an appropriate distance
|
// Now check if there is a second level LOD node at an appropriate distance
|
||||||
@@ -306,17 +310,15 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
|
|||||||
lodnode = (osg::LOD*) lodnode1->getChild(j);
|
lodnode = (osg::LOD*) lodnode1->getChild(j);
|
||||||
if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) {
|
if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) {
|
||||||
// We've found the right leaf LOD node
|
// We've found the right leaf LOD node
|
||||||
//cout << "Found existing LOD leaf node. Distance:"<< (lodnode->getCenter() - pos).length() << "\n";
|
|
||||||
found = true;
|
found = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// No suitable leave node was found, so we need to add one.
|
// No suitable leave node was found, so we need to add one.
|
||||||
lodnode = new osg::LOD();
|
lodnode = new osg::LOD();
|
||||||
lodnode1->addChild(lodnode, 0.0f, 4*RADIUS_LEVEL_1);
|
lodnode1->addChild(lodnode, 0.0f, 4*RADIUS_LEVEL_1);
|
||||||
//cout << "Adding cloud to new LoD node\n";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
transform->setPosition(pos);
|
transform->setPosition(pos);
|
||||||
lodnode->addChild(transform.get(), 0.0f, view_distance);
|
lodnode->addChild(transform.get(), 0.0f, view_distance);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ private:
|
|||||||
bool visible;
|
bool visible;
|
||||||
};
|
};
|
||||||
|
|
||||||
float Rnd(float);
|
float Rnd(float);
|
||||||
|
|
||||||
// Radius of the LoD nodes for the dynamic quadtrees.
|
// Radius of the LoD nodes for the dynamic quadtrees.
|
||||||
static float RADIUS_LEVEL_1;
|
static float RADIUS_LEVEL_1;
|
||||||
@@ -79,23 +79,25 @@ private:
|
|||||||
// this is a relative position only, with that we can move all clouds at once
|
// this is a relative position only, with that we can move all clouds at once
|
||||||
SGVec3f relative_position;
|
SGVec3f relative_position;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> field_root;
|
osg::ref_ptr<osg::Group> field_root;
|
||||||
osg::ref_ptr<osg::Group> placed_root;
|
osg::ref_ptr<osg::Group> placed_root;
|
||||||
osg::ref_ptr<osg::PositionAttitudeTransform> field_transform;
|
osg::ref_ptr<osg::PositionAttitudeTransform> field_transform;
|
||||||
osg::ref_ptr<osg::PositionAttitudeTransform> altitude_transform;
|
osg::ref_ptr<osg::PositionAttitudeTransform> altitude_transform;
|
||||||
osg::ref_ptr<osg::LOD> field_lod;
|
osg::ref_ptr<osg::LOD> field_lod;
|
||||||
|
|
||||||
osg::Vec3f old_pos;
|
osg::Vec3f old_pos;
|
||||||
CloudHash cloud_hash;
|
CloudHash cloud_hash;
|
||||||
|
|
||||||
struct CloudFog : public simgear::Singleton<CloudFog>
|
struct CloudFog : public simgear::Singleton<CloudFog>
|
||||||
{
|
{
|
||||||
CloudFog();
|
CloudFog();
|
||||||
osg::ref_ptr<osg::Fog> fog;
|
osg::ref_ptr<osg::Fog> fog;
|
||||||
};
|
};
|
||||||
|
|
||||||
void removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform);
|
void removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform);
|
||||||
void addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, float lon, float lat, float alt, float x, float y);
|
void addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, float lon, float lat, float alt, float x, float y);
|
||||||
|
void addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc, float x, float y);
|
||||||
|
void addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc);
|
||||||
void applyVisRangeAndCoverage(void);
|
void applyVisRangeAndCoverage(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -122,44 +124,44 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reposition the cloud layer at the specified origin and
|
* reposition the cloud layer at the specified origin and
|
||||||
* orientation.
|
* orientation.
|
||||||
* @param p position vector
|
* @param p position vector
|
||||||
* @param up the local up vector
|
* @param up the local up vector
|
||||||
* @param lon specifies a rotation about the Z axis
|
* @param lon specifies a rotation about the Z axis
|
||||||
* @param lat specifies a rotation about the new Y axis
|
* @param lat specifies a rotation about the new Y axis
|
||||||
* @param dt the time elapsed since the last call
|
* @param dt the time elapsed since the last call
|
||||||
* @param asl altitude of the layer
|
* @param asl altitude of the layer
|
||||||
* @param speed of cloud layer movement (due to wind)
|
* @param speed of cloud layer movement (due to wind)
|
||||||
* @param direction of cloud layer movement (due to wind)
|
* @param direction of cloud layer movement (due to wind)
|
||||||
*/
|
*/
|
||||||
bool reposition( const SGVec3f& p, const SGVec3f& up,
|
bool reposition( const SGVec3f& p, const SGVec3f& up,
|
||||||
double lon, double lat, double dt, int asl, float speed, float direction);
|
double lon, double lat, double dt, int asl, float speed, float direction);
|
||||||
|
|
||||||
osg::Group* getNode() { return field_root.get(); }
|
osg::Group* getNode() { return field_root.get(); }
|
||||||
|
|
||||||
// visibility distance for clouds in meters
|
// visibility distance for clouds in meters
|
||||||
static float CloudVis;
|
static float CloudVis;
|
||||||
|
|
||||||
static SGVec3f view_vec, view_X, view_Y;
|
static SGVec3f view_vec, view_X, view_Y;
|
||||||
|
|
||||||
static float view_distance;
|
static float view_distance;
|
||||||
static double timer_dt;
|
static double timer_dt;
|
||||||
static float fieldSize;
|
static float fieldSize;
|
||||||
static bool wrap;
|
static bool wrap;
|
||||||
|
|
||||||
static bool getWrap(void) { return wrap; }
|
static bool getWrap(void) { return wrap; }
|
||||||
static void setWrap(bool w) { wrap = w; }
|
static void setWrap(bool w) { wrap = w; }
|
||||||
|
|
||||||
static float getVisRange(void) { return view_distance; }
|
static float getVisRange(void) { return view_distance; }
|
||||||
static void setVisRange(float d) { view_distance = d; }
|
static void setVisRange(float d) { view_distance = d; }
|
||||||
void applyVisRange(void);
|
void applyVisRange(void);
|
||||||
|
|
||||||
static osg::Fog* getFog()
|
static osg::Fog* getFog()
|
||||||
{
|
{
|
||||||
return CloudFog::instance()->fog.get();
|
return CloudFog::instance()->fog.get();
|
||||||
}
|
}
|
||||||
static void updateFog(double visibility, const osg::Vec4f& color);
|
static void updateFog(double visibility, const osg::Vec4f& color);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _CLOUDFIELD_HXX
|
#endif // _CLOUDFIELD_HXX
|
||||||
|
|||||||
@@ -135,11 +135,12 @@ osg::Geometry* SGNewCloud::createOrthQuad(float w, float h, int varieties_x, int
|
|||||||
osg::Vec2Array& t = *(new osg::Vec2Array(4));
|
osg::Vec2Array& t = *(new osg::Vec2Array(4));
|
||||||
|
|
||||||
float cw = w*0.5f;
|
float cw = w*0.5f;
|
||||||
|
float ch = h*0.5f;
|
||||||
|
|
||||||
v[0].set(0.0f, -cw, 0.0f);
|
v[0].set(0.0f, -cw, -ch);
|
||||||
v[1].set(0.0f, cw, 0.0f);
|
v[1].set(0.0f, cw, -ch);
|
||||||
v[2].set(0.0f, cw, h);
|
v[2].set(0.0f, cw, ch);
|
||||||
v[3].set(0.0f, -cw, h);
|
v[3].set(0.0f, -cw, ch);
|
||||||
|
|
||||||
// The texture coordinate range is not the
|
// The texture coordinate range is not the
|
||||||
// entire coordinate space - as the texture
|
// entire coordinate space - as the texture
|
||||||
|
|||||||
Reference in New Issue
Block a user