SGEnviro sgEnviro;
-SGEnviro::SGEnviro(void) {
+SGEnviro::SGEnviro(void) :
+ view_in_cloud(false),
+ precipitation_enable_state(false),
+ precipitation_density(100.0)
+
+{
}
SGEnviro::~SGEnviro(void) {
}
+void SGEnviro::startOfFrame(void) {
+ view_in_cloud = false;
+ if(SGNewCloud::cldCache)
+ SGNewCloud::cldCache->startNewFrame();
+}
+
+void SGEnviro::endOfFrame(void) {
+}
+
+// this can be queried to add some turbulence for example
+bool SGEnviro::is_view_in_cloud(void) const {
+ return view_in_cloud;
+}
+void SGEnviro::set_view_in_cloud(bool incloud) {
+ view_in_cloud = incloud;
+}
+
+int SGEnviro::get_CacheResolution(void) const {
+ return SGCloudField::get_CacheResolution();
+}
+
int SGEnviro::get_clouds_CacheSize(void) const {
return SGCloudField::get_CacheSize();
}
return SGCloudField::get_enable3dClouds();
}
+void SGEnviro::set_CacheResolution(int resolutionPixels) {
+ SGCloudField::set_CacheResolution(resolutionPixels);
+}
+
void SGEnviro::set_clouds_CacheSize(int sizeKb) {
SGCloudField::set_CacheSize(sizeKb);
}
// rain/snow
float SGEnviro::get_precipitation_density(void) const {
- return 0.0;
+ return precipitation_density;
}
bool SGEnviro::get_precipitation_enable_state(void) const {
- return false;
+ return precipitation_enable_state;
}
void SGEnviro::set_precipitation_density(float density) {
+ precipitation_density = density;
}
void SGEnviro::set_precipitation_enable_state(bool enable) {
+ precipitation_enable_state = enable;
}
// others
class SGEnviro {
+private:
+ bool view_in_cloud;
+ bool precipitation_enable_state;
+ float precipitation_density;
+
public:
SGEnviro();
~SGEnviro();
+ void startOfFrame(void);
+ void endOfFrame(void);
+
+ // this can be queried to add some turbulence for example
+ bool is_view_in_cloud(void) const;
+ void set_view_in_cloud(bool incloud);
+
// Clouds
// return the size of the memory pool used by texture impostors
int get_clouds_CacheSize(void) const;
+ int get_CacheResolution(void) const;
float get_clouds_visibility(void) const;
float get_clouds_density(void) const;
bool get_clouds_enable_state(void) const;
void set_clouds_CacheSize(int sizeKb);
+ void set_CacheResolution(int resolutionPixels);
void set_clouds_visibility(float distance);
void set_clouds_density(float density);
void set_clouds_enable_state(bool enable);
extern SGEnviro sgEnviro;
-#endif // _VISUAL_ENVIRO_HXX
-
+#endif // _VISUAL_ENVIRO_HXX
\ No newline at end of file
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glBindTexture(GL_TEXTURE_2D, 0);
cacheSizeKb = (textureDimension * textureDimension * 4);
rt(0),
rtAvailable(false),
frameNumber(0),
- maxImpostorRegenFrame(10)
+ maxImpostorRegenFrame(20)
{
}
}
}
if( cacheCount )
- allocTextureMemory( cacheCount, 256 );
+ allocTextureMemory( cacheCount, 64 );
}
-// TODO:not callable atm, texture size not handled correctly
+
bool SGBbCache::setCacheSize(int count, int textureDimension) {
if( count < 0 || count > 500)
return false;
return allocTextureMemory( count, textureDimension);
}
-// TODO:not callable atm, texture size not handled correctly
+
bool SGBbCache::setCacheSize(int sizeKb) {
if( sizeKb < 0 || sizeKb > 256*1024)
return false;
return true;
int count = 1;
int textureDimension = 256;
- if( cacheSizeKb >= 8*1024 ) {
+ if( sizeKb >= 8*1024 ) {
// more than 32 256x256 textures
textureDimension = 256;
- } else if( cacheSizeKb >= 2*1024 ) {
+ } else if( sizeKb >= 2*1024 ) {
// more than 32 128x128 textures
textureDimension = 128;
} else {
if( builtBBframe >= maxImpostorRegenFrame )
return true;
- if( fabs(angleY - bbList[bbId].angleY) >= 5.0 )
+ if( fabs(angleY - bbList[bbId].angleY) >= 4.0 )
return false;
- if( fabs(angleX - bbList[bbId].angleX) >= 5.0 )
+ if( fabs(angleX - bbList[bbId].angleX) >= 4.0 )
return false;
bbList[bbId].frameUsed = frameNumber;
#include <plib/sg.h>
#include <plib/ssg.h>
#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/polar3d.hxx>
#include STL_ALGORITHM
#include <vector>
SG_USING_STD(vector);
+#include <simgear/environment/visual_enviro.hxx>
#include "newcloud.hxx"
#include "cloudfield.hxx"
bool SGCloudField::enable3D = true;
// 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 = 30000.0;
+double SGCloudField::fieldSize = 27000.0;
float SGCloudField::density = 100.0;
-static int last_cache_size = 4*1024;
+static int last_cache_size = 1*1024;
+static int cacheResolution = 64;
+
+int SGCloudField::get_CacheResolution(void) {
+ return cacheResolution;
+}
+
+void SGCloudField::set_CacheResolution(int resolutionPixels) {
+ if(cacheResolution == resolutionPixels)
+ return;
+ cacheResolution = resolutionPixels;
+ if(enable3D) {
+ int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
+ if(count == 0)
+ count = 1;
+ SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
+ }
+}
int SGCloudField::get_CacheSize(void) {
return SGNewCloud::cldCache->queryCacheSize();
return;
if(sizeKb)
last_cache_size = sizeKb;
- SGNewCloud::cldCache->setCacheSize(sizeKb);
+ if(enable3D) {
+ int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
+ if(count == 0)
+ count = 1;
+// SGNewCloud::cldCache->setCacheSize(sizeKb);
+ SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
+ }
}
void SGCloudField::set_CloudVis(float distance) {
- SGCloudField::CloudVis = distance;
+ if( distance <= fieldSize )
+ SGCloudField::CloudVis = distance;
}
void SGCloudField::set_density(float density) {
SGCloudField::density = density;
sgMakeCoordMat4( transform, &layerpos );
- // TODO:use a simple sphere earth
- double az1, az2, s;
- geo_inverse_wgs_84( 0.0, 0.0, 0.0, lat*SG_RADIANS_TO_DEGREES , lon*SG_RADIANS_TO_DEGREES, &az1, &az2, &s);
- az1 = az1 * SG_DEGREES_TO_RADIANS;
- // compute the view position on a 'flat' earth
- deltay = -cos(SG_PI/2+az1) * s;
- deltax = -sin(SG_PI/2+az1) * s;
-// deltax = cos(0.0) * s;
-// deltay = sin(0.0) * s;
+
this->alt = alt;
// simulate clouds movement from wind
relative_position[SG_Y] += by;
}
+ if ( lon != last_lon || lat != last_lat || sp_dist != 0 ) {
+ Point3D start( last_lon, last_lat, 0.0 );
+ Point3D dest( lon, lat, 0.0 );
+ double course = 0.0, dist = 0.0;
+
+ calc_gc_course_dist( dest, start, &course, &dist );
+ // if start and dest are too close together,
+ // calc_gc_course_dist() can return a course of "nan". If
+ // this happens, lets just use the last known good course.
+ // This is a hack, and it would probably be better to make
+ // calc_gc_course_dist() more robust.
+ if ( isnan(course) ) {
+ course = last_course;
+ } else {
+ last_course = course;
+ }
+
+ // calculate cloud movement due to external forces
+ double ax = 0.0, ay = 0.0;
+
+ if (dist > 0.0) {
+ ax = cos(course) * dist;
+ ay = sin(course) * dist;
+ }
+
+ deltax += ax;
+ deltay += ay;
+
+ last_lon = lon;
+ last_lat = lat;
+ }
+
+
// correct the frustum with the right far plane
ssgContext *context = ssgGetCurrentContext();
frustum = *context->getFrustum();
frustum.setNearFar(1.0, CloudVis);
}
-SGCloudField::SGCloudField() {
+SGCloudField::SGCloudField() :
+ last_density(0.0),
+ deltax(0.0),
+ deltay(0.0),
+ last_course(0.0)
+{
sgSetVec3( relative_position, 0,0,0);
theField.reserve(200);
inViewClouds.reserve(200);
}
+// 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},
+ {1,0,0,0,0,0,0,0,0,0},
+ {1,0,0,0,1,0,0,0,0,0},
+ {1,0,0,0,1,0,0,1,0,0}, // 30%
+ {1,0,1,0,1,0,0,1,0,0},
+ {1,0,1,0,1,0,1,1,0,0}, // 50%
+ {1,0,1,0,1,0,1,1,0,1},
+ {1,0,1,1,1,0,1,1,0,1}, // 70%
+ {1,1,1,1,1,0,1,1,0,1},
+ {1,1,1,1,1,0,1,1,1,1}, // 90%
+ {1,1,1,1,1,1,1,1,1,1}
+};
+
+// set the visible flag depending on density
+void SGCloudField::applyDensity(void) {
+ int row = (int) (density / 10.0);
+ int col = 0;
+ list_of_Cloud::iterator iCloud;
+ for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
+ if(++col > 9)
+ col = 0;
+ if( densTable[row][col] ) {
+ iCloud->visible = true;
+ } else
+ iCloud->visible = false;
+ }
+ last_density = density;
+}
+
// 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 );
theField.push_back( cl );
}
// build a field of cloud of size 25x25 km, its a grid of 11x11 clouds
void SGCloudField::buildTestLayer(void) {
- const float s = 2200.0f;
+ const float s = 2250.0f;
for( int z = -5 ; z <= 5 ; z++) {
for( int x = -5 ; x <= 5 ; x++ ) {
addCloud(pos, cloud);
}
}
-
+ applyDensity();
}
// cull all clouds of a tiled field
void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
list_of_Cloud::iterator iCloud;
-// const float distVisCompare = CloudVis * CloudVis;
// TODO:cull the field before culling the clouds in the field (should eliminate 3 fields)
for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
sgVec3 dist;
sgSphere sphere;
+ if( ! iCloud->visible )
+ continue;
sgSubVec3( dist, iCloud->pos, eyePos );
sphere.setCenter(dist[0], dist[2], dist[1]);
- sphere.setRadius(iCloud->aCloud->getRadius());
+ float radius = iCloud->aCloud->getRadius();
+ sphere.setRadius(radius);
sphere.orthoXform(mat);
if( frustum.contains( & sphere ) != SG_OUTSIDE ) {
float squareDist = dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2];
// save distance for later sort, opposite distance because we want back to front
tmp.dist = - squareDist;
inViewClouds.push_back(tmp);
+ if( squareDist - radius*radius < 100*100 )
+ sgEnviro.set_view_in_cloud(true);
}
}
}
-static inline void myswap(float &a, float &b) {
- float tmp = a;
- a = b;
- b = tmp;
-}
// Render a cloud field
// because no field can have an infinite size (and we don't want to reach his border)
if( ! enable3D )
return;
+ if( last_density != density ) {
+ last_density = density;
+ applyDensity();
+ }
+
// ask the impostor cache to do some cleanup
// TODO:don't do that for every field
SGNewCloud::cldCache->startNewFrame();
inViewClouds.clear();
- // cloud fields are tiled on the flat earth
- // compute the position in the tile
- relx = -fmod( deltax + relative_position[SG_X], fieldSize );
- rely = -fmod( deltay + relative_position[SG_Y], fieldSize );
glPushMatrix();
sgMat4 modelview, tmp, invtrans;
- // try to find the sun position (buggy)
+ // try to find the sun position
sgTransposeNegateMat4( invtrans, transform );
sgVec3 lightVec;
ssgGetLight( 0 )->getPosition( lightVec );
- sgNegateVec3( lightVec );
sgXformVec3( lightVec, invtrans );
- sgNormaliseVec3( lightVec );
sgCopyVec3( SGNewCloud::modelSunDir, lightVec );
-
+ sgSetVec3( SGNewCloud::modelSunDir, lightVec[0], lightVec[2], lightVec[1]);
// try to find the lighting data (buggy)
sgVec4 diffuse, ambient;
ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
sgCopyMat4( tmp, transform );
sgPostMultMat4( tmp, modelview );
- sgSetVec3( eyePos, -relx, -tmp[3][2], -rely);
- sgSetVec3( eyePos, -relx, 0, -rely);
+ // 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 );
+
sgSetVec3( eyePos, -relx, alt, -rely);
-// sgSetVec3( eyePos, 0, - tmp[3][2], 0);
-// sgSetVec3( eyePos, 20000, - tmp[3][2], 20000);
tmp[3][2] = 0;
tmp[3][0] = 0;
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.0f);
glDisable(GL_CULL_FACE);
-// glDisable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
-// glEnable( GL_COLOR_MATERIAL );
glEnable( GL_TEXTURE_2D );
glDisable( GL_FOG );
glDisable(GL_LIGHTING);
glBindTexture(GL_TEXTURE_2D, 0);
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
+ glEnable( GL_FOG );
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
ssgLoadModelviewMatrix( modelview );
glPopMatrix();
-// glEnable(GL_DEPTH_TEST);
}
sgVec3 eyePos;
float dist;
bool operator<(const culledCloud &b) const {
- return (this->dist < b.dist);
+ return this->dist < b.dist;
}
};
typedef vector<culledCloud> list_of_culledCloud;
public:
SGNewCloud *aCloud;
sgVec3 pos;
+ bool visible;
// float dist;
// bool culled;
// cull all clouds of a tiled field
void cullClouds(sgVec3 eyePos, sgMat4 mat);
+ void applyDensity(void);
+
list_of_Cloud theField;
// this is a relative position only, with that we can move all clouds at once
sgVec3 relative_position;
sgMat4 transform;
double deltax, deltay, alt;
+ double last_lon, last_lat, last_course;
+
+ float last_density;
public:
// return the size of the memory pool used by texture impostors
static int get_CacheSize(void);
+ static int get_CacheResolution(void);
static float get_CloudVis(void) { return CloudVis; }
static float get_density(void) { return density; }
static bool get_enable3dClouds(void) { return enable3D; }
static void set_CacheSize(int sizeKb);
+ static void set_CacheResolution(int resolutionPixels);
static void set_CloudVis(float distance);
static void set_density(float density);
static void set_enable3dClouds(bool enable);
#include STL_ALGORITHM
#include SG_GLU_H
+#include "cloudfield.hxx"
#include "newcloud.hxx"
bool SGNewCloud::useAnisotropic = true;
SGBbCache *SGNewCloud::cldCache = 0;
static bool texturesLoaded = false;
-float SGNewCloud::nearRadius = 2500.0f;
+float SGNewCloud::nearRadius = 3500.0f;
bool SGNewCloud::lowQuality = false;
sgVec3 SGNewCloud::sunlight = {0.5f, 0.5f, 0.5f};
sgVec3 SGNewCloud::ambLight = {0.5f, 0.5f, 0.5f};
// if( ! texturesLoaded ) {}
if( cldCache == 0 ) {
cldCache = new SGBbCache;
- cldCache->init( 20 );
+ cldCache->init( 64 );
}
}
// definition of a cu cloud, only for testing
void SGNewCloud::new_cu(void) {
- float s = 150.0f;
+ float s = 250.0f;
float r = Rnd(1.0) + 0.5;
if( r < 0.5f ) {
addContainer(0.0f, 0.0f, 0.0f, s, CLbox_cumulus);
} else {
// cb
- s = 475.0f;
+ s = 675.0f;
addContainer(0, 0, 0, s, CLbox_cumulus);
addContainer(0, 0, s, s, CLbox_cumulus);
addContainer(s, 0, s, s, CLbox_cumulus);
-/*
-Public Sub drawContainers()
- Dim N As Integer, i As Integer
- N = UBound(tbSpriteCont())
-
- Call glPolygonMode(faceFrontAndBack, pgmLine)
-
- Call glColor3f(0.9, 0.9, 0.9)
-
- For i = 0 To N - 1
- With tbSpriteCont(i)
- Call glPushMatrix
- Call glTranslatef(.x * c_scale + cloudpos(0), .y * c_scale + cloudpos(1), .z * c_scale + cloudpos(2))
- Call gCtl.Shapes.box(.r * c_scale, .r * c_scale, .r * c_scale)
- Call glPopMatrix
- End With
- Next i
-If 0 Then
- Call glPushMatrix
- Call glTranslatef(ccenter(0), ccenter(1), ccenter(2))
- Call gCtl.Shapes.Sphere(cradius, 8, 8)
- Call glPopMatrix
-End If
-End Sub
-*/
void SGNewCloud::drawContainers() {
}
-/*
-*/
-
-//bool SGNewCloud::compareSpriteFunction(const spriteDef &a, const spriteDef &b) {
-// return (a.dist > b.dist);
-//}
// sort on distance to eye because of transparency
void SGNewCloud::sortSprite( sgVec3 eye ) {
/* int clrank = fadingrank / 10;
int clfadeinrank = fadingrank - clrank * 10;*/
+ float CloudVisFade = 1.0 / (1.5 * SGCloudField::get_CloudVis());
+
+ computeSimpleLight( FakeEyePos );
+ // view point sort, we sort because of transparency
+ sortSprite( FakeEyePos );
GLint previousTexture = -1, thisTexture;
list_of_spriteDef::iterator iSprite;
sgCopyVec4 ( l3, iSprite->l3 );
if( ! drawBB ) {
// blend clouds with sky based on distance to limit the contrast of distant cloud
- // TODO:use cloudfield vis, not hardcoded value
- float t = 1.0f - dist_center / (15000.0f * 2.0 );
+ 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
glRotatef(angleX, 1.0f, 0.0f, 0.0f);
// blend clouds with sky based on distance to limit the contrast of distant cloud
- // TODO:use cloudfield vis, not hardcoded value
- float t = 1.0f - dist_center / (15000.0f * 2.0 );
+ float CloudVisFade = (1.5 * SGCloudField::get_CloudVis());
+
+ float t = 1.0f - dist_center / CloudVisFade;
// err the alpha value is not good for impostor, debug that
t *= 1.65;
if ( t < 0.0f )
glVertex2f(-r, -r);
glEnd();
-#if 1 // debug only
+#if 0 // debug only
int age = cldCache->queryImpostorAge(bbId);
// draw a red border for the newly generated BBs else draw a white border
if( age < 200 )
// near clouds we don't want to use BB
return false;
}
-// return false;
return true;
}
// render the cloud, fakepos is a relative position inside the cloud field
-void SGNewCloud::Render(sgVec3 fakepos) {
- sgVec3 eyePos, FakeEyePos;
+void SGNewCloud::Render(sgVec3 FakeEyePos) {
sgVec3 dist;
- glColor3f(1.0f, 1.0f, 1.0f);
-
- // obsolete code
- sgCopyVec3( eyePos, fakepos );
-
- sgCopyVec3( FakeEyePos, fakepos);
sgVec3 deltaPos;
-// sgSubVec3( deltaPos, eyePos, FakeEyePos);
sgCopyVec3( deltaPos, FakeEyePos);
sgSubVec3( dist, center, FakeEyePos);
float dist_center = sgLengthVec3(dist);
- // eeeek don't do that so early, perhaps we will use an impostor
- computeSimpleLight( FakeEyePos );
-
- // view point sort, we sort because of transparency
- // eeeek don't do that so early, perhaps we will use an impostor
- sortSprite( FakeEyePos );
if( !isBillboardable(dist_center) ) {