]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/oursun.cxx
Merge branch 'ehofman/sound'
[simgear.git] / simgear / scene / sky / oursun.cxx
1 // oursun.hxx -- model earth's sun
2 //
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). 
7 //
8 // Separated out rendering pieces and converted to ssg by Curt Olson,
9 // March 2000
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.
14 //
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.
19 //
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.
23 //
24 // $Id$
25
26
27 #ifdef HAVE_CONFIG_H
28 #  include <simgear_config.h>
29 #endif
30
31 #include <simgear/compiler.h>
32
33 #include <osg/AlphaFunc>
34 #include <osg/BlendFunc>
35 #include <osg/Fog>
36 #include <osg/Geode>
37 #include <osg/Geometry>
38 #include <osg/Material>
39 #include <osg/ShadeModel>
40 #include <osg/TexEnv>
41 #include <osg/Texture2D>
42 #include <osgDB/ReadFile>
43
44 #include <simgear/math/SGMath.hxx>
45 #include <simgear/misc/PathOptions.hxx>
46 #include <simgear/screen/colors.hxx>
47 #include <simgear/scene/model/model.hxx>
48 #include "oursun.hxx"
49
50 using namespace simgear;
51
52 // Constructor
53 SGSun::SGSun( void ) :
54     visibility(-9999.0), prev_sun_angle(-9999.0), path_distance(60000.0),
55     sun_exp2_punch_through(7.0e-06)
56 {
57
58 }
59
60
61 // Destructor
62 SGSun::~SGSun( void ) {
63 }
64
65
66 // initialize the sun object and connect it into our scene graph root
67 osg::Node*
68 SGSun::build( SGPath path, double sun_size, SGPropertyNode *property_tree_Node ) {
69
70     env_node = property_tree_Node;
71
72     osg::ref_ptr<osgDB::ReaderWriter::Options> options
73         = makeOptionsFromPath(path);
74     // build the ssg scene graph sub tree for the sky and connected
75     // into the provide scene graph branch
76     sun_transform = new osg::MatrixTransform;
77     osg::StateSet* stateSet = sun_transform->getOrCreateStateSet();
78
79     osg::TexEnv* texEnv = new osg::TexEnv;
80     texEnv->setMode(osg::TexEnv::MODULATE);
81     stateSet->setTextureAttribute(0, texEnv, osg::StateAttribute::ON);
82  
83     osg::Material* material = new osg::Material;
84     material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
85     material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4(0,0,0,1));
86     material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(0,0,0,1));
87     stateSet->setAttribute(material);
88
89     osg::ShadeModel* shadeModel = new osg::ShadeModel;
90     shadeModel->setMode(osg::ShadeModel::SMOOTH);
91     stateSet->setAttributeAndModes(shadeModel);
92
93     osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
94     alphaFunc->setFunction(osg::AlphaFunc::ALWAYS);
95     stateSet->setAttributeAndModes(alphaFunc);
96
97     osg::BlendFunc* blendFunc = new osg::BlendFunc;
98     blendFunc->setSource(osg::BlendFunc::SRC_ALPHA);
99     blendFunc->setDestination(osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
100     stateSet->setAttributeAndModes(blendFunc);
101
102     stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
103     stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
104     stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
105     stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
106
107     osg::Geode* geode = new osg::Geode;
108     stateSet = geode->getOrCreateStateSet();
109
110     stateSet->setRenderBinDetails(-6, "RenderBin");
111
112     // set up the sun-state
113     osg::Texture2D* texture = SGLoadTexture2D("sun.png", options.get());
114     stateSet->setTextureAttributeAndModes(0, texture);
115
116     // Build scenegraph
117     sun_cl = new osg::Vec4Array;
118     sun_cl->push_back(osg::Vec4(1, 1, 1, 1));
119
120     scene_cl = new osg::Vec4Array;
121     scene_cl->push_back(osg::Vec4(1, 1, 1, 1));
122
123     osg::Vec3Array* sun_vl = new osg::Vec3Array;
124     sun_vl->push_back(osg::Vec3(-sun_size, 0, -sun_size));
125     sun_vl->push_back(osg::Vec3(sun_size, 0, -sun_size));
126     sun_vl->push_back(osg::Vec3(-sun_size, 0, sun_size));
127     sun_vl->push_back(osg::Vec3(sun_size, 0, sun_size));
128
129     osg::Vec2Array* sun_tl = new osg::Vec2Array;
130     sun_tl->push_back(osg::Vec2(0, 0));
131     sun_tl->push_back(osg::Vec2(1, 0));
132     sun_tl->push_back(osg::Vec2(0, 1));
133     sun_tl->push_back(osg::Vec2(1, 1));
134
135     osg::Geometry* geometry = new osg::Geometry;
136     geometry->setUseDisplayList(false);
137     geometry->setVertexArray(sun_vl);
138     geometry->setColorArray(sun_cl.get());
139     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
140     geometry->setNormalBinding(osg::Geometry::BIND_OFF);
141     geometry->setTexCoordArray(0, sun_tl);
142     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
143     geode->addDrawable(geometry);
144
145     sun_transform->addChild( geode );
146
147     // set up the inner-halo state
148     geode = new osg::Geode;
149     stateSet = geode->getOrCreateStateSet();
150     stateSet->setRenderBinDetails(-7, "RenderBin");
151     
152     texture = SGLoadTexture2D("inner_halo.png", options.get());
153     stateSet->setTextureAttributeAndModes(0, texture);
154
155     // Build ssg structure
156     ihalo_cl = new osg::Vec4Array;
157     ihalo_cl->push_back(osg::Vec4(1, 1, 1, 1));
158
159     float ihalo_size = sun_size * 2.0;
160     osg::Vec3Array* ihalo_vl = new osg::Vec3Array;
161     ihalo_vl->push_back(osg::Vec3(-ihalo_size, 0, -ihalo_size));
162     ihalo_vl->push_back(osg::Vec3(ihalo_size, 0, -ihalo_size));
163     ihalo_vl->push_back(osg::Vec3(-ihalo_size, 0, ihalo_size));
164     ihalo_vl->push_back(osg::Vec3(ihalo_size, 0, ihalo_size));
165
166     osg::Vec2Array* ihalo_tl = new osg::Vec2Array;
167     ihalo_tl->push_back(osg::Vec2(0, 0));
168     ihalo_tl->push_back(osg::Vec2(1, 0));
169     ihalo_tl->push_back(osg::Vec2(0, 1));
170     ihalo_tl->push_back(osg::Vec2(1, 1));
171
172     geometry = new osg::Geometry;
173     geometry->setUseDisplayList(false);
174     geometry->setVertexArray(ihalo_vl);
175     geometry->setColorArray(ihalo_cl.get());
176     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
177     geometry->setNormalBinding(osg::Geometry::BIND_OFF);
178     geometry->setTexCoordArray(0, ihalo_tl);
179     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
180     geode->addDrawable(geometry);
181
182     sun_transform->addChild( geode );
183     
184     // set up the outer halo state
185     
186     geode = new osg::Geode;
187     stateSet = geode->getOrCreateStateSet();
188     stateSet->setRenderBinDetails(-8, "RenderBin");
189
190     texture = SGLoadTexture2D("outer_halo.png", options.get());
191     stateSet->setTextureAttributeAndModes(0, texture);
192
193     // Build ssg structure
194     ohalo_cl = new osg::Vec4Array;
195     ohalo_cl->push_back(osg::Vec4(1, 1, 1, 1));
196
197     double ohalo_size = sun_size * 8.0;
198     osg::Vec3Array* ohalo_vl = new osg::Vec3Array;
199     ohalo_vl->push_back(osg::Vec3(-ohalo_size, 0, -ohalo_size));
200     ohalo_vl->push_back(osg::Vec3(ohalo_size, 0, -ohalo_size));
201     ohalo_vl->push_back(osg::Vec3(-ohalo_size, 0, ohalo_size));
202     ohalo_vl->push_back(osg::Vec3(ohalo_size, 0, ohalo_size));
203
204     osg::Vec2Array* ohalo_tl = new osg::Vec2Array;
205     ohalo_tl->push_back(osg::Vec2(0, 0));
206     ohalo_tl->push_back(osg::Vec2(1, 0));
207     ohalo_tl->push_back(osg::Vec2(0, 1));
208     ohalo_tl->push_back(osg::Vec2(1, 1));
209
210     geometry = new osg::Geometry;
211     geometry->setUseDisplayList(false);
212     geometry->setVertexArray(ohalo_vl);
213     geometry->setColorArray(ohalo_cl.get());
214     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
215     geometry->setNormalBinding(osg::Geometry::BIND_OFF);
216     geometry->setTexCoordArray(0, ohalo_tl);
217     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
218     geode->addDrawable(geometry);
219
220     sun_transform->addChild( geode );
221
222     // force a repaint of the sun colors with arbitrary defaults
223     repaint( 0.0, 1.0 );
224
225     return sun_transform.get();
226 }
227
228
229 // repaint the sun colors based on current value of sun_angle in
230 // degrees relative to verticle
231 // 0 degrees = high noon
232 // 90 degrees = sun rise/set
233 // 180 degrees = darkest midnight
234 bool SGSun::repaint( double sun_angle, double new_visibility ) {
235
236     if ( visibility != new_visibility ) {
237         if (new_visibility < 100.0) new_visibility = 100.0;
238         else if (new_visibility > 45000.0) new_visibility = 45000.0;
239         visibility = new_visibility;
240         sun_exp2_punch_through = 2.0/log(visibility);
241     }
242
243     if ( prev_sun_angle != sun_angle ) {
244         prev_sun_angle = sun_angle;
245
246         // determine how much aerosols are in the air (rough guess)
247         double aerosol_factor;
248         if ( visibility < 100 ) {
249             aerosol_factor = 8000;
250         }
251         else {
252             aerosol_factor = 80.5 / log( visibility / 99.9 );
253         }
254
255         // get environmental data from property tree or use defaults
256         double rel_humidity, density_avg;
257
258         if ( !env_node ) {
259             rel_humidity = 0.5;
260             density_avg = 0.7;
261         }
262         else {
263             rel_humidity = env_node->getFloatValue( "relative-humidity" ); 
264             density_avg =  env_node->getFloatValue( "atmosphere/density-tropo-avg" );
265         }
266
267         // ok, now let's go and generate the sun and scene color
268         osg::Vec4 i_halo_color, o_halo_color, scene_color, sun_color;
269
270         // Some comments: 
271         // * When the sunangle changes, light has to travel a longer
272         //   distance through the atmosphere. So it's scattered more due
273         //   to raleigh scattering, which affects blue more than green
274         //   light.
275         // * Red is almost not scattered and effectively only get's
276         //   touched when the sun is near the horizon.
277         // * Visability also affects suncolor inasmuch as more particles
278         //   are in the air that cause more scattering.
279         // * We base our calculation on the halo's color, which is most
280         //   scattered. 
281         double red_scat_f, red_scat_corr_f, green_scat_f, blue_scat_f;
282  
283         // Red - is almost not scattered     
284         // Lambda is 700 nm
285         
286         red_scat_f = (aerosol_factor * path_distance * density_avg)/5E+07;
287         red_scat_corr_f = sun_exp2_punch_through / (1 - red_scat_f);
288         sun_color[0] = 1;
289         scene_color[0] = 1 - red_scat_f;
290
291         // Green - 546.1 nm
292         green_scat_f = (aerosol_factor * path_distance * density_avg)/8.8938E+06;
293         sun_color[1] = 1 - green_scat_f * red_scat_corr_f;
294         scene_color[1] = 1 - green_scat_f;
295  
296         // Blue - 435.8 nm
297         blue_scat_f = (aerosol_factor * path_distance * density_avg)/3.607E+06;
298         sun_color[2] = 1 - blue_scat_f * red_scat_corr_f;
299         scene_color[2] = 1 - blue_scat_f;
300
301         // Alpha
302         sun_color[3] = 1;
303         scene_color[3] = 1;
304
305         // Now that we have the color calculated 
306         // let's consider the saturation which is produced by mie scattering
307         double saturation = 1 - ( rel_humidity / 200 );
308         scene_color[1] += (( 1 - saturation ) * ( 1 - scene_color[1] ));
309         scene_color[2] += (( 1 - saturation ) * ( 1 - scene_color[2] ));
310
311         if (sun_color[0] > 1.0) sun_color[0] = 1.0;
312         if (sun_color[0] < 0.0) sun_color[0] = 0.0;
313         if (sun_color[1] > 1.0) sun_color[1] = 1.0;
314         if (sun_color[1] < 0.0) sun_color[1] = 0.0;
315         if (sun_color[2] > 1.0) sun_color[2] = 1.0;
316         if (sun_color[2] < 0.0) sun_color[2] = 0.0;
317
318         if (scene_color[0] > 1.0) scene_color[0] = 1.0;
319         if (scene_color[0] < 0.0) scene_color[0] = 0.0;
320         if (scene_color[1] > 1.0) scene_color[1] = 1.0;
321         if (scene_color[1] < 0.0) scene_color[1] = 0.0;
322         if (scene_color[2] > 1.0) scene_color[2] = 1.0;
323         if (scene_color[2] < 0.0) scene_color[2] = 0.0;
324
325         double scene_f = 0.5 * (1 / (1 - red_scat_f));
326         double sun_f = 1.0 - scene_f;
327         i_halo_color[0] = sun_f * sun_color[0] + scene_f * scene_color[0];
328         i_halo_color[1] = sun_f * sun_color[1] + scene_f * scene_color[1];
329         i_halo_color[2] = sun_f * sun_color[2] + scene_f * scene_color[2];
330         i_halo_color[3] = 1;
331
332         o_halo_color[0] = 0.2 * sun_color[0] + 0.8 * scene_color[0];
333         o_halo_color[1] = 0.2 * sun_color[1] + 0.8 * scene_color[1];
334         o_halo_color[2] = 0.2 * sun_color[2] + 0.8 * scene_color[2];
335         o_halo_color[3] = blue_scat_f;
336         if ((visibility < 10000) && (blue_scat_f > 1)) {
337             o_halo_color[3] = 2 - blue_scat_f;
338         }
339         if (o_halo_color[3] > 1) o_halo_color[3] = 1;
340         if (o_halo_color[3] < 0) o_halo_color[3] = 0;
341
342         gamma_correct_rgb( i_halo_color._v );
343         gamma_correct_rgb( o_halo_color._v );
344         gamma_correct_rgb( scene_color._v );    
345         gamma_correct_rgb( sun_color._v );
346
347         (*sun_cl)[0] = sun_color;
348         sun_cl->dirty();
349         (*scene_cl)[0] = scene_color;
350         scene_cl->dirty();
351         (*ihalo_cl)[0] = i_halo_color;
352         ihalo_cl->dirty();
353         (*ohalo_cl)[0] = o_halo_color;
354         ohalo_cl->dirty();
355     }
356
357     return true;
358 }
359
360
361 // reposition the sun at the specified right ascension and
362 // declination, offset by our current position (p) so that it appears
363 // fixed at a great distance from the viewer.  Also add in an optional
364 // rotation (i.e. for the current time of day.)
365 // Then calculate stuff needed for the sun-coloring
366 bool SGSun::reposition( double rightAscension, double declination, 
367                         double sun_dist, double lat, double alt_asl, double sun_angle)
368 {
369     // GST - GMT sidereal time 
370     osg::Matrix T2, RA, DEC;
371
372     // xglRotatef( ((SGD_RADIANS_TO_DEGREES * rightAscension)- 90.0),
373     //             0.0, 0.0, 1.0);
374     RA.makeRotate(rightAscension - 90*SGD_DEGREES_TO_RADIANS, osg::Vec3(0, 0, 1));
375
376     // xglRotatef((SGD_RADIANS_TO_DEGREES * declination), 1.0, 0.0, 0.0);
377     DEC.makeRotate(declination, osg::Vec3(1, 0, 0));
378
379     // xglTranslatef(0,sun_dist);
380     T2.makeTranslate(osg::Vec3(0, sun_dist, 0));
381
382     sun_transform->setMatrix(T2*DEC*RA);
383
384     // Suncolor related things:
385     if ( prev_sun_angle != sun_angle ) {
386       if ( sun_angle == 0 ) sun_angle = 0.1;
387          const double r_earth_pole = 6356752.314;
388          const double r_tropo_pole = 6356752.314 + 8000;
389          const double epsilon_earth2 = 6.694380066E-3;
390          const double epsilon_tropo2 = 9.170014946E-3;
391
392          double r_tropo = r_tropo_pole / sqrt ( 1 - ( epsilon_tropo2 * pow ( cos( lat ), 2 )));
393          double r_earth = r_earth_pole / sqrt ( 1 - ( epsilon_earth2 * pow ( cos( lat ), 2 )));
394  
395          double position_radius = r_earth + alt_asl;
396
397          double gamma =  SG_PI - sun_angle;
398          double sin_beta =  ( position_radius * sin ( gamma )  ) / r_tropo;
399          if (sin_beta > 1.0) sin_beta = 1.0;
400          double alpha =  SG_PI - gamma - asin( sin_beta );
401
402          // OK, now let's calculate the distance the light travels
403          path_distance = sqrt( pow( position_radius, 2 ) + pow( r_tropo, 2 )
404                         - ( 2 * position_radius * r_tropo * cos( alpha ) ));
405
406          double alt_half = sqrt( pow ( r_tropo, 2 ) + pow( path_distance / 2, 2 ) - r_tropo * path_distance * cos( asin( sin_beta )) ) - r_earth;
407
408          if ( alt_half < 0.0 ) alt_half = 0.0;
409
410          // Push the data to the property tree, so it can be used in the enviromental code
411          if ( env_node ){
412             env_node->setDoubleValue( "atmosphere/altitude-troposphere-top", r_tropo - r_earth );
413             env_node->setDoubleValue( "atmosphere/altitude-half-to-sun", alt_half );
414       }
415     }
416
417     return true;
418 }
419
420 SGVec4f
421 SGSun::get_color()
422 {
423     return SGVec4f((*sun_cl)[0][0], (*sun_cl)[0][1], (*sun_cl)[0][2], (*sun_cl)[0][3]);
424 }
425
426 SGVec4f
427 SGSun::get_scene_color()
428 {
429     return SGVec4f((*scene_cl)[0][0], (*scene_cl)[0][1], (*scene_cl)[0][2], (*scene_cl)[0][3]);
430 }