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.
This commit is contained in:
ehofman 2005-05-15 09:27:00 +00:00
parent e19091d809
commit 2ea9e723c2
12 changed files with 1197 additions and 197 deletions

View File

@ -20,32 +20,145 @@
// //
// //
#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/cloudfield.hxx>
#include <simgear/scene/sky/newcloud.hxx> #include <simgear/scene/sky/newcloud.hxx>
#include "visual_enviro.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;
SGEnviro::SGEnviro(void) : SGEnviro::SGEnviro(void) :
view_in_cloud(false), 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) precipitation_density(100.0)
{ {
for(int i = 0; i < MAX_RAIN_SLICE ; i++)
rainpos[i] = sg_random();
} }
SGEnviro::~SGEnviro(void) { 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; view_in_cloud = false;
// ask the impostor cache to do some cleanup
if(SGNewCloud::cldCache) if(SGNewCloud::cldCache)
SGNewCloud::cldCache->startNewFrame(); 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) { 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 // this can be queried to add some turbulence for example
bool SGEnviro::is_view_in_cloud(void) const { bool SGEnviro::is_view_in_cloud(void) const {
return view_in_cloud; return view_in_cloud;
@ -71,6 +184,10 @@ bool SGEnviro::get_clouds_enable_state(void) const {
return SGCloudField::get_enable3dClouds(); return SGCloudField::get_enable3dClouds();
} }
bool SGEnviro::get_turbulence_enable_state(void) const {
return turbulence_enable_state;
}
void SGEnviro::set_CacheResolution(int resolutionPixels) { void SGEnviro::set_CacheResolution(int resolutionPixels) {
SGCloudField::set_CacheResolution(resolutionPixels); SGCloudField::set_CacheResolution(resolutionPixels);
} }
@ -87,7 +204,9 @@ void SGEnviro::set_clouds_density(float density) {
void SGEnviro::set_clouds_enable_state(bool enable) { void SGEnviro::set_clouds_enable_state(bool enable) {
SGCloudField::set_enable3dClouds(enable); SGCloudField::set_enable3dClouds(enable);
} }
void SGEnviro::set_turbulence_enable_state(bool enable) {
turbulence_enable_state = enable;
}
// rain/snow // rain/snow
float SGEnviro::get_precipitation_density(void) const { float SGEnviro::get_precipitation_density(void) const {
return precipitation_density; return precipitation_density;
@ -105,9 +224,453 @@ void SGEnviro::set_precipitation_enable_state(bool enable) {
// others // others
bool SGEnviro::get_lightning_enable_state(void) const { bool SGEnviro::get_lightning_enable_state(void) const {
return false; return lightning_enable_state;
} }
void SGEnviro::set_lightning_enable_state(bool enable) { 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;
}

View File

@ -22,23 +22,89 @@
#ifndef _VISUAL_ENVIRO_HXX #ifndef _VISUAL_ENVIRO_HXX
#define _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: 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 view_in_cloud;
bool precipitation_enable_state; bool precipitation_enable_state;
float precipitation_density; 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: public:
SGEnviro(); SGEnviro();
~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); 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 // this can be queried to add some turbulence for example
bool is_view_in_cloud(void) const; bool is_view_in_cloud(void) const;
void set_view_in_cloud(bool incloud); void set_view_in_cloud(bool incloud);
double get_cloud_turbulence(void) const;
// Clouds // Clouds
// return the size of the memory pool used by texture impostors // return the size of the memory pool used by texture impostors
@ -47,23 +113,76 @@ public:
float get_clouds_visibility(void) const; float get_clouds_visibility(void) const;
float get_clouds_density(void) const; float get_clouds_density(void) const;
bool get_clouds_enable_state(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); 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); 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); 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); 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); 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 // rain/snow
float get_precipitation_density(void) const; float get_precipitation_density(void) const;
bool get_precipitation_enable_state(void) const; bool get_precipitation_enable_state(void) const;
void set_precipitation_density(float density); 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); void set_precipitation_enable_state(bool enable);
// others // others
bool get_lightning_enable_state(void) const; 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); 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; extern SGEnviro sgEnviro;

View File

