]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/cloudfield.cxx
Stuart:
[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 "sky.hxx"
44 #include "newcloud.hxx"
45 #include "cloudfield.hxx"
46
47 #if defined(__MINGW32__)
48 #define isnan(x) _isnan(x)
49 #endif
50
51 #if defined (__FreeBSD__)
52 #  if __FreeBSD_version < 500000
53      extern "C" {
54        inline int isnan(double r) { return !(r <= 0 || r >= 0); }
55      }
56 #  endif
57 #endif
58
59
60 #if defined (__CYGWIN__)
61 #include <ieeefp.h>
62 #endif
63
64 float SGCloudField::fieldSize = 50000.0f;
65 float SGCloudField::density = 100.0f;
66 double SGCloudField::timer_dt = 0.0;
67 sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
68
69 void SGCloudField::set_density(float density) {
70         SGCloudField::density = density;
71 }
72
73 // reposition the cloud layer at the specified origin and orientation
74 bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
75                                double dt )
76 {
77     osg::Matrix T, LON, LAT;
78     
79     // Calculating the reposition information is expensive. 
80     // Only perform the reposition every 60 frames.
81     reposition_count = (reposition_count + 1) % 60;
82     if ((reposition_count != 0) || !defined3D) return false;
83     
84     SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
85     
86     double dist = SGGeodesy::distanceM(cld_pos, pos);
87     
88     if (dist > (fieldSize * 2)) {
89         // First time or very large distance
90         SGVec3<double> cart;
91         SGGeodesy::SGGeodToCart(SGGeod::fromRad(lon, lat), cart);
92         T.makeTranslate(cart.osg());
93         
94         LON.makeRotate(lon, osg::Vec3(0, 0, 1));
95         LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
96
97         field_transform->setMatrix( LAT*LON*T );
98         cld_pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
99     } else if (dist > fieldSize) {
100         // Distance requires repositioning of cloud field.
101         // We can easily work out the direction to reposition
102         // from the course between the cloud position and the
103         // camera position.
104         SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
105         
106         float crs = SGGeoc::courseDeg(cld_pos, pos);
107         if ((crs < 45.0) || (crs > 315.0)) {
108             SGGeodesy::advanceRadM(cld_pos, 0.0, fieldSize, cld_pos);
109         }
110         
111         if ((crs > 45.0) && (crs < 135.0)) {
112             SGGeodesy::advanceRadM(cld_pos, SGD_PI_2, fieldSize, cld_pos);
113         }
114
115         if ((crs > 135.0) && (crs < 225.0)) {
116             SGGeodesy::advanceRadM(cld_pos, SGD_PI, fieldSize, cld_pos);
117         }
118         
119         if ((crs > 225.0) && (crs < 315.0)) {
120             SGGeodesy::advanceRadM(cld_pos, SGD_PI + SGD_PI_2, fieldSize, cld_pos);
121         }
122         
123         SGVec3<double> cart;
124         SGGeodesy::SGGeodToCart(SGGeod::fromRad(cld_pos.getLongitudeRad(), cld_pos.getLatitudeRad()), cart);
125         T.makeTranslate(cart.osg());
126         
127         LON.makeRotate(cld_pos.getLongitudeRad(), osg::Vec3(0, 0, 1));
128         LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - cld_pos.getLatitudeRad(), osg::Vec3(0, 1, 0));
129
130         field_transform->setMatrix( LAT*LON*T );
131     }
132     return true;
133 }
134
135 SGCloudField::SGCloudField() :
136         field_root(new osg::Group),
137         field_transform(new osg::MatrixTransform),
138         deltax(0.0),
139         deltay(0.0),
140         last_course(0.0),
141         last_density(0.0),
142         defined3D(false),
143         reposition_count(0)
144 {
145     cld_pos = SGGeoc();
146     field_root->addChild(field_transform.get());
147     field_root->setName("3D Cloud field root");
148     
149     osg::ref_ptr<osg::Group> quad_root = new osg::Group();
150     osg::ref_ptr<osg::LOD> quad[BRANCH_SIZE][BRANCH_SIZE];
151     
152     for (int i = 0; i < BRANCH_SIZE; i++) {
153         for (int j = 0; j < BRANCH_SIZE; j++) {
154             quad[i][j] = new osg::LOD();
155             quad[i][j]->setName("Quad");
156             quad_root->addChild(quad[i][j].get());
157         }
158     }
159     
160     int leafs = QUADTREE_SIZE / BRANCH_SIZE;
161
162     for (int x = 0; x < QUADTREE_SIZE; x++) {
163         for (int y = 0; y < QUADTREE_SIZE; y++) {
164             field_group[x][y]= new osg::Switch;
165             field_group[x][y]->setName("3D cloud group");
166             
167             // Work out where to put this node in the quad tree
168             int i = x / leafs;
169             int j = y / leafs;
170             quad[i][j]->addChild(field_group[x][y].get(), 0.0f, 20000.0f);
171         }
172     }
173
174     // We duplicate the defined field group in a 3x3 array. This way,
175     // we can simply shift entire groups around.
176     // TODO: "Bend" the edge groups so when shifted they line up.
177     // Currently the clouds "jump down" when we reposition them.
178     for(int x = -1 ; x <= 1 ; x++) {
179         for(int y = -1 ; y <= 1 ; y++ ) {
180             osg::ref_ptr<osg::PositionAttitudeTransform> transform =
181                     new osg::PositionAttitudeTransform;
182             transform->addChild(quad_root.get());
183             transform->setPosition(osg::Vec3(x*fieldSize, y * fieldSize, 0.0));
184
185             field_transform->addChild(transform.get());
186         }
187     }
188 }
189
190 SGCloudField::~SGCloudField() {
191 }
192
193
194 void SGCloudField::clear(void) {
195     for (int x = 0; x < QUADTREE_SIZE; x++) {
196         for (int y = 0; y < QUADTREE_SIZE; y++) {
197             int num_children = field_group[x][y]->getNumChildren();
198
199             for (int i = 0; i < num_children; i++) {
200                 field_group[x][y]->removeChild(i);
201             }
202         }
203     }
204     
205     SGCloudField::defined3D = false;
206 }
207
208 // use a table or else we see poping when moving the slider...
209 static int densTable[][10] = {
210         {0,0,0,0,0,0,0,0,0,0},
211         {1,0,0,0,0,0,0,0,0,0},
212         {1,0,0,0,1,0,0,0,0,0},
213         {1,0,0,0,1,0,0,1,0,0}, // 30%
214         {1,0,1,0,1,0,0,1,0,0},
215         {1,0,1,0,1,0,1,1,0,0}, // 50%
216         {1,0,1,0,1,0,1,1,0,1},
217         {1,0,1,1,1,0,1,1,0,1}, // 70%
218         {1,1,1,1,1,0,1,1,0,1},
219         {1,1,1,1,1,0,1,1,1,1}, // 90%
220         {1,1,1,1,1,1,1,1,1,1}
221 };
222
223 void SGCloudField::applyDensity(void) {
224
225         int row = (int) (density / 10.0);
226         int col = 0;
227
228         if (density != last_density) {
229             for (int x = 0; x < QUADTREE_SIZE; x++) {
230                 for (int y = 0; y < QUADTREE_SIZE; y++) {
231                 // Switch on/off the children depending on the required density.
232                     int num_children = field_group[x][y]->getNumChildren();
233                     for (int i = 0; i < num_children; i++) {
234                         if (++col > 9) col = 0;
235                         if ( densTable[row][col] ) {
236                             field_group[x][y]->setValue(i, true);
237                         } else {
238                             field_group[x][y]->setValue(i, false);
239                         }
240                     }
241                 }
242             }
243         }
244
245         last_density = density;
246 }
247
248 void SGCloudField::addCloud( SGVec3f& pos, SGNewCloud *cloud) {
249         defined3D = true;
250         osg::ref_ptr<osg::Geode> geode = cloud->genCloud();
251         
252         // Determine which quadtree to put it in.
253         int x = (int) floor((pos.x() + fieldSize/2.0) * QUADTREE_SIZE / fieldSize);
254         if (x >= QUADTREE_SIZE) x = (QUADTREE_SIZE - 1);
255         if (x < 0) x = 0;
256         
257         int y = (int) floor((pos.y() + fieldSize/2.0) * QUADTREE_SIZE / fieldSize);
258         if (y >= QUADTREE_SIZE) y = (QUADTREE_SIZE - 1);
259         if (y < 0) y = 0;
260
261         osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
262
263         transform->setPosition(pos.osg());
264         transform->addChild(geode.get());
265         
266         field_group[x][y]->addChild(transform.get(), true);
267 }