]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/cloudfield.cxx
Merge branch 'ehofman/sound'
[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 <plib/sg.h>
35 #include <simgear/math/sg_random.h>
36 #include <simgear/math/sg_geodesy.hxx>
37 #include <simgear/math/polar3d.hxx>
38
39 #include <algorithm>
40 #include <vector>
41
42 using std::vector;
43
44 #include <simgear/environment/visual_enviro.hxx>
45 #include <simgear/scene/util/RenderConstants.hxx>
46 #include <simgear/scene/util/SGUpdateVisitor.hxx>
47 #include "sky.hxx"
48 #include "newcloud.hxx"
49 #include "cloudfield.hxx"
50
51 #if defined(__MINGW32__)
52 #define isnan(x) _isnan(x)
53 #endif
54
55 #if defined (__FreeBSD__)
56 #  if __FreeBSD_version < 500000
57      extern "C" {
58        inline int isnan(double r) { return !(r <= 0 || r >= 0); }
59      }
60 #  endif
61 #endif
62
63 using namespace simgear;
64
65
66 #if defined (__CYGWIN__)
67 #include <ieeefp.h>
68 #endif
69
70 float SGCloudField::fieldSize = 50000.0f;
71 double SGCloudField::timer_dt = 0.0;
72 float SGCloudField::view_distance = 20000.0f;
73 sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
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 )
78 {
79     osg::Matrix T, LON, LAT;
80     
81     // Always update the altitude transform, as this allows
82     // the clouds to rise and fall smoothly depending on environment updates.
83     altitude_transform->setPosition(osg::Vec3d(0.0, 0.0, (double) asl));
84     
85     // Calculating the reposition information is expensive. 
86     // Only perform the reposition every 60 frames.
87     reposition_count = (reposition_count + 1) % 60;
88     if ((reposition_count != 0) || !defined3D) return false;
89     
90     SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
91     
92     double dist = SGGeodesy::distanceM(cld_pos, pos);
93     
94     if (dist > (fieldSize * 2)) {
95         // First time or very large distance
96         SGVec3<double> cart;
97         SGGeodesy::SGGeodToCart(SGGeod::fromRad(lon, lat), cart);
98         T.makeTranslate(toOsg(cart));
99         
100         LON.makeRotate(lon, osg::Vec3(0, 0, 1));
101         LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
102
103         field_transform->setMatrix( LAT*LON*T );
104         cld_pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
105     } else if (dist > fieldSize) {
106         // Distance requires repositioning of cloud field.
107         // We can easily work out the direction to reposition
108         // from the course between the cloud position and the
109         // camera position.
110         SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
111         
112         float crs = SGGeoc::courseDeg(cld_pos, pos);
113         if ((crs < 45.0) || (crs > 315.0)) {
114             SGGeodesy::advanceRadM(cld_pos, 0.0, fieldSize, cld_pos);
115         }
116         
117         if ((crs > 45.0) && (crs < 135.0)) {
118             SGGeodesy::advanceRadM(cld_pos, SGD_PI_2, fieldSize, cld_pos);
119         }
120
121         if ((crs > 135.0) && (crs < 225.0)) {
122             SGGeodesy::advanceRadM(cld_pos, SGD_PI, fieldSize, cld_pos);
123         }
124         
125         if ((crs > 225.0) && (crs < 315.0)) {
126             SGGeodesy::advanceRadM(cld_pos, SGD_PI + SGD_PI_2, fieldSize, cld_pos);
127         }
128         
129         SGVec3<double> cart;
130         SGGeodesy::SGGeodToCart(SGGeod::fromRad(cld_pos.getLongitudeRad(), cld_pos.getLatitudeRad()), cart);
131         T.makeTranslate(toOsg(cart));
132         
133         LON.makeRotate(cld_pos.getLongitudeRad(), osg::Vec3(0, 0, 1));
134         LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - cld_pos.getLatitudeRad(), osg::Vec3(0, 1, 0));
135
136         field_transform->setMatrix( LAT*LON*T );
137     }
138     
139     field_root->getStateSet()->setRenderBinDetails(asl, "DepthSortedBin");
140
141     return true;
142 }
143
144 SGCloudField::SGCloudField() :
145         field_root(new osg::Group),
146         field_transform(new osg::MatrixTransform),
147         altitude_transform(new osg::PositionAttitudeTransform),
148         deltax(0.0),
149         deltay(0.0),
150         last_course(0.0),
151         last_coverage(0.0),
152         coverage(0.0),
153         reposition_count(0),
154         defined3D(false)
155 {
156     cld_pos = SGGeoc();
157     field_root->addChild(field_transform.get());
158     field_root->setName("3D Cloud field root");
159     osg::StateSet *rootSet = field_root->getOrCreateStateSet();
160     rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
161     rootSet->setAttributeAndModes(getFog());
162     
163     osg::ref_ptr<osg::Group> quad_root = new osg::Group();
164     
165     for (int i = 0; i < BRANCH_SIZE; i++) {
166         for (int j = 0; j < BRANCH_SIZE; j++) {
167             quad[i][j] = new osg::LOD();
168             quad[i][j]->setName("Quad");
169             quad_root->addChild(quad[i][j].get());
170         }
171     }
172     
173     int leafs = QUADTREE_SIZE / BRANCH_SIZE;
174
175     for (int x = 0; x < QUADTREE_SIZE; x++) {
176         for (int y = 0; y < QUADTREE_SIZE; y++) {
177             field_group[x][y]= new osg::Switch;
178             field_group[x][y]->setName("3D cloud group");
179             
180             // Work out where to put this node in the quad tree
181             int i = x / leafs;
182             int j = y / leafs;
183             quad[i][j]->addChild(field_group[x][y].get(), 0.0f, view_distance);
184         }
185     }
186     
187     field_transform->addChild(altitude_transform.get());
188             
189     // We duplicate the defined field group in a 3x3 array. This way,
190     // we can simply shift entire groups around.
191     // TODO: "Bend" the edge groups so when shifted they line up.
192     // Currently the clouds "jump down" when we reposition them.
193     for(int x = -1 ; x <= 1 ; x++) {
194         for(int y = -1 ; y <= 1 ; y++ ) {
195             osg::ref_ptr<osg::PositionAttitudeTransform> transform =
196                     new osg::PositionAttitudeTransform;
197             transform->addChild(quad_root.get());
198             transform->setPosition(osg::Vec3(x*fieldSize, y * fieldSize, 0.0));
199             
200             altitude_transform->addChild(transform.get());
201         }
202     }
203 }
204
205 SGCloudField::~SGCloudField() {
206 }
207
208
209 void SGCloudField::clear(void) {
210     for (int x = 0; x < QUADTREE_SIZE; x++) {
211         for (int y = 0; y < QUADTREE_SIZE; y++) {
212             int num_children = field_group[x][y]->getNumChildren();
213             field_group[x][y]->removeChildren(0, num_children);
214         }
215     }
216     
217     SGCloudField::defined3D = false;
218 }
219
220 // use a table or else we see poping when moving the slider...
221 static int densTable[][10] = {
222         {0,0,0,0,0,0,0,0,0,0},
223         {1,0,0,0,0,0,0,0,0,0},
224         {1,0,0,0,1,0,0,0,0,0},
225         {1,0,0,0,1,0,0,1,0,0}, // 30%
226         {1,0,1,0,1,0,0,1,0,0},
227         {1,0,1,0,1,0,1,1,0,0}, // 50%
228         {1,0,1,0,1,0,1,1,0,1},
229         {1,0,1,1,1,0,1,1,0,1}, // 70%
230         {1,1,1,1,1,0,1,1,0,1},
231         {1,1,1,1,1,0,1,1,1,1}, // 90%
232         {1,1,1,1,1,1,1,1,1,1}
233 };
234
235 void SGCloudField::applyCoverage(void) {
236
237         int row = (int) (coverage * 10.0);
238         if (row > 9) row = 9;
239         int col = 0;
240
241         if (coverage != last_coverage) {
242             for (int x = 0; x < QUADTREE_SIZE; x++) {
243                 for (int y = 0; y < QUADTREE_SIZE; y++) {
244                 // Switch on/off the children depending on the required coverage.
245                     int num_children = field_group[x][y]->getNumChildren();
246                     for (int i = 0; i < num_children; i++) {
247                         if (++col > 9) col = 0;
248                         if ( densTable[row][col] ) {
249                             field_group[x][y]->setValue(i, true);
250                         } else {
251                             field_group[x][y]->setValue(i, false);
252                         }
253                     }
254                 }
255             }
256         }
257
258         last_coverage = coverage;
259 }
260
261 void SGCloudField::addCloud( SGVec3f& pos, SGNewCloud *cloud) {
262         defined3D = true;
263         osg::ref_ptr<osg::Geode> geode = cloud->genCloud();
264         
265         // Determine which quadtree to put it in.
266         int x = (int) floor((pos.x() + fieldSize/2.0) * QUADTREE_SIZE / fieldSize);
267         if (x >= QUADTREE_SIZE) x = (QUADTREE_SIZE - 1);
268         if (x < 0) x = 0;
269         
270         int y = (int) floor((pos.y() + fieldSize/2.0) * QUADTREE_SIZE / fieldSize);
271         if (y >= QUADTREE_SIZE) y = (QUADTREE_SIZE - 1);
272         if (y < 0) y = 0;
273         
274         osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
275
276         transform->setPosition(toOsg(pos));
277         transform->addChild(geode.get());
278         
279         field_group[x][y]->addChild(transform.get(), true);
280 }
281
282 void SGCloudField::applyVisRange(void) { 
283     
284     for (int x = 0; x < BRANCH_SIZE; x++) {
285         for (int y = 0; y < BRANCH_SIZE; y++) {
286             int num_children = quad[x][y]->getNumChildren(); 
287             for (int i = 0; i < num_children; i++) { 
288                 quad[x][y]->setRange(i, 0.0f, view_distance);
289             }
290         }
291     }
292 }
293
294 SGCloudField::CloudFog::CloudFog()
295 {
296     fog = new osg::Fog;
297     fog->setMode(osg::Fog::EXP2);
298     fog->setDataVariance(osg::Object::DYNAMIC);
299 }
300
301 void SGCloudField::updateFog(double visibility, const osg::Vec4f& color)
302 {
303     const double sqrt_m_log01 = sqrt(-log(0.01));
304     osg::Fog* fog = CloudFog::instance()->fog.get();
305     fog->setColor(color);
306     fog->setDensity(sqrt_m_log01 / visibility);
307 }