From a1bb62f43c2f68a5ce4424f4d99f785b6d88bafe Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sat, 22 Oct 2011 21:28:09 +0100 Subject: [PATCH] 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. --- simgear/scene/sky/cloudfield.cxx | 126 ++++++++++++++++--------------- simgear/scene/sky/cloudfield.hxx | 96 +++++++++++------------ simgear/scene/sky/newcloud.cxx | 11 +-- 3 files changed, 119 insertions(+), 114 deletions(-) diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index 2b145b57..086899d7 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -68,8 +68,8 @@ float SGCloudField::fieldSize = 50000.0f; double SGCloudField::timer_dt = 0.0; float SGCloudField::view_distance = 20000.0f; bool SGCloudField::wrap = true; -float SGCloudField::RADIUS_LEVEL_1 = 20000.0f; -float SGCloudField::RADIUS_LEVEL_2 = 5000.0f; +float SGCloudField::RADIUS_LEVEL_1 = 5000.0f; +float SGCloudField::RADIUS_LEVEL_2 = 1000.0f; 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; SGVec3 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::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, sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt, 0.0f); - //cout << "Wind: " << direction << "@" << speed << "\n"; - osg::Vec3f windosg = field_transform->getAttitude() * wind; field_transform->setPosition(field_transform->getPosition() + windosg); @@ -108,39 +108,40 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, field_transform->setPosition(osg_pos); field_transform->setAttitude(orient); 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 - osg::Vec3f delta = field_transform->getAttitude().inverse() * (osg_pos - old_pos); - //cout << "Delta: " << delta.length() << "\n"; - - for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) { - osg::ref_ptr lodnode1 = (osg::LOD*) placed_root->getChild(i); - osg::Vec3f v = delta - lodnode1->getCenter(); - - if ((v.x() < -0.5*fieldSize) || - (v.x() > 0.5*fieldSize) || - (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); - if (v.x() > 0.5*fieldSize) { shift += osg::Vec3f(fieldSize, 0.0f, 0.0f); } - 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); } - if (v.y() < -0.5*fieldSize) { shift -= osg::Vec3f(0.0f, fieldSize, 0.0f); } - - //cout << "Shift: " << shift.x() << ", " << shift.y() << "\n\n"; - - for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) { - osg::ref_ptr lodnode2 = (osg::LOD*) lodnode1->getChild(j); - for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) { - osg::ref_ptr pat =(osg::PositionAttitudeTransform*) lodnode2->getChild(k); - pat->setPosition(pat->getPosition() + shift); - } - } + osg::Vec3f delta = ftainv * (osg_pos - old_pos); + old_pos = osg_pos; + + // Check if any of the clouds should be moved. + for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end(); + itr != end; + ++itr) { + + osg::ref_ptr pat = itr->second; + osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition(); + + // Determine the vector from the new position to the cloud in cloud-space. + osg::Vec3f w = ftainv * (currpos - toOsg(cart)); + + // Determine a course if required. Note that this involves some axis translation. + 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; } + + if ((x != 0.0) || (y != 0.0)) { + removeCloudFromTree(pat); + SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() + + fta * pat->getPosition())); + addCloudToTree(pat, p, x, y); } - } + } } // Render the clouds in order from farthest away layer to nearest one. @@ -231,28 +232,32 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t float lon, float lat, float alt, float x, float y) { // 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 transform, + SGGeod loc, float x, float y) { + + float alt = loc.getElevationFt(); // Determine any shift by x/y if ((x != 0.0f) || (y != 0.0f)) { - double crs; - - if (y == 0.0f) { - crs = (x < 0.0f) ? -90.0 : 90.0; - } else { - crs = SG_RADIANS_TO_DEGREES * tan(x/y); - } - + double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x); double dst = sqrt(x*x + y*y); 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); } - // The direct call provides the position at 0 alt, so adjust as required. - loc.setElevationFt(alt); + // The direct call provides the position at 0 alt, so adjust as required. + loc.setElevationFt(alt); + addCloudToTree(transform, loc); +} + +void SGCloudField::addCloudToTree(osg::ref_ptr transform, SGGeod loc) { // Work out where this cloud should go in OSG coordinates. SGVec3 cart; SGGeodesy::SGGeodToCart(loc, cart); @@ -266,16 +271,17 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t // Convert to the scenegraph orientation where we just rotate around // the y axis 180 degrees. 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); - old_pos = toOsg(fieldcenter); + old_pos = osg_pos; } - + + // Convert position to cloud-coordinates 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 // to. If there are no appropriate nodes in the quad tree, they are @@ -288,7 +294,6 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t lodnode1 = (osg::LOD*) placed_root->getChild(i); 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. - //cout << "Adding cloud to existing LoD level 1 node. Distance:" << (lodnode1->getCenter() - pos).length() << "\n"; found = true; } } @@ -296,7 +301,6 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t if (!found) { lodnode1 = new osg::LOD(); 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 @@ -306,17 +310,15 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t lodnode = (osg::LOD*) lodnode1->getChild(j); if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) { // We've found the right leaf LOD node - //cout << "Found existing LOD leaf node. Distance:"<< (lodnode->getCenter() - pos).length() << "\n"; found = true; - } } + } if (!found) { // No suitable leave node was found, so we need to add one. lodnode = new osg::LOD(); lodnode1->addChild(lodnode, 0.0f, 4*RADIUS_LEVEL_1); - //cout << "Adding cloud to new LoD node\n"; -} + } transform->setPosition(pos); lodnode->addChild(transform.get(), 0.0f, view_distance); diff --git a/simgear/scene/sky/cloudfield.hxx b/simgear/scene/sky/cloudfield.hxx index 77f504b6..1de2fce3 100644 --- a/simgear/scene/sky/cloudfield.hxx +++ b/simgear/scene/sky/cloudfield.hxx @@ -70,7 +70,7 @@ private: bool visible; }; - float Rnd(float); + float Rnd(float); // Radius of the LoD nodes for the dynamic quadtrees. 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 SGVec3f relative_position; - osg::ref_ptr field_root; + osg::ref_ptr field_root; osg::ref_ptr placed_root; osg::ref_ptr field_transform; - osg::ref_ptr altitude_transform; - osg::ref_ptr field_lod; + osg::ref_ptr altitude_transform; + osg::ref_ptr field_lod; osg::Vec3f old_pos; CloudHash cloud_hash; - struct CloudFog : public simgear::Singleton - { - CloudFog(); - osg::ref_ptr fog; - }; + struct CloudFog : public simgear::Singleton + { + CloudFog(); + osg::ref_ptr fog; + }; void removeCloudFromTree(osg::ref_ptr transform); void addCloudToTree(osg::ref_ptr transform, float lon, float lat, float alt, float x, float y); + void addCloudToTree(osg::ref_ptr transform, SGGeod loc, float x, float y); + void addCloudToTree(osg::ref_ptr transform, SGGeod loc); void applyVisRangeAndCoverage(void); public: @@ -122,44 +124,44 @@ public: /** - * reposition the cloud layer at the specified origin and - * orientation. - * @param p position vector - * @param up the local up vector - * @param lon specifies a rotation about the Z axis - * @param lat specifies a rotation about the new Y axis - * @param dt the time elapsed since the last call - * @param asl altitude of the layer - * @param speed of cloud layer movement (due to wind) - * @param direction of cloud layer movement (due to wind) - */ - bool reposition( const SGVec3f& p, const SGVec3f& up, - double lon, double lat, double dt, int asl, float speed, float direction); - - osg::Group* getNode() { return field_root.get(); } - - // visibility distance for clouds in meters - static float CloudVis; - - static SGVec3f view_vec, view_X, view_Y; - - static float view_distance; - static double timer_dt; - static float fieldSize; - static bool wrap; - - static bool getWrap(void) { return wrap; } - static void setWrap(bool w) { wrap = w; } - - static float getVisRange(void) { return view_distance; } - static void setVisRange(float d) { view_distance = d; } - void applyVisRange(void); - - static osg::Fog* getFog() - { - return CloudFog::instance()->fog.get(); - } - static void updateFog(double visibility, const osg::Vec4f& color); + * reposition the cloud layer at the specified origin and + * orientation. + * @param p position vector + * @param up the local up vector + * @param lon specifies a rotation about the Z axis + * @param lat specifies a rotation about the new Y axis + * @param dt the time elapsed since the last call + * @param asl altitude of the layer + * @param speed of cloud layer movement (due to wind) + * @param direction of cloud layer movement (due to wind) + */ + bool reposition( const SGVec3f& p, const SGVec3f& up, + double lon, double lat, double dt, int asl, float speed, float direction); + + osg::Group* getNode() { return field_root.get(); } + + // visibility distance for clouds in meters + static float CloudVis; + + static SGVec3f view_vec, view_X, view_Y; + + static float view_distance; + static double timer_dt; + static float fieldSize; + static bool wrap; + + static bool getWrap(void) { return wrap; } + static void setWrap(bool w) { wrap = w; } + + static float getVisRange(void) { return view_distance; } + static void setVisRange(float d) { view_distance = d; } + void applyVisRange(void); + + static osg::Fog* getFog() + { + return CloudFog::instance()->fog.get(); + } + static void updateFog(double visibility, const osg::Vec4f& color); }; #endif // _CLOUDFIELD_HXX diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index 0e9b7325..6cd0a93c 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -135,11 +135,12 @@ osg::Geometry* SGNewCloud::createOrthQuad(float w, float h, int varieties_x, int osg::Vec2Array& t = *(new osg::Vec2Array(4)); float cw = w*0.5f; - - v[0].set(0.0f, -cw, 0.0f); - v[1].set(0.0f, cw, 0.0f); - v[2].set(0.0f, cw, h); - v[3].set(0.0f, -cw, h); + float ch = h*0.5f; + + v[0].set(0.0f, -cw, -ch); + v[1].set(0.0f, cw, -ch); + v[2].set(0.0f, cw, ch); + v[3].set(0.0f, -cw, ch); // The texture coordinate range is not the // entire coordinate space - as the texture -- 2.39.5