]> git.mxchange.org Git - simgear.git/blob - simgear/environment/visual_enviro.cxx
d3888f5c50b880c52e85a37b123773e66f75958f
[simgear.git] / simgear / environment / visual_enviro.cxx
1 // Visual environment helper class
2 //
3 // Written by Harald JOHNSEN, started April 2005.
4 //
5 // Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
6 //
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.
11 //
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.
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 <plib/sg.h>
27 #include <simgear/constants.h>
28 #include <simgear/structure/SGReferenced.hxx>
29 #include <simgear/structure/SGSharedPtr.hxx>
30 #include <simgear/math/sg_random.h>
31 #include <simgear/math/sg_geodesy.hxx>
32 #include <simgear/math/point3d.hxx>
33 #include <simgear/math/polar3d.hxx>
34 #include <simgear/sound/soundmgr_openal.hxx>
35 #include <simgear/scene/sky/cloudfield.hxx>
36 #include <simgear/scene/sky/newcloud.hxx>
37 #include <simgear/props/props.hxx>
38 #include "visual_enviro.hxx"
39
40 #include <vector>
41
42 SG_USING_STD(vector);
43
44
45 typedef struct {
46         Point3D         pt;
47         int                     depth;
48         int                     prev;
49 } lt_tree_seg;
50
51 #define MAX_RAIN_SLICE  200
52 static float rainpos[MAX_RAIN_SLICE];
53 #define MAX_LT_TREE_SEG 400
54
55 #define DFL_MIN_LIGHT 0.35
56 sgVec3 SGEnviro::min_light = {DFL_MIN_LIGHT, DFL_MIN_LIGHT, DFL_MIN_LIGHT};
57 #define DFL_STREAK_BRIGHT_NEARMOST_LAYER 0.9
58 SGfloat SGEnviro::streak_bright_nearmost_layer = DFL_STREAK_BRIGHT_NEARMOST_LAYER;
59 #define DFL_STREAK_BRIGHT_FARMOST_LAYER 0.5
60 SGfloat SGEnviro::streak_bright_farmost_layer = DFL_STREAK_BRIGHT_FARMOST_LAYER;
61 #define DFL_STREAK_PERIOD_MAX 2.5
62 SGfloat SGEnviro::streak_period_max = DFL_STREAK_PERIOD_MAX;
63 #define DFL_STREAK_PERIOD_CHANGE_PER_KT 0.005
64 SGfloat SGEnviro::streak_period_change_per_kt = DFL_STREAK_PERIOD_CHANGE_PER_KT;
65 #define DFL_STREAK_PERIOD_MIN 1.0
66 SGfloat SGEnviro::streak_period_min = DFL_STREAK_PERIOD_MIN;
67 #define DFL_STREAK_LENGTH_MIN 0.03
68 SGfloat SGEnviro::streak_length_min = DFL_STREAK_LENGTH_MIN;
69 #define DFL_STREAK_LENGTH_CHANGE_PER_KT 0.0005
70 SGfloat SGEnviro::streak_length_change_per_kt = DFL_STREAK_LENGTH_CHANGE_PER_KT;
71 #define DFL_STREAK_LENGTH_MAX 0.1
72 SGfloat SGEnviro::streak_length_max = DFL_STREAK_LENGTH_MAX;
73 #define DFL_STREAK_COUNT_MIN 40
74 int SGEnviro::streak_count_min = DFL_STREAK_COUNT_MIN;
75 #define DFL_STREAK_COUNT_MAX 190
76 #if (DFL_STREAK_COUNT_MAX > MAX_RAIN_SLICE)
77 #error "Bad default!"
78 #endif
79 int SGEnviro::streak_count_max = DFL_STREAK_COUNT_MAX;
80 #define DFL_CONE_BASE_RADIUS 15.0
81 SGfloat SGEnviro::cone_base_radius = DFL_CONE_BASE_RADIUS;
82 #define DFL_CONE_HEIGHT 30.0
83 SGfloat SGEnviro::cone_height = DFL_CONE_HEIGHT;
84
85
86 void SGEnviro::config(const SGPropertyNode* n)
87 {
88         if (!n)
89                 return;
90
91         const float ml = n->getFloatValue("min-light", DFL_MIN_LIGHT);
92         sgSetVec3(min_light, ml, ml, ml);
93
94         streak_bright_nearmost_layer = n->getFloatValue(
95                         "streak-brightness-nearmost-layer",
96                         DFL_STREAK_BRIGHT_NEARMOST_LAYER);
97         streak_bright_farmost_layer = n->getFloatValue(
98                         "streak-brightness-farmost-layer",
99                         DFL_STREAK_BRIGHT_FARMOST_LAYER);
100
101         streak_period_max = n->getFloatValue(
102                         "streak-period-max",
103                         DFL_STREAK_PERIOD_MAX);
104         streak_period_min = n->getFloatValue(
105                         "streak-period-min",
106                         DFL_STREAK_PERIOD_MIN);
107         streak_period_change_per_kt = n->getFloatValue(
108                         "streak-period-change-per-kt",
109                         DFL_STREAK_PERIOD_CHANGE_PER_KT);
110
111         streak_length_max = n->getFloatValue(
112                         "streak-length-max",
113                         DFL_STREAK_LENGTH_MAX);
114         streak_length_min = n->getFloatValue(
115                         "streak-length-min",
116                         DFL_STREAK_LENGTH_MIN);
117         streak_length_change_per_kt = n->getFloatValue(
118                         "streak-length-change-per-kt",
119                         DFL_STREAK_LENGTH_CHANGE_PER_KT);
120
121         streak_count_min = n->getIntValue(
122                         "streak-count-min", DFL_STREAK_COUNT_MIN);
123         streak_count_max = n->getIntValue(
124                         "streak-count-max", DFL_STREAK_COUNT_MAX);
125         if (streak_count_max > MAX_RAIN_SLICE)
126                 streak_count_max = MAX_RAIN_SLICE;
127
128         cone_base_radius = n->getFloatValue(
129                         "cone-base-radius", DFL_CONE_BASE_RADIUS);
130         cone_height = n->getFloatValue("cone_height", DFL_CONE_HEIGHT);
131 }
132
133
134 /**
135  * A class to render lightnings.
136  */
137 class SGLightning {
138 public:
139     /**
140      * Build a new lightning.
141      * The lightning has a limited life time. It will also play a thunder sounder once.
142      * @param lon lon longitude in degree
143      * @param lat lat latitude in degree
144      * @param alt asl of top of lightning
145      */
146         SGLightning(double lon, double lat, double alt);
147         ~SGLightning();
148         void lt_Render(void);
149         void lt_build(void);
150         void lt_build_tree_branch(int tree_nr, Point3D &start, float energy, int nbseg, float segsize);
151
152         // contains all the segments of the lightning
153         lt_tree_seg lt_tree[MAX_LT_TREE_SEG];
154         // segment count
155         int             nb_tree;
156         // position of lightning
157         double  lon, lat, alt;
158         int             sequence_count;
159         // time to live
160         double  age;
161 };
162
163 typedef vector<SGLightning *> list_of_lightning;
164 static list_of_lightning lightnings;
165
166 SGEnviro sgEnviro;
167
168 SGEnviro::SGEnviro() :
169         view_in_cloud(false),
170         precipitation_enable_state(true),
171         precipitation_density(100.0),
172         precipitation_max_alt(0.0),
173         turbulence_enable_state(false),
174         last_cloud_turbulence(0.0),
175         cloud_turbulence(0.0),
176         lightning_enable_state(false),
177         elapsed_time(0.0),
178         dt(0.0),
179         soundMgr(NULL),
180         snd_active(false),
181         snd_dist(0.0),
182         min_time_before_lt(0.0),
183         fov_width(55.0),
184         fov_height(55.0)
185
186 {
187         for(int i = 0; i < MAX_RAIN_SLICE ; i++)
188                 rainpos[i] = sg_random();
189         radarEcho.reserve(100);
190 }
191
192 SGEnviro::~SGEnviro(void) {
193         list_of_lightning::iterator iLightning;
194         for( iLightning = lightnings.begin() ; iLightning != lightnings.end() ; iLightning++ ) {
195                 delete (*iLightning);
196         }
197         lightnings.clear();
198 }
199
200 void SGEnviro::startOfFrame( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double delta_time) {
201         view_in_cloud = false;
202         // ask the impostor cache to do some cleanup
203         if(SGNewCloud::cldCache)
204                 SGNewCloud::cldCache->startNewFrame();
205         last_cloud_turbulence = cloud_turbulence;
206         cloud_turbulence = 0.0;
207         elapsed_time += delta_time;
208         min_time_before_lt -= delta_time;
209         dt = delta_time;
210
211         sgMat4 T1, LON, LAT;
212     sgVec3 axis;
213
214     sgMakeTransMat4( T1, p );
215
216     sgSetVec3( axis, 0.0, 0.0, 1.0 );
217     sgMakeRotMat4( LON, lon, axis );
218
219     sgSetVec3( axis, 0.0, 1.0, 0.0 );
220     sgMakeRotMat4( LAT, 90.0 - lat, axis );
221
222     sgMat4 TRANSFORM;
223
224     sgCopyMat4( TRANSFORM, T1 );
225     sgPreMultMat4( TRANSFORM, LON );
226     sgPreMultMat4( TRANSFORM, LAT );
227
228     sgCoord pos;
229     sgSetCoord( &pos, TRANSFORM );
230
231         sgMakeCoordMat4( transform, &pos );
232     last_lon = lon;
233     last_lat = lat;
234         last_alt = alt;
235
236         radarEcho.clear();
237         precipitation_max_alt = 400.0;
238 }
239
240 void SGEnviro::endOfFrame(void) {
241 }
242
243 double SGEnviro::get_cloud_turbulence(void) const {
244         return last_cloud_turbulence;
245 }
246
247 // this can be queried to add some turbulence for example
248 bool SGEnviro::is_view_in_cloud(void) const {
249         return view_in_cloud;
250 }
251 void SGEnviro::set_view_in_cloud(bool incloud) {
252         view_in_cloud = incloud;
253 }
254
255 int SGEnviro::get_CacheResolution(void) const {
256         return SGCloudField::get_CacheResolution();
257 }
258
259 int SGEnviro::get_clouds_CacheSize(void) const {
260         return SGCloudField::get_CacheSize();
261 }
262 float SGEnviro::get_clouds_visibility(void) const {
263         return SGCloudField::get_CloudVis();
264 }
265 float SGEnviro::get_clouds_density(void) const {
266         return SGCloudField::get_density();
267 }
268 bool SGEnviro::get_clouds_enable_state(void) const {
269         return SGCloudField::get_enable3dClouds();
270 }
271
272 bool SGEnviro::get_turbulence_enable_state(void) const {
273         return turbulence_enable_state;
274 }
275
276 void SGEnviro::set_CacheResolution(int resolutionPixels) {
277         SGCloudField::set_CacheResolution(resolutionPixels);
278 }
279
280 void SGEnviro::set_clouds_CacheSize(int sizeKb) {
281         SGCloudField::set_CacheSize(sizeKb);
282 }
283 void SGEnviro::set_clouds_visibility(float distance) {
284         SGCloudField::set_CloudVis(distance);
285 }
286 void SGEnviro::set_clouds_density(float density) {
287         SGCloudField::set_density(density);
288 }
289 void SGEnviro::set_clouds_enable_state(bool enable) {
290         SGCloudField::set_enable3dClouds(enable);
291 }
292 void SGEnviro::set_turbulence_enable_state(bool enable) {
293         turbulence_enable_state = enable;
294 }
295 // rain/snow
296 float SGEnviro::get_precipitation_density(void) const {
297         return precipitation_density;
298 }
299 bool SGEnviro::get_precipitation_enable_state(void) const {
300         return precipitation_enable_state;
301 }
302
303 void SGEnviro::set_precipitation_density(float density) {
304         precipitation_density = density;
305 }
306 void SGEnviro::set_precipitation_enable_state(bool enable) {
307         precipitation_enable_state = enable;
308 }
309
310 // others
311 bool SGEnviro::get_lightning_enable_state(void) const {
312         return lightning_enable_state;
313 }
314
315 void SGEnviro::set_lightning_enable_state(bool enable) {
316         lightning_enable_state = enable;
317         if( ! enable ) {
318                 // TODO:cleanup
319         }
320 }
321
322 void SGEnviro::setLight(sgVec4 adj_fog_color) {
323         sgCopyVec4( fog_color, adj_fog_color );
324         if( false ) {
325         //    ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, l->scene_diffuse() );
326         }
327 }
328
329 void SGEnviro::callback_cloud(float heading, float alt, float radius, int family, float dist, int cloudId) {
330         // send data to wx radar
331         // compute turbulence
332         // draw precipitation
333         // draw lightning
334         // compute illumination
335
336         // http://www.pilotfriend.com/flight_training/weather/THUNDERSTORM%20HAZARDS1.htm
337         double turbulence = 0.0;
338         if( dist < radius * radius * 2.25f ) {
339                 switch(family) {
340                         case SGNewCloud::CLFamilly_st:
341                                 turbulence = 0.2;
342                                 break;
343                         case SGNewCloud::CLFamilly_ci:
344                         case SGNewCloud::CLFamilly_cs:
345                         case SGNewCloud::CLFamilly_cc:
346                         case SGNewCloud::CLFamilly_ac:
347                         case SGNewCloud::CLFamilly_as:
348                                 turbulence = 0.1;
349                                 break;
350                         case SGNewCloud::CLFamilly_sc:
351                                 turbulence = 0.3;
352                                 break;
353                         case SGNewCloud::CLFamilly_ns:
354                                 turbulence = 0.4;
355                                 break;
356                         case SGNewCloud::CLFamilly_cu:
357                                 turbulence = 0.5;
358                                 break;
359                         case SGNewCloud::CLFamilly_cb:
360                                 turbulence = 0.6;
361                                 break;
362                 }
363                 // full turbulence inside cloud, half in the vicinity
364                 if( dist > radius * radius )
365                         turbulence *= 0.5;
366                 if( turbulence > cloud_turbulence )
367                         cloud_turbulence = turbulence;
368                 // we can do 'local' precipitations too
369         }
370
371         // convert to LWC for radar (experimental)
372         // http://www-das.uwyo.edu/~geerts/cwx/notes/chap08/moist_cloud.html
373         double LWC = 0.0;
374         switch(family) {
375                 case SGNewCloud::CLFamilly_st:
376                         LWC = 0.29;
377                         break;
378                 case SGNewCloud::CLFamilly_cu:
379                         LWC = 0.27;
380                         break;
381                 case SGNewCloud::CLFamilly_cb:
382                         LWC = 2.0;
383                         break;
384                 case SGNewCloud::CLFamilly_sc:
385                         LWC = 0.44;
386                         break;
387                 case SGNewCloud::CLFamilly_ci:
388                         LWC = 0.03;
389                         break;
390                 // no data
391                 case SGNewCloud::CLFamilly_cs:
392                 case SGNewCloud::CLFamilly_cc:
393                 case SGNewCloud::CLFamilly_ac:
394                 case SGNewCloud::CLFamilly_as:
395                         LWC = 0.03;
396                         break;
397                 case SGNewCloud::CLFamilly_ns:
398                         LWC = 0.29*2.0;
399                         break;
400         }
401         // add to the list for the wxRadar instrument
402         if( LWC > 0.0 )
403                 radarEcho.push_back( SGWxRadarEcho ( heading, alt, radius, dist, LWC, false, cloudId ) );
404
405         // NB:data valid only from cockpit view
406
407         // spawn a new lightning
408         if(lightning_enable_state && min_time_before_lt <= 0.0 && (family == SGNewCloud::CLFamilly_cb) &&
409                 dist < 15000.0 * 15000.0 && sg_random() > 0.9f) {
410                 double lat, lon;
411                 Point3D orig, dest;
412                 orig.setlat(last_lat * SG_DEGREES_TO_RADIANS );
413                 orig.setlon(last_lon * SG_DEGREES_TO_RADIANS );
414                 orig.setelev(0.0);
415                 dist = sgSqrt(dist);
416                 dest = calc_gc_lon_lat(orig, heading, dist);
417                 lon = dest.lon() * SG_RADIANS_TO_DEGREES;
418                 lat = dest.lat() * SG_RADIANS_TO_DEGREES;
419                 addLightning( lon, lat, alt );
420
421                 // reset timer
422                 min_time_before_lt = 5.0 + sg_random() * 30;
423                 // DEBUG only
424 //              min_time_before_lt = 5.0;
425         }
426         if( (alt - radius * 0.1) > precipitation_max_alt )
427                 switch(family) {
428                         case SGNewCloud::CLFamilly_st:
429                         case SGNewCloud::CLFamilly_cu:
430                         case SGNewCloud::CLFamilly_cb:
431                         case SGNewCloud::CLFamilly_ns:
432                         case SGNewCloud::CLFamilly_sc:
433                                 precipitation_max_alt = alt - radius * 0.1;
434                                 break;
435                 }
436 }
437
438 list_of_SGWxRadarEcho *SGEnviro::get_radar_echo(void) {
439         return &radarEcho;
440 }
441
442 // precipitation rendering code
443 void SGEnviro::DrawCone2(float baseRadius, float height, int slices, bool down, double rain_norm, double speed) {
444         sgVec3 light;
445         sgAddVec3( light, fog_color, min_light );
446         float da = SG_PI * 2.0f / (float) slices;
447         // low number = faster
448         float speedf = streak_period_max - speed * streak_period_change_per_kt;
449         if( speedf < streak_period_min )
450                 speedf = streak_period_min;
451         float lenf = streak_length_min + speed * streak_length_change_per_kt;
452         if( lenf > streak_length_max )
453                 lenf = streak_length_max;
454     float t = fmod((float) elapsed_time, speedf) / speedf;
455 //      t = 0.1f;
456         if( !down )
457                 t = 1.0f - t;
458         float angle = 0.0f;
459         //glColor4f(1.0f, 0.7f, 0.7f, 0.9f); // XXX unneeded? overriden below
460         glBegin(GL_LINES);
461         if (slices >  MAX_RAIN_SLICE)
462                 slices = MAX_RAIN_SLICE; // should never happen
463         for( int i = 0 ; i < slices ; i++ ) {
464                 float x = cos(angle) * baseRadius;
465                 float y = sin(angle) * baseRadius;
466                 angle += da;
467                 sgVec3 dir = {x, -height, y};
468
469                 // rain drops at 2 different speed to simulate depth
470                 float t1 = (i & 1 ? t : t + t) + rainpos[i];
471                 if(t1 > 1.0f)   t1 -= 1.0f;
472                 if(t1 > 1.0f)   t1 -= 1.0f;
473
474                 // distant raindrops are more transparent
475                 float c = t1 * (i & 1 ?
476                                 streak_bright_farmost_layer
477                                 : streak_bright_nearmost_layer);
478                 glColor4f(c * light[0], c * light[1], c * light[2], c);
479                 sgVec3 p1, p2;
480                 sgScaleVec3(p1, dir, t1);
481                 // distant raindrops are shorter
482                 float t2 = t1 + (i & 1 ? lenf : lenf+lenf);
483                 sgScaleVec3(p2, dir, t2);
484
485                 glVertex3f(p1[0], p1[1] + height, p1[2]);
486                 glVertex3f(p2[0], p2[1] + height, p2[2]);
487         }
488         glEnd();
489 }
490
491 void SGEnviro::drawRain(double pitch, double roll, double heading, double hspeed, double rain_norm) {
492
493 #if 0
494         static int debug_period = 0;
495         if (debug_period++ == 50) {
496                 debug_period = 0;
497                 cout << "drawRain("
498                         << pitch << ", "
499                         << roll  << ", "
500                         << heading << ", "
501                         << hspeed << ", "
502                         << rain_norm << ");"
503                         //" angle = " << angle
504                         //<< " raindrop(KTS) = " << raindrop_speed_kts
505                         << endl;
506         }
507 #endif
508
509
510         glBindTexture(GL_TEXTURE_2D, 0);
511
512         glDisable(GL_DEPTH_TEST);
513         glShadeModel(GL_SMOOTH);
514         glEnable(GL_BLEND);
515         glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
516         glDisable( GL_FOG );
517         glDisable(GL_LIGHTING);
518
519         int slice_count = static_cast<int>(
520                         (streak_count_min + rain_norm*(streak_count_max-streak_count_min))
521                                 * precipitation_density / 100.0);
522
523         // www.wonderquest.com/falling-raindrops.htm says that
524         // Raindrop terminal velocity is 5 to 20mph
525         // Rather than model it accurately (temp, pressure, diameter), and make it
526         // smaller than terminal when closer to the precipitation cloud base,
527         // we interpolate in the 5-20mph range according to rain_norm.
528         double raindrop_speed_kts
529                 = (5.0 + rain_norm*15.0) * SG_MPH_TO_MPS * SG_MPS_TO_KT;
530
531         float angle = atanf(hspeed / raindrop_speed_kts) * SG_RADIANS_TO_DEGREES;
532         glPushMatrix();
533                 // the cone rotate with hspeed
534                 angle = -pitch - angle;
535                 glRotatef(roll, 0.0, 0.0, 1.0);
536                 glRotatef(heading, 0.0, 1.0, 0.0);
537                 glRotatef(angle, 1.0, 0.0, 0.0);
538
539                 // up cone
540                 DrawCone2(cone_base_radius, cone_height, 
541                                 slice_count, true, rain_norm, hspeed);
542                 // down cone (usually not visible)
543                 if(angle > 0.0 || heading != 0.0)
544                         DrawCone2(cone_base_radius, -cone_height, 
545                                         slice_count, false, rain_norm, hspeed);
546
547         glPopMatrix();
548
549         glEnable(GL_LIGHTING);
550         glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
551         glEnable( GL_FOG );
552         glEnable(GL_DEPTH_TEST);
553
554 }
555
556 void SGEnviro::set_soundMgr(SGSoundMgr *mgr) {
557         soundMgr = mgr;
558 }
559
560 void SGEnviro::drawPrecipitation(double rain_norm, double snow_norm, double hail_norm, double pitch, double roll, double heading, double hspeed) {
561         if( precipitation_enable_state && rain_norm > 0.0)
562           if( precipitation_max_alt >= last_alt )
563                 drawRain(pitch, roll, heading, hspeed, rain_norm);
564 }
565
566
567 SGLightning::SGLightning(double _lon, double _lat, double _alt) :
568         nb_tree(0),
569         lon(_lon),
570         lat(_lat),
571         alt(_alt),
572         age(1.0 + sg_random() * 4.0)
573 {
574 //      sequence_count = 1 + sg_random() * 5.0;
575         lt_build();
576 }
577
578 SGLightning::~SGLightning() {
579 }
580
581 // lightning rendering code
582 void SGLightning::lt_build_tree_branch(int tree_nr, Point3D &start, float energy, int nbseg, float segsize) {
583
584         sgVec3 dir, newdir;
585         int nseg = 0;
586         Point3D pt = start;
587         if( nbseg == 50 )
588                 sgSetVec3( dir, 0.0, -1.0, 0.0 );
589         else {
590                 sgSetVec3( dir, sg_random() - 0.5f, sg_random() - 0.5f, sg_random() - 0.5f);
591                 sgNormaliseVec3(dir);
592         }
593         if( nb_tree >= MAX_LT_TREE_SEG )
594                 return;
595
596         lt_tree[nb_tree].depth = tree_nr;
597         nseg = 0;
598         lt_tree[nb_tree].pt = pt;
599         lt_tree[nb_tree].prev = -1;
600         nb_tree ++;
601
602         // TODO:check agl
603         while(nseg < nbseg && pt.y() > 0.0) {
604         int prev = nb_tree - 1;
605         nseg++;
606                 // add a branch
607         if( energy * sg_random() > 0.8f )
608                         lt_build_tree_branch(tree_nr + 1, pt, energy * 0.9f, nbseg == 50 ? 10 : static_cast<int>(nbseg * 0.4f), segsize * 0.7f);
609
610                 if( nb_tree >= MAX_LT_TREE_SEG )
611                         return;
612                 sgSetVec3(newdir, (sg_random() - 0.5f), (sg_random() - 0.5f) - (nbseg == 50 ? 0.5f : 0.0), (sg_random() - 0.5f));
613                 sgNormaliseVec3(newdir);
614                 sgAddVec3( dir, newdir);
615                 sgNormaliseVec3(dir);
616                 sgVec3 scaleDir;
617                 sgScaleVec3( scaleDir, dir, segsize * energy * 0.5f );
618                 pt[PX] += scaleDir[0];
619                 pt[PY] += scaleDir[1];
620                 pt[PZ] += scaleDir[2];
621
622                 lt_tree[nb_tree].depth = tree_nr;
623                 lt_tree[nb_tree].pt = pt;
624                 lt_tree[nb_tree].prev = prev;
625                 nb_tree ++;
626         }
627 }
628
629 void SGLightning::lt_build(void) {
630     Point3D top;
631     nb_tree = 0;
632     top[PX] = 0 ;
633     top[PY] = alt;
634     top[PZ] = 0;
635     lt_build_tree_branch(0, top, 1.0, 50, top[PY] / 8.0);
636         if( ! sgEnviro.soundMgr )
637                 return;
638         Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
639         Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 );
640         double course = 0.0, dist = 0.0;
641         calc_gc_course_dist( dest, start, &course, &dist );
642         if( dist < 10000.0 && ! sgEnviro.snd_playing && (dist < sgEnviro.snd_dist || ! sgEnviro.snd_active) ) {
643                 sgEnviro.snd_timer = 0.0;
644                 sgEnviro.snd_wait  = dist / 340;
645                 sgEnviro.snd_dist  = dist;
646                 sgEnviro.snd_pos_lat = lat;
647                 sgEnviro.snd_pos_lon = lon;
648                 sgEnviro.snd_active = true;
649                 sgEnviro.snd_playing = false;
650         }
651 }
652
653
654 void SGLightning::lt_Render(void) {
655         float flash = 0.5;
656         if( fmod(sgEnviro.elapsed_time*100.0, 100.0) > 50.0 )
657                 flash = sg_random() * 0.75f + 0.25f;
658     float h = lt_tree[0].pt[PY];
659         sgVec4 col={0.62f, 0.83f, 1.0f, 1.0f};
660         sgVec4 c;
661
662 #define DRAW_SEG() \
663                         {glColorMaterial(GL_FRONT, GL_EMISSION);  \
664                         glDisable(GL_LINE_SMOOTH); glBegin(GL_LINES); \
665                                 glColor4fv(c); \
666                 glVertex3f(lt_tree[n].pt[PX], lt_tree[n].pt[PZ], lt_tree[n].pt[PY]); \
667                 glVertex3f(lt_tree[lt_tree[n].prev].pt[PX], lt_tree[lt_tree[n].prev].pt[PZ], lt_tree[lt_tree[n].prev].pt[PY]); \
668                         glEnd(); glEnable(GL_LINE_SMOOTH);}
669
670         glDepthMask( GL_FALSE );
671         glEnable(GL_BLEND);
672         glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
673         glBindTexture(GL_TEXTURE_2D, 0);
674
675         glDisable(GL_LIGHTING);
676         glDisable( GL_FOG );
677         glPushMatrix();
678         sgMat4 modelview, tmp;
679     ssgGetModelviewMatrix( modelview );
680         sgCopyMat4( tmp, sgEnviro.transform );
681     sgPostMultMat4( tmp, modelview );
682     ssgLoadModelviewMatrix( tmp );
683
684     Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
685     Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 );
686     double course = 0.0, dist = 0.0;
687     calc_gc_course_dist( dest, start, &course, &dist );
688     double ax = 0.0, ay = 0.0;
689     ax = cos(course) * dist;
690     ay = sin(course) * dist;
691
692         glTranslatef( ax, ay, -sgEnviro.last_alt );
693
694         sgEnviro.radarEcho.push_back( SGWxRadarEcho ( course, 0.0, 0.0, dist, age, true, 0 ) );
695
696         for( int n = 0 ; n < nb_tree ; n++ ) {
697         if( lt_tree[n].prev < 0 )
698                         continue;
699
700         float t1 = sgLerp(0.5, 1.0, lt_tree[n].pt[PY] / h);
701                 t1 *= flash;
702                 if( lt_tree[n].depth >= 2 ) {
703             glLineWidth(3);
704                         sgScaleVec4(c, col, t1 * 0.6f);
705                         DRAW_SEG();
706                 } else {
707                         if( lt_tree[n].depth == 0 ) {
708                 glLineWidth(12);
709                                 sgScaleVec4(c, col, t1 * 0.5f);
710                                 DRAW_SEG();
711
712                 glLineWidth(6);
713                                 sgScaleVec4(c, col, t1);
714                                 DRAW_SEG();
715                         } else {
716                 glLineWidth(6);
717                                 sgScaleVec4(c, col, t1 * 0.7f);
718                                 DRAW_SEG();
719                         }
720
721             if( lt_tree[n].depth == 0 ) 
722                 glLineWidth(3);
723                         else
724                 glLineWidth(2);
725
726             sgSetVec4(c, t1, t1, t1, t1);
727                         DRAW_SEG();
728                 }
729
730         }
731     glLineWidth(1);
732         glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
733         glPopMatrix();
734         glDepthMask( GL_TRUE ); 
735         glEnable( GL_FOG );
736         glEnable(GL_LIGHTING);
737 }
738
739 void SGEnviro::addLightning(double lon, double lat, double alt) {
740         if( lightnings.size() > 10)
741                 return;
742         SGLightning *lt= new SGLightning(lon, lat, alt);
743         lightnings.push_back(lt);
744 }
745
746 void SGEnviro::drawLightning(void) {
747         list_of_lightning::iterator iLightning;
748         // play 'thunder' for lightning
749         if( snd_active )
750                 if( !snd_playing ) {
751                         // wait until sound has reached us
752                         snd_timer += dt;
753                         if( snd_timer >= snd_wait ) {
754                                 snd_playing = true;
755                                 // compute relative position of lightning
756                                 Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
757                                 Point3D dest( snd_pos_lon*SG_DEGREES_TO_RADIANS, snd_pos_lat*SG_DEGREES_TO_RADIANS, 0.0 );
758                                 double course = 0.0, dist = 0.0;
759                                 calc_gc_course_dist( dest, start, &course, &dist );
760                                 double ax = 0.0, ay = 0.0;
761                                 ax = cos(course) * dist;
762                                 ay = sin(course) * dist;
763                                 SGSharedPtr<SGSoundSample> snd = soundMgr->find("thunder");
764                                 if( snd ) {
765                                         ALfloat pos[3]={ax, ay, -sgEnviro.last_alt };
766                                         snd->set_source_pos(pos);
767                                         snd->play_once();
768                                 }
769                         }
770                 } else {
771                         if( !soundMgr->is_playing("thunder") ) {
772                                 snd_active = false;
773                                 snd_playing = false;
774                         }
775                 }
776
777         if( ! lightning_enable_state )
778                 return;
779
780         for( iLightning = lightnings.begin() ; iLightning != lightnings.end() ; iLightning++ ) {
781                 if( dt )
782                         if( sg_random() > 0.95f )
783                                 (*iLightning)->lt_build();
784                 (*iLightning)->lt_Render();
785                 (*iLightning)->age -= dt;
786                 if( (*iLightning)->age < 0.0 ) {
787                         delete (*iLightning);
788                         lightnings.erase( iLightning );
789                         break;
790                 }
791         }
792
793 }
794
795
796 void SGEnviro::setFOV( float w, float h ) {
797         fov_width = w;
798         fov_height = h;
799 }
800
801 void SGEnviro::getFOV( float &w, float &h ) {
802         w = fov_width;
803         h = fov_height;
804 }