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