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