]> git.mxchange.org Git - simgear.git/commitdiff
Fixes to 3D clouds
authorStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Sat, 22 Oct 2011 20:28:09 +0000 (21:28 +0100)
committerStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Sat, 22 Oct 2011 20:28:09 +0000 (21:28 +0100)
 - 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
simgear/scene/sky/cloudfield.hxx
simgear/scene/sky/newcloud.cxx

index 2b145b577abb9ef723201f74878ec2dde1d4af46..086899d747a9675597d64f64d3ebbcd727ec8f77 100644 (file)
@@ -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<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::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<osg::LOD> 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<osg::LOD> lodnode2 = (osg::LOD*) lodnode1->getChild(j);
-                    for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) {
-                        osg::ref_ptr<osg::PositionAttitudeTransform> 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<osg::PositionAttitudeTransform> 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<osg::PositionAttitudeTransform> 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<osg::PositionAttitudeTransform> 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<osg::PositionAttitudeTransform> transform, SGGeod loc) {        
     // Work out where this cloud should go in OSG coordinates.
     SGVec3<double> cart;
     SGGeodesy::SGGeodToCart(loc, cart);
@@ -266,16 +271,17 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> 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<osg::PositionAttitudeTransform> 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<osg::PositionAttitudeTransform> 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<osg::PositionAttitudeTransform> 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);
index 77f504b6503723aec1e9966761eacf97af7dda49..1de2fce35920760345e0c8a36bdb51063c91c648 100644 (file)
@@ -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<osg::Group> field_root;
+  osg::ref_ptr<osg::Group> field_root;
   osg::ref_ptr<osg::Group> placed_root;
   osg::ref_ptr<osg::PositionAttitudeTransform> field_transform;
-        osg::ref_ptr<osg::PositionAttitudeTransform> altitude_transform;
-        osg::ref_ptr<osg::LOD> field_lod;
+  osg::ref_ptr<osg::PositionAttitudeTransform> altitude_transform;
+  osg::ref_ptr<osg::LOD> field_lod;
 
   osg::Vec3f old_pos;
   CloudHash cloud_hash;
 
-        struct CloudFog : public simgear::Singleton<CloudFog>
-        {
-                CloudFog();
-                osg::ref_ptr<osg::Fog> fog;
-        };
+  struct CloudFog : public simgear::Singleton<CloudFog>
+  {
+    CloudFog();
+    osg::ref_ptr<osg::Fog> fog;
+  };
 
   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, SGGeod loc, float x, float y);
+  void addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> 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
index 0e9b7325de8fa16cb7da5f3b36adba70c8a200e4..6cd0a93cdeb7945519d20a153745064e7734102c 100644 (file)
@@ -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