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