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