]> git.mxchange.org Git - simgear.git/commitdiff
Harald JOHNSEN:
authorehofman <ehofman>
Sun, 15 May 2005 09:27:00 +0000 (09:27 +0000)
committerehofman <ehofman>
Sun, 15 May 2005 09:27:00 +0000 (09:27 +0000)
This is another update for the cloud code, a lot of lines but this time I have started to add the doxygen doc.

Misc
====

- corrected a bug when RTT is not available, the current rendering context was
  altered
- if RTT is not available then 3d clouds are not drawn at all
- impostors lighting is now recomputed when the sun changes position
- distant objects are no more seen in front of clouds
- blending of distant clouds is a bit better now
- litle optimization of code (uses a less cpu time)
- use layer wind speed and direction (no more hardcoded wind)
- fov is no more hardcoded

Changes
=======

- clouds (cu only) are dissipating/reforming (experimental)
- compute a turbulence factor that depends on surrounding clouds and type of
  clouds (experimental)
- clouds shapes are defined in cloudlayers.xml
- type of clouds present in a layer is also defined in cloudlayers.xml
- cloud layers are generated from metar and other misc. data (in progress)
- added a rain effect around the viewer (enabled in the rendering dialog and
  when the metar property says so)
- added a lightning effect (enabled in the rendering dialog) : cb clouds spawn
  new lightnings
- added a dialog to select from different weather source : metar/property,
  a 'fair weather' environment and a 'thunderstorm' environment.

12 files changed:
simgear/environment/visual_enviro.cxx
simgear/environment/visual_enviro.hxx
simgear/scene/sky/bbcache.cxx
simgear/scene/sky/bbcache.hxx
simgear/scene/sky/cloud.cxx
simgear/scene/sky/cloud.hxx
simgear/scene/sky/cloudfield.cxx
simgear/scene/sky/cloudfield.hxx
simgear/scene/sky/newcloud.cxx
simgear/scene/sky/newcloud.hxx
simgear/scene/sky/sky.cxx
simgear/screen/Makefile.am

index ebe8f85549c625ac4d09822d7fd4d22a33ba0ccd..0c5192eb0d55f03ecac6d68937589b69dc371f0c 100644 (file)
 //
 //
 
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include <plib/sg.h>
+#include <simgear/constants.h>
+#include <simgear/math/sg_random.h>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/point3d.hxx>
+#include <simgear/math/polar3d.hxx>
+#include <simgear/sound/soundmgr_openal.hxx>
 #include <simgear/scene/sky/cloudfield.hxx>
 #include <simgear/scene/sky/newcloud.hxx>
 #include "visual_enviro.hxx"
 
+#include <vector>
+
+SG_USING_STD(vector);
+
+
+typedef struct {
+       Point3D         pt;
+       int                     depth;
+       int                     prev;
+} lt_tree_seg;
+
+#define MAX_RAIN_SLICE 200
+static float rainpos[MAX_RAIN_SLICE];
+#define MAX_LT_TREE_SEG        400
+
+/**
+ * A class to render lightnings.
+ */
+class SGLightning {
+public:
+    /**
+     * Build a new lightning.
+     * The lightning has a limited life time. It will also play a thunder sounder once.
+     * @param lon lon longitude in degree
+     * @param lat lat latitude in degree
+     * @param alt asl of top of lightning
+     */
+       SGLightning(double lon, double lat, double alt);
+       ~SGLightning();
+       void lt_Render(void);
+       void lt_build(void);
+       void lt_build_tree_branch(int tree_nr, Point3D &start, float energy, int nbseg, float segsize);
+
+       // contains all the segments of the lightning
+       lt_tree_seg lt_tree[MAX_LT_TREE_SEG];
+       // segment count
+       int             nb_tree;
+       // position of lightning
+       double  lon, lat, alt;
+       int             sequence_count;
+       // time to live
+       double  age;
+};
+
+typedef vector<SGLightning *> list_of_lightning;
+static list_of_lightning lightnings;
+
 SGEnviro sgEnviro;
 
 SGEnviro::SGEnviro(void) :
        view_in_cloud(false),
-       precipitation_enable_state(false),
+       turbulence_enable_state(false),
+       precipitation_enable_state(true),
+       lightning_enable_state(false),
+       soundMgr(NULL),
+       snd_active(false),
+       snd_dist(0.0),
+       last_cloud_turbulence(0.0),
+       cloud_turbulence(0.0),
+       elapsed_time(0.0),
+       dt(0.0),
+       min_time_before_lt(0.0),
+       fov_width(55.0),
+       fov_height(55.0),
        precipitation_density(100.0)
 
 {
+       for(int i = 0; i < MAX_RAIN_SLICE ; i++)
+               rainpos[i] = sg_random();
+
 }
 
 SGEnviro::~SGEnviro(void) {
+       list_of_lightning::iterator iLightning;
+       for( iLightning = lightnings.begin() ; iLightning != lightnings.end() ; iLightning++ ) {
+               delete (*iLightning);
+       }
+       lightnings.clear();
 }
 
