]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/cloudfield.cxx
Remove an extern SGSky *thesky reference that isn't used in the code anyway.
[simgear.git] / simgear / scene / sky / cloudfield.cxx
1 // a layer of 3d clouds
2 //
3 // Written by Harald JOHNSEN, started April 2005.
4 //
5 // Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 //
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear_config.h>
25 #endif
26
27 #include <simgear/compiler.h>
28
29 #include <plib/sg.h>
30 #include <simgear/math/sg_random.h>
31 #include <simgear/math/sg_geodesy.hxx>
32 #include <simgear/math/polar3d.hxx>
33
34 #include STL_ALGORITHM
35 #include <vector>
36
37 SG_USING_STD(vector);
38
39 #include <simgear/environment/visual_enviro.hxx>
40 #include "sky.hxx"
41 #include "newcloud.hxx"
42 #include "cloudfield.hxx"
43
44 #if defined(__MINGW32__)
45 #define isnan(x) _isnan(x)
46 #endif
47
48 #if defined (__FreeBSD__)
49 #  if __FreeBSD_version < 500000
50      extern "C" {
51        inline int isnan(double r) { return !(r <= 0 || r >= 0); }
52      }
53 #  endif
54 #endif
55
56
57 #if defined (__CYGWIN__)
58 #include <ieeefp.h>
59 #endif
60
61 static list_of_culledCloud inViewClouds;
62
63 // visibility distance for clouds in meters
64 float SGCloudField::CloudVis = 25000.0f;
65 bool SGCloudField::enable3D = false;
66 // fieldSize must be > CloudVis or we can destroy the impostor cache
67 // a cloud must only be seen once or the impostor will be generated for each of his positions
68 double SGCloudField::fieldSize = 50000.0;
69 float SGCloudField::density = 100.0;
70 double SGCloudField::timer_dt = 0.0;
71 sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
72
73 static int last_cache_size = 1*1024;
74 static int cacheResolution = 64;
75 static sgVec3 last_sunlight={0.0f, 0.0f, 0.0f};
76
77 int SGCloudField::get_CacheResolution(void) {
78 #if 0
79         return cacheResolution;
80 #endif
81         return 0;
82 }
83
84 void SGCloudField::set_CacheResolution(int resolutionPixels) {
85 #if 0
86         if(cacheResolution == resolutionPixels)
87                 return;
88         cacheResolution = resolutionPixels;
89         if(enable3D) {
90                 int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
91                 if(count == 0)
92                         count = 1;
93                 SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
94         }
95 #endif
96 }
97
98 int SGCloudField::get_CacheSize(void) { 
99 #if 0
100         return SGNewCloud::cldCache->queryCacheSize(); 
101 #endif
102         return 0;
103 }
104
105 void SGCloudField::set_CacheSize(int sizeKb) {
106 #if 0
107         // apply in rendering option dialog
108         if(last_cache_size == sizeKb)
109                 return;
110         if(sizeKb == 0)
111                 return;
112         if(sizeKb)
113                 last_cache_size = sizeKb;
114         if(enable3D) {
115                 int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
116                 if(count == 0)
117                         count = 1;
118                 SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
119         }
120 #endif
121 }
122 void SGCloudField::set_CloudVis(float distance) {
123 #if 0
124         if( distance <= fieldSize )
125                 SGCloudField::CloudVis = distance;
126 #endif
127 }
128 void SGCloudField::set_density(float density) {
129 #if 0
130         SGCloudField::density = density;
131 #endif
132 }
133 void SGCloudField::set_enable3dClouds(bool enable) {
134 #if 0
135         if(enable3D == enable)
136                 return;
137         enable3D = enable;
138         if(enable) {
139                 int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4);
140                 if(count == 0)
141                         count = 1;
142                 SGNewCloud::cldCache->setCacheSize(count, cacheResolution);
143         } else {
144                 SGNewCloud::cldCache->setCacheSize(0);
145         }
146 #endif
147 }
148
149 // reposition the cloud layer at the specified origin and orientation
150 void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt, float direction, float speed) {
151 #if 0
152     sgMat4 T1, LON, LAT;
153     sgVec3 axis;
154
155     sgMakeTransMat4( T1, p );
156
157     sgSetVec3( axis, 0.0, 0.0, 1.0 );
158     sgMakeRotMat4( LON, lon * SGD_RADIANS_TO_DEGREES, axis );
159
160     sgSetVec3( axis, 0.0, 1.0, 0.0 );
161     sgMakeRotMat4( LAT, 90.0 - lat * SGD_RADIANS_TO_DEGREES, axis );
162
163     sgMat4 TRANSFORM;
164
165     sgCopyMat4( TRANSFORM, T1 );
166     sgPreMultMat4( TRANSFORM, LON );
167     sgPreMultMat4( TRANSFORM, LAT );
168
169     sgCoord layerpos;
170     sgSetCoord( &layerpos, TRANSFORM );
171
172         sgMakeCoordMat4( transform, &layerpos );
173
174
175         this->alt = alt;
176
177         // simulate clouds movement from wind
178     double sp_dist = speed*dt;
179     if (sp_dist > 0) {
180         double bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
181         double by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
182                 relative_position[SG_X] += bx;
183                 relative_position[SG_Y] += by;
184     }
185
186     if ( lon != last_lon || lat != last_lat || sp_dist != 0 ) {
187         Point3D start( last_lon, last_lat, 0.0 );
188         Point3D dest( lon, lat, 0.0 );
189         double course = 0.0, dist = 0.0;
190
191         calc_gc_course_dist( dest, start, &course, &dist );
192         // if start and dest are too close together,
193         // calc_gc_course_dist() can return a course of "nan".  If
194         // this happens, lets just use the last known good course.
195         // This is a hack, and it would probably be better to make
196         // calc_gc_course_dist() more robust.
197         if ( isnan(course) ) {
198             course = last_course;
199         } else {
200             last_course = course;
201         }
202
203         // calculate cloud movement due to external forces
204         double ax = 0.0, ay = 0.0;
205
206         if (dist > 0.0) {
207             ax = cos(course) * dist;
208             ay = sin(course) * dist;
209         }
210
211                 deltax += ax;
212                 deltay += ay;
213
214         last_lon = lon;
215         last_lat = lat;
216     }
217
218
219         // correct the frustum with the right far plane
220         ssgContext *context = ssgGetCurrentContext();
221         frustum = *context->getFrustum();
222
223         float w, h;
224         sgEnviro.getFOV( w, h );
225         frustum.setFOV( w, h );
226         frustum.setNearFar(1.0, CloudVis);
227         timer_dt = dt;
228 #endif
229 }
230
231 SGCloudField::SGCloudField() :
232         deltax(0.0),
233         deltay(0.0),
234         last_course(0.0),
235         last_density(0.0),
236         draw_in_3d(true)
237 {
238 #if 0
239         sgSetVec3( relative_position, 0,0,0);
240         theField.reserve(200);
241         inViewClouds.reserve(200);
242         sg_srandom_time_10();
243 #else
244         draw_in_3d = false;
245 #endif
246 }
247
248 SGCloudField::~SGCloudField() {
249 #if 0
250         list_of_Cloud::iterator iCloud;
251         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
252                 delete iCloud->aCloud;
253         }
254         theField.clear();
255 #endif
256 }
257
258
259 void SGCloudField::clear(void) {
260 #if 0
261         list_of_Cloud::iterator iCloud;
262         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
263                 delete iCloud->aCloud;
264         }
265         theField.clear();
266         // force a recompute of density on first redraw
267         last_density = 0.0;
268         // true to come back in set density after layer is built
269         draw_in_3d = true;
270 #endif
271 }
272
273 // use a table or else we see poping when moving the slider...
274 static int densTable[][10] = {
275         {0,0,0,0,0,0,0,0,0,0},
276         {1,0,0,0,0,0,0,0,0,0},
277         {1,0,0,0,1,0,0,0,0,0},
278         {1,0,0,0,1,0,0,1,0,0}, // 30%
279         {1,0,1,0,1,0,0,1,0,0},
280         {1,0,1,0,1,0,1,1,0,0}, // 50%
281         {1,0,1,0,1,0,1,1,0,1},
282         {1,0,1,1,1,0,1,1,0,1}, // 70%
283         {1,1,1,1,1,0,1,1,0,1},
284         {1,1,1,1,1,0,1,1,1,1}, // 90%
285         {1,1,1,1,1,1,1,1,1,1}
286 };
287
288 // set the visible flag depending on density
289 void SGCloudField::applyDensity(void) {
290 #if 0
291         int row = (int) (density / 10.0);
292         int col = 0;
293     sgBox fieldBox;
294
295         list_of_Cloud::iterator iCloud;
296         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
297                 if(++col > 9)
298                         col = 0;
299                 if( densTable[row][col] ) {
300                         iCloud->visible = true;
301             fieldBox.extend( *iCloud->aCloud->getCenter() );
302                 } else
303                         iCloud->visible = false;
304         }
305         last_density = density;
306         draw_in_3d = ( theField.size() != 0);
307     sgVec3 center;
308     sgSubVec3( center, fieldBox.getMax(), fieldBox.getMin() );
309     sgScaleVec3( center, 0.5f );
310     center[1] = 0.0f;
311     field_sphere.setCenter( center );
312     field_sphere.setRadius( fieldSize * 0.5f * 1.414f );
313 #endif
314 }
315
316 // add one cloud, data is not copied, ownership given
317 void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
318 #if 0
319         Cloud cl;
320         cl.aCloud = cloud;
321         cl.visible = true;
322         cloud->SetPos( pos );
323         sgCopyVec3( cl.pos, *cloud->getCenter() );
324         theField.push_back( cl );
325 #endif
326 }
327
328
329 static float Rnd(float n) {
330         return n * (-0.5f + sg_random());
331 }
332
333 // for debug only
334 // build a field of cloud of size 25x25 km, its a grid of 11x11 clouds
335 void SGCloudField::buildTestLayer(void) {
336 #if 0
337         const float s = 2250.0f;
338
339         for( int z = -5 ; z <= 5 ; z++) {
340                 for( int x = -5 ; x <= 5 ; x++ ) {
341                         SGNewCloud *cloud = new SGNewCloud(SGNewCloud::CLFamilly_cu);
342                         cloud->new_cu();
343                         sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s};
344             addCloud(pos, cloud);
345                 }
346         }
347         applyDensity();
348 #endif
349 }
350
351 // cull all clouds of a tiled field
352 void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
353 #if 0
354         list_of_Cloud::iterator iCloud;
355
356     sgSphere tile_sphere;
357     tile_sphere.setRadius( field_sphere.getRadius() );
358     sgVec3 tile_center;
359     sgSubVec3( tile_center, field_sphere.getCenter(), eyePos );
360     tile_sphere.setCenter( tile_center );
361     tile_sphere.orthoXform(mat);
362     if( frustum.contains( & tile_sphere ) == SG_OUTSIDE )
363         return;
364
365         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
366                 sgVec3 dist;
367                 sgSphere sphere;
368                 if( ! iCloud->visible )
369                         continue;
370                 sgSubVec3( dist, iCloud->pos, eyePos );
371                 sphere.setCenter(dist[0], dist[2], dist[1] + eyePos[1]);
372                 float radius = iCloud->aCloud->getRadius();
373                 sphere.setRadius(radius);
374                 sphere.orthoXform(mat);
375                 if( frustum.contains( & sphere ) != SG_OUTSIDE ) {
376                         float squareDist = dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2];
377                         culledCloud tmp;
378                         tmp.aCloud = iCloud->aCloud;
379                         sgCopyVec3( tmp.eyePos, eyePos ); 
380                         // save distance for later sort, opposite distance because we want back to front
381                         tmp.dist   =  - squareDist;
382                         tmp.heading = -SG_PI/2.0 - atan2( dist[0], dist[2] ); // + SG_PI;
383                         tmp.alt         = iCloud->pos[1];
384                         inViewClouds.push_back(tmp);
385                         if( squareDist - radius*radius < 100*100 )
386                                 sgEnviro.set_view_in_cloud(true);
387                 }
388         }
389 #endif
390 }
391
392
393 // Render a cloud field
394 // because no field can have an infinite size (and we don't want to reach his border)
395 // we draw this field and adjacent fields.
396 // adjacent fields are not real, its the same field displaced by some offset
397 void SGCloudField::Render(float *sun_color) {
398     // sun_color used to depend on an extern SGSky *thesky definition
399     // above.  However, this is bad form for a library and it's much
400     // more clean to just pass in the needed value.  For reference, here is
401     // the old way that sun_color was fetched ...
402     // float *sun_color = thesky->get_sun_color();
403
404 #if 0
405     sgVec3 eyePos;
406         double relx, rely;
407
408         if( ! enable3D )
409                 return;
410
411         if( last_density != density ) {
412                 last_density = density;
413                 applyDensity();
414         }
415
416         if( ! draw_in_3d )
417                 return;
418
419         if( ! SGNewCloud::cldCache->isRttAvailable() )
420                 return;
421
422         inViewClouds.clear();
423
424  
425         glPushMatrix();
426  
427         sgMat4 modelview, tmp, invtrans;
428
429         // try to find the sun position
430         sgTransposeNegateMat4( invtrans, transform );
431     sgVec3 lightVec;
432     ssgGetLight( 0 )->getPosition( lightVec );
433     sgXformVec3( lightVec, invtrans );
434
435         sgSetVec3(  SGNewCloud::modelSunDir, lightVec[0], lightVec[2], lightVec[1]);
436         // try to find the lighting data (not accurate)
437         sgVec4 diffuse, ambient;
438         ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
439         ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient );
440 //      sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 1.0f);
441         sgScaleVec3 ( SGNewCloud::ambLight, ambient , 1.1f);
442         // trying something else : clouds are more yellow/red at dawn/dusk
443         // and added a bit of blue ambient
444         sgScaleVec3 ( SGNewCloud::sunlight, sun_color , 0.4f);
445         SGNewCloud::ambLight[2] += 0.1f;
446
447         sgVec3 delta_light;
448         sgSubVec3(delta_light, last_sunlight, SGNewCloud::sunlight);
449         if( (fabs(delta_light[0]) + fabs(delta_light[1]) + fabs(delta_light[2])) > 0.05f ) {
450                 sgCopyVec3( last_sunlight, SGNewCloud::sunlight );
451                 // force the redraw of all the impostors
452                 SGNewCloud::cldCache->invalidateCache();
453         }
454
455         // voodoo things on the matrix stack
456     ssgGetModelviewMatrix( modelview );
457         sgCopyMat4( tmp, transform );
458     sgPostMultMat4( tmp, modelview );
459
460         // cloud fields are tiled on the flat earth
461         // compute the position in the tile
462         relx = fmod( deltax + relative_position[SG_X], fieldSize );
463         rely = fmod( deltay + relative_position[SG_Y], fieldSize );
464
465         relx = fmod( relx + fieldSize, fieldSize );
466         rely = fmod( rely + fieldSize, fieldSize );
467         sgSetVec3( eyePos, relx, alt, rely);
468
469         sgSetVec3( view_X, tmp[0][0], tmp[1][0], tmp[2][0] );
470         sgSetVec3( view_Y, tmp[0][1], tmp[1][1], tmp[2][1] );
471         sgSetVec3( view_vec, tmp[0][2], tmp[1][2], tmp[2][2] );
472
473     ssgLoadModelviewMatrix( tmp );
474  
475 /* flat earth
476
477         ^
478         |
479         |    FFF
480         |    FoF
481         |    FFF
482         |
483         O----------->
484                 o = we are here
485                 F = adjacent fields
486 */
487
488         for(int x = -1 ; x <= 1 ; x++)
489                 for(int y = -1 ; y <= 1 ; y++ ) {
490                         sgVec3 fieldPos;
491                         // pretend we are not where we are
492                         sgSetVec3(fieldPos, eyePos[0] + x*fieldSize, eyePos[1], eyePos[2] + y*fieldSize);
493                         cullClouds(fieldPos, tmp);
494                 }
495         // sort all visible clouds back to front (because of transparency)
496         std::sort( inViewClouds.begin(), inViewClouds.end() );
497  
498         // TODO:push states
499         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
500     glEnable(GL_ALPHA_TEST);
501     glAlphaFunc(GL_GREATER, 0.0f);
502         glDisable(GL_CULL_FACE);
503         glEnable(GL_DEPTH_TEST);
504         glDepthMask( GL_FALSE );
505         glEnable(GL_SMOOTH);
506     glEnable(GL_BLEND);
507         glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
508         glEnable( GL_TEXTURE_2D );
509         glDisable( GL_FOG );
510     glDisable(GL_LIGHTING);
511
512         // test data: field = 11x11 cloud, 9 fields
513         // depending on position and view direction, perhaps 40 to 60 clouds not culled
514         list_of_culledCloud::iterator iCloud;
515         for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) {
516 //              iCloud->aCloud->drawContainers();
517                 iCloud->aCloud->Render(iCloud->eyePos);
518                 sgEnviro.callback_cloud(iCloud->heading, iCloud->alt, 
519                         iCloud->aCloud->getRadius(), iCloud->aCloud->getFamilly(), - iCloud->dist, iCloud->aCloud->getId());
520         }
521
522         glBindTexture(GL_TEXTURE_2D, 0);
523     glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
524         glEnable( GL_FOG );
525         glEnable(GL_CULL_FACE);
526         glEnable(GL_DEPTH_TEST);
527
528         ssgLoadModelviewMatrix( modelview );
529
530         glPopMatrix();
531 #endif
532 }