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