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:
parent
e19091d809
commit
2ea9e723c2
@ -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/newcloud.hxx>
|
||||
#include "visual_enviro.hxx"
|
||||
|
||||
#include <vector>
|
||||
|
||||
SG_USING_STD(vector);
|
||||
|
||||
|
||||
typedef struct {
|
||||
Point3D pt;
|
||||
int depth;
|
||||
int prev;
|
||||
} lt_tree_seg;
|
||||
|
||||
#define MAX_RAIN_SLICE 200
|
||||
static float rainpos[MAX_RAIN_SLICE];
|
||||
#define MAX_LT_TREE_SEG 400
|
||||
|
||||
/**
|
||||
* A class to render lightnings.
|
||||
*/
|
||||
class SGLightning {
|
||||
public:
|
||||
/**
|
||||
* Build a new lightning.
|
||||
* The lightning has a limited life time. It will also play a thunder sounder once.
|
||||
* @param lon lon longitude in degree
|
||||
* @param lat lat latitude in degree
|
||||
* @param alt asl of top of lightning
|
||||
*/
|
||||
SGLightning(double lon, double lat, double alt);
|
||||
~SGLightning();
|
||||
void lt_Render(void);
|
||||
void lt_build(void);
|
||||
void lt_build_tree_branch(int tree_nr, Point3D &start, float energy, int nbseg, float segsize);
|
||||
|
||||
// contains all the segments of the lightning
|
||||
lt_tree_seg lt_tree[MAX_LT_TREE_SEG];
|
||||
// segment count
|
||||
int nb_tree;
|
||||
// position of lightning
|
||||
double lon, lat, alt;
|
||||
int sequence_count;
|
||||
// time to live
|
||||
double age;
|
||||
};
|
||||
|
||||
typedef vector<SGLightning *> list_of_lightning;
|
||||
static list_of_lightning lightnings;
|
||||
|
||||
SGEnviro sgEnviro;
|
||||
|
||||
SGEnviro::SGEnviro(void) :
|
||||
view_in_cloud(false),
|
||||
precipitation_enable_state(false),
|
||||
turbulence_enable_state(false),
|
||||
precipitation_enable_state(true),
|
||||
lightning_enable_state(false),
|
||||
soundMgr(NULL),
|
||||
snd_active(false),
|
||||
snd_dist(0.0),
|
||||
last_cloud_turbulence(0.0),
|
||||
cloud_turbulence(0.0),
|
||||
elapsed_time(0.0),
|
||||
dt(0.0),
|
||||
min_time_before_lt(0.0),
|
||||
fov_width(55.0),
|
||||
fov_height(55.0),
|
||||
precipitation_density(100.0)
|
||||
|
||||
{
|
||||
for(int i = 0; i < MAX_RAIN_SLICE ; i++)
|
||||
rainpos[i] = sg_random();
|
||||
|
||||
}
|
||||
|
||||
SGEnviro::~SGEnviro(void) {
|
||||
list_of_lightning::iterator iLightning;
|
||||
for( iLightning = lightnings.begin() ; iLightning != lightnings.end() ; iLightning++ ) {
|
||||
delete (*iLightning);
|
||||
}
|
||||
lightnings.clear();
|
||||
}
|
||||
|
||||
void SGEnviro::startOfFrame(void) {
|
||||
void SGEnviro::startOfFrame( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double delta_time) {
|
||||
view_in_cloud = false;
|
||||
// ask the impostor cache to do some cleanup
|
||||
if(SGNewCloud::cldCache)
|
||||
SGNewCloud::cldCache->startNewFrame();
|
||||
last_cloud_turbulence = cloud_turbulence;
|
||||
cloud_turbulence = 0.0;
|
||||
elapsed_time += delta_time;
|
||||
min_time_before_lt -= delta_time;
|
||||
dt = delta_time;
|
||||
|
||||
sgMat4 T1, LON, LAT;
|
||||
sgVec3 axis;
|
||||
|
||||
sgMakeTransMat4( T1, p );
|
||||
|
||||
sgSetVec3( axis, 0.0, 0.0, 1.0 );
|
||||
sgMakeRotMat4( LON, lon, axis );
|
||||
|
||||
sgSetVec3( axis, 0.0, 1.0, 0.0 );
|
||||
sgMakeRotMat4( LAT, 90.0 - lat, axis );
|
||||
|
||||
sgMat4 TRANSFORM;
|
||||
|
||||
sgCopyMat4( TRANSFORM, T1 );
|
||||
sgPreMultMat4( TRANSFORM, LON );
|
||||
sgPreMultMat4( TRANSFORM, LAT );
|
||||
|
||||
sgCoord pos;
|
||||
sgSetCoord( &pos, TRANSFORM );
|
||||
|
||||
sgMakeCoordMat4( transform, &pos );
|
||||
last_lon = lon;
|
||||
last_lat = lat;
|
||||
last_alt = alt;
|
||||
}
|
||||
|
||||
void SGEnviro::endOfFrame(void) {
|
||||
}
|
||||
|
||||
double SGEnviro::get_cloud_turbulence(void) const {
|
||||
return last_cloud_turbulence;
|
||||
}
|
||||
|
||||
// this can be queried to add some turbulence for example
|
||||
bool SGEnviro::is_view_in_cloud(void) const {
|
||||
return view_in_cloud;
|
||||
@ -71,6 +184,10 @@ bool SGEnviro::get_clouds_enable_state(void) const {
|
||||
return SGCloudField::get_enable3dClouds();
|
||||
}
|
||||
|
||||
bool SGEnviro::get_turbulence_enable_state(void) const {
|
||||
return turbulence_enable_state;
|
||||
}
|
||||
|
||||
void SGEnviro::set_CacheResolution(int resolutionPixels) {
|
||||
SGCloudField::set_CacheResolution(resolutionPixels);
|
||||
}
|
||||
@ -87,7 +204,9 @@ void SGEnviro::set_clouds_density(float density) {
|
||||
void SGEnviro::set_clouds_enable_state(bool enable) {
|
||||
SGCloudField::set_enable3dClouds(enable);
|
||||
}
|
||||
|
||||
void SGEnviro::set_turbulence_enable_state(bool enable) {
|
||||
turbulence_enable_state = enable;
|
||||
}
|
||||
// rain/snow
|
||||
float SGEnviro::get_precipitation_density(void) const {
|
||||
return precipitation_density;
|
||||
@ -105,9 +224,453 @@ void SGEnviro::set_precipitation_enable_state(bool enable) {
|
||||
|
||||
// others
|
||||
bool SGEnviro::get_lightning_enable_state(void) const {
|
||||
return false;
|
||||
return lightning_enable_state;
|
||||
}
|
||||
|
||||
void SGEnviro::set_lightning_enable_state(bool enable) {
|
||||
lightning_enable_state = enable;
|
||||
if( ! enable ) {
|
||||
// TODO:cleanup
|
||||
}
|
||||
}
|
||||
|
||||
void SGEnviro::setLight(sgVec4 adj_fog_color) {
|
||||
sgCopyVec4( fog_color, adj_fog_color );
|
||||
if( false ) {
|
||||
// ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, l->scene_diffuse() );
|
||||
}
|
||||
}
|
||||
|
||||
void SGEnviro::callback_cloud(float heading, float alt, float radius, int familly, float dist) {
|
||||
// send data to wx radar
|
||||
// compute turbulence
|
||||
// draw precipitation
|
||||
// draw lightning
|
||||
// compute illumination
|
||||
|
||||
// http://www.pilotfriend.com/flight_training/weather/THUNDERSTORM%20HAZARDS1.htm
|
||||
double turbulence = 0.0;
|
||||
if( dist < radius * radius * 2.25f ) {
|
||||
switch(familly) {
|
||||
case SGNewCloud::CLFamilly_st:
|
||||
turbulence = 0.2;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_ci:
|
||||
case SGNewCloud::CLFamilly_cs:
|
||||
case SGNewCloud::CLFamilly_cc:
|
||||
case SGNewCloud::CLFamilly_ac:
|
||||
case SGNewCloud::CLFamilly_as:
|
||||
turbulence = 0.1;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_sc:
|
||||
turbulence = 0.3;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_ns:
|
||||
turbulence = 0.4;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_cu:
|
||||
turbulence = 0.5;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_cb:
|
||||
turbulence = 0.6;
|
||||
break;
|
||||
}
|
||||
// full turbulence inside cloud, half in the vicinity
|
||||
if( dist > radius * radius )
|
||||
turbulence *= 0.5;
|
||||
if( turbulence > cloud_turbulence )
|
||||
cloud_turbulence = turbulence;
|
||||
// we can do 'local' precipitations too
|
||||
}
|
||||
|
||||
// convert to LWC for radar (experimental)
|
||||
// http://www-das.uwyo.edu/~geerts/cwx/notes/chap08/moist_cloud.html
|
||||
double LWC = 0.0;
|
||||
switch(familly) {
|
||||
case SGNewCloud::CLFamilly_st:
|
||||
LWC = 0.29;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_cu:
|
||||
LWC = 0.27;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_cb:
|
||||
LWC = 2.0;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_sc:
|
||||
LWC = 0.44;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_ci:
|
||||
LWC = 0.03;
|
||||
break;
|
||||
// no data
|
||||
case SGNewCloud::CLFamilly_cs:
|
||||
case SGNewCloud::CLFamilly_cc:
|
||||
case SGNewCloud::CLFamilly_ac:
|
||||
case SGNewCloud::CLFamilly_as:
|
||||
LWC = 0.03;
|
||||
break;
|
||||
case SGNewCloud::CLFamilly_ns:
|
||||
LWC = 0.29*2.0;
|
||||
break;
|
||||
}
|
||||
// TODO:send data to radar antenna
|
||||
// NB:data valid only from cockpit view
|
||||
|
||||
// spawn a new lightning
|
||||
if(min_time_before_lt <= 0.0 && (familly == SGNewCloud::CLFamilly_cb) &&
|
||||
dist < 15000.0 * 15000.0 && sg_random() > 0.9f) {
|
||||
double lat, lon;
|
||||
Point3D orig, dest;
|
||||
orig.setlat(last_lat * SG_DEGREES_TO_RADIANS );
|
||||
orig.setlon(last_lon * SG_DEGREES_TO_RADIANS );
|
||||
orig.setelev(0.0);
|
||||
dist = sgSqrt(dist);
|
||||
dest = calc_gc_lon_lat(orig, heading, dist);
|
||||
lon = dest.lon() * SG_RADIANS_TO_DEGREES;
|
||||
lat = dest.lat() * SG_RADIANS_TO_DEGREES;
|
||||
addLightning( lon, lat, alt );
|
||||
|
||||
// reset timer
|
||||
min_time_before_lt = 5.0 + sg_random() * 30;
|
||||
// DEBUG only
|
||||
// min_time_before_lt = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// precipitation rendering code
|
||||
void SGEnviro::DrawCone2(float baseRadius, float height, int slices, bool down, double rain_norm, double speed) {
|
||||
|
||||
sgVec3 light;
|
||||
sgVec3 min_light = {0.35, 0.35, 0.35};
|
||||
sgAddVec3( light, fog_color, min_light );
|
||||
float da = SG_PI * 2.0f / (float) slices;
|
||||
// low number = faster
|
||||
float speedf = 2.5f - speed / 200.0;
|
||||
if( speedf < 1.0f )
|
||||
speedf = 1.0f;
|
||||
float lenf = 0.03f + speed / 2000.0;
|
||||
if( lenf > 0.10f )
|
||||
lenf = 0.10f;
|
||||
float t = fmod((float) elapsed_time, speedf) / speedf;
|
||||
// t = 0.1f;
|
||||
if( !down )
|
||||
t = 1.0f - t;
|
||||
float angle = 0.0f;
|
||||
glColor4f(1.0f, 0.7f, 0.7f, 0.9f);
|
||||
glBegin(GL_LINES);
|
||||
int rainpos_indice = 0;
|
||||
for( int i = 0 ; i < slices ; i++ ) {
|
||||
float x = cos(angle) * baseRadius;
|
||||
float y = sin(angle) * baseRadius;
|
||||
angle += da;
|
||||
sgVec3 dir = {x, -height, y};
|
||||
|
||||
// rain drops at 2 different speed to simulate depth
|
||||
float t1 = (i & 1 ? t : t + t) + rainpos[rainpos_indice];
|
||||
if(t1 > 1.0f) t1 -= 1.0f;
|
||||
if(t1 > 1.0f) t1 -= 1.0f;
|
||||
|
||||
// distant raindrops are more transparent
|
||||
float c = (i & 1 ? t1 * 0.5f : t1 * 0.9f);
|
||||
glColor4f(c * light[0], c * light[1], c * light[2], c);
|
||||
sgVec3 p1, p2;
|
||||
sgScaleVec3(p1, dir, t1);
|
||||
// distant raindrops are shorter
|
||||
float t2 = t1 + (i & 1 ? lenf : lenf+lenf);
|
||||
sgScaleVec3(p2, dir, t2);
|
||||
|
||||
glVertex3f(p1[0], p1[1] + height, p1[2]);
|
||||
glVertex3f(p2[0], p2[1] + height, p2[2]);
|
||||
if( ++rainpos_indice >= MAX_RAIN_SLICE )
|
||||
rainpos_indice = 0;
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// TODO:check alt vs layer
|
||||
void SGEnviro::drawRain(double pitch, double roll, double speed, double rain_norm) {
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glShadeModel(GL_SMOOTH);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||||
glDisable( GL_FOG );
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
int slice_count = (40.0 + rain_norm*150.0)* precipitation_density / 100.0;
|
||||
|
||||
float angle = speed;
|
||||
if( angle > 90.0 )
|
||||
angle = 90.0;
|
||||
|
||||
glPushMatrix();
|
||||
// TODO:find the real view orientation, not the AC one
|
||||
// the cone rotate with speed
|
||||
angle = -pitch - angle;
|
||||
glRotatef(angle, 1.0, 0.0, 0.0);
|
||||
glRotatef(roll, 0.0, 1.0, 0.0);
|
||||
|
||||
// up cone
|
||||
DrawCone2(15.0, 30.0, slice_count, true, rain_norm, speed);
|
||||
// down cone (usually not visible)
|
||||
if(angle > 0.0)
|
||||
DrawCone2(15.0, -30.0, slice_count, false, rain_norm, speed);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
|
||||
glEnable( GL_FOG );
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
}
|
||||
|
||||
void SGEnviro::set_soundMgr(SGSoundMgr *mgr) {
|
||||
soundMgr = mgr;
|
||||
}
|
||||
|
||||
void SGEnviro::drawPrecipitation(double rain_norm, double snow_norm, double hail_norm, double pitch, double roll, double speed) {
|
||||
// TODO:check alt with right layer (wich layer ?)
|
||||
if( precipitation_enable_state && rain_norm > 0.0)
|
||||
drawRain(pitch, roll, speed, rain_norm);
|
||||
}
|
||||
|
||||
|
||||
SGLightning::SGLightning(double _lon, double _lat, double _alt) :
|
||||
lon(_lon),
|
||||
lat(_lat),
|
||||
alt(_alt),
|
||||
age(1.0 + sg_random() * 4.0),
|
||||
nb_tree(0)
|
||||
{
|
||||
// sequence_count = 1 + sg_random() * 5.0;
|
||||
lt_build();
|
||||
}
|
||||
|
||||
SGLightning::~SGLightning() {
|
||||
}
|
||||
|
||||
// lightning rendering code
|
||||
void SGLightning::lt_build_tree_branch(int tree_nr, Point3D &start, float energy, int nbseg, float segsize) {
|
||||
|
||||
sgVec3 dir, newdir;
|
||||
int nseg = 0;
|
||||
Point3D pt = start;
|
||||
if( nbseg == 50 )
|
||||
sgSetVec3( dir, 0.0, -1.0, 0.0 );
|
||||
else {
|
||||
sgSetVec3( dir, sg_random() - 0.5f, sg_random() - 0.5f, sg_random() - 0.5f);
|
||||
sgNormaliseVec3(dir);
|
||||
}
|
||||
if( nb_tree >= MAX_LT_TREE_SEG )
|
||||
return;
|
||||
|
||||
lt_tree[nb_tree].depth = tree_nr;
|
||||
nseg = 0;
|
||||
lt_tree[nb_tree].pt = pt;
|
||||
lt_tree[nb_tree].prev = -1;
|
||||
nb_tree ++;
|
||||
|
||||
// TODO:check agl
|
||||
while(nseg < nbseg && pt.y() > 0.0) {
|
||||
int prev = nb_tree - 1;
|
||||
nseg++;
|
||||
// add a branch
|
||||
if( energy * sg_random() > 0.8f )
|
||||
lt_build_tree_branch(tree_nr + 1, pt, energy * 0.9f, nbseg == 50 ? 10 : nbseg * 0.4f, segsize * 0.7f);
|
||||
|
||||
if( nb_tree >= MAX_LT_TREE_SEG )
|
||||
return;
|
||||
sgSetVec3(newdir, (sg_random() - 0.5f), (sg_random() - 0.5f) - (nbseg == 50 ? 0.5f : 0.0), (sg_random() - 0.5f));
|
||||
sgNormaliseVec3(newdir);
|
||||
sgAddVec3( dir, newdir);
|
||||
sgNormaliseVec3(dir);
|
||||
sgVec3 scaleDir;
|
||||
sgScaleVec3( scaleDir, dir, segsize * energy * 0.5f );
|
||||
pt[PX] += scaleDir[0];
|
||||
pt[PY] += scaleDir[1];
|
||||
pt[PZ] += scaleDir[2];
|
||||
|
||||
lt_tree[nb_tree].depth = tree_nr;
|
||||
lt_tree[nb_tree].pt = pt;
|
||||
lt_tree[nb_tree].prev = prev;
|
||||
nb_tree ++;
|
||||
}
|
||||
}
|
||||
|
||||
void SGLightning::lt_build(void) {
|
||||
Point3D top;
|
||||
nb_tree = 0;
|
||||
top[PX] = 0 ;
|
||||
top[PY] = alt;
|
||||
top[PZ] = 0;
|
||||
lt_build_tree_branch(0, top, 1.0, 50, top[PY] / 8.0);
|
||||
if( ! sgEnviro.soundMgr )
|
||||
return;
|
||||
Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
|
||||
Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 );
|
||||
double course = 0.0, dist = 0.0;
|
||||
calc_gc_course_dist( dest, start, &course, &dist );
|
||||
if( dist < 10000.0 && ! sgEnviro.snd_playing && (dist < sgEnviro.snd_dist || ! sgEnviro.snd_active) ) {
|
||||
sgEnviro.snd_timer = 0.0;
|
||||
sgEnviro.snd_wait = dist / 340;
|
||||
sgEnviro.snd_dist = dist;
|
||||
sgEnviro.snd_pos_lat = lat;
|
||||
sgEnviro.snd_pos_lon = lon;
|
||||
sgEnviro.snd_active = true;
|
||||
sgEnviro.snd_playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SGLightning::lt_Render(void) {
|
||||
float flash = 0.5;
|
||||
if( fmod(sgEnviro.elapsed_time*100.0, 100.0) > 50.0 )
|
||||
flash = sg_random() * 0.75f + 0.25f;
|
||||
float h = lt_tree[0].pt[PY];
|
||||
sgVec4 col={0.62f, 0.83f, 1.0f, 1.0f};
|
||||
sgVec4 c;
|
||||
|
||||
#define DRAW_SEG() \
|
||||
{glBegin(GL_LINES); \
|
||||
glColor4fv(c); \
|
||||
glVertex3f(lt_tree[n].pt[PX], lt_tree[n].pt[PZ], lt_tree[n].pt[PY]); \
|
||||
glVertex3f(lt_tree[lt_tree[n].prev].pt[PX], lt_tree[lt_tree[n].prev].pt[PZ], lt_tree[lt_tree[n].prev].pt[PY]); \
|
||||
glEnd();}
|
||||
|
||||
glDepthMask( GL_FALSE );
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable( GL_FOG );
|
||||
glPushMatrix();
|
||||
sgMat4 modelview, tmp;
|
||||
ssgGetModelviewMatrix( modelview );
|
||||
sgCopyMat4( tmp, sgEnviro.transform );
|
||||
sgPostMultMat4( tmp, modelview );
|
||||
ssgLoadModelviewMatrix( tmp );
|
||||
|
||||
Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
|
||||
Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 );
|
||||
double course = 0.0, dist = 0.0;
|
||||
calc_gc_course_dist( dest, start, &course, &dist );
|
||||
double ax = 0.0, ay = 0.0;
|
||||
ax = cos(course) * dist;
|
||||
ay = sin(course) * dist;
|
||||
|
||||
glTranslatef( ax, ay, -sgEnviro.last_alt );
|
||||
// glTranslatef( ax, ay, 0 );
|
||||
|
||||
for( int n = 0 ; n < nb_tree ; n++ ) {
|
||||
if( lt_tree[n].prev < 0 )
|
||||
continue;
|
||||
|
||||
float t1 = sgLerp(0.5, 1.0, lt_tree[n].pt[PY] / h);
|
||||
t1 *= flash;
|
||||
if( lt_tree[n].depth >= 2 ) {
|
||||
glLineWidth(3);
|
||||
sgScaleVec4(c, col, t1 * 0.6f);
|
||||
DRAW_SEG();
|
||||
} else {
|
||||
if( lt_tree[n].depth == 0 ) {
|
||||
glLineWidth(12);
|
||||
sgScaleVec4(c, col, t1 * 0.5f);
|
||||
DRAW_SEG();
|
||||
|
||||
glLineWidth(6);
|
||||
sgScaleVec4(c, col, t1);
|
||||
DRAW_SEG();
|
||||
} else {
|
||||
glLineWidth(6);
|
||||
sgScaleVec4(c, col, t1 * 0.7f);
|
||||
DRAW_SEG();
|
||||
}
|
||||
|
||||
if( lt_tree[n].depth == 0 )
|
||||
glLineWidth(3);
|
||||
else
|
||||
glLineWidth(2);
|
||||
|
||||
sgSetVec4(c, t1, t1, t1, t1);
|
||||
DRAW_SEG();
|
||||
}
|
||||
|
||||
}
|
||||
glLineWidth(1);
|
||||
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
|
||||
glPopMatrix();
|
||||
glDepthMask( GL_TRUE );
|
||||
glEnable( GL_FOG );
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
void SGEnviro::addLightning(double lon, double lat, double alt) {
|
||||
if( lightnings.size() > 10)
|
||||
return;
|
||||
SGLightning *lt= new SGLightning(lon, lat, alt);
|
||||
lightnings.push_back(lt);
|
||||
}
|
||||
|
||||
void SGEnviro::drawLightning(void) {
|
||||
list_of_lightning::iterator iLightning;
|
||||
// play 'thunder' for lightning
|
||||
if( snd_active )
|
||||
if( !snd_playing ) {
|
||||
// wait until sound has reached us
|
||||
snd_timer += dt;
|
||||
if( snd_timer >= snd_wait ) {
|
||||
snd_playing = true;
|
||||
// compute relative position of lightning
|
||||
Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
|
||||
Point3D dest( snd_pos_lon*SG_DEGREES_TO_RADIANS, snd_pos_lat*SG_DEGREES_TO_RADIANS, 0.0 );
|
||||
double course = 0.0, dist = 0.0;
|
||||
calc_gc_course_dist( dest, start, &course, &dist );
|
||||
double ax = 0.0, ay = 0.0;
|
||||
ax = cos(course) * dist;
|
||||
ay = sin(course) * dist;
|
||||
SGSoundSample *snd = soundMgr->find("thunder");
|
||||
if( snd ) {
|
||||
ALfloat pos[3]={ax, ay, -sgEnviro.last_alt };
|
||||
snd->set_source_pos(pos);
|
||||
snd->play_once();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if( !soundMgr->is_playing("thunder") ) {
|
||||
snd_active = false;
|
||||
snd_playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
if( ! lightning_enable_state )
|
||||
return;
|
||||
|
||||
for( iLightning = lightnings.begin() ; iLightning != lightnings.end() ; iLightning++ ) {
|
||||
if( dt )
|
||||
if( sg_random() > 0.95f )
|
||||
(*iLightning)->lt_build();
|
||||
(*iLightning)->lt_Render();
|
||||
(*iLightning)->age -= dt;
|
||||
if( (*iLightning)->age < 0.0 ) {
|
||||
delete (*iLightning);
|
||||
lightnings.erase( iLightning );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SGEnviro::setFOV( float w, float h ) {
|
||||
fov_width = w;
|
||||
fov_height = h;
|
||||
}
|
||||
|
||||
void SGEnviro::getFOV( float &w, float &h ) {
|
||||
w = fov_width;
|
||||
h = fov_height;
|
||||
}
|
||||
|
@ -22,23 +22,89 @@
|
||||
#ifndef _VISUAL_ENVIRO_HXX
|
||||
#define _VISUAL_ENVIRO_HXX
|
||||
|
||||
class SGEnviro {
|
||||
#include <simgear/compiler.h>
|
||||
#include STL_STRING
|
||||
|
||||
SG_USING_STD(string);
|
||||
|
||||
class SGLightning;
|
||||
class SGSoundMgr;
|
||||
|
||||
/**
|
||||
* Visual environment helper class.
|
||||
*/
|
||||
class SGEnviro {
|
||||
friend SGLightning;
|
||||
private:
|
||||
void DrawCone2(float baseRadius, float height, int slices, bool down, double rain_norm, double speed);
|
||||
void lt_update(void);
|
||||
|
||||
bool view_in_cloud;
|
||||
bool precipitation_enable_state;
|
||||
float precipitation_density;
|
||||
bool turbulence_enable_state;
|
||||
double last_cloud_turbulence, cloud_turbulence;
|
||||
bool lightning_enable_state;
|
||||
double elapsed_time, dt;
|
||||
sgVec4 fog_color;
|
||||
sgMat4 transform;
|
||||
double last_lon, last_lat, last_alt;
|
||||
SGSoundMgr *soundMgr;
|
||||
bool snd_active, snd_playing;
|
||||
double snd_timer, snd_wait, snd_pos_lat, snd_pos_lon, snd_dist;
|
||||
double min_time_before_lt;
|
||||
|
||||
float fov_width, fov_height;
|
||||
|
||||
public:
|
||||
SGEnviro();
|
||||
~SGEnviro();
|
||||
|
||||
void startOfFrame(void);
|
||||
/**
|
||||
* Forward a few states used for renderings.
|
||||
*/
|
||||
void startOfFrame( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double delta_time);
|
||||
|
||||
void endOfFrame(void);
|
||||
|
||||
/**
|
||||
* Whenever a cloud is drawn we check his 'impact' on the environment.
|
||||
* @param heading direction of cloud in radians
|
||||
* @param alt asl of cloud in meters
|
||||
* @param radius radius of cloud in meters
|
||||
* @param familly cloud familly
|
||||
* @param dist squared dist to cloud in meters
|
||||
*/
|
||||
void callback_cloud(float heading, float alt, float radius, int familly, float dist);
|
||||
|
||||
void drawRain(double pitch, double roll, double speed, double rain_norm);
|
||||
/**
|
||||
* Draw rain or snow precipitation around the viewer.
|
||||
* @param rain_norm rain normalized intensity given by metar class
|
||||
* @param snow_norm snow normalized intensity given by metar class
|
||||
* @param hail_norm hail normalized intensity given by metar class
|
||||
* @param pitch pitch rotation of viewer
|
||||
* @param roll roll rotation of viewer
|
||||
* @param speed moving speed of viewer in kt
|
||||
*/
|
||||
void drawPrecipitation(double rain_norm, double snow_norm, double hail_norm,
|
||||
double pitch, double roll, double speed);
|
||||
|
||||
/**
|
||||
* Draw the lightnings spawned by cumulo nimbus.
|
||||
*/
|
||||
void drawLightning(void);
|
||||
|
||||
/**
|
||||
* Forward the fog color used by the rain rendering.
|
||||
* @param adj_fog_color color of the fog
|
||||
*/
|
||||
void setLight(sgVec4 adj_fog_color);
|
||||
|
||||
// this can be queried to add some turbulence for example
|
||||
bool is_view_in_cloud(void) const;
|
||||
void set_view_in_cloud(bool incloud);
|
||||
double get_cloud_turbulence(void) const;
|
||||
|
||||
// Clouds
|
||||
// return the size of the memory pool used by texture impostors
|
||||
@ -47,23 +113,76 @@ 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;
|
||||
|
@ -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,11 +79,12 @@ bool SGBbCache::allocTextureMemory(int cacheCount, int textureDimension) {
|
||||
cacheSizeKb = (textureDimension * textureDimension * 4);
|
||||
cacheSizeKb *= cacheCount;
|
||||
cacheSizeKb /= 1024;
|
||||
if(rt) {
|
||||
rt->BeginCapture();
|
||||
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 )
|
||||
if( bbList[bbId].needRedraw )
|
||||
return false;
|
||||
|
||||
if( fabs(angleX - bbList[bbId].angleX) >= 4.0 )
|
||||
return false;
|
||||
// if( fabs(angleY - bbList[bbId].angleY) >= 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;
|
||||
}
|
||||
|
||||
|
@ -29,20 +29,36 @@
|
||||
#include <simgear/screen/extensions.hxx>
|
||||
#include <simgear/screen/RenderTexture.h>
|
||||
|
||||
/**
|
||||
* Billboard helper class.
|
||||
*/
|
||||
class SGBbCache {
|
||||
private:
|
||||
|
||||
typedef struct {
|
||||
/**
|
||||
* storage class for impostors state.
|
||||
*/
|
||||
class bbInfo {
|
||||
public:
|
||||
/// the texture used by this impostor
|
||||
GLuint texID;
|
||||
/// the cloud owning this impostor
|
||||
int cldID;
|
||||
float angleX, angleY;
|
||||
// creation frame number for debug only
|
||||
int frame;
|
||||
// last time this entry was used
|
||||
/// last time this entry was used
|
||||
int frameUsed;
|
||||
} bbInfo;
|
||||
/// dirty flag for lazy rebuild of impostor
|
||||
bool needRedraw;
|
||||
};
|
||||
|
||||
void freeTextureMemory(void);
|
||||
/**
|
||||
* Allocate and initialize the texture pool.
|
||||
* @param count the number of texture to build
|
||||
* @param textureDimension size in pixel of each texture
|
||||
*/
|
||||
bool allocTextureMemory(int count, int textureDimension);
|
||||
|
||||
// a list of impostors
|
||||
@ -64,53 +80,126 @@ public:
|
||||
SGBbCache(void);
|
||||
~SGBbCache(void);
|
||||
|
||||
// call this first to initialize everything, cacheCount is the number of texture to allocate
|
||||
/**
|
||||
* Call this first to initialize the cache.
|
||||
* @param cacheCount the number of texture to allocate
|
||||
*/
|
||||
void init(int cacheCount);
|
||||
|
||||
// free one cache slot, usualy when the cached object is destroyed
|
||||
/**
|
||||
* Free one cache slot, usualy when the cached object is destroyed.
|
||||
* @param bbId the impostor slot
|
||||
* @param cldId the cloud identifier
|
||||
*/
|
||||
void free(int bbId, int cldId);
|
||||
|
||||
// allocate a new texture, return an index in the cache
|
||||
/**
|
||||
* Allocate a new impostor.
|
||||
* @param cldId the cloud identifier
|
||||
* @return an impostor slot
|
||||
*/
|
||||
int alloc(int cldId);
|
||||
|
||||
// give the texture name to use
|
||||
/**
|
||||
* Query the texture name associated with this cloud.
|
||||
* @param bbId the impostor slot
|
||||
* @param cldId the cloud identifier
|
||||
* @return a texture name
|
||||
*/
|
||||
GLuint QueryTexID(int cldId, int bbId);
|
||||
|
||||
// save the rendered texture from the current context to a new texture
|
||||
/**
|
||||
* Save the rendered texture from the current context to a new texture.
|
||||
* @param bbId the impostor slot
|
||||
*/
|
||||
void setTextureData(int bbId);
|
||||
|
||||
// start the rendering of a billboard in the RTT context
|
||||
/**
|
||||
* Start the rendering of a billboard in the RTT context.
|
||||
*/
|
||||
void beginCapture(void);
|
||||
|
||||
// adjust the projection matrix of the RTT context to the size of the object
|
||||
/**
|
||||
* Adjust the projection matrix of the RTT context to the size of the object.
|
||||
* @param radius radius in meters of the object to draw
|
||||
* @param dist_center distance between viewer and object
|
||||
*/
|
||||
void setRadius(float radius, float dist_center);
|
||||
|
||||
// forget the RTT and go back to the previous rendering context
|
||||
/**
|
||||
* Forget the RTT and go back to the previous rendering context.
|
||||
*/
|
||||
void endCapture(void);
|
||||
|
||||
// for debugging only, give the number of frames since the inpostor was built
|
||||
/**
|
||||
* For debugging only, give the number of frames since the impostor was built.
|
||||
* @param bbId the impostor slot
|
||||
*/
|
||||
int queryImpostorAge(int bbId);
|
||||
|
||||
// can we still use this impostor ?
|
||||
/**
|
||||
* Can we still use this impostor ? Check versus view angles and load.
|
||||
* @param bbId the impostor slot
|
||||
* @param cloudId the cloud identifier
|
||||
* @param angleY rotation needed to face the impostor
|
||||
* @param angleX rotation needed to face the impostor
|
||||
*/
|
||||
bool isBbValid( int cloudId, int bbId, float angleY, float angleX);
|
||||
|
||||
// save view angles of this billboard
|
||||
/**
|
||||
* Save view angles of this billboard.
|
||||
* @param bbId the impostor slot
|
||||
* @param cloudId the cloud identifier
|
||||
* @param angleY rotation needed to face the impostor
|
||||
* @param angleX rotation needed to face the impostor
|
||||
*/
|
||||
void setReference( int cloudId, int bbId, float angleY, float angleX);
|
||||
|
||||
// prepare the cache for the rendering of a new frame
|
||||
/**
|
||||
* Prepare the cache for the rendering of a new frame.
|
||||
* Do some garbage collect of unused impostors
|
||||
*/
|
||||
void startNewFrame(void);
|
||||
|
||||
// alloc the impostors texture memory given the size of the memory pool
|
||||
// if sizeKb == 0 then the memory pool is freed and impostors are disabled
|
||||
/**
|
||||
* Alloc the impostors texture memory given the size of the memory pool.
|
||||
* If sizeKb == 0 then the memory pool is freed and impostors are disabled
|
||||
* @param sizeKb size of the texture pool in K
|
||||
*/
|
||||
bool setCacheSize(int sizeKb);
|
||||
|
||||
// alloc the impostors texture memory given the count and size of texture
|
||||
// if count == 0 then the memory pool is freed and impostors are disabled
|
||||
/**
|
||||
* Alloc the impostors texture memory given the count and size of texture.
|
||||
* If count == 0 then the memory pool is freed and impostors are disabled
|
||||
* @param count number of texture to allocate
|
||||
* @param textureDimension size of each texture in pixels
|
||||
*/
|
||||
bool setCacheSize(int count, int textureDimension);
|
||||
|
||||
// return the size of the memory pool used by texture impostors
|
||||
bool isRttAvailable(void) { return rtAvailable; }
|
||||
|
||||
/**
|
||||
* Force all impostors to be rebuilt.
|
||||
*/
|
||||
void invalidateCache(void);
|
||||
|
||||
/**
|
||||
* Flag the impostor for a lazy update.
|
||||
* @param bbId the impostor slot
|
||||
* @param cldId the cloud identifier
|
||||
*/
|
||||
void invalidate(int cldId, int bbId);
|
||||
|
||||
/**
|
||||
* Return the size of the memory pool used by texture impostors.
|
||||
* @return size of the memory pool in Kb
|
||||
*/
|
||||
int queryCacheSize(void);
|
||||
|
||||
/**
|
||||
* Maximum number of impostor to regen each frame.
|
||||
* If we can't update all of them we will do that in the next frame
|
||||
*/
|
||||
int maxImpostorRegenFrame;
|
||||
};
|
||||
|
||||
|
@ -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 ) {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -37,12 +37,17 @@ public:
|
||||
SGNewCloud *aCloud;
|
||||
sgVec3 eyePos;
|
||||
float dist;
|
||||
float heading;
|
||||
float alt;
|
||||
bool operator<(const culledCloud &b) const {
|
||||
return this->dist < b.dist;
|
||||
return (this->dist < b.dist);
|
||||
}
|
||||
};
|
||||
typedef vector<culledCloud> list_of_culledCloud;
|
||||
|
||||
/**
|
||||
* A layer of 3D clouds.
|
||||
*/
|
||||
class SGCloudField {
|
||||
|
||||
private:
|
||||
@ -51,12 +56,6 @@ private:
|
||||
SGNewCloud *aCloud;
|
||||
sgVec3 pos;
|
||||
bool visible;
|
||||
// float dist;
|
||||
// bool culled;
|
||||
|
||||
// bool operator<(const Cloud &b) {
|
||||
// return this->dist < b.dist;
|
||||
// }
|
||||
};
|
||||
|
||||
|
||||
@ -79,12 +78,15 @@ private:
|
||||
double last_lon, last_lat, last_course;
|
||||
|
||||
float last_density;
|
||||
bool draw_in_3d;
|
||||
|
||||
public:
|
||||
|
||||
SGCloudField();
|
||||
~SGCloudField();
|
||||
|
||||
void clear(void);
|
||||
|
||||
// add one cloud, data is not copied, ownership given
|
||||
void addCloud( sgVec3 pos, SGNewCloud *cloud);
|
||||
|
||||
@ -95,13 +97,17 @@ public:
|
||||
void Render(void);
|
||||
|
||||
// reposition the cloud layer at the specified origin and orientation
|
||||
void reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt);
|
||||
void reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt, float direction, float speed);
|
||||
|
||||
bool is3D(void) { return draw_in_3d; }
|
||||
|
||||
// visibility distance for clouds in meters
|
||||
static float CloudVis;
|
||||
|
||||
static float density;
|
||||
static sgVec3 view_vec;
|
||||
|
||||
static float density;
|
||||
static double timer_dt;
|
||||
static double fieldSize;
|
||||
static bool enable3D;
|
||||
|
||||
|
@ -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 ++ ) {
|
||||
sc = 1;
|
||||
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);
|
||||
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,11 +490,6 @@ 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);
|
||||
|
||||
|
||||
@ -433,24 +497,33 @@ void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos,
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<spriteDef> list_of_spriteDef;
|
||||
typedef vector<spriteContainer> list_of_spriteContainer;
|
||||
|
||||
void init(void);
|
||||
|
||||
void computeSimpleLight(sgVec3 eyePos);
|
||||
void addSprite(float x, float y, float z, float r, CLbox_type type, int box);
|
||||
|
||||
@ -91,23 +112,26 @@ private:
|
||||
void CalcAngles(sgVec3 refpos, sgVec3 eyePos, float *angleY, float *angleX);
|
||||
|
||||
// draw a cloud but this time we use the impostor texture
|
||||
void RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dist_center);
|
||||
void RenderBB(sgVec3 deltaPos, bool first_time, float dist_center);
|
||||
|
||||
// determine if it is a good idea to use an impostor to render the cloud
|
||||
bool isBillboardable(float dist);
|
||||
|
||||
int cloudId, bbId;
|
||||
sgVec3 rotX, rotY;
|
||||
|
||||
// int rank;
|
||||
sgVec3 cloudpos, center;
|
||||
float delta_base;
|
||||
list_of_spriteDef list_spriteDef;
|
||||
list_of_spriteContainer list_spriteContainer;
|
||||
float minx, maxx, miny, maxy, minz, maxz;
|
||||
float radius;
|
||||
CLFamilly_type familly;
|
||||
|
||||
// fading data
|
||||
bool direction, fadeActive;
|
||||
float duration, pauseLength;
|
||||
// need timer here
|
||||
float duration, pauseLength, fadetimer;
|
||||
float last_step;
|
||||
|
||||
public:
|
||||
// add a new box to the cloud
|
||||
@ -133,6 +157,9 @@ public:
|
||||
void setFade(float howMuch);
|
||||
|
||||
inline float getRadius() { return radius; }
|
||||
inline sgVec3 *getCenter() { return ¢er; }
|
||||
|
||||
inline CLFamilly_type getFamilly(void) { return familly; }
|
||||
|
||||
// load all textures used to draw cloud sprites
|
||||
static void loadTextures( const string &tex_path );
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include <simgear/math/sg_random.h>
|
||||
|
||||
#include "sky.hxx"
|
||||
|
||||
#include "cloudfield.hxx"
|
||||
|
||||
// Constructor
|
||||
SGSky::SGSky( void ) {
|
||||
@ -187,6 +187,8 @@ void SGSky::preDraw( float alt, float fog_exp2_density ) {
|
||||
// in cloud layer
|
||||
|
||||
// bail now and don't draw any clouds
|
||||
if( cloud_layers[i]->get_layer3D()->is3D() && SGCloudField::enable3D )
|
||||
continue;
|
||||
in_cloud = i;
|
||||
} else {
|
||||
// above cloud layer
|
||||
@ -286,7 +288,8 @@ void SGSky::modify_vis( float alt, float time_factor ) {
|
||||
ratio = 1.0;
|
||||
}
|
||||
|
||||
if ( cloud_layers[i]->getCoverage() == SGCloudLayer::SG_CLOUD_CLEAR ) {
|
||||
if ( cloud_layers[i]->getCoverage() == SGCloudLayer::SG_CLOUD_CLEAR ||
|
||||
cloud_layers[i]->get_layer3D()->is3D() && SGCloudField::enable3D) {
|
||||
// do nothing, clear layers aren't drawn, don't affect
|
||||
// visibility andn dont' need to be faded in or out.
|
||||
} else if ( (cloud_layers[i]->getCoverage() ==
|
||||
|
@ -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 \
|
||||
|
Loading…
Reference in New Issue
Block a user