]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/cloudfield.cxx
Work around apparent OSG 3.2.0 normal binding bug.
[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 #include <osgSim/Impostor>
32
33 #include <simgear/compiler.h>
34
35 #include <simgear/math/sg_random.h>
36 #include <simgear/math/sg_geodesy.hxx>
37 #include <simgear/scene/util/SGSceneUserData.hxx>
38
39 #include <algorithm>
40 #include <vector>
41 #include <iostream>
42
43 using namespace std;
44
45 using std::vector;
46
47 //#include <simgear/environment/visual_enviro.hxx>
48 #include <simgear/scene/util/RenderConstants.hxx>
49 #include <simgear/scene/util/SGUpdateVisitor.hxx>
50 #include "sky.hxx"
51 #include "newcloud.hxx"
52 #include "cloudfield.hxx"
53
54 #if defined(__MINGW32__)
55 #define isnan(x) _isnan(x)
56 #endif
57
58 #if defined (__FreeBSD__)
59 #  if __FreeBSD_version < 500000
60      extern "C" {
61        inline int isnan(double r) { return !(r <= 0 || r >= 0); }
62      }
63 #  endif
64 #endif
65
66 using namespace simgear;
67
68 float SGCloudField::fieldSize = 50000.0f;
69 double SGCloudField::timer_dt = 0.0;
70 float SGCloudField::view_distance = 20000.0f;
71 bool SGCloudField::wrap = true;
72 float SGCloudField::MAX_CLOUD_DEPTH = 2000.0f;
73 bool SGCloudField::use_impostors = false;
74 float SGCloudField::lod1_range = 8000.0f;
75 float SGCloudField::lod2_range = 4000.0f;
76 float SGCloudField::impostor_distance = 15000.0f;
77
78 int impostorcount = 0;
79 int lodcount = 0;
80 int cloudcount = 0;
81
82 SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
83
84
85 // Reposition the cloud layer at the specified origin and orientation
86 bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
87                                double dt, int asl, float speed, float direction ) {
88     // Determine any movement of the placed clouds
89     if (placed_root->getNumChildren() == 0) return false;
90
91     SGVec3<double> cart;
92     SGGeod new_pos = SGGeod::fromRadFt(lon, lat, 0.0f);
93                             
94     SGGeodesy::SGGeodToCart(new_pos, cart);
95     osg::Vec3f osg_pos = toOsg(cart);
96     osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
97     
98     // Always update the altitude transform, as this allows
99     // the clouds to rise and fall smoothly depending on environment updates.
100     altitude_transform->setPosition(osg::Vec3f(0.0f, 0.0f, (float) asl));
101     
102     // Similarly, always determine the effects of the wind
103     osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
104                                  sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
105                                  0.0f);
106     osg::Vec3f windosg = field_transform->getAttitude() * wind;
107     field_transform->setPosition(field_transform->getPosition() + windosg);
108     
109     if (!wrap) {
110         // If we're not wrapping the cloudfield, then we make no effort to reposition
111         return false;
112     }
113         
114     if ((old_pos - osg_pos).length() > fieldSize*2) {
115         // Big movement - reposition centered to current location.
116         field_transform->setPosition(osg_pos);
117         field_transform->setAttitude(orient);
118         old_pos = osg_pos;
119     } else {        
120         osg::Quat fta =  field_transform->getAttitude();
121         osg::Quat ftainv = field_transform->getAttitude().inverse();
122         
123         // delta is the vector from the old position to the new position in cloud-coords
124         // osg::Vec3f delta = ftainv * (osg_pos - old_pos);
125         old_pos = osg_pos;
126                 
127         // Check if any of the clouds should be moved.
128         for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
129             itr != end;
130             ++itr) {
131               
132              osg::ref_ptr<osg::PositionAttitudeTransform> pat = itr->second;
133             
134              if (pat == 0) {
135                 continue;
136              }
137              
138              osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition();
139                                       
140              // Determine the vector from the new position to the cloud in cloud-space.
141              osg::Vec3f w =  ftainv * (currpos - toOsg(cart));               
142               
143              // Determine a course if required. Note that this involves some axis translation.
144              float x = 0.0;
145              float y = 0.0;
146              if (w.x() >  0.6*fieldSize) { y =  fieldSize; }
147              if (w.x() < -0.6*fieldSize) { y = -fieldSize; }
148              if (w.y() >  0.6*fieldSize) { x = -fieldSize; }
149              if (w.y() < -0.6*fieldSize) { x =  fieldSize; }
150               
151              if ((x != 0.0) || (y != 0.0)) {
152                  removeCloudFromTree(pat);
153                  SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() + 
154                                                   fta * pat->getPosition()));
155                  addCloudToTree(pat, p, x, y);                
156             }
157         }        
158     }
159     
160     // Render the clouds in order from farthest away layer to nearest one.
161     field_root->getStateSet()->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
162     return true;
163 }
164
165 SGCloudField::SGCloudField() :
166         field_root(new osg::Group),
167         field_transform(new osg::PositionAttitudeTransform),
168         altitude_transform(new osg::PositionAttitudeTransform)
169 {
170     old_pos = osg::Vec3f(0.0f, 0.0f, 0.0f);
171     field_root->addChild(field_transform.get());
172     field_root->setName("3D Cloud field root");
173     osg::StateSet *rootSet = field_root->getOrCreateStateSet();
174     rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
175     rootSet->setAttributeAndModes(getFog());
176     
177     field_transform->addChild(altitude_transform.get());
178     placed_root = new osg::Group();
179     altitude_transform->addChild(placed_root);
180     impostorcount = 0;
181     lodcount = 0;
182     cloudcount = 0;
183 }
184     
185 SGCloudField::~SGCloudField() {
186 }
187
188
189 void SGCloudField::clear(void) {
190
191     for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
192         itr != end;
193         ++itr) {
194         removeCloudFromTree(itr->second);
195     }
196     
197     cloud_hash.clear();
198 }
199
200 void SGCloudField::applyVisAndLoDRange(void)
201 {
202     for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) {
203         osg::ref_ptr<osg::LOD> lodnode1 = (osg::LOD*) placed_root->getChild(i);
204         for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) {
205             lodnode1->setRange(j, 0.0f, lod1_range + lod2_range + view_distance + MAX_CLOUD_DEPTH);
206             osg::ref_ptr<osg::LOD> lodnode2 = (osg::LOD*) lodnode1->getChild(j);
207             for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) {
208                 lodnode2->setRange(k, 0.0f, view_distance + MAX_CLOUD_DEPTH);
209             }
210         }
211     }
212 }
213             
214 bool SGCloudField::addCloud(float lon, float lat, float alt, int index, osg::ref_ptr<EffectGeode> geode) {
215   return addCloud(lon, lat, alt, 0.0f, 0.0f, index, geode);
216 }
217
218 bool SGCloudField::addCloud(float lon, float lat, float alt, float x, float y, int index, osg::ref_ptr<EffectGeode> geode) {
219     // If this cloud index already exists, don't replace it.
220     if (cloud_hash[index]) return false;
221
222     osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
223
224     transform->addChild(geode.get());
225     addCloudToTree(transform, lon, lat, alt, x, y);
226     cloud_hash[index] = transform;
227     return true;
228 }
229
230 // Remove a give cloud from inside the tree, without removing it from the cloud hash
231 void SGCloudField::removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform)
232 {
233     if (transform == 0)
234     {
235         // Ooops!
236         return;
237     }
238     osg::ref_ptr<osg::Group> lodnode = transform->getParent(0);
239     lodnode->removeChild(transform);
240     cloudcount--;
241
242     if (lodnode->getNumChildren() == 0) {
243         osg::ref_ptr<osg::Group> lodnode1 = lodnode->getParent(0);
244         osg::ref_ptr<osgSim::Impostor> impostornode = (osgSim::Impostor*) lodnode1->getParent(0);
245
246         lodnode1->removeChild(lodnode);
247         lodcount--;
248
249         if (lodnode1->getNumChildren() == 0) {
250           impostornode->removeChild(lodnode1);
251           placed_root->removeChild(impostornode);
252           impostorcount--;
253         }
254     }
255 }
256
257 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
258                                   float lon, float lat, float alt, float x, float y) {
259     
260     // Get the base position
261     SGGeod loc = SGGeod::fromDegFt(lon, lat, alt);
262     addCloudToTree(transform, loc, x, y);      
263 }
264
265
266 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
267                                   SGGeod loc, float x, float y) {
268         
269     float alt = loc.getElevationFt();
270     // Determine any shift by x/y
271     if ((x != 0.0f) || (y != 0.0f)) {
272         double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x); 
273         double dst = sqrt(x*x + y*y);
274         double endcrs;
275         
276         SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f);            
277         SGGeodesy::direct(base_pos, crs, dst, loc, endcrs);
278     }
279     
280     // The direct call provides the position at 0 alt, so adjust as required.      
281     loc.setElevationFt(alt);    
282     addCloudToTree(transform, loc);    
283 }
284         
285         
286 void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc) {        
287     // Work out where this cloud should go in OSG coordinates.
288     SGVec3<double> cart;
289     SGGeodesy::SGGeodToCart(loc, cart);
290     osg::Vec3f pos = toOsg(cart);
291
292
293     if (old_pos == osg::Vec3f(0.0f, 0.0f, 0.0f)) {
294         // First setup.
295         SGVec3<double> fieldcenter;
296         SGGeodesy::SGGeodToCart(SGGeod::fromDegFt(loc.getLongitudeDeg(), loc.getLatitudeDeg(), 0.0f), fieldcenter);
297         // Convert to the scenegraph orientation where we just rotate around
298         // the y axis 180 degrees.
299         osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
300         
301         osg::Vec3f osg_pos = toOsg(fieldcenter);            
302         
303         field_transform->setPosition(osg_pos);
304         field_transform->setAttitude(orient);
305         old_pos = osg_pos;
306     }
307     
308     // Convert position to cloud-coordinates
309     pos = pos - field_transform->getPosition();
310     pos = field_transform->getAttitude().inverse() * pos;    
311
312     // We have a two level dynamic quad tree which the cloud will be added
313     // to. If there are no appropriate nodes in the quad tree, they are
314     // created as required.
315     bool found = false;
316     osg::ref_ptr<osg::LOD> lodnode1;
317     osg::ref_ptr<osg::LOD> lodnode;
318     osg::ref_ptr<osgSim::Impostor> impostornode;
319
320     for (unsigned int i = 0; (!found) && (i < placed_root->getNumChildren()); i++) {
321         lodnode1 = (osg::LOD*) placed_root->getChild(i);
322         if ((lodnode1->getCenter() - pos).length2() < lod1_range*lod1_range) {
323           // New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
324           found = true;
325         }         
326     }
327
328     if (!found) {
329         if (use_impostors) {
330           impostornode = new osgSim::Impostor();
331           impostornode->setImpostorThreshold(impostor_distance);
332           //impostornode->setImpostorThresholdToBound();
333           //impostornode->setCenter(pos);                
334           placed_root->addChild(impostornode.get());
335           lodnode1 = (osg::ref_ptr<osg::LOD>) impostornode;
336         } else {
337           lodnode1 = new osg::LOD();
338           placed_root->addChild(lodnode1.get());
339         }
340         impostorcount++;
341     }
342
343     // Now check if there is a second level LOD node at an appropriate distance
344     found = false;
345     
346     for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {      
347         lodnode = (osg::LOD*) lodnode1->getChild(j);
348         if ((lodnode->getCenter() - pos).length2() < lod2_range*lod2_range) {
349             // We've found the right leaf LOD node
350             found = true;
351         }
352     }
353
354     if (!found) {
355         // No suitable leaf node was found, so we need to add one.
356         lodnode = new osg::LOD();
357         lodnode1->addChild(lodnode, 0.0f, lod1_range + lod2_range + view_distance + MAX_CLOUD_DEPTH);
358         lodcount++;
359     } 
360     
361     transform->setPosition(pos);
362     lodnode->addChild(transform.get(), 0.0f, view_distance + MAX_CLOUD_DEPTH);
363     cloudcount++;
364     SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "Impostors: " << impostorcount <<
365                                      " LoD: " << lodcount << 
366                                      " Clouds: " << cloudcount);
367
368     lodnode->dirtyBound();
369     lodnode1->dirtyBound();
370     field_root->dirtyBound();
371 }
372         
373 bool SGCloudField::deleteCloud(int identifier) {
374     osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
375     if (transform == 0) return false;
376         
377     removeCloudFromTree(transform);
378     cloud_hash.erase(identifier);
379
380     return true;
381 }
382         
383 bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt) {
384     return repositionCloud(identifier, lon, lat, alt, 0.0f, 0.0f);
385 }
386
387 bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt, float x, float y) {
388     osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
389     
390     if (transform == NULL) return false;
391
392     removeCloudFromTree(transform);
393     addCloudToTree(transform, lon, lat, alt, x, y);
394     return true;
395     }
396
397 bool SGCloudField::isDefined3D(void) {
398     return (! cloud_hash.empty());
399 }
400
401 SGCloudField::CloudFog::CloudFog() {
402     fog = new osg::Fog;
403     fog->setMode(osg::Fog::EXP2);
404     fog->setDataVariance(osg::Object::DYNAMIC);
405 }
406
407 void SGCloudField::updateFog(double visibility, const osg::Vec4f& color) {
408     const double sqrt_m_log01 = sqrt(-log(0.01));
409     osg::Fog* fog = CloudFog::instance()->fog.get();
410     fog->setColor(color);
411     fog->setDensity(sqrt_m_log01 / visibility);
412 }
413