@ -47,6 +47,7 @@ void SGBbCache::freeTextureMemory(void) {
if( bbListCount ) { if( bbListCount ) {
for(int i = 0 ; i < bbListCount ; i++) { for(int i = 0 ; i < bbListCount ; i++) {
bbList[i].cldID = 0;
if(bbList[i].texID) if(bbList[i].texID)
glDeleteTextures(1, & bbList[i].texID); glDeleteTextures(1, & bbList[i].texID);
} }
@ -78,11 +79,12 @@ bool SGBbCache::allocTextureMemory(int cacheCount, int textureDimension) {
cacheSizeKb = (textureDimension * textureDimension * 4); cacheSizeKb = (textureDimension * textureDimension * 4);
cacheSizeKb *= cacheCount; cacheSizeKb *= cacheCount;
cacheSizeKb /= 1024; cacheSizeKb /= 1024;
if(rt) { if(rtAvailable) {
rt->BeginCapture(); if( rt->BeginCapture() ) {
glViewport(0, 0, textureDimension, textureDimension); glViewport(0, 0, textureDimension, textureDimension);
rt->EndCapture(); rt->EndCapture();
} }
}
return true; return true;
} }
@ -113,9 +115,9 @@ void SGBbCache::init(int cacheCount) {
rt->Reset("rgba tex2D ctt"); rt->Reset("rgba tex2D ctt");
// rt->Reset("rgba tex2D"); // rt->Reset("rgba tex2D");
if( rt->Initialize(256, 256, true) ) { if( rt->Initialize(256, 256, true) ) {
rtAvailable = true;
if (rt->BeginCapture()) if (rt->BeginCapture())
{ {
rtAvailable = true;
glViewport(0, 0, 256, 256); glViewport(0, 0, 256, 256);
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
@ -219,6 +221,7 @@ int SGBbCache::alloc(int cldId) {
bbList[i].angleX = -999; bbList[i].angleX = -999;
bbList[i].angleY = -999; bbList[i].angleY = -999;
bbList[i].frameUsed = 0; bbList[i].frameUsed = 0;
bbList[i].needRedraw = true;
return i; return i;
} }
} }
@ -279,6 +282,7 @@ void SGBbCache::setTextureData(int bbId) {
// bbList[bbId].angleY = angleY; // bbList[bbId].angleY = angleY;
bbList[bbId].frame = frameNumber; bbList[bbId].frame = frameNumber;
bbList[bbId].frameUsed = frameNumber; bbList[bbId].frameUsed = frameNumber;
bbList[bbId].needRedraw = false;
builtBBCount ++; builtBBCount ++;
builtBBframe ++; builtBBframe ++;
} }
@ -305,11 +309,14 @@ bool SGBbCache::isBbValid( int cldId, int bbId, float angleY, float angleX) {
if( builtBBframe >= maxImpostorRegenFrame ) if( builtBBframe >= maxImpostorRegenFrame )
return true; return true;
if( fabs(angleY - bbList[bbId].angleY) >= 4.0 ) if( bbList[bbId].needRedraw )
return false; return false;
if( fabs(angleX - bbList[bbId].angleX) >= 4.0 ) // if( fabs(angleY - bbList[bbId].angleY) >= 4.0 )
return false; // return false;
// if( fabs(angleX - bbList[bbId].angleX) >= 4.0 )
// return false;
bbList[bbId].frameUsed = frameNumber; bbList[bbId].frameUsed = frameNumber;
return true; return true;
@ -328,7 +335,7 @@ void SGBbCache::setReference( int cldId, int bbId, float angleY, float angleX) {
void SGBbCache::startNewFrame(void) { void SGBbCache::startNewFrame(void) {
builtBBframe = 0; builtBBframe = 0;
// TOTO:find reasonable value // TOTO:find reasonable value
int minFrameNumber = frameNumber - 500; int minFrameNumber = frameNumber - 100;
frameNumber++; frameNumber++;
// cleanup of unused enties // cleanup of unused enties
for( int bbId = 0 ; bbId < bbListCount ; bbId++) for( int bbId = 0 ; bbId < bbListCount ; bbId++)
@ -337,3 +344,21 @@ void SGBbCache::startNewFrame(void) {
bbList[bbId].cldID = 0; 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;
}

View File

@ -29,20 +29,36 @@
#include <simgear/screen/extensions.hxx> #include <simgear/screen/extensions.hxx>
#include <simgear/screen/RenderTexture.h> #include <simgear/screen/RenderTexture.h>
/**
* Billboard helper class.
*/
class SGBbCache { class SGBbCache {
private: private:
typedef struct { /**
* storage class for impostors state.
*/
class bbInfo {
public:
/// the texture used by this impostor
GLuint texID; GLuint texID;
/// the cloud owning this impostor
int cldID; int cldID;
float angleX, angleY; float angleX, angleY;
// creation frame number for debug only // creation frame number for debug only
int frame; int frame;
// last time this entry was used /// last time this entry was used
int frameUsed; int frameUsed;
} bbInfo; /// dirty flag for lazy rebuild of impostor
bool needRedraw;
};
void freeTextureMemory(void); 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); bool allocTextureMemory(int count, int textureDimension);
// a list of impostors // a list of impostors
@ -64,53 +80,126 @@ public:
SGBbCache(void); SGBbCache(void);
~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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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; int maxImpostorRegenFrame;
}; };

View File

@ -843,7 +843,7 @@ bool SGCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat,
last_lat = lat; last_lat = lat;
} }
layer3D->reposition( p, up, lon, lat, alt, dt); layer3D->reposition( p, up, lon, lat, alt, dt, direction, speed);
return true; return true;
} }
@ -851,7 +851,7 @@ bool SGCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat,
void SGCloudLayer::draw( bool top ) { void SGCloudLayer::draw( bool top ) {
if ( layer_coverage != SG_CLOUD_CLEAR ) { if ( layer_coverage != SG_CLOUD_CLEAR ) {
if ( SGCloudField::enable3D ) if ( SGCloudField::enable3D && layer3D->is3D())
layer3D->Render(); layer3D->Render();
else else
if ( bump_mapping && enable_bump_mapping ) { if ( bump_mapping && enable_bump_mapping ) {

View File

@ -184,6 +184,9 @@ public:
static bool enable_bump_mapping; static bool enable_bump_mapping;
/** return the 3D layer cloud associated with this 2D layer */
SGCloudField *get_layer3D(void) { return layer3D; }
private: private:
struct CloudVertex { struct CloudVertex {

View File

@ -45,13 +45,17 @@ static list_of_culledCloud inViewClouds;
// visibility distance for clouds in meters // visibility distance for clouds in meters
float SGCloudField::CloudVis = 25000.0f; float SGCloudField::CloudVis = 25000.0f;
bool SGCloudField::enable3D = true; bool SGCloudField::enable3D = false;
// fieldSize must be > CloudVis or we can destroy the impostor cache // 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 // 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; float SGCloudField::density = 100.0;
double SGCloudField::timer_dt = 0.0;
sgVec3 SGCloudField::view_vec;
static int last_cache_size = 1*1024; static int last_cache_size = 1*1024;
static int cacheResolution = 64; static int cacheResolution = 64;
static sgVec3 last_sunlight={0.0f, 0.0f, 0.0f};
int SGCloudField::get_CacheResolution(void) { int SGCloudField::get_CacheResolution(void) {
return cacheResolution; return cacheResolution;
@ -85,7 +89,6 @@ void SGCloudField::set_CacheSize(int sizeKb) {
int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4); int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
if(count == 0) if(count == 0)
count = 1; count = 1;
// SGNewCloud::cldCache->setCacheSize(sizeKb);
SGNewCloud::cldCache->setCacheSize(count, cacheResolution); SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
} }
} }
@ -101,14 +104,17 @@ void SGCloudField::set_enable3dClouds(bool enable) {
return; return;
enable3D = enable; enable3D = enable;
if(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 { } else {
SGNewCloud::cldCache->setCacheSize(0); SGNewCloud::cldCache->setCacheSize(0);
} }
} }
// reposition the cloud layer at the specified origin and orientation // 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; sgMat4 T1, LON, LAT;
sgVec3 axis; sgVec3 axis;
@ -135,8 +141,6 @@ void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, doub
this->alt = alt; this->alt = alt;
// simulate clouds movement from wind // simulate clouds movement from wind
double speed = 10.0f;
double direction = 45.0;
double sp_dist = speed*dt; double sp_dist = speed*dt;
if (sp_dist > 0) { if (sp_dist > 0) {
double bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist; 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 // correct the frustum with the right far plane
ssgContext *context = ssgGetCurrentContext(); ssgContext *context = ssgGetCurrentContext();
frustum = *context->getFrustum(); frustum = *context->getFrustum();
frustum.setFOV(55.0,0);
float w, h;
sgEnviro.getFOV( w, h );
frustum.setFOV( w, h );
frustum.setNearFar(1.0, CloudVis); frustum.setNearFar(1.0, CloudVis);
timer_dt = dt;
} }
SGCloudField::SGCloudField() : SGCloudField::SGCloudField() :
draw_in_3d(true),
last_density(0.0), last_density(0.0),
deltax(0.0), deltax(0.0),
deltay(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... // use a table or else we see poping when moving the slider...
static int densTable[][10] = { static int densTable[][10] = {
{0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0},
@ -235,15 +256,16 @@ void SGCloudField::applyDensity(void) {
iCloud->visible = false; iCloud->visible = false;
} }
last_density = density; last_density = density;
draw_in_3d = ( theField.size() != 0);
} }
// add one cloud, data is not copied, ownership given // add one cloud, data is not copied, ownership given
void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) { void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
Cloud cl; Cloud cl;
sgCopyVec3( cl.pos, pos );
cl.aCloud = cloud; cl.aCloud = cloud;
cl.visible = true; cl.visible = true;
cloud->SetPos( pos ); cloud->SetPos( pos );
sgCopyVec3( cl.pos, *cloud->getCenter() );
theField.push_back( cl ); theField.push_back( cl );
} }
@ -260,7 +282,7 @@ void SGCloudField::buildTestLayer(void) {
for( int z = -5 ; z <= 5 ; z++) { for( int z = -5 ; z <= 5 ; z++) {
for( int x = -5 ; x <= 5 ; x++ ) { for( int x = -5 ; x <= 5 ; x++ ) {
SGNewCloud *cloud = new SGNewCloud; SGNewCloud *cloud = new SGNewCloud(SGNewCloud::CLFamilly_cu);
cloud->new_cu(); cloud->new_cu();
sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s}; sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s};
addCloud(pos, cloud); addCloud(pos, cloud);
@ -291,6 +313,8 @@ void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
sgCopyVec3( tmp.eyePos, eyePos ); sgCopyVec3( tmp.eyePos, eyePos );
// save distance for later sort, opposite distance because we want back to front // save distance for later sort, opposite distance because we want back to front
tmp.dist = - squareDist; 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); inViewClouds.push_back(tmp);
if( squareDist - radius*radius < 100*100 ) if( squareDist - radius*radius < 100*100 )
sgEnviro.set_view_in_cloud(true); sgEnviro.set_view_in_cloud(true);
@ -316,9 +340,11 @@ void SGCloudField::Render(void) {
applyDensity(); applyDensity();
} }
// ask the impostor cache to do some cleanup if( ! draw_in_3d )
// TODO:don't do that for every field return;
SGNewCloud::cldCache->startNewFrame();
if( ! SGNewCloud::cldCache->isRttAvailable() )
return;
inViewClouds.clear(); inViewClouds.clear();
@ -332,14 +358,22 @@ void SGCloudField::Render(void) {
sgVec3 lightVec; sgVec3 lightVec;
ssgGetLight( 0 )->getPosition( lightVec ); ssgGetLight( 0 )->getPosition( lightVec );
sgXformVec3( lightVec, invtrans ); sgXformVec3( lightVec, invtrans );
sgCopyVec3( SGNewCloud::modelSunDir, lightVec );
sgSetVec3( SGNewCloud::modelSunDir, lightVec[0], lightVec[2], lightVec[1]); 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; sgVec4 diffuse, ambient;
ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse ); ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient ); ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient );
sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 0.70f); sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 1.0f);
sgScaleVec3 ( SGNewCloud::ambLight, ambient , 0.60f); 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 // voodoo things on the matrix stack
ssgGetModelviewMatrix( modelview ); ssgGetModelviewMatrix( modelview );
@ -348,10 +382,13 @@ void SGCloudField::Render(void) {
// cloud fields are tiled on the flat earth // cloud fields are tiled on the flat earth
// compute the position in the tile // compute the position in the tile
relx = -fmod( deltax + relative_position[SG_X] + tmp[3][0], fieldSize ); relx = fmod( deltax + relative_position[SG_X] + tmp[3][0], fieldSize );
rely = -fmod( deltay + relative_position[SG_Y] + tmp[3][1], 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][2] = 0;
tmp[3][0] = 0; tmp[3][0] = 0;
@ -400,6 +437,8 @@ void SGCloudField::Render(void) {
for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) { for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) {
// iCloud->aCloud->drawContainers(); // iCloud->aCloud->drawContainers();
iCloud->aCloud->Render(iCloud->eyePos); 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); glBindTexture(GL_TEXTURE_2D, 0);

View File

@ -37,12 +37,17 @@ public:
SGNewCloud *aCloud; SGNewCloud *aCloud;
sgVec3 eyePos; sgVec3 eyePos;
float dist; float dist;
float heading;
float alt;
bool operator<(const culledCloud &b) const { bool operator<(const culledCloud &b) const {
return this->dist < b.dist; return (this->dist < b.dist);
} }
}; };
typedef vector<culledCloud> list_of_culledCloud; typedef vector<culledCloud> list_of_culledCloud;
/**
* A layer of 3D clouds.
*/
class SGCloudField { class SGCloudField {
private: private:
@ -51,12 +56,6 @@ private:
SGNewCloud *aCloud; SGNewCloud *aCloud;
sgVec3 pos; sgVec3 pos;
bool visible; 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; double last_lon, last_lat, last_course;
float last_density; float last_density;
bool draw_in_3d;
public: public:
SGCloudField(); SGCloudField();
~SGCloudField(); ~SGCloudField();
void clear(void);
// add one cloud, data is not copied, ownership given // add one cloud, data is not copied, ownership given
void addCloud( sgVec3 pos, SGNewCloud *cloud); void addCloud( sgVec3 pos, SGNewCloud *cloud);
@ -95,13 +97,17 @@ public:
void Render(void); void Render(void);
// reposition the cloud layer at the specified origin and orientation // 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 // visibility distance for clouds in meters
static float CloudVis; static float CloudVis;
static float density; static sgVec3 view_vec;
static float density;
static double timer_dt;
static double fieldSize; static double fieldSize;
static bool enable3D; static bool enable3D;

View File

@ -47,6 +47,8 @@ static ssgTexture *cloudTextures[SGNewCloud::CLTexture_max];
bool SGNewCloud::useAnisotropic = true; bool SGNewCloud::useAnisotropic = true;
SGBbCache *SGNewCloud::cldCache = 0; SGBbCache *SGNewCloud::cldCache = 0;
static bool texturesLoaded = false; static bool texturesLoaded = false;
static float minx, maxx, miny, maxy, minz, maxz;
float SGNewCloud::nearRadius = 3500.0f; float SGNewCloud::nearRadius = 3500.0f;
bool SGNewCloud::lowQuality = false; bool SGNewCloud::lowQuality = false;
sgVec3 SGNewCloud::sunlight = {0.5f, 0.5f, 0.5f}; 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}; sgVec3 SGNewCloud::modelSunDir = {0,1,0};
// constructor void SGNewCloud::init(void) {
SGNewCloud::SGNewCloud() : bbId = -1;
bbId(-1), fadeActive = false;
// rank(-1), duration = 100.0f;
minx(999), miny(999), minz(999), maxx(-999), maxy(-999), maxz(-999) fadetimer = 100.0f;
pauseLength = 0.0f;
{ last_step = -1.0f;
familly = CLFamilly_nn;
cloudId = (int) this; cloudId = (int) this;
sgSetVec3(center, 0.0f, 0.0f, 0.0f); sgSetVec3(center, 0.0f, 0.0f, 0.0f);
sgSetVec3(cloudpos, 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_spriteContainer.reserve(8);
list_spriteDef.reserve(40); list_spriteDef.reserve(40);
// if( ! texturesLoaded ) {}
if( cldCache == 0 ) { if( cldCache == 0 ) {
cldCache = new SGBbCache; cldCache = new SGBbCache;
cldCache->init( 64 ); 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() { SGNewCloud::~SGNewCloud() {
list_spriteDef.clear(); list_spriteDef.clear();
list_spriteContainer.clear(); list_spriteContainer.clear();
@ -101,12 +138,26 @@ void SGNewCloud::loadTextures(const string &tex_path) {
} }
void SGNewCloud::startFade(bool direction, float duration, float pauseLength) { 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) { 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); 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 // compute the light for a cloud sprite corner
// from the normal and the sun, scaled by the Rayleigh factor // from the normal and the sun, scaled by the Rayleigh factor
// and finaly added to the ambient light // 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 cosAngle = sgScalarProductVec3( normal, SGNewCloud::modelSunDir);
float vl = (1.0f - 0.1f + cosAngle / 10.0f) * pf; float vl = (1.0f - 0.5f + cosAngle * 0.5f) * pf;
sgScaleVec3( light, SGNewCloud::sunlight, vl ); sgScaleVec3( light, SGNewCloud::sunlight, 0.25f + 0.75f * vl );
sgAddVec3( light, SGNewCloud::ambLight ); sgAddVec3( light, SGNewCloud::ambLight );
// we need to clamp or else the light will bug when adding transparency // we need to clamp or else the light will bug when adding transparency
if( light[0] > 1.0 ) light[0] = 1.0; 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 // compute the light for a cloud sprite
// we use ambient light and orientation versus sun position // we use ambient light and orientation versus sun position
// TODO:check sun pos and check code
void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) { void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) {
// constant Rayleigh factor if we are not doing Anisotropic lighting // constant Rayleigh factor if we are not doing Anisotropic lighting
float pf = 1.0f; float pf = 1.0f;
const float ang = 45.0f * SG_PI / 180.0f;
list_of_spriteDef::iterator iSprite; list_of_spriteDef::iterator iSprite;
for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) { for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
if( useAnisotropic ) { if( useAnisotropic ) {
@ -156,44 +206,11 @@ void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) {
float cosAngle = sgScalarProductVec3(eyeDir, modelSunDir); float cosAngle = sgScalarProductVec3(eyeDir, modelSunDir);
pf = rayleighCoeffAngular(cosAngle); pf = rayleighCoeffAngular(cosAngle);
} }
// compute the vector going from the container box center to the sprite lightFunction(iSprite->n0, iSprite->l0, pf);
// TODO : this is a constant except for cloudpos, compute the normal in setpos function lightFunction(iSprite->n1, iSprite->l1, pf);
sgVec3 normal; lightFunction(iSprite->n2, iSprite->l2, pf);
spriteContainer *thisSpriteContainer = &list_spriteContainer[iSprite->box]; lightFunction(iSprite->n3, iSprite->l3, pf);
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);
} 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; cont.cont_type = type;
sgSetVec3( cont.center, 0.0f, 0.0f, 0.0f); sgSetVec3( cont.center, 0.0f, 0.0f, 0.0f);
list_spriteContainer.push_back( cont ); 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 // add a sprite inside a box
void SGNewCloud::addSprite(float x, float y, float z, float r, CLbox_type type, int box) { void SGNewCloud::addSprite(float x, float y, float z, float r, CLbox_type type, int box) {
spriteDef newSpriteDef; spriteDef newSpriteDef;
int rank = list_spriteDef.size(); int rank = list_spriteDef.size();
sgSetVec3( newSpriteDef.pos, x, y, z); sgSetVec3( newSpriteDef.pos, x, y - delta_base, z);
newSpriteDef.box = box; newSpriteDef.box = box;
newSpriteDef.sprite_type = type; newSpriteDef.sprite_type = type;
newSpriteDef.rank = rank; 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 ); sgSubVec3( deltaPos, newSpriteDef.pos, thisBox->pos );
sgAddVec3( thisBox->center, deltaPos ); sgAddVec3( thisBox->center, deltaPos );
r = r * 0.6f; // 0.5 * 1.xxx r = r * 0.65f; // 0.5 * 1.xxx
if( x - r < minx ) if( x - r < minx )
minx = x - r; minx = x - r;
if( y - r < miny ) if( y - r < miny )
@ -248,19 +268,21 @@ static float Rnd(float n) {
void SGNewCloud::genSprites( void ) { void SGNewCloud::genSprites( void ) {
float x, y, z, r; float x, y, z, r;
int N, sc; int N, sc;
minx = miny = minz = 99999.0;
maxx = maxy = maxz = -99999.0;
N = list_spriteContainer.size(); N = list_spriteContainer.size();
for(int i = 0 ; i < N ; i++ ) { for(int i = 0 ; i < N ; i++ ) {
spriteContainer *thisBox = & list_spriteContainer[i]; spriteContainer *thisBox = & list_spriteContainer[i];
// the type defines how the sprites can be positioned inside the box, their size, etc // the type defines how the sprites can be positioned inside the box, their size, etc
switch(thisBox->cont_type) { switch(thisBox->cont_type) {
case CLbox_sc: case CLbox_sc:
for( sc = 0 ; sc <= 4 ; sc ++ ) { sc = 1;
r = thisBox->r + Rnd(0.2f); r = thisBox->r + Rnd(0.2f);
x = thisBox->pos[SG_X] + Rnd(thisBox->r); x = thisBox->pos[SG_X] + Rnd(thisBox->r * 0.75f);
y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.2f); y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.75f);
z = thisBox->pos[SG_Z] + Rnd(thisBox->r); z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f);
addSprite(x, y, z, r, thisBox->cont_type, i); addSprite(x, y, z, r, thisBox->cont_type, i);
}
break; break;
case CLbox_stratus: case CLbox_stratus:
sc = 1; sc = 1;
@ -302,10 +324,46 @@ void SGNewCloud::genSprites( void ) {
radius /= 2.0f; radius /= 2.0f;
sgSetVec3( center, (maxx + minx) / 2.0f, (maxy + miny) / 2.0f, (maxz + minz) / 2.0f ); sgSetVec3( center, (maxx + minx) / 2.0f, (maxy + miny) / 2.0f, (maxz + minz) / 2.0f );
/* fadingrank = 0 const float ang = 45.0f * SG_PI / 180.0f;
' fadingrank = UBound(tbSpriteDef()) * 10
fadingdir = 0*/ // compute normals
// TODO : compute initial sprite normals for lighting function 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 ); sgAddVec3( center, deltaPos );
sgSetVec3( cloudpos, newPos[SG_X], newPos[SG_Y], newPos[SG_Z]); 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 // 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 ) { void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos, float dist_center ) {
/* int clrank = fadingrank / 10; float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration;
int clfadeinrank = fadingrank - clrank * 10;*/ int clrank = (int) step;
float CloudVisFade = 1.0 / (1.5 * SGCloudField::get_CloudVis()); 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 ); computeSimpleLight( FakeEyePos );
// view point sort, we sort because of transparency // view point sort, we sort because of transparency
sortSprite( FakeEyePos ); sortSprite( FakeEyePos );
float dark = (familly == CLFamilly_cb ? 0.9f : 1.0f);
GLint previousTexture = -1, thisTexture; GLint previousTexture = -1, thisTexture;
list_of_spriteDef::iterator iSprite; list_of_spriteDef::iterator iSprite;
for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; 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 // choose texture to use depending on sprite type
switch(iSprite->sprite_type) { switch(iSprite->sprite_type) {
case CLbox_stratus: case CLbox_stratus:
@ -421,11 +490,6 @@ void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos,
} }
sgVec3 translate; sgVec3 translate;
if( drawBB ) {
sgCopyVec3( translate, iSprite->pos);
sgSubVec3( translate, iSprite->pos, deltaPos );
}
else
sgSubVec3( translate, iSprite->pos, deltaPos); sgSubVec3( translate, iSprite->pos, deltaPos);
@ -433,24 +497,33 @@ void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos,
float flipx = (float) ( iSprite->rank & 1 ); float flipx = (float) ( iSprite->rank & 1 );
float flipy = (float) ( (iSprite->rank >> 1) & 1 ); float flipy = (float) ( (iSprite->rank >> 1) & 1 );
// cu texture have a flat bottom so we can't do a vertical flip // 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; flipy = 0.0f;
if( iSprite->sprite_type == CLbox_stratus ) // if( iSprite->sprite_type == CLbox_stratus )
flipx = 0.0f; // flipx = 0.0f;
// adjust colors depending on cloud type // adjust colors depending on cloud type
// TODO : rewrite that later, still experimental // TODO : rewrite that later, still experimental
switch(iSprite->sprite_type) { switch(iSprite->sprite_type) {
case CLbox_cumulus: case CLbox_cumulus:
// dark bottom // dark bottom
sgScaleVec3(iSprite->l0, 0.6f); sgScaleVec3(iSprite->l0, 0.8f * dark);
sgScaleVec3(iSprite->l1, 0.6f); sgScaleVec3(iSprite->l1, 0.8f * dark);
sgScaleVec3(iSprite->l2, dark);
sgScaleVec3(iSprite->l3, dark);
break; break;
case CLbox_stratus: case CLbox_stratus:
// usually dark grey // usually dark grey
if( familly == CLFamilly_st ) {
sgScaleVec3(iSprite->l0, 0.8f); sgScaleVec3(iSprite->l0, 0.8f);
sgScaleVec3(iSprite->l1, 0.8f); sgScaleVec3(iSprite->l1, 0.8f);
sgScaleVec3(iSprite->l2, 0.8f); sgScaleVec3(iSprite->l2, 0.8f);
sgScaleVec3(iSprite->l3, 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; break;
default: default:
// darker bottom than top // darker bottom than top
@ -466,27 +539,40 @@ void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos,
sgCopyVec4 ( l2, iSprite->l2 ); sgCopyVec4 ( l2, iSprite->l2 );
sgCopyVec4 ( l3, iSprite->l3 ); sgCopyVec4 ( l3, iSprite->l3 );
if( ! drawBB ) { 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 // now clouds at the far plane are half blended
sgScaleVec4( l0, t ); sgScaleVec4( l0, t );
sgScaleVec4( l1, t ); sgScaleVec4( l1, t );
sgScaleVec4( l2, t ); sgScaleVec4( l2, t );
sgScaleVec4( l3, 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 // compute the rotations so that the quad is facing the camera
sgVec3 pos; sgVec3 pos;
sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] ); sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
sgCopyVec3( translate, pos ); sgCopyVec3( translate, pos );
sgNormaliseVec3( translate ); 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}; sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
sgVectorProductVec3(x, translate, up); sgVectorProductVec3(x, translate, up);
sgNormaliseVec3(x);
sgScaleVec3(x, r);
sgVectorProductVec3(y, x, translate); sgVectorProductVec3(y, x, translate);
sgNormaliseVec3(y); sgScaleVec3(x, r);
sgScaleVec3(y, r); sgScaleVec3(y, r);
sgVec3 left, right; 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 // draw a cloud but this time we use the impostor texture
void SGNewCloud::RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dist_center) { void SGNewCloud::RenderBB(sgVec3 deltaPos, bool first_time, float dist_center) {
// TODO:glrotate is not needed
glPushMatrix(); sgVec3 translate;
glTranslatef(center[SG_X] - deltaPos[SG_X], center[SG_Z] - deltaPos[SG_Z], center[SG_Y] - deltaPos[SG_Y]); sgSubVec3( translate, center, deltaPos);
glRotatef(angleY, 0.0f, 0.0f, 1.0f);
glRotatef(angleX, 1.0f, 0.0f, 0.0f);
// blend clouds with sky based on distance to limit the contrast of distant cloud // 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; float t = 1.0f - (dist_center - 1.0*radius) / CloudVisFade;
// err the alpha value is not good for impostor, debug that
t *= 1.65;
if ( t < 0.0f ) 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); glColor4f(t, t, t, t);
float r = radius; 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); 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); 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(); glEnd();
#if 0 // debug only #if 0 // debug only
@ -607,8 +721,6 @@ void SGNewCloud::RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dis
#endif #endif
glPopMatrix();
} }
// determine if it is a good idea to use an impostor to render the cloud // 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) { void SGNewCloud::Render(sgVec3 FakeEyePos) {
sgVec3 dist; sgVec3 dist;
sgVec3 deltaPos; sgVec3 deltaPos;
sgCopyVec3( deltaPos, FakeEyePos); sgCopyVec3( deltaPos, FakeEyePos);
sgSubVec3( dist, center, FakeEyePos); sgSubVec3( dist, center, FakeEyePos);
float dist_center = sgLengthVec3(dist); 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) ) { if( !isBillboardable(dist_center) ) {
// not a good candidate for impostors, draw a real cloud // not a good candidate for impostors, draw a real cloud
Render3Dcloud(false, FakeEyePos, deltaPos, dist_center); Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
} else { } else {
GLuint texID = 0; GLuint texID = 0;
bool first_time = false;
// lets use our impostor // lets use our impostor
if( bbId >= 0) if( bbId >= 0)
texID = cldCache->QueryTexID(cloudId, bbId); texID = cldCache->QueryTexID(cloudId, bbId);
@ -653,13 +772,19 @@ void SGNewCloud::Render(sgVec3 FakeEyePos) {
// allocate a new Impostor // allocate a new Impostor
bbId = cldCache->alloc(cloudId); bbId = cldCache->alloc(cloudId);
texID = cldCache->QueryTexID(cloudId, bbId); texID = cldCache->QueryTexID(cloudId, bbId);
first_time = true;
} }
if( texID == 0 ) { if( texID == 0 ) {
// no more free texture in the pool // no more free texture in the pool
Render3Dcloud(false, FakeEyePos, deltaPos, dist_center); Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
} else { } else {
float angleX, angleY; float angleX=0.0f, angleY=0.0f;
CalcAngles(center, FakeEyePos, &angleY, &angleX);
// 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)) { if( ! cldCache->isBbValid( cloudId, bbId, angleY, angleX)) {
// we must build or rebuild this billboard // we must build or rebuild this billboard
// start render to texture // start render to texture
@ -679,7 +804,7 @@ void SGNewCloud::Render(sgVec3 FakeEyePos) {
} }
// draw the newly built BB or an old one // draw the newly built BB or an old one
glBindTexture(GL_TEXTURE_2D, texID); glBindTexture(GL_TEXTURE_2D, texID);
RenderBB(deltaPos, angleY, angleX, dist_center); RenderBB(deltaPos, first_time, dist_center);
} }
} }

View File

@ -33,10 +33,27 @@
SG_USING_STD(string); SG_USING_STD(string);
SG_USING_STD(vector); SG_USING_STD(vector);
/**
* 3D cloud class.
*/
class SGNewCloud { class SGNewCloud {
public: 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(); ~SGNewCloud();
enum CLbox_type { enum CLbox_type {
@ -51,6 +68,7 @@ public:
CLTexture_stratus = 2, CLTexture_stratus = 2,
CLTexture_max CLTexture_max
}; };
private: private:
class spriteDef { class spriteDef {
@ -59,11 +77,12 @@ private:
float r; float r;
CLbox_type sprite_type; CLbox_type sprite_type;
sgVec4 l0, l1, l2, l3; sgVec4 l0, l1, l2, l3;
sgVec3 normal, n0, n1, n2, n3;
int rank; int rank;
int box; int box;
float dist; // distance used during sort float dist; // distance used during sort
bool operator<(const spriteDef &b) const { 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<spriteDef> list_of_spriteDef;
typedef vector<spriteContainer> list_of_spriteContainer; typedef vector<spriteContainer> list_of_spriteContainer;
void init(void);
void computeSimpleLight(sgVec3 eyePos); void computeSimpleLight(sgVec3 eyePos);
void addSprite(float x, float y, float z, float r, CLbox_type type, int box); 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); void CalcAngles(sgVec3 refpos, sgVec3 eyePos, float *angleY, float *angleX);
// draw a cloud but this time we use the impostor texture // 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 // determine if it is a good idea to use an impostor to render the cloud
bool isBillboardable(float dist); bool isBillboardable(float dist);
int cloudId, bbId; int cloudId, bbId;
sgVec3 rotX, rotY;
// int rank; // int rank;
sgVec3 cloudpos, center; sgVec3 cloudpos, center;
float delta_base;
list_of_spriteDef list_spriteDef; list_of_spriteDef list_spriteDef;
list_of_spriteContainer list_spriteContainer; list_of_spriteContainer list_spriteContainer;
float minx, maxx, miny, maxy, minz, maxz;
float radius; float radius;
CLFamilly_type familly;
// fading data // fading data
bool direction, fadeActive; bool direction, fadeActive;
float duration, pauseLength; float duration, pauseLength, fadetimer;
// need timer here float last_step;
public: public:
// add a new box to the cloud // add a new box to the cloud
@ -133,6 +157,9 @@ public:
void setFade(float howMuch); void setFade(float howMuch);
inline float getRadius() { return radius; } 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 // load all textures used to draw cloud sprites
static void loadTextures( const string &tex_path ); static void loadTextures( const string &tex_path );

View File

@ -29,7 +29,7 @@
#include <simgear/math/sg_random.h> #include <simgear/math/sg_random.h>
#include "sky.hxx" #include "sky.hxx"
#include "cloudfield.hxx"
// Constructor // Constructor
SGSky::SGSky( void ) { SGSky::SGSky( void ) {
@ -187,6 +187,8 @@ void SGSky::preDraw( float alt, float fog_exp2_density ) {
// in cloud layer // in cloud layer
// bail now and don't draw any clouds // bail now and don't draw any clouds
if( cloud_layers[i]->get_layer3D()->is3D() && SGCloudField::enable3D )
continue;
in_cloud = i; in_cloud = i;
} else { } else {
// above cloud layer // above cloud layer
@ -286,7 +288,8 @@ void SGSky::modify_vis( float alt, float time_factor ) {
ratio = 1.0; 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 // do nothing, clear layers aren't drawn, don't affect
// visibility andn dont' need to be faded in or out. // visibility andn dont' need to be faded in or out.
} else if ( (cloud_layers[i]->getCoverage() == } else if ( (cloud_layers[i]->getCoverage() ==

View File

@ -12,7 +12,7 @@ IMAGE_SERVER_INCL =
IMAGE_SERVER_SRCS = IMAGE_SERVER_SRCS =
endif endif
noinst_HEADERS = colours.h RenderTexture.h noinst_HEADERS = colours.h GLBitmaps.h
include_HEADERS = \ include_HEADERS = \
colors.hxx \ colors.hxx \
@ -20,11 +20,12 @@ include_HEADERS = \
$(IMAGE_SERVER_INCL) \ $(IMAGE_SERVER_INCL) \
screen-dump.hxx \ screen-dump.hxx \
extensions.hxx \ extensions.hxx \
RenderTexture.h \
tr.h tr.h
libsgscreen_a_SOURCES = \ libsgscreen_a_SOURCES = \
texture.cxx \ texture.cxx \
GLBitmaps.cxx GLBitmaps.h \ GLBitmaps.cxx \
$(IMAGE_SERVER_SRCS) \ $(IMAGE_SERVER_SRCS) \
screen-dump.cxx \ screen-dump.cxx \
tr.cxx \ tr.cxx \