cloudfield.cxx

00001 // a layer of 3d clouds
00002 //
00003 // Written by Harald JOHNSEN, started April 2005.
00004 //
00005 // Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public License as
00009 // published by the Free Software Foundation; either version 2 of the
00010 // License, or (at your option) any later version.
00011 //
00012 // This program is distributed in the hope that it will be useful, but
00013 // WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 // General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with this program; if not, write to the Free Software
00019 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020 //
00021 //
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #  include <simgear_config.h>
00025 #endif
00026 
00027 #include <simgear/compiler.h>
00028 
00029 #include <plib/sg.h>
00030 #include <plib/ssg.h>
00031 #include <simgear/math/sg_random.h>
00032 #include <simgear/math/sg_geodesy.hxx>
00033 #include <simgear/math/polar3d.hxx>
00034 
00035 #include STL_ALGORITHM
00036 #include <vector>
00037 
00038 SG_USING_STD(vector);
00039 
00040 #include <simgear/environment/visual_enviro.hxx>
00041 #include "sky.hxx"
00042 #include "newcloud.hxx"
00043 #include "cloudfield.hxx"
00044 
00045 #if defined(__MINGW32__)
00046 #define isnan(x) _isnan(x)
00047 #endif
00048 
00049 #if defined (__FreeBSD__)
00050 #  if __FreeBSD_version < 500000
00051      extern "C" {
00052        inline int isnan(double r) { return !(r <= 0 || r >= 0); }
00053      }
00054 #  endif
00055 #endif
00056 
00057 
00058 #if defined (__CYGWIN__)
00059 #include <ieeefp.h>
00060 #endif
00061 
00062 static list_of_culledCloud inViewClouds;
00063 
00064 // visibility distance for clouds in meters
00065 float SGCloudField::CloudVis = 25000.0f;
00066 bool SGCloudField::enable3D = false;
00067 // fieldSize must be > CloudVis or we can destroy the impostor cache
00068 // a cloud must only be seen once or the impostor will be generated for each of his positions
00069 double SGCloudField::fieldSize = 50000.0;
00070 float SGCloudField::density = 100.0;
00071 double SGCloudField::timer_dt = 0.0;
00072 sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
00073 
00074 static int last_cache_size = 1*1024;
00075 static int cacheResolution = 64;
00076 static sgVec3 last_sunlight={0.0f, 0.0f, 0.0f};
00077 
00078 int SGCloudField::get_CacheResolution(void) {
00079         return cacheResolution;
00080 }
00081 
00082 void SGCloudField::set_CacheResolution(int resolutionPixels) {
00083         if (resolutionPixels == 0)
00084             resolutionPixels = 64;
00085         if(cacheResolution == resolutionPixels)
00086                 return;
00087         cacheResolution = resolutionPixels;
00088         if(enable3D) {
00089                 int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
00090                 if(count == 0)
00091                         count = 1;
00092                 SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
00093         }
00094 }
00095 
00096 int SGCloudField::get_CacheSize(void) { 
00097         return SGNewCloud::cldCache->queryCacheSize(); 
00098 }
00099 
00100 void SGCloudField::set_CacheSize(int sizeKb) {
00101         // apply in rendering option dialog
00102         if (sizeKb == 0)
00103             sizeKb = 1024;
00104         if(last_cache_size == sizeKb)
00105                 return;
00106         if(sizeKb)
00107                 last_cache_size = sizeKb;
00108         if(enable3D) {
00109                 int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
00110                 if(count == 0)
00111                         count = 1;
00112                 SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
00113         }
00114 }
00115 void SGCloudField::set_CloudVis(float distance) {
00116         if( distance <= fieldSize )
00117                 SGCloudField::CloudVis = distance;
00118 }
00119 void SGCloudField::set_density(float density) {
00120         SGCloudField::density = density;
00121 }
00122 void SGCloudField::set_enable3dClouds(bool enable) {
00123         if(enable3D == enable)
00124                 return;
00125         enable3D = enable;
00126         if(enable) {
00127                 int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
00128                 if(count == 0)
00129                         count = 1;
00130                 SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
00131         } else {
00132                 SGNewCloud::cldCache->setCacheSize(0);
00133         }
00134 }
00135 
00136 // reposition the cloud layer at the specified origin and orientation
00137 void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt, float direction, float speed) {
00138     sgMat4 T1, LON, LAT;
00139     sgVec3 axis;
00140 
00141     sgMakeTransMat4( T1, p );
00142 
00143     sgSetVec3( axis, 0.0, 0.0, 1.0 );
00144     sgMakeRotMat4( LON, lon * SGD_RADIANS_TO_DEGREES, axis );
00145 
00146     sgSetVec3( axis, 0.0, 1.0, 0.0 );
00147     sgMakeRotMat4( LAT, 90.0 - lat * SGD_RADIANS_TO_DEGREES, axis );
00148 
00149     sgMat4 TRANSFORM;
00150 
00151     sgCopyMat4( TRANSFORM, T1 );
00152     sgPreMultMat4( TRANSFORM, LON );
00153     sgPreMultMat4( TRANSFORM, LAT );
00154 
00155     sgCoord layerpos;
00156     sgSetCoord( &layerpos, TRANSFORM );
00157 
00158         sgMakeCoordMat4( transform, &layerpos );
00159 
00160 
00161         this->alt = alt;
00162 
00163         // simulate clouds movement from wind
00164     double sp_dist = speed*dt;
00165     if (sp_dist > 0) {
00166         double bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
00167         double by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
00168                 relative_position[SG_X] += bx;
00169                 relative_position[SG_Y] += by;
00170     }
00171 
00172     if ( lon != last_lon || lat != last_lat || sp_dist != 0 ) {
00173         Point3D start( last_lon, last_lat, 0.0 );
00174         Point3D dest( lon, lat, 0.0 );
00175         double course = 0.0, dist = 0.0;
00176 
00177         calc_gc_course_dist( dest, start, &course, &dist );
00178         // if start and dest are too close together,
00179         // calc_gc_course_dist() can return a course of "nan".  If
00180         // this happens, lets just use the last known good course.
00181         // This is a hack, and it would probably be better to make
00182         // calc_gc_course_dist() more robust.
00183         if ( isnan(course) ) {
00184             course = last_course;
00185         } else {
00186             last_course = course;
00187         }
00188 
00189         // calculate cloud movement due to external forces
00190         double ax = 0.0, ay = 0.0;
00191 
00192         if (dist > 0.0) {
00193             ax = cos(course) * dist;
00194             ay = sin(course) * dist;
00195         }
00196 
00197                 deltax += ax;
00198                 deltay += ay;
00199 
00200         last_lon = lon;
00201         last_lat = lat;
00202     }
00203 
00204 
00205         // correct the frustum with the right far plane
00206         ssgContext *context = ssgGetCurrentContext();
00207         frustum = *context->getFrustum();
00208 
00209         float w, h;
00210         sgEnviro.getFOV( w, h );
00211         frustum.setFOV( w, h );
00212         frustum.setNearFar(1.0, CloudVis);
00213         timer_dt = dt;
00214 }
00215 
00216 SGCloudField::SGCloudField() :
00217         deltax(0.0),
00218         deltay(0.0),
00219         last_course(0.0),
00220         last_density(0.0),
00221         draw_in_3d(true)
00222 {
00223         sgSetVec3( relative_position, 0,0,0);
00224         theField.reserve(200);
00225         inViewClouds.reserve(200);
00226         sg_srandom_time_10();
00227 }
00228 
00229 SGCloudField::~SGCloudField() {
00230         list_of_Cloud::iterator iCloud;
00231         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
00232                 delete iCloud->aCloud;
00233         }
00234         theField.clear();
00235 }
00236 
00237 
00238 void SGCloudField::clear(void) {
00239         list_of_Cloud::iterator iCloud;
00240         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
00241                 delete iCloud->aCloud;
00242         }
00243         theField.clear();
00244         // force a recompute of density on first redraw
00245         last_density = 0.0;
00246         // true to come back in set density after layer is built
00247         draw_in_3d = true;
00248 }
00249 
00250 // use a table or else we see poping when moving the slider...
00251 static int densTable[][10] = {
00252         {0,0,0,0,0,0,0,0,0,0},
00253         {1,0,0,0,0,0,0,0,0,0},
00254         {1,0,0,0,1,0,0,0,0,0},
00255         {1,0,0,0,1,0,0,1,0,0}, // 30%
00256         {1,0,1,0,1,0,0,1,0,0},
00257         {1,0,1,0,1,0,1,1,0,0}, // 50%
00258         {1,0,1,0,1,0,1,1,0,1},
00259         {1,0,1,1,1,0,1,1,0,1}, // 70%
00260         {1,1,1,1,1,0,1,1,0,1},
00261         {1,1,1,1,1,0,1,1,1,1}, // 90%
00262         {1,1,1,1,1,1,1,1,1,1}
00263 };
00264 
00265 // set the visible flag depending on density
00266 void SGCloudField::applyDensity(void) {
00267         int row = (int) (density / 10.0);
00268         int col = 0;
00269     sgBox fieldBox;
00270 
00271         list_of_Cloud::iterator iCloud;
00272         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
00273                 if(++col > 9)
00274                         col = 0;
00275                 if( densTable[row][col] ) {
00276                         iCloud->visible = true;
00277             fieldBox.extend( *iCloud->aCloud->getCenter() );
00278                 } else
00279                         iCloud->visible = false;
00280         }
00281         last_density = density;
00282         draw_in_3d = ( theField.size() != 0);
00283     sgVec3 center;
00284     sgSubVec3( center, fieldBox.getMax(), fieldBox.getMin() );
00285     sgScaleVec3( center, 0.5f );
00286     center[1] = 0.0f;
00287     field_sphere.setCenter( center );
00288     field_sphere.setRadius( fieldSize * 0.5f * 1.414f );
00289 }
00290 
00291 // add one cloud, data is not copied, ownership given
00292 void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
00293         Cloud cl;
00294         cl.aCloud = cloud;
00295         cl.visible = true;
00296         cloud->SetPos( pos );
00297         sgCopyVec3( cl.pos, *cloud->getCenter() );
00298         theField.push_back( cl );
00299 }
00300 
00301 
00302 static float Rnd(float n) {
00303         return n * (-0.5f + sg_random());
00304 }
00305 
00306 // for debug only
00307 // build a field of cloud of size 25x25 km, its a grid of 11x11 clouds
00308 void SGCloudField::buildTestLayer(void) {
00309 
00310         const float s = 2250.0f;
00311 
00312         for( int z = -5 ; z <= 5 ; z++) {
00313                 for( int x = -5 ; x <= 5 ; x++ ) {
00314                         SGNewCloud *cloud = new SGNewCloud(SGNewCloud::CLFamilly_cu);
00315                         cloud->new_cu();
00316                         sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s};
00317             addCloud(pos, cloud);
00318                 }
00319         }
00320         applyDensity();
00321 }
00322 
00323 // cull all clouds of a tiled field
00324 void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
00325         list_of_Cloud::iterator iCloud;
00326 
00327     sgSphere tile_sphere;
00328     tile_sphere.setRadius( field_sphere.getRadius() );
00329     sgVec3 tile_center;
00330     sgSubVec3( tile_center, field_sphere.getCenter(), eyePos );
00331     tile_sphere.setCenter( tile_center );
00332     tile_sphere.orthoXform(mat);
00333     if( frustum.contains( & tile_sphere ) == SG_OUTSIDE )
00334         return;
00335 
00336         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
00337                 sgVec3 dist;
00338                 sgSphere sphere;
00339                 if( ! iCloud->visible )
00340                         continue;
00341                 sgSubVec3( dist, iCloud->pos, eyePos );
00342                 sphere.setCenter(dist[0], dist[2], dist[1] + eyePos[1]);
00343                 float radius = iCloud->aCloud->getRadius();
00344                 sphere.setRadius(radius);
00345                 sphere.orthoXform(mat);
00346                 if( frustum.contains( & sphere ) != SG_OUTSIDE ) {
00347                         float squareDist = dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2];
00348                         culledCloud tmp;
00349                         tmp.aCloud = iCloud->aCloud;
00350                         sgCopyVec3( tmp.eyePos, eyePos ); 
00351                         // save distance for later sort, opposite distance because we want back to front
00352                         tmp.dist   =  - squareDist;
00353                         tmp.heading = -SG_PI/2.0 - atan2( dist[0], dist[2] ); // + SG_PI;
00354                         tmp.alt         = iCloud->pos[1];
00355                         inViewClouds.push_back(tmp);
00356                         if( squareDist - radius*radius < 100*100 )
00357                                 sgEnviro.set_view_in_cloud(true);
00358                 }
00359         }
00360 
00361 }
00362 
00363 
00364 // Render a cloud field
00365 // because no field can have an infinite size (and we don't want to reach his border)
00366 // we draw this field and adjacent fields.
00367 // adjacent fields are not real, its the same field displaced by some offset
00368 void SGCloudField::Render( float *sun_color ) {
00369     sgVec3 eyePos;
00370         double relx, rely;
00371 
00372         if( ! enable3D )
00373                 return;
00374 
00375         if( last_density != density ) {
00376                 last_density = density;
00377                 applyDensity();
00378         }
00379 
00380         if( ! draw_in_3d )
00381                 return;
00382 
00383         if( ! SGNewCloud::cldCache->isRttAvailable() )
00384                 return;
00385 
00386         inViewClouds.clear();
00387 
00388  
00389         glPushMatrix();
00390  
00391         sgMat4 modelview, tmp, invtrans;
00392 
00393         // try to find the sun position
00394         sgTransposeNegateMat4( invtrans, transform );
00395     sgVec3 lightVec;
00396     ssgGetLight( 0 )->getPosition( lightVec );
00397     sgXformVec3( lightVec, invtrans );
00398 
00399         sgSetVec3(  SGNewCloud::modelSunDir, lightVec[0], lightVec[2], lightVec[1]);
00400         // try to find the lighting data (not accurate)
00401         sgVec4 diffuse, ambient;
00402         ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
00403         ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient );
00404 //      sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 1.0f);
00405         sgScaleVec3 ( SGNewCloud::ambLight, ambient , 1.1f);
00406         // trying something else : clouds are more yellow/red at dawn/dusk
00407         // and added a bit of blue ambient
00408         sgScaleVec3 ( SGNewCloud::sunlight, sun_color , 0.4f);
00409         SGNewCloud::ambLight[2] += 0.1f;
00410 
00411         sgVec3 delta_light;
00412         sgSubVec3(delta_light, last_sunlight, SGNewCloud::sunlight);
00413         if( (fabs(delta_light[0]) + fabs(delta_light[1]) + fabs(delta_light[2])) > 0.05f ) {
00414                 sgCopyVec3( last_sunlight, SGNewCloud::sunlight );
00415                 // force the redraw of all the impostors
00416                 SGNewCloud::cldCache->invalidateCache();
00417         }
00418 
00419         // voodoo things on the matrix stack
00420     ssgGetModelviewMatrix( modelview );
00421         sgCopyMat4( tmp, transform );
00422     sgPostMultMat4( tmp, modelview );
00423 
00424         // cloud fields are tiled on the flat earth
00425         // compute the position in the tile
00426         relx = fmod( deltax + relative_position[SG_X], fieldSize );
00427         rely = fmod( deltay + relative_position[SG_Y], fieldSize );
00428 
00429         relx = fmod( relx + fieldSize, fieldSize );
00430         rely = fmod( rely + fieldSize, fieldSize );
00431         sgSetVec3( eyePos, relx, alt, rely);
00432 
00433         sgSetVec3( view_X, tmp[0][0], tmp[1][0], tmp[2][0] );
00434         sgSetVec3( view_Y, tmp[0][1], tmp[1][1], tmp[2][1] );
00435         sgSetVec3( view_vec, tmp[0][2], tmp[1][2], tmp[2][2] );
00436 
00437     ssgLoadModelviewMatrix( tmp );
00438  
00439 /* flat earth
00440 
00441         ^
00442         |
00443         |    FFF
00444         |    FoF
00445         |    FFF
00446         |
00447         O----------->
00448                 o = we are here
00449                 F = adjacent fields
00450 */
00451 
00452         for(int x = -1 ; x <= 1 ; x++)
00453                 for(int y = -1 ; y <= 1 ; y++ ) {
00454                         sgVec3 fieldPos;
00455                         // pretend we are not where we are
00456                         sgSetVec3(fieldPos, eyePos[0] + x*fieldSize, eyePos[1], eyePos[2] + y*fieldSize);
00457                         cullClouds(fieldPos, tmp);
00458                 }
00459         // sort all visible clouds back to front (because of transparency)
00460         std::sort( inViewClouds.begin(), inViewClouds.end() );
00461  
00462         // TODO:push states
00463         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00464     glEnable(GL_ALPHA_TEST);
00465     glAlphaFunc(GL_GREATER, 0.0f);
00466         glDisable(GL_CULL_FACE);
00467         glEnable(GL_DEPTH_TEST);
00468         glDepthMask( GL_FALSE );
00469         glEnable(GL_SMOOTH);
00470     glEnable(GL_BLEND);
00471         glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
00472         glEnable( GL_TEXTURE_2D );
00473         glDisable( GL_FOG );
00474     glDisable(GL_LIGHTING);
00475 
00476         // test data: field = 11x11 cloud, 9 fields
00477         // depending on position and view direction, perhaps 40 to 60 clouds not culled
00478         list_of_culledCloud::iterator iCloud;
00479         for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) {
00480 //              iCloud->aCloud->drawContainers();
00481                 iCloud->aCloud->Render(iCloud->eyePos);
00482                 sgEnviro.callback_cloud(iCloud->heading, iCloud->alt, 
00483                         iCloud->aCloud->getRadius(), iCloud->aCloud->getFamilly(), - iCloud->dist, iCloud->aCloud->getId());
00484         }
00485 
00486         glBindTexture(GL_TEXTURE_2D, 0);
00487     glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
00488         glEnable( GL_FOG );
00489         glEnable(GL_CULL_FACE);
00490         glEnable(GL_DEPTH_TEST);
00491 
00492         ssgLoadModelviewMatrix( modelview );
00493 
00494         glPopMatrix();
00495 
00496 }
00497 

Generated on Mon Dec 17 09:30:54 2007 for SimGear by  doxygen 1.5.1