1 // cloud.cxx -- model a single cloud layer
3 // Written by Curtis Olson, started June 2000.
5 // Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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.
23 # include <simgear_config.h>
26 #include <simgear/compiler.h>
32 #include <simgear/structure/OSGVersion.hxx>
33 #include <osg/AlphaFunc>
34 #include <osg/BlendFunc>
35 #include <osg/CullFace>
37 #include <osg/Geometry>
38 #include <osg/Material>
39 #include <osg/ShadeModel>
41 #include <osg/TexEnvCombine>
42 #include <osg/Texture2D>
46 #include <simgear/math/sg_random.h>
47 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
48 #include <simgear/debug/logstream.hxx>
49 #include <simgear/scene/model/model.hxx>
50 #include <simgear/scene/util/RenderConstants.hxx>
51 #include <simgear/scene/util/OsgMath.hxx>
52 #include <simgear/scene/util/StateAttributeFactory.hxx>
53 #include <simgear/screen/extensions.hxx>
55 #include "newcloud.hxx"
56 #include "cloudfield.hxx"
59 using namespace simgear;
62 static osg::ref_ptr<osg::StateSet> layer_states[SGCloudLayer::SG_MAX_CLOUD_COVERAGES];
63 static osg::ref_ptr<osg::StateSet> layer_states2[SGCloudLayer::SG_MAX_CLOUD_COVERAGES];
64 static bool state_initialized = false;
66 const std::string SGCloudLayer::SG_CLOUD_OVERCAST_STRING = "overcast";
67 const std::string SGCloudLayer::SG_CLOUD_BROKEN_STRING = "broken";
68 const std::string SGCloudLayer::SG_CLOUD_SCATTERED_STRING = "scattered";
69 const std::string SGCloudLayer::SG_CLOUD_FEW_STRING = "few";
70 const std::string SGCloudLayer::SG_CLOUD_CIRRUS_STRING = "cirrus";
71 const std::string SGCloudLayer::SG_CLOUD_CLEAR_STRING = "clear";
73 // make an StateSet for a cloud layer given the named texture
75 SGMakeState(const SGPath &path, const char* colorTexture,
76 const char* normalTexture)
78 osg::StateSet *stateSet = new osg::StateSet;
80 osg::ref_ptr<SGReaderWriterOptions> options;
81 options = SGReaderWriterOptions::fromPath(path.str());
82 stateSet->setTextureAttribute(0, SGLoadTexture2D(colorTexture,
84 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
85 StateAttributeFactory* attribFactory = StateAttributeFactory::instance();
86 stateSet->setAttributeAndModes(attribFactory->getSmoothShadeModel());
87 stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
88 stateSet->setAttributeAndModes(attribFactory->getStandardAlphaFunc());
89 stateSet->setAttributeAndModes(attribFactory->getStandardBlendFunc());
90 stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
96 SGCloudLayer::SGCloudLayer( const string &tex_path ) :
97 cloud_root(new osg::Switch),
98 layer_root(new osg::Switch),
99 group_top(new osg::Group),
100 group_bottom(new osg::Group),
101 layer_transform(new osg::MatrixTransform),
103 texture_path(tex_path),
106 layer_thickness(0.0),
107 layer_transition(0.0),
108 layer_visibility(25.0),
109 layer_coverage(SG_CLOUD_CLEAR),
117 // Render bottoms before the rest of transparent objects (rendered
118 // in bin 10), tops after. The negative numbers on the bottoms
119 // RenderBins and the positive numbers on the tops enforce this
121 cloud_root->addChild(layer_root.get(), true);
122 layer_root->addChild(group_bottom.get());
123 layer_root->addChild(group_top.get());
124 osg::StateSet *rootSet = layer_root->getOrCreateStateSet();
125 rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
126 rootSet->setTextureAttribute(0, new osg::TexMat);
127 rootSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
128 // Combiner for fog color and cloud alpha
129 osg::TexEnvCombine* combine0 = new osg::TexEnvCombine;
130 osg::TexEnvCombine* combine1 = new osg::TexEnvCombine;
131 combine0->setCombine_RGB(osg::TexEnvCombine::MODULATE);
132 combine0->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);
133 combine0->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
134 combine0->setSource1_RGB(osg::TexEnvCombine::TEXTURE0);
135 combine0->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
136 combine0->setCombine_Alpha(osg::TexEnvCombine::MODULATE);
137 combine0->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);
138 combine0->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
139 combine0->setSource1_Alpha(osg::TexEnvCombine::TEXTURE0);
140 combine0->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);
142 combine1->setCombine_RGB(osg::TexEnvCombine::MODULATE);
143 combine1->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);
144 combine1->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
145 combine1->setSource1_RGB(osg::TexEnvCombine::CONSTANT);
146 combine1->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
147 combine1->setCombine_Alpha(osg::TexEnvCombine::MODULATE);
148 combine1->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS);
149 combine1->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
150 combine1->setSource1_Alpha(osg::TexEnvCombine::CONSTANT);
151 combine1->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);
152 combine1->setDataVariance(osg::Object::DYNAMIC);
153 rootSet->setTextureAttributeAndModes(0, combine0);
154 rootSet->setTextureAttributeAndModes(1, combine1);
155 rootSet->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::ON);
156 rootSet->setTextureAttributeAndModes(1, StateAttributeFactory::instance()
158 osg::StateAttribute::ON);
159 rootSet->setDataVariance(osg::Object::DYNAMIC);
161 // Ensure repeatability of the random seed within 10 minutes,
162 // to keep multi-computer systems in sync.
163 sg_srandom_time_10();
164 base = osg::Vec2(sg_random(), sg_random());
165 group_top->addChild(layer_transform.get());
166 group_bottom->addChild(layer_transform.get());
168 layer3D = new SGCloudField();
169 cloud_root->addChild(layer3D->getNode(), false);
175 SGCloudLayer::~SGCloudLayer()
181 SGCloudLayer::getSpan_m () const
187 SGCloudLayer::setSpan_m (float span_m)
189 if (span_m != layer_span) {
196 SGCloudLayer::getElevation_m () const
202 SGCloudLayer::setElevation_m (float elevation_m, bool set_span)
204 layer_asl = elevation_m;
207 if (elevation_m > 4000)
208 setSpan_m( elevation_m * 10 );
215 SGCloudLayer::getThickness_m () const
217 return layer_thickness;
221 SGCloudLayer::setThickness_m (float thickness_m)
223 layer_thickness = thickness_m;
227 SGCloudLayer::getVisibility_m() const
229 return layer_visibility;
233 SGCloudLayer::setVisibility_m (float visibility_m)
235 layer_visibility = visibility_m;
239 SGCloudLayer::getTransition_m () const
241 return layer_transition;
245 SGCloudLayer::setTransition_m (float transition_m)
247 layer_transition = transition_m;
250 SGCloudLayer::Coverage
251 SGCloudLayer::getCoverage () const
253 return layer_coverage;
257 SGCloudLayer::setCoverage (Coverage coverage)
259 if (coverage != layer_coverage) {
260 layer_coverage = coverage;
266 SGCloudLayer::getCoverageString( Coverage coverage )
269 case SG_CLOUD_OVERCAST:
270 return SG_CLOUD_OVERCAST_STRING;
271 case SG_CLOUD_BROKEN:
272 return SG_CLOUD_BROKEN_STRING;
273 case SG_CLOUD_SCATTERED:
274 return SG_CLOUD_SCATTERED_STRING;
276 return SG_CLOUD_FEW_STRING;
277 case SG_CLOUD_CIRRUS:
278 return SG_CLOUD_CIRRUS_STRING;
281 return SG_CLOUD_CLEAR_STRING;
285 SGCloudLayer::Coverage
286 SGCloudLayer::getCoverageType( const std::string & coverage )
288 if( SG_CLOUD_OVERCAST_STRING == coverage ) {
289 return SG_CLOUD_OVERCAST;
290 } else if( SG_CLOUD_BROKEN_STRING == coverage ) {
291 return SG_CLOUD_BROKEN;
292 } else if( SG_CLOUD_SCATTERED_STRING == coverage ) {
293 return SG_CLOUD_SCATTERED;
294 } else if( SG_CLOUD_FEW_STRING == coverage ) {
296 } else if( SG_CLOUD_CIRRUS_STRING == coverage ) {
297 return SG_CLOUD_CIRRUS;
299 return SG_CLOUD_CLEAR;
304 SGCloudLayer::getCoverageString() const
306 return getCoverageString(layer_coverage);
310 SGCloudLayer::setCoverageString( const std::string & coverage )
312 setCoverage( getCoverageType(coverage) );
316 SGCloudLayer::setTextureOffset(const osg::Vec2& offset)
318 osg::StateAttribute* attr = layer_root->getStateSet()
319 ->getTextureAttribute(0, osg::StateAttribute::TEXMAT);
320 osg::TexMat* texMat = dynamic_cast<osg::TexMat*>(attr);
323 texMat->setMatrix(osg::Matrix::translate(offset[0], offset[1], 0.0));
326 // colors for debugging the cloud layers
328 Vec3 cloudColors[] = {Vec3(1.0f, 1.0f, 1.0f), Vec3(1.0f, 0.0f, 0.0f),
329 Vec3(0.0f, 1.0f, 0.0f), Vec3(0.0f, 0.0f, 1.0f)};
331 Vec3 cloudColors[] = {Vec3(1.0f, 1.0f, 1.0f), Vec3(1.0f, 1.0f, 1.0f),
332 Vec3(1.0f, 1.0f, 1.0f), Vec3(1.0f, 1.0f, 1.0f)};
335 // build the cloud object
337 SGCloudLayer::rebuild()
339 // Initialize states and sizes if necessary.
340 if ( !state_initialized ) {
341 state_initialized = true;
343 SG_LOG(SG_ASTRO, SG_INFO, "initializing cloud layers");
345 osg::StateSet* state;
346 state = SGMakeState(texture_path, "overcast.png", "overcast_n.png");
347 layer_states[SG_CLOUD_OVERCAST] = state;
348 state = SGMakeState(texture_path, "overcast_top.png", "overcast_top_n.png");
349 layer_states2[SG_CLOUD_OVERCAST] = state;
351 state = SGMakeState(texture_path, "broken.png", "broken_n.png");
352 layer_states[SG_CLOUD_BROKEN] = state;
353 layer_states2[SG_CLOUD_BROKEN] = state;
355 state = SGMakeState(texture_path, "scattered.png", "scattered_n.png");
356 layer_states[SG_CLOUD_SCATTERED] = state;
357 layer_states2[SG_CLOUD_SCATTERED] = state;
359 state = SGMakeState(texture_path, "few.png", "few_n.png");
360 layer_states[SG_CLOUD_FEW] = state;
361 layer_states2[SG_CLOUD_FEW] = state;
363 state = SGMakeState(texture_path, "cirrus.png", "cirrus_n.png");
364 layer_states[SG_CLOUD_CIRRUS] = state;
365 layer_states2[SG_CLOUD_CIRRUS] = state;
367 layer_states[SG_CLOUD_CLEAR] = 0;
368 layer_states2[SG_CLOUD_CLEAR] = 0;
370 // experimental optimization that may not make any difference
373 for (int i = 0; i < SG_MAX_CLOUD_COVERAGES; ++i) {
374 StateAttributeFactory *saf = StateAttributeFactory::instance();
375 if (layer_states[i].valid()) {
376 if (layer_states[i] == layer_states2[i])
377 layer_states2[i] = static_cast<osg::StateSet*>(layer_states[i]->clone(copyOp));
378 layer_states[i]->setAttribute(saf ->getCullFaceFront());
379 layer_states2[i]->setAttribute(saf ->getCullFaceBack());
387 setTextureOffset(base);
388 // build the cloud layer
389 const float layer_scale = layer_span / scale;
390 const float mpi = SG_PI/4;
392 // caclculate the difference between a flat-earth model and
393 // a round earth model given the span and altutude ASL of
394 // the cloud layer. This is the difference in altitude between
395 // the top of the inverted bowl and the edge of the bowl.
396 // const float alt_diff = layer_asl * 0.8;
397 const float layer_to_core = (SG_EARTH_RAD * 1000 + layer_asl);
398 const float layer_angle = 0.5*layer_span / layer_to_core; // The angle is half the span
399 const float border_to_core = layer_to_core * cos(layer_angle);
400 const float alt_diff = layer_to_core - border_to_core;
402 for (int i = 0; i < 4; i++) {
403 if ( layer[i] != NULL ) {
404 layer_transform->removeChild(layer[i].get()); // automatic delete
407 vl[i] = new osg::Vec3Array;
408 cl[i] = new osg::Vec4Array;
409 tl[i] = new osg::Vec2Array;
412 osg::Vec3 vertex(layer_span*(i-2)/2, -layer_span,
413 alt_diff * (sin(i*mpi) - 2));
414 osg::Vec2 tc(layer_scale * i/4, 0.0f);
415 osg::Vec4 color(cloudColors[0], (i == 0) ? 0.0f : 0.15f);
417 cl[i]->push_back(color);
418 vl[i]->push_back(vertex);
419 tl[i]->push_back(tc);
421 for (int j = 0; j < 4; j++) {
422 vertex = osg::Vec3(layer_span*(i-1)/2, layer_span*(j-2)/2,
423 alt_diff * (sin((i+1)*mpi) + sin(j*mpi) - 2));
424 tc = osg::Vec2(layer_scale * (i+1)/4, layer_scale * j/4);
425 color = osg::Vec4(cloudColors[0],
426 ( (j == 0) || (i == 3)) ?
427 ( (j == 0) && (i == 3)) ? 0.0f : 0.15f : 1.0f );
429 cl[i]->push_back(color);
430 vl[i]->push_back(vertex);
431 tl[i]->push_back(tc);
433 vertex = osg::Vec3(layer_span*(i-2)/2, layer_span*(j-1)/2,
434 alt_diff * (sin(i*mpi) + sin((j+1)*mpi) - 2) );
435 tc = osg::Vec2(layer_scale * i/4, layer_scale * (j+1)/4 );
436 color = osg::Vec4(cloudColors[0],
437 ((j == 3) || (i == 0)) ?
438 ((j == 3) && (i == 0)) ? 0.0f : 0.15f : 1.0f );
439 cl[i]->push_back(color);
440 vl[i]->push_back(vertex);
441 tl[i]->push_back(tc);
444 vertex = osg::Vec3(layer_span*(i-1)/2, layer_span,
445 alt_diff * (sin((i+1)*mpi) - 2));
447 tc = osg::Vec2(layer_scale * (i+1)/4, layer_scale);
449 color = osg::Vec4(cloudColors[0], (i == 3) ? 0.0f : 0.15f );
451 cl[i]->push_back( color );
452 vl[i]->push_back( vertex );
453 tl[i]->push_back( tc );
455 osg::Geometry* geometry = new osg::Geometry;
456 geometry->setUseDisplayList(false);
457 geometry->setVertexArray(vl[i].get());
458 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
459 geometry->setColorArray(cl[i].get(), osg::Array::BIND_PER_VERTEX);
460 geometry->setTexCoordArray(0, tl[i].get(), osg::Array::BIND_PER_VERTEX);
461 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, vl[i]->size()));
462 layer[i] = new osg::Geode;
464 std::stringstream sstr;
465 sstr << "Cloud Layer (" << i << ")";
466 geometry->setName(sstr.str());
467 layer[i]->setName(sstr.str());
468 layer[i]->addDrawable(geometry);
469 layer_transform->addChild(layer[i].get());
473 if ( layer_states[layer_coverage].valid() ) {
474 osg::CopyOp copyOp; // shallow copy
475 // render bin will be set in reposition
476 osg::StateSet* stateSet = static_cast<osg::StateSet*>(layer_states2[layer_coverage]->clone(copyOp));
477 stateSet->setDataVariance(osg::Object::DYNAMIC);
478 group_top->setStateSet(stateSet);
479 stateSet = static_cast<osg::StateSet*>(layer_states[layer_coverage]->clone(copyOp));
480 stateSet->setDataVariance(osg::Object::DYNAMIC);
481 group_bottom->setStateSet(stateSet);
485 // repaint the cloud layer colors
486 bool SGCloudLayer::repaint( const SGVec3f& fog_color ) {
487 osg::Vec4f combineColor(toOsg(fog_color), cloud_alpha);
488 osg::TexEnvCombine* combiner
489 = dynamic_cast<osg::TexEnvCombine*>(layer_root->getStateSet()
490 ->getTextureAttribute(1, osg::StateAttribute::TEXENV));
491 combiner->setConstantColor(combineColor);
493 // Set the fog color for the 3D clouds too.
494 //cloud3dfog->setColor(combineColor);
498 // reposition the cloud layer at the specified origin and orientation
499 // lon specifies a rotation about the Z axis
500 // lat specifies a rotation about the new Y axis
501 // spin specifies a rotation about the new Z axis (and orients the
502 // sunrise/set effects
503 bool SGCloudLayer::reposition( const SGVec3f& p,
511 if (getCoverage() != SGCloudLayer::SG_CLOUD_CLEAR)
513 // combine p and asl (meters) to get translation offset
514 osg::Vec3 asl_offset(toOsg(up));
515 asl_offset.normalize();
516 if ( alt <= layer_asl ) {
517 asl_offset *= layer_asl;
519 asl_offset *= layer_asl + layer_thickness;
522 // cout << "asl_offset = " << asl_offset[0] << "," << asl_offset[1]
523 // << "," << asl_offset[2] << endl;
524 asl_offset += toOsg(p);
525 // cout << " asl_offset = " << asl_offset[0] << "," << asl_offset[1]
526 // << "," << asl_offset[2] << endl;
528 osg::Matrix T, LON, LAT;
529 // Translate to zero elevation
530 // Point3D zero_elev = current_view.get_cur_zero_elev();
531 T.makeTranslate( asl_offset );
533 // printf(" Translated to %.2f %.2f %.2f\n",
534 // zero_elev.x, zero_elev.y, zero_elev.z );
536 // Rotate to proper orientation
537 // printf(" lon = %.2f lat = %.2f\n",
538 // lon * SGD_RADIANS_TO_DEGREES,
539 // lat * SGD_RADIANS_TO_DEGREES);
540 LON.makeRotate(lon, osg::Vec3(0, 0, 1));
542 // xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
544 LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
546 layer_transform->setMatrix( LAT*LON*T );
548 // The layers need to be drawn in order because they are
549 // translucent, but OSG transparency sorting doesn't work because
550 // the cloud polys are huge. However, the ordering is simple: the
551 // bottom polys should be drawn from high altitude to low, and the
552 // top polygons from low to high. The altitude can be used
553 // directly to order the polygons!
554 group_bottom->getStateSet()->setRenderBinDetails(-(int)layer_asl,
556 group_top->getStateSet()->setRenderBinDetails((int)layer_asl,
558 if ( alt <= layer_asl ) {
559 layer_root->setSingleChildOn(0);
560 } else if ( alt >= layer_asl + layer_thickness ) {
561 layer_root->setSingleChildOn(1);
563 layer_root->setAllChildrenOff();
567 // now calculate update texture coordinates
568 SGGeod pos = SGGeod::fromRad(lon, lat);
569 if ( last_pos == SGGeod() ) {
573 double sp_dist = speed*dt;
576 if ( lon != last_pos.getLongitudeRad() || lat != last_pos.getLatitudeRad() || sp_dist != 0 ) {
577 double course = SGGeodesy::courseDeg(last_pos, pos) * SG_DEGREES_TO_RADIANS,
578 dist = SGGeodesy::distanceM(last_pos, pos);
580 // if start and dest are too close together,
581 // calc_gc_course_dist() can return a course of "nan". If
582 // this happens, lets just use the last known good course.
583 // This is a hack, and it would probably be better to make
584 // calc_gc_course_dist() more robust.
585 if ( isNaN(course) ) {
586 course = last_course;
588 last_course = course;
591 // calculate cloud movement due to external forces
592 double ax = 0.0, ay = 0.0, bx = 0.0, by = 0.0;
595 ax = -cos(course) * dist;
596 ay = sin(course) * dist;
600 bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
601 by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
605 double xoff = (ax + bx) / (2 * scale);
606 double yoff = (ay + by) / (2 * scale);
609 // const float layer_scale = layer_span / scale;
611 // cout << "xoff = " << xoff << ", yoff = " << yoff << endl;
614 // the while loops can lead to *long* pauses if base[0] comes
615 // with a bogus value.
616 // while ( base[0] > 1.0 ) { base[0] -= 1.0; }
617 // while ( base[0] < 0.0 ) { base[0] += 1.0; }
618 if ( base[0] > -10.0 && base[0] < 10.0 ) {
619 base[0] -= (int)base[0];
621 SG_LOG(SG_ASTRO, SG_DEBUG,
622 "Error: base = " << base[0] << "," << base[1] <<
623 " course = " << course << " dist = " << dist );
628 // the while loops can lead to *long* pauses if base[0] comes
629 // with a bogus value.
630 // while ( base[1] > 1.0 ) { base[1] -= 1.0; }
631 // while ( base[1] < 0.0 ) { base[1] += 1.0; }
632 if ( base[1] > -10.0 && base[1] < 10.0 ) {
633 base[1] -= (int)base[1];
635 SG_LOG(SG_ASTRO, SG_DEBUG,
636 "Error: base = " << base[0] << "," << base[1] <<
637 " course = " << course << " dist = " << dist );
641 // cout << "base = " << base[0] << "," << base[1] << endl;
643 setTextureOffset(base);
648 layer3D->reposition( p, up, lon, lat, dt, layer_asl, speed, direction);
652 void SGCloudLayer::set_enable3dClouds(bool enable) {
654 if (layer3D->isDefined3D() && enable) {
655 cloud_root->setChildValue(layer3D->getNode(), true);
656 cloud_root->setChildValue(layer_root.get(), false);
658 cloud_root->setChildValue(layer3D->getNode(), false);
659 cloud_root->setChildValue(layer_root.get(), true);