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