From 2ea9e723c205359a05d2a5c4cb939728d85e3f15 Mon Sep 17 00:00:00 2001 From: ehofman Date: Sun, 15 May 2005 09:27:00 +0000 Subject: [PATCH] Harald JOHNSEN: 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. --- simgear/environment/visual_enviro.cxx | 571 +++++++++++++++++++++++++- simgear/environment/visual_enviro.hxx | 125 +++++- simgear/scene/sky/bbcache.cxx | 45 +- simgear/scene/sky/bbcache.hxx | 129 +++++- simgear/scene/sky/cloud.cxx | 4 +- simgear/scene/sky/cloud.hxx | 3 + simgear/scene/sky/cloudfield.cxx | 79 +++- simgear/scene/sky/cloudfield.hxx | 24 +- simgear/scene/sky/newcloud.cxx | 361 ++++++++++------ simgear/scene/sky/newcloud.hxx | 39 +- simgear/scene/sky/sky.cxx | 7 +- simgear/screen/Makefile.am | 5 +- 12 files changed, 1196 insertions(+), 196 deletions(-) diff --git a/simgear/environment/visual_enviro.cxx b/simgear/environment/visual_enviro.cxx index ebe8f855..0c5192eb 100644 --- a/simgear/environment/visual_enviro.cxx +++ b/simgear/environment/visual_enviro.cxx @@ -20,32 +20,145 @@ // // +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include #include #include #include "visual_enviro.hxx" +#include + +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 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; +} diff --git a/simgear/environment/visual_enviro.hxx b/simgear/environment/visual_enviro.hxx index f8a7e6c0..695e1444 100644 --- a/simgear/environment/visual_enviro.hxx +++ b/simgear/environment/visual_enviro.hxx @@ -22,23 +22,89 @@ #ifndef _VISUAL_ENVIRO_HXX #define _VISUAL_ENVIRO_HXX -class SGEnviro { +#include +#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 diff --git a/simgear/scene/sky/bbcache.cxx b/simgear/scene/sky/bbcache.cxx index 64bb7c63..c9af9c26 100644 --- a/simgear/scene/sky/bbcache.cxx +++ b/simgear/scene/sky/bbcache.cxx @@ -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; +} + diff --git a/simgear/scene/sky/bbcache.hxx b/simgear/scene/sky/bbcache.hxx index f2ba5641..fae611de 100644 --- a/simgear/scene/sky/bbcache.hxx +++ b/simgear/scene/sky/bbcache.hxx @@ -29,20 +29,36 @@ #include #include +/** + * 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; }; diff --git a/simgear/scene/sky/cloud.cxx b/simgear/scene/sky/cloud.cxx index 776044a3..1dd6034f 100644 --- a/simgear/scene/sky/cloud.cxx +++ b/simgear/scene/sky/cloud.cxx @@ -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 ) { diff --git a/simgear/scene/sky/cloud.hxx b/simgear/scene/sky/cloud.hxx index 9d0f139e..6a179d9f 100644 --- a/simgear/scene/sky/cloud.hxx +++ b/simgear/scene/sky/cloud.hxx @@ -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 { diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index af0abc33..0deadad7 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -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); diff --git a/simgear/scene/sky/cloudfield.hxx b/simgear/scene/sky/cloudfield.hxx index 2a547eb6..8bd8599f 100644 --- a/simgear/scene/sky/cloudfield.hxx +++ b/simgear/scene/sky/cloudfield.hxx @@ -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 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; diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index 88286eee..2fd119b0 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -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); } } diff --git a/simgear/scene/sky/newcloud.hxx b/simgear/scene/sky/newcloud.hxx index 6a789c0d..f30294c5 100644 --- a/simgear/scene/sky/newcloud.hxx +++ b/simgear/scene/sky/newcloud.hxx @@ -33,10 +33,27 @@ 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 list_of_spriteDef; typedef vector 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 ¢er; } + + inline CLFamilly_type getFamilly(void) { return familly; } // load all textures used to draw cloud sprites static void loadTextures( const string &tex_path ); diff --git a/simgear/scene/sky/sky.cxx b/simgear/scene/sky/sky.cxx index 53ff7314..5100635a 100644 --- a/simgear/scene/sky/sky.cxx +++ b/simgear/scene/sky/sky.cxx @@ -29,7 +29,7 @@ #include #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() == diff --git a/simgear/screen/Makefile.am b/simgear/screen/Makefile.am index 743f2ad0..aaa007a3 100644 --- a/simgear/screen/Makefile.am +++ b/simgear/screen/Makefile.am @@ -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 \ -- 2.39.5