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