]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/cloudfield.cxx
Adjust LoD ranges for 3D clouds so that they become visible at maximum range, taking...
[simgear.git] / simgear / scene / sky / cloudfield.cxx
1 // a layer of 3d clouds
2 //
3 // Written by Harald JOHNSEN, started April 2005.
4 //
5 // Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 //
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear_config.h>
25 #endif
26
27 #include <osg/Fog>
28 #include <osg/Texture2D>
29 #include <osg/PositionAttitudeTransform>
30 #include <osg/Vec4f>
31
32 #include <simgear/compiler.h>
33
34 #include <simgear/math/sg_random.h>
35 #include <simgear/math/sg_geodesy.hxx>
36 #include <simgear/scene/util/SGSceneUserData.hxx>
37
38 #include <algorithm>
39 #include <vector>
40 #include <iostream>
41
42 using namespace std;
43
44 using std::vector;
45
46 //#include <simgear/environment/visual_enviro.hxx>
47 #include <simgear/scene/util/RenderConstants.hxx>
48 #include <simgear/scene/util/SGUpdateVisitor.hxx>
49 #include "sky.hxx"
50 #include "newcloud.hxx"
51 #include "cloudfield.hxx"
52
53 #if defined(__MINGW32__)
54 #define isnan(x) _isnan(x)
55 #endif
56
57 #if defined (__FreeBSD__)
58 #  if __FreeBSD_version < 500000
59      extern "C" {
60        inline int isnan(double r) { return !(r <= 0 || r >= 0); }
61      }
62 #  endif
63 #endif
64
65 using namespace simgear;
66
67 float SGCloudField::fieldSize = 50000.0f;
68 double SGCloudField::timer_dt = 0.0;
69 float SGCloudField::view_distance = 20000.0f;
70 bool SGCloudField::wrap = true;
71 float SGCloudField::RADIUS_LEVEL_1 = 5000.0f;
72 float SGCloudField::RADIUS_LEVEL_2 = 2000.0f;
73 float SGCloudField::MAX_CLOUD_DEPTH = 2000.0f;
74
75 SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
76
77
78 // Reposition the cloud layer at the specified origin and orientation
79 bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
80                                double dt, int asl, float speed, float direction ) {
81     // Determine any movement of the placed clouds
82     if (placed_root->getNumChildren() == 0) return false;
83
84     SGVec3<double> cart;
85     SGGeod new_pos = SGGeod::fromRadFt(lon, lat, 0.0f);
86                             
87     SGGeodesy::SGGeodToCart(new_pos, cart);
88     osg::Vec3f osg_pos = toOsg(cart);
89     osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
90     
91     // Always update the altitude transform, as this allows
92     // the clouds to rise and fall smoothly depending on environment updates.
93     altitude_transform->setPosition(osg::Vec3f(0.0f, 0.0f, (float) asl));
94     
95     // Similarly, always determine the effects of the wind
96     osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
97                                  sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
98                                  0.0f);
99     osg::Vec3f windosg = field_transform->getAttitude() * wind;
100     field_transform->setPosition(field_transform->getPosition() + windosg);
101     
102     if (!wrap) {
103         // If we're not wrapping the cloudfield, then we make no effort to reposition
104         return false;
105     }
106         
107     if ((old_pos - osg_pos).length() > fieldSize*2) {
108         // Big movement - reposition centered to current location.
109         field_transform->setPosition(osg_pos);
110         field_transform->setAttitude(orient);
111         old_pos = osg_pos;
112     } else {        
113         osg::Quat fta =  field_transform->getAttitude();
114         osg::Quat ftainv = field_transform->getAttitude().inverse();
115         
116         // delta is the vector from the old position to the new position in cloud-coords
117         // osg::Vec3f delta = ftainv * (osg_pos - old_pos);
118         old_pos = osg_pos;
119                 
120         // Check if any of the clouds should be moved.
121         for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
122             itr != end;
123             ++itr) {
124               
125              osg::ref_ptr<osg::PositionAttitudeTransform> pat = itr->second;
126              osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition();
127                                       
128              // Determine the vector from the new position to the cloud in cloud-space.
129              osg::Vec3f w =  ftainv * (currpos - toOsg(cart));               
130               
131              // Determine a course if required. Note that this involves some axis translation.
132              float x = 0.0;
133              float y = 0.0;
134              if (w.x() >  0.6*fieldSize) { y =  fieldSize; }
135              if (w.x() < -0.6*fieldSize) { y = -fieldSize; }
136              if (w.y() >  0.6*fieldSize) { x = -fieldSize; }
137              if (w.y() < -0.6*fieldSize) { x =  fieldSize; }
138               
139              if ((x != 0.0) || (y != 0.0)) {
140                  removeCloudFromTree(pat);
141                  SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() + 
142                                                   fta * pat->getPosition()));
143                  addCloudToTree(pat, p, x, y);                
144             }
145         }        
146     }
147     
148     // Render the clouds in order from farthest away layer to nearest one.
149     field_root->getStateSet()->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
150     return true;
151 }
152
153 SGCloudField::SGCloudField() :
154         field_root(new osg::Group),
155         field_transform(new osg::PositionAttitudeTransform),
156         altitude_transform(new osg::PositionAttitudeTransform)
157 {
158     old_pos = osg::Vec3f(0.0f, 0.0f, 0.0f);
159     field_root->addChild(field_transform.get());
160     field_root->setName("3D Cloud field root");
161     osg::StateSet *rootSet = field_root->getOrCreateStateSet();
162     rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
163     rootSet->setAttributeAndModes(getFog());
164     
165     field_transform->addChild(altitude_transform.get());
166     placed_root = new osg::Group();
167     altitude_transform->addChild(placed_root);
168 }
169     
170 SGCloudField::~SGCloudField() {
171         }
172
173
174 void SGCloudField::clear(void) {
175
176     for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
177         itr != end;
178         ++itr) {
179         removeCloudFromTree(itr->second);
180     }
181     
182     cloud_hash.clear();
183 }
184
185 void SGCloudField::applyVisRange(void)
186 {
187     for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) {
188         osg::ref_ptr<osg::LOD> lodnode1 = (osg::LOD*) placed_root->getChild(i);
189         for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) {
190             lodnode1->setRange(j, 0.0f, view_distance + RADIUS_LEVEL_1 + RADIUS_LEVEL_2 + MAX_CLOUD_DEPTH);
191             osg::ref_ptr<osg::LOD> lodnode2 = (osg::LOD*) lodnode1->getChild(j);
192             for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) {
193                 lodnode2->setRange(k, 0.0f, view_distance + MAX_CLOUD_DEPTH);
194             }
195         }
196     }
197 }
198             
199 bool SGCloudField::addCloud(float lon, float lat, float alt, int index, osg::ref_ptr<EffectGeode> geode) {
200   return addCloud(lon, lat, alt, 0.0f, 0.0f, index, geode);
201         }
202
203 bool SGCloudField::addCloud(float lon, float lat, float alt, float x, float y, int index, osg::ref_ptr<EffectGeode> geode) {
204     // If this cloud index already exists, don't replace it.
205     if (cloud_hash[index]) return false;
206
207     osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
208
209     transform->addChild(geode.get());
210     addCloudToTree(transform, lon, lat, alt, x, y);
211     cloud_hash[index] = transform;
212     return true;
213     }
214     
215 // Remove a give cloud from inside the tree, without removing it from the cloud hash
216 void SGCloudField::removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform)
217 {
218     osg::ref_ptr<osg::Group> lodnode = transform->getParent(0);
219     lodnode->removeChild(transform);
220
221     // Clean up the LOD nodes if required
222     if (lodnode->getNumChildren() == 0) {
223         osg::ref_ptr<osg::Group> lodnode1 = lodnode->getParent(0);
224             
225         lodnode1->removeChild(lodnode);
226             
227         if (lodnode1->getNumChildren() == 0) {
228             placed_root->removeChild(lodnode1);
229         }
230     }
231 }
232
233 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
234                                   float lon, float lat, float alt, float x, float y) {
235     
236     // Get the base position
237     SGGeod loc = SGGeod::fromDegFt(lon, lat, alt);
238     addCloudToTree(transform, loc, x, y);      
239 }
240
241
242 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
243                                   SGGeod loc, float x, float y) {
244         
245     float alt = loc.getElevationFt();
246     // Determine any shift by x/y
247     if ((x != 0.0f) || (y != 0.0f)) {
248         double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x); 
249         double dst = sqrt(x*x + y*y);
250         double endcrs;
251         
252         SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f);            
253         SGGeodesy::direct(base_pos, crs, dst, loc, endcrs);
254     }
255     
256     // The direct call provides the position at 0 alt, so adjust as required.      
257     loc.setElevationFt(alt);    
258     addCloudToTree(transform, loc);    
259 }
260         
261         
262 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc) {        
263     // Work out where this cloud should go in OSG coordinates.
264     SGVec3<double> cart;
265     SGGeodesy::SGGeodToCart(loc, cart);
266     osg::Vec3f pos = toOsg(cart);
267
268
269     if (old_pos == osg::Vec3f(0.0f, 0.0f, 0.0f)) {
270         // First setup.
271         SGVec3<double> fieldcenter;
272         SGGeodesy::SGGeodToCart(SGGeod::fromDegFt(loc.getLongitudeDeg(), loc.getLatitudeDeg(), 0.0f), fieldcenter);
273         // Convert to the scenegraph orientation where we just rotate around
274         // the y axis 180 degrees.
275         osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
276         
277         osg::Vec3f osg_pos = toOsg(fieldcenter);            
278         
279         field_transform->setPosition(osg_pos);
280         field_transform->setAttitude(orient);
281         old_pos = osg_pos;
282     }
283     
284     // Convert position to cloud-coordinates
285     pos = pos - field_transform->getPosition();
286     pos = field_transform->getAttitude().inverse() * pos;    
287
288     // We have a two level dynamic quad tree which the cloud will be added
289     // to. If there are no appropriate nodes in the quad tree, they are
290     // created as required.
291     bool found = false;
292     osg::ref_ptr<osg::LOD> lodnode1;
293     osg::ref_ptr<osg::LOD> lodnode;
294
295     for (unsigned int i = 0; (!found) && (i < placed_root->getNumChildren()); i++) {
296         lodnode1 = (osg::LOD*) placed_root->getChild(i);
297         if ((lodnode1->getCenter() - pos).length2() < RADIUS_LEVEL_1*RADIUS_LEVEL_1) {
298             // New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
299             found = true;
300         }
301     }
302
303     if (!found) {
304         lodnode1 = new osg::LOD();
305         placed_root->addChild(lodnode1.get());
306     }
307
308     // Now check if there is a second level LOD node at an appropriate distance
309     found = false;
310
311     for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {
312         lodnode = (osg::LOD*) lodnode1->getChild(j);
313         if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) {
314             // We've found the right leaf LOD node
315             found = true;
316         }
317     }
318
319     if (!found) {
320         // No suitable leave node was found, so we need to add one.
321         lodnode = new osg::LOD();
322         lodnode1->addChild(lodnode, 0.0f, view_distance + RADIUS_LEVEL_1 + RADIUS_LEVEL_2 + MAX_CLOUD_DEPTH);
323     }
324
325     transform->setPosition(pos);
326     lodnode->addChild(transform.get(), 0.0f, view_distance + MAX_CLOUD_DEPTH);
327
328     lodnode->dirtyBound();
329     lodnode1->dirtyBound();
330     field_root->dirtyBound();
331 }
332         
333 bool SGCloudField::deleteCloud(int identifier) {
334     osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
335     if (transform == NULL) return false;
336         
337     removeCloudFromTree(transform);
338     cloud_hash.erase(identifier);
339
340     return true;
341 }
342         
343 bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt) {
344     return repositionCloud(identifier, lon, lat, alt, 0.0f, 0.0f);
345 }
346
347 bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt, float x, float y) {
348     osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
349     
350     if (transform == NULL) return false;
351
352     removeCloudFromTree(transform);
353     addCloudToTree(transform, lon, lat, alt, x, y);
354     return true;
355     }
356
357 bool SGCloudField::isDefined3D(void) {
358     return (cloud_hash.size() > 0);
359 }
360
361 SGCloudField::CloudFog::CloudFog() {
362     fog = new osg::Fog;
363     fog->setMode(osg::Fog::EXP2);
364     fog->setDataVariance(osg::Object::DYNAMIC);
365 }
366
367 void SGCloudField::updateFog(double visibility, const osg::Vec4f& color) {
368     const double sqrt_m_log01 = sqrt(-log(0.01));
369     osg::Fog* fog = CloudFog::instance()->fog.get();
370     fog->setColor(color);
371     fog->setDensity(sqrt_m_log01 / visibility);
372 }
373