]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/cloud.cxx
Replace OpenThreads with SGThread to avoid useless OSG dependency.
[simgear.git] / simgear / scene / sky / cloud.cxx
1 // cloud.cxx -- model a single cloud layer
2 //
3 // Written by Curtis Olson, started June 2000.
4 //
5 // Copyright (C) 2000  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Library General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //
12 // This library 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 #ifdef HAVE_CONFIG_H
23 #  include <simgear_config.h>
24 #endif
25
26 #include <simgear/compiler.h>
27
28 #include <sstream>
29
30 #include <math.h>
31
32 #include <simgear/structure/OSGVersion.hxx>
33 #include <osg/AlphaFunc>
34 #include <osg/BlendFunc>
35 #include <osg/CullFace>
36 #include <osg/Geode>
37 #include <osg/Geometry>
38 #include <osg/Material>
39 #include <osg/ShadeModel>
40 #include <osg/TexEnv>
41 #include <osg/TexEnvCombine>
42 #include <osg/Texture2D>
43 #include <osg/TextureCubeMap>
44 #include <osg/TexMat>
45 #include <osg/Fog>
46 #if SG_OSG_MIN_VERSION_REQUIRED(2,9,5)
47 #include <osgDB/Options>
48 #endif
49
50 #include <simgear/math/sg_random.h>
51 #include <simgear/misc/PathOptions.hxx>
52 #include <simgear/debug/logstream.hxx>
53 #include <simgear/scene/model/model.hxx>
54 #include <simgear/scene/util/RenderConstants.hxx>
55 #include <simgear/scene/util/StateAttributeFactory.hxx>
56 #include <simgear/screen/extensions.hxx>
57
58 #include "newcloud.hxx"
59 #include "cloudfield.hxx"
60 #include "cloud.hxx"
61
62 using namespace simgear;
63 using namespace osg;
64
65 #if defined(__MINGW32__)
66 #define isnan(x) _isnan(x)
67 #endif
68
69 // #if defined (__FreeBSD__)
70 // #  if __FreeBSD_version < 500000
71 //      extern "C" {
72 //        inline int isnan(double r) { return !(r <= 0 || r >= 0); }
73 //      }
74 // #  endif
75 // #endif
76
77 static osg::ref_ptr<osg::StateSet> layer_states[SGCloudLayer::SG_MAX_CLOUD_COVERAGES];
78 static osg::ref_ptr<osg::StateSet> layer_states2[SGCloudLayer::SG_MAX_CLOUD_COVERAGES];
79 static osg::ref_ptr<osg::TextureCubeMap> cubeMap;
80 static bool state_initialized = false;
81
82 const std::string SGCloudLayer::SG_CLOUD_OVERCAST_STRING = "overcast";
83 const std::string SGCloudLayer::SG_CLOUD_BROKEN_STRING = "broken";
84 const std::string SGCloudLayer::SG_CLOUD_SCATTERED_STRING = "scattered";
85 const std::string SGCloudLayer::SG_CLOUD_FEW_STRING = "few";
86 const std::string SGCloudLayer::SG_CLOUD_CIRRUS_STRING = "cirrus";
87 const std::string SGCloudLayer::SG_CLOUD_CLEAR_STRING = "clear";
88
89 // make an StateSet for a cloud layer given the named texture
90 static osg::StateSet*
91 SGMakeState(const SGPath &path, const char* colorTexture,
92             const char* normalTexture)
93 {
94     osg::StateSet *stateSet = new osg::StateSet;
95
96     osg::ref_ptr<osgDB::ReaderWriter::Options> options
97         = makeOptionsFromPath(path);
98     stateSet->setTextureAttribute(0, SGLoadTexture2D(colorTexture,
99                                                      options.get()));
100     stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
101     StateAttributeFactory* attribFactory = StateAttributeFactory::instance();
102     stateSet->setAttributeAndModes(attribFactory->getSmoothShadeModel());
103     stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
104     stateSet->setAttributeAndModes(attribFactory->getStandardAlphaFunc());
105     stateSet->setAttributeAndModes(attribFactory->getStandardBlendFunc());
106
107 //     osg::Material* material = new osg::Material;
108 //     material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
109 //     material->setEmission(osg::Material::FRONT_AND_BACK,
110 //                           osg::Vec4(0.05, 0.05, 0.05, 0));
111 //     material->setSpecular(osg::Material::FRONT_AND_BACK,
112 //                           osg::Vec4(0, 0, 0, 1));
113 //     stateSet->setAttribute(material);
114
115     stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
116
117     // OSGFIXME: invented by me ...
118 //     stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
119 //     stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
120
121 //     stateSet->setMode(GL_LIGHT0, osg::StateAttribute::OFF);
122
123     // If the normal texture is given prepare a bumpmapping enabled state
124 //     if (normalTexture) {
125 //       SGPath normalPath(path);
126 //       normalPath.append(normalTexture);
127 //       stateSet->setTextureAttribute(2, SGLoadTexture2D(normalPath));
128 //       stateSet->setTextureMode(2, GL_TEXTURE_2D, osg::StateAttribute::ON);
129 //     }
130
131     return stateSet;
132 }
133
134 // Constructor
135 SGCloudLayer::SGCloudLayer( const string &tex_path ) :
136     cloud_root(new osg::Switch),
137     layer_root(new osg::Switch),
138     group_top(new osg::Group),
139     group_bottom(new osg::Group),
140     layer_transform(new osg::MatrixTransform),
141     cloud_alpha(1.0),
142     texture_path(tex_path),
143     layer_span(0.0),
144     layer_asl(0.0),
145     layer_thickness(0.0),
146     layer_transition(0.0),
147     layer_visibility(25.0),
148     layer_coverage(SG_CLOUD_CLEAR),
149     scale(4000.0),
150     speed(0.0),
151     direction(0.0),
152     last_course(0.0),
153     max_alpha(1.0)
154 {
155     // XXX
156     // Render bottoms before the rest of transparent objects (rendered
157     // in bin 10), tops after. The negative numbers on the bottoms
158     // RenderBins and the positive numbers on the tops enforce this
159     // order.
160   cloud_root->addChild(layer_root.get(), true);
161   layer_root->addChild(group_bottom.get());
162   layer_root->addChild(group_top.get());
163   osg::StateSet *rootSet = layer_root->getOrCreateStateSet();
164   rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
165   rootSet->setTextureAttribute(0, new osg::TexMat);
166   rootSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
167   // Combiner for fog color and cloud alpha
168   osg::TexEnvCombine* combine0 = new osg::TexEnvCombine;
169   osg::TexEnvCombine* combine1 = new osg::TexEnvCombine;
170   combine0->setCombine_RGB(osg::TexEnvCombine::MODULATE);
171   combine0->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);
172   combine0->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
173   combine0->setSource1_RGB(osg::TexEnvCombine::TEXTURE0);
174   combine0->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
175   combine0->setCombine_Alpha(osg::TexEnvCombine::MODULATE);
176   combine0->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);
177   combine0->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
178   combine0->setSource1_Alpha(osg::TexEnvCombine::TEXTURE0);
179   combine0->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);
180
181   combine1->setCombine_RGB(osg::TexEnvCombine::MODULATE);
182   combine1->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);
183   combine1->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
184   combine1->setSource1_RGB(osg::TexEnvCombine::CONSTANT);
185   combine1->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
186   combine1->setCombine_Alpha(osg::TexEnvCombine::MODULATE);
187   combine1->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);
188   combine1->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
189   combine1->setSource1_Alpha(osg::TexEnvCombine::CONSTANT);
190   combine1->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);
191   combine1->setDataVariance(osg::Object::DYNAMIC);
192   rootSet->setTextureAttributeAndModes(0, combine0);
193   rootSet->setTextureAttributeAndModes(1, combine1);
194   rootSet->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::ON);
195   rootSet->setTextureAttributeAndModes(1, StateAttributeFactory::instance()
196                                        ->getWhiteTexture(),
197                                        osg::StateAttribute::ON);
198   rootSet->setDataVariance(osg::Object::DYNAMIC);
199
200   base = osg::Vec2(sg_random(), sg_random());
201   group_top->addChild(layer_transform.get());
202   group_bottom->addChild(layer_transform.get());
203
204   layer3D = new SGCloudField();
205   cloud_root->addChild(layer3D->getNode(), false);
206
207   rebuild();
208 }
209
210 // Destructor
211 SGCloudLayer::~SGCloudLayer()
212 {
213   delete layer3D;
214 }
215
216 float
217 SGCloudLayer::getSpan_m () const
218 {
219     return layer_span;
220 }
221
222 void
223 SGCloudLayer::setSpan_m (float span_m)
224 {
225     if (span_m != layer_span) {
226         layer_span = span_m;
227         rebuild();
228     }
229 }
230
231 float
232 SGCloudLayer::getElevation_m () const
233 {
234     return layer_asl;
235 }
236
237 void
238 SGCloudLayer::setElevation_m (float elevation_m, bool set_span)
239 {
240     layer_asl = elevation_m;
241
242     if (set_span) {
243         if (elevation_m > 4000)
244             setSpan_m(  elevation_m * 10 );
245         else
246             setSpan_m( 40000 );
247     }
248 }
249
250 float
251 SGCloudLayer::getThickness_m () const
252 {
253     return layer_thickness;
254 }
255
256 void
257 SGCloudLayer::setThickness_m (float thickness_m)
258 {
259     layer_thickness = thickness_m;
260 }
261
262 float
263 SGCloudLayer::getVisibility_m() const
264 {
265     return layer_visibility;
266 }
267
268 void
269 SGCloudLayer::setVisibility_m (float visibility_m)
270 {
271     layer_visibility = visibility_m;
272 }
273
274 float
275 SGCloudLayer::getTransition_m () const
276 {
277     return layer_transition;
278 }
279
280 void
281 SGCloudLayer::setTransition_m (float transition_m)
282 {
283     layer_transition = transition_m;
284 }
285
286 SGCloudLayer::Coverage
287 SGCloudLayer::getCoverage () const
288 {
289     return layer_coverage;
290 }
291
292 void
293 SGCloudLayer::setCoverage (Coverage coverage)
294 {
295     if (coverage != layer_coverage) {
296         layer_coverage = coverage;
297         rebuild();
298     }
299 }
300
301 const std::string &
302 SGCloudLayer::getCoverageString( Coverage coverage )
303 {
304         switch( coverage ) {
305                 case SG_CLOUD_OVERCAST:
306                         return SG_CLOUD_OVERCAST_STRING;
307                 case SG_CLOUD_BROKEN:
308                         return SG_CLOUD_BROKEN_STRING;
309                 case SG_CLOUD_SCATTERED:
310                         return SG_CLOUD_SCATTERED_STRING;
311                 case SG_CLOUD_FEW:
312                         return SG_CLOUD_FEW_STRING;
313                 case SG_CLOUD_CIRRUS:
314                         return SG_CLOUD_CIRRUS_STRING;
315                 case SG_CLOUD_CLEAR:
316                 default:
317                         return SG_CLOUD_CLEAR_STRING;
318         }
319 }
320
321 SGCloudLayer::Coverage 
322 SGCloudLayer::getCoverageType( const std::string & coverage )
323 {
324         if( SG_CLOUD_OVERCAST_STRING == coverage ) {
325                 return SG_CLOUD_OVERCAST;
326         } else if( SG_CLOUD_BROKEN_STRING == coverage ) {
327                 return SG_CLOUD_BROKEN;
328         } else if( SG_CLOUD_SCATTERED_STRING == coverage ) {
329                 return SG_CLOUD_SCATTERED;
330         } else if( SG_CLOUD_FEW_STRING == coverage ) {
331                 return SG_CLOUD_FEW;
332         } else if( SG_CLOUD_CIRRUS_STRING == coverage ) {
333                 return SG_CLOUD_CIRRUS;
334         } else {
335                 return SG_CLOUD_CLEAR;
336         }
337 }
338
339 const std::string &
340 SGCloudLayer::getCoverageString() const
341 {
342         return getCoverageString(layer_coverage);
343 }
344
345 void
346 SGCloudLayer::setCoverageString( const std::string & coverage )
347 {
348         setCoverage( getCoverageType(coverage) );
349 }
350
351 void
352 SGCloudLayer::setTextureOffset(const osg::Vec2& offset)
353 {
354     osg::StateAttribute* attr = layer_root->getStateSet()
355         ->getTextureAttribute(0, osg::StateAttribute::TEXMAT);
356     osg::TexMat* texMat = dynamic_cast<osg::TexMat*>(attr);
357     if (!texMat)
358         return;
359     texMat->setMatrix(osg::Matrix::translate(offset[0], offset[1], 0.0));
360 }
361
362 // colors for debugging the cloud layers
363 #ifdef CLOUD_DEBUG
364 Vec3 cloudColors[] = {Vec3(1.0f, 1.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f),
365                       Vec3(0.0f, 1.0f, 0.0f), Vec3(0.0f, 0.0f, 1.0f)};
366 #else
367 Vec3 cloudColors[] = {Vec3(1.0f, 1.0f, 1.0f), Vec3(1.0f, 1.0f, 1.0f),
368                       Vec3(1.0f, 1.0f, 1.0f), Vec3(1.0f, 1.0f, 1.0f)};
369 #endif
370
371 // build the cloud object
372 void
373 SGCloudLayer::rebuild()
374 {
375     // Initialize states and sizes if necessary.
376     if ( !state_initialized ) { 
377         state_initialized = true;
378
379         SG_LOG(SG_ASTRO, SG_INFO, "initializing cloud layers");
380
381         // This bump mapping code was inspired by the tutorial available at 
382         // http://www.paulsprojects.net/tutorials/simplebump/simplebump.html
383         // and a NVidia white paper 
384         //  http://developer.nvidia.com/object/bumpmappingwithregistercombiners.html
385         // The normal map textures were generated by the normal map Gimp plugin :
386         //  http://nifelheim.dyndns.org/~cocidius/normalmap/
387         //
388         cubeMap = new osg::TextureCubeMap;
389         cubeMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
390         cubeMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
391         cubeMap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
392         cubeMap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
393         cubeMap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
394
395         const int size = 32;
396         const float half_size = 16.0f;
397         const float offset = 0.5f;
398         osg::Vec3 zero_normal(0.5, 0.5, 0.5);
399
400         osg::Image* image = new osg::Image;
401         image->allocateImage(size, size, 1, GL_RGB, GL_UNSIGNED_BYTE);
402         unsigned char *ptr = image->data(0, 0);
403         for (int j = 0; j < size; j++ ) {
404           for (int i = 0; i < size; i++ ) {
405             osg::Vec3 tmp(half_size, -( j + offset - half_size ),
406                           -( i + offset - half_size ) );
407             tmp.normalize();
408             tmp = tmp*0.5 - zero_normal;
409             
410             *ptr++ = (unsigned char)( tmp[ 0 ] * 255 );
411             *ptr++ = (unsigned char)( tmp[ 1 ] * 255 );
412             *ptr++ = (unsigned char)( tmp[ 2 ] * 255 );
413           }
414         }
415         cubeMap->setImage(osg::TextureCubeMap::POSITIVE_X, image);
416
417         image = new osg::Image;
418         image->allocateImage(size, size, 1, GL_RGB, GL_UNSIGNED_BYTE);
419         ptr = image->data(0, 0);
420         for (int j = 0; j < size; j++ ) {
421           for (int i = 0; i < size; i++ ) {
422             osg::Vec3 tmp(-half_size, -( j + offset - half_size ),
423                           ( i + offset - half_size ) );
424             tmp.normalize();
425             tmp = tmp*0.5 - zero_normal;
426             
427             *ptr++ = (unsigned char)( tmp[ 0 ] * 255 );
428             *ptr++ = (unsigned char)( tmp[ 1 ] * 255 );
429             *ptr++ = (unsigned char)( tmp[ 2 ] * 255 );
430           }
431         }
432         cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_X, image);
433
434         image = new osg::Image;
435         image->allocateImage(size, size, 1, GL_RGB, GL_UNSIGNED_BYTE);
436         ptr = image->data(0, 0);
437         for (int j = 0; j < size; j++ ) {
438           for (int i = 0; i < size; i++ ) {
439             osg::Vec3 tmp(( i + offset - half_size ), half_size,
440                           ( j + offset - half_size ) );
441             tmp.normalize();
442             tmp = tmp*0.5 - zero_normal;
443             
444             *ptr++ = (unsigned char)( tmp[ 0 ] * 255 );
445             *ptr++ = (unsigned char)( tmp[ 1 ] * 255 );
446             *ptr++ = (unsigned char)( tmp[ 2 ] * 255 );
447           }
448         }
449         cubeMap->setImage(osg::TextureCubeMap::POSITIVE_Y, image);
450
451         image = new osg::Image;
452         image->allocateImage(size, size, 1, GL_RGB, GL_UNSIGNED_BYTE);
453         ptr = image->data(0, 0);
454         for (int j = 0; j < size; j++ ) {
455           for (int i = 0; i < size; i++ ) {
456             osg::Vec3 tmp(( i + offset - half_size ), -half_size,
457                           -( j + offset - half_size ) );
458             tmp.normalize();
459             tmp = tmp*0.5 - zero_normal;
460
461             *ptr++ = (unsigned char)( tmp[ 0 ] * 255 );
462             *ptr++ = (unsigned char)( tmp[ 1 ] * 255 );
463             *ptr++ = (unsigned char)( tmp[ 2 ] * 255 );
464           }
465         }
466         cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_Y, image);
467
468         image = new osg::Image;
469         image->allocateImage(size, size, 1, GL_RGB, GL_UNSIGNED_BYTE);
470         ptr = image->data(0, 0);
471         for (int j = 0; j < size; j++ ) {
472           for (int i = 0; i < size; i++ ) {
473             osg::Vec3 tmp(( i + offset - half_size ),
474                           -( j + offset - half_size ), half_size );
475             tmp.normalize();
476             tmp = tmp*0.5 - zero_normal;
477             
478             *ptr++ = (unsigned char)( tmp[ 0 ] * 255 );
479             *ptr++ = (unsigned char)( tmp[ 1 ] * 255 );
480             *ptr++ = (unsigned char)( tmp[ 2 ] * 255 );
481           }
482         }
483         cubeMap->setImage(osg::TextureCubeMap::POSITIVE_Z, image);
484
485         image = new osg::Image;
486         image->allocateImage(size, size, 1, GL_RGB, GL_UNSIGNED_BYTE);
487         ptr = image->data(0, 0);
488         for (int j = 0; j < size; j++ ) {
489           for (int i = 0; i < size; i++ ) {
490             osg::Vec3 tmp(-( i + offset - half_size ),
491                           -( j + offset - half_size ), -half_size );
492             tmp.normalize();
493             tmp = tmp*0.5 - zero_normal;
494             *ptr++ = (unsigned char)( tmp[ 0 ] * 255 );
495             *ptr++ = (unsigned char)( tmp[ 1 ] * 255 );
496             *ptr++ = (unsigned char)( tmp[ 2 ] * 255 );
497           }
498         }
499         cubeMap->setImage(osg::TextureCubeMap::NEGATIVE_Z, image);
500
501         osg::StateSet* state;
502         state = SGMakeState(texture_path, "overcast.png", "overcast_n.png");
503         layer_states[SG_CLOUD_OVERCAST] = state;
504         state = SGMakeState(texture_path, "overcast_top.png", "overcast_top_n.png");
505         layer_states2[SG_CLOUD_OVERCAST] = state;
506         
507         state = SGMakeState(texture_path, "broken.png", "broken_n.png");
508         layer_states[SG_CLOUD_BROKEN] = state;
509         layer_states2[SG_CLOUD_BROKEN] = state;
510         
511         state = SGMakeState(texture_path, "scattered.png", "scattered_n.png");
512         layer_states[SG_CLOUD_SCATTERED] = state;
513         layer_states2[SG_CLOUD_SCATTERED] = state;
514         
515         state = SGMakeState(texture_path, "few.png", "few_n.png");
516         layer_states[SG_CLOUD_FEW] = state;
517         layer_states2[SG_CLOUD_FEW] = state;
518         
519         state = SGMakeState(texture_path, "cirrus.png", "cirrus_n.png");
520         layer_states[SG_CLOUD_CIRRUS] = state;
521         layer_states2[SG_CLOUD_CIRRUS] = state;
522         
523         layer_states[SG_CLOUD_CLEAR] = 0;
524         layer_states2[SG_CLOUD_CLEAR] = 0;
525 #if 1
526         // experimental optimization that may not make any difference
527         // at all :/
528         osg::CopyOp copyOp;
529         for (int i = 0; i < SG_MAX_CLOUD_COVERAGES; ++i) {
530             StateAttributeFactory *saf = StateAttributeFactory::instance();
531             if (layer_states[i].valid()) {
532                 if (layer_states[i] == layer_states2[i])
533                     layer_states2[i] = static_cast<osg::StateSet*>(layer_states[i]->clone(copyOp));
534                 layer_states[i]->setAttribute(saf ->getCullFaceFront());
535                 layer_states2[i]->setAttribute(saf ->getCullFaceBack());
536             }
537         }
538 #endif
539     }
540
541     scale = 4000.0;
542
543     setTextureOffset(base);
544     // build the cloud layer
545     const float layer_scale = layer_span / scale;
546     const float mpi = SG_PI/4;
547     
548     // caclculate the difference between a flat-earth model and 
549     // a round earth model given the span and altutude ASL of
550     // the cloud layer. This is the difference in altitude between
551     // the top of the inverted bowl and the edge of the bowl.
552     // const float alt_diff = layer_asl * 0.8;
553     const float layer_to_core = (SG_EARTH_RAD * 1000 + layer_asl);
554     const float layer_angle = 0.5*layer_span / layer_to_core; // The angle is half the span
555     const float border_to_core = layer_to_core * cos(layer_angle);
556     const float alt_diff = layer_to_core - border_to_core;
557     
558     for (int i = 0; i < 4; i++) {
559       if ( layer[i] != NULL ) {
560         layer_transform->removeChild(layer[i].get()); // automatic delete
561       }
562       
563       vl[i] = new osg::Vec3Array;
564       cl[i] = new osg::Vec4Array;
565       tl[i] = new osg::Vec2Array;
566       
567       
568       osg::Vec3 vertex(layer_span*(i-2)/2, -layer_span,
569                        alt_diff * (sin(i*mpi) - 2));
570       osg::Vec2 tc(layer_scale * i/4, 0.0f);
571       osg::Vec4 color(cloudColors[0], (i == 0) ? 0.0f : 0.15f);
572       
573       cl[i]->push_back(color);
574       vl[i]->push_back(vertex);
575       tl[i]->push_back(tc);
576       
577       for (int j = 0; j < 4; j++) {
578         vertex = osg::Vec3(layer_span*(i-1)/2, layer_span*(j-2)/2,
579                            alt_diff * (sin((i+1)*mpi) + sin(j*mpi) - 2));
580         tc = osg::Vec2(layer_scale * (i+1)/4, layer_scale * j/4);
581         color = osg::Vec4(cloudColors[0],
582                           ( (j == 0) || (i == 3)) ?  
583                           ( (j == 0) && (i == 3)) ? 0.0f : 0.15f : 1.0f );
584         
585         cl[i]->push_back(color);
586         vl[i]->push_back(vertex);
587         tl[i]->push_back(tc);
588         
589         vertex = osg::Vec3(layer_span*(i-2)/2, layer_span*(j-1)/2,
590                            alt_diff * (sin(i*mpi) + sin((j+1)*mpi) - 2) );
591         tc = osg::Vec2(layer_scale * i/4, layer_scale * (j+1)/4 );
592         color = osg::Vec4(cloudColors[0],
593                           ((j == 3) || (i == 0)) ?
594                           ((j == 3) && (i == 0)) ? 0.0f : 0.15f : 1.0f );
595         cl[i]->push_back(color);
596         vl[i]->push_back(vertex);
597         tl[i]->push_back(tc);
598       }
599       
600       vertex = osg::Vec3(layer_span*(i-1)/2, layer_span, 
601                          alt_diff * (sin((i+1)*mpi) - 2));
602       
603       tc = osg::Vec2(layer_scale * (i+1)/4, layer_scale);
604       
605       color = osg::Vec4(cloudColors[0], (i == 3) ? 0.0f : 0.15f );
606       
607       cl[i]->push_back( color );
608       vl[i]->push_back( vertex );
609       tl[i]->push_back( tc );
610       
611       osg::Geometry* geometry = new osg::Geometry;
612       geometry->setUseDisplayList(false);
613       geometry->setVertexArray(vl[i].get());
614       geometry->setNormalBinding(osg::Geometry::BIND_OFF);
615       geometry->setColorArray(cl[i].get());
616       geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
617       geometry->setTexCoordArray(0, tl[i].get());
618       geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl[i]->size()));
619       layer[i] = new osg::Geode;
620       
621       std::stringstream sstr;
622       sstr << "Cloud Layer (" << i << ")";
623       geometry->setName(sstr.str());
624       layer[i]->setName(sstr.str());
625       layer[i]->addDrawable(geometry);
626       layer_transform->addChild(layer[i].get());
627     }
628     
629     //OSGFIXME: true
630     if ( layer_states[layer_coverage].valid() ) {
631       osg::CopyOp copyOp;    // shallow copy
632       // render bin will be set in reposition
633       osg::StateSet* stateSet = static_cast<osg::StateSet*>(layer_states2[layer_coverage]->clone(copyOp));
634       stateSet->setDataVariance(osg::Object::DYNAMIC);
635       group_top->setStateSet(stateSet);
636       stateSet = static_cast<osg::StateSet*>(layer_states[layer_coverage]->clone(copyOp));
637       stateSet->setDataVariance(osg::Object::DYNAMIC);
638       group_bottom->setStateSet(stateSet);
639     }
640 }
641
642 // repaint the cloud layer colors
643 bool SGCloudLayer::repaint( const SGVec3f& fog_color ) {
644     osg::Vec4f combineColor(toOsg(fog_color), cloud_alpha);
645     osg::TexEnvCombine* combiner
646         = dynamic_cast<osg::TexEnvCombine*>(layer_root->getStateSet()
647                                             ->getTextureAttribute(1, osg::StateAttribute::TEXENV));
648     combiner->setConstantColor(combineColor);
649
650     // Set the fog color for the 3D clouds too.
651     //cloud3dfog->setColor(combineColor);
652     return true;
653 }
654
655 // reposition the cloud layer at the specified origin and orientation
656 // lon specifies a rotation about the Z axis
657 // lat specifies a rotation about the new Y axis
658 // spin specifies a rotation about the new Z axis (and orients the
659 // sunrise/set effects
660 bool SGCloudLayer::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
661                                double alt, double dt )
662 {
663   
664     if (getCoverage() != SGCloudLayer::SG_CLOUD_CLEAR)
665     {
666         // combine p and asl (meters) to get translation offset
667         osg::Vec3 asl_offset(toOsg(up));
668         asl_offset.normalize();
669         if ( alt <= layer_asl ) {
670             asl_offset *= layer_asl;
671         } else {
672             asl_offset *= layer_asl + layer_thickness;
673         }
674
675         // cout << "asl_offset = " << asl_offset[0] << "," << asl_offset[1]
676         //      << "," << asl_offset[2] << endl;
677         asl_offset += toOsg(p);
678         // cout << "  asl_offset = " << asl_offset[0] << "," << asl_offset[1]
679         //      << "," << asl_offset[2] << endl;
680
681         osg::Matrix T, LON, LAT;
682         // Translate to zero elevation
683         // Point3D zero_elev = current_view.get_cur_zero_elev();
684         T.makeTranslate( asl_offset );
685
686         // printf("  Translated to %.2f %.2f %.2f\n", 
687         //        zero_elev.x, zero_elev.y, zero_elev.z );
688
689         // Rotate to proper orientation
690         // printf("  lon = %.2f  lat = %.2f\n", 
691         //        lon * SGD_RADIANS_TO_DEGREES,
692         //        lat * SGD_RADIANS_TO_DEGREES);
693         LON.makeRotate(lon, osg::Vec3(0, 0, 1));
694
695         // xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
696         //             0.0, 1.0, 0.0 );
697         LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
698
699         layer_transform->setMatrix( LAT*LON*T );
700
701         // The layers need to be drawn in order because they are
702         // translucent, but OSG transparency sorting doesn't work because
703         // the cloud polys are huge. However, the ordering is simple: the
704         // bottom polys should be drawn from high altitude to low, and the
705         // top polygons from low to high. The altitude can be used
706         // directly to order the polygons!
707         group_bottom->getStateSet()->setRenderBinDetails(-(int)layer_asl,
708                                                          "RenderBin");
709         group_top->getStateSet()->setRenderBinDetails((int)layer_asl,
710                                                       "RenderBin");
711         if ( alt <= layer_asl ) {
712           layer_root->setSingleChildOn(0);
713         } else if ( alt >= layer_asl + layer_thickness ) {
714           layer_root->setSingleChildOn(1);
715         } else {
716           layer_root->setAllChildrenOff();
717         }
718             
719
720         // now calculate update texture coordinates
721         SGGeod pos = SGGeod::fromRad(lon, lat);
722         if ( last_pos == SGGeod() ) {
723             last_pos = pos;
724         }
725
726         double sp_dist = speed*dt;
727         
728         
729         if ( lon != last_pos.getLongitudeRad() || lat != last_pos.getLatitudeRad() || sp_dist != 0 ) {
730             double course = SGGeodesy::courseDeg(last_pos, pos) * SG_DEGREES_TO_RADIANS, 
731                 dist = SGGeodesy::distanceM(last_pos, pos);
732
733             // if start and dest are too close together,
734             // calc_gc_course_dist() can return a course of "nan".  If
735             // this happens, lets just use the last known good course.
736             // This is a hack, and it would probably be better to make
737             // calc_gc_course_dist() more robust.
738             if ( isnan(course) ) {
739                 course = last_course;
740             } else {
741                 last_course = course;
742             }
743
744             // calculate cloud movement due to external forces
745             double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0;
746
747             if (dist > 0.0) {
748                 ax = -cos(course) * dist;
749                 ay = sin(course) * dist;
750             }
751
752             if (sp_dist > 0) {
753                 bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
754                 by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
755             }
756
757
758             double xoff = (ax + bx) / (2 * scale);
759             double yoff = (ay + by) / (2 * scale);
760
761
762     //        const float layer_scale = layer_span / scale;
763
764             // cout << "xoff = " << xoff << ", yoff = " << yoff << endl;
765             base[0] += xoff;
766
767             // the while loops can lead to *long* pauses if base[0] comes
768             // with a bogus value.
769             // while ( base[0] > 1.0 ) { base[0] -= 1.0; }
770             // while ( base[0] < 0.0 ) { base[0] += 1.0; }
771             if ( base[0] > -10.0 && base[0] < 10.0 ) {
772                 base[0] -= (int)base[0];
773             } else {
774                 SG_LOG(SG_ASTRO, SG_DEBUG,
775                     "Error: base = " << base[0] << "," << base[1] <<
776                     " course = " << course << " dist = " << dist );
777                 base[0] = 0.0;
778             }
779
780             base[1] += yoff;
781             // the while loops can lead to *long* pauses if base[0] comes
782             // with a bogus value.
783             // while ( base[1] > 1.0 ) { base[1] -= 1.0; }
784             // while ( base[1] < 0.0 ) { base[1] += 1.0; }
785             if ( base[1] > -10.0 && base[1] < 10.0 ) {
786                 base[1] -= (int)base[1];
787             } else {
788                 SG_LOG(SG_ASTRO, SG_DEBUG,
789                         "Error: base = " << base[0] << "," << base[1] <<
790                         " course = " << course << " dist = " << dist );
791                 base[1] = 0.0;
792             }
793
794             // cout << "base = " << base[0] << "," << base[1] << endl;
795
796             setTextureOffset(base);
797             last_pos = pos;
798         }
799     }
800
801     layer3D->reposition( p, up, lon, lat, dt, layer_asl, speed, direction);
802     return true;
803 }
804
805 void SGCloudLayer::set_enable3dClouds(bool enable) {
806      
807     if (layer3D->isDefined3D() && enable) {
808         cloud_root->setChildValue(layer3D->getNode(), true);
809         cloud_root->setChildValue(layer_root.get(),   false);
810     } else {
811         cloud_root->setChildValue(layer3D->getNode(), false);
812         cloud_root->setChildValue(layer_root.get(),   true);
813     }
814 }