1 // a layer of 3d clouds
3 // Written by Harald JOHNSEN, started April 2005.
5 // Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
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.
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.
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.
24 # include <simgear_config.h>
27 #include <osg/Texture2D>
28 #include <osg/PositionAttitudeTransform>
30 #include <simgear/compiler.h>
33 #include <simgear/math/sg_random.h>
34 #include <simgear/math/sg_geodesy.hxx>
35 #include <simgear/math/polar3d.hxx>
42 #include <simgear/environment/visual_enviro.hxx>
43 #include <simgear/scene/util/RenderConstants.hxx>
45 #include "newcloud.hxx"
46 #include "cloudfield.hxx"
48 #if defined(__MINGW32__)
49 #define isnan(x) _isnan(x)
52 #if defined (__FreeBSD__)
53 # if __FreeBSD_version < 500000
55 inline int isnan(double r) { return !(r <= 0 || r >= 0); }
60 using namespace simgear;
63 #if defined (__CYGWIN__)
67 float SGCloudField::fieldSize = 50000.0f;
68 float SGCloudField::coverage = 1.0f;
69 double SGCloudField::timer_dt = 0.0;
70 float SGCloudField::view_distance = 20000.0f;
71 sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
74 // reposition the cloud layer at the specified origin and orientation
75 bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
78 osg::Matrix T, LON, LAT;
80 // Calculating the reposition information is expensive.
81 // Only perform the reposition every 60 frames.
82 reposition_count = (reposition_count + 1) % 60;
83 if ((reposition_count != 0) || !defined3D) return false;
85 SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
87 double dist = SGGeodesy::distanceM(cld_pos, pos);
89 if (dist > (fieldSize * 2)) {
90 // First time or very large distance
92 SGGeodesy::SGGeodToCart(SGGeod::fromRad(lon, lat), cart);
93 T.makeTranslate(cart.osg());
95 LON.makeRotate(lon, osg::Vec3(0, 0, 1));
96 LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
98 field_transform->setMatrix( LAT*LON*T );
99 cld_pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
100 } else if (dist > fieldSize) {
101 // Distance requires repositioning of cloud field.
102 // We can easily work out the direction to reposition
103 // from the course between the cloud position and the
105 SGGeoc pos = SGGeoc::fromGeod(SGGeod::fromRad(lon, lat));
107 float crs = SGGeoc::courseDeg(cld_pos, pos);
108 if ((crs < 45.0) || (crs > 315.0)) {
109 SGGeodesy::advanceRadM(cld_pos, 0.0, fieldSize, cld_pos);
112 if ((crs > 45.0) && (crs < 135.0)) {
113 SGGeodesy::advanceRadM(cld_pos, SGD_PI_2, fieldSize, cld_pos);
116 if ((crs > 135.0) && (crs < 225.0)) {
117 SGGeodesy::advanceRadM(cld_pos, SGD_PI, fieldSize, cld_pos);
120 if ((crs > 225.0) && (crs < 315.0)) {
121 SGGeodesy::advanceRadM(cld_pos, SGD_PI + SGD_PI_2, fieldSize, cld_pos);
125 SGGeodesy::SGGeodToCart(SGGeod::fromRad(cld_pos.getLongitudeRad(), cld_pos.getLatitudeRad()), cart);
126 T.makeTranslate(cart.osg());
128 LON.makeRotate(cld_pos.getLongitudeRad(), osg::Vec3(0, 0, 1));
129 LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - cld_pos.getLatitudeRad(), osg::Vec3(0, 1, 0));
131 field_transform->setMatrix( LAT*LON*T );
134 field_root->getStateSet()->setRenderBinDetails(asl, "RenderBin");
139 SGCloudField::SGCloudField() :
140 field_root(new osg::Group),
141 field_transform(new osg::MatrixTransform),
150 field_root->addChild(field_transform.get());
151 field_root->setName("3D Cloud field root");
152 osg::StateSet *rootSet = field_root->getOrCreateStateSet();
153 rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
155 osg::ref_ptr<osg::Group> quad_root = new osg::Group();
157 for (int i = 0; i < BRANCH_SIZE; i++) {
158 for (int j = 0; j < BRANCH_SIZE; j++) {
159 quad[i][j] = new osg::LOD();
160 quad[i][j]->setName("Quad");
161 quad_root->addChild(quad[i][j].get());
165 int leafs = QUADTREE_SIZE / BRANCH_SIZE;
167 for (int x = 0; x < QUADTREE_SIZE; x++) {
168 for (int y = 0; y < QUADTREE_SIZE; y++) {
169 field_group[x][y]= new osg::Switch;
170 field_group[x][y]->setName("3D cloud group");
172 // Work out where to put this node in the quad tree
175 quad[i][j]->addChild(field_group[x][y].get(), 0.0f, view_distance);
179 // We duplicate the defined field group in a 3x3 array. This way,
180 // we can simply shift entire groups around.
181 // TODO: "Bend" the edge groups so when shifted they line up.
182 // Currently the clouds "jump down" when we reposition them.
183 for(int x = -1 ; x <= 1 ; x++) {
184 for(int y = -1 ; y <= 1 ; y++ ) {
185 osg::ref_ptr<osg::PositionAttitudeTransform> transform =
186 new osg::PositionAttitudeTransform;
187 transform->addChild(quad_root.get());
188 transform->setPosition(osg::Vec3(x*fieldSize, y * fieldSize, 0.0));
190 field_transform->addChild(transform.get());
195 SGCloudField::~SGCloudField() {
199 void SGCloudField::clear(void) {
200 for (int x = 0; x < QUADTREE_SIZE; x++) {
201 for (int y = 0; y < QUADTREE_SIZE; y++) {
202 int num_children = field_group[x][y]->getNumChildren();
204 for (int i = 0; i < num_children; i++) {
205 field_group[x][y]->removeChild(i);
210 SGCloudField::defined3D = false;
213 // use a table or else we see poping when moving the slider...
214 static int densTable[][10] = {
215 {0,0,0,0,0,0,0,0,0,0},
216 {1,0,0,0,0,0,0,0,0,0},
217 {1,0,0,0,1,0,0,0,0,0},
218 {1,0,0,0,1,0,0,1,0,0}, // 30%
219 {1,0,1,0,1,0,0,1,0,0},
220 {1,0,1,0,1,0,1,1,0,0}, // 50%
221 {1,0,1,0,1,0,1,1,0,1},
222 {1,0,1,1,1,0,1,1,0,1}, // 70%
223 {1,1,1,1,1,0,1,1,0,1},
224 {1,1,1,1,1,0,1,1,1,1}, // 90%
225 {1,1,1,1,1,1,1,1,1,1}
228 void SGCloudField::applyCoverage(void) {
230 int row = (int) (coverage * 10.0);
231 if (row > 10) row = 9;
234 if (coverage != last_coverage) {
235 for (int x = 0; x < QUADTREE_SIZE; x++) {
236 for (int y = 0; y < QUADTREE_SIZE; y++) {
237 // Switch on/off the children depending on the required coverage.
238 int num_children = field_group[x][y]->getNumChildren();
239 for (int i = 0; i < num_children; i++) {
240 if (++col > 9) col = 0;
241 if ( densTable[row][col] ) {
242 field_group[x][y]->setValue(i, true);
244 field_group[x][y]->setValue(i, false);
251 last_coverage = coverage;
254 void SGCloudField::addCloud( SGVec3f& pos, SGNewCloud *cloud) {
256 osg::ref_ptr<osg::Geode> geode = cloud->genCloud();
258 // Determine which quadtree to put it in.
259 int x = (int) floor((pos.x() + fieldSize/2.0) * QUADTREE_SIZE / fieldSize);
260 if (x >= QUADTREE_SIZE) x = (QUADTREE_SIZE - 1);
263 int y = (int) floor((pos.y() + fieldSize/2.0) * QUADTREE_SIZE / fieldSize);
264 if (y >= QUADTREE_SIZE) y = (QUADTREE_SIZE - 1);
267 osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
269 transform->setPosition(pos.osg());
270 transform->addChild(geode.get());
272 field_group[x][y]->addChild(transform.get(), true);
275 void SGCloudField::applyVisRange(void) {
277 for (int x = 0; x < BRANCH_SIZE; x++) {
278 for (int y = 0; y < BRANCH_SIZE; y++) {
279 int num_children = quad[x][y]->getNumChildren();
280 for (int i = 0; i < num_children; i++) {
281 quad[x][y]->setRange(i, 0.0f, view_distance);