-void SGEnviro::startOfFrame(void) {
+void SGEnviro::startOfFrame( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double delta_time) {
        view_in_cloud = false;
+       // ask the impostor cache to do some cleanup
        if(SGNewCloud::cldCache)
                SGNewCloud::cldCache->startNewFrame();
+       last_cloud_turbulence = cloud_turbulence;
+       cloud_turbulence = 0.0;
+       elapsed_time += delta_time;
+       min_time_before_lt -= delta_time;
+       dt = delta_time;
+
+       sgMat4 T1, LON, LAT;
+    sgVec3 axis;
+
+    sgMakeTransMat4( T1, p );
+
+    sgSetVec3( axis, 0.0, 0.0, 1.0 );
+    sgMakeRotMat4( LON, lon, axis );
+
+    sgSetVec3( axis, 0.0, 1.0, 0.0 );
+    sgMakeRotMat4( LAT, 90.0 - lat, axis );
+
+    sgMat4 TRANSFORM;
+
+    sgCopyMat4( TRANSFORM, T1 );
+    sgPreMultMat4( TRANSFORM, LON );
+    sgPreMultMat4( TRANSFORM, LAT );
+
+    sgCoord pos;
+    sgSetCoord( &pos, TRANSFORM );
+
+       sgMakeCoordMat4( transform, &pos );
+    last_lon = lon;
+    last_lat = lat;
+       last_alt = alt;
 }
 
 void SGEnviro::endOfFrame(void) {
 }
 
+double SGEnviro::get_cloud_turbulence(void) const {
+       return last_cloud_turbulence;
+}
+
 // this can be queried to add some turbulence for example
 bool SGEnviro::is_view_in_cloud(void) const {
        return view_in_cloud;
@@ -71,6 +184,10 @@ bool SGEnviro::get_clouds_enable_state(void) const {
        return SGCloudField::get_enable3dClouds();
 }
 
+bool SGEnviro::get_turbulence_enable_state(void) const {
+       return turbulence_enable_state;
+}
+
 void SGEnviro::set_CacheResolution(int resolutionPixels) {
        SGCloudField::set_CacheResolution(resolutionPixels);
 }
@@ -87,7 +204,9 @@ void SGEnviro::set_clouds_density(float density) {
 void SGEnviro::set_clouds_enable_state(bool enable) {
        SGCloudField::set_enable3dClouds(enable);
 }
-
+void SGEnviro::set_turbulence_enable_state(bool enable) {
+       turbulence_enable_state = enable;
+}
 // rain/snow
 float SGEnviro::get_precipitation_density(void) const {
        return precipitation_density;
@@ -105,9 +224,453 @@ void SGEnviro::set_precipitation_enable_state(bool enable) {
 
 // others
 bool SGEnviro::get_lightning_enable_state(void) const {
-       return false;
+       return lightning_enable_state;
 }
 
 void SGEnviro::set_lightning_enable_state(bool enable) {
+       lightning_enable_state = enable;
+       if( ! enable ) {
+               // TODO:cleanup
+       }
 }
 
+void SGEnviro::setLight(sgVec4 adj_fog_color) {
+       sgCopyVec4( fog_color, adj_fog_color );
+       if( false ) {
+       //    ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, l->scene_diffuse() );
+       }
+}
+
+void SGEnviro::callback_cloud(float heading, float alt, float radius, int familly, float dist) {
+       // send data to wx radar
+       // compute turbulence
+       // draw precipitation
+       // draw lightning
+       // compute illumination
+
+       // http://www.pilotfriend.com/flight_training/weather/THUNDERSTORM%20HAZARDS1.htm
+       double turbulence = 0.0;
+       if( dist < radius * radius * 2.25f ) {
+               switch(familly) {
+                       case SGNewCloud::CLFamilly_st:
+                               turbulence = 0.2;
+                               break;
+                       case SGNewCloud::CLFamilly_ci:
+                       case SGNewCloud::CLFamilly_cs:
+                       case SGNewCloud::CLFamilly_cc:
+                       case SGNewCloud::CLFamilly_ac:
+                       case SGNewCloud::CLFamilly_as:
+                               turbulence = 0.1;
+                               break;
+                       case SGNewCloud::CLFamilly_sc:
+                               turbulence = 0.3;
+                               break;
+                       case SGNewCloud::CLFamilly_ns:
+                               turbulence = 0.4;
+                               break;
+                       case SGNewCloud::CLFamilly_cu:
+                               turbulence = 0.5;
+                               break;
+                       case SGNewCloud::CLFamilly_cb:
+                               turbulence = 0.6;
+                               break;
+               }
+               // full turbulence inside cloud, half in the vicinity
+               if( dist > radius * radius )
+                       turbulence *= 0.5;
+               if( turbulence > cloud_turbulence )
+                       cloud_turbulence = turbulence;
+               // we can do 'local' precipitations too
+       }
+
+       // convert to LWC for radar (experimental)
+       // http://www-das.uwyo.edu/~geerts/cwx/notes/chap08/moist_cloud.html
+       double LWC = 0.0;
+       switch(familly) {
+               case SGNewCloud::CLFamilly_st:
+                       LWC = 0.29;
+                       break;
+               case SGNewCloud::CLFamilly_cu:
+                       LWC = 0.27;
+                       break;
+               case SGNewCloud::CLFamilly_cb:
+                       LWC = 2.0;
+                       break;
+               case SGNewCloud::CLFamilly_sc:
+                       LWC = 0.44;
+                       break;
+               case SGNewCloud::CLFamilly_ci:
+                       LWC = 0.03;
+                       break;
+               // no data
+               case SGNewCloud::CLFamilly_cs:
+               case SGNewCloud::CLFamilly_cc:
+               case SGNewCloud::CLFamilly_ac:
+               case SGNewCloud::CLFamilly_as:
+                       LWC = 0.03;
+                       break;
+               case SGNewCloud::CLFamilly_ns:
+                       LWC = 0.29*2.0;
+                       break;
+       }
+       // TODO:send data to radar antenna
+       // NB:data valid only from cockpit view
+
+       // spawn a new lightning
+       if(min_time_before_lt <= 0.0 && (familly == SGNewCloud::CLFamilly_cb) &&
+               dist < 15000.0 * 15000.0 && sg_random() > 0.9f) {
+               double lat, lon;
+               Point3D orig, dest;
+               orig.setlat(last_lat * SG_DEGREES_TO_RADIANS );
+               orig.setlon(last_lon * SG_DEGREES_TO_RADIANS );
+               orig.setelev(0.0);
+               dist = sgSqrt(dist);
+               dest = calc_gc_lon_lat(orig, heading, dist);
+               lon = dest.lon() * SG_RADIANS_TO_DEGREES;
+               lat = dest.lat() * SG_RADIANS_TO_DEGREES;
+               addLightning( lon, lat, alt );
+
+               // reset timer
+               min_time_before_lt = 5.0 + sg_random() * 30;
+               // DEBUG only
+//             min_time_before_lt = 1.0;
+       }
+}
+
+// precipitation rendering code
+void SGEnviro::DrawCone2(float baseRadius, float height, int slices, bool down, double rain_norm, double speed) {
+
+       sgVec3 light;
+       sgVec3 min_light = {0.35, 0.35, 0.35};
+       sgAddVec3( light, fog_color, min_light );
+       float da = SG_PI * 2.0f / (float) slices;
+       // low number = faster
+       float speedf = 2.5f - speed / 200.0;
+       if( speedf < 1.0f )
+               speedf = 1.0f;
+       float lenf = 0.03f + speed / 2000.0;
+       if( lenf > 0.10f )
+               lenf = 0.10f;
+    float t = fmod((float) elapsed_time, speedf) / speedf;
+//     t = 0.1f;
+       if( !down )
+               t = 1.0f - t;
+       float angle = 0.0f;
+       glColor4f(1.0f, 0.7f, 0.7f, 0.9f);
+       glBegin(GL_LINES);
+       int rainpos_indice = 0;
+       for( int i = 0 ; i < slices ; i++ ) {
+               float x = cos(angle) * baseRadius;
+               float y = sin(angle) * baseRadius;
+               angle += da;
+               sgVec3 dir = {x, -height, y};
+
+               // rain drops at 2 different speed to simulate depth
+               float t1 = (i & 1 ? t : t + t) + rainpos[rainpos_indice];
+               if(t1 > 1.0f)   t1 -= 1.0f;
+               if(t1 > 1.0f)   t1 -= 1.0f;
+
+               // distant raindrops are more transparent
+               float c = (i & 1 ? t1 * 0.5f : t1 * 0.9f);
+               glColor4f(c * light[0], c * light[1], c * light[2], c);
+               sgVec3 p1, p2;
+               sgScaleVec3(p1, dir, t1);
+               // distant raindrops are shorter
+               float t2 = t1 + (i & 1 ? lenf : lenf+lenf);
+               sgScaleVec3(p2, dir, t2);
+
+               glVertex3f(p1[0], p1[1] + height, p1[2]);
+               glVertex3f(p2[0], p2[1] + height, p2[2]);
+               if( ++rainpos_indice >= MAX_RAIN_SLICE )
+                       rainpos_indice = 0;
+       }
+       glEnd();
+}
+
+// TODO:check alt vs layer
+void SGEnviro::drawRain(double pitch, double roll, double speed, double rain_norm) {
+
+       glBindTexture(GL_TEXTURE_2D, 0);
+
+       glDisable(GL_DEPTH_TEST);
+       glShadeModel(GL_SMOOTH);
+       glEnable(GL_BLEND);
+       glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+       glDisable( GL_FOG );
+       glDisable(GL_LIGHTING);
+
+       int slice_count = (40.0 + rain_norm*150.0)* precipitation_density / 100.0;
+
+       float angle = speed;
+       if( angle > 90.0 )
+               angle = 90.0;
+
+       glPushMatrix();
+               // TODO:find the real view orientation, not the AC one
+               // the cone rotate with speed
+               angle = -pitch - angle;
+               glRotatef(angle, 1.0, 0.0, 0.0);
+               glRotatef(roll, 0.0, 1.0, 0.0);
+
+               // up cone
+               DrawCone2(15.0, 30.0, slice_count, true, rain_norm, speed);
+               // down cone (usually not visible)
+               if(angle > 0.0)
+                       DrawCone2(15.0, -30.0, slice_count, false, rain_norm, speed);
+
+       glPopMatrix();
+
+       glEnable(GL_LIGHTING);
+       glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
+       glEnable( GL_FOG );
+       glEnable(GL_DEPTH_TEST);
+
+}
+
+void SGEnviro::set_soundMgr(SGSoundMgr *mgr) {
+       soundMgr = mgr;
+}
+
+void SGEnviro::drawPrecipitation(double rain_norm, double snow_norm, double hail_norm, double pitch, double roll, double speed) {
+       // TODO:check alt with right layer (wich layer ?)
+       if( precipitation_enable_state && rain_norm > 0.0)
+               drawRain(pitch, roll, speed, rain_norm);
+}
+
+
+SGLightning::SGLightning(double _lon, double _lat, double _alt) :
+       lon(_lon),
+       lat(_lat),
+       alt(_alt),
+       age(1.0 + sg_random() * 4.0),
+       nb_tree(0)
+{
+//     sequence_count = 1 + sg_random() * 5.0;
+       lt_build();
+}
+
+SGLightning::~SGLightning() {
+}
+
+// lightning rendering code
+void SGLightning::lt_build_tree_branch(int tree_nr, Point3D &start, float energy, int nbseg, float segsize) {
+
+       sgVec3 dir, newdir;
+       int nseg = 0;
+       Point3D pt = start;
+       if( nbseg == 50 )
+               sgSetVec3( dir, 0.0, -1.0, 0.0 );
+       else {
+               sgSetVec3( dir, sg_random() - 0.5f, sg_random() - 0.5f, sg_random() - 0.5f);
+               sgNormaliseVec3(dir);
+       }
+       if( nb_tree >= MAX_LT_TREE_SEG )
+               return;
+
+       lt_tree[nb_tree].depth = tree_nr;
+       nseg = 0;
+       lt_tree[nb_tree].pt = pt;
+       lt_tree[nb_tree].prev = -1;
+       nb_tree ++;
+
+       // TODO:check agl
+       while(nseg < nbseg && pt.y() > 0.0) {
+        int prev = nb_tree - 1;
+        nseg++;
+               // add a branch
+        if( energy * sg_random() > 0.8f )
+                       lt_build_tree_branch(tree_nr + 1, pt, energy * 0.9f, nbseg == 50 ? 10 : nbseg * 0.4f, segsize * 0.7f);
+
+               if( nb_tree >= MAX_LT_TREE_SEG )
+                       return;
+               sgSetVec3(newdir, (sg_random() - 0.5f), (sg_random() - 0.5f) - (nbseg == 50 ? 0.5f : 0.0), (sg_random() - 0.5f));
+               sgNormaliseVec3(newdir);
+               sgAddVec3( dir, newdir);
+               sgNormaliseVec3(dir);
+               sgVec3 scaleDir;
+               sgScaleVec3( scaleDir, dir, segsize * energy * 0.5f );
+               pt[PX] += scaleDir[0];
+               pt[PY] += scaleDir[1];
+               pt[PZ] += scaleDir[2];
+
+               lt_tree[nb_tree].depth = tree_nr;
+               lt_tree[nb_tree].pt = pt;
+               lt_tree[nb_tree].prev = prev;
+               nb_tree ++;
+       }
+}
+
+void SGLightning::lt_build(void) {
+    Point3D top;
+    nb_tree = 0;
+    top[PX] = 0 ;
+    top[PY] = alt;
+    top[PZ] = 0;
+    lt_build_tree_branch(0, top, 1.0, 50, top[PY] / 8.0);
+       if( ! sgEnviro.soundMgr )
+               return;
+       Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
+       Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 );
+       double course = 0.0, dist = 0.0;
+       calc_gc_course_dist( dest, start, &course, &dist );
+       if( dist < 10000.0 && ! sgEnviro.snd_playing && (dist < sgEnviro.snd_dist || ! sgEnviro.snd_active) ) {
+               sgEnviro.snd_timer = 0.0;
+               sgEnviro.snd_wait  = dist / 340;
+               sgEnviro.snd_dist  = dist;
+               sgEnviro.snd_pos_lat = lat;
+               sgEnviro.snd_pos_lon = lon;
+               sgEnviro.snd_active = true;
+               sgEnviro.snd_playing = false;
+       }
+}
+
+
+void SGLightning::lt_Render(void) {
+       float flash = 0.5;
+       if( fmod(sgEnviro.elapsed_time*100.0, 100.0) > 50.0 )
+               flash = sg_random() * 0.75f + 0.25f;
+    float h = lt_tree[0].pt[PY];
+       sgVec4 col={0.62f, 0.83f, 1.0f, 1.0f};
+       sgVec4 c;
+
+#define DRAW_SEG() \
+                       {glBegin(GL_LINES); \
+                               glColor4fv(c); \
+                glVertex3f(lt_tree[n].pt[PX], lt_tree[n].pt[PZ], lt_tree[n].pt[PY]); \
+                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]); \
+                       glEnd();}
+
+       glDepthMask( GL_FALSE );
+       glEnable(GL_BLEND);
+       glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+       glBindTexture(GL_TEXTURE_2D, 0);
+
+       glDisable(GL_LIGHTING);
+       glDisable( GL_FOG );
+       glPushMatrix();
+       sgMat4 modelview, tmp;
+    ssgGetModelviewMatrix( modelview );
+       sgCopyMat4( tmp, sgEnviro.transform );
+    sgPostMultMat4( tmp, modelview );
+    ssgLoadModelviewMatrix( tmp );
+
+    Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
+    Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 );
+    double course = 0.0, dist = 0.0;
+    calc_gc_course_dist( dest, start, &course, &dist );
+    double ax = 0.0, ay = 0.0;
+    ax = cos(course) * dist;
+    ay = sin(course) * dist;
+
+       glTranslatef( ax, ay, -sgEnviro.last_alt );
+//     glTranslatef( ax, ay, 0 );
+
+       for( int n = 0 ; n < nb_tree ; n++ ) {
+        if( lt_tree[n].prev < 0 )
+                       continue;
+
+        float t1 = sgLerp(0.5, 1.0, lt_tree[n].pt[PY] / h);
+               t1 *= flash;
+               if( lt_tree[n].depth >= 2 ) {
+            glLineWidth(3);
+                       sgScaleVec4(c, col, t1 * 0.6f);
+                       DRAW_SEG();
+               } else {
+                       if( lt_tree[n].depth == 0 ) {
+                glLineWidth(12);
+                               sgScaleVec4(c, col, t1 * 0.5f);
+                               DRAW_SEG();
+
+                glLineWidth(6);
+                               sgScaleVec4(c, col, t1);
+                               DRAW_SEG();
+                       } else {
+                glLineWidth(6);
+                               sgScaleVec4(c, col, t1 * 0.7f);
+                               DRAW_SEG();
+                       }
+
+            if( lt_tree[n].depth == 0 ) 
+                glLineWidth(3);
+                       else
+                glLineWidth(2);
+
+            sgSetVec4(c, t1, t1, t1, t1);
+                       DRAW_SEG();
+               }
+
+       }
+    glLineWidth(1);
+       glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
+       glPopMatrix();
+       glDepthMask( GL_TRUE ); 
+       glEnable( GL_FOG );
+       glEnable(GL_LIGHTING);
+}
+
+void SGEnviro::addLightning(double lon, double lat, double alt) {
+       if( lightnings.size() > 10)
+               return;
+       SGLightning *lt= new SGLightning(lon, lat, alt);
+       lightnings.push_back(lt);
+}
+
+void SGEnviro::drawLightning(void) {
+       list_of_lightning::iterator iLightning;
+       // play 'thunder' for lightning
+       if( snd_active )
+               if( !snd_playing ) {
+                       // wait until sound has reached us
+                       snd_timer += dt;
+                       if( snd_timer >= snd_wait ) {
+                               snd_playing = true;
+                               // compute relative position of lightning
+                               Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
+                               Point3D dest( snd_pos_lon*SG_DEGREES_TO_RADIANS, snd_pos_lat*SG_DEGREES_TO_RADIANS, 0.0 );
+                               double course = 0.0, dist = 0.0;
+                               calc_gc_course_dist( dest, start, &course, &dist );
+                               double ax = 0.0, ay = 0.0;
+                               ax = cos(course) * dist;
+                               ay = sin(course) * dist;
+                               SGSoundSample *snd = soundMgr->find("thunder");
+                               if( snd ) {
+                                       ALfloat pos[3]={ax, ay, -sgEnviro.last_alt };
+                                       snd->set_source_pos(pos);
+                                       snd->play_once();
+                               }
+                       }
+               } else {
+                       if( !soundMgr->is_playing("thunder") ) {
+                               snd_active = false;
+                               snd_playing = false;
+                       }
+               }
+
+       if( ! lightning_enable_state )
+               return;
+
+       for( iLightning = lightnings.begin() ; iLightning != lightnings.end() ; iLightning++ ) {
+               if( dt )
+                       if( sg_random() > 0.95f )
+                               (*iLightning)->lt_build();
+               (*iLightning)->lt_Render();
+               (*iLightning)->age -= dt;
+               if( (*iLightning)->age < 0.0 ) {
+                       delete (*iLightning);
+                       lightnings.erase( iLightning );
+                       break;
+               }
+       }
+
+}
+
+
+void SGEnviro::setFOV( float w, float h ) {
+       fov_width = w;
+       fov_height = h;
+}
+
+void SGEnviro::getFOV( float &w, float &h ) {
+       w = fov_width;
+       h = fov_height;
+}
index f8a7e6c0e9fdb0f171cdf6ce1a8ea21a137869b2..695e14440532d983cc824ab5fbf3a4c0d6a11871 100644 (file)
 #ifndef _VISUAL_ENVIRO_HXX
 #define _VISUAL_ENVIRO_HXX
 
-class SGEnviro {
+#include <simgear/compiler.h>
+#include STL_STRING
+
+SG_USING_STD(string);
 
+class SGLightning;
+class SGSoundMgr;
+
+/**
+ * Visual environment helper class.
+ */
+class SGEnviro {
+friend SGLightning;
 private:
+       void DrawCone2(float baseRadius, float height, int slices, bool down, double rain_norm, double speed);
+       void lt_update(void);
+
        bool view_in_cloud;
        bool precipitation_enable_state;
        float precipitation_density;
+       bool turbulence_enable_state;
+       double last_cloud_turbulence, cloud_turbulence;
+       bool lightning_enable_state;
+       double elapsed_time, dt;
+       sgVec4  fog_color;
+       sgMat4 transform;
+       double last_lon, last_lat, last_alt;
+       SGSoundMgr      *soundMgr;
+       bool            snd_active, snd_playing;
+       double          snd_timer, snd_wait, snd_pos_lat, snd_pos_lon, snd_dist;
+       double          min_time_before_lt;
+
+       float fov_width, fov_height;
 
 public:
        SGEnviro();
        ~SGEnviro();
 
-       void startOfFrame(void);
+    /**
+     * Forward a few states used for renderings.
+     */
+       void startOfFrame( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double delta_time);
+
        void endOfFrame(void);
 
+    /**
+     * Whenever a cloud is drawn we check his 'impact' on the environment.
+     * @param heading direction of cloud in radians
+     * @param alt asl of cloud in meters
+     * @param radius radius of cloud in meters
+     * @param familly cloud familly
+     * @param dist  squared dist to cloud in meters
+     */
+       void callback_cloud(float heading, float alt, float radius, int familly, float dist);
+
+       void drawRain(double pitch, double roll, double speed, double rain_norm);
+    /**
+     * Draw rain or snow precipitation around the viewer.
+     * @param rain_norm rain normalized intensity given by metar class
+     * @param snow_norm snow normalized intensity given by metar class
+     * @param hail_norm hail normalized intensity given by metar class
+     * @param pitch pitch rotation of viewer
+     * @param roll roll rotation of viewer
+     * @param speed moving speed of viewer in kt
+     */
+       void drawPrecipitation(double rain_norm, double snow_norm, double hail_norm,
+                                                       double pitch, double roll, double speed);
+
+    /**
+     * Draw the lightnings spawned by cumulo nimbus.
+     */
+       void drawLightning(void);
+
+    /**
+     * Forward the fog color used by the rain rendering.
+     * @param adj_fog_color color of the fog
+     */
+       void setLight(sgVec4 adj_fog_color);
+
        // this can be queried to add some turbulence for example
        bool is_view_in_cloud(void) const;
        void set_view_in_cloud(bool incloud);
+       double get_cloud_turbulence(void) const;
 
        // Clouds
        // return the size of the memory pool used by texture impostors
@@ -47,25 +113,78 @@ public:
        float get_clouds_visibility(void) const;
        float get_clouds_density(void) const;
        bool get_clouds_enable_state(void) const;
+       bool get_turbulence_enable_state(void) const;
 
+    /**
+     * Set the size of the impostor texture cache for 3D clouds.
+     * @param sizeKb size of the texture pool in Kb
+     */
        void set_clouds_CacheSize(int sizeKb);
+    /**
+     * Set the resolution of the impostor texture for 3D clouds.
+     * @param resolutionPixels size of each texture in pixels (64|128|256)
+     */
        void set_CacheResolution(int resolutionPixels);
+    /**
+     * Set the maximum range used when drawing clouds.
+        * Clouds are blended from totaly transparent at max range to totaly opaque around the viewer
+     * @param distance in meters
+     */
        void set_clouds_visibility(float distance);
+    /**
+     * Set the proportion of clouds that will be rendered to limit drop in FPS.
+     * @param density 0..100 no clouds drawn when density == 0, all are drawn when density == 100
+     */
        void set_clouds_density(float density);
+    /**
+     * Enable or disable the use of 3D clouds.
+     * @param enable when false we draw the 2D layers
+     */
        void set_clouds_enable_state(bool enable);
+    /**
+     * Enable or disable the use of proximity cloud turbulence.
+     * @param enable when true the turbulence is computed based on type of cloud around the AC
+     */
+       void set_turbulence_enable_state(bool enable);
 
        // rain/snow
        float get_precipitation_density(void) const;
        bool get_precipitation_enable_state(void) const;
 
        void set_precipitation_density(float density);
+    /**
+     * Enable or disable the rendering of precipitation around the viewer.
+     * @param enable when true we will draw precipitation depending on metar data
+     */
        void set_precipitation_enable_state(bool enable);
 
        // others
        bool get_lightning_enable_state(void) const;
+    /**
+     * Enable or disable the rendering of lightning and the thunder sound.
+     * @param enable when true we will draw lightning spwaned by cumulonimbus
+     */
        void set_lightning_enable_state(bool enable);
+
+    /**
+     * Spawn a new lighning at specified lon/lat.
+     * @param lon position of the new lightning
+     * @param lat position of the new lightning
+     * @param alt asl of the starting point of the lightning in meters
+     */
+       void addLightning(double lon, double lat, double alt);
+
+    /**
+     * Forward the sound manager instance to be able to play samples.
+     * @param mgr a running sound manager
+     */
+       void set_soundMgr(SGSoundMgr *mgr);
+
+       void setFOV( float w, float h );
+       void getFOV( float &w, float &h );
+
 };
 
 extern SGEnviro sgEnviro;
 
-#endif // _VISUAL_ENVIRO_HXX
\ No newline at end of file
+#endif // _VISUAL_ENVIRO_HXX
index 64bb7c6363a89670f1afe36a97b5df7800875795..c9af9c2683fadeff2e690a2eebe3e25f4346124d 100644 (file)
@@ -47,6 +47,7 @@ void SGBbCache::freeTextureMemory(void) {
 
        if( bbListCount ) {
                for(int i = 0 ; i < bbListCount ; i++) {
+                       bbList[i].cldID = 0;
                        if(bbList[i].texID)
                                glDeleteTextures(1, & bbList[i].texID);
                }
@@ -78,10 +79,11 @@ bool SGBbCache::allocTextureMemory(int cacheCount, int textureDimension) {
        cacheSizeKb = (textureDimension * textureDimension * 4);
        cacheSizeKb *= cacheCount;
        cacheSizeKb /= 1024;
-       if(rt) {
-               rt->BeginCapture();
-               glViewport(0, 0, textureDimension, textureDimension);
-               rt->EndCapture();
+       if(rtAvailable) {
+               if( rt->BeginCapture() ) {
+                       glViewport(0, 0, textureDimension, textureDimension);
+                       rt->EndCapture();
+               }
        }
        return true;
 }
@@ -113,9 +115,9 @@ void SGBbCache::init(int cacheCount) {
        rt->Reset("rgba tex2D ctt");
 //     rt->Reset("rgba tex2D");
        if( rt->Initialize(256, 256, true) ) {
-               rtAvailable = true;
                if (rt->BeginCapture())
                {
+                       rtAvailable = true;
                        glViewport(0, 0, 256, 256);
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
@@ -219,6 +221,7 @@ int SGBbCache::alloc(int cldId) {
                        bbList[i].angleX = -999;
                        bbList[i].angleY = -999;
                        bbList[i].frameUsed = 0;
+                       bbList[i].needRedraw = true;
                        return i;
                }
        }
@@ -279,6 +282,7 @@ void SGBbCache::setTextureData(int bbId) {
 //    bbList[bbId].angleY = angleY;
     bbList[bbId].frame = frameNumber;
        bbList[bbId].frameUsed = frameNumber;
+       bbList[bbId].needRedraw = false;
     builtBBCount ++;
        builtBBframe ++;
 }
@@ -305,11 +309,14 @@ bool SGBbCache::isBbValid( int cldId, int bbId, float angleY, float angleX) {
        if( builtBBframe >= maxImpostorRegenFrame )
                return true;
 
-    if( fabs(angleY - bbList[bbId].angleY) >= 4.0 )
-        return false;
+       if( bbList[bbId].needRedraw )
+               return false;
+
+//    if( fabs(angleY - bbList[bbId].angleY) >= 4.0 )
+//        return false;
 
-    if( fabs(angleX - bbList[bbId].angleX) >= 4.0 )
-        return false;
+//    if( fabs(angleX - bbList[bbId].angleX) >= 4.0 )
+//        return false;
 
        bbList[bbId].frameUsed = frameNumber;
        return true;
@@ -328,7 +335,7 @@ void SGBbCache::setReference( int cldId, int bbId, float angleY, float angleX) {
 void SGBbCache::startNewFrame(void) {
        builtBBframe = 0;
        // TOTO:find reasonable value
-       int minFrameNumber = frameNumber - 500;
+       int minFrameNumber = frameNumber - 100;
        frameNumber++;
        // cleanup of unused enties
        for( int bbId = 0 ; bbId < bbListCount ; bbId++)
@@ -337,3 +344,21 @@ void SGBbCache::startNewFrame(void) {
                        bbList[bbId].cldID = 0;
                }
 }
+
+// force all impostors to be rebuilt, this will enventually be done over several frames
+void SGBbCache::invalidateCache(void) {
+
+       for( int bbId = 0 ; bbId < bbListCount ; bbId++)
+//             bbList[bbId].cldID = 0;
+               bbList[bbId].needRedraw = true;
+}
+
+// flag the impostor for a lazy update
+void SGBbCache::invalidate(int cldId, int bbId) {
+       if( bbId < 0 || bbId >= bbListCount )
+               return;
+       if( bbList[bbId].cldID != cldId )
+               return;
+       bbList[bbId].needRedraw = true;
+}
+
index f2ba56419d30315629f54509ab1b5e79b0c6c3b4..fae611de8472a470b6a90f966ac2ef80a5107107 100644 (file)
 #include <simgear/screen/extensions.hxx>
 #include <simgear/screen/RenderTexture.h>
 
+/**
+ * Billboard helper class.
+ */
 class SGBbCache {
 private:
 
-       typedef struct {
+       /**
+       * storage class for impostors state.
+       */
+       class bbInfo {
+       public:
+               /// the texture used by this impostor
                GLuint  texID;
+               /// the cloud owning this impostor
                int             cldID;
                float   angleX, angleY;
                // creation frame number for debug only
                int             frame;
-               // last time this entry was used
+               /// last time this entry was used
                int             frameUsed;
-       } bbInfo;
+               /// dirty flag for lazy rebuild of impostor
+               bool    needRedraw;
+       };
 
        void freeTextureMemory(void);
+    /**
+     * Allocate and initialize the texture pool.
+     * @param count the number of texture to build
+     * @param textureDimension size in pixel of each texture
+     */
        bool allocTextureMemory(int count, int textureDimension);
 
        // a list of impostors
@@ -64,53 +80,126 @@ public:
        SGBbCache(void);
        ~SGBbCache(void);
 
-       // call this first to initialize everything, cacheCount is the number of texture to allocate
+    /**
+     * Call this first to initialize the cache.
+     * @param cacheCount the number of texture to allocate
+     */
        void init(int cacheCount);
 
-       // free one cache slot, usualy when the cached object is destroyed
+    /**
+     * Free one cache slot, usualy when the cached object is destroyed.
+     * @param bbId the impostor slot
+     * @param cldId the cloud identifier
+     */
        void free(int bbId, int cldId);
 
-       // allocate a new texture, return an index in the cache
+    /**
+     * Allocate a new impostor.
+     * @param cldId the cloud identifier
+     * @return an impostor slot
+     */
        int alloc(int cldId);
 
-       // give the texture name to use
+    /**
+     * Query the texture name associated with this cloud.
+     * @param bbId the impostor slot
+     * @param cldId the cloud identifier
+     * @return a texture name
+     */
        GLuint QueryTexID(int cldId, int bbId);
 
-       // save the rendered texture from the current context to a new texture
+    /**
+     * Save the rendered texture from the current context to a new texture.
+     * @param bbId the impostor slot
+     */
        void setTextureData(int bbId);
 
-       // start the rendering of a billboard in the RTT context
+    /**
+     * Start the rendering of a billboard in the RTT context.
+     */
        void beginCapture(void);
 
-       // adjust the projection matrix of the RTT context to the size of the object
+    /**
+     * Adjust the projection matrix of the RTT context to the size of the object.
+     * @param radius radius in meters of the object to draw
+     * @param dist_center distance between viewer and object
+     */
        void setRadius(float radius, float dist_center);
 
-       // forget the RTT and go back to the previous rendering context
+    /**
+     * Forget the RTT and go back to the previous rendering context.
+     */
        void endCapture(void);
 
-       // for debugging only, give the number of frames since the inpostor was built
+    /**
+     * For debugging only, give the number of frames since the impostor was built.
+     * @param bbId the impostor slot
+     */
        int queryImpostorAge(int bbId);
 
-       // can we still use this impostor ?
+    /**
+     * Can we still use this impostor ? Check versus view angles and load.
+     * @param bbId the impostor slot
+     * @param cloudId the cloud identifier
+     * @param angleY rotation needed to face the impostor
+     * @param angleX rotation needed to face the impostor
+     */
        bool isBbValid( int cloudId, int bbId, float angleY, float angleX);
 
-       // save view angles of this billboard
+    /**
+     * Save view angles of this billboard.
+     * @param bbId the impostor slot
+     * @param cloudId the cloud identifier
+     * @param angleY rotation needed to face the impostor
+     * @param angleX rotation needed to face the impostor
+     */
        void setReference( int cloudId, int bbId, float angleY, float angleX);
 
-       // prepare the cache for the rendering of a new frame
+    /**
+     * Prepare the cache for the rendering of a new frame.
+     * Do some garbage collect of unused impostors
+     */
        void startNewFrame(void);
 
-       // alloc the impostors texture memory given the size of the memory pool
-       // if sizeKb == 0 then the memory pool is freed and impostors are disabled
+    /**
+     * Alloc the impostors texture memory given the size of the memory pool.
+        * If sizeKb == 0 then the memory pool is freed and impostors are disabled
+     * @param sizeKb size of the texture pool in K
+     */
        bool setCacheSize(int sizeKb);
 
-       // alloc the impostors texture memory given the count and size of texture
-       // if count == 0 then the memory pool is freed and impostors are disabled
+    /**
+     * Alloc the impostors texture memory given the count and size of texture.
+        * If count == 0 then the memory pool is freed and impostors are disabled
+     * @param count number of texture to allocate
+     * @param textureDimension size of each texture in pixels
+     */
        bool setCacheSize(int count, int textureDimension);
 
-       // return the size of the memory pool used by texture impostors
+       bool isRttAvailable(void) { return rtAvailable; }
+
+    /**
+     * Force all impostors to be rebuilt.
+     */
+       void invalidateCache(void);
+
+    /**
+     * Flag the impostor for a lazy update.
+     * @param bbId the impostor slot
+     * @param cldId the cloud identifier
+     */
+       void invalidate(int cldId, int bbId);
+
+    /**
+     * Return the size of the memory pool used by texture impostors.
+     * @return size of the memory pool in Kb
+     */
        int queryCacheSize(void);
 
+    /**
+     * Maximum number of impostor to regen each frame.
+     * If we can't update all of them we will do that in the next frame
+     */
        int maxImpostorRegenFrame;
 };
 
index 776044a3c5cf13624ebfb66d4157813c362098c7..1dd6034f6f02e8f98435a0c8e22a53dc8969fcbc 100644 (file)
@@ -843,7 +843,7 @@ bool SGCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat,
         last_lat = lat;
     }
 
-       layer3D->reposition( p, up, lon, lat, alt, dt);
+       layer3D->reposition( p, up, lon, lat, alt, dt, direction, speed);
     return true;
 }
 
@@ -851,7 +851,7 @@ bool SGCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat,
 void SGCloudLayer::draw( bool top ) {
     if ( layer_coverage != SG_CLOUD_CLEAR ) {
 
-               if ( SGCloudField::enable3D )
+               if ( SGCloudField::enable3D && layer3D->is3D())
                        layer3D->Render();
                else
         if ( bump_mapping && enable_bump_mapping ) {
index 9d0f139e6a3c3d113cdf8849ab1694c23fd0f5e5..6a179d9f768719ef2d8d7ac67564d419ad089e3d 100644 (file)
@@ -184,6 +184,9 @@ public:
 
     static bool enable_bump_mapping;
 
+       /** return the 3D layer cloud associated with this 2D layer */
+       SGCloudField *get_layer3D(void) { return layer3D; }
+
 private:
 
     struct CloudVertex {
index af0abc33423e3534a00b19b8f73470fb9da3db18..0deadad7a86a151207612904ad48d6331688c6be 100644 (file)
@@ -45,13 +45,17 @@ static list_of_culledCloud inViewClouds;
 
 // visibility distance for clouds in meters
 float SGCloudField::CloudVis = 25000.0f;
-bool SGCloudField::enable3D = true;
+bool SGCloudField::enable3D = false;
 // fieldSize must be > CloudVis or we can destroy the impostor cache
 // a cloud must only be seen once or the impostor will be generated for each of his positions
-double SGCloudField::fieldSize = 27000.0;
+double SGCloudField::fieldSize = 50000.0;
 float SGCloudField::density = 100.0;
+double SGCloudField::timer_dt = 0.0;
+sgVec3 SGCloudField::view_vec;
+
 static int last_cache_size = 1*1024;
 static int cacheResolution = 64;
+static sgVec3 last_sunlight={0.0f, 0.0f, 0.0f};
 
 int SGCloudField::get_CacheResolution(void) {
        return cacheResolution;
@@ -85,7 +89,6 @@ void SGCloudField::set_CacheSize(int sizeKb) {
                int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
                if(count == 0)
                        count = 1;
-//             SGNewCloud::cldCache->setCacheSize(sizeKb);
                SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
        }
 }
@@ -101,14 +104,17 @@ void SGCloudField::set_enable3dClouds(bool enable) {
                return;
        enable3D = enable;
        if(enable) {
-               SGNewCloud::cldCache->setCacheSize(last_cache_size);
+               int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
+               if(count == 0)
+                       count = 1;
+               SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
        } else {
                SGNewCloud::cldCache->setCacheSize(0);
        }
 }
 
 // reposition the cloud layer at the specified origin and orientation
-void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt) {
+void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt, float direction, float speed) {
     sgMat4 T1, LON, LAT;
     sgVec3 axis;
 
@@ -135,8 +141,6 @@ void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, doub
        this->alt = alt;
 
        // simulate clouds movement from wind
-       double speed = 10.0f;
-       double direction = 45.0;
     double sp_dist = speed*dt;
     if (sp_dist > 0) {
         double bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
@@ -181,11 +185,16 @@ void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, doub
        // correct the frustum with the right far plane
        ssgContext *context = ssgGetCurrentContext();
        frustum = *context->getFrustum();
-       frustum.setFOV(55.0,0);
+
+       float w, h;
+       sgEnviro.getFOV( w, h );
+       frustum.setFOV( w, h );
        frustum.setNearFar(1.0, CloudVis);
+       timer_dt = dt;
 }
 
 SGCloudField::SGCloudField() :
+       draw_in_3d(true),
        last_density(0.0),
        deltax(0.0),
        deltay(0.0),
@@ -206,6 +215,18 @@ SGCloudField::~SGCloudField() {
 }
 
 
+void SGCloudField::clear(void) {
+       list_of_Cloud::iterator iCloud;
+       for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
+               delete iCloud->aCloud;
+       }
+       theField.clear();
+       // force a recompute of density on first redraw
+       last_density = 0.0;
+       // true to come back in set density after layer is built
+       draw_in_3d = true;
+}
+
 // use a table or else we see poping when moving the slider...
 static int densTable[][10] = {
        {0,0,0,0,0,0,0,0,0,0},
@@ -235,15 +256,16 @@ void SGCloudField::applyDensity(void) {
                        iCloud->visible = false;
        }
        last_density = density;
+       draw_in_3d = ( theField.size() != 0);
 }
 
 // add one cloud, data is not copied, ownership given
 void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
        Cloud cl;
-       sgCopyVec3( cl.pos, pos );
        cl.aCloud = cloud;
        cl.visible = true;
        cloud->SetPos( pos );
+       sgCopyVec3( cl.pos, *cloud->getCenter() );
        theField.push_back( cl );
 }
 
@@ -260,7 +282,7 @@ void SGCloudField::buildTestLayer(void) {
 
        for( int z = -5 ; z <= 5 ; z++) {
                for( int x = -5 ; x <= 5 ; x++ ) {
-            SGNewCloud *cloud = new SGNewCloud;
+                       SGNewCloud *cloud = new SGNewCloud(SGNewCloud::CLFamilly_cu);
                        cloud->new_cu();
                        sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s};
             addCloud(pos, cloud);
@@ -291,6 +313,8 @@ void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
                        sgCopyVec3( tmp.eyePos, eyePos ); 
                        // save distance for later sort, opposite distance because we want back to front
                        tmp.dist   =  - squareDist;
+                       tmp.heading = -SG_PI/2.0 - atan2( dist[0], dist[2] ); // + SG_PI;
+                       tmp.alt         = iCloud->pos[1];
                        inViewClouds.push_back(tmp);
                        if( squareDist - radius*radius < 100*100 )
                                sgEnviro.set_view_in_cloud(true);
@@ -316,9 +340,11 @@ void SGCloudField::Render(void) {
                applyDensity();
        }
 
-       // ask the impostor cache to do some cleanup
-       // TODO:don't do that for every field
-       SGNewCloud::cldCache->startNewFrame();
+       if( ! draw_in_3d )
+               return;
+
+       if( ! SGNewCloud::cldCache->isRttAvailable() )
+               return;
 
        inViewClouds.clear();
 
@@ -332,14 +358,22 @@ void SGCloudField::Render(void) {
     sgVec3 lightVec;
     ssgGetLight( 0 )->getPosition( lightVec );
     sgXformVec3( lightVec, invtrans );
-       sgCopyVec3( SGNewCloud::modelSunDir, lightVec );
+
        sgSetVec3(  SGNewCloud::modelSunDir, lightVec[0], lightVec[2], lightVec[1]);
-       // try to find the lighting data (buggy)
+       // try to find the lighting data (not accurate)
        sgVec4 diffuse, ambient;
        ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
        ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient );
-       sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 0.70f);
-       sgScaleVec3 ( SGNewCloud::ambLight, ambient , 0.60f);
+       sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 1.0f);
+       sgScaleVec3 ( SGNewCloud::ambLight, ambient , 1.0f);
+
+       sgVec3 delta_light;
+       sgSubVec3(delta_light, last_sunlight, SGNewCloud::sunlight);
+       if( (fabs(delta_light[0]) + fabs(delta_light[1]) + fabs(delta_light[2])) > 0.05f ) {
+               sgCopyVec3( last_sunlight, SGNewCloud::sunlight );
+               // force the redraw of all the impostors
+               SGNewCloud::cldCache->invalidateCache();
+       }
 
        // voodoo things on the matrix stack
     ssgGetModelviewMatrix( modelview );
@@ -348,10 +382,13 @@ void SGCloudField::Render(void) {
 
        // cloud fields are tiled on the flat earth
        // compute the position in the tile
-       relx = -fmod( deltax + relative_position[SG_X] + tmp[3][0], fieldSize );
-       rely = -fmod( deltay + relative_position[SG_Y] + tmp[3][1], fieldSize );
+       relx = fmod( deltax + relative_position[SG_X] + tmp[3][0], fieldSize );
+       rely = fmod( deltay + relative_position[SG_Y] + tmp[3][1], fieldSize );
 
-       sgSetVec3( eyePos, -relx, alt, -rely);
+       relx = fmod( relx + fieldSize, fieldSize );
+       rely = fmod( rely + fieldSize, fieldSize );
+       sgSetVec3( eyePos, relx, alt, rely);
+       sgCopyVec3( view_vec, tmp[1] );
 
        tmp[3][2] = 0;
        tmp[3][0] = 0;
@@ -400,6 +437,8 @@ void SGCloudField::Render(void) {
        for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) {
 //             iCloud->aCloud->drawContainers();
                iCloud->aCloud->Render(iCloud->eyePos);
+               sgEnviro.callback_cloud(iCloud->heading, iCloud->alt, 
+                       iCloud->aCloud->getRadius(), iCloud->aCloud->getFamilly(), - iCloud->dist);
        }
 
        glBindTexture(GL_TEXTURE_2D, 0);
index 2a547eb61497cac658f1bc2e11c70da2132b2747..8bd8599f1a0c9f5928cc2eb8e4e30ac51ecefcab 100644 (file)
@@ -37,12 +37,17 @@ public:
        SGNewCloud      *aCloud;
        sgVec3          eyePos;
        float           dist;
+       float           heading;
+       float           alt;
        bool operator<(const culledCloud &b) const {
-               return this->dist < b.dist;
+               return (this->dist < b.dist);
        }
 };
 typedef vector<culledCloud> list_of_culledCloud;
 
+/**
+ * A layer of 3D clouds.
+ */
 class SGCloudField {
 
 private:
@@ -51,12 +56,6 @@ private:
                SGNewCloud      *aCloud;
                sgVec3          pos;
                bool            visible;
-//             float           dist;
-//             bool            culled;
-
-//             bool operator<(const Cloud &b) {
-//                     return this->dist < b.dist;
-//             }
        };
 
 
@@ -79,12 +78,15 @@ private:
     double last_lon, last_lat, last_course;
 
        float   last_density;
+       bool    draw_in_3d;
 
 public:
 
        SGCloudField();
        ~SGCloudField();
 
+       void clear(void);
+
        // add one cloud, data is not copied, ownership given
        void addCloud( sgVec3 pos, SGNewCloud *cloud);
 
@@ -95,13 +97,17 @@ public:
        void Render(void);
 
        // reposition the cloud layer at the specified origin and orientation
-       void reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt);
+       void reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt, float direction, float speed);
+
+       bool is3D(void) { return draw_in_3d; }
 
        // visibility distance for clouds in meters
        static float CloudVis;
 
-       static float density;
+       static sgVec3 view_vec;
 
+       static float density;
+       static double timer_dt;
        static double fieldSize;
        static bool enable3D;
 
index 88286eeee218e6929b013c5efcd6c21bf786151a..2fd119b0d5c9e6c9369dd73eaa88c1c7e257b0fe 100644 (file)
@@ -47,6 +47,8 @@ static ssgTexture *cloudTextures[SGNewCloud::CLTexture_max];
 bool SGNewCloud::useAnisotropic = true;
 SGBbCache *SGNewCloud::cldCache = 0;
 static bool texturesLoaded = false;
+static float minx, maxx, miny, maxy, minz, maxz;
+
 float SGNewCloud::nearRadius = 3500.0f;
 bool SGNewCloud::lowQuality = false;
 sgVec3 SGNewCloud::sunlight = {0.5f, 0.5f, 0.5f};
@@ -54,25 +56,60 @@ sgVec3 SGNewCloud::ambLight = {0.5f, 0.5f, 0.5f};
 sgVec3 SGNewCloud::modelSunDir = {0,1,0};
 
 
-// constructor
-SGNewCloud::SGNewCloud() :
-       bbId(-1),
-//     rank(-1),
-       minx(999), miny(999), minz(999), maxx(-999), maxy(-999), maxz(-999)
-
-{
+void SGNewCloud::init(void) {
+       bbId = -1;
+       fadeActive = false;
+       duration = 100.0f;
+       fadetimer = 100.0f;
+       pauseLength = 0.0f;
+       last_step = -1.0f;
+       familly = CLFamilly_nn;
        cloudId = (int) this;
        sgSetVec3(center, 0.0f, 0.0f, 0.0f);
        sgSetVec3(cloudpos, 0.0f, 0.0f, 0.0f);
+       radius = 0.0f;
+       delta_base = 0.0f;
        list_spriteContainer.reserve(8);
        list_spriteDef.reserve(40);
-//     if( ! texturesLoaded ) {}
+
        if( cldCache == 0 ) {
                cldCache = new SGBbCache;
                cldCache->init( 64 );
        }
 }
 
+// constructor
+SGNewCloud::SGNewCloud(CLFamilly_type classification)
+{
+       init();
+       familly = classification;
+}
+
+SGNewCloud::SGNewCloud(string classification)
+{
+       init();
+       if( classification == "cu" )
+               familly = CLFamilly_cu;
+       else if( classification == "cb" )
+               familly = CLFamilly_cb;
+       else if( classification == "st" )
+               familly = CLFamilly_st;
+       else if( classification == "ns" )
+               familly = CLFamilly_ns;
+       else if( classification == "sc" )
+               familly = CLFamilly_sc;
+       else if( classification == "as" )
+               familly = CLFamilly_as;
+       else if( classification == "ac" )
+               familly = CLFamilly_ac;
+       else if( classification == "ci" )
+               familly = CLFamilly_ci;
+       else if( classification == "cc" )
+               familly = CLFamilly_cc;
+       else if( classification == "cs" )
+               familly = CLFamilly_cs;
+}
+
 SGNewCloud::~SGNewCloud() {
        list_spriteDef.clear();
        list_spriteContainer.clear();
@@ -101,12 +138,26 @@ void SGNewCloud::loadTextures(const string &tex_path) {
 }
 
 void SGNewCloud::startFade(bool direction, float duration, float pauseLength) {
+      if(duration <= 0.0) {
+              fadeActive = false;
+              return;
+      }
+      this->direction = direction;
+      fadetimer = 0.0;
+      this->duration = duration;
+      this->pauseLength = pauseLength;
+      last_step = -1.0;
+      fadeActive = true;
 }
 void SGNewCloud::setFade(float howMuch) {
+      duration = 100.0;
+      fadetimer = howMuch;
+      fadeActive = false;
+      last_step = -1.0;
 }
 
 
-static float rayleighCoeffAngular(float cosAngle) {
+static inline float rayleighCoeffAngular(float cosAngle) {
        return 3.0f / 4.0f * (1.0f + cosAngle * cosAngle);
 }
 
@@ -128,10 +179,10 @@ static void PolarToCart3d(sgVec3 p, sgVec3 cart) {
 // compute the light for a cloud sprite corner
 // from the normal and the sun, scaled by the Rayleigh factor
 // and finaly added to the ambient light
-static void lightFunction(sgVec3 normal, sgVec4 light, float pf) {
+static inline void lightFunction(sgVec3 normal, sgVec4 light, float pf) {
        float cosAngle = sgScalarProductVec3( normal, SGNewCloud::modelSunDir);
-       float vl = (1.0f - 0.1f + cosAngle / 10.0f) * pf;
-       sgScaleVec3( light, SGNewCloud::sunlight, vl );
+       float vl = (1.0f - 0.5f + cosAngle * 0.5f) * pf;
+       sgScaleVec3( light, SGNewCloud::sunlight, 0.25f + 0.75f * vl );
        sgAddVec3( light, SGNewCloud::ambLight );
        // we need to clamp or else the light will bug when adding transparency
        if( light[0] > 1.0 )    light[0] = 1.0;
@@ -142,11 +193,10 @@ static void lightFunction(sgVec3 normal, sgVec4 light, float pf) {
 
 // compute the light for a cloud sprite
 // we use ambient light and orientation versus sun position
-// TODO:check sun pos and check code
 void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) {
        // constant Rayleigh factor if we are not doing Anisotropic lighting
        float pf = 1.0f;
-       const float ang = 45.0f * SG_PI / 180.0f;
+
        list_of_spriteDef::iterator iSprite;
        for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
                if( useAnisotropic ) {
@@ -156,44 +206,11 @@ void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) {
             float cosAngle = sgScalarProductVec3(eyeDir, modelSunDir);
             pf = rayleighCoeffAngular(cosAngle);
                }
-               // compute the vector going from the container box center to the sprite
-               // TODO : this is a constant except for cloudpos, compute the normal in setpos function
-               sgVec3 normal;
-               spriteContainer *thisSpriteContainer = &list_spriteContainer[iSprite->box];
-        sgSubVec3(normal, iSprite->pos, thisSpriteContainer->pos);
-        sgSubVec3(normal, thisSpriteContainer->center);
-        sgSubVec3(normal, cloudpos);
-        sgNormaliseVec3(normal);
-               if( lowQuality ) {
-                       // juste use the traditional normal to compute some lightning
-                       sgVec4 centerColor;
-                       lightFunction(normal, centerColor, pf);
-                       sgCopyVec4(iSprite->l0, centerColor);
-                       sgCopyVec4(iSprite->l1, centerColor);
-                       sgCopyVec4(iSprite->l2, centerColor);
-                       sgCopyVec4(iSprite->l3, centerColor);
+               lightFunction(iSprite->n0, iSprite->l0, pf);
+               lightFunction(iSprite->n1, iSprite->l1, pf);
+               lightFunction(iSprite->n2, iSprite->l2, pf);
+               lightFunction(iSprite->n3, iSprite->l3, pf);
 
-               } else {
-                       // use exotic lightning function, this will give more 'relief' to the clouds
-                       // compute a normal for each vextex this will simulate a smooth shading for a round shape
-                       sgVec3 polar, cart, pt;
-                       // I suspect this code to be bugged...
-            CartToPolar3d(normal, polar);
-
-                       // offset the normal vector by some angle for each vertex
-            sgSetVec3(pt, polar[0] - ang, polar[1] - ang, polar[2]);
-            PolarToCart3d(pt, cart);
-            lightFunction(cart, iSprite->l0, pf);
-            sgSetVec3(pt, polar[0] + ang, polar[1] - ang, polar[2]);
-            PolarToCart3d(pt, cart);
-            lightFunction(cart, iSprite->l1, pf);
-            sgSetVec3(pt, polar[0] + ang, polar[1] + ang, polar[2]);
-            PolarToCart3d(pt, cart);
-            lightFunction(cart, iSprite->l2, pf);
-            sgSetVec3(pt, polar[0] - ang, polar[1] + ang, polar[2]);
-            PolarToCart3d(pt, cart);
-            lightFunction(cart, iSprite->l3, pf);
-               }
        }
 }
 
@@ -206,13 +223,16 @@ void SGNewCloud::addContainer (float x, float y, float z, float r, CLbox_type ty
        cont.cont_type = type;
        sgSetVec3( cont.center, 0.0f, 0.0f, 0.0f);
        list_spriteContainer.push_back( cont );
+       // don't place cloud below his base
+       if( y - r*0.50 < delta_base )
+               delta_base = y - r*0.50;
 }
 
 // add a sprite inside a box
 void SGNewCloud::addSprite(float x, float y, float z, float r, CLbox_type type, int box) {
        spriteDef newSpriteDef;
        int rank = list_spriteDef.size();
-       sgSetVec3( newSpriteDef.pos, x, y, z);
+       sgSetVec3( newSpriteDef.pos, x, y - delta_base, z);
        newSpriteDef.box = box;
        newSpriteDef.sprite_type = type;
        newSpriteDef.rank = rank;
@@ -223,7 +243,7 @@ void SGNewCloud::addSprite(float x, float y, float z, float r, CLbox_type type,
        sgSubVec3( deltaPos, newSpriteDef.pos, thisBox->pos );
        sgAddVec3( thisBox->center, deltaPos );
 
-       r = r * 0.6f;   // 0.5 * 1.xxx
+       r = r * 0.65f;  // 0.5 * 1.xxx
     if( x - r < minx )
                minx = x - r;
     if( y - r < miny )
@@ -248,19 +268,21 @@ static float Rnd(float n) {
 void SGNewCloud::genSprites( void ) {
        float x, y, z, r;
     int N, sc;
+       minx = miny = minz = 99999.0;
+       maxx = maxy = maxz = -99999.0;
+
     N = list_spriteContainer.size();
        for(int i = 0 ; i < N ; i++ ) {
                spriteContainer *thisBox = & list_spriteContainer[i];
                // the type defines how the sprites can be positioned inside the box, their size, etc
                switch(thisBox->cont_type) {
                        case CLbox_sc:
-                               for( sc = 0 ; sc <= 4 ; sc ++ ) {
-                                       r = thisBox->r + Rnd(0.2f);
-                                       x = thisBox->pos[SG_X] + Rnd(thisBox->r);
-                                       y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.2f);
-                                       z = thisBox->pos[SG_Z] + Rnd(thisBox->r);
-                                       addSprite(x, y, z, r, thisBox->cont_type, i);
-                               }
+                               sc = 1;
+                               r = thisBox->r + Rnd(0.2f);
+                               x = thisBox->pos[SG_X] + Rnd(thisBox->r * 0.75f);
+                               y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.75f);
+                               z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f);
+                               addSprite(x, y, z, r, thisBox->cont_type, i);
                                break;
                        case CLbox_stratus:
                                sc = 1;
@@ -302,10 +324,46 @@ void SGNewCloud::genSprites( void ) {
     radius /= 2.0f;
     sgSetVec3( center, (maxx + minx) / 2.0f, (maxy + miny) / 2.0f, (maxz + minz) / 2.0f );
 
-/*    fadingrank = 0
-'    fadingrank = UBound(tbSpriteDef()) * 10
-    fadingdir = 0*/
-       // TODO : compute initial sprite normals for lighting function
+       const float ang = 45.0f * SG_PI / 180.0f;
+
+       // compute normals
+       list_of_spriteDef::iterator iSprite;
+       for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
+               sgVec3 normal;
+               spriteContainer *thisSpriteContainer = &list_spriteContainer[iSprite->box];
+               if( familly == CLFamilly_sc || familly == CLFamilly_cu || familly == CLFamilly_cb) {
+                       sgSubVec3(normal, iSprite->pos, center);
+               } else {
+                       sgSubVec3(normal, iSprite->pos, thisSpriteContainer->pos);
+                       sgSubVec3(normal, thisSpriteContainer->center);
+                       sgSubVec3(normal, cloudpos);
+               }
+               if( normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f )
+                       sgSetVec3( normal, 0.0f, 1.0f, 0.0f );
+        sgNormaliseVec3(normal);
+               // use exotic lightning function, this will give more 'relief' to the clouds
+               // compute a normal for each vextex this will simulate a smooth shading for a round shape
+               sgVec3 polar, pt;
+               // I suspect this code to be bugged...
+        CartToPolar3d(normal, polar);
+               sgCopyVec3(iSprite->normal, normal);
+
+               // offset the normal vector by some angle for each vertex
+        sgSetVec3(pt, polar[0] - ang, polar[1] - ang, polar[2]);
+        PolarToCart3d(pt, iSprite->n0);
+        sgSetVec3(pt, polar[0] + ang, polar[1] - ang, polar[2]);
+        PolarToCart3d(pt, iSprite->n1);
+        sgSetVec3(pt, polar[0] + ang, polar[1] + ang, polar[2]);
+        PolarToCart3d(pt, iSprite->n2);
+        sgSetVec3(pt, polar[0] - ang, polar[1] + ang, polar[2]);
+        PolarToCart3d(pt, iSprite->n3);
+       }
+
+       // experimental : clouds are dissipating with time
+       if( familly == CLFamilly_cu ) {
+               startFade(true, 300.0f, 30.0f);
+               fadetimer = sg_random() * 300.0f;
+       }
 }
 
 
@@ -364,7 +422,6 @@ void SGNewCloud::SetPos(sgVec3 newPos) {
        }
        sgAddVec3( center, deltaPos );
     sgSetVec3( cloudpos, newPos[SG_X], newPos[SG_Y], newPos[SG_Z]);
-       // TODO : recompute sprite normal so we don't have to redo that each frame
 }
 
 
@@ -393,18 +450,30 @@ void SGNewCloud::sortSprite( sgVec3 eye ) {
 // render the cloud on screen or on the RTT texture to build the impostor
 void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos, float dist_center ) {
 
-/*    int clrank                = fadingrank / 10;
-       int clfadeinrank = fadingrank - clrank * 10;*/
-       float CloudVisFade = 1.0 / (1.5 * SGCloudField::get_CloudVis());
+       float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration;
+       int clrank = (int) step;
+       float clfadeinrank = (step - clrank);
+       last_step = step;
+
+       float CloudVisFade = 1.0 / (0.7f * SGCloudField::get_CloudVis());
+       // blend clouds with sky based on distance to limit the contrast of distant cloud
+       float t = 1.0f - dist_center * CloudVisFade;
+//     if ( t < 0.0f ) 
+//             return;
 
     computeSimpleLight( FakeEyePos );
 
     // view point sort, we sort because of transparency
        sortSprite( FakeEyePos );
 
+       float dark = (familly == CLFamilly_cb ? 0.9f : 1.0f);
+
        GLint previousTexture = -1, thisTexture;
        list_of_spriteDef::iterator iSprite;
        for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
+               // skip this sprite if faded
+               if(iSprite->rank > clrank)
+                       continue;
                // choose texture to use depending on sprite type
                switch(iSprite->sprite_type) {
                        case CLbox_stratus:
@@ -421,36 +490,40 @@ void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos,
                }
 
                        sgVec3 translate;
-                       if( drawBB ) {
-                               sgCopyVec3( translate, iSprite->pos);
-                               sgSubVec3( translate, iSprite->pos, deltaPos );
-                       }
-                       else
-                               sgSubVec3( translate, iSprite->pos, deltaPos);
+                       sgSubVec3( translate, iSprite->pos, deltaPos);
 
 
                        // flipx and flipy are random texture flip flags, this gives more random clouds
                        float flipx = (float) ( iSprite->rank & 1 );
                        float flipy = (float) ( (iSprite->rank >> 1) & 1 );
                        // cu texture have a flat bottom so we can't do a vertical flip
-                       if( iSprite->sprite_type == CLbox_cumulus || iSprite->sprite_type == CLbox_stratus )
+                       if( iSprite->sprite_type == CLbox_cumulus )
                                flipy = 0.0f;
-                       if( iSprite->sprite_type == CLbox_stratus )
-                               flipx = 0.0f;
+//                     if( iSprite->sprite_type == CLbox_stratus )
+//                             flipx = 0.0f;
                        // adjust colors depending on cloud type
                        // TODO : rewrite that later, still experimental
                        switch(iSprite->sprite_type) {
                                case CLbox_cumulus:
                                        // dark bottom
-                    sgScaleVec3(iSprite->l0, 0.6f);
-                    sgScaleVec3(iSprite->l1, 0.6f);
+                    sgScaleVec3(iSprite->l0, 0.8f * dark);
+                    sgScaleVec3(iSprite->l1, 0.8f * dark);
+                                       sgScaleVec3(iSprite->l2, dark);
+                                       sgScaleVec3(iSprite->l3, dark);
                                        break;
                                case CLbox_stratus:
                                        // usually dark grey
-                    sgScaleVec3(iSprite->l0, 0.8f);
-                    sgScaleVec3(iSprite->l1, 0.8f);
-                    sgScaleVec3(iSprite->l2, 0.8f);
-                    sgScaleVec3(iSprite->l3, 0.8f);
+                                       if( familly == CLFamilly_st ) {
+                                               sgScaleVec3(iSprite->l0, 0.8f);
+                                               sgScaleVec3(iSprite->l1, 0.8f);
+                                               sgScaleVec3(iSprite->l2, 0.8f);
+                                               sgScaleVec3(iSprite->l3, 0.8f);
+                                       } else {
+                                               sgScaleVec3(iSprite->l0, 0.7f);
+                                               sgScaleVec3(iSprite->l1, 0.7f);
+                                               sgScaleVec3(iSprite->l2, 0.7f);
+                                               sgScaleVec3(iSprite->l3, 0.7f);
+                                       }
                                        break;
                                default:
                                        // darker bottom than top
@@ -466,27 +539,40 @@ void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos,
                        sgCopyVec4 ( l2, iSprite->l2 );
                        sgCopyVec4 ( l3, iSprite->l3 );
                        if( ! drawBB ) {
-                               // blend clouds with sky based on distance to limit the contrast of distant cloud
-                               float t = 1.0f - dist_center * CloudVisFade;
-                               if ( t < 0.0f ) 
-                                       t = 0.0f;       // no, it should have been culled
                                // now clouds at the far plane are half blended
                                sgScaleVec4( l0, t );
                                sgScaleVec4( l1, t );
                                sgScaleVec4( l2, t );
                                sgScaleVec4( l3, t );
                        }
+                       if( iSprite->rank == clrank ) {
+                               sgScaleVec4( l0, clfadeinrank );
+                               sgScaleVec4( l1, clfadeinrank );
+                               sgScaleVec4( l2, clfadeinrank );
+                               sgScaleVec4( l3, clfadeinrank );
+                       }
                        // compute the rotations so that the quad is facing the camera
                        sgVec3 pos;
                        sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
                        sgCopyVec3( translate, pos );
                        sgNormaliseVec3( translate );
+#if 0
+                       // change view angle when near a sprite
+                       sgVec3 trans={translate[0], translate[2], translate[1]};
+                       float angle = sgScalarProductVec3( SGCloudField::view_vec, trans );
+                       if( fabs(angle) < 0.85f ) {
+                               // view not ok from under
+                               sgSetVec3( translate, -SGCloudField::view_vec[0],-SGCloudField::view_vec[2],-SGCloudField::view_vec[1] );
+//                             sgSetVec3( l0,1,0,0 );
+//                             sgSetVec3( l1,1,0,0 );
+//                             sgSetVec3( l2,1,0,0 );
+//                             sgSetVec3( l3,1,0,0 );
+                       }
+#endif
                        sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
                        sgVectorProductVec3(x, translate, up);
-                       sgNormaliseVec3(x);
-                       sgScaleVec3(x, r);
                        sgVectorProductVec3(y, x, translate);
-                       sgNormaliseVec3(y);
+                       sgScaleVec3(x, r);
                        sgScaleVec3(y, r);
  
                        sgVec3 left, right;
@@ -558,33 +644,61 @@ void SGNewCloud::CalcAngles(sgVec3 refpos, sgVec3 FakeEyePos, float *angleY, flo
 }
 
 // draw a cloud but this time we use the impostor texture
-void SGNewCloud::RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dist_center) {
-       // TODO:glrotate is not needed
-    glPushMatrix();
-        glTranslatef(center[SG_X] - deltaPos[SG_X], center[SG_Z] - deltaPos[SG_Z], center[SG_Y] - deltaPos[SG_Y]);
-        glRotatef(angleY, 0.0f, 0.0f, 1.0f);
-        glRotatef(angleX, 1.0f, 0.0f, 0.0f);
+void SGNewCloud::RenderBB(sgVec3 deltaPos, bool first_time, float dist_center) {
+
+               sgVec3 translate;
+               sgSubVec3( translate, center, deltaPos);
+
                // blend clouds with sky based on distance to limit the contrast of distant cloud
-               float CloudVisFade = (1.5 * SGCloudField::get_CloudVis());
+               float CloudVisFade = (1.0f * SGCloudField::get_CloudVis());
 
-               float t = 1.0f - dist_center / CloudVisFade;
-               // err the alpha value is not good for impostor, debug that
-               t *= 1.65;
+               float t = 1.0f - (dist_center - 1.0*radius) / CloudVisFade;
                if ( t < 0.0f ) 
-                       t = 0.0f;
-  
+                       return;
+               if( t > 1.0f )
+                       t = 1.0f;
+               if( t > 0.50f )
+                       t *= 1.1f;
         glColor4f(t, t, t, t);
         float r = radius;
+               // compute the rotations so that the quad is facing the camera
+               sgVec3 pos;
+               sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
+               sgCopyVec3( translate, pos );
+               sgNormaliseVec3( translate );
+               sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
+               sgVectorProductVec3(x, translate, up);
+               sgVectorProductVec3(y, x, translate);
+               if(first_time) {
+                       sgCopyVec3( rotX, x );
+                       sgCopyVec3( rotY, y );
+               } else if(fabs(sgScalarProductVec3(rotX, x)) < 0.93f || fabs(sgScalarProductVec3(rotY, y)) < 0.93f ) {
+                       // ask for a redraw of this impostor if the view angle changed too much
+                       sgCopyVec3( rotX, x );
+                       sgCopyVec3( rotY, y );
+                       cldCache->invalidate(cloudId, bbId);
+               }
+               sgScaleVec3(x, r);
+               sgScaleVec3(y, r);
+
+               sgVec3 left, right;
+               sgCopyVec3( left, pos );
+               sgSubVec3 (left, y);
+               sgAddVec3 (right, left, x);
+               sgSubVec3 (left, x);
+
                glBegin(GL_QUADS);
-                       glTexCoord2f(0.0f, 1.0f);
-                       glVertex2f(-r, r);
-                       glTexCoord2f(1.0f, 1.0f);
-                       glVertex2f(r, r);
-                       glTexCoord2f(1.0f, 0.0f);
-                       glVertex2f(r, -r);
                        glTexCoord2f(0.0f, 0.0f);
-                       glVertex2f(-r, -r);
+            glVertex3fv(left);
+                       glTexCoord2f(1.0f, 0.0f);
+            glVertex3fv(right);
+                       sgScaleVec3( y, 2.0 );
+                       sgAddVec3( left, y);
+                       sgAddVec3( right, y);
+                       glTexCoord2f(1.0f, 1.0f);
+            glVertex3fv(right);
+                       glTexCoord2f(0.0f, 1.0f);
+            glVertex3fv(left);
                glEnd();
 
 #if 0  // debug only
@@ -607,8 +721,6 @@ void SGNewCloud::RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dis
 
 #endif
 
-       glPopMatrix();
-
 }
 
 // determine if it is a good idea to use an impostor to render the cloud
@@ -631,19 +743,26 @@ bool SGNewCloud::isBillboardable(float dist) {
 void SGNewCloud::Render(sgVec3 FakeEyePos) {
        sgVec3 dist;
 
-
        sgVec3 deltaPos;
        sgCopyVec3( deltaPos, FakeEyePos);
     sgSubVec3( dist, center, FakeEyePos);
     float dist_center = sgLengthVec3(dist);
 
-
+       if( fadeActive ) {
+               fadetimer += SGCloudField::timer_dt;
+               if( fadetimer > duration + pauseLength ) {
+                       // fade out after fade in, and vice versa
+                       direction = ! direction;
+                       fadetimer = 0.0;
+               }
+       }
 
        if( !isBillboardable(dist_center) ) {
                // not a good candidate for impostors, draw a real cloud
                Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
        } else {
                GLuint texID = 0;
+                       bool first_time = false;
                        // lets use our impostor
                        if( bbId >= 0)
                                texID = cldCache->QueryTexID(cloudId, bbId);
@@ -653,14 +772,20 @@ void SGNewCloud::Render(sgVec3 FakeEyePos) {
                 // allocate a new Impostor
                 bbId = cldCache->alloc(cloudId);
                                texID = cldCache->QueryTexID(cloudId, bbId);
+                               first_time = true;
                        }
                        if( texID == 0 ) {
                 // no more free texture in the pool
                 Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
                        } else {
-                float angleX, angleY;
-                CalcAngles(center, FakeEyePos, &angleY, &angleX);
-                               if( ! cldCache->isBbValid( cloudId, bbId, angleY, angleX) ) {
+                float angleX=0.0f, angleY=0.0f;
+
+                               // force a redraw of the impostor if the cloud shape has changed enought
+                               float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration;
+                               if( fabs(step - last_step) > 0.5f )
+                                       cldCache->invalidate(cloudId, bbId);
+
+                               if( ! cldCache->isBbValid( cloudId, bbId, angleY, angleX)) {
                     // we must build or rebuild this billboard
                                        // start render to texture
                     cldCache->beginCapture();
@@ -679,7 +804,7 @@ void SGNewCloud::Render(sgVec3 FakeEyePos) {
                                }
                 // draw the newly built BB or an old one
                 glBindTexture(GL_TEXTURE_2D, texID);
-                RenderBB(deltaPos, angleY, angleX, dist_center);
+                RenderBB(deltaPos, first_time, dist_center);
                        }
        }
 
index 6a789c0d23258f1a81f9a613dd65227770945518..f30294c5bcedd4e4cfd9ef47c13a9ca872f8cc3d 100644 (file)
 SG_USING_STD(string);
 SG_USING_STD(vector);
 
+/**
+ * 3D cloud class.
+ */
 class SGNewCloud {
 
 public:
-       SGNewCloud();
+       enum CLFamilly_type {
+               CLFamilly_cu = 0,
+               CLFamilly_cb,
+               CLFamilly_st,
+               CLFamilly_ns,
+               CLFamilly_sc,
+               CLFamilly_as,
+               CLFamilly_ac,
+               CLFamilly_ci,
+               CLFamilly_cc,
+               CLFamilly_cs,
+               CLFamilly_nn
+       };
+       SGNewCloud(CLFamilly_type classification=CLFamilly_nn);
+       SGNewCloud(string classification);
        ~SGNewCloud();
 
        enum CLbox_type {
@@ -51,6 +68,7 @@ public:
                CLTexture_stratus = 2,
                CLTexture_max
        };
+
 private:
 
        class spriteDef {
@@ -59,11 +77,12 @@ private:
                float           r;
                CLbox_type      sprite_type;
                sgVec4          l0, l1, l2, l3;
+               sgVec3          normal, n0, n1, n2, n3;
                int                     rank;
                int                     box;
                float           dist;           // distance used during sort
                bool operator<(const spriteDef &b) const {
-                       return this->dist < b.dist;
+                       return (this->dist < b.dist);
                }
        };
 
@@ -78,6 +97,8 @@ private:
        typedef vector<spriteDef>               list_of_spriteDef;
        typedef vector<spriteContainer> list_of_spriteContainer;
 
+       void init(void);
+
        void computeSimpleLight(sgVec3 eyePos);
        void addSprite(float x, float y, float z, float r, CLbox_type type, int box);
 
@@ -91,23 +112,26 @@ private:
        void CalcAngles(sgVec3 refpos, sgVec3 eyePos, float *angleY, float *angleX);
 
        // draw a cloud but this time we use the impostor texture
-       void RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dist_center);
+       void RenderBB(sgVec3 deltaPos, bool first_time, float dist_center);
 
        // determine if it is a good idea to use an impostor to render the cloud
        bool isBillboardable(float dist);
 
        int             cloudId, bbId;
+       sgVec3  rotX, rotY;
+
 //     int             rank;
        sgVec3  cloudpos, center;
+       float   delta_base;
        list_of_spriteDef               list_spriteDef;
        list_of_spriteContainer list_spriteContainer;
-       float minx, maxx, miny, maxy, minz, maxz;
        float radius;
+       CLFamilly_type familly;
 
        // fading data
        bool direction, fadeActive;
-       float duration, pauseLength;
-       // need timer here
+       float duration, pauseLength, fadetimer;
+       float last_step;
 
 public:
        // add a new box to the cloud
@@ -133,6 +157,9 @@ public:
        void setFade(float howMuch);
 
        inline float getRadius() { return radius; }
+       inline sgVec3 *getCenter() { return &center; }
+
+       inline CLFamilly_type getFamilly(void) { return familly; }
 
        // load all textures used to draw cloud sprites
        static void loadTextures( const string &tex_path );
index 53ff7314c6ed9f328821abbfd51c9c9e0863f4e5..5100635a7d246b9ca926042d6073a955f5d14f35 100644 (file)
@@ -29,7 +29,7 @@
 #include <simgear/math/sg_random.h>
 
 #include "sky.hxx"
-
+#include "cloudfield.hxx"
 
 // Constructor
 SGSky::SGSky( void ) {
@@ -187,6 +187,8 @@ void SGSky::preDraw( float alt, float fog_exp2_density ) {
             // in cloud layer
 
             // bail now and don't draw any clouds
+                       if( cloud_layers[i]->get_layer3D()->is3D() && SGCloudField::enable3D )
+                               continue;
             in_cloud = i;
         } else {
             // above cloud layer
@@ -286,7 +288,8 @@ void SGSky::modify_vis( float alt, float time_factor ) {
            ratio = 1.0;
        }
 
-        if ( cloud_layers[i]->getCoverage() == SGCloudLayer::SG_CLOUD_CLEAR ) {
+        if ( cloud_layers[i]->getCoverage() == SGCloudLayer::SG_CLOUD_CLEAR ||
+                       cloud_layers[i]->get_layer3D()->is3D() && SGCloudField::enable3D) {
             // do nothing, clear layers aren't drawn, don't affect
             // visibility andn dont' need to be faded in or out.
         } else if ( (cloud_layers[i]->getCoverage() == 
index 743f2ad065649a66ce232b07ea131f781aa4dc33..aaa007a3f2b402b39c93226d7b970639e516185b 100644 (file)
@@ -12,7 +12,7 @@ IMAGE_SERVER_INCL =
 IMAGE_SERVER_SRCS = 
 endif
 
-noinst_HEADERS = colours.h RenderTexture.h
+noinst_HEADERS = colours.h GLBitmaps.h
 
 include_HEADERS = \
        colors.hxx \
@@ -20,11 +20,12 @@ include_HEADERS = \
        $(IMAGE_SERVER_INCL) \
        screen-dump.hxx \
        extensions.hxx \
+       RenderTexture.h \
        tr.h
 
 libsgscreen_a_SOURCES = \
        texture.cxx \
-       GLBitmaps.cxx GLBitmaps.h \
+       GLBitmaps.cxx \
        $(IMAGE_SERVER_SRCS) \
        screen-dump.cxx \
        tr.cxx \