1 // oursun.hxx -- model earth's sun
3 // Written by Durk Talsma. Originally started October 1997, for distribution
4 // with the FlightGear project. Version 2 was written in August and
5 // September 1998. This code is based upon algorithms and data kindly
6 // provided by Mr. Paul Schlyter. (pausch@saaf.se).
8 // Separated out rendering pieces and converted to ssg by Curt Olson,
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Library General Public
12 // License as published by the Free Software Foundation; either
13 // version 2 of the License, or (at your option) any later version.
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Library General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 # include <simgear_config.h>
31 #include <simgear/compiler.h>
33 #include <osg/AlphaFunc>
34 #include <osg/BlendFunc>
37 #include <osg/Geometry>
38 #include <osg/Material>
39 #include <osg/ShadeModel>
41 #include <osg/Texture2D>
42 #include <osgDB/ReadFile>
44 #include <simgear/misc/PathOptions.hxx>
45 #include <simgear/screen/colors.hxx>
46 #include <simgear/scene/model/model.hxx>
49 using namespace simgear;
52 SGSun::SGSun( void ) {
53 prev_sun_angle = -9999.0;
59 SGSun::~SGSun( void ) {
63 // initialize the sun object and connect it into our scene graph root
65 SGSun::build( SGPath path, double sun_size, SGPropertyNode *property_tree_Node ) {
67 env_node = property_tree_Node;
69 osg::ref_ptr<osgDB::ReaderWriter::Options> options
70 = makeOptionsFromPath(path);
71 // build the ssg scene graph sub tree for the sky and connected
72 // into the provide scene graph branch
73 sun_transform = new osg::MatrixTransform;
74 osg::StateSet* stateSet = sun_transform->getOrCreateStateSet();
76 osg::TexEnv* texEnv = new osg::TexEnv;
77 texEnv->setMode(osg::TexEnv::MODULATE);
78 stateSet->setTextureAttribute(0, texEnv, osg::StateAttribute::ON);
80 osg::Material* material = new osg::Material;
81 material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
82 material->setEmission(osg::Material::FRONT_AND_BACK,
83 osg::Vec4(0, 0, 0, 1));
84 material->setSpecular(osg::Material::FRONT_AND_BACK,
85 osg::Vec4(0, 0, 0, 1));
86 stateSet->setAttribute(material);
88 osg::ShadeModel* shadeModel = new osg::ShadeModel;
89 shadeModel->setMode(osg::ShadeModel::SMOOTH);
90 stateSet->setAttributeAndModes(shadeModel);
92 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
93 alphaFunc->setFunction(osg::AlphaFunc::ALWAYS);
94 stateSet->setAttributeAndModes(alphaFunc);
96 osg::BlendFunc* blendFunc = new osg::BlendFunc;
97 blendFunc->setSource(osg::BlendFunc::SRC_ALPHA);
98 blendFunc->setDestination(osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
99 stateSet->setAttributeAndModes(blendFunc);
101 stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
102 stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
103 stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
104 stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
106 osg::Geode* geode = new osg::Geode;
107 stateSet = geode->getOrCreateStateSet();
109 stateSet->setRenderBinDetails(-6, "RenderBin");
111 // set up the sun-state
112 osg::Texture2D* texture = SGLoadTexture2D("sun.rgba", options.get());
113 stateSet->setTextureAttributeAndModes(0, texture);
116 sun_cl = new osg::Vec4Array;
117 sun_cl->push_back(osg::Vec4(1, 1, 1, 1));
119 osg::Vec3Array* sun_vl = new osg::Vec3Array;
120 sun_vl->push_back(osg::Vec3(-sun_size, 0, -sun_size));
121 sun_vl->push_back(osg::Vec3(sun_size, 0, -sun_size));
122 sun_vl->push_back(osg::Vec3(-sun_size, 0, sun_size));
123 sun_vl->push_back(osg::Vec3(sun_size, 0, sun_size));
125 osg::Vec2Array* sun_tl = new osg::Vec2Array;
126 sun_tl->push_back(osg::Vec2(0, 0));
127 sun_tl->push_back(osg::Vec2(1, 0));
128 sun_tl->push_back(osg::Vec2(0, 1));
129 sun_tl->push_back(osg::Vec2(1, 1));
131 osg::Geometry* geometry = new osg::Geometry;
132 geometry->setUseDisplayList(false);
133 geometry->setVertexArray(sun_vl);
134 geometry->setColorArray(sun_cl.get());
135 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
136 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
137 geometry->setTexCoordArray(0, sun_tl);
138 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
139 geode->addDrawable(geometry);
141 sun_transform->addChild( geode );
143 // set up the inner-halo state
144 geode = new osg::Geode;
145 stateSet = geode->getOrCreateStateSet();
146 stateSet->setRenderBinDetails(-7, "RenderBin");
148 texture = SGLoadTexture2D("inner_halo.rgba", options.get());
149 stateSet->setTextureAttributeAndModes(0, texture);
151 // Build ssg structure
152 ihalo_cl = new osg::Vec4Array;
153 ihalo_cl->push_back(osg::Vec4(1, 1, 1, 1));
155 float ihalo_size = sun_size * 2.0;
156 osg::Vec3Array* ihalo_vl = new osg::Vec3Array;
157 ihalo_vl->push_back(osg::Vec3(-ihalo_size, 0, -ihalo_size));
158 ihalo_vl->push_back(osg::Vec3(ihalo_size, 0, -ihalo_size));
159 ihalo_vl->push_back(osg::Vec3(-ihalo_size, 0, ihalo_size));
160 ihalo_vl->push_back(osg::Vec3(ihalo_size, 0, ihalo_size));
162 osg::Vec2Array* ihalo_tl = new osg::Vec2Array;
163 ihalo_tl->push_back(osg::Vec2(0, 0));
164 ihalo_tl->push_back(osg::Vec2(1, 0));
165 ihalo_tl->push_back(osg::Vec2(0, 1));
166 ihalo_tl->push_back(osg::Vec2(1, 1));
168 geometry = new osg::Geometry;
169 geometry->setUseDisplayList(false);
170 geometry->setVertexArray(ihalo_vl);
171 geometry->setColorArray(ihalo_cl.get());
172 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
173 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
174 geometry->setTexCoordArray(0, ihalo_tl);
175 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
176 geode->addDrawable(geometry);
178 sun_transform->addChild( geode );
180 // set up the outer halo state
182 geode = new osg::Geode;
183 stateSet = geode->getOrCreateStateSet();
184 stateSet->setRenderBinDetails(-8, "RenderBin");
186 texture = SGLoadTexture2D("outer_halo.rgba", options.get());
187 stateSet->setTextureAttributeAndModes(0, texture);
189 // Build ssg structure
190 ohalo_cl = new osg::Vec4Array;
191 ohalo_cl->push_back(osg::Vec4(1, 1, 1, 1));
193 double ohalo_size = sun_size * 8.0;
194 osg::Vec3Array* ohalo_vl = new osg::Vec3Array;
195 ohalo_vl->push_back(osg::Vec3(-ohalo_size, 0, -ohalo_size));
196 ohalo_vl->push_back(osg::Vec3(ohalo_size, 0, -ohalo_size));
197 ohalo_vl->push_back(osg::Vec3(-ohalo_size, 0, ohalo_size));
198 ohalo_vl->push_back(osg::Vec3(ohalo_size, 0, ohalo_size));
200 osg::Vec2Array* ohalo_tl = new osg::Vec2Array;
201 ohalo_tl->push_back(osg::Vec2(0, 0));
202 ohalo_tl->push_back(osg::Vec2(1, 0));
203 ohalo_tl->push_back(osg::Vec2(0, 1));
204 ohalo_tl->push_back(osg::Vec2(1, 1));
206 geometry = new osg::Geometry;
207 geometry->setUseDisplayList(false);
208 geometry->setVertexArray(ohalo_vl);
209 geometry->setColorArray(ohalo_cl.get());
210 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
211 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
212 geometry->setTexCoordArray(0, ohalo_tl);
213 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
214 geode->addDrawable(geometry);
216 sun_transform->addChild( geode );
219 // force a repaint of the sun colors with arbitrary defaults
222 return sun_transform.get();
226 // repaint the sun colors based on current value of sun_angle in
227 // degrees relative to verticle
228 // 0 degrees = high noon
229 // 90 degrees = sun rise/set
230 // 180 degrees = darkest midnight
231 bool SGSun::repaint( double sun_angle, double new_visibility ) {
233 if ( visibility != new_visibility ) {
234 visibility = new_visibility;
236 static const double sqrt_m_log01 = sqrt( -log( 0.01 ) );
237 sun_exp2_punch_through = sqrt_m_log01 / ( visibility * 15 );
240 if ( prev_sun_angle != sun_angle ) {
241 prev_sun_angle = sun_angle;
243 // determine how much aerosols are in the air (rough guess)
244 double aerosol_factor;
245 if ( visibility < 100 ){
246 aerosol_factor = 8000;
249 aerosol_factor = 80.5 / log( visibility / 100 );
252 // get environmental data from property tree or use defaults
253 double rel_humidity, density_avg;
262 rel_humidity = env_node->getFloatValue( "relative-humidity" );
263 density_avg = env_node->getFloatValue( "atmosphere/density-tropo-avg" );
266 // ok, now let's go and generate the sun color
267 osg::Vec4 i_halo_color, o_halo_color, sun_color;
270 // When the sunangle changes, light has to travel a longer distance through the atmosphere.
271 // So it's scattered more due to raleigh scattering, which affects blue more than green light.
272 // Red is almost not scattered and effectively only get's touched when the sun is near the horizon.
273 // Visability also affects suncolor inasmuch as more particles are in the air that cause more scattering.
274 // We base our calculation on the halo's color, which is most scattered.
276 // Red - is almost not scattered
279 double red_scat_f = ( aerosol_factor * path_distance * density_avg ) / 5E+07;
280 sun_color[0] = 1 - red_scat_f;
281 i_halo_color[0] = 1 - ( 1.1 * red_scat_f );
282 o_halo_color[0] = 1 - ( 1.4 * red_scat_f );
285 double green_scat_f = ( aerosol_factor * path_distance * density_avg ) / 8.8938E+06;
286 sun_color[1] = 1 - green_scat_f;
287 i_halo_color[1] = 1 - ( 1.1 * green_scat_f );
288 o_halo_color[1] = 1 - ( 1.4 * green_scat_f );
291 double blue_scat_f = ( aerosol_factor * path_distance * density_avg ) / 3.607E+06;
292 sun_color[2] = 1 - blue_scat_f;
293 i_halo_color[2] = 1 - ( 1.1 * blue_scat_f );
294 o_halo_color[2] = 1 - ( 1.4 * blue_scat_f );
300 o_halo_color[3] = blue_scat_f;
301 if ( ( new_visibility < 10000 ) && ( blue_scat_f > 1 )){
302 o_halo_color[3] = 2 - blue_scat_f;
306 // Now that we have the color calculated
307 // let's consider the saturation which is produced by mie scattering
308 double saturation = 1 - ( rel_humidity / 200 );
309 sun_color[1] += (( 1 - saturation ) * ( 1 - sun_color[1] ));
310 sun_color[2] += (( 1 - saturation ) * ( 1 - sun_color[2] ));
312 i_halo_color[1] += (( 1 - saturation ) * ( 1 - i_halo_color[1] ));
313 i_halo_color[2] += (( 1 - saturation ) * ( 1 - i_halo_color[2] ));
315 o_halo_color[1] += (( 1 - saturation ) * ( 1 - o_halo_color[1] ));
316 o_halo_color[2] += (( 1 - saturation ) * ( 1 - o_halo_color[2] ));
318 // just to make sure we're in the limits
319 if ( sun_color[0] < 0 ) sun_color[0] = 0;
320 else if ( sun_color[0] > 1) sun_color[0] = 1;
321 if ( i_halo_color[0] < 0 ) i_halo_color[0] = 0;
322 else if ( i_halo_color[0] > 1) i_halo_color[0] = 1;
323 if ( o_halo_color[0] < 0 ) o_halo_color[0] = 0;
324 else if ( o_halo_color[0] > 1) o_halo_color[0] = 1;
326 if ( sun_color[1] < 0 ) sun_color[1] = 0;
327 else if ( sun_color[1] > 1) sun_color[1] = 1;
328 if ( i_halo_color[1] < 0 ) i_halo_color[1] = 0;
329 else if ( i_halo_color[1] > 1) i_halo_color[1] = 1;
330 if ( o_halo_color[1] < 0 ) o_halo_color[1] = 0;
331 else if ( o_halo_color[1] > 1) o_halo_color[1] = 1;
333 if ( sun_color[2] < 0 ) sun_color[2] = 0;
334 else if ( sun_color[2] > 1) sun_color[2] = 1;
335 if ( i_halo_color[2] < 0 ) i_halo_color[2] = 0;
336 else if ( i_halo_color[2] > 1) i_halo_color[2] = 1;
337 if ( o_halo_color[2] < 0 ) o_halo_color[2] = 0;
338 else if ( o_halo_color[2] > 1) o_halo_color[2] = 1;
339 if ( o_halo_color[3] < 0 ) o_halo_color[2] = 0;
340 else if ( o_halo_color[3] > 1) o_halo_color[3] = 1;
343 gamma_correct_rgb( i_halo_color._v );
344 gamma_correct_rgb( o_halo_color._v );
345 gamma_correct_rgb( sun_color._v );
347 (*sun_cl)[0] = sun_color;
349 (*ihalo_cl)[0] = i_halo_color;
351 (*ohalo_cl)[0] = o_halo_color;
359 // reposition the sun at the specified right ascension and
360 // declination, offset by our current position (p) so that it appears
361 // fixed at a great distance from the viewer. Also add in an optional
362 // rotation (i.e. for the current time of day.)
363 // Then calculate stuff needed for the sun-coloring
364 bool SGSun::reposition( const SGVec3f& p, double angle,
365 double rightAscension, double declination,
366 double sun_dist, double lat, double alt_asl, double sun_angle)
368 // GST - GMT sidereal time
369 osg::Matrix T1, T2, GST, RA, DEC;
371 T1.makeTranslate(p.osg());
372 GST.makeRotate(SGD_DEGREES_TO_RADIANS*angle, osg::Vec3(0, 0, -1));
374 // xglRotatef( ((SGD_RADIANS_TO_DEGREES * rightAscension)- 90.0),
376 RA.makeRotate(rightAscension - 90*SGD_DEGREES_TO_RADIANS, osg::Vec3(0, 0, 1));
378 // xglRotatef((SGD_RADIANS_TO_DEGREES * declination), 1.0, 0.0, 0.0);
379 DEC.makeRotate(declination, osg::Vec3(1, 0, 0));
381 // xglTranslatef(0,sun_dist);
382 T2.makeTranslate(osg::Vec3(0, sun_dist, 0));
384 sun_transform->setMatrix(T2*DEC*RA*GST*T1);
386 // Suncolor related things:
387 if ( prev_sun_angle != sun_angle ) {
388 if ( sun_angle == 0 ) sun_angle = 0.1;
389 const double r_earth_pole = 6356752.314;
390 const double r_tropo_pole = 6356752.314 + 8000;
391 const double epsilon_earth2 = 6.694380066E-3;
392 const double epsilon_tropo2 = 9.170014946E-3;
394 double r_tropo = r_tropo_pole / sqrt ( 1 - ( epsilon_tropo2 * pow ( cos( lat ), 2 )));
395 double r_earth = r_earth_pole / sqrt ( 1 - ( epsilon_earth2 * pow ( cos( lat ), 2 )));
397 double position_radius = r_earth + alt_asl;
399 double gamma = SG_PI - sun_angle;
400 double sin_beta = ( position_radius * sin ( gamma ) ) / r_tropo;
401 double alpha = SG_PI - gamma - asin( sin_beta );
403 // OK, now let's calculate the distance the light travels
404 path_distance = sqrt( pow( position_radius, 2 ) + pow( r_tropo, 2 )
405 - ( 2 * position_radius * r_tropo * cos( alpha ) ));
407 double alt_half = sqrt( pow ( r_tropo, 2 ) + pow( path_distance / 2, 2 ) - r_tropo * path_distance * cos( asin( sin_beta )) ) - r_earth;
409 if ( alt_half < 0.0 ) alt_half = 0.0;
411 // Push the data to the property tree, so it can be used in the enviromental code
413 env_node->setDoubleValue( "atmosphere/altitude-troposphere-top", r_tropo - r_earth );
414 env_node->setDoubleValue( "atmosphere/altitude-half-to-sun", alt_half );
424 return SGVec4f((*sun_cl)[0][0], (*sun_cl)[0][1], (*sun_cl)[0][2], (*sun_cl)[0][3]);