]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/cloudfield.cxx
Harald Johnson:
[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_geodesy.hxx>
32
33 #include STL_ALGORITHM
34 #include SG_GLUT_H
35 #include <vector>
36
37 SG_USING_STD(vector);
38
39 #include "newcloud.hxx"
40 #include "cloudfield.hxx"
41
42 static list_of_culledCloud inViewClouds;
43
44 // visibility distance for clouds in meters
45 float SGCloudField::CloudVis = 25000.0f;
46 bool SGCloudField::enable3D = true;
47 // fieldSize must be > CloudVis or we can destroy the impostor cache
48 // a cloud must only be seen once or the impostor will be generated for each of his positions
49 double SGCloudField::fieldSize = 30000.0;
50 float SGCloudField::density = 100.0;
51 static int last_cache_size = 4*1024;
52
53 int SGCloudField::get_CacheSize(void) { 
54         return SGNewCloud::cldCache->queryCacheSize(); 
55 }
56
57 void SGCloudField::set_CacheSize(int sizeKb) {
58         // apply in rendering option dialog
59         if(last_cache_size == sizeKb)
60                 return;
61         if(sizeKb == 0)
62                 return;
63         if(sizeKb)
64                 last_cache_size = sizeKb;
65         SGNewCloud::cldCache->setCacheSize(sizeKb);
66 }
67 void SGCloudField::set_CloudVis(float distance) {
68         SGCloudField::CloudVis = distance;
69 }
70 void SGCloudField::set_density(float density) {
71         SGCloudField::density = density;
72 }
73 void SGCloudField::set_enable3dClouds(bool enable) {
74         if(enable3D == enable)
75                 return;
76         enable3D = enable;
77         if(enable) {
78                 SGNewCloud::cldCache->setCacheSize(last_cache_size);
79         } else {
80                 SGNewCloud::cldCache->setCacheSize(0);
81         }
82 }
83
84 // reposition the cloud layer at the specified origin and orientation
85 void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt) {
86     sgMat4 T1, LON, LAT;
87     sgVec3 axis;
88
89     sgMakeTransMat4( T1, p );
90
91     sgSetVec3( axis, 0.0, 0.0, 1.0 );
92     sgMakeRotMat4( LON, lon * SGD_RADIANS_TO_DEGREES, axis );
93
94     sgSetVec3( axis, 0.0, 1.0, 0.0 );
95     sgMakeRotMat4( LAT, 90.0 - lat * SGD_RADIANS_TO_DEGREES, axis );
96
97     sgMat4 TRANSFORM;
98
99     sgCopyMat4( TRANSFORM, T1 );
100     sgPreMultMat4( TRANSFORM, LON );
101     sgPreMultMat4( TRANSFORM, LAT );
102
103     sgCoord layerpos;
104     sgSetCoord( &layerpos, TRANSFORM );
105
106         sgMakeCoordMat4( transform, &layerpos );
107
108         // TODO:use a simple sphere earth
109         double az1, az2, s;
110         geo_inverse_wgs_84( 0.0, 0.0, 0.0, lat*SG_RADIANS_TO_DEGREES , lon*SG_RADIANS_TO_DEGREES, &az1, &az2, &s);
111         az1 = az1 * SG_DEGREES_TO_RADIANS;
112         // compute the view position on a 'flat' earth
113         deltay = -cos(SG_PI/2+az1) * s;
114         deltax = -sin(SG_PI/2+az1) * s;
115 //      deltax = cos(0.0) * s;
116 //      deltay = sin(0.0) * s;
117         this->alt = alt;
118
119         // simulate clouds movement from wind
120         double speed = 10.0f;
121         double direction = 45.0;
122     double sp_dist = speed*dt;
123     if (sp_dist > 0) {
124         double bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
125         double by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
126                 relative_position[SG_X] += bx;
127                 relative_position[SG_Y] += by;
128     }
129
130         // correct the frustum with the right far plane
131         ssgContext *context = ssgGetCurrentContext();
132         frustum = *context->getFrustum();
133         frustum.setNearFar(1.0, CloudVis);
134 }
135
136 SGCloudField::SGCloudField() {
137         sgSetVec3( relative_position, 0,0,0);
138         theField.reserve(200);
139         inViewClouds.reserve(200);
140 }
141
142 SGCloudField::~SGCloudField() {
143         list_of_Cloud::iterator iCloud;
144         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
145                 delete iCloud->aCloud;
146         }
147         theField.clear();
148 }
149
150
151 // add one cloud, data is not copied, ownership given
152 void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
153         Cloud cl;
154         sgCopyVec3( cl.pos, pos );
155         cl.aCloud = cloud;
156         cloud->SetPos( pos );
157         theField.push_back( cl );
158 }
159
160
161 static float Rnd(float n) {
162         return n * (-0.5f + rand() / (float) RAND_MAX);
163 }
164
165 // for debug only
166 // build a field of cloud of size 25x25 km, its a grid of 11x11 clouds
167 void SGCloudField::buildTestLayer(void) {
168
169         const float s = 2200.0f;
170
171         for( int z = -5 ; z <= 5 ; z++) {
172                 for( int x = -5 ; x <= 5 ; x++ ) {
173             SGNewCloud *cloud = new SGNewCloud;
174                         cloud->new_cu();
175                         sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s};
176             addCloud(pos, cloud);
177                 }
178         }
179
180 }
181
182 // cull all clouds of a tiled field
183 void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
184         list_of_Cloud::iterator iCloud;
185 //      const float distVisCompare = CloudVis * CloudVis;
186
187         // TODO:cull the field before culling the clouds in the field (should eliminate 3 fields)
188         for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
189                 sgVec3 dist;
190                 sgSphere sphere;
191                 sgSubVec3( dist, iCloud->pos, eyePos );
192                 sphere.setCenter(dist[0], dist[2], dist[1]);
193                 sphere.setRadius(iCloud->aCloud->getRadius());
194                 sphere.orthoXform(mat);
195                 if( frustum.contains( & sphere ) != SG_OUTSIDE ) {
196                         float squareDist = dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2];
197                         culledCloud tmp;
198                         tmp.aCloud = iCloud->aCloud;
199                         sgCopyVec3( tmp.eyePos, eyePos ); 
200                         // save distance for later sort, opposite distance because we want back to front
201                         tmp.dist   =  - squareDist;
202                         inViewClouds.push_back(tmp);
203                 }
204         }
205
206 }
207
208 static inline void myswap(float &a, float &b) {
209         float tmp = a;
210         a = b;
211         b = tmp;
212 }
213
214 // Render a cloud field
215 // because no field can have an infinite size (and we don't want to reach his border)
216 // we draw this field and adjacent fields.
217 // adjacent fields are not real, its the same field displaced by some offset
218 void SGCloudField::Render(void) {
219     sgVec3 eyePos;
220         double relx, rely;
221
222         if( ! enable3D )
223                 return;
224
225         // ask the impostor cache to do some cleanup
226         // TODO:don't do that for every field
227         SGNewCloud::cldCache->startNewFrame();
228
229         inViewClouds.clear();
230
231         // cloud fields are tiled on the flat earth
232         // compute the position in the tile
233         relx = -fmod( deltax + relative_position[SG_X], fieldSize );
234         rely = -fmod( deltay + relative_position[SG_Y], fieldSize );
235  
236         glPushMatrix();
237  
238         sgMat4 modelview, tmp, invtrans;
239
240         // try to find the sun position (buggy)
241         sgTransposeNegateMat4( invtrans, transform );
242     sgVec3 lightVec;
243     ssgGetLight( 0 )->getPosition( lightVec );
244     sgNegateVec3( lightVec );
245     sgXformVec3( lightVec, invtrans );
246         sgNormaliseVec3( lightVec );
247         sgCopyVec3( SGNewCloud::modelSunDir, lightVec );
248
249         // try to find the lighting data (buggy)
250         sgVec4 diffuse, ambient;
251         ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
252         ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient );
253         sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 0.70f);
254         sgScaleVec3 ( SGNewCloud::ambLight, ambient , 0.60f);
255
256         // voodoo things on the matrix stack
257     ssgGetModelviewMatrix( modelview );
258         sgCopyMat4( tmp, transform );
259     sgPostMultMat4( tmp, modelview );
260
261         sgSetVec3( eyePos, -relx, -tmp[3][2], -rely);
262         sgSetVec3( eyePos, -relx, 0, -rely);
263         sgSetVec3( eyePos, -relx, alt, -rely);
264 //      sgSetVec3( eyePos, 0, - tmp[3][2], 0);
265 //      sgSetVec3( eyePos, 20000, - tmp[3][2], 20000);
266
267         tmp[3][2] = 0;
268         tmp[3][0] = 0;
269         tmp[3][1] = 0;
270     ssgLoadModelviewMatrix( tmp );
271  
272 /* flat earth
273
274         ^
275         |
276         |    FFF
277         |    FoF
278         |    FFF
279         |
280         O----------->
281                 o = we are here
282                 F = adjacent fields
283 */
284
285         for(int x = -1 ; x <= 1 ; x++)
286                 for(int y = -1 ; y <= 1 ; y++ ) {
287                         sgVec3 fieldPos;
288                         // pretend we are not where we are
289                         sgSetVec3(fieldPos, eyePos[0] + x*fieldSize, eyePos[1], eyePos[2] + y*fieldSize);
290                         cullClouds(fieldPos, tmp);
291                 }
292         // sort all visible clouds back to front (because of transparency)
293         sort( inViewClouds.begin(), inViewClouds.end() );
294  
295         // TODO:push states
296         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
297     glEnable(GL_ALPHA_TEST);
298     glAlphaFunc(GL_GREATER, 0.0f);
299         glDisable(GL_CULL_FACE);
300 //      glDisable(GL_DEPTH_TEST);
301         glEnable(GL_DEPTH_TEST);
302         glEnable(GL_SMOOTH);
303     glEnable(GL_BLEND);
304         glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
305 //      glEnable( GL_COLOR_MATERIAL ); 
306         glEnable( GL_TEXTURE_2D );
307         glDisable( GL_FOG );
308     glDisable(GL_LIGHTING);
309
310         // test data: field = 11x11 cloud, 9 fields
311         // depending on position and view direction, perhaps 40 to 60 clouds not culled
312         list_of_culledCloud::iterator iCloud;
313         for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) {
314 //              iCloud->aCloud->drawContainers();
315                 iCloud->aCloud->Render(iCloud->eyePos);
316         }
317
318         glBindTexture(GL_TEXTURE_2D, 0);
319     glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
320
321         ssgLoadModelviewMatrix( modelview );
322
323         glPopMatrix();
324 //      glEnable(GL_DEPTH_TEST);
325
326 }
327