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