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