]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/cloudfield.cxx
Replace OpenThreads with SGThread to avoid useless OSG dependency.
[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 = 1000.0f;
73
74 SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
75
76
77 // Reposition the cloud layer at the specified origin and orientation
78 bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
79                                double dt, int asl, float speed, float direction ) {
80     // Determine any movement of the placed clouds
81     if (placed_root->getNumChildren() == 0) return false;
82
83     SGVec3<double> cart;
84     SGGeod new_pos = SGGeod::fromRadFt(lon, lat, 0.0f);
85                             
86     SGGeodesy::SGGeodToCart(new_pos, cart);
87     osg::Vec3f osg_pos = toOsg(cart);
88     osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
89     
90     // Always update the altitude transform, as this allows
91     // the clouds to rise and fall smoothly depending on environment updates.
92     altitude_transform->setPosition(osg::Vec3f(0.0f, 0.0f, (float) asl));
93     
94     // Similarly, always determine the effects of the wind
95     osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
96                                  sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
97                                  0.0f);
98     osg::Vec3f windosg = field_transform->getAttitude() * wind;
99     field_transform->setPosition(field_transform->getPosition() + windosg);
100     
101     if (!wrap) {
102         // If we're not wrapping the cloudfield, then we make no effort to reposition
103         return false;
104     }
105         
106     if ((old_pos - osg_pos).length() > fieldSize*2) {
107         // Big movement - reposition centered to current location.
108         field_transform->setPosition(osg_pos);
109         field_transform->setAttitude(orient);
110         old_pos = osg_pos;
111     } else {        
112         osg::Quat fta =  field_transform->getAttitude();
113         osg::Quat ftainv = field_transform->getAttitude().inverse();
114         
115         // delta is the vector from the old position to the new position in cloud-coords
116         osg::Vec3f delta = ftainv * (osg_pos - old_pos);
117         old_pos = osg_pos;
118                 
119         // Check if any of the clouds should be moved.
120         for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
121             itr != end;
122             ++itr) {
123               
124              osg::ref_ptr<osg::PositionAttitudeTransform> pat = itr->second;
125              osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition();
126                                       
127              // Determine the vector from the new position to the cloud in cloud-space.
128              osg::Vec3f w =  ftainv * (currpos - toOsg(cart));               
129               
130              // Determine a course if required. Note that this involves some axis translation.
131              float x = 0.0;
132              float y = 0.0;
133              if (w.x() >  0.6*fieldSize) { y =  fieldSize; }
134              if (w.x() < -0.6*fieldSize) { y = -fieldSize; }
135              if (w.y() >  0.6*fieldSize) { x = -fieldSize; }
136              if (w.y() < -0.6*fieldSize) { x =  fieldSize; }
137               
138              if ((x != 0.0) || (y != 0.0)) {
139                  removeCloudFromTree(pat);
140                  SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() + 
141                                                   fta * pat->getPosition()));
142                  addCloudToTree(pat, p, x, y);                
143             }
144         }        
145     }
146     
147     // Render the clouds in order from farthest away layer to nearest one.
148     field_root->getStateSet()->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
149     return true;
150 }
151
152 SGCloudField::SGCloudField() :
153         field_root(new osg::Group),
154         field_transform(new osg::PositionAttitudeTransform),
155         altitude_transform(new osg::PositionAttitudeTransform)
156 {
157     old_pos = osg::Vec3f(0.0f, 0.0f, 0.0f);
158     field_root->addChild(field_transform.get());
159     field_root->setName("3D Cloud field root");
160     osg::StateSet *rootSet = field_root->getOrCreateStateSet();
161     rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
162     rootSet->setAttributeAndModes(getFog());
163     
164     field_transform->addChild(altitude_transform.get());
165     placed_root = new osg::Group();
166     altitude_transform->addChild(placed_root);
167 }
168     
169 SGCloudField::~SGCloudField() {
170         }
171
172
173 void SGCloudField::clear(void) {
174
175     for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
176         itr != end;
177         ++itr) {
178         removeCloudFromTree(itr->second);
179     }
180     
181     cloud_hash.clear();
182 }
183
184 void SGCloudField::applyVisRange(void)
185 {
186     for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) {
187         osg::ref_ptr<osg::LOD> lodnode1 = (osg::LOD*) placed_root->getChild(i);
188         for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) {
189             osg::ref_ptr<osg::LOD> lodnode2 = (osg::LOD*) lodnode1->getChild(j);
190             for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) {
191                 lodnode2->setRange(k, 0.0f, view_distance);
192             }
193         }
194     }
195 }
196             
197 bool SGCloudField::addCloud(float lon, float lat, float alt, int index, osg::ref_ptr<EffectGeode> geode) {
198   return addCloud(lon, lat, alt, 0.0f, 0.0f, index, geode);
199         }
200
201 bool SGCloudField::addCloud(float lon, float lat, float alt, float x, float y, int index, osg::ref_ptr<EffectGeode> geode) {
202     // If this cloud index already exists, don't replace it.
203     if (cloud_hash[index]) return false;
204
205     osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
206
207     transform->addChild(geode.get());
208     addCloudToTree(transform, lon, lat, alt, x, y);
209     cloud_hash[index] = transform;
210     return true;
211     }
212     
213 // Remove a give cloud from inside the tree, without removing it from the cloud hash
214 void SGCloudField::removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform)
215 {
216     osg::ref_ptr<osg::Group> lodnode = transform->getParent(0);
217     lodnode->removeChild(transform);
218
219     // Clean up the LOD nodes if required
220     if (lodnode->getNumChildren() == 0) {
221         osg::ref_ptr<osg::Group> lodnode1 = lodnode->getParent(0);
222             
223         lodnode1->removeChild(lodnode);
224             
225         if (lodnode1->getNumChildren() == 0) {
226             placed_root->removeChild(lodnode1);
227         }
228     }
229 }
230
231 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
232                                   float lon, float lat, float alt, float x, float y) {
233     
234     // Get the base position
235     SGGeod loc = SGGeod::fromDegFt(lon, lat, alt);
236     addCloudToTree(transform, loc, x, y);      
237 }
238
239
240 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
241                                   SGGeod loc, float x, float y) {
242         
243     float alt = loc.getElevationFt();
244     // Determine any shift by x/y
245     if ((x != 0.0f) || (y != 0.0f)) {
246         double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x); 
247         double dst = sqrt(x*x + y*y);
248         double endcrs;
249         
250         SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f);            
251         SGGeodesy::direct(base_pos, crs, dst, loc, endcrs);
252     }
253     
254     // The direct call provides the position at 0 alt, so adjust as required.      
255     loc.setElevationFt(alt);    
256     addCloudToTree(transform, loc);    
257 }
258         
259         
260 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc) {        
261     // Work out where this cloud should go in OSG coordinates.
262     SGVec3<double> cart;
263     SGGeodesy::SGGeodToCart(loc, cart);
264     osg::Vec3f pos = toOsg(cart);
265
266
267     if (old_pos == osg::Vec3f(0.0f, 0.0f, 0.0f)) {
268         // First setup.
269         SGVec3<double> fieldcenter;
270         SGGeodesy::SGGeodToCart(SGGeod::fromDegFt(loc.getLongitudeDeg(), loc.getLatitudeDeg(), 0.0f), fieldcenter);
271         // Convert to the scenegraph orientation where we just rotate around
272         // the y axis 180 degrees.
273         osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
274         
275         osg::Vec3f osg_pos = toOsg(fieldcenter);            
276         
277         field_transform->setPosition(osg_pos);
278         field_transform->setAttitude(orient);
279         old_pos = osg_pos;
280     }
281     
282     // Convert position to cloud-coordinates
283     pos = pos - field_transform->getPosition();
284     pos = field_transform->getAttitude().inverse() * pos;    
285
286     // We have a two level dynamic quad tree which the cloud will be added
287     // to. If there are no appropriate nodes in the quad tree, they are
288     // created as required.
289     bool found = false;
290     osg::ref_ptr<osg::LOD> lodnode1;
291     osg::ref_ptr<osg::LOD> lodnode;
292
293     for (unsigned int i = 0; (!found) && (i < placed_root->getNumChildren()); i++) {
294         lodnode1 = (osg::LOD*) placed_root->getChild(i);
295         if ((lodnode1->getCenter() - pos).length2() < RADIUS_LEVEL_1*RADIUS_LEVEL_1) {
296             // New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
297             found = true;
298         }
299     }
300
301     if (!found) {
302         lodnode1 = new osg::LOD();
303         placed_root->addChild(lodnode1.get());
304     }
305
306     // Now check if there is a second level LOD node at an appropriate distance
307     found = false;
308
309     for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {
310         lodnode = (osg::LOD*) lodnode1->getChild(j);
311         if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) {
312             // We've found the right leaf LOD node
313             found = true;
314         }
315     }
316
317     if (!found) {
318         // No suitable leave node was found, so we need to add one.
319         lodnode = new osg::LOD();
320         lodnode1->addChild(lodnode, 0.0f, 4*RADIUS_LEVEL_1);
321     }
322
323     transform->setPosition(pos);
324     lodnode->addChild(transform.get(), 0.0f, view_distance);
325
326     lodnode->dirtyBound();
327     lodnode1->dirtyBound();
328     field_root->dirtyBound();
329 }
330         
331 bool SGCloudField::deleteCloud(int identifier) {
332     osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
333     if (transform == NULL) return false;
334         
335     removeCloudFromTree(transform);
336     cloud_hash.erase(identifier);
337
338     return true;
339 }
340         
341 bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt) {
342     return repositionCloud(identifier, lon, lat, alt, 0.0f, 0.0f);
343 }
344
345 bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt, float x, float y) {
346     osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
347     
348     if (transform == NULL) return false;
349
350     removeCloudFromTree(transform);
351     addCloudToTree(transform, lon, lat, alt, x, y);
352     return true;
353     }
354
355 bool SGCloudField::isDefined3D(void) {
356     return (cloud_hash.size() > 0);
357 }
358
359 SGCloudField::CloudFog::CloudFog() {
360     fog = new osg::Fog;
361     fog->setMode(osg::Fog::EXP2);
362     fog->setDataVariance(osg::Object::DYNAMIC);
363 }
364
365 void SGCloudField::updateFog(double visibility, const osg::Vec4f& color) {
366     const double sqrt_m_log01 = sqrt(-log(0.01));
367     osg::Fog* fog = CloudFog::instance()->fog.get();
368     fog->setColor(color);
369     fog->setDensity(sqrt_m_log01 / visibility);
370 }
371