]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/newcloud.cxx
Syd Adams: Replace rgb with png.
[simgear.git] / simgear / scene / sky / newcloud.cxx
1 // 3D cloud class
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 <osg/ref_ptr>
28 #include <osg/Texture2D>
29
30 #include <simgear/compiler.h>
31
32 #include <plib/sg.h>
33 #include <simgear/math/sg_random.h>
34 #include <simgear/misc/sg_path.hxx>
35
36 #include <algorithm>
37 #include <osg/GLU>
38
39 #include "cloudfield.hxx"
40 #include "newcloud.hxx"
41
42
43 /*
44 */
45
46 static osg::ref_ptr<osg::Texture2D> cloudTextures[SGNewCloud::CLTexture_max];
47
48
49 bool SGNewCloud::useAnisotropic = true;
50 SGBbCache *SGNewCloud::cldCache = 0;
51 static bool texturesLoaded = false;
52 static float minx, maxx, miny, maxy, minz, maxz;
53 static int cloudIdCounter = 1;
54
55 float SGNewCloud::nearRadius = 3500.0f;
56 bool SGNewCloud::lowQuality = false;
57 sgVec3 SGNewCloud::sunlight = {0.5f, 0.5f, 0.5f};
58 sgVec3 SGNewCloud::ambLight = {0.5f, 0.5f, 0.5f};
59 sgVec3 SGNewCloud::modelSunDir = {0,1,0};
60
61
62 void SGNewCloud::init(void) {
63         bbId = -1;
64         fadeActive = false;
65         duration = 100.0f;
66         fadetimer = 100.0f;
67         pauseLength = 0.0f;
68         last_step = -1.0f;
69         familly = CLFamilly_nn;
70         cloudId = ++cloudIdCounter;
71         sgSetVec3(center, 0.0f, 0.0f, 0.0f);
72         sgSetVec3(cloudpos, 0.0f, 0.0f, 0.0f);
73         radius = 0.0f;
74         delta_base = 0.0f;
75         list_spriteContainer.reserve(8);
76         list_spriteDef.reserve(40);
77
78         if( cldCache == 0 ) {
79                 cldCache = new SGBbCache;
80                 cldCache->init( 64 );
81         }
82 }
83
84 // constructor
85 SGNewCloud::SGNewCloud(CLFamilly_type classification)
86 {
87         init();
88         familly = classification;
89 }
90
91 SGNewCloud::SGNewCloud(string classification)
92 {
93         init();
94         if( classification == "cu" )
95                 familly = CLFamilly_cu;
96         else if( classification == "cb" )
97                 familly = CLFamilly_cb;
98         else if( classification == "st" )
99                 familly = CLFamilly_st;
100         else if( classification == "ns" )
101                 familly = CLFamilly_ns;
102         else if( classification == "sc" )
103                 familly = CLFamilly_sc;
104         else if( classification == "as" )
105                 familly = CLFamilly_as;
106         else if( classification == "ac" )
107                 familly = CLFamilly_ac;
108         else if( classification == "ci" )
109                 familly = CLFamilly_ci;
110         else if( classification == "cc" )
111                 familly = CLFamilly_cc;
112         else if( classification == "cs" )
113                 familly = CLFamilly_cs;
114 }
115
116 SGNewCloud::~SGNewCloud() {
117         list_spriteDef.clear();
118         list_spriteContainer.clear();
119         cldCache->free( bbId, cloudId );
120 }
121
122
123 // load all textures used to draw cloud sprites
124 void SGNewCloud::loadTextures(const string &tex_path) {
125         if( texturesLoaded )
126                 return;
127         texturesLoaded = true;
128
129         SGPath cloud_path;
130
131     cloud_path.set(tex_path);
132     cloud_path.append("cl_cumulus.png");
133     // OSGFIXME
134 //     cloudTextures[ CLTexture_cumulus ] = new osg::Texture2D( cloud_path.str().c_str(), false, false, false );
135     cloudTextures[ CLTexture_cumulus ] = new osg::Texture2D;
136
137     cloud_path.set(tex_path);
138     cloud_path.append("cl_stratus.png");
139     // OSGFIXME
140 //     cloudTextures[ CLTexture_stratus ] = new ssgTexture( cloud_path.str().c_str(), false, false, false );
141     cloudTextures[ CLTexture_stratus ] = new osg::Texture2D;
142
143 }
144
145 void SGNewCloud::startFade(bool direction, float duration, float pauseLength) {
146       if(duration <= 0.0) {
147               fadeActive = false;
148               return;
149       }
150       this->direction = direction;
151       fadetimer = 0.0;
152       this->duration = duration;
153       this->pauseLength = pauseLength;
154       last_step = -1.0;
155       fadeActive = true;
156 }
157 void SGNewCloud::setFade(float howMuch) {
158       duration = 100.0;
159       fadetimer = howMuch;
160       fadeActive = false;
161       last_step = -1.0;
162 }
163
164
165 static inline float rayleighCoeffAngular(float cosAngle) {
166         return 3.0f / 4.0f * (1.0f + cosAngle * cosAngle);
167 }
168
169 // cp is normalized (len==1)
170 static void CartToPolar3d(sgVec3 cp, sgVec3 polar) {
171     polar[0] = atan2(cp[1], cp[0]);
172     polar[1] = SG_PI / 2.0f - atan2(sqrt (cp[0] * cp[0] + cp[1] * cp[1]), cp[2]);
173         polar[2] = 1.0f;
174 }
175
176 static void PolarToCart3d(sgVec3 p, sgVec3 cart) {
177     float tmp = cos(p[1]);
178     cart[0] = cos(p[0]) * tmp;
179     cart[1] = sin(p[0]) * tmp;
180     cart[2] = sin(p[1]);
181 }
182
183
184 // compute the light for a cloud sprite corner
185 // from the normal and the sun, scaled by the Rayleigh factor
186 // and finaly added to the ambient light
187 static inline void lightFunction(sgVec3 normal, sgVec4 light, float pf) {
188         float cosAngle = sgScalarProductVec3( normal, SGNewCloud::modelSunDir);
189         float vl = (1.0f - 0.5f + cosAngle * 0.5f) * pf;
190         sgScaleVec3( light, SGNewCloud::sunlight, 0.25f + 0.75f * vl );
191         sgAddVec3( light, SGNewCloud::ambLight );
192         // we need to clamp or else the light will bug when adding transparency
193         if( light[0] > 1.0 )    light[0] = 1.0;
194         if( light[1] > 1.0 )    light[1] = 1.0;
195         if( light[2] > 1.0 )    light[2] = 1.0;
196         light[3] = 1.0;
197 }
198
199 // compute the light for a cloud sprite
200 // we use ambient light and orientation versus sun position
201 void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) {
202         // constant Rayleigh factor if we are not doing Anisotropic lighting
203         float pf = 1.0f;
204
205         list_of_spriteDef::iterator iSprite;
206         for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
207                 if( useAnisotropic ) {
208                         sgVec3 eyeDir;
209             sgSubVec3(eyeDir, iSprite->pos, FakeEyePos);
210             sgNormaliseVec3(eyeDir);
211             float cosAngle = sgScalarProductVec3(eyeDir, modelSunDir);
212             pf = rayleighCoeffAngular(cosAngle);
213                 }
214                 lightFunction(iSprite->n0, iSprite->l0, pf);
215                 lightFunction(iSprite->n1, iSprite->l1, pf);
216                 lightFunction(iSprite->n2, iSprite->l2, pf);
217                 lightFunction(iSprite->n3, iSprite->l3, pf);
218
219         }
220 }
221
222
223 // add a new box to the cloud
224 void SGNewCloud::addContainer (float x, float y, float z, float r, CLbox_type type) {
225         spriteContainer cont;
226         sgSetVec3( cont.pos, x, y, z );
227         cont.r = r;
228         cont.cont_type = type;
229         sgSetVec3( cont.center, 0.0f, 0.0f, 0.0f);
230         list_spriteContainer.push_back( cont );
231         // don't place cloud below his base
232         if( y - r*0.50 < delta_base )
233                 delta_base = y - r*0.50;
234 }
235
236 // add a sprite inside a box
237 void SGNewCloud::addSprite(float x, float y, float z, float r, CLbox_type type, int box) {
238         spriteDef newSpriteDef;
239         int rank = list_spriteDef.size();
240         sgSetVec3( newSpriteDef.pos, x, y - delta_base, z);
241         newSpriteDef.box = box;
242         newSpriteDef.sprite_type = type;
243         newSpriteDef.rank = rank;
244         newSpriteDef.r = r;
245         list_spriteDef.push_back( newSpriteDef );
246         spriteContainer *thisBox = &list_spriteContainer[box];
247         sgVec3 deltaPos;
248         sgSubVec3( deltaPos, newSpriteDef.pos, thisBox->pos );
249         sgAddVec3( thisBox->center, deltaPos );
250
251         r = r * 0.70f;  // 0.5 * 1.xxx
252     if( x - r < minx )
253                 minx = x - r;
254     if( y - r < miny )
255                 miny = y - r;
256     if( z - r < minz )
257                 minz = z - r;
258     if( x + r > maxx )
259                 maxx = x + r;
260     if( y + r > maxy )
261                 maxy = y + r;
262     if( z + r > maxz )
263                 maxz = z + r;
264
265 }
266
267 // return a random number between -n/2 and n/2
268 static float Rnd(float n) {
269         return n * (-0.5f + sg_random());
270 }
271
272 // generate all sprite with defined boxes
273 void SGNewCloud::genSprites( void ) {
274         float x, y, z, r;
275     int N, sc;
276         minx = miny = minz = 99999.0;
277         maxx = maxy = maxz = -99999.0;
278
279     N = list_spriteContainer.size();
280         for(int i = 0 ; i < N ; i++ ) {
281                 spriteContainer *thisBox = & list_spriteContainer[i];
282                 // the type defines how the sprites can be positioned inside the box, their size, etc
283                 switch(thisBox->cont_type) {
284                         case CLbox_sc:
285                                 sc = 1;
286                                 r = thisBox->r + Rnd(0.2f);
287                                 x = thisBox->pos[SG_X] + Rnd(thisBox->r * 0.75f);
288                                 y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.75f);
289                                 z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f);
290                                 addSprite(x, y, z, r, thisBox->cont_type, i);
291                                 break;
292                         case CLbox_stratus:
293                                 sc = 1;
294                                 r = thisBox->r;
295                                 x = thisBox->pos[SG_X];
296                                 y = thisBox->pos[SG_Y];
297                                 z = thisBox->pos[SG_Z];
298                                 addSprite(x, y, z, r, thisBox->cont_type, i);
299                                 break;
300                         case CLbox_cumulus:
301                                 for( sc = 0 ; sc <= 4 ; sc ++ ) {
302                                         r = thisBox->r + Rnd(0.2f);
303                                         x = thisBox->pos[SG_X] + Rnd(thisBox->r * 0.75f);
304                                         y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.5f);
305                                         z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f);
306                                         if ( y < thisBox->pos[SG_Y] - thisBox->r / 10.0f )
307                                                 y = thisBox->pos[SG_Y] - thisBox->r / 10.0f;
308                                         addSprite(x, y, z, r, thisBox->cont_type, i);
309                                 }
310                                 break;
311                         default:
312                                 for( sc = 0 ; sc <= 4 ; sc ++ ) {
313                                         r = thisBox->r + Rnd(0.2f);
314                                         x = thisBox->pos[SG_X] + Rnd(thisBox->r);
315                                         y = thisBox->pos[SG_Y] + Rnd(thisBox->r);
316                                         z = thisBox->pos[SG_Z] + Rnd(thisBox->r);
317                                         addSprite(x, y, z, r, thisBox->cont_type, i);
318                                 }
319                                 break;
320                 }
321         sgScaleVec3(thisBox->center, 1.0f / sc);
322         }
323
324         radius = maxx - minx;
325     if ( (maxy - miny) > radius )
326                 radius = (maxy - miny);
327     if ( (maxz - minz) > radius )
328                 radius = (maxz - minz);
329     radius /= 2.0f;
330     sgSetVec3( center, (maxx + minx) / 2.0f, (maxy + miny) / 2.0f, (maxz + minz) / 2.0f );
331
332         const float ang = 45.0f * SG_PI / 180.0f;
333
334         // compute normals
335         list_of_spriteDef::iterator iSprite;
336         for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
337                 sgVec3 normal;
338                 spriteContainer *thisSpriteContainer = &list_spriteContainer[iSprite->box];
339                 if( familly == CLFamilly_sc || familly == CLFamilly_cu || familly == CLFamilly_cb) {
340                         sgSubVec3(normal, iSprite->pos, center);
341                 } else {
342                         sgSubVec3(normal, iSprite->pos, thisSpriteContainer->pos);
343                         sgSubVec3(normal, thisSpriteContainer->center);
344                         sgSubVec3(normal, cloudpos);
345                 }
346                 if( normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f )
347                         sgSetVec3( normal, 0.0f, 1.0f, 0.0f );
348         sgNormaliseVec3(normal);
349                 // use exotic lightning function, this will give more 'relief' to the clouds
350                 // compute a normal for each vextex this will simulate a smooth shading for a round shape
351                 sgVec3 polar, pt;
352                 // I suspect this code to be bugged...
353         CartToPolar3d(normal, polar);
354                 sgCopyVec3(iSprite->normal, normal);
355
356                 // offset the normal vector by some angle for each vertex
357         sgSetVec3(pt, polar[0] - ang, polar[1] - ang, polar[2]);
358         PolarToCart3d(pt, iSprite->n0);
359         sgSetVec3(pt, polar[0] + ang, polar[1] - ang, polar[2]);
360         PolarToCart3d(pt, iSprite->n1);
361         sgSetVec3(pt, polar[0] + ang, polar[1] + ang, polar[2]);
362         PolarToCart3d(pt, iSprite->n2);
363         sgSetVec3(pt, polar[0] - ang, polar[1] + ang, polar[2]);
364         PolarToCart3d(pt, iSprite->n3);
365         }
366
367         // experimental : clouds are dissipating with time
368         if( familly == CLFamilly_cu ) {
369                 startFade(true, 300.0f, 30.0f);
370                 fadetimer = sg_random() * 300.0f;
371         }
372 }
373
374
375 // definition of a cu cloud, only for testing
376 void SGNewCloud::new_cu(void) {
377         float s = 250.0f;
378         float r = Rnd(1.0) + 0.5;
379         if( r < 0.5f ) {
380                 addContainer(0.0f, 0.0f, 0.0f, s, CLbox_cumulus);
381                 addContainer(s, 0, 0, s, CLbox_cumulus);
382                 addContainer(0, 0, 2 * s, s, CLbox_cumulus);
383                 addContainer(s, 0, 2 * s, s, CLbox_cumulus);
384
385                 addContainer(-1.2f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
386                 addContainer(0.2f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
387                 addContainer(1.6f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
388         } else if ( r < 0.90f ) {
389                 addContainer(0, 0, 0, s * 1.2, CLbox_cumulus);
390                 addContainer(s, 0, 0, s, CLbox_cumulus);
391                 addContainer(0, 0, s, s, CLbox_cumulus);
392                 addContainer(s * 1.1, 0, s, s * 1.2, CLbox_cumulus);
393
394                 addContainer(-1.2 * s, 1 + 0.2 * s, s * 0.5, s * 1.4, CLbox_standard);
395                 addContainer(0.2 * s, 1 + 0.25 * s, s * 0.5, s * 1.5, CLbox_standard);
396                 addContainer(1.6 * s, 1 + 0.2 * s, s * 0.5, s * 1.4, CLbox_standard);
397
398         } else {
399                 // cb
400                 s = 675.0f;
401                 addContainer(0, 0, 0, s, CLbox_cumulus);
402                 addContainer(0, 0, s, s, CLbox_cumulus);
403                 addContainer(s, 0, s, s, CLbox_cumulus);
404                 addContainer(s, 0, 0, s, CLbox_cumulus);
405
406                 addContainer(s / 2, s, s / 2, s * 1.5, CLbox_standard);
407
408                 addContainer(0, 2 * s, 0, s, CLbox_standard);
409                 addContainer(0, 2 * s, s, s, CLbox_standard);
410                 addContainer(s, 2 * s, s, s, CLbox_standard);
411                 addContainer(s, 2 * s, 0, s, CLbox_standard);
412
413         }
414         genSprites();
415 }
416
417
418 // define the new position of the cloud (inside the cloud field, not on sphere)
419 void SGNewCloud::SetPos(sgVec3 newPos) {
420     int N = list_spriteDef.size();
421     sgVec3 deltaPos;
422         sgSubVec3( deltaPos, newPos, cloudpos );
423
424     // for each particle
425         for(int i = 0 ; i < N ; i ++) {
426                 sgAddVec3( list_spriteDef[i].pos, deltaPos );
427         }
428         sgAddVec3( center, deltaPos );
429     sgSetVec3( cloudpos, newPos[SG_X], newPos[SG_Y], newPos[SG_Z]);
430 }
431
432
433
434
435 void SGNewCloud::drawContainers() {
436
437
438 }
439
440
441
442 // sort on distance to eye because of transparency
443 void SGNewCloud::sortSprite( sgVec3 eye ) {
444         list_of_spriteDef::iterator iSprite;
445
446         // compute distance from sprite to eye
447         for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
448                 sgVec3 dist;
449                 sgSubVec3( dist, iSprite->pos, eye );
450                 iSprite->dist = -(dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2]);
451         }
452         std::sort( list_spriteDef.begin(), list_spriteDef.end() );
453 }
454
455 // render the cloud on screen or on the RTT texture to build the impostor
456 void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos, float dist_center ) {
457
458         float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration;
459         int clrank = (int) step;
460         float clfadeinrank = (step - clrank);
461         last_step = step;
462
463         float CloudVisFade = 1.0 / (0.7f * SGCloudField::get_CloudVis());
464         // blend clouds with sky based on distance to limit the contrast of distant cloud
465         float t = 1.0f - dist_center * CloudVisFade;
466 //      if ( t < 0.0f ) 
467 //              return;
468
469     computeSimpleLight( FakeEyePos );
470
471     // view point sort, we sort because of transparency
472         sortSprite( FakeEyePos );
473
474         float dark = (familly == CLFamilly_cb ? 0.9f : 1.0f);
475
476         GLint previousTexture = -1, thisTexture;
477         list_of_spriteDef::iterator iSprite;
478         for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
479                 // skip this sprite if faded
480                 if(iSprite->rank > clrank)
481                         continue;
482                 // choose texture to use depending on sprite type
483                 switch(iSprite->sprite_type) {
484                         case CLbox_stratus:
485                                 thisTexture = CLTexture_stratus;
486                                 break;
487                         default:
488                                 thisTexture = CLTexture_cumulus;
489                                 break;
490                 }
491                 // in practice there is no texture switch (atm)
492                 if( previousTexture != thisTexture ) {
493                         previousTexture = thisTexture;
494                         // OSGFIXME
495 //                      glBindTexture(GL_TEXTURE_2D, cloudTextures[thisTexture]->getHandle());
496                 }
497
498                         sgVec3 translate;
499                         sgSubVec3( translate, iSprite->pos, deltaPos);
500
501
502                         // flipx and flipy are random texture flip flags, this gives more random clouds
503                         float flipx = (float) ( iSprite->rank & 1 );
504                         float flipy = (float) ( (iSprite->rank >> 1) & 1 );
505                         // cu texture have a flat bottom so we can't do a vertical flip
506                         if( iSprite->sprite_type == CLbox_cumulus )
507                                 flipy = 0.0f;
508 //                      if( iSprite->sprite_type == CLbox_stratus )
509 //                              flipx = 0.0f;
510                         // adjust colors depending on cloud type
511                         // TODO : rewrite that later, still experimental
512                         switch(iSprite->sprite_type) {
513                                 case CLbox_cumulus:
514                                         // dark bottom
515                     sgScaleVec3(iSprite->l0, 0.8f * dark);
516                     sgScaleVec3(iSprite->l1, 0.8f * dark);
517                                         sgScaleVec3(iSprite->l2, dark);
518                                         sgScaleVec3(iSprite->l3, dark);
519                                         break;
520                                 case CLbox_stratus:
521                                         // usually dark grey
522                                         if( familly == CLFamilly_st ) {
523                                                 sgScaleVec3(iSprite->l0, 0.8f);
524                                                 sgScaleVec3(iSprite->l1, 0.8f);
525                                                 sgScaleVec3(iSprite->l2, 0.8f);
526                                                 sgScaleVec3(iSprite->l3, 0.8f);
527                                         } else {
528                                                 sgScaleVec3(iSprite->l0, 0.7f);
529                                                 sgScaleVec3(iSprite->l1, 0.7f);
530                                                 sgScaleVec3(iSprite->l2, 0.7f);
531                                                 sgScaleVec3(iSprite->l3, 0.7f);
532                                         }
533                                         break;
534                                 default:
535                                         // darker bottom than top
536                     sgScaleVec3(iSprite->l0, 0.8f);
537                     sgScaleVec3(iSprite->l1, 0.8f);
538                                         break;
539                         }
540                         float r = iSprite->r * 0.5f;
541
542                         sgVec4 l0, l1, l2, l3;
543                         sgCopyVec4 ( l0, iSprite->l0 );
544                         sgCopyVec4 ( l1, iSprite->l1 );
545                         sgCopyVec4 ( l2, iSprite->l2 );
546                         sgCopyVec4 ( l3, iSprite->l3 );
547                         if( ! drawBB ) {
548                                 // now clouds at the far plane are half blended
549                                 sgScaleVec4( l0, t );
550                                 sgScaleVec4( l1, t );
551                                 sgScaleVec4( l2, t );
552                                 sgScaleVec4( l3, t );
553                         }
554                         if( iSprite->rank == clrank ) {
555                                 sgScaleVec4( l0, clfadeinrank );
556                                 sgScaleVec4( l1, clfadeinrank );
557                                 sgScaleVec4( l2, clfadeinrank );
558                                 sgScaleVec4( l3, clfadeinrank );
559                         }
560                         // compute the rotations so that the quad is facing the camera
561                         sgVec3 pos;
562                         sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
563                         sgCopyVec3( translate, pos );
564                         translate[2] -= FakeEyePos[1];
565 //                      sgNormaliseVec3( translate );
566                         float dist_sprite = sgLengthVec3 ( translate );
567                         sgScaleVec3 ( translate, SG_ONE / dist_sprite ) ;
568                         sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
569                         if( dist_sprite > 2*r ) {
570                                 sgVectorProductVec3(x, translate, up);
571                                 sgVectorProductVec3(y, x, translate);
572                         } else {
573                                 sgCopyVec3( x, SGCloudField::view_X );
574                                 sgCopyVec3( y, SGCloudField::view_Y );
575                         }
576                         sgScaleVec3(x, r);
577                         sgScaleVec3(y, r);
578  
579                         sgVec3 left, right;
580                         if( drawBB )
581                                 sgSetVec3( left, iSprite->pos[SG_X], iSprite->pos[SG_Z], iSprite->pos[SG_Y]);
582                         else
583                                 sgCopyVec3( left, pos );
584                         sgSubVec3 (left, y);
585                         sgAddVec3 (right, left, x);
586                         sgSubVec3 (left, x);
587
588                         glBegin(GL_QUADS);
589                                 glColor4fv(l0);
590                 glTexCoord2f(flipx, 1.0f - flipy);
591                 glVertex3fv(left);
592                 glColor4fv(l1);
593                 glTexCoord2f(1.0f - flipx, 1.0f - flipy);
594                 glVertex3fv(right);
595                                 sgScaleVec3( y, 2.0 );
596                                 sgAddVec3( left, y);
597                                 sgAddVec3( right, y);
598                 glColor4fv(l2);
599                 glTexCoord2f(1.0f - flipx, flipy);
600                 glVertex3fv(right);
601                 glColor4fv(l3);
602                 glTexCoord2f(flipx, flipy);
603                 glVertex3fv(left);
604
605                         glEnd();        
606
607         }
608 }
609
610
611 // compute rotations so that a quad is facing the camera
612 // TODO:change obsolete code because we dont use glrotate anymore
613 void SGNewCloud::CalcAngles(sgVec3 refpos, sgVec3 FakeEyePos, float *angleY, float *angleX) {
614     sgVec3 upAux, lookAt, objToCamProj, objToCam;
615         float angle, angle2;
616
617     sgSetVec3(objToCamProj, -FakeEyePos[SG_X] + refpos[SG_X], -FakeEyePos[SG_Z] + refpos[SG_Z], 0.0f);
618     sgNormaliseVec3(objToCamProj);
619
620     sgSetVec3(lookAt, 0.0f, 1.0f, 0.0f);
621     sgVectorProductVec3(upAux, lookAt, objToCamProj);
622     angle = sgScalarProductVec3(lookAt, objToCamProj);
623         if( (angle < 0.9999f) && (angle > -0.9999f) ) {
624         angle = acos(angle) * 180.0f / SG_PI;
625         if( upAux[2] < 0.0f )
626             angle = -angle;
627         } else
628         angle = 0.0f;
629
630     sgSetVec3(objToCam, -FakeEyePos[SG_X] + refpos[SG_X], -FakeEyePos[SG_Z] + refpos[SG_Z], -FakeEyePos[SG_Y] + refpos[SG_Y]);
631         sgNormaliseVec3(objToCam);
632
633     angle2 = sgScalarProductVec3(objToCamProj, objToCam);
634         if( (angle2 < 0.9999f) && (angle2 > -0.9999f) ) {
635         angle2 = -acos(angle2) * 180.0f / SG_PI;
636         if(  objToCam[2] > 0.0f )
637             angle2 = -angle2;
638         } else
639         angle2 = 0.0f;
640
641         angle2 += 90.0f;
642
643         *angleY = angle;
644     *angleX = angle2;
645 }
646
647 // draw a cloud but this time we use the impostor texture
648 void SGNewCloud::RenderBB(sgVec3 deltaPos, bool first_time, float dist_center) {
649
650                 sgVec3 translate;
651                 sgSubVec3( translate, center, deltaPos);
652
653                 // blend clouds with sky based on distance to limit the contrast of distant cloud
654                 float CloudVisFade = (1.0f * SGCloudField::get_CloudVis());
655
656                 float t = 1.0f - (dist_center - 1.0*radius) / CloudVisFade;
657                 if ( t < 0.0f ) 
658                         return;
659                 if( t > 1.0f )
660                         t = 1.0f;
661                 if( t > 0.50f )
662                         t *= 1.1f;
663         glColor4f(t, t, t, t);
664         float r = radius;
665                 // compute the rotations so that the quad is facing the camera
666                 sgVec3 pos;
667                 sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
668                 sgCopyVec3( translate, pos );
669                 pos[2] += deltaPos[1];
670
671                 sgNormaliseVec3( translate );
672                 sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
673                 sgVectorProductVec3(x, translate, up);
674                 sgVectorProductVec3(y, x, translate);
675                 if(first_time) {
676                         sgCopyVec3( rotX, x );
677                         sgCopyVec3( rotY, y );
678                 } else if(fabs(sgScalarProductVec3(rotX, x)) < 0.93f || fabs(sgScalarProductVec3(rotY, y)) < 0.93f ) {
679                         // ask for a redraw of this impostor if the view angle changed too much
680                         sgCopyVec3( rotX, x );
681                         sgCopyVec3( rotY, y );
682                         cldCache->invalidate(cloudId, bbId);
683                 }
684                 sgScaleVec3(x, r);
685                 sgScaleVec3(y, r);
686
687                 sgVec3 left, right;
688                 sgCopyVec3( left, pos );
689                 sgSubVec3 (left, y);
690                 sgAddVec3 (right, left, x);
691                 sgSubVec3 (left, x);
692
693                 glBegin(GL_QUADS);
694                         glTexCoord2f(0.0f, 0.0f);
695             glVertex3fv(left);
696                         glTexCoord2f(1.0f, 0.0f);
697             glVertex3fv(right);
698                         sgScaleVec3( y, 2.0 );
699                         sgAddVec3( left, y);
700                         sgAddVec3( right, y);
701                         glTexCoord2f(1.0f, 1.0f);
702             glVertex3fv(right);
703                         glTexCoord2f(0.0f, 1.0f);
704             glVertex3fv(left);
705                 glEnd();
706
707 #if 0   // debug only
708                 int age = cldCache->queryImpostorAge(bbId);
709                 // draw a red border for the newly generated BBs else draw a white border
710         if( age < 200 )
711             glColor3f(1, 0, 0);
712                 else
713             glColor3f(1, 1, 1);
714
715         glBindTexture(GL_TEXTURE_2D, 0);
716         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
717         glBegin(GL_QUADS);
718             glVertex2f(-r, -r);
719             glVertex2f(r, -r);
720             glVertex2f(r, r);
721             glVertex2f(-r, r);
722         glEnd();
723                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
724
725 #endif
726
727 }
728
729 // determine if it is a good idea to use an impostor to render the cloud
730 bool SGNewCloud::isBillboardable(float dist) {
731
732         if( dist <= ( 2.1f * radius ) ) {
733         // inside cloud
734         return false;
735         }
736         if( (dist-radius) <= nearRadius ) {
737         // near clouds we don't want to use BB
738         return false;
739         }
740         return true;
741 }
742
743
744
745 // render the cloud, fakepos is a relative position inside the cloud field
746 void SGNewCloud::Render(sgVec3 FakeEyePos) {
747         sgVec3 dist;
748
749         sgVec3 deltaPos;
750         sgCopyVec3( deltaPos, FakeEyePos);
751         deltaPos[1] = 0.0;
752     sgSubVec3( dist, center, FakeEyePos);
753     float dist_center = sgLengthVec3(dist);
754
755         if( fadeActive ) {
756                 fadetimer += SGCloudField::timer_dt;
757                 if( fadetimer > duration + pauseLength ) {
758                         // fade out after fade in, and vice versa
759                         direction = ! direction;
760                         fadetimer = 0.0;
761                 }
762         }
763
764         if( !isBillboardable(dist_center) ) {
765                 // not a good candidate for impostors, draw a real cloud
766                 Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
767         } else {
768                 GLuint texID = 0;
769                         bool first_time = false;
770                         // lets use our impostor
771                         if( bbId >= 0)
772                                 texID = cldCache->QueryTexID(cloudId, bbId);
773
774                         // ok someone took our impostor, so allocate a new one
775                         if( texID == 0 ) {
776                 // allocate a new Impostor
777                 bbId = cldCache->alloc(cloudId);
778                                 texID = cldCache->QueryTexID(cloudId, bbId);
779                                 first_time = true;
780                         }
781                         if( texID == 0 ) {
782                 // no more free texture in the pool
783                 Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
784                         } else {
785                 float angleX=0.0f, angleY=0.0f;
786
787                                 // force a redraw of the impostor if the cloud shape has changed enought
788                                 float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration;
789                                 if( fabs(step - last_step) > 0.5f )
790                                         cldCache->invalidate(cloudId, bbId);
791
792                                 if( ! cldCache->isBbValid( cloudId, bbId, angleY, angleX)) {
793                     // we must build or rebuild this billboard
794                                         // start render to texture
795                     cldCache->beginCapture();
796                                         // set transformation matrices
797                     cldCache->setRadius(radius, dist_center);
798                                         gluLookAt(FakeEyePos[SG_X], FakeEyePos[SG_Z], FakeEyePos[SG_Y], center[SG_X], center[SG_Z], center[SG_Y], 0.0, 0.0, 1.0);
799                                         // draw into texture
800                     Render3Dcloud(true, FakeEyePos, deltaPos, dist_center);
801                                         // save rotation angles for later use
802                                         // TODO:this is not ok
803                                         cldCache->setReference(cloudId, bbId, angleY, angleX);
804                                         // save the rendered cloud into the cache
805                                         cldCache->setTextureData( bbId );
806                                         // finish render to texture and go back into standard context
807                     cldCache->endCapture();
808                                 }
809                 // draw the newly built BB or an old one
810                 glBindTexture(GL_TEXTURE_2D, texID);
811                 RenderBB(FakeEyePos, first_time, dist_center);
812                         }
813         }
814
815 }